pleumrouter 0.5.29 → 0.5.30
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/package.json +1 -1
- package/src/health.mjs +26 -13
- package/src/locales/es.mjs +2 -1
- package/src/locales/ja.mjs +2 -1
- package/src/locales/ko.mjs +2 -1
- package/src/locales/zh.mjs +2 -1
package/package.json
CHANGED
package/src/health.mjs
CHANGED
|
@@ -9,6 +9,10 @@ const ok = (s) => `${style.green("●")} ${s}`;
|
|
|
9
9
|
const bad = (s) => `${style.red("●")} ${s}`;
|
|
10
10
|
const warn = (s) => `${style.yellow("●")} ${s}`;
|
|
11
11
|
|
|
12
|
+
// 백엔드는 "ok" 문자열. db는 "ok:supabase"처럼 백엔드 힌트가 붙을 수 있다 — 정상으로 본다
|
|
13
|
+
// (과거엔 "ok"만 인정해 정상 DB가 빨간 점으로 떴다).
|
|
14
|
+
export const isHealthy = (v) => v === true || v === "ok" || (typeof v === "string" && v.startsWith("ok:"));
|
|
15
|
+
|
|
12
16
|
// health 응답 + 로컬 상태 → 표시 문자열(순수 함수 — fixture 테스트 대상).
|
|
13
17
|
export function renderHealth({ root, health, error, ms, keyPrefix }, styler = style) {
|
|
14
18
|
const lines = [];
|
|
@@ -17,16 +21,16 @@ export function renderHealth({ root, health, error, ms, keyPrefix }, styler = st
|
|
|
17
21
|
if (error) {
|
|
18
22
|
lines.push(bad(t("Gateway connection failed: {error}", { error })));
|
|
19
23
|
} else {
|
|
20
|
-
const isOk =
|
|
24
|
+
const isOk = isHealthy;
|
|
21
25
|
lines.push((isOk(health.status) ? ok : warn)(`${t("Gateway")} ${health.status}${ms != null ? styler.dim(` (${ms}ms)`) : ""}`));
|
|
22
26
|
if (health.db != null) lines.push((isOk(health.db) ? ok : bad)(`DB ${isOk(health.db) ? t("healthy") : health.db}`));
|
|
23
27
|
if (health.redis != null) lines.push((isOk(health.redis) ? ok : bad)(`Redis ${isOk(health.redis) ? t("healthy") : health.redis}`));
|
|
24
28
|
if (health.providers_configured != null) lines.push(ok(t("{n} providers configured", { n: health.providers_configured })));
|
|
25
29
|
const fx = health.fx_rate;
|
|
26
30
|
if (fx != null) {
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
if (rate != null) lines.push(ok(t("FX rate USD/KRW {rate}
|
|
31
|
+
// {rate, applied_rate, source} — 출처(source)는 운영 관측용이라 화면엔 표시하지 않는다.
|
|
32
|
+
const rate = typeof fx === "object" ? fx.rate : fx;
|
|
33
|
+
if (rate != null) lines.push(ok(t("FX rate USD/KRW {rate}", { rate })));
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
lines.push(
|
|
@@ -37,6 +41,23 @@ export function renderHealth({ root, health, error, ms, keyPrefix }, styler = st
|
|
|
37
41
|
return lines.join("\n");
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
// 게이트웨이 응답 대기 상한. 없으면 서버·네트워크가 멈췄을 때 끝없이(79초 실측) 기다린다.
|
|
45
|
+
export const HEALTH_TIMEOUT_MS = 15_000;
|
|
46
|
+
|
|
47
|
+
// /v1/health 1회 조회 → {data, error}. 예외를 던지지 않는다(테스트 대상).
|
|
48
|
+
export async function fetchHealth(url, timeoutMs = HEALTH_TIMEOUT_MS) {
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
51
|
+
if (!res.ok) return { data: null, error: `HTTP ${res.status}` };
|
|
52
|
+
return { data: await res.json(), error: null };
|
|
53
|
+
} catch (e) {
|
|
54
|
+
if (e?.name === "TimeoutError" || e?.name === "AbortError") {
|
|
55
|
+
return { data: null, error: t("No response within {s}s", { s: Math.ceil(timeoutMs / 1000) }) };
|
|
56
|
+
}
|
|
57
|
+
return { data: null, error: e.message };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
40
61
|
export async function health({ exitOnError = true } = {}) {
|
|
41
62
|
const cfg = readConfig();
|
|
42
63
|
const keyPrefix = isLoggedIn(cfg)
|
|
@@ -45,15 +66,7 @@ export async function health({ exitOnError = true } = {}) {
|
|
|
45
66
|
? cfg.key.slice(0, 8)
|
|
46
67
|
: null;
|
|
47
68
|
const t0 = Date.now();
|
|
48
|
-
|
|
49
|
-
let error = null;
|
|
50
|
-
try {
|
|
51
|
-
const res = await fetch(`${V1}/health`);
|
|
52
|
-
if (!res.ok) error = `HTTP ${res.status}`;
|
|
53
|
-
else data = await res.json();
|
|
54
|
-
} catch (e) {
|
|
55
|
-
error = e.message;
|
|
56
|
-
}
|
|
69
|
+
const { data, error } = await fetchHealth(`${V1}/health`);
|
|
57
70
|
const ms = Date.now() - t0;
|
|
58
71
|
console.log(renderHealth({ root: ROOT, health: data, error, ms, keyPrefix }));
|
|
59
72
|
if (error && exitOnError) process.exit(1);
|
package/src/locales/es.mjs
CHANGED
|
@@ -53,7 +53,7 @@ export const ES = {
|
|
|
53
53
|
"Error:": "Error:",
|
|
54
54
|
"Example: pleum launch claude --model claude-sonnet-4": "Ejemplo: pleum launch claude --model claude-sonnet-4",
|
|
55
55
|
"Examples:": "Ejemplos:",
|
|
56
|
-
"FX rate USD/KRW {rate}
|
|
56
|
+
"FX rate USD/KRW {rate}": "Tipo de cambio USD/KRW {rate}",
|
|
57
57
|
"Failed to check balance (HTTP {status})": "No se pudo verificar el saldo (HTTP {status})",
|
|
58
58
|
"Failed to fetch leaderboard ({status})": "No se pudo obtener el ranking ({status})",
|
|
59
59
|
"Failed to fetch model detail ({status})": "No se pudo obtener el detalle del modelo ({status})",
|
|
@@ -80,6 +80,7 @@ export const ES = {
|
|
|
80
80
|
"Aliases: {list}": "Alias: {list}",
|
|
81
81
|
"Gateway": "Gateway",
|
|
82
82
|
"Gateway connection failed: {error}": "Error de conexión al gateway: {error}",
|
|
83
|
+
"No response within {s}s": "Sin respuesta en {s} s",
|
|
83
84
|
"Gateway connectivity · login status": "Conectividad del gateway · estado de la sesión",
|
|
84
85
|
"Goodbye.": "Adiós.",
|
|
85
86
|
"Goose sets its model in its own config — use the openai/<id> format in `goose configure`.": "Goose define su modelo en su propia configuración — usa el formato openai/<id> en `goose configure`.",
|
package/src/locales/ja.mjs
CHANGED
|
@@ -53,7 +53,7 @@ export const JA = {
|
|
|
53
53
|
"Error:": "エラー:",
|
|
54
54
|
"Example: pleum launch claude --model claude-sonnet-4": "例: pleum launch claude --model claude-sonnet-4",
|
|
55
55
|
"Examples:": "例:",
|
|
56
|
-
"FX rate USD/KRW {rate}
|
|
56
|
+
"FX rate USD/KRW {rate}": "為替レート USD/KRW {rate}",
|
|
57
57
|
"Failed to check balance (HTTP {status})": "残高確認に失敗しました (HTTP {status})",
|
|
58
58
|
"Failed to fetch leaderboard ({status})": "リーダーボードの取得に失敗しました ({status})",
|
|
59
59
|
"Failed to fetch model detail ({status})": "モデル詳細の取得に失敗しました ({status})",
|
|
@@ -81,6 +81,7 @@ export const JA = {
|
|
|
81
81
|
"Aliases: {list}": "エイリアス: {list}",
|
|
82
82
|
"Gateway": "ゲートウェイ",
|
|
83
83
|
"Gateway connection failed: {error}": "ゲートウェイ接続に失敗しました: {error}",
|
|
84
|
+
"No response within {s}s": "{s}秒以内に応答がありません",
|
|
84
85
|
"Gateway connectivity · login status": "ゲートウェイ接続 · ログイン状態",
|
|
85
86
|
"Goodbye.": "さようなら。",
|
|
86
87
|
"Goose sets its model in its own config — use the openai/<id> format in `goose configure`.": "Goose は自身の設定でモデルを指定します — `goose configure` で openai/<id> 形式を使用してください。",
|
package/src/locales/ko.mjs
CHANGED
|
@@ -120,10 +120,11 @@ export const KO = {
|
|
|
120
120
|
|
|
121
121
|
// health.mjs
|
|
122
122
|
"Gateway connection failed: {error}": "게이트웨이 연결 실패: {error}",
|
|
123
|
+
"No response within {s}s": "{s}초 안에 응답 없음",
|
|
123
124
|
"Gateway": "게이트웨이",
|
|
124
125
|
"healthy": "정상",
|
|
125
126
|
"{n} providers configured": "providers {n}개 구성됨",
|
|
126
|
-
"FX rate USD/KRW {rate}
|
|
127
|
+
"FX rate USD/KRW {rate}": "환율 USD/KRW {rate}",
|
|
127
128
|
"Logged in": "로그인됨",
|
|
128
129
|
"key": "키",
|
|
129
130
|
"Not logged in": "로그인 안 됨",
|
package/src/locales/zh.mjs
CHANGED
|
@@ -53,7 +53,7 @@ export const ZH = {
|
|
|
53
53
|
"Error:": "错误:",
|
|
54
54
|
"Example: pleum launch claude --model claude-sonnet-4": "示例:pleum launch claude --model claude-sonnet-4",
|
|
55
55
|
"Examples:": "示例:",
|
|
56
|
-
"FX rate USD/KRW {rate}
|
|
56
|
+
"FX rate USD/KRW {rate}": "汇率 USD/KRW {rate}",
|
|
57
57
|
"Failed to check balance (HTTP {status})": "查询余额失败(HTTP {status})",
|
|
58
58
|
"Failed to fetch leaderboard ({status})": "获取排行榜失败({status})",
|
|
59
59
|
"Failed to fetch model detail ({status})": "获取模型详情失败({status})",
|
|
@@ -80,6 +80,7 @@ export const ZH = {
|
|
|
80
80
|
"Aliases: {list}": "别名:{list}",
|
|
81
81
|
"Gateway": "网关",
|
|
82
82
|
"Gateway connection failed: {error}": "网关连接失败:{error}",
|
|
83
|
+
"No response within {s}s": "{s} 秒内无响应",
|
|
83
84
|
"Gateway connectivity · login status": "网关连通性 · 登录状态",
|
|
84
85
|
"Goodbye.": "再见。",
|
|
85
86
|
"Goose sets its model in its own config — use the openai/<id> format in `goose configure`.": "Goose 在自己的配置中设置模型 — 在 `goose configure` 中使用 openai/<id> 格式。",
|