pleumrouter 0.5.27 → 0.5.29
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 +4 -4
- package/bin/pleum.mjs +2 -2
- package/package.json +1 -1
- package/src/balance.mjs +18 -9
- package/src/completion.mjs +15 -6
- package/src/health.mjs +2 -2
- package/src/interactive.mjs +2 -1
- package/src/locales/es.mjs +6 -1
- package/src/locales/ja.mjs +6 -1
- package/src/locales/ko.mjs +6 -1
- package/src/locales/zh.mjs +6 -1
- package/src/menu.mjs +25 -5
- package/src/models.mjs +1 -1
- package/src/run.mjs +1 -1
- package/src/usage.mjs +10 -3
- package/src/wire.mjs +4 -0
package/README.md
CHANGED
|
@@ -41,8 +41,8 @@ SSH/CI에서는 `pleum login --device`를 쓰세요.
|
|
|
41
41
|
| `pleum models [query] [--json]` | 모델 목록(공개) — provider·컨텍스트·가격(₩/1M) 컬럼, `--json`은 스크립트용 |
|
|
42
42
|
| `pleum models <id> --info` | 모델 상세 — 가격·컨텍스트·provider별 지연/처리량/업타임·주간 사용량·형제 모델 |
|
|
43
43
|
| `pleum top [--period daily\|weekly\|monthly]` | 라우터 전체 모델 리더보드 + 제공자 점유율(공개 통계) |
|
|
44
|
-
| `pleum balance` | 크레딧 잔액 조회 |
|
|
45
|
-
| `pleum usage [--by model\|provider\|api_key\|day] [--days N]` | 내 지출 리포트(막대 차트) — OAuth 로그인 세션 필요 |
|
|
44
|
+
| `pleum balance [--switch]` | 크레딧 잔액 조회 · 워크스페이스(개인/팀) 전환 |
|
|
45
|
+
| `pleum usage [--by model\|provider\|api_key\|day\|tag\|user] [--days N]` | 내 지출 리포트(막대 차트) — OAuth 로그인 세션 필요 |
|
|
46
46
|
| `pleum health` | 게이트웨이 연결·DB·Redis·환율·로그인 상태 점검(셀프호스트 진단용) |
|
|
47
47
|
| `pleum lang [code]` | 표시 언어 조회/변경 — `en`(기본)·`ko`·`ja`·`zh`·`es`. 인자 없이 TTY면 화살표로 선택, `PLEUM_LANG` 환경변수로도 덮어쓰기 가능 |
|
|
48
48
|
| `pleum completion <zsh\|bash>` | 셸 자동완성 스크립트 출력 |
|
|
@@ -74,9 +74,9 @@ SSH/CI에서는 `pleum login --device`를 쓰세요.
|
|
|
74
74
|
|
|
75
75
|
## 자동 실행 지원 도구
|
|
76
76
|
|
|
77
|
-
`claude` · `codex` · `aider` · `goose` · `openhands` · `opencode` · `omp` · `hermes` · `qwen` · `copilot` · `gemini` · `agy` · `pi` · `crush` —
|
|
77
|
+
`claude` · `codex` · `aider` · `goose` · `openhands` · `opencode` · `omp` · `hermes` · `qwen` · `copilot` · `gemini` · `agy` · `pi` · `crush` · `minimax` · `mimo` · `openclaw` · `plandex` · `interpreter` —
|
|
78
78
|
env 또는 격리 홈(`CODEX_HOME` / `PI_CODING_AGENT_DIR` / `CRUSH_GLOBAL_CONFIG`)만 세팅하고 바로 실행한다.
|
|
79
|
-
별칭: `antigravity`→`agy`, `picode`→`pi`, `ohmypi`→`omp`.
|
|
79
|
+
별칭: `antigravity`→`agy`, `picode`→`pi`, `ohmypi`/`oh-my-pi`→`omp`, `qwencode`/`qwen-code`→`qwen`, `minimax-code`/`mcode`→`minimax`, `mimocode`/`mimo-code`→`mimo`, `open-interpreter`→`interpreter`.
|
|
80
80
|
|
|
81
81
|
`agy`만 사용자 `~/.gemini/antigravity-cli/settings.json`에 `modelProvider=gemini`를 병합한다(제품이 이 파일 없이 키를 무시함). Kiro는 공식 BYOK가 없어 지원하지 않는다.
|
|
82
82
|
|
package/bin/pleum.mjs
CHANGED
|
@@ -34,8 +34,8 @@ function buildHelp() {
|
|
|
34
34
|
["pleum models [query] [--json]", t("Model list (price, context included)")],
|
|
35
35
|
["pleum models <id> --info", t("Model detail (price, perf, per-provider uptime)")],
|
|
36
36
|
["pleum top [--period weekly]", t("Model leaderboard · provider share (daily|weekly|monthly)")],
|
|
37
|
-
["pleum balance", t("Check your credit balance")],
|
|
38
|
-
["pleum usage [--by model] [--days 30]", t("Your spend report (model|provider|api_key|day)")],
|
|
37
|
+
["pleum balance [--switch]", t("Check your credit balance")],
|
|
38
|
+
["pleum usage [--by model] [--days 30]", t("Your spend report (model|provider|api_key|day|tag|user)")],
|
|
39
39
|
["pleum health", t("Check gateway connectivity and login status")],
|
|
40
40
|
["pleum lang [code]", t("Show/change display language (en|ko|ja|zh|es)")],
|
|
41
41
|
["pleum completion <zsh|bash>", t("Print a shell completion script")],
|
package/package.json
CHANGED
package/src/balance.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export async function fetchCreditSummary() {
|
|
|
14
14
|
const res = await authFetch("/credits");
|
|
15
15
|
if (!res.ok) {
|
|
16
16
|
const body = await res.text().catch(() => "");
|
|
17
|
-
throw new Error(
|
|
17
|
+
throw new Error(t("Failed to fetch balance ({status})", { status: res.status }) + (body ? `: ${body}` : ""));
|
|
18
18
|
}
|
|
19
19
|
const personal = await res.json();
|
|
20
20
|
|
|
@@ -26,24 +26,26 @@ export async function fetchCreditSummary() {
|
|
|
26
26
|
}
|
|
27
27
|
} catch {}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
let teamWallets = [];
|
|
30
30
|
if (Array.isArray(orgs) && orgs.length > 0) {
|
|
31
|
-
await Promise.all(
|
|
31
|
+
const results = await Promise.all(
|
|
32
32
|
orgs.map(async (org) => {
|
|
33
33
|
try {
|
|
34
34
|
const wRes = await authFetch(`/orgs/${org.id}/wallet`);
|
|
35
35
|
if (wRes.ok) {
|
|
36
36
|
const wallet = await wRes.json();
|
|
37
|
-
|
|
37
|
+
return {
|
|
38
38
|
id: String(org.id),
|
|
39
39
|
name: org.name,
|
|
40
40
|
role: org.role,
|
|
41
41
|
balance_krw: wallet.available_krw ?? wallet.balance_krw ?? 0,
|
|
42
|
-
}
|
|
42
|
+
};
|
|
43
43
|
}
|
|
44
44
|
} catch {}
|
|
45
|
+
return null;
|
|
45
46
|
})
|
|
46
47
|
);
|
|
48
|
+
teamWallets = results.filter(Boolean);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
return {
|
|
@@ -92,7 +94,13 @@ export function buildCreditOptions({ personal, teams = [], activeContext = { typ
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
export async function switchCreditWorkspace({ summary, selectFn = select } = {}) {
|
|
95
|
-
|
|
97
|
+
let data;
|
|
98
|
+
try {
|
|
99
|
+
data = summary || (await fetchCreditSummary());
|
|
100
|
+
} catch (e) {
|
|
101
|
+
console.error(`✗ ${e.message}`);
|
|
102
|
+
return getActiveContext();
|
|
103
|
+
}
|
|
96
104
|
const active = getActiveContext();
|
|
97
105
|
const options = buildCreditOptions({
|
|
98
106
|
personal: data.personal,
|
|
@@ -141,13 +149,14 @@ export async function switchCreditWorkspace({ summary, selectFn = select } = {})
|
|
|
141
149
|
return newContext;
|
|
142
150
|
}
|
|
143
151
|
|
|
144
|
-
export async function balance({ interactive = false, switchOnly = false } = {}) {
|
|
152
|
+
export async function balance({ interactive = false, switchOnly = false, exitOnError = true } = {}) {
|
|
145
153
|
let summary;
|
|
146
154
|
try {
|
|
147
155
|
summary = await fetchCreditSummary();
|
|
148
156
|
} catch (e) {
|
|
149
|
-
console.error(e.message);
|
|
150
|
-
process.exit(1);
|
|
157
|
+
console.error(`✗ ${e.message}`);
|
|
158
|
+
if (exitOnError) process.exit(1);
|
|
159
|
+
return;
|
|
151
160
|
}
|
|
152
161
|
|
|
153
162
|
if (switchOnly) {
|
package/src/completion.mjs
CHANGED
|
@@ -14,9 +14,14 @@ _pleum() {
|
|
|
14
14
|
COMPREPLY=( $(compgen -W "${COMMANDS}" -- "$cur") ); return
|
|
15
15
|
fi
|
|
16
16
|
case "$prev" in
|
|
17
|
-
launch) COMPREPLY=( $(compgen -W "${TOOLS}" -- "$cur") );;
|
|
18
|
-
wire|unwire) COMPREPLY=( $(compgen -W "claude codex" -- "$cur") );;
|
|
19
|
-
run)
|
|
17
|
+
launch) COMPREPLY=( $(compgen -W "${TOOLS} --model" -- "$cur") );;
|
|
18
|
+
wire|unwire) COMPREPLY=( $(compgen -W "claude codex --revoke-key" -- "$cur") );;
|
|
19
|
+
run|models) COMPREPLY=( $(compgen -W "$(pleum models 2>/dev/null | awk 'NR>1{print $1}') --json --info" -- "$cur") );;
|
|
20
|
+
balance) COMPREPLY=( $(compgen -W "--switch" -- "$cur") );;
|
|
21
|
+
usage) COMPREPLY=( $(compgen -W "--by --days" -- "$cur") );;
|
|
22
|
+
--by) COMPREPLY=( $(compgen -W "model provider api_key day tag user" -- "$cur") );;
|
|
23
|
+
lang) COMPREPLY=( $(compgen -W "en ko ja zh es" -- "$cur") );;
|
|
24
|
+
top) COMPREPLY=( $(compgen -W "daily weekly monthly --period" -- "$cur") );;
|
|
20
25
|
completion) COMPREPLY=( $(compgen -W "zsh bash" -- "$cur") );;
|
|
21
26
|
esac
|
|
22
27
|
}
|
|
@@ -31,9 +36,13 @@ _pleum() {
|
|
|
31
36
|
compadd ${COMMANDS}; return
|
|
32
37
|
fi
|
|
33
38
|
case \${words[2]} in
|
|
34
|
-
launch) compadd ${TOOLS};;
|
|
35
|
-
wire|unwire) compadd claude codex;;
|
|
36
|
-
run)
|
|
39
|
+
launch) compadd ${TOOLS} --model;;
|
|
40
|
+
wire|unwire) compadd claude codex --revoke-key;;
|
|
41
|
+
run|models) compadd \${(f)"$(pleum models 2>/dev/null | awk 'NR>1{print \$1}')"} --json --info;;
|
|
42
|
+
balance) compadd --switch;;
|
|
43
|
+
usage) compadd --by --days model provider api_key day tag user;;
|
|
44
|
+
lang) compadd en ko ja zh es;;
|
|
45
|
+
top) compadd daily weekly monthly --period;;
|
|
37
46
|
completion) compadd zsh bash;;
|
|
38
47
|
esac
|
|
39
48
|
}
|
package/src/health.mjs
CHANGED
|
@@ -37,7 +37,7 @@ export function renderHealth({ root, health, error, ms, keyPrefix }, styler = st
|
|
|
37
37
|
return lines.join("\n");
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export async function health() {
|
|
40
|
+
export async function health({ exitOnError = true } = {}) {
|
|
41
41
|
const cfg = readConfig();
|
|
42
42
|
const keyPrefix = isLoggedIn(cfg)
|
|
43
43
|
? "oauth"
|
|
@@ -56,5 +56,5 @@ export async function health() {
|
|
|
56
56
|
}
|
|
57
57
|
const ms = Date.now() - t0;
|
|
58
58
|
console.log(renderHealth({ root: ROOT, health: data, error, ms, keyPrefix }));
|
|
59
|
-
if (error) process.exit(1);
|
|
59
|
+
if (error && exitOnError) process.exit(1);
|
|
60
60
|
}
|
package/src/interactive.mjs
CHANGED
|
@@ -155,7 +155,8 @@ export async function select(items, {
|
|
|
155
155
|
if (key.type === "ctrlc" || key.type === "escape") return finish(null);
|
|
156
156
|
if (key.type === "enter") {
|
|
157
157
|
const pool = matches(items, query);
|
|
158
|
-
|
|
158
|
+
const safeCursor = Math.max(0, Math.min(cursor, pool.length - 1));
|
|
159
|
+
return finish(pool[safeCursor] ?? null);
|
|
159
160
|
}
|
|
160
161
|
if (key.type === "up") cursor = Math.max(0, cursor - 1);
|
|
161
162
|
else if (key.type === "down") cursor = Math.min(limit - 1, cursor + 1);
|
package/src/locales/es.mjs
CHANGED
|
@@ -203,7 +203,7 @@ export const ES = {
|
|
|
203
203
|
"Usage: pleum models <model-id> --info": "Uso: pleum models <model-id> --info",
|
|
204
204
|
"Usage: pleum run <model> [\"prompt\"]\nModel list: pleum models": "Uso: pleum run <model> [\"prompt\"]\nLista de modelos: pleum models",
|
|
205
205
|
"Use default (clear)": "Usar predeterminado (borrar)",
|
|
206
|
-
"Your spend report (model|provider|api_key|day)": "Tu informe de gasto (model|provider|api_key|day)",
|
|
206
|
+
"Your spend report (model|provider|api_key|day|tag|user)": "Tu informe de gasto (model|provider|api_key|day|tag|user)",
|
|
207
207
|
"by day": "por día",
|
|
208
208
|
"by key": "por clave",
|
|
209
209
|
"by model": "por modelo",
|
|
@@ -274,5 +274,10 @@ export const ES = {
|
|
|
274
274
|
"Failed to update credit context": "Error al actualizar el contexto de crédito",
|
|
275
275
|
"You are not a member of any team. Using personal credit.": "No eres miembro de ningún equipo. Usando crédito personal.",
|
|
276
276
|
"Run `pleum balance --switch` to switch credit workspace.": "Ejecuta `pleum balance --switch` para cambiar de espacio de crédito.",
|
|
277
|
+
"Example: pleum launch claude --model claude-sonnet-5": "Ejemplo: pleum launch claude --model claude-sonnet-5",
|
|
278
|
+
"supported (reasoning_effort: minimal|none|low|medium|high|xhigh|max)": "compatible (reasoning_effort: minimal|none|low|medium|high|xhigh|max)",
|
|
279
|
+
"by tag": "por etiqueta",
|
|
280
|
+
"by user": "por usuario",
|
|
281
|
+
"Failed to fetch balance ({status})": "Error al obtener el saldo ({status})",
|
|
277
282
|
};
|
|
278
283
|
|
package/src/locales/ja.mjs
CHANGED
|
@@ -203,7 +203,7 @@ export const JA = {
|
|
|
203
203
|
"Usage: pleum unwire [claude|codex] [--revoke-key]\n": "使い方: pleum unwire [claude|codex] [--revoke-key]\n",
|
|
204
204
|
"Usage: pleum wire [claude|codex]\n": "使い方: pleum wire [claude|codex]\n",
|
|
205
205
|
"Use default (clear)": "デフォルトを使用(解除)",
|
|
206
|
-
"Your spend report (model|provider|api_key|day)": "支出レポート (model|provider|api_key|day)",
|
|
206
|
+
"Your spend report (model|provider|api_key|day|tag|user)": "支出レポート (model|provider|api_key|day|tag|user)",
|
|
207
207
|
"by day": "日別",
|
|
208
208
|
"by key": "キー別",
|
|
209
209
|
"by model": "モデル別",
|
|
@@ -274,5 +274,10 @@ export const JA = {
|
|
|
274
274
|
"Failed to update credit context": "クレジットコンテキストの更新に失敗しました",
|
|
275
275
|
"You are not a member of any team. Using personal credit.": "所属しているチームがありません。個人クレジットを使用します。",
|
|
276
276
|
"Run `pleum balance --switch` to switch credit workspace.": "`pleum balance --switch` でクレジットワークスペースを変更できます。",
|
|
277
|
+
"Example: pleum launch claude --model claude-sonnet-5": "例: pleum launch claude --model claude-sonnet-5",
|
|
278
|
+
"supported (reasoning_effort: minimal|none|low|medium|high|xhigh|max)": "対応 (reasoning_effort: minimal|none|low|medium|high|xhigh|max)",
|
|
279
|
+
"by tag": "タグ別",
|
|
280
|
+
"by user": "ユーザー別",
|
|
281
|
+
"Failed to fetch balance ({status})": "残高の取得に失敗しました ({status})",
|
|
277
282
|
};
|
|
278
283
|
|
package/src/locales/ko.mjs
CHANGED
|
@@ -263,7 +263,7 @@ export const KO = {
|
|
|
263
263
|
"Model list (price, context included)": "모델 목록(가격·컨텍스트 포함)",
|
|
264
264
|
"Model detail (price, perf, per-provider uptime)": "모델 상세(가격·성능·provider별 uptime)",
|
|
265
265
|
"Model leaderboard · provider share (daily|weekly|monthly)": "모델 리더보드·제공자 점유율(daily|weekly|monthly)",
|
|
266
|
-
"Your spend report (model|provider|api_key|day)": "내 지출 리포트(model|provider|api_key|day)",
|
|
266
|
+
"Your spend report (model|provider|api_key|day|tag|user)": "내 지출 리포트(model|provider|api_key|day|tag|user)",
|
|
267
267
|
"Check gateway connectivity and login status": "게이트웨이 연결·로그인 상태 점검",
|
|
268
268
|
"Show/change display language (en|ko|ja|zh|es)": "표시 언어 조회/변경 (en|ko|ja|zh|es)",
|
|
269
269
|
"Print a shell completion script": "셸 자동완성 스크립트 출력",
|
|
@@ -337,5 +337,10 @@ export const KO = {
|
|
|
337
337
|
"Failed to update credit context": "크레딧 컨텍스트 변경 실패",
|
|
338
338
|
"You are not a member of any team. Using personal credit.": "소속된 팀이 없습니다. 개인 크레딧을 사용합니다.",
|
|
339
339
|
"Run `pleum balance --switch` to switch credit workspace.": "`pleum balance --switch` 명령으로 크레딧 워크스페이스를 변경할 수 있습니다.",
|
|
340
|
+
"Example: pleum launch claude --model claude-sonnet-5": "예시: pleum launch claude --model claude-sonnet-5",
|
|
341
|
+
"supported (reasoning_effort: minimal|none|low|medium|high|xhigh|max)": "지원됨 (reasoning_effort: minimal|none|low|medium|high|xhigh|max)",
|
|
342
|
+
"by tag": "태그별",
|
|
343
|
+
"by user": "사용자별",
|
|
344
|
+
"Failed to fetch balance ({status})": "잔액 조회 실패 ({status})",
|
|
340
345
|
};
|
|
341
346
|
|
package/src/locales/zh.mjs
CHANGED
|
@@ -203,7 +203,7 @@ export const ZH = {
|
|
|
203
203
|
"Usage: pleum models <model-id> --info": "用法:pleum models <model-id> --info",
|
|
204
204
|
"Usage: pleum run <model> [\"prompt\"]\nModel list: pleum models": "用法:pleum run <model> [\"prompt\"]\n模型列表:pleum models",
|
|
205
205
|
"Use default (clear)": "使用默认值(清除)",
|
|
206
|
-
"Your spend report (model|provider|api_key|day)": "你的花费报告(model|provider|api_key|day)",
|
|
206
|
+
"Your spend report (model|provider|api_key|day|tag|user)": "你的花费报告(model|provider|api_key|day|tag|user)",
|
|
207
207
|
"by day": "按天",
|
|
208
208
|
"by key": "按密钥",
|
|
209
209
|
"by model": "按模型",
|
|
@@ -274,5 +274,10 @@ export const ZH = {
|
|
|
274
274
|
"Failed to update credit context": "更新额度上下文失败",
|
|
275
275
|
"You are not a member of any team. Using personal credit.": "您不属于任何团队。使用个人额度。",
|
|
276
276
|
"Run `pleum balance --switch` to switch credit workspace.": "运行 `pleum balance --switch` 可切换额度工作区。",
|
|
277
|
+
"Example: pleum launch claude --model claude-sonnet-5": "示例:pleum launch claude --model claude-sonnet-5",
|
|
278
|
+
"supported (reasoning_effort: minimal|none|low|medium|high|xhigh|max)": "支持 (reasoning_effort: minimal|none|low|medium|high|xhigh|max)",
|
|
279
|
+
"by tag": "按标签",
|
|
280
|
+
"by user": "按用户",
|
|
281
|
+
"Failed to fetch balance ({status})": "获取余额失败 ({status})",
|
|
277
282
|
};
|
|
278
283
|
|
package/src/menu.mjs
CHANGED
|
@@ -374,19 +374,39 @@ export async function mainMenu() {
|
|
|
374
374
|
return true;
|
|
375
375
|
}
|
|
376
376
|
case "models":
|
|
377
|
-
|
|
377
|
+
try {
|
|
378
|
+
await listModels([]);
|
|
379
|
+
} catch (e) {
|
|
380
|
+
console.error(`✗ ${sanitizeTerminalText(e.message)}`);
|
|
381
|
+
}
|
|
378
382
|
continue;
|
|
379
383
|
case "top":
|
|
380
|
-
|
|
384
|
+
try {
|
|
385
|
+
await top([]);
|
|
386
|
+
} catch (e) {
|
|
387
|
+
console.error(`✗ ${sanitizeTerminalText(e.message)}`);
|
|
388
|
+
}
|
|
381
389
|
continue;
|
|
382
390
|
case "usage":
|
|
383
|
-
|
|
391
|
+
try {
|
|
392
|
+
await usage([]);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
console.error(`✗ ${sanitizeTerminalText(e.message)}`);
|
|
395
|
+
}
|
|
384
396
|
continue;
|
|
385
397
|
case "balance":
|
|
386
|
-
|
|
398
|
+
try {
|
|
399
|
+
await balance({ interactive: true, exitOnError: false });
|
|
400
|
+
} catch (e) {
|
|
401
|
+
console.error(`✗ ${sanitizeTerminalText(e.message)}`);
|
|
402
|
+
}
|
|
387
403
|
continue;
|
|
388
404
|
case "health":
|
|
389
|
-
|
|
405
|
+
try {
|
|
406
|
+
await health({ exitOnError: false });
|
|
407
|
+
} catch (e) {
|
|
408
|
+
console.error(`✗ ${sanitizeTerminalText(e.message)}`);
|
|
409
|
+
}
|
|
390
410
|
continue;
|
|
391
411
|
case "settings": {
|
|
392
412
|
const r = await runSettings();
|
package/src/models.mjs
CHANGED
|
@@ -45,7 +45,7 @@ export function fmtPrice(model) {
|
|
|
45
45
|
if (v == null) return "—";
|
|
46
46
|
const n = Number(v) * 1_000_000;
|
|
47
47
|
if (!Number.isFinite(n)) return "—";
|
|
48
|
-
return sym === "₩" ? `${Math.round(n).toLocaleString("en-US")}
|
|
48
|
+
return sym === "₩" ? `${Math.round(n).toLocaleString("en-US")} ${t("credits")}` : `$${n.toFixed(2)}`;
|
|
49
49
|
};
|
|
50
50
|
const kr = model.pricing_krw || {};
|
|
51
51
|
if (kr.prompt != null || kr.completion != null) return [per1m(kr.prompt, "₩"), per1m(kr.completion, "₩")];
|
package/src/run.mjs
CHANGED
|
@@ -165,7 +165,7 @@ export function buildContent(text, { attachments = [], lstat = lstatSync, read =
|
|
|
165
165
|
// 응답 후 한 줄 통계(순수 함수 — 테스트 대상). verbose일 때만 토큰/속도 표시.
|
|
166
166
|
export function formatStats({ cost, usage, ms, tokPerSec, verbose }) {
|
|
167
167
|
const bits = [];
|
|
168
|
-
if (cost != null && cost !== "") bits.push(`${fmtCost(cost)}
|
|
168
|
+
if (cost != null && cost !== "") bits.push(`${fmtCost(cost)} ${t("credits")}`);
|
|
169
169
|
if (verbose && usage) {
|
|
170
170
|
bits.push(`↑${sanitizeTerminalText(usage.prompt_tokens ?? "?")} ↓${sanitizeTerminalText(usage.completion_tokens ?? "?")} tok`);
|
|
171
171
|
if (tokPerSec) bits.push(`${tokPerSec.toFixed(1)} tok/s`);
|
package/src/usage.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { authFetch } from "./config.mjs";
|
|
|
5
5
|
import { style, padDisplay, barChart, divider } from "./ansi.mjs";
|
|
6
6
|
import { t } from "./i18n.mjs";
|
|
7
7
|
|
|
8
|
-
const GROUPS = ["model", "provider", "api_key", "day"];
|
|
8
|
+
const GROUPS = ["model", "provider", "api_key", "day", "tag", "user"];
|
|
9
9
|
|
|
10
10
|
export function parseUsageArgs(rest = []) {
|
|
11
11
|
let by = "model";
|
|
@@ -21,12 +21,19 @@ export function parseUsageArgs(rest = []) {
|
|
|
21
21
|
|
|
22
22
|
// 원장 눈금 0.1 — Math.round로 자르면 소액 사용이 0으로 보인다.
|
|
23
23
|
const fmtKrw = (n) =>
|
|
24
|
-
`${Number(n).toLocaleString("en-US", { minimumFractionDigits: 1, maximumFractionDigits: 1 })}
|
|
24
|
+
`${Number(n).toLocaleString("en-US", { minimumFractionDigits: 1, maximumFractionDigits: 1 })} ${t("credits")}`;
|
|
25
25
|
|
|
26
26
|
// report 응답 → 표시 문자열(순수 함수 — fixture 테스트 대상).
|
|
27
27
|
export function renderUsage(data, styler = style) {
|
|
28
28
|
const lines = [];
|
|
29
|
-
const byLabel = {
|
|
29
|
+
const byLabel = {
|
|
30
|
+
model: t("by model"),
|
|
31
|
+
provider: t("by provider"),
|
|
32
|
+
api_key: t("by key"),
|
|
33
|
+
day: t("by day"),
|
|
34
|
+
tag: t("by tag"),
|
|
35
|
+
user: t("by user"),
|
|
36
|
+
}[data.group_by] || data.group_by;
|
|
30
37
|
lines.push(`${styler.bold(styler.plum(t("Usage")))} ${styler.dim(`· ${byLabel} · ${t("last {days} days", { days: data.days })}`)}`);
|
|
31
38
|
lines.push(divider(undefined, styler));
|
|
32
39
|
const items = data.items || [];
|
package/src/wire.mjs
CHANGED
|
@@ -15,6 +15,8 @@ const CLAUDE_ENV_KEYS = [
|
|
|
15
15
|
"ANTHROPIC_AUTH_TOKEN",
|
|
16
16
|
"ANTHROPIC_API_KEY",
|
|
17
17
|
"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY",
|
|
18
|
+
"API_TIMEOUT_MS",
|
|
19
|
+
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC",
|
|
18
20
|
];
|
|
19
21
|
|
|
20
22
|
export function wirePaths(home = homedir()) {
|
|
@@ -87,6 +89,8 @@ export function wireClaudeSettings(existingJson, { root, key }) {
|
|
|
87
89
|
ANTHROPIC_AUTH_TOKEN: key,
|
|
88
90
|
ANTHROPIC_API_KEY: key,
|
|
89
91
|
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
|
|
92
|
+
API_TIMEOUT_MS: "3000000",
|
|
93
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
90
94
|
};
|
|
91
95
|
return `${JSON.stringify(obj, null, 2)}\n`;
|
|
92
96
|
}
|