pinglet-cli 0.2.3 โ†’ 0.2.5

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
@@ -1,127 +1,85 @@
1
- # Pinglet Client
1
+ # Pinglet ๐Ÿ’Œ
2
2
 
3
- AI๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž์˜ Ping์„ ๋งŒ๋‚˜๋Š” ๋ฉ”์‹œ์ง€ ๋„คํŠธ์›Œํฌ โ€” ๋กœ์ปฌ ๋Ÿฐํƒ€์ž„(CLI).
3
+ **AI๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๋™์•ˆ, ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž์˜ ํ•œ ์ค„์„ ๋งŒ๋‚˜์„ธ์š”.**
4
4
 
5
- `pinglet-backend`(NestJS)์™€ ์—ฐ๋™๋˜๋ฉฐ, ํ•œ ๋ฒˆ ์„ค์น˜ํ•˜๋ฉด ํ‰์†Œ์ฒ˜๋Ÿผ `claude` / `codex`๋ฅผ
6
- ์‹คํ–‰ํ•˜๊ธฐ๋งŒ ํ•ด๋„ ๋Œ€๊ธฐ ๊ตฌ๊ฐ„์— ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž์˜ ์งง์€ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค.
5
+ Claude Code๊ฐ€ ์ฝ”๋“œ๋ฅผ ๊ณ ์น˜๋Š” ๋™์•ˆ "Befuddlingโ€ฆ" ์Šคํ”ผ๋„ˆ๋งŒ ๋ฐ”๋ผ๋ณด๋˜ ๊ทธ ์ž๋ฆฌ์—,
6
+ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋‚จ๊ธด ์งง์€ ๋ฉ”์‹œ์ง€(**Ping**)๊ฐ€ ๋Œ€์‹  ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
7
7
 
8
- ## ์„ค์น˜ ๋ฐ ์‚ฌ์šฉ
9
-
10
- ```bash
11
- npm install -g pinglet-cli # ์„ค์น˜ ํ›„ ๋ช…๋ น์–ด๋Š” `pinglet`
8
+ ```
9
+ โœถ ๐Ÿ’Œ "๊ธˆ์š”์ผ ์˜คํ›„ ๋ฐฐํฌ๋Š” ๋‹ค์Œ ์ƒ์— ํ•ฉ์‹œ๋‹ค." (12s ยท โ†“ 1.2k tokens)
12
10
  ```
13
11
 
14
- npm ๊ธ€๋กœ๋ฒŒ ์„ค์น˜ ์‹œ postinstall์ด `pinglet install`๊นŒ์ง€ ์ž๋™ ์ˆ˜ํ–‰ํ•œ๋‹ค.
15
- (pnpm/bun์ฒ˜๋Ÿผ postinstall์„ ์ฐจ๋‹จํ•˜๋Š” ๋งค๋‹ˆ์ €์—์„œ๋Š” `pinglet install`์„ ์ง์ ‘ ์‹คํ–‰)
16
-
17
- ์ œ๊ฑฐํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ integration ์ •๋ฆฌ๋ฅผ ๋จผ์ € ํ•œ๋‹ค โ€” npm์€ uninstall ์‹œ
18
- ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์„ค์ •์ด ๋‚จ๋Š”๋‹ค:
12
+ ## ์‹œ์ž‘ํ•˜๊ธฐ
19
13
 
20
14
  ```bash
21
- pinglet uninstall
22
- npm uninstall -g pinglet-cli
15
+ npm install -g pinglet-cli
23
16
  ```
24
17
 
25
- ๊ฐœ๋ฐœ ์‹œ์—๋Š” ๋กœ์ปฌ ๋ฐฑ์—”๋“œ์— ์—ฐ๊ฒฐํ•œ๋‹ค:
18
+ ์„ค์น˜๊ฐ€ ๋๋‚˜๋ฉด ์ž๋™์œผ๋กœ Claude Code์— ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„๋Š” ํ‰์†Œ์ฒ˜๋Ÿผ `claude`๋ฅผ
19
+ ์‹คํ–‰ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ผ์š”. ์ž๋™ ์„ค์ •์ด ์•ˆ ๋๋‹ค๋ฉด `pinglet install` ํ•œ ๋ฒˆ์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.
26
20
 
27
- ```bash
28
- npm install
29
- npm run build
21
+ - ๊ธฐ์กด statusline ์„ค์ •์ด ์žˆ๋‹ค๋ฉด **๋ฐฑ์—… ํ›„ ๊ต์ฒดํ• ์ง€ ๋ฌผ์–ด๋ณด๊ณ **, ์ œ๊ฑฐ ์‹œ ์›๋ž˜๋Œ€๋กœ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค.
22
+ - Codex๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค(experimental) โ€” turn ์™„๋ฃŒ ์‹œ macOS ์•Œ๋ฆผ์œผ๋กœ Ping์ด ๋„์ฐฉํ•ฉ๋‹ˆ๋‹ค.
30
23
 
