@bifos/dooray-cli 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +26 -0
  2. package/dist/index.js +374 -14
  3. package/package.json +8 -2
package/README.md CHANGED
@@ -82,6 +82,32 @@ dooray wiki pages tc-ocr # 페이지 목록
82
82
  dooray wiki page get tc-ocr <page-id> # 페이지 상세
83
83
  ```
84
84
 
85
+ ### 메일
86
+
87
+ IMAP을 통해 Dooray 메일을 조회할 수 있습니다.
88
+
89
+ ```bash
90
+ # 초기 설정 (서버 정보는 기본값 제공)
91
+ dooray config set imap-username your@email.com
92
+ dooray config set imap-password <IMAP_APP_PASSWORD>
93
+
94
+ # 메일 조회
95
+ dooray mail list # 최근 메일 목록
96
+ dooray mail list --unread # 안읽은 메일만
97
+ dooray mail list --search "키워드" # 제목 검색
98
+ dooray mail list --size 50 # 조회 개수 지정
99
+ dooray mail get <uid> # 메일 상세
100
+ dooray mail get <uid> --json # JSON 출력
101
+
102
+ # 메일 발송
103
+ dooray mail send --to "user@example.com" --subject "제목" --body "본문"
104
+ dooray mail send --to "a@b.com" --cc "c@d.com" --subject "제목" --body-file ./content.md
105
+ dooray mail send --to "a@b.com" --subject "HTML 메일" --body "<h1>Hello</h1>" --html
106
+
107
+ # 메일 답장 (스레드 유지)
108
+ dooray mail reply <uid> --body "답장 내용"
109
+ ```
110
+
85
111
  ## 출력 모드
86
112
 
87
113
  | 플래그 | 설명 | 용도 |
package/dist/index.js CHANGED
@@ -24,8 +24,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_commander23 = require("commander");
28
- var import_chalk4 = __toESM(require("chalk"));
27
+ var import_commander27 = require("commander");
28
+ var import_chalk6 = __toESM(require("chalk"));
29
29
 
30
30
  // src/commands/config.ts
31
31
  var import_commander = require("commander");
@@ -52,6 +52,15 @@ var EXIT_AUTH_ERROR = 2;
52
52
  var EXIT_PARAM_ERROR = 3;
53
53
  var EXIT_CONFIG_ERROR = 4;
54
54
 
55
+ // src/config/types.ts
56
+ var DEFAULTS = {
57
+ baseUrl: "https://api.dooray.com",
58
+ imapHost: "imap.dooray.com",
59
+ imapPort: 993,
60
+ smtpHost: "smtp.dooray.com",
61
+ smtpPort: 465
62
+ };
63
+
55
64
  // src/config/store.ts
56
65
  var DOORAY_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".dooray");
57
66
  var CONFIG_PATH = (0, import_node_path.join)(DOORAY_DIR, "config.json");
@@ -81,7 +90,7 @@ async function setConfigValue(key, value) {
81
90
  const config = await getConfig() ?? {
82
91
  version: 1,
83
92
  apiKey: "",
84
- baseUrl: ""
93
+ baseUrl: DEFAULTS.baseUrl
85
94
  };
86
95
  switch (key) {
87
96
  case "api-key":
@@ -90,10 +99,28 @@ async function setConfigValue(key, value) {
90
99
  case "base-url":
91
100
  config.baseUrl = value;
92
101
  break;
102
+ case "imap-host":
103
+ config.imapHost = value;
104
+ break;
105
+ case "imap-port":
106
+ config.imapPort = parseInt(value, 10);
107
+ break;
108
+ case "imap-username":
109
+ config.imapUsername = value;
110
+ break;
111
+ case "imap-password":
112
+ config.imapPassword = value;
113
+ break;
114
+ case "smtp-host":
115
+ config.smtpHost = value;
116
+ break;
117
+ case "smtp-port":
118
+ config.smtpPort = parseInt(value, 10);
119
+ break;
93
120
  default:
94
121
  throw new DoorayCliError(
95
122
  `\uC54C \uC218 \uC5C6\uB294 \uC124\uC815 \uD0A4: ${key}
96
- \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD0A4: api-key, base-url`,
123
+ \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD0A4: api-key, base-url, imap-host, imap-port, imap-username, imap-password, smtp-host, smtp-port`,
97
124
  EXIT_CONFIG_ERROR
98
125
  );
99
126
  }
