pleumrouter 0.5.32 → 0.5.34
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/bin/pleum.mjs +0 -0
- package/package.json +1 -1
- package/src/interactive.mjs +13 -6
- package/src/locales/es.mjs +7 -8
- package/src/locales/ja.mjs +7 -8
- package/src/locales/ko.mjs +7 -8
- package/src/locales/zh.mjs +7 -8
- package/src/menu.mjs +46 -53
- package/src/recipes.mjs +12 -7
- package/src/wire.mjs +0 -1
package/bin/pleum.mjs
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/src/interactive.mjs
CHANGED
|
@@ -92,6 +92,13 @@ export function keyFeeder() {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
// 문자열의 터미널 줄 수 계산 — 개행 문자(\n)가 있어도 줄 단위로 쪼개어 실제 출력 행 수를 정확히 합산
|
|
96
|
+
export function rowsFor(s, width = terminalWidth()) {
|
|
97
|
+
return String(s)
|
|
98
|
+
.split("\n")
|
|
99
|
+
.reduce((sum, line) => sum + Math.max(1, Math.ceil(displayWidth(line) / Math.max(width, 1))), 0);
|
|
100
|
+
}
|
|
101
|
+
|
|
95
102
|
// 화살표 이동 · 타이핑 필터 · Enter 선택 · Esc/Ctrl+C 취소(null).
|
|
96
103
|
// 비TTY면 즉시 null — 호출자가 기존 번호 피커 등으로 폴백한다.
|
|
97
104
|
// process.exit는 절대 호출하지 않는다(취소 처리는 호출자 몫).
|
|
@@ -112,7 +119,6 @@ export async function select(items, {
|
|
|
112
119
|
let windowStart = 0;
|
|
113
120
|
let lastRows = 0;
|
|
114
121
|
|
|
115
|
-
const rowsFor = (s) => Math.max(1, Math.ceil(displayWidth(s) / Math.max(terminalWidth(), 1)));
|
|
116
122
|
const erase = () => {
|
|
117
123
|
if (!lastRows) return;
|
|
118
124
|
output.write("\r" + (lastRows > 1 ? `\x1b[${lastRows - 1}A` : "") + "\x1b[0J");
|
|
@@ -130,14 +136,14 @@ export async function select(items, {
|
|
|
130
136
|
windowStart = Math.max(0, Math.min(windowStart, Math.max(0, pool.length - limit)));
|
|
131
137
|
|
|
132
138
|
const shown = pool.slice(windowStart, windowStart + limit);
|
|
133
|
-
const
|
|
139
|
+
const rawLines = [
|
|
134
140
|
`${style.plum("?")} ${style.bold(message)} ${style.dim(t("type to filter · ↑↓ move · Enter select · Esc cancel"))}`,
|
|
135
141
|
`${style.gray("›")} ${query}`,
|
|
136
142
|
];
|
|
137
143
|
if (windowStart > 0) {
|
|
138
|
-
|
|
144
|
+
rawLines.push(style.dim(` ${t("↑ {n} more above", { n: windowStart })}`));
|
|
139
145
|
}
|
|
140
|
-
|
|
146
|
+
rawLines.push(
|
|
141
147
|
...shown.map((it, idx) => {
|
|
142
148
|
const itemIndex = windowStart + idx;
|
|
143
149
|
return itemIndex === cursor
|
|
@@ -147,10 +153,11 @@ export async function select(items, {
|
|
|
147
153
|
);
|
|
148
154
|
const remainingBelow = pool.length - (windowStart + shown.length);
|
|
149
155
|
if (remainingBelow > 0) {
|
|
150
|
-
|
|
156
|
+
rawLines.push(style.dim(` ${t("… {n} more — type to narrow down", { n: remainingBelow })}`));
|
|
151
157
|
}
|
|
152
|
-
if (!pool.length)
|
|
158
|
+
if (!pool.length) rawLines.push(style.dim(` ${t("No matches")}`));
|
|
153
159
|
erase();
|
|
160
|
+
const lines = rawLines.flatMap((l) => String(l).split("\n"));
|
|
154
161
|
const frame = lines.join("\n");
|
|
155
162
|
output.write(frame);
|
|
156
163
|
lastRows = lines.reduce((n, l) => n + rowsFor(l), 0);
|
package/src/locales/es.mjs
CHANGED
|
@@ -308,14 +308,13 @@ export const ES = {
|
|
|
308
308
|
"Update completed! Please run `pleum` again.": "¡Actualización completada! Ejecute `pleum` de nuevo.",
|
|
309
309
|
|
|
310
310
|
// Claude Code Tier Mapping
|
|
311
|
-
"
|
|
312
|
-
"
|
|
313
|
-
"
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"Subagent model (Haiku tier)": "Modelo de subagente (nivel Haiku)",
|
|
311
|
+
"Sonnet model": "Modelo Sonnet",
|
|
312
|
+
"Opus model": "Modelo Opus",
|
|
313
|
+
"Haiku model": "Modelo Haiku",
|
|
314
|
+
"Use existing model configuration?": "¿Continuar con la configuración de modelos existente?",
|
|
315
|
+
"Keep existing configuration": "Continuar con la configuración existente",
|
|
316
|
+
"Reconfigure tiers": "Reconfigurar modelos",
|
|
317
|
+
"Choose Sonnet, Opus, and Haiku models again": "Volver a elegir los modelos Sonnet, Opus y Haiku",
|
|
319
318
|
|
|
320
319
|
// Codex & agy Effort
|
|
321
320
|
"Select reasoning effort": "Seleccionar esfuerzo de razonamiento",
|
package/src/locales/ja.mjs
CHANGED
|
@@ -308,14 +308,13 @@ export const JA = {
|
|
|
308
308
|
"Update completed! Please run `pleum` again.": "アップデート完了!`pleum` を再起動してください。",
|
|
309
309
|
|
|
310
310
|
// Claude Code Tier Mapping
|
|
311
|
-
"
|
|
312
|
-
"
|
|
313
|
-
"
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"Subagent model (Haiku tier)": "サブエージェントモデル (Haiku ティア)",
|
|
311
|
+
"Sonnet model": "Sonnet モデル",
|
|
312
|
+
"Opus model": "Opus モデル",
|
|
313
|
+
"Haiku model": "Haiku モデル",
|
|
314
|
+
"Use existing model configuration?": "既存のモデル設定で続行しますか?",
|
|
315
|
+
"Keep existing configuration": "既存の設定のまま続行",
|
|
316
|
+
"Reconfigure tiers": "モデルを再設定",
|
|
317
|
+
"Choose Sonnet, Opus, and Haiku models again": "Sonnet、Opus、Haiku モデルを再選択",
|
|
319
318
|
|
|
320
319
|
// Codex & agy Effort
|
|
321
320
|
"Select reasoning effort": "推論強度 (effort) の選択",
|
package/src/locales/ko.mjs
CHANGED
|
@@ -371,14 +371,13 @@ export const KO = {
|
|
|
371
371
|
"Update completed! Please run `pleum` again.": "업데이트 완료! `pleum`을 다시 실행하세요.",
|
|
372
372
|
|
|
373
373
|
// Claude Code Tier Mapping
|
|
374
|
-
"
|
|
375
|
-
"
|
|
376
|
-
"
|
|
377
|
-
"
|
|
378
|
-
"
|
|
379
|
-
"
|
|
380
|
-
"
|
|
381
|
-
"Subagent model (Haiku tier)": "서브에이전트 모델 (Haiku 티어)",
|
|
374
|
+
"Sonnet model": "Sonnet 모델",
|
|
375
|
+
"Opus model": "Opus 모델",
|
|
376
|
+
"Haiku model": "Haiku 모델",
|
|
377
|
+
"Use existing model configuration?": "기존 모델 설정대로 진행하시겠습니까?",
|
|
378
|
+
"Keep existing configuration": "기존 설정대로 진행",
|
|
379
|
+
"Reconfigure tiers": "모델 다시 설정",
|
|
380
|
+
"Choose Sonnet, Opus, and Haiku models again": "Sonnet, Opus, Haiku 모델 새로 지정",
|
|
382
381
|
|
|
383
382
|
// Codex & agy Effort
|
|
384
383
|
"Select reasoning effort": "추론 강도(effort) 선택",
|
package/src/locales/zh.mjs
CHANGED
|
@@ -308,14 +308,13 @@ export const ZH = {
|
|
|
308
308
|
"Update completed! Please run `pleum` again.": "更新完成!请重新运行 `pleum`。",
|
|
309
309
|
|
|
310
310
|
// Claude Code Tier Mapping
|
|
311
|
-
"
|
|
312
|
-
"
|
|
313
|
-
"
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"Subagent model (Haiku tier)": "子代理模型 (Haiku 层级)",
|
|
311
|
+
"Sonnet model": "Sonnet 模型",
|
|
312
|
+
"Opus model": "Opus 模型",
|
|
313
|
+
"Haiku model": "Haiku 模型",
|
|
314
|
+
"Use existing model configuration?": "使用现有的模型配置继续吗?",
|
|
315
|
+
"Keep existing configuration": "保持现有配置继续",
|
|
316
|
+
"Reconfigure tiers": "重新配置模型",
|
|
317
|
+
"Choose Sonnet, Opus, and Haiku models again": "重新选择 Sonnet、Opus 和 Haiku 模型",
|
|
319
318
|
|
|
320
319
|
// Codex & agy Effort
|
|
321
320
|
"Select reasoning effort": "选择推理强度 (effort)",
|
package/src/menu.mjs
CHANGED
|
@@ -201,7 +201,7 @@ export async function pickModelFor(item, defaultModel) {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
export async function pickClaudeCodeTiers(agentItem) {
|
|
204
|
+
export async function pickClaudeCodeTiers(agentItem, { input = process.stdin, output = process.stdout } = {}) {
|
|
205
205
|
const cfg = readConfig() ?? {};
|
|
206
206
|
const savedTiers = cfg.claude_tiers;
|
|
207
207
|
const defaultTiers = {
|
|
@@ -209,69 +209,58 @@ export async function pickClaudeCodeTiers(agentItem) {
|
|
|
209
209
|
opus: "claude-opus-5-5",
|
|
210
210
|
haiku: DEFAULT_MODEL,
|
|
211
211
|
};
|
|
212
|
+
const baseTiers = {
|
|
213
|
+
sonnet: savedTiers?.sonnet || defaultTiers.sonnet,
|
|
214
|
+
opus: savedTiers?.opus || defaultTiers.opus,
|
|
215
|
+
haiku: savedTiers?.haiku || defaultTiers.haiku,
|
|
216
|
+
};
|
|
212
217
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
id
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
const picked = await select(options, {
|
|
243
|
-
message: `${agentItem.title} — ${t("Select model tier mapping")}`,
|
|
244
|
-
formatItem: (x) => `${x.title}\n ${style.dim(x.desc)}`,
|
|
245
|
-
limit: 5,
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
if (!picked) return null;
|
|
249
|
-
|
|
250
|
-
if (picked.tiers) {
|
|
251
|
-
cfg.claude_tiers = picked.tiers;
|
|
252
|
-
writeConfig(cfg);
|
|
253
|
-
return picked.tiers;
|
|
218
|
+
// 기존 설정이 존재하면 1-클릭 확인 메뉴: Enter 1회로 기존 설정대로 즉시 실행
|
|
219
|
+
if (savedTiers && savedTiers.sonnet && savedTiers.opus && savedTiers.haiku) {
|
|
220
|
+
const keepChoice = await select(
|
|
221
|
+
[
|
|
222
|
+
{
|
|
223
|
+
id: "keep",
|
|
224
|
+
title: t("Keep existing configuration"),
|
|
225
|
+
desc: `Sonnet: ${savedTiers.sonnet} · Opus: ${savedTiers.opus} · Haiku: ${savedTiers.haiku}`,
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: "change",
|
|
229
|
+
title: t("Reconfigure tiers"),
|
|
230
|
+
desc: t("Choose Sonnet, Opus, and Haiku models again"),
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
{
|
|
234
|
+
message: `${agentItem.title} — ${t("Use existing model configuration?")}`,
|
|
235
|
+
formatItem: (x) => `${x.title} ${style.dim(x.desc)}`,
|
|
236
|
+
limit: 5,
|
|
237
|
+
input,
|
|
238
|
+
output,
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
if (!keepChoice) return null;
|
|
242
|
+
if (keepChoice.id === "keep") {
|
|
243
|
+
return savedTiers;
|
|
244
|
+
}
|
|
254
245
|
}
|
|
255
246
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// Step 1: Sonnet (Main)
|
|
247
|
+
// 1/3단계: Sonnet 모델 지정 (10개 핵심 추천 + 200+ 전체 검색 + 프로바이더 라우팅)
|
|
259
248
|
const sonnet = await pickModelFor(
|
|
260
|
-
{ title: `${agentItem.title} — ${t("
|
|
249
|
+
{ title: `${agentItem.title} (1/3) — ${t("Sonnet model")}` },
|
|
261
250
|
baseTiers.sonnet,
|
|
262
251
|
);
|
|
263
252
|
if (!sonnet) return null;
|
|
264
253
|
|
|
265
|
-
//
|
|
254
|
+
// 2/3단계: Opus 모델 지정 (10개 핵심 추천 + 200+ 전체 검색 + 프로바이더 라우팅)
|
|
266
255
|
const opus = await pickModelFor(
|
|
267
|
-
{ title: `${agentItem.title} — ${t("
|
|
256
|
+
{ title: `${agentItem.title} (2/3) — ${t("Opus model")}` },
|
|
268
257
|
baseTiers.opus,
|
|
269
258
|
);
|
|
270
259
|
if (!opus) return null;
|
|
271
260
|
|
|
272
|
-
//
|
|
261
|
+
// 3/3단계: Haiku 모델 지정 (10개 핵심 추천 + 200+ 전체 검색 + 프로바이더 라우팅)
|
|
273
262
|
const haiku = await pickModelFor(
|
|
274
|
-
{ title: `${agentItem.title} — ${t("
|
|
263
|
+
{ title: `${agentItem.title} (3/3) — ${t("Haiku model")}` },
|
|
275
264
|
baseTiers.haiku,
|
|
276
265
|
);
|
|
277
266
|
if (!haiku) return null;
|
|
@@ -359,7 +348,10 @@ export async function runList(items, { header, footer, initialCursor = 0 } = {})
|
|
|
359
348
|
let windowStart = 0;
|
|
360
349
|
let lastRows = 0;
|
|
361
350
|
|
|
362
|
-
const rowsFor = (s) =>
|
|
351
|
+
const rowsFor = (s) =>
|
|
352
|
+
String(s)
|
|
353
|
+
.split("\n")
|
|
354
|
+
.reduce((sum, line) => sum + Math.max(1, Math.ceil(displayWidth(line) / Math.max(terminalWidth(), 1))), 0);
|
|
363
355
|
const erase = () => {
|
|
364
356
|
if (!lastRows) return;
|
|
365
357
|
output.write("\r" + (lastRows > 1 ? `\x1b[${lastRows - 1}A` : "") + "\x1b[0J");
|
|
@@ -411,8 +403,9 @@ export async function runList(items, { header, footer, initialCursor = 0 } = {})
|
|
|
411
403
|
lines.push(style.dim(footer));
|
|
412
404
|
|
|
413
405
|
erase();
|
|
414
|
-
|
|
415
|
-
|
|
406
|
+
const frameLines = lines.flatMap((l) => String(l).split("\n"));
|
|
407
|
+
output.write(frameLines.join("\n"));
|
|
408
|
+
lastRows = frameLines.reduce((n, l) => n + rowsFor(l), 0);
|
|
416
409
|
};
|
|
417
410
|
|
|
418
411
|
const move = (dir) => {
|
package/src/recipes.mjs
CHANGED
|
@@ -52,19 +52,24 @@ export const RECIPES = {
|
|
|
52
52
|
ANTHROPIC_BASE_URL: ROOT,
|
|
53
53
|
ANTHROPIC_API_KEY: key,
|
|
54
54
|
ANTHROPIC_AUTH_TOKEN: key,
|
|
55
|
-
//
|
|
56
|
-
|
|
55
|
+
// CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY는 의도적으로 설정하지 않는다:
|
|
56
|
+
// 활성화 시 Claude Code가 GET /v1/models를 호출해 카탈로그 200+개 모델을
|
|
57
|
+
// /model 메뉴에 쏟아부어 화면이 난잡해지는 원인이 된다.
|
|
57
58
|
ANTHROPIC_DEFAULT_SONNET_MODEL: toAnthropic(sonnet),
|
|
59
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: "Sonnet",
|
|
60
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION: "Sonnet 5 · Efficient for routine tasks",
|
|
58
61
|
ANTHROPIC_DEFAULT_OPUS_MODEL: toAnthropic(opus),
|
|
62
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: "Opus",
|
|
63
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION: "Opus 5.5 · Best for everyday, complex tasks",
|
|
59
64
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: toAnthropic(haiku),
|
|
65
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME: "Haiku",
|
|
66
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION: "Haiku 4.5 · Fastest for quick answers",
|
|
60
67
|
ANTHROPIC_SMALL_MODEL: toAnthropic(haiku),
|
|
61
68
|
};
|
|
62
69
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return ["--model", main.startsWith("anthropic/") ? main : `anthropic/${main}`];
|
|
67
|
-
},
|
|
70
|
+
// CLI 인자에 임의의 --model을 넘기지 않고 Claude Code의 내장 티어(Default/Opus)로 기동하게 하여
|
|
71
|
+
// /model 화면에 깨끗한 5개 표준 항목(Default, Opus, Fable, Sonnet, Haiku)만 표시되도록 한다.
|
|
72
|
+
args: () => [],
|
|
68
73
|
},
|
|
69
74
|
codex: {
|
|
70
75
|
label: "Codex CLI",
|
package/src/wire.mjs
CHANGED
|
@@ -88,7 +88,6 @@ export function wireClaudeSettings(existingJson, { root, key }) {
|
|
|
88
88
|
ANTHROPIC_BASE_URL: root,
|
|
89
89
|
ANTHROPIC_AUTH_TOKEN: key,
|
|
90
90
|
ANTHROPIC_API_KEY: key,
|
|
91
|
-
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
|
|
92
91
|
API_TIMEOUT_MS: "3000000",
|
|
93
92
|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
94
93
|
};
|