pinglet-cli 0.2.0 → 0.2.3

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
@@ -114,3 +114,14 @@ install 시 `~/.claude/commands/pinglet.md` slash command가 함께 설치되며
114
114
  state.json # 현재 표시 중 메시지, seen 카운트, flush/refresh 타이밍
115
115
  events.jsonl # Local Event Queue (append-only)
116
116
  ```
117
+
118
+ ## 약관 및 개인정보처리방침
119
+
120
+ Pinglet은 GitHub 계정 정보(로그인 시), 설치 환경 정보, 접속 IP를 서비스 제공·어뷰징
121
+ 방지 목적으로 처리합니다. 로그인하여 메시지를 작성하면 아래 문서에 동의한 것으로
122
+ 간주됩니다.
123
+
124
+ - [이용약관](https://pinglet.halluci.co.kr/terms)
125
+ - [개인정보처리방침](https://pinglet.halluci.co.kr/privacy)
126
+
127
+ 문의: leeyeonggyun@naver.com
@@ -286,9 +286,12 @@ function installClaudeIntegration(config, options = {}) {
286
286
  const existing = settings.statusLine;
287
287
  const isOurs = existing?.command?.includes("pinglet");
288
288
  if (existing && !isOurs && !options.force) {
289
+ // needsForce: 호출부(install 커맨드)가 대화형이면 사용자에게 물어보고
290
+ // force로 재시도할 수 있다.
289
291
  return {
290
292
  ok: false,
291
- reason: "기존 statusLine 설정이 있습니다. 덮어쓰려면 --force로 다시 실행하세요.",
293
+ needsForce: true,
294
+ reason: "기존 statusLine 설정이 있습니다 (백업 후 교체하려면 --force).",
292
295
  };
293
296
  }
294
297
  config.adapters.claude = {
@@ -90,7 +90,8 @@ function installCodexIntegration(config, options = {}) {
90
90
  if (existing && !isOurs && !options.force) {
91
91
  return {
92
92
  ok: false,
93
- reason: "기존 notify 설정이 있습니다. 덮어쓰려면 --force로 다시 실행하세요.",
93
+ needsForce: true,
94
+ reason: "기존 notify 설정이 있습니다 (백업 후 교체하려면 --force).",
94
95
  };
95
96
  }
96
97
  config.adapters.codex = {
@@ -1,11 +1,64 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.runInstall = runInstall;
37
+ const readline = __importStar(require("readline"));
4
38
  const config_1 = require("../config");
5
39
  const api_1 = require("../api");
6
40
  const cache_1 = require("../cache");
7
41
  const claude_1 = require("../adapters/claude");
8
42
  const codex_1 = require("../adapters/codex");
43
+ /**
44
+ * 기존 설정 교체 확인. Enter 또는 y면 진행(기본 Yes — 설치 의도가 명확하므로).
45
+ * non-TTY(postinstall, CI)에서는 묻지 않고 false — 사용자 설정을 말없이
46
+ * 바꾸지 않는다.
47
+ */
48
+ function confirmReplace(question) {
49
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
50
+ return Promise.resolve(false);
51
+ }
52
+ const rl = readline.createInterface({
53
+ input: process.stdin,
54
+ output: process.stdout,
55
+ });
56
+ return new Promise((resolve) => rl.question(question, (answer) => {
57
+ rl.close();
58
+ const trimmed = answer.trim();
59
+ resolve(trimmed === "" || /^y(es)?$/i.test(trimmed));
60
+ }));
61
+ }
9
62
  async function runInstall(args) {
10
63
  const force = args.includes("--force");
11
64
  // --claude / --codex로 대상을 고를 수 있다. 둘 다 없으면 감지된 전부.
@@ -45,14 +98,24 @@ async function runInstall(args) {
45
98
  };
46
99
  if (hasClaude) {
47
100
  await register("CLAUDE");
48
- const result = (0, claude_1.installClaudeIntegration)(config, { force });
101
+ let result = (0, claude_1.installClaudeIntegration)(config, { force });
102
+ if (!result.ok && result.needsForce) {
103
+ const yes = await confirmReplace(" 기존 statusLine 설정이 있습니다. 백업 후 교체할까요? (uninstall 시 복원) (Y/n) ");
104
+ if (yes)
105
+ result = (0, claude_1.installClaudeIntegration)(config, { force: true });
106
+ }
49
107
  console.log(result.ok
50
108
  ? "✓ Claude integration installed"
51
109
  : `✗ Claude integration skipped — ${result.reason}`);
52
110
  }
53
111
  if (hasCodex) {
54
112
  await register("CODEX");
55
- const result = (0, codex_1.installCodexIntegration)(config, { force });
113
+ let result = (0, codex_1.installCodexIntegration)(config, { force });
114
+ if (!result.ok && result.needsForce) {
115
+ const yes = await confirmReplace(" 기존 notify 설정이 있습니다. 백업 후 교체할까요? (uninstall 시 복원) (Y/n) ");
116
+ if (yes)
117
+ result = (0, codex_1.installCodexIntegration)(config, { force: true });
118
+ }
56
119
  console.log(result.ok
57
120
  ? "✓ Codex integration installed (experimental)"
58
121
  : `✗ Codex integration skipped — ${result.reason}`);
@@ -61,7 +61,8 @@ async function runLogin(args) {
61
61
  }
62
62
  if (!token) {
63
63
  const url = `${config.apiBaseUrl}/auth/github`;
64
- console.log("\n브라우저에서 GitHub 로그인 후, 응답 JSON의 token 값을 붙여넣어 주세요.");
64
+ console.log(`\n로그인하면 이용약관(${config.apiBaseUrl}/terms)과 개인정보처리방침(${config.apiBaseUrl}/privacy)에 동의하는 것으로 간주됩니다.`);
65
+ console.log("브라우저에서 GitHub 로그인 후, 응답 JSON의 token 값을 붙여넣어 주세요.");
65
66
  console.log(` ${url}\n`);
66
67
  if (process.platform === "darwin") {
67
68
  try {
@@ -33,8 +33,11 @@ async function runPost(args) {
33
33
  }
34
34
  const text = rest.join(" ").trim();
35
35
  if (!text) {
36
- console.log('사용법: pinglet post "메시지" [--category <카테고리>]');
37
- process.exitCode = 1;
36
+ console.log(quiet
37
+ ? "✗ 메시지가 비어 있습니다. /pinglet 뒤에 보낼 메시지를 적어주세요."
38
+ : '사용법: pinglet post "메시지" [--category <카테고리>]');
39
+ if (!quiet)
40
+ process.exitCode = 1;
38
41
  return;
39
42
  }
40
43
  const config = (0, config_1.loadConfig)();
@@ -56,7 +59,10 @@ async function runPost(args) {
56
59
  default:
57
60
  console.log("✗ 메시지 등록에 실패했습니다.");
58
61
  }
59
- process.exitCode = 1;
62
+ // --quiet(/pinglet slash command)에서는 exit 0 유지 — exit 1이면 Claude Code가
63
+ // 안내 문구 대신 "Shell command failed" 에러 덤프를 보여준다.
64
+ if (!quiet)
65
+ process.exitCode = 1;
60
66
  return;
61
67
  }
62
68
  // statusline 도달 표시용으로 이 기기에서 쓴 메시지를 기록해 둔다.
@@ -80,7 +86,8 @@ async function runPost(args) {
80
86
  break;
81
87
  case "REJECTED":
82
88
  console.log(`✗ 메시지가 거절됐습니다${reason ? ` — ${reason}` : ""}.`);
83
- process.exitCode = 1;
89
+ if (!quiet)
90
+ process.exitCode = 1;
84
91
  break;
85
92
  }
86
93
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinglet-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "AI가 생각하는 동안 다른 개발자의 Ping을 만나는 메시지 네트워크 — Claude Code / Codex integration",
5
5
  "license": "MIT",
6
6
  "repository": {