@@ -1555,20 +1582,347 @@ var wikiPageEditCommand = new import_commander22.Command("edit").description("\u
1555
1582
  `);
1556
1583
  });
1557
1584
 
1585
+ // src/commands/mail/list.ts
1586
+ var import_commander23 = require("commander");
1587
+
1588
+ // src/api/imapClient.ts
1589
+ var import_imapflow = require("imapflow");
1590
+ var import_mailparser = require("mailparser");
1591
+ function getImapConfigOrThrow(config) {
1592
+ if (!config.imapUsername || !config.imapPassword) {
1593
+ throw new DoorayCliError(
1594
+ "IMAP \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uC124\uC815\uC744 \uC9C4\uD589\uD558\uC138\uC694:\n dooray config set imap-username <YOUR_EMAIL>\n dooray config set imap-password <YOUR_IMAP_PASSWORD>",
1595
+ EXIT_CONFIG_ERROR
1596
+ );
1597
+ }
1598
+ return {
1599
+ host: config.imapHost ?? DEFAULTS.imapHost,
1600
+ port: config.imapPort ?? DEFAULTS.imapPort,
1601
+ username: config.imapUsername,
1602
+ password: config.imapPassword
1603
+ };
1604
+ }
1605
+ function createClient(config) {
1606
+ const imap = getImapConfigOrThrow(config);
1607
+ return new import_imapflow.ImapFlow({
1608
+ host: imap.host,
1609
+ port: imap.port,
1610
+ secure: true,
1611
+ auth: { user: imap.username, pass: imap.password },
1612
+ logger: false
1613
+ });
1614
+ }
1615
+ async function listMails(config, opts) {
1616
+ const client = createClient(config);
1617
+ const limit = opts.limit ?? 20;
1618
+ try {
1619
+ await client.connect();
1620
+ const lock = await client.getMailboxLock("INBOX");
1621
+ try {
1622
+ const query = {};
1623
+ if (opts.unread) query.seen = false;
1624
+ if (opts.search) query.subject = opts.search;
1625
+ if (Object.keys(query).length === 0) query.all = true;
1626
+ const uids = await client.search(query, { uid: true });
1627
+ if (uids.length === 0) return [];
1628
+ const sorted = uids.sort((a, b) => b - a).slice(0, limit);
1629
+ const uidSet = sorted.join(",");
1630
+ const messages = [];
1631
+ for await (const msg of client.fetch(uidSet, {
1632
+ uid: true,
1633
+ flags: true,
1634
+ envelope: true
1635
+ }, { uid: true })) {
1636
+ messages.push({
1637
+ uid: msg.uid,
1638
+ subject: msg.envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
1639
+ from: msg.envelope.from?.[0] ? `${msg.envelope.from[0].name || ""} <${msg.envelope.from[0].address || ""}>` : "(unknown)",
1640
+ to: (msg.envelope.to ?? []).map(
1641
+ (t) => `${t.name || ""} <${t.address || ""}>`
1642
+ ),
1643
+ date: msg.envelope.date ?? null,
1644
+ isRead: msg.flags.has("\\Seen")
1645
+ });
1646
+ }
1647
+ messages.sort((a, b) => b.uid - a.uid);
1648
+ return messages;
1649
+ } finally {
1650
+ lock.release();
1651
+ }
1652
+ } finally {
1653
+ await client.logout();
1654
+ }
1655
+ }
1656
+ async function getMail(config, uid) {
1657
+ const client = createClient(config);
1658
+ try {
1659
+ await client.connect();
1660
+ const lock = await client.getMailboxLock("INBOX");
1661
+ try {
1662
+ const msg = await client.fetchOne(String(uid), {
1663
+ uid: true,
1664
+ flags: true,
1665
+ envelope: true,
1666
+ source: true
1667
+ }, { uid: true });
1668
+ if (!msg) {
1669
+ throw new DoorayCliError(`\uBA54\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: UID ${uid}`, 1);
1670
+ }
1671
+ const parsed = await (0, import_mailparser.simpleParser)(msg.source);
1672
+ const body = parsed.text ?? parsed.html ?? "(\uBCF8\uBB38 \uC5C6\uC74C)";
1673
+ return {
1674
+ uid: msg.uid,
1675
+ subject: msg.envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
1676
+ from: msg.envelope.from?.[0] ? `${msg.envelope.from[0].name || ""} <${msg.envelope.from[0].address || ""}>` : "(unknown)",
1677
+ to: (msg.envelope.to ?? []).map(
1678
+ (t) => `${t.name || ""} <${t.address || ""}>`
1679
+ ),
1680
+ date: msg.envelope.date ?? null,
1681
+ isRead: msg.flags.has("\\Seen"),
1682
+ body
1683
+ };
1684
+ } finally {
1685
+ lock.release();
1686
+ }
1687
+ } finally {
1688
+ await client.logout();
1689
+ }
1690
+ }
1691
+
1692
+ // src/commands/mail/list.ts
1693
+ var import_chalk4 = __toESM(require("chalk"));
1694
+ var mailListCommand = new import_commander23.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) => {
1695
+ const globalOpts = mailListCommand.optsWithGlobals();
1696
+ const config = await getConfigOrThrow();
1697
+ startSpinner("\uBA54\uC77C \uC870\uD68C \uC911...");
1698
+ const mails = await listMails(config, {
1699
+ unread: opts.unread,
1700
+ search: opts.search,
1701
+ limit: Number(opts.size)
1702
+ });
1703
+ stopSpinner(true, `\uBA54\uC77C ${mails.length}\uAC74 \uC870\uD68C \uC644\uB8CC`);
1704
+ output(globalOpts, {
1705
+ headers: ["UID", "\uC77D\uC74C", "\uB0A0\uC9DC", "\uBCF4\uB0B8\uC0AC\uB78C", "\uC81C\uBAA9"],
1706
+ rows: mails.map((m) => [
1707
+ String(m.uid),
1708
+ m.isRead ? "\u2713" : import_chalk4.default.bold("\u25CF"),
1709
+ m.date ? m.date.toLocaleDateString("ko-KR") : "",
1710
+ m.from.replace(/<.*>/, "").trim() || m.from,
1711
+ m.subject
1712
+ ]),
1713
+ raw: mails.map((m) => ({
1714
+ uid: m.uid,
1715
+ subject: m.subject,
1716
+ from: m.from,
1717
+ to: m.to,
1718
+ date: m.date?.toISOString() ?? null,
1719
+ isRead: m.isRead
1720
+ })),
1721
+ ids: mails.map((m) => String(m.uid))
1722
+ });
1723
+ });
1724
+
1725
+ // src/commands/mail/get.ts
1726
+ var import_commander24 = require("commander");
1727
+ var import_chalk5 = __toESM(require("chalk"));
1728
+ var mailGetCommand = new import_commander24.Command("get").description("\uBA54\uC77C \uC0C1\uC138 \uC870\uD68C").argument("<uid>", "\uBA54\uC77C UID").action(async (uid) => {
1729
+ const globalOpts = mailGetCommand.optsWithGlobals();
1730
+ const config = await getConfigOrThrow();
1731
+ startSpinner("\uBA54\uC77C \uC870\uD68C \uC911...");
1732
+ const mail = await getMail(config, Number(uid));
1733
+ stopSpinner(true, "\uBA54\uC77C \uC870\uD68C \uC644\uB8CC");
1734
+ if (globalOpts.json) {
1735
+ printJson({
1736
+ uid: mail.uid,
1737
+ subject: mail.subject,
1738
+ from: mail.from,
1739
+ to: mail.to,
1740
+ date: mail.date?.toISOString() ?? null,
1741
+ isRead: mail.isRead,
1742
+ body: mail.body
1743
+ });
1744
+ } else {
1745
+ process.stdout.write(
1746
+ `${import_chalk5.default.bold("\uC81C\uBAA9:")} ${mail.subject}
1747
+ ${import_chalk5.default.bold("\uBCF4\uB0B8\uC0AC\uB78C:")} ${mail.from}
1748
+ ${import_chalk5.default.bold("\uBC1B\uB294\uC0AC\uB78C:")} ${mail.to.join(", ")}
1749
+ ${import_chalk5.default.bold("\uB0A0\uC9DC:")} ${mail.date?.toLocaleString("ko-KR") ?? ""}
1750
+ ${import_chalk5.default.bold("\uC77D\uC74C:")} ${mail.isRead ? "\uC608" : "\uC544\uB2C8\uC624"}
1751
+
1752
+ ${mail.body}
1753
+ `
1754
+ );
1755
+ }
1756
+ });
1757
+
1758
+ // src/commands/mail/send.ts
1759
+ var import_commander25 = require("commander");
1760
+ var import_promises9 = require("fs/promises");
1761
+
1762
+ // src/api/smtpClient.ts
1763
+ var import_nodemailer = __toESM(require("nodemailer"));
1764
+ function getSmtpConfigOrThrow(config) {
1765
+ if (!config.imapUsername || !config.imapPassword) {
1766
+ throw new DoorayCliError(
1767
+ "\uBA54\uC77C \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uC124\uC815\uC744 \uC9C4\uD589\uD558\uC138\uC694:\n dooray config set imap-username <YOUR_EMAIL>\n dooray config set imap-password <YOUR_IMAP_PASSWORD>",
1768
+ EXIT_CONFIG_ERROR
1769
+ );
1770
+ }
1771
+ return {
1772
+ host: config.smtpHost ?? DEFAULTS.smtpHost,
1773
+ port: config.smtpPort ?? DEFAULTS.smtpPort,
1774
+ username: config.imapUsername,
1775
+ password: config.imapPassword
1776
+ };
1777
+ }
1778
+ async function sendMail(config, opts) {
1779
+ const smtp = getSmtpConfigOrThrow(config);
1780
+ const transporter = import_nodemailer.default.createTransport({
1781
+ host: smtp.host,
1782
+ port: smtp.port,
1783
+ secure: true,
1784
+ auth: { user: smtp.username, pass: smtp.password }
1785
+ });
1786
+ const mailOptions = {
1787
+ from: smtp.username,
1788
+ to: opts.to.join(", "),
1789
+ subject: opts.subject,
1790
+ inReplyTo: opts.inReplyTo,
1791
+ references: opts.references
1792
+ };
1793
+ if (opts.cc?.length) mailOptions.cc = opts.cc.join(", ");
1794
+ if (opts.bcc?.length) mailOptions.bcc = opts.bcc.join(", ");
1795
+ if (opts.html) {
1796
+ mailOptions.html = opts.body;
1797
+ } else {
1798
+ mailOptions.text = opts.body;
1799
+ }
1800
+ const info = await transporter.sendMail(mailOptions);
1801
+ return {
1802
+ messageId: info.messageId,
1803
+ accepted: info.accepted ?? [],
1804
+ rejected: info.rejected ?? []
1805
+ };
1806
+ }
1807
+
1808
+ // src/commands/mail/send.ts
1809
+ var mailSendCommand = new import_commander25.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) => {
1810
+ const globalOpts = mailSendCommand.optsWithGlobals();
1811
+ const config = await getConfigOrThrow();
1812
+ let body = opts.body ?? "";
1813
+ if (opts.bodyFile) {
1814
+ body = await (0, import_promises9.readFile)(opts.bodyFile, "utf-8");
1815
+ }
1816
+ if (!body) {
1817
+ process.stderr.write("\uC624\uB958: --body \uB610\uB294 --body-file\uC744 \uC9C0\uC815\uD558\uC138\uC694\n");
1818
+ process.exit(3);
1819
+ }
1820
+ startSpinner("\uBA54\uC77C \uBC1C\uC1A1 \uC911...");
1821
+ const result = await sendMail(config, {
1822
+ to: opts.to,
1823
+ cc: opts.cc,
1824
+ bcc: opts.bcc,
1825
+ subject: opts.subject,
1826
+ body,
1827
+ html: opts.html
1828
+ });
1829
+ stopSpinner(true, "\uBA54\uC77C \uBC1C\uC1A1 \uC644\uB8CC");
1830
+ if (globalOpts.json) {
1831
+ printJson(result);
1832
+ } else {
1833
+ process.stdout.write(
1834
+ `\uBA54\uC77C \uBC1C\uC1A1 \uC644\uB8CC
1835
+ Message-ID: ${result.messageId}
1836
+ \uC218\uC2E0: ${result.accepted.join(", ")}
1837
+ ` + (result.rejected.length ? ` \uAC70\uBD80: ${result.rejected.join(", ")}
1838
+ ` : "")
1839
+ );
1840
+ }
1841
+ });
1842
+
1843
+ // src/commands/mail/reply.ts
1844
+ var import_commander26 = require("commander");
1845
+ var import_promises10 = require("fs/promises");
1846
+ var import_imapflow2 = require("imapflow");
1847
+ async function getMessageId(config, uid) {
1848
+ const imap = getImapConfigOrThrow(config);
1849
+ const client = new import_imapflow2.ImapFlow({
1850
+ host: imap.host,
1851
+ port: imap.port,
1852
+ secure: true,
1853
+ auth: { user: imap.username, pass: imap.password },
1854
+ logger: false
1855
+ });
1856
+ try {
1857
+ await client.connect();
1858
+ const lock = await client.getMailboxLock("INBOX");
1859
+ try {
1860
+ const msg = await client.fetchOne(String(uid), {
1861
+ uid: true,
1862
+ envelope: true
1863
+ }, { uid: true });
1864
+ return msg?.envelope.messageId ?? null;
1865
+ } finally {
1866
+ lock.release();
1867
+ }
1868
+ } finally {
1869
+ await client.logout();
1870
+ }
1871
+ }
1872
+ var mailReplyCommand = new import_commander26.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) => {
1873
+ const globalOpts = mailReplyCommand.optsWithGlobals();
1874
+ const config = await getConfigOrThrow();
1875
+ let body = opts.body ?? "";
1876
+ if (opts.bodyFile) {
1877
+ body = await (0, import_promises10.readFile)(opts.bodyFile, "utf-8");
1878
+ }
1879
+ if (!body) {
1880
+ process.stderr.write("\uC624\uB958: --body \uB610\uB294 --body-file\uC744 \uC9C0\uC815\uD558\uC138\uC694\n");
1881
+ process.exit(3);
1882
+ }
1883
+ startSpinner("\uC6D0\uBCF8 \uBA54\uC77C \uC870\uD68C \uC911...");
1884
+ const original = await getMail(config, Number(uid));
1885
+ const messageId = await getMessageId(config, Number(uid));
1886
+ const fromMatch = original.from.match(/<(.+?)>/);
1887
+ const replyTo = fromMatch ? fromMatch[1] : original.from;
1888
+ stopSpinner(true, "\uC6D0\uBCF8 \uBA54\uC77C \uC870\uD68C \uC644\uB8CC");
1889
+ startSpinner("\uB2F5\uC7A5 \uBC1C\uC1A1 \uC911...");
1890
+ const result = await sendMail(config, {
1891
+ to: [replyTo],
1892
+ cc: opts.cc,
1893
+ subject: original.subject.startsWith("Re: ") ? original.subject : `Re: ${original.subject}`,
1894
+ body,
1895
+ html: opts.html,
1896
+ inReplyTo: messageId ?? void 0,
1897
+ references: messageId ?? void 0
1898
+ });
1899
+ stopSpinner(true, "\uB2F5\uC7A5 \uBC1C\uC1A1 \uC644\uB8CC");
1900
+ if (globalOpts.json) {
1901
+ printJson(result);
1902
+ } else {
1903
+ process.stdout.write(
1904
+ `\uB2F5\uC7A5 \uBC1C\uC1A1 \uC644\uB8CC
1905
+ To: ${replyTo}
1906
+ Message-ID: ${result.messageId}
1907
+ `
1908
+ );
1909
+ }
1910
+ });
1911
+
1558
1912
  // src/index.ts
1559
- var program = new import_commander23.Command();
1560
- program.name("dooray").description("Dooray REST API CLI").version("0.1.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");
1913
+ var program = new import_commander27.Command();
1914
+ program.name("dooray").description("Dooray REST API CLI").version("0.2.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");
1561
1915
  program.hook("preAction", () => {
1562
1916
  const opts = program.opts();
1563
1917
  if (opts.color === false || process.env.NO_COLOR) {
1564
- import_chalk4.default.level = 0;
1918
+ import_chalk6.default.level = 0;
1565
1919
  }
1566
1920
  });
1567
- var projectCommand = new import_commander23.Command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB828 \uBA85\uB839");
1921
+ var projectCommand = new import_commander27.Command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB828 \uBA85\uB839");
1568
1922
  projectCommand.addCommand(projectListCommand);
1569
1923
  projectCommand.addCommand(projectMembersCommand);
1570
1924
  projectCommand.addCommand(projectWorkflowsCommand);
1571
- var postCommand = new import_commander23.Command("post").description("\uC5C5\uBB34 \uAD00\uB828 \uBA85\uB839");
1925
+ var postCommand = new import_commander27.Command("post").description("\uC5C5\uBB34 \uAD00\uB828 \uBA85\uB839");
1572
1926
  postCommand.addCommand(postListCommand);
1573
1927
  postCommand.addCommand(postSearchCommand);
1574
1928
  postCommand.addCommand(postGetCommand);
@@ -1576,31 +1930,37 @@ postCommand.addCommand(postEditCommand);
1576
1930
  postCommand.addCommand(postCreateCommand);
1577
1931
  postCommand.addCommand(postDoneCommand);
1578
1932
  postCommand.addCommand(postWorkflowCommand);
1579
- var commentCommand = new import_commander23.Command("comment").description("\uB313\uAE00 \uAD00\uB828 \uBA85\uB839");
1933
+ var commentCommand = new import_commander27.Command("comment").description("\uB313\uAE00 \uAD00\uB828 \uBA85\uB839");
1580
1934
  commentCommand.addCommand(commentListCommand);
1581
1935
  commentCommand.addCommand(commentAddCommand);
1582
1936
  commentCommand.addCommand(commentEditCommand);
1583
1937
  commentCommand.addCommand(commentDeleteCommand);
1584
1938
  postCommand.addCommand(commentCommand);
1585
- var wikiCommand = new import_commander23.Command("wiki").description("\uC704\uD0A4 \uAD00\uB828 \uBA85\uB839");
1939
+ var wikiCommand = new import_commander27.Command("wiki").description("\uC704\uD0A4 \uAD00\uB828 \uBA85\uB839");
1586
1940
  wikiCommand.addCommand(wikiListCommand);
1587
1941
  wikiCommand.addCommand(wikiPagesCommand);
1588
- var wikiPageCommand = new import_commander23.Command("page").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uAD00\uB828 \uBA85\uB839");
1942
+ var wikiPageCommand = new import_commander27.Command("page").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uAD00\uB828 \uBA85\uB839");
1589
1943
  wikiPageCommand.addCommand(wikiPageGetCommand);
1590
1944
  wikiPageCommand.addCommand(wikiPageCreateCommand);
1591
1945
  wikiPageCommand.addCommand(wikiPageEditCommand);
1592
1946
  wikiCommand.addCommand(wikiPageCommand);
1947
+ var mailCommand = new import_commander27.Command("mail").description("\uBA54\uC77C \uAD00\uB828 \uBA85\uB839");
1948
+ mailCommand.addCommand(mailListCommand);
1949
+ mailCommand.addCommand(mailGetCommand);
1950
+ mailCommand.addCommand(mailSendCommand);
1951
+ mailCommand.addCommand(mailReplyCommand);
1593
1952
  program.addCommand(configCommand);
1594
1953
  program.addCommand(cacheCommand);
1595
1954
  program.addCommand(doctorCommand);
1596
1955
  program.addCommand(projectCommand);
1597
1956
  program.addCommand(postCommand);
1598
1957
  program.addCommand(wikiCommand);
1958
+ program.addCommand(mailCommand);
1599
1959
  program.parseAsync().catch((err) => {
1600
1960
  if (err instanceof DoorayCliError) {
1601
- process.stderr.write(import_chalk4.default.red(`\uC624\uB958: ${err.message}`) + "\n");
1961
+ process.stderr.write(import_chalk6.default.red(`\uC624\uB958: ${err.message}`) + "\n");
1602
1962
  process.exit(err.exitCode);
1603
1963
  }
1604
- process.stderr.write(import_chalk4.default.red(`\uC624\uB958: ${err.message}`) + "\n");
1964
+ process.stderr.write(import_chalk6.default.red(`\uC624\uB958: ${err.message}`) + "\n");
1605
1965
  process.exit(1);
1606
1966
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifos/dooray-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "CLI tool for Dooray project management — AI agent & terminal friendly",
5
5
  "keywords": [
6
6
  "dooray",
@@ -21,7 +21,8 @@
21
21
  "dooray": "./dist/index.js"
22
22
  },
23
23
  "files": [
24
- "dist"
24
+ "dist",
25
+ "README.md"
25
26
  ],
26
27
  "engines": {
27
28
  "node": ">=18"
@@ -35,14 +36,19 @@
35
36
  "chalk": "^5.6.2",
36
37
  "cli-table3": "^0.6.5",
37
38
  "commander": "^14.0.3",
39
+ "imapflow": "^1.2.18",
38
40
  "js-yaml": "^4.1.1",
39
41
  "ky": "^1.14.3",
42
+ "mailparser": "^3.9.6",
43
+ "nodemailer": "^8.0.4",
40
44
  "ora": "^9.3.0",
41
45
  "tmp": "^0.2.5"
42
46
  },
43
47
  "devDependencies": {
44
48
  "@types/js-yaml": "^4.0.9",
49
+ "@types/mailparser": "^3.4.6",
45
50
  "@types/node": "^25.5.0",
51
+ "@types/nodemailer": "^7.0.11",
46
52
  "@types/tmp": "^0.2.6",
47
53
  "tsup": "^8.5.1",
48
54
  "typescript": "^6.0.2"