31
- # ๊ฐœ๋ฐœ ์ค‘ ๋กœ์ปฌ ๋ฐฑ์—”๋“œ์— ์—ฐ๊ฒฐ
32
- node dist/cli.js install --api http://localhost:3000
24
+ ## Ping ๋ณด๋‚ด๊ธฐ
33
25
 
34
- # ์ดํ›„ ํ‰์†Œ์ฒ˜๋Ÿผ
35
- claude # statusline์— ๐Ÿ’Œ Ping ํ‘œ์‹œ
36
- codex # turn ์™„๋ฃŒ ์‹œ macOS ์•Œ๋ฆผ์œผ๋กœ Ping ์ „๋‹ฌ (experimental)
37
- ```
26
+ Claude Code ์„ธ์…˜ ์•ˆ์—์„œ ๋ฐ”๋กœ:
38
27
 
39
- | ๋ช…๋ น | ์„ค๋ช… |
40
- |---|---|
41
- | `pinglet install [--api <url>] [--force]` | Claude Code / Codex ์ž๋™ ๊ฐ์ง€ ํ›„ integration ์„ค์น˜ + ์„œ๋ฒ„ ๋“ฑ๋ก + ์ดˆ๊ธฐ feed ์บ์‹œ |
42
- | `pinglet uninstall [--purge]` | integration ์ œ๊ฑฐ, ๊ธฐ์กด ์„ค์ • ๋ณต์›. `--purge`๋Š” `~/.pinglet`๊นŒ์ง€ ์‚ญ์ œ |
43
- | `pinglet login [--token <jwt>]` | GitHub ๋กœ๊ทธ์ธ ํ›„ ์ต๋ช… ์„ค์น˜๋ฅผ ๊ณ„์ •์— ์—ฐ๊ฒฐ |
44
- | `pinglet post "๋ฉ”์‹œ์ง€" [--category <c>]` | ๋ฉ”์‹œ์ง€ ์ž‘์„ฑ (GitHub ๋กœ๊ทธ์ธ ํ•„์š”, ์ฝ๊ธฐ๋Š” ๋กœ๊ทธ์ธ ์—†์ด ๊ฐ€๋Šฅ) |
45
- | `pinglet doctor` | ์„ค์น˜/์บ์‹œ/ํ/์„œ๋ฒ„ ์ƒํƒœ ์ง„๋‹จ |
46
- | `pinglet ping` | ๋ฉ”์‹œ์ง€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ (์ด๋ฒคํŠธ ๊ธฐ๋ก ์—†์Œ) |
28
+ ```
29
+ > /pinglet ์˜ค๋Š˜๋„ ๋นŒ๋“œ๊ฐ€ ์ดˆ๋ก๋ถˆ์ด๊ธธ
30
+ ```
47
31
 
48
- ๋‚ด๋ถ€ ๋ช…๋ น(integration์ด ํ˜ธ์ถœ): `statusline`, `notify`, `flush`, `refresh`
32
+ ๋˜๋Š” ํ„ฐ๋ฏธ๋„์—์„œ:
49
33
 
50
- ### ์„ธ์…˜ ์•ˆ์—์„œ ๋ฐ”๋กœ ์ž‘์„ฑ
34
+ ```bash
35
+ pinglet login # GitHub ๋กœ๊ทธ์ธ โ€” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—ด๋ฆฌ๊ณ  ์ž๋™์œผ๋กœ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค (์ตœ์ดˆ 1ํšŒ)
36
+ pinglet post "๋ฉ”์‹œ์ง€"
37
+ ```
51
38
 
52
- Claude Code ์„ธ์…˜์—์„œ `/pinglet ๋ฉ”์‹œ์ง€`๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๋“ฑ๋ก๋œ๋‹ค.
53
- install ์‹œ `~/.claude/commands/pinglet.md` slash command๊ฐ€ ํ•จ๊ป˜ ์„ค์น˜๋˜๋ฉฐ,
54
- ๋“ฑ๋ก ์ž์ฒด๋Š” command ์•ˆ์˜ ์ธ๋ผ์ธ bash ์‹คํ–‰(`pinglet post`)์ด ์ˆ˜ํ–‰ํ•˜๊ณ 
55
- ๋ชจ๋ธ์€ ๊ฒฐ๊ณผ ํ•œ ์ค„๋งŒ ์ „๋‹ฌํ•˜๋ฏ€๋กœ ํ† ํฐ ์‚ฌ์šฉ์ด ๋งค์šฐ ์ ๋‹ค.
39
+ ์ฝ๊ธฐ๋Š” ๋กœ๊ทธ์ธ ์—†์ด ๊ฐ€๋Šฅํ•˜๊ณ , **์ž‘์„ฑ์—๋งŒ GitHub ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.**
40
+ ๋‚ด๊ฐ€ ๋ณด๋‚ธ Ping์ด ์–ผ๋งˆ๋‚˜ ํผ์กŒ๋Š”์ง€๋Š” statusline์—์„œ ๋ฐ”๋กœ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค:
56
41
 
57
42
  ```
