pinglet-cli 0.3.1 → 0.4.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/README.ja.md +6 -0
- package/README.ko.md +6 -0
- package/README.md +6 -0
- package/dist/commands/refresh.js +3 -0
- package/dist/config.js +3 -1
- package/dist/update.js +122 -0
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -56,6 +56,12 @@ pinglet post "メッセージ"
|
|
|
56
56
|
|
|
57
57
|
statusline の言語はシステム言語に応じて自動選択されます(韓国語/日本語、それ以外は英語)。言語で判定できない場合はタイムゾーン(ソウル/東京)で推定します。
|
|
58
58
|
|
|
59
|
+
## 自動アップデート
|
|
60
|
+
|
|
61
|
+
新しいバージョンが出ると、バックグラウンドで1日1回チェックして自動的に
|
|
62
|
+
アップデートします(npm グローバルインストールのみ — pnpm/yarn は対象外)。
|
|
63
|
+
無効にするには `~/.pinglet/config.json` に `"autoUpdate": false` を追加してください。
|
|
64
|
+
|
|
59
65
|
## コマンド
|
|
60
66
|
|
|
61
67
|
| コマンド | 説明 |
|
package/README.ko.md
CHANGED
|
@@ -56,6 +56,12 @@ pinglet post "메시지"
|
|
|
56
56
|
|
|
57
57
|
statusline 언어는 시스템 언어에 따라 자동 선택됩니다(한국어/일본어, 그 외는 영어). 시스템 언어로 판정이 안 되면 타임존(서울/도쿄)으로 추정합니다.
|
|
58
58
|
|
|
59
|
+
## 자동 업데이트
|
|
60
|
+
|
|
61
|
+
새 버전이 나오면 백그라운드에서 하루 1회 확인해 자동으로 업데이트합니다
|
|
62
|
+
(npm 전역 설치일 때만 — pnpm/yarn 설치는 건드리지 않습니다).
|
|
63
|
+
끄려면 `~/.pinglet/config.json`에 `"autoUpdate": false`를 추가하세요.
|
|
64
|
+
|
|
59
65
|
## 명령어
|
|
60
66
|
|
|
61
67
|
| 명령 | 설명 |
|
package/README.md
CHANGED
|
@@ -56,6 +56,12 @@ The statusline shows how many terminals are coding alongside you right now:
|
|
|
56
56
|
|
|
57
57
|
The statusline picks its language from your system language (Korean/Japanese, English otherwise), falling back to your timezone (Seoul/Tokyo) when the language doesn't decide it.
|
|
58
58
|
|
|
59
|
+
## Auto-update
|
|
60
|
+
|
|
61
|
+
Pinglet checks for a new version once a day in the background and updates
|
|
62
|
+
itself automatically (only for npm global installs — pnpm/yarn installs are
|
|
63
|
+
left untouched). To opt out, add `"autoUpdate": false` to `~/.pinglet/config.json`.
|
|
64
|
+
|
|
59
65
|
## Commands
|
|
60
66
|
|
|
61
67
|
| Command | Description |
|
package/dist/commands/refresh.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runRefresh = runRefresh;
|
|
4
4
|
const config_1 = require("../config");
|
|
5
5
|
const api_1 = require("../api");
|
|
6
|
+
const update_1 = require("../update");
|
|
6
7
|
const cache_1 = require("../cache");
|
|
7
8
|
const claude_1 = require("../adapters/claude");
|
|
8
9
|
/**
|
|
@@ -46,4 +47,6 @@ async function runRefresh() {
|
|
|
46
47
|
(0, cache_1.saveState)(state);
|
|
47
48
|
}
|
|
48
49
|
}
|
|
50
|
+
// 하루 1회 새 버전 확인 후 npm 전역 설치면 백그라운드 자동 업데이트.
|
|
51
|
+
await (0, update_1.maybeSelfUpdate)(config);
|
|
49
52
|
}
|
package/dist/config.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.DEFAULT_API_BASE_URL = exports.ONLINE_PATH = exports.POSTS_PATH = exports.EVENTS_PATH = exports.STATE_PATH = exports.FEED_PATH = exports.CONFIG_PATH = exports.PINGLET_DIR = exports.VERSION = void 0;
|
|
36
|
+
exports.DEFAULT_API_BASE_URL = exports.UPDATE_PATH = exports.ONLINE_PATH = exports.POSTS_PATH = exports.EVENTS_PATH = exports.STATE_PATH = exports.FEED_PATH = exports.CONFIG_PATH = exports.PINGLET_DIR = exports.VERSION = void 0;
|
|
37
37
|
exports.ensurePingletDir = ensurePingletDir;
|
|
38
38
|
exports.readJsonFile = readJsonFile;
|
|
39
39
|
exports.writeJsonFile = writeJsonFile;
|
|
@@ -62,6 +62,8 @@ exports.EVENTS_PATH = path.join(exports.PINGLET_DIR, "events.jsonl");
|
|
|
62
62
|
exports.POSTS_PATH = path.join(exports.PINGLET_DIR, "posts.json");
|
|
63
63
|
/** 지금 켜져 있는 터미널 수 캐시 — heartbeat 응답으로 갱신, statusline이 읽는다 */
|
|
64
64
|
exports.ONLINE_PATH = path.join(exports.PINGLET_DIR, "online.json");
|
|
65
|
+
/** 자동 업데이트 진행 기록 — refresh가 하루 1회 갱신 */
|
|
66
|
+
exports.UPDATE_PATH = path.join(exports.PINGLET_DIR, "update.json");
|
|
65
67
|
exports.DEFAULT_API_BASE_URL = process.env.PINGLET_API_URL ?? "https://pinglet.halluci.co.kr";
|
|
66
68
|
function ensurePingletDir() {
|
|
67
69
|
fs.mkdirSync(exports.PINGLET_DIR, { recursive: true });
|
package/dist/update.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
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
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isNewerVersion = isNewerVersion;
|
|
37
|
+
exports.maybeSelfUpdate = maybeSelfUpdate;
|
|
38
|
+
const child_process_1 = require("child_process");
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const config_1 = require("./config");
|
|
41
|
+
const REGISTRY_LATEST_URL = "https://registry.npmjs.org/pinglet-cli/latest";
|
|
42
|
+
const CHECK_INTERVAL_MS = 24 * 3600_000; // 하루 1회 확인
|
|
43
|
+
const RETRY_INTERVAL_MS = 7 * 24 * 3600_000; // 같은 버전 재시도는 주 1회
|
|
44
|
+
/** Windows에서 npm은 npm.cmd라 shell 경유가 필요하다 (Node의 .cmd spawn 제한). */
|
|
45
|
+
function npmSpawnOptions() {
|
|
46
|
+
return process.platform === "win32" ? { shell: true } : {};
|
|
47
|
+
}
|
|
48
|
+
async function fetchLatestVersion() {
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(REGISTRY_LATEST_URL, {
|
|
51
|
+
signal: AbortSignal.timeout(5000),
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok)
|
|
54
|
+
return null;
|
|
55
|
+
const body = (await res.json());
|
|
56
|
+
return typeof body.version === "string" ? body.version : null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** 단순 semver(major.minor.patch) 비교. 형식이 어긋나면 업데이트하지 않는 쪽으로 판정한다. */
|
|
63
|
+
function isNewerVersion(latest, current) {
|
|
64
|
+
const parse = (v) => v.trim().split(".").map((n) => Number(n));
|
|
65
|
+
const a = parse(latest);
|
|
66
|
+
const b = parse(current);
|
|
67
|
+
if (a.length !== 3 || b.length !== 3)
|
|
68
|
+
return false;
|
|
69
|
+
if ([...a, ...b].some((n) => !Number.isInteger(n) || n < 0))
|
|
70
|
+
return false;
|
|
71
|
+
for (let i = 0; i < 3; i++) {
|
|
72
|
+
if (a[i] !== b[i])
|
|
73
|
+
return a[i] > b[i];
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/** npm 전역 루트. npm이 없거나 실패하면 null — 자동 업데이트를 포기한다. */
|
|
78
|
+
function npmGlobalRoot() {
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
(0, child_process_1.execFile)("npm", ["root", "-g"], { timeout: 10_000, windowsHide: true, ...npmSpawnOptions() }, (err, stdout) => resolve(err ? null : stdout.trim() || null));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 새 버전이 있으면 백그라운드로 자기 자신을 업데이트한다.
|
|
85
|
+
* - npm 전역 설치일 때만 실행한다 (cliPath가 `npm root -g` 아래인지 확인).
|
|
86
|
+
* pnpm/yarn/링크 설치에 npm을 덧씌우면 다른 복사본만 생기고 적용도 안 된다.
|
|
87
|
+
* - statusline은 매 틱 새 프로세스로 뜨므로, 설치가 끝나면 다음 틱부터
|
|
88
|
+
* 새 버전이 실행된다. 전역 경로는 버전과 무관해 설정 변경도 필요 없다.
|
|
89
|
+
* - 실패해도 조용히 넘어간다 (렌더링·refresh에 영향 금지). 재시도는 주 1회.
|
|
90
|
+
*/
|
|
91
|
+
async function maybeSelfUpdate(config) {
|
|
92
|
+
if (config.autoUpdate === false)
|
|
93
|
+
return;
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
const state = (0, config_1.readJsonFile)(config_1.UPDATE_PATH) ?? {};
|
|
96
|
+
const lastCheck = state.lastCheckAt ? Date.parse(state.lastCheckAt) : NaN;
|
|
97
|
+
if (!Number.isNaN(lastCheck) && now - lastCheck < CHECK_INTERVAL_MS)
|
|
98
|
+
return;
|
|
99
|
+
// 확인 시각을 먼저 기록 — 레지스트리 장애 시에도 하루 1회 이상 두드리지 않는다.
|
|
100
|
+
state.lastCheckAt = new Date(now).toISOString();
|
|
101
|
+
(0, config_1.writeJsonFile)(config_1.UPDATE_PATH, state);
|
|
102
|
+
const latest = await fetchLatestVersion();
|
|
103
|
+
if (!latest || !isNewerVersion(latest, config_1.VERSION))
|
|
104
|
+
return;
|
|
105
|
+
if (state.lastAttempt?.version === latest) {
|
|
106
|
+
const at = Date.parse(state.lastAttempt.at);
|
|
107
|
+
if (!Number.isNaN(at) && now - at < RETRY_INTERVAL_MS)
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const root = await npmGlobalRoot();
|
|
111
|
+
if (!root || !(0, config_1.cliPath)().startsWith(root + path.sep))
|
|
112
|
+
return;
|
|
113
|
+
state.lastAttempt = { version: latest, at: new Date(now).toISOString() };
|
|
114
|
+
(0, config_1.writeJsonFile)(config_1.UPDATE_PATH, state);
|
|
115
|
+
try {
|
|
116
|
+
const child = (0, child_process_1.spawn)("npm", ["install", "-g", `pinglet-cli@${latest}`], { detached: true, stdio: "ignore", windowsHide: true, ...npmSpawnOptions() });
|
|
117
|
+
child.unref();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// npm 실행 실패 — 다음 주기에 재시도된다.
|
|
121
|
+
}
|
|
122
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinglet-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Short messages from other developers shown in your Claude Code spinner while the AI thinks — zero tokens, never reads your code. Claude Code / Codex integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|