@bifos/dooray-cli 0.15.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,12 +24,11 @@ dooray setup
24
24
  ```
25
25
 
26
26
  아래 순서로 진행합니다:
27
-
28
27
  1. API Endpoint 선택
29
28
  2. API Key 입력
30
29
  3. 연결 테스트
31
30
  4. 메일 설정 (선택)
32
- API 토큰은 `https://{tenant}.dooray.com/setting/api/token`에서 발급할 수 있습니다.
31
+ API 토큰은 `https://{tenant}.dooray.com/setting/api/token`에서 발급할 수 있습니다.
33
32
 
34
33
  수동 설정도 가능합니다:
35
34
 
@@ -100,12 +99,12 @@ dooray post get <project> 42 --json # JSON 출력
100
99
 
101
100
  #### 업무 식별 방식 (post 하위 16개 명령 공통)
102
101
 
103
- | 방식 | 예시 |
104
- | --------------------- | ------------------------------------------------------------- |
105
- | `<project> <number>` | `dooray post get <project> 42` |
106
- | Dooray URL positional | `dooray post get https://x.dooray.com/task/to/<postId>` |
107
- | `--id <postId>` | `dooray post get --id <postId>` |
108
- | `--url <url>` | `dooray post get --url https://x.dooray.com/task/to/<postId>` |
102
+ | 방식 | 예시 |
103
+ |---|---|
104
+ | `<project> <number>` | `dooray post get <project> 42` |
105
+ | Dooray URL positional | `dooray post get https://x.dooray.com/task/to/<postId>` |
106
+ | `--id <postId>` | `dooray post get --id <postId>` |
107
+ | `--url <url>` | `dooray post get --url https://x.dooray.com/task/to/<postId>` |
109
108
 
110
109
  지원 URL 형식 3종 (positional 첫 인자 / `--url` 공통):
111
110
 
@@ -335,7 +334,6 @@ dooray post create <project> --title "..." --cc 1234567890123456789
335
334
  ```
336
335
 
337
336
  분기 규칙 (`resolveMember` 자동 판단):
338
-
339
337
  - `^\d{15,}$` — memberId 직접 사용
340
338
  - `^[^\s@]+@[^\s@]+\.[^\s@]+$` — 이메일, `searchMembers` exact 조회
341
339
  - 그 외 — 이름 부분일치
@@ -474,7 +472,6 @@ SNIPPET=$(dooray wiki page file upload <project> <page-id> --file ./diagram.png
474
472
  ```
475
473
 
476
474
  **주의**:
477
-
478
475
  - `upload` 시 multipart 필드 순서 (`type` → `file`) 가 중요.
479
476
  클라이언트가 자동으로 강제
480
477
  - `inline_image` 로 올린 파일은 본문에 markdown 으로 박혀야 위키에서 보임.
@@ -513,7 +510,6 @@ dooray wiki page comment list "https://<tenant>.dooray.com/wiki/<wikiId>/<pageId
513
510
  ```
514
511
 
515
512
  **post comment 와의 차이**:
516
-
517
513
  - mention / cc / 받는 사람 미지원 — wiki API 부재
518
514
  - 첨부 파일 미지원 — wiki comment 전용 endpoint 부재 (페이지 본문 파일은 `wiki page file` 사용)
519
515
  - 본문은 markdown 그대로 전송 (mimeType 자동)
@@ -542,6 +538,10 @@ dooray mail send --to "recipient@example.com" --subject "HTML 메일" --body "<h
542
538
 
543
539
  # 메일 답장 (스레드 유지)
544
540
  dooray mail reply <uid> --body "답장 내용"
541
+
542
+ # 저장된 메일 사용자명·앱 비밀번호 제거
543
+ dooray mail logout
544
+ dooray mail logout --yes # 비대화형 환경
545
545
  ```
546
546
 
547
547
  ### 메신저