58
- > /pinglet ๊ธˆ์š”์ผ ์˜คํ›„ ๋ฐฐํฌ๋Š” ๋‹ค์Œ ์ƒ์— ํ•ฉ์‹œ๋‹ค.
43
+ ๐Ÿ’Œ ๋‚ด Ping "์˜ค๋Š˜๋„ ๋นŒ๋“œ๊ฐ€ ์ดˆ๋ก๋ถˆ์ด๊ธธ" โ†’ ๋„๋‹ฌ๋ฅ  17%
59
44
  ```
60
45
 
61
- ## Integration ๋ฐฉ์‹
62
-
63
- - **Claude Adapter** โ€” `~/.claude/settings.json`์˜ `statusLine` hook์—
64
- `node <cli> statusline`์„ ์—ฐ๊ฒฐํ•œ๋‹ค. statusline์€ UI ์˜์—ญ์ด๋ผ LLM context์—
65
- ์ „ํ˜€ ๋“ค์–ด๊ฐ€์ง€ ์•Š์œผ๋ฉฐ(Zero Token), Thinking/Working ์ค‘์— ๋ฉ”์‹œ์ง€๊ฐ€ ๋ณด์ธ๋‹ค.
66
- ๊ธฐ์กด statusLine ์„ค์ •์€ ๋ฐฑ์—… ํ›„ uninstall ์‹œ ๋ณต์›ํ•œ๋‹ค.
67
- - **Spinner verbs (pool=1 ํšŒ์ „)** โ€” `spinnerVerbs`(mode: "replace")์— ๋ฉ”์‹œ์ง€๋ฅผ
68
- **ํ•œ ๋ฒˆ์— ํ•˜๋‚˜๋งŒ** ๋“ฑ๋กํ•ด "Befuddlingโ€ฆ" ์ž๋ฆฌ์— Ping์ด ํ‘œ์‹œ๋œ๋‹ค. statusline์ด
69
- 60์ดˆ๋งˆ๋‹ค ๋‹ค์Œ ๋ฉ”์‹œ์ง€๋กœ ๊ต์ฒดํ•˜๋ฉฐ, Claude Code๊ฐ€ ์„ค์ •์„ ํ•ซ๋ฆฌ๋กœ๋“œํ•˜๋ฏ€๋กœ ์„ธ์…˜
70
- ์ค‘์—๋„ ์ฆ‰์‹œ ๋ฐ˜์˜๋œ๋‹ค. pool์ด 1๊ฐœ๋ผ "spinner๊ฐ€ ๋Œ์•˜๋‹ค = ์ด ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋๋‹ค"๊ฐ€
71
- ํ™•์ •๋œ๋‹ค. Pinglet verb๋Š” zero-width space ๋งˆ์ปค๋กœ ์‹๋ณ„ํ•˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘
72
- ๋“ฑ๋กํ•œ verb๋Š” ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ , uninstall ์‹œ ์›๋ž˜๋Œ€๋กœ ๋ณต์›ํ•œ๋‹ค.
73
- - **Codex Adapter (experimental)** โ€” `~/.codex/config.toml`์˜ `notify` hook์— ์—ฐ๊ฒฐ.
74
- notify๋Š” TUI ๋ฐ– ํ”„๋กœ์„ธ์Šค๋ผ ํ„ฐ๋ฏธ๋„ ์•ˆ์— ๊ทธ๋ฆด ์ˆ˜ ์—†์–ด, MVP์—์„œ๋Š” turn ์™„๋ฃŒ ์‹œ
75
- macOS ์•Œ๋ฆผ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค.
76
-
77
- ## ์•„ํ‚คํ…์ฒ˜ ์›์น™ ๊ตฌํ˜„
78
-
79
- - **Local-first Rendering** โ€” ์„œ๋ฒ„ feed๋ฅผ 30~50๊ฐœ ๋ฏธ๋ฆฌ ๋ฐ›์•„ `~/.pinglet/feed.json`์—
80
- ์บ์‹œํ•˜๊ณ , ๋ Œ๋”๋ง ์ˆœ๊ฐ„์—๋Š” ๋กœ์ปฌ ํŒŒ์ผ๋งŒ ๋™๊ธฐ I/O๋กœ ์ฝ๋Š”๋‹ค. ๋ Œ๋”๋ง path์—
81
- ๋„คํŠธ์›Œํฌ๊ฐ€ ์ ˆ๋Œ€ ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค.
82
- - **์˜คํ”„๋ผ์ธ ๋™์ž‘** โ€” ์„œ๋ฒ„ ๋ฏธ์—ฐ๊ฒฐ ์‹œ ์‹œ๋“œ ๋ฉ”์‹œ์ง€๋กœ ๋™์ž‘ํ•˜๊ณ , ๋“ฑ๋ก/feed ๊ฐฑ์‹ ์€
83
- ๋ฐฑ๊ทธ๋ผ์šด๋“œ `refresh`๊ฐ€ ์˜จ๋ผ์ธ์ด ๋˜๋ฉด ์ž๋™ ์žฌ์‹œ๋„ํ•œ๋‹ค.
84
- - **DELIVERED / QUALIFIED_IMPRESSION ๋ถ„๋ฆฌ** โ€” ๋ฉ”์‹œ์ง€๊ฐ€ spinner์— armed๋˜๋ฉด
85
- DELIVERED(์„ค์น˜๋‹น 1ํšŒ), armed๋œ ๋™์•ˆ spinner๊ฐ€ 3์ดˆ ์ด์ƒ ์‹ค์ œ๋กœ ๋Œ์•˜์œผ๋ฉด
86
- QUALIFIED_IMPRESSION(visibleMs ํฌํ•จ)์œผ๋กœ ์ •์‚ฐํ•œ๋‹ค. "์‹ค์ œ๋กœ ๋Œ์•˜๋Š”์ง€"๋Š”
87
- statusline hook์ด ๋ฐ›๋Š” `cost.total_api_duration_ms` ์ฆ๊ฐ€๋ถ„์œผ๋กœ ์ธก์ •ํ•œ๋‹ค โ€”
88
- pool์ด 1๊ฐœ์ด๋ฏ€๋กœ API ํ™œ๋™ ์‹œ๊ฐ„๋งŒํผ ๊ทธ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋œ ๊ฒƒ์ด ํ™•์ •๋œ๋‹ค.
89
- - **Async Telemetry** โ€” ์ด๋ฒคํŠธ๋Š” `~/.pinglet/events.jsonl`(Local Event Queue)์—
90
- appendํ•˜๊ณ , 20๊ฐœ ์ด์ƒ ๋˜๋Š” 60์ดˆ ์ฃผ๊ธฐ๋กœ detached ํ”„๋กœ์„ธ์Šค๊ฐ€ batch ์ „์†กํ•œ๋‹ค.
91
- ์ „์†ก ์„ฑ๊ณต๋ถ„๋งŒ ํ์—์„œ ์ œ๊ฑฐํ•˜๋ฏ€๋กœ ์‹คํŒจ๋ถ„์€ ์ž๋™ ์žฌ์‹œ๋„๋˜๋ฉฐ, ์žฌ์ „์†ก ์ค‘๋ณต์€
92
- ์„œ๋ฒ„๊ฐ€ `eventId`๋กœ dedupeํ•œ๋‹ค.
93
- - **Privacy** โ€” prompt/์‘๋‹ต/์ฝ”๋“œ/ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ฝ์ง€ ์•Š๋Š”๋‹ค. ์ˆ˜์ง‘ํ•˜๋Š” ๊ฒƒ์€
94
- installationId, agentType, OS, clientVersion, ๋ฉ”์‹œ์ง€ delivery ์ด๋ฒคํŠธ๋ฟ์ด๋‹ค.
95
-
96
- ## ๋ฐฑ์—”๋“œ API ์‚ฌ์šฉ (pinglet-backend)
97
-
98
- | ์—”๋“œํฌ์ธํŠธ | ์šฉ๋„ |
99
- |---|---|
100
- | `POST /installations` | agent๋ณ„ ์ต๋ช… ์„ค์น˜ ๋“ฑ๋ก โ†’ `{installationId, token}` |
101
- | `GET /feed?limit=50` | ๋กœ์ปฌ ์บ์‹œ์šฉ feed ์ˆ˜์‹  (installation JWT) |
102
- | `POST /events/batch` | ์ด๋ฒคํŠธ batch ์ „์†ก (์ตœ๋Œ€ 200๊ฐœ, eventId dedupe) |
103
- | `POST /messages` | ๋ฉ”์‹œ์ง€ ์ž‘์„ฑ (`post` ๋ช…๋ น, ์œ ์ € JWT ๋˜๋Š” installation JWT) |
104
- | `POST /installations/heartbeat` | lastSeenAt ๊ฐฑ์‹  |
105
- | `POST /installations/link` | ๋กœ๊ทธ์ธ ํ›„ ์„ค์น˜๋ฅผ ๊ณ„์ •์— ์—ฐ๊ฒฐ |
106
- | `GET /auth/github` | GitHub OAuth ์‹œ์ž‘ (login ๋ช…๋ น์ด ๋ธŒ๋ผ์šฐ์ €๋กœ ์—ฐ๋‹ค) |
46
+ ## ๋ช…๋ น์–ด
107
47
 
108
- ## ๋กœ์ปฌ ํŒŒ์ผ
48
+ | ๋ช…๋ น | ์„ค๋ช… |
49
+ |---|---|
50
+ | `pinglet install` | Claude Code / Codex ๊ฐ์ง€ ํ›„ ์—ฐ๊ฒฐ |
51
+ | `pinglet login` | GitHub ๊ณ„์ • ์—ฐ๊ฒฐ (๋ฉ”์‹œ์ง€ ์ž‘์„ฑ์— ํ•„์š”) |
52
+ | `pinglet post "๋ฉ”์‹œ์ง€"` | Ping ๋ณด๋‚ด๊ธฐ |
53
+ | `pinglet ping` | ์ง€๊ธˆ ํ‘œ์‹œ๋  ๋ฉ”์‹œ์ง€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ |
54
+ | `pinglet doctor` | ์„ค์น˜ยท์—ฐ๊ฒฐ ์ƒํƒœ ์ง„๋‹จ |
55
+ | `pinglet uninstall` | ์—ฐ๊ฒฐ ํ•ด์ œ ๋ฐ ๊ธฐ์กด ์„ค์ • ๋ณต์› |
56
+
57
+ ## ์•ˆ์‹ฌํ•˜๊ณ  ์“ฐ์„ธ์š”
58
+
59
+ - **ํ† ํฐ ์‚ฌ์šฉ๋Ÿ‰ 0** โ€” ๋ฉ”์‹œ์ง€๋Š” Claude Code์˜ UI ์˜์—ญ(statusline/spinner)์—๋งŒ
60
+ ํ‘œ์‹œ๋˜๊ณ  ๋ชจ๋ธ ์ปจํ…์ŠคํŠธ์—๋Š” ์ „ํ˜€ ๋“ค์–ด๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. API ๋น„์šฉ๊ณผ ์‘๋‹ต ํ’ˆ์งˆ์—
61
+ ์˜ํ–ฅ์ด ์—†์Šต๋‹ˆ๋‹ค.
62
+ - **์ฝ”๋“œ๋ฅผ ์ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค** โ€” prompt, ์‘๋‹ต, ์ฝ”๋“œ, ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
63
+ ์ˆ˜์ง‘ํ•˜๋Š” ๊ฒƒ์€ ์„ค์น˜ ID, OS ์ข…๋ฅ˜, ํด๋ผ์ด์–ธํŠธ ๋ฒ„์ „, ๋ฉ”์‹œ์ง€ ๋…ธ์ถœ ์ด๋ฒคํŠธ๋ฟ์ž…๋‹ˆ๋‹ค.
64
+ - **ํ„ฐ๋ฏธ๋„์ด ๋А๋ ค์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค** โ€” ํ‘œ์‹œ๋Š” ๋กœ์ปฌ ์บ์‹œ๋งŒ ์ฝ์–ด์„œ ๊ทธ๋ฆฌ๋ฉฐ, ๋„คํŠธ์›Œํฌ๋Š”
65
+ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜คํ”„๋ผ์ธ์—์„œ๋„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
66
+ - **๋ชจ๋“  ๋ฉ”์‹œ์ง€๋Š” ๊ฒ€์ˆ˜๋ฅผ ๊ฑฐ์นฉ๋‹ˆ๋‹ค** โ€” URLยท๊ฐœ์ธ์ •๋ณดยท์ œ์–ด๋ฌธ์žยท๋ถ€์ ์ ˆํ•œ ํ‘œํ˜„์€
67
+ ์ž๋™์œผ๋กœ ๊ฑธ๋Ÿฌ์ง€๋ฉฐ, ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ๋ฉ”์‹œ์ง€๋Š” ๋ฐœ๊ฒฌ ์‹œ ๋ฐ”๋กœ ๋‚ด๋ ค์ฃผ์„ธ์š”
68
+ (์•„๋ž˜ ๋ฌธ์˜ยทํ”ผ๋“œ๋ฐฑ ๋งํฌ).
69
+
70
+ ## ์ œ๊ฑฐ
109
71
 
72
+ ```bash
73
+ pinglet uninstall # ์—ฐ๊ฒฐ ํ•ด์ œ + ๊ธฐ์กด ์„ค์ • ๋ณต์› (npm ์ œ๊ฑฐ๋ณด๋‹ค ๋จผ์ €!)
74
+ npm uninstall -g pinglet-cli
110
75
  ```
111
- ~/.pinglet/
112
- config.json # apiBaseUrl, agent๋ณ„ installation ํ† ํฐ, adapter ์„ค์น˜ ์ •๋ณด
113
- feed.json # Local Feed Cache
114
- state.json # ํ˜„์žฌ ํ‘œ์‹œ ์ค‘ ๋ฉ”์‹œ์ง€, seen ์นด์šดํŠธ, flush/refresh ํƒ€์ด๋ฐ
115
- events.jsonl # Local Event Queue (append-only)
116
- ```
76
+
77
+ ๋กœ์ปฌ ๋ฐ์ดํ„ฐ(`~/.pinglet`)๊นŒ์ง€ ์ง€์šฐ๋ ค๋ฉด `pinglet uninstall --purge`.
117
78
 