@@ -608,7 +608,6 @@ dooray post file upload <project> <number> ./report.pdf --json
608
608
  ```
609
609
 
610
610
  URL/`--id`/`--url` 모드에서는 sub-id를 옵션으로 전달:
611
-
612
611
  ```bash
613
612
  dooray post file download --url <url> --file-id <fileId> -o ./downloads
614
613
  dooray post file delete --url <url> --file-id <fileId>
@@ -642,11 +641,11 @@ dooray post comment file delete <project> <post-num> <comment-id> <file-id> --ye
642
641
 
643
642
  ## 출력 모드
644
643
 
645
- | 플래그 | 설명 | 용도 |
646
- | --------- | ----------- | ---------------- |
647
- | (없음) | 테이블 출력 | 사람이 읽기 좋음 |
648
- | `--json` | JSON 출력 | 파싱, 파이프라인 |
649
- | `--quiet` | ID만 출력 | 스크립팅 |
644
+ | 플래그 | 설명 | 용도 |
645
+ |--------|------|------|
646
+ | (없음) | 테이블 출력 | 사람이 읽기 좋음 |
647
+ | `--json` | JSON 출력 | 파싱, 파이프라인 |
648
+ | `--quiet` | ID만 출력 | 스크립팅 |
650
649
 
651
650
  ```bash
652
651
  # 파이프라인 예시
@@ -721,13 +720,11 @@ dooray --help
721
720
  이 레포는 두 개의 워크플로를 사용합니다:
722
721
 
723
722
  ### CI (`.github/workflows/ci.yml`)
724
-
725
723
  - 트리거: `main` 으로 push, `main` 대상 PR
726
724
  - 동작: `pnpm install --frozen-lockfile`, `pnpm test`, `pnpm build` (Node 18, ubuntu-latest)
727
725
  - 별도 secret 불필요
728
726
 
729
727
  ### Claude code review (`.github/workflows/claude-code-review.yml`)
730
-
731
728
  - 트리거: PR opened, PR 댓글에 `/review` 포함
732
729
  - 동작: 4 병렬 specialist 에이전트 (TypeScript / Conventions / Security / Architecture) 가 인라인 리뷰 + 요약 댓글 1개 게시
733
730
  - 필요 secret: `CLAUDE_CODE_OAUTH_TOKEN`
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_commander67 = require("commander");
28
- var import_chalk8 = __toESM(require("chalk"));
27
+ var import_commander68 = require("commander");
28
+ var import_chalk9 = __toESM(require("chalk"));
29
29
 
30
30
  // src/commands/config.ts
31
31
  var import_commander = require("commander");
@@ -143,6 +143,19 @@ async function saveConfig(config) {
143
143
  await ensureDir();
144
144
  await (0, import_promises.writeFile)(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
145
145
  }
146
+ function removeMailCredentials(config) {
147
+ const next = { ...config };
148
+ delete next.imapUsername;
149
+ delete next.imapPassword;
150
+ return next;
151
+ }
152
+ async function clearMailCredentials() {
153
+ const config = await getConfig();
154
+ if (!config) return false;
155
+ const hadCredentials = !!(config.imapUsername || config.imapPassword);
156
+ await saveConfig(removeMailCredentials(config));
157
+ return hadCredentials;
158
+ }
146
159
 
147
160
  // src/commands/config.ts
148
161
  function maskApiKey(key) {
@@ -372,7 +385,7 @@ var import_node_path3 = __toESM(require("path"));
372
385
  var import_node_os3 = require("os");
373
386
 
374
387
  // src/version.ts
375
- var CLI_VERSION = true ? "0.15.0" : "0.0.0-dev";
388
+ var CLI_VERSION = true ? "0.15.1" : "0.0.0-dev";
376
389
 
377
390
  // src/skill/context.ts
378
391
  function resolveSkillDataRoot(homeDir, xdgDataHome = process.env.XDG_DATA_HOME) {
@@ -5905,6 +5918,51 @@ var import_commander59 = require("commander");
5905
5918
  // src/api/imapClient.ts
5906
5919
  var import_imapflow = require("imapflow");
5907
5920
  var import_mailparser = require("mailparser");
5921
+
5922
+ // src/api/mailErrors.ts
5923
+ function getErrorField(error, key) {
5924
+ if (typeof error !== "object" || error === null) return void 0;
5925
+ return error[key];
5926
+ }
5927
+ function isAuthenticationFailure(error) {
5928
+ if (getErrorField(error, "authenticationFailed") === true) return true;
5929
+ if (getErrorField(error, "code") === "EAUTH") return true;
5930
+ const details = [
5931
+ getErrorField(error, "message"),
5932
+ getErrorField(error, "responseText"),
5933
+ getErrorField(error, "responseCode")
5934
+ ].filter((value) => typeof value === "string").join(" ");
5935
+ return /auth(?:enticate|entication)?\s*(?:login\s*)?failed|invalid login|invalid credentials/i.test(
5936
+ details
5937
+ );
5938
+ }
5939
+ function isConnectionFailure(error) {
5940
+ const code = getErrorField(error, "code");
5941
+ return typeof code === "string" && [
5942
+ "ECONNECTION",
5943
+ "ECONNREFUSED",
5944
+ "ECONNRESET",
5945
+ "EHOSTUNREACH",
5946
+ "ENETUNREACH",
5947
+ "ESOCKET",
5948
+ "ETIMEDOUT"
5949
+ ].includes(code);
5950
+ }
5951
+ function toMailConnectionError(protocol, host, port, error) {
5952
+ if (isAuthenticationFailure(error)) {
5953
+ return new DoorayCliError(
5954
+ `${protocol} \uC778\uC99D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. Dooray \uBA54\uC77C \uC124\uC815\uC5D0\uC11C \uC571 \uBE44\uBC00\uBC88\uD638\uB97C \uC0C8\uB85C \uBC1C\uAE09\uD55C \uB4A4 \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694:
5955
+ dooray config set imap-password <NEW_IMAP_APP_PASSWORD>
5956
+ \uAE30\uC874 \uBA54\uC77C \uC778\uC99D\uC815\uBCF4 \uC81C\uAC70: dooray mail logout`,
5957
+ EXIT_AUTH_ERROR
5958
+ );
5959
+ }
5960
+ const reason = error instanceof Error && error.message ? `: ${error.message}` : "";
5961
+ const message = isConnectionFailure(error) ? `${protocol} \uC11C\uBC84 \uC5F0\uACB0\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4 (${host}:${port})${reason}` : `${protocol} \uCC98\uB9AC \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4${reason}`;
5962
+ return new DoorayCliError(message, EXIT_API_ERROR);
5963
+ }
5964
+
5965
+ // src/api/imapClient.ts
5908
5966
  function getImapConfigOrThrow(config) {
5909
5967
  if (!config.imapUsername || !config.imapPassword) {
5910
5968
  throw new DoorayCliError(
@@ -5919,7 +5977,7 @@ function getImapConfigOrThrow(config) {
5919
5977
  password: config.imapPassword
5920
5978
  };
5921
5979
  }
5922
- function createClient(config) {
5980
+ function createImapClient(config) {
5923
5981
  const imap = getImapConfigOrThrow(config);
5924
5982
  return new import_imapflow.ImapFlow({
5925
5983
  host: imap.host,
@@ -5929,11 +5987,30 @@ function createClient(config) {
5929
5987
  logger: false
5930
5988
  });
5931
5989
  }
5990
+ async function connectImapClient(client, config) {
5991
+ const imap = getImapConfigOrThrow(config);
5992
+ try {
5993
+ await client.connect();
5994
+ } catch (error) {
5995
+ throw toMailConnectionError("IMAP", imap.host, imap.port, error);
5996
+ }
5997
+ }
5998
+ async function closeImapClient(client) {
5999
+ if (!client.usable) {
6000
+ client.close();
6001
+ return;
6002
+ }
6003
+ try {
6004
+ await client.logout();
6005
+ } catch {
6006
+ client.close();
6007
+ }
6008
+ }
5932
6009
  async function listMails(config, opts) {
5933
- const client = createClient(config);
6010
+ const client = createImapClient(config);
5934
6011
  const limit = opts.limit ?? 20;
5935
6012
  try {
5936
- await client.connect();
6013
+ await connectImapClient(client, config);
5937
6014
  const lock = await client.getMailboxLock("INBOX");
5938
6015
  try {
5939
6016
  const query = {};
@@ -5969,13 +6046,13 @@ async function listMails(config, opts) {
5969
6046
  lock.release();
5970
6047
  }
5971
6048
  } finally {
5972
- await client.logout();
6049
+ await closeImapClient(client);
5973
6050
  }
5974
6051
  }
5975
6052
  async function getMail(config, uid) {
5976
- const client = createClient(config);
6053
+ const client = createImapClient(config);
5977
6054
  try {
5978
- await client.connect();
6055
+ await connectImapClient(client, config);
5979
6056
  const lock = await client.getMailboxLock("INBOX");
5980
6057
  try {
5981
6058
  const msg = await client.fetchOne(String(uid), {
@@ -6011,7 +6088,7 @@ async function getMail(config, uid) {
6011
6088
  lock.release();
6012
6089
  }
6013
6090
  } finally {
6014
- await client.logout();
6091
+ await closeImapClient(client);
6015
6092
  }
6016
6093
  }
6017
6094
 
@@ -6123,7 +6200,9 @@ async function sendMail(config, opts) {
6123
6200
  } else {
6124
6201
  mailOptions.text = opts.body;
6125
6202
  }
6126
- const info = await transporter.sendMail(mailOptions);
6203
+ const info = await transporter.sendMail(mailOptions).catch((error) => {
6204
+ throw toMailConnectionError("SMTP", smtp.host, smtp.port, error);
6205
+ });
6127
6206
  return {
6128
6207
  messageId: info.messageId,
6129
6208
  accepted: info.accepted ?? [],
@@ -6169,18 +6248,10 @@ var mailSendCommand = new import_commander61.Command("send").description("\uBA54
6169
6248
  // src/commands/mail/reply.ts
6170
6249
  var import_commander62 = require("commander");
6171
6250
  var import_promises14 = require("fs/promises");
6172
- var import_imapflow2 = require("imapflow");
6173
6251
  async function getMessageId(config, uid) {
6174
- const imap = getImapConfigOrThrow(config);
6175
- const client = new import_imapflow2.ImapFlow({
6176
- host: imap.host,
6177
- port: imap.port,
6178
- secure: true,
6179
- auth: { user: imap.username, pass: imap.password },
6180
- logger: false
6181
- });
6252
+ const client = createImapClient(config);
6182
6253
  try {
6183
- await client.connect();
6254
+ await connectImapClient(client, config);
6184
6255
  const lock = await client.getMailboxLock("INBOX");
6185
6256
  try {
6186
6257
  const msg = await client.fetchOne(String(uid), {
@@ -6193,7 +6264,7 @@ async function getMessageId(config, uid) {
6193
6264
  lock.release();
6194
6265
  }
6195
6266
  } finally {
6196
- await client.logout();
6267
+ await closeImapClient(client);
6197
6268
  }
6198
6269
  }
6199
6270
  var mailReplyCommand = new import_commander62.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) => {
@@ -6236,12 +6307,47 @@ var mailReplyCommand = new import_commander62.Command("reply").description("\uBA
6236
6307
  }
6237
6308
  });
6238
6309
 
6310
+ // src/commands/mail/logout.ts
6311
+ var import_commander63 = require("commander");
6312
+ var import_chalk8 = __toESM(require("chalk"));
6313
+ async function authorizeMailLogout(skipConfirmation, isTTY, confirmRemoval) {
6314
+ if (skipConfirmation) return true;
6315
+ if (!isTTY) {
6316
+ throw new DoorayCliError(
6317
+ "non-TTY \uD658\uACBD\uC5D0\uC11C\uB294 \uD655\uC778 \uC5C6\uC774 \uBA54\uC77C \uC778\uC99D\uC815\uBCF4\uB97C \uC81C\uAC70\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --yes(-y) \uD50C\uB798\uADF8\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
6318
+ EXIT_PARAM_ERROR
6319
+ );
6320
+ }
6321
+ return confirmRemoval();
6322
+ }
6323
+ var mailLogoutCommand = new import_commander63.Command("logout").description("\uC800\uC7A5\uB41C \uBA54\uC77C \uC778\uC99D\uC815\uBCF4 \uC81C\uAC70").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uC778\uC99D\uC815\uBCF4 \uC81C\uAC70 (\uC790\uB3D9\uD654\uC6A9)").action(async (opts) => {
6324
+ const confirmed = await authorizeMailLogout(
6325
+ !!opts.yes,
6326
+ !!process.stdin.isTTY,
6327
+ async () => {
6328
+ const { confirm: confirm2 } = await import("@inquirer/prompts");
6329
+ return confirm2({
6330
+ message: "\uC800\uC7A5\uB41C IMAP \uC0AC\uC6A9\uC790\uBA85\uACFC \uC571 \uBE44\uBC00\uBC88\uD638\uB97C \uC81C\uAC70\uD560\uAE4C\uC694?",
6331
+ default: false
6332
+ });
6333
+ }
6334
+ );
6335
+ if (!confirmed) {
6336
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
6337
+ return;
6338
+ }
6339
+ const removed = await clearMailCredentials();
6340
+ process.stderr.write(
6341
+ removed ? import_chalk8.default.green("\u2713 \uBA54\uC77C \uC778\uC99D\uC815\uBCF4\uB97C \uC81C\uAC70\uD588\uC2B5\uB2C8\uB2E4.\n") : "\uC800\uC7A5\uB41C \uBA54\uC77C \uC778\uC99D\uC815\uBCF4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.\n"
6342
+ );
6343
+ });
6344
+
6239
6345
  // src/commands/messenger/index.ts
6240
- var import_commander65 = require("commander");
6346
+ var import_commander66 = require("commander");
6241
6347
 
6242
6348
  // src/commands/messenger/send.ts
6243
- var import_commander63 = require("commander");
6244
- var messengerSendCommand = new import_commander63.Command("send").description("\uBA54\uC2E0\uC800 1:1 \uB2E4\uC774\uB809\uD2B8 \uBA54\uC2DC\uC9C0 \uC804\uC1A1 (--body \uC5C6\uC73C\uBA74 $EDITOR)").option("--to <id|email>", "\uBC1B\uB294 \uC0AC\uB78C (organizationMemberId \uB610\uB294 \uC774\uBA54\uC77C \u2014 \uC774\uB984 \uBBF8\uC9C0\uC6D0)").option("--body <text>", "\uBA54\uC2DC\uC9C0 \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)").action(async (opts) => {
6349
+ var import_commander64 = require("commander");
6350
+ var messengerSendCommand = new import_commander64.Command("send").description("\uBA54\uC2E0\uC800 1:1 \uB2E4\uC774\uB809\uD2B8 \uBA54\uC2DC\uC9C0 \uC804\uC1A1 (--body \uC5C6\uC73C\uBA74 $EDITOR)").option("--to <id|email>", "\uBC1B\uB294 \uC0AC\uB78C (organizationMemberId \uB610\uB294 \uC774\uBA54\uC77C \u2014 \uC774\uB984 \uBBF8\uC9C0\uC6D0)").option("--body <text>", "\uBA54\uC2DC\uC9C0 \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)").action(async (opts) => {
6245
6351
  if (!opts.to) {
6246
6352
  throw new DoorayCliError("--to \uC635\uC158\uC740 \uD544\uC218\uC785\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
6247
6353
  }
@@ -6281,7 +6387,7 @@ var messengerSendCommand = new import_commander63.Command("send").description("\
6281
6387
  });
6282
6388
 
6283
6389
  // src/commands/messenger/channel-send.ts
6284
- var import_commander64 = require("commander");
6390
+ var import_commander65 = require("commander");
6285
6391
 
6286
6392
  // src/resolvers/messenger-channel.ts
6287
6393
  var CHANNEL_ID_RE = /^\d{15,}$/;
@@ -6299,7 +6405,7 @@ async function resolveMessengerChannel(client, input2) {
6299
6405
  }
6300
6406
 
6301
6407
  // src/commands/messenger/channel-send.ts
6302
- var messengerChannelSendCommand = new import_commander64.Command("channel-send").description("\uBA54\uC2E0\uC800 \uB300\uD654\uBC29 \uBA54\uC2DC\uC9C0 \uC804\uC1A1 (--body \uC5C6\uC73C\uBA74 $EDITOR)").option("--channel <channelId|\uC774\uB984>", "\uB300\uD654\uBC29 channelId \uB610\uB294 \uC774\uB984 (\uBD80\uBD84\uC77C\uCE58)").option("--body <text>", "\uBA54\uC2DC\uC9C0 \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)").action(async (opts) => {
6408
+ var messengerChannelSendCommand = new import_commander65.Command("channel-send").description("\uBA54\uC2E0\uC800 \uB300\uD654\uBC29 \uBA54\uC2DC\uC9C0 \uC804\uC1A1 (--body \uC5C6\uC73C\uBA74 $EDITOR)").option("--channel <channelId|\uC774\uB984>", "\uB300\uD654\uBC29 channelId \uB610\uB294 \uC774\uB984 (\uBD80\uBD84\uC77C\uCE58)").option("--body <text>", "\uBA54\uC2DC\uC9C0 \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)").action(async (opts) => {
6303
6409
  if (!opts.channel) {
6304
6410
  throw new DoorayCliError("--channel \uC635\uC158\uC740 \uD544\uC218\uC785\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
6305
6411
  }
@@ -6333,12 +6439,12 @@ var messengerChannelSendCommand = new import_commander64.Command("channel-send")
6333
6439
  });
6334
6440
 
6335
6441
  // src/commands/messenger/index.ts
6336
- var messengerCommand = new import_commander65.Command("messenger").description("\uBA54\uC2E0\uC800 \uAD00\uB828 \uBA85\uB839");
6442
+ var messengerCommand = new import_commander66.Command("messenger").description("\uBA54\uC2E0\uC800 \uAD00\uB828 \uBA85\uB839");
6337
6443
  messengerCommand.addCommand(messengerSendCommand);
6338
6444
  messengerCommand.addCommand(messengerChannelSendCommand);
6339
6445
 
6340
6446
  // src/commands/feedback.ts
6341
- var import_commander66 = require("commander");
6447
+ var import_commander67 = require("commander");
6342
6448
  var import_node_child_process2 = require("child_process");
6343
6449
  var import_node_util = require("util");
6344
6450
  var import_promises17 = require("fs/promises");
@@ -6449,7 +6555,7 @@ async function readBody(opts) {
6449
6555
  if (opts.bodyFile) return await (0, import_promises17.readFile)(opts.bodyFile, "utf-8");
6450
6556
  return void 0;
6451
6557
  }
6452
- var feedbackCommand = new import_commander66.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(
6558
+ var feedbackCommand = new import_commander67.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(
6453
6559
  "--label <name>",
6454
6560
  "\uB77C\uBCA8 (\uBC18\uBCF5 \uAC00\uB2A5)",
6455
6561
  (value, prev) => [...prev, value],
@@ -6612,25 +6718,25 @@ function sanitizeArgv(argv) {
6612
6718
  }
6613
6719
 
6614
6720
  // src/index.ts
6615
- var program = new import_commander67.Command();
6721
+ var program = new import_commander68.Command();
6616
6722
  program.name("dooray").description("Dooray REST API CLI").version(CLI_VERSION).option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--quiet", "ID\uB9CC \uCD9C\uB825").option("--no-color", "\uC0C9\uC0C1 \uBE44\uD65C\uC131\uD654");
6617
6723
  program.hook("preAction", () => {
6618
6724
  const opts = program.opts();
6619
6725
  if (opts.color === false || process.env.NO_COLOR) {
6620
- import_chalk8.default.level = 0;
6726
+ import_chalk9.default.level = 0;
6621
6727
  }
6622
6728
  if (opts.json || opts.quiet) {
6623
6729
  setQuiet(true);
6624
6730
  }
6625
6731
  });
6626
- var projectCommand = new import_commander67.Command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB828 \uBA85\uB839");
6732
+ var projectCommand = new import_commander68.Command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB828 \uBA85\uB839");
6627
6733
  projectCommand.addCommand(projectListCommand);
6628
6734
  projectCommand.addCommand(projectMembersCommand);
6629
6735
  projectCommand.addCommand(projectWorkflowsCommand);
6630
6736
  projectCommand.addCommand(projectGroupsCommand);
6631
6737
  projectCommand.addCommand(projectTagsCommand);
6632
6738
  projectCommand.addCommand(projectTemplatesCommand);
6633
- var postCommand = new import_commander67.Command("post").description("\uC5C5\uBB34 \uAD00\uB828 \uBA85\uB839");
6739
+ var postCommand = new import_commander68.Command("post").description("\uC5C5\uBB34 \uAD00\uB828 \uBA85\uB839");
6634
6740
  postCommand.addCommand(postListCommand);
6635
6741
  postCommand.addCommand(postSearchCommand);
6636
6742
  postCommand.addCommand(postGetCommand);
@@ -6638,7 +6744,7 @@ postCommand.addCommand(postEditCommand);
6638
6744
  postCommand.addCommand(postCreateCommand);
6639
6745
  postCommand.addCommand(postDoneCommand);
6640
6746
  postCommand.addCommand(postWorkflowCommand);
6641
- var commentCommand = new import_commander67.Command("comment").description("\uB313\uAE00 \uAD00\uB828 \uBA85\uB839");
6747
+ var commentCommand = new import_commander68.Command("comment").description("\uB313\uAE00 \uAD00\uB828 \uBA85\uB839");
6642
6748
  commentCommand.addCommand(commentListCommand);
6643
6749
  commentCommand.addCommand(commentLatestCommand);
6644
6750
  commentCommand.addCommand(commentGetCommand);
@@ -6647,18 +6753,18 @@ commentCommand.addCommand(commentEditCommand);
6647
6753
  commentCommand.addCommand(commentDeleteCommand);
6648
6754
  commentCommand.addCommand(commentFileCommand);
6649
6755
  postCommand.addCommand(commentCommand);
6650
- var fileCommand = new import_commander67.Command("file").description("\uCCA8\uBD80\uD30C\uC77C \uAD00\uB828 \uBA85\uB839");
6756
+ var fileCommand = new import_commander68.Command("file").description("\uCCA8\uBD80\uD30C\uC77C \uAD00\uB828 \uBA85\uB839");
6651
6757
  fileCommand.addCommand(fileListCommand);
6652
6758
  fileCommand.addCommand(fileDownloadCommand);
6653
6759
  fileCommand.addCommand(fileDownloadAllCommand);
6654
6760
  fileCommand.addCommand(fileUploadCommand);
6655
6761
  fileCommand.addCommand(fileDeleteCommand);
6656
6762
  postCommand.addCommand(fileCommand);
6657
- var wikiCommand = new import_commander67.Command("wiki").description("\uC704\uD0A4 \uAD00\uB828 \uBA85\uB839");
6763
+ var wikiCommand = new import_commander68.Command("wiki").description("\uC704\uD0A4 \uAD00\uB828 \uBA85\uB839");
6658
6764
  wikiCommand.addCommand(wikiListCommand);
6659
6765
  wikiCommand.addCommand(wikiPagesCommand);
6660
6766
  wikiCommand.addCommand(wikiTreeCommand);
6661
- var wikiPageCommand = new import_commander67.Command("page").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uAD00\uB828 \uBA85\uB839");
6767
+ var wikiPageCommand = new import_commander68.Command("page").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uAD00\uB828 \uBA85\uB839");
6662
6768
  wikiPageCommand.addCommand(wikiPageGetCommand);
6663
6769
  wikiPageCommand.addCommand(wikiPageCreateCommand);
6664
6770
  wikiPageCommand.addCommand(wikiPageEditCommand);
@@ -6666,11 +6772,12 @@ wikiPageCommand.addCommand(wikiPageDeleteCommand);
6666
6772
  wikiPageCommand.addCommand(wikiPageFileCommand);
6667
6773
  wikiPageCommand.addCommand(wikiPageCommentCommand);
6668
6774
  wikiCommand.addCommand(wikiPageCommand);
6669
- var mailCommand = new import_commander67.Command("mail").description("\uBA54\uC77C \uAD00\uB828 \uBA85\uB839");
6775
+ var mailCommand = new import_commander68.Command("mail").description("\uBA54\uC77C \uAD00\uB828 \uBA85\uB839");
6670
6776
  mailCommand.addCommand(mailListCommand);
6671
6777
  mailCommand.addCommand(mailGetCommand);
6672
6778
  mailCommand.addCommand(mailSendCommand);
6673
6779
  mailCommand.addCommand(mailReplyCommand);
6780
+ mailCommand.addCommand(mailLogoutCommand);
6674
6781
  program.addCommand(setupCommand);
6675
6782
  program.addCommand(skillCommand);
6676
6783
  program.addCommand(configCommand);
@@ -6701,6 +6808,6 @@ program.parseAsync().catch(async (err) => {
6701
6808
  }
6702
6809
  } catch {
6703
6810
  }
6704
- process.stderr.write(import_chalk8.default.red(`\uC624\uB958: ${errorMessage}`) + "\n");
6811
+ process.stderr.write(import_chalk9.default.red(`\uC624\uB958: ${errorMessage}`) + "\n");
6705
6812
  process.exit(exitCode);
6706
6813
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifos/dooray-cli",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "CLI tool for Dooray project management — AI agent & terminal friendly",
5
5
  "keywords": [
6
6
  "dooray",
@@ -67,6 +67,7 @@
67
67
  | 메일 상세 | `dooray mail get <uid>` |
68
68
  | 메일 발송 | `dooray mail send --to "..." --subject "..." --body "..."` |
69
69
  | 메일 답장 | `dooray mail reply <uid> --body "..."` |
70
+ | 저장된 메일 인증정보 제거 | `dooray mail logout` (비대화형 환경: `--yes`) |
70
71
  | 메신저 1:1 다이렉트 메시지 | `dooray messenger send --to "<id\|email>" --body "..."` (`--to`는 id/이메일만, 이름 미지원) |
71
72
  | 메신저 대화방 메시지 | `dooray messenger channel-send --channel "<channelId\|이름>" --body "..."` (이름은 자신이 속한 방만 검색) |
72
73
  | 첨부파일 목록 | `dooray post file list <project> <number>` |
@@ -89,4 +90,3 @@
89
90
  | `dooray post edit --id <postId> --tag-remove <name>` | 특정 태그 제거 |
90
91
 
91
92
  > **제목 옵션 네이밍**: `post` 와 `wiki page` 모두 `--title` 표준. `post`의 `--subject`는 deprecated alias로 당분간 동작하되, 새 코드에서는 `--title` 사용을 권장.
92
-