118
79
  ## ์•ฝ๊ด€ ๋ฐ ๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ
119
80
 
120
- Pinglet์€ GitHub ๊ณ„์ • ์ •๋ณด(๋กœ๊ทธ์ธ ์‹œ), ์„ค์น˜ ํ™˜๊ฒฝ ์ •๋ณด, ์ ‘์† IP๋ฅผ ์„œ๋น„์Šค ์ œ๊ณตยท์–ด๋ทฐ์ง•
121
- ๋ฐฉ์ง€ ๋ชฉ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธํ•˜์—ฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์•„๋ž˜ ๋ฌธ์„œ์— ๋™์˜ํ•œ ๊ฒƒ์œผ๋กœ
122
- ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค.
81
+ ๋กœ๊ทธ์ธํ•˜์—ฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์•„๋ž˜ ๋ฌธ์„œ์— ๋™์˜ํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค.
123
82
 
124
- - [์ด์šฉ์•ฝ๊ด€](https://pinglet.halluci.co.kr/terms)
125
- - [๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ](https://pinglet.halluci.co.kr/privacy)
83
+ - [์ด์šฉ์•ฝ๊ด€](https://pinglet.halluci.co.kr/terms) ยท [๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ](https://pinglet.halluci.co.kr/privacy)
126
84
 
127
- ๋ฌธ์˜: leeyeonggyun@naver.com
85
+ ๋ฌธ์˜ยทํ”ผ๋“œ๋ฐฑ: leeyeonggyun@naver.com ยท [GitHub Issues](https://github.com/mabyko/pinglet-client/issues)
@@ -35,9 +35,11 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.runLogin = runLogin;
37
37
  const child_process_1 = require("child_process");
38
+ const http = __importStar(require("http"));
38
39
  const readline = __importStar(require("readline"));
39
40
  const config_1 = require("../config");
40
41
  const api_1 = require("../api");
42
+ const LOGIN_TIMEOUT_MS = 180_000;
41
43
  function prompt(question) {
42
44
  const rl = readline.createInterface({
43
45
  input: process.stdin,
@@ -48,10 +50,72 @@ function prompt(question) {
48
50
  resolve(answer);
49
51
  }));
50
52
  }
53
+ function openBrowser(url) {
54
+ const cmd = process.platform === "darwin"
55
+ ? ["open", url]
56
+ : process.platform === "win32"
57
+ ? ["cmd", "/c", "start", "", url]
58
+ : ["xdg-open", url];
59
+ try {
60
+ (0, child_process_1.spawn)(cmd[0], cmd.slice(1), { stdio: "ignore" }).unref();
61
+ return true;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ const RESULT_HTML = (ok) => `<!doctype html>
68
+ <html lang="ko"><head><meta charset="utf-8"><title>Pinglet</title>
69
+ <style>body{font-family:-apple-system,sans-serif;display:flex;align-items:center;
70
+ justify-content:center;height:100vh;margin:0;background:#0f1115;color:#e6e9ef}
71
+ div{text-align:center}p{color:#8b93a3}</style></head><body><div>
72
+ <h1>${ok ? "๐Ÿ’Œ ๋กœ๊ทธ์ธ ์™„๋ฃŒ" : "โœ— ๋กœ๊ทธ์ธ ์‹คํŒจ"}</h1>
73
+ <p>${ok ? "์ด ์ฐฝ์„ ๋‹ซ๊ณ  ํ„ฐ๋ฏธ๋„๋กœ ๋Œ์•„๊ฐ€์„ธ์š”." : "ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”."}</p>
74
+ </div></body></html>`;
51
75
  /**
52
- * GitHub OAuth ๋กœ๊ทธ์ธ.
53
- * ๋ธŒ๋ผ์šฐ์ €์—์„œ /auth/github โ†’ callback JSON์˜ token์„ CLI์— ๋ถ™์—ฌ๋„ฃ๋Š” ๋ฐฉ์‹ (MVP).
76
+ * ๋ฃจํ”„๋ฐฑ ๋กœ๊ทธ์ธ: ์ผํšŒ์šฉ ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ๋„์šฐ๊ณ  ๋ธŒ๋ผ์šฐ์ €์—์„œ OAuth๋ฅผ ๋งˆ์น˜๋ฉด
77
+ * ์„œ๋ฒ„๊ฐ€ http://127.0.0.1:{port}/callback?token=... ์œผ๋กœ ํ† ํฐ์„ ๋„˜๊ฒจ์ค€๋‹ค.
78
+ * ํƒ€์ž„์•„์›ƒ/์‹คํŒจ ์‹œ null โ€” ํ˜ธ์ถœ๋ถ€๊ฐ€ ์ˆ˜๋™ ๋ถ™์—ฌ๋„ฃ๊ธฐ๋กœ ํด๋ฐฑํ•œ๋‹ค.
54
79
  */
80
+ function loginViaLoopback(apiBaseUrl) {
81
+ return new Promise((resolve) => {
82
+ let finished = false;
83
+ const done = (token) => {
84
+ if (finished)
85
+ return;
86
+ finished = true;
87
+ clearTimeout(timer);
88
+ // ๋งˆ์ง€๋ง‰ ์‘๋‹ต์ด flush๋  ์‹œ๊ฐ„์„ ์ฃผ๊ณ  ๋‹ซ๋Š”๋‹ค.
89
+ setTimeout(() => {
90
+ server.closeAllConnections?.();
91
+ server.close();
92
+ }, 200).unref();
93
+ resolve(token);
94
+ };
95
+ const timer = setTimeout(() => done(null), LOGIN_TIMEOUT_MS);
96
+ const server = http.createServer((req, res) => {
97
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
98
+ if (url.pathname !== "/callback") {
99
+ res.statusCode = 404;
100
+ res.end();
101
+ return;
102
+ }
103
+ const token = url.searchParams.get("token");
104
+ res.setHeader("content-type", "text/html; charset=utf-8");
105
+ res.end(RESULT_HTML(Boolean(token)));
106
+ done(token);
107
+ });
108
+ server.on("error", () => done(null));
109
+ server.listen(0, "127.0.0.1", () => {
110
+ const port = server.address().port;
111
+ const url = `${apiBaseUrl}/auth/github?cli_port=${port}`;
112
+ console.log("๋ธŒ๋ผ์šฐ์ €์—์„œ GitHub ๋กœ๊ทธ์ธ์„ ์™„๋ฃŒํ•ด ์ฃผ์„ธ์š”โ€ฆ");
113
+ console.log(` ${url}\n`);
114
+ openBrowser(url);
115
+ });
116
+ });
117
+ }
118
+ /** GitHub OAuth ๋กœ๊ทธ์ธ โ€” ๋ฃจํ”„๋ฐฑ ์ž๋™ ์ˆ˜์‹ , ์‹คํŒจ ์‹œ ์ˆ˜๋™ ๋ถ™์—ฌ๋„ฃ๊ธฐ ํด๋ฐฑ. */
55
119
  async function runLogin(args) {
56
120
  const config = (0, config_1.loadConfig)();
57
121
  let token;
@@ -60,19 +124,14 @@ async function runLogin(args) {
60
124
  token = args[tokenIndex + 1];
61
125
  }
62
126
  if (!token) {
63
- const url = `${config.apiBaseUrl}/auth/github`;
64
127
  console.log(`\n๋กœ๊ทธ์ธํ•˜๋ฉด ์ด์šฉ์•ฝ๊ด€(${config.apiBaseUrl}/terms)๊ณผ ๊ฐœ์ธ์ •๋ณด์ฒ˜๋ฆฌ๋ฐฉ์นจ(${config.apiBaseUrl}/privacy)์— ๋™์˜ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค.`);
65
- console.log("๋ธŒ๋ผ์šฐ์ €์—์„œ GitHub ๋กœ๊ทธ์ธ ํ›„, ์‘๋‹ต JSON์˜ token ๊ฐ’์„ ๋ถ™์—ฌ๋„ฃ์–ด ์ฃผ์„ธ์š”.");
66
- console.log(` ${url}\n`);
67
- if (process.platform === "darwin") {
68
- try {
69
- (0, child_process_1.spawn)("open", [url], { stdio: "ignore" }).unref();
70
- }
71
- catch {
72
- // ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋ชป ์—ด์–ด๋„ URL์€ ์ถœ๋ ฅํ–ˆ์œผ๋ฏ€๋กœ ์ง„ํ–‰
73
- }
128
+ token = (await loginViaLoopback(config.apiBaseUrl)) ?? undefined;
129
+ if (!token) {
130
+ // SSH ๋“ฑ ๋ธŒ๋ผ์šฐ์ €์™€ ํ„ฐ๋ฏธ๋„์ด ๋‹ค๋ฅธ ๊ธฐ๊ณ„์ธ ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•œ ํด๋ฐฑ.
131
+ console.log("์ž๋™ ๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ URL์—์„œ ๋กœ๊ทธ์ธ ํ›„ ์‘๋‹ต JSON์˜ token ๊ฐ’์„ ๋ถ™์—ฌ๋„ฃ์–ด ์ฃผ์„ธ์š”.");
132
+ console.log(` ${config.apiBaseUrl}/auth/github\n`);
133
+ token = (await prompt("token: ")).trim();
74
134
  }
75
- token = (await prompt("token: ")).trim();
76
135
  }
77
136
  if (!token) {
78
137
  console.log("โœ— token์ด ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinglet-cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "AI๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž์˜ Ping์„ ๋งŒ๋‚˜๋Š” ๋ฉ”์‹œ์ง€ ๋„คํŠธ์›Œํฌ โ€” Claude Code / Codex integration",
5
5
  "license": "MIT",
6
6
  "repository": {