pleumrouter 0.5.22 → 0.5.24

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
@@ -74,11 +74,11 @@ SSH/CI에서는 `pleum login --device`를 쓰세요.
74
74
 
75
75
  ## 자동 실행 지원 도구
76
76
 
77
- `claude` · `aider` · `codex` · `goose` · `openhands` · `opencode` · `omp` · `hermes` · `qwen` · `copilot` —
78
- env(또는 격리된 `CODEX_HOME`/임시 설정 문자열)만 세팅하고 바로 실행한다. 사용자의 기존 설정 파일은
79
- 건드리지 않는다.
77
+ `claude` · `codex` · `aider` · `goose` · `openhands` · `opencode` · `omp` · `hermes` · `qwen` · `copilot` · `gemini` · `agy` · `pi` · `crush` —
78
+ env 또는 격리 홈(`CODEX_HOME` / `PI_CODING_AGENT_DIR` / `CRUSH_GLOBAL_CONFIG`)만 세팅하고 바로 실행한다.
79
+ 별칭: `antigravity`→`agy`, `picode`→`pi`, `ohmypi`→`omp`.
80
80
 
81
- `crush` 는 provider 정의가 파일에 필요해 설정 스니펫만 안내한다.
81
+ `agy`만 사용자 `~/.gemini/antigravity-cli/settings.json`에 `modelProvider=gemini`를 병합한다(제품이 이 파일 없이 키를 무시함). Kiro는 공식 BYOK가 없어 지원하지 않는다.
82
82
 
83
83
  ## 환경변수
84
84
 
@@ -97,7 +97,7 @@ npm link # 로컬에서 pleum 명령 노출
97
97
 
98
98
  1. `npm version patch` (또는 minor/major)로 `package.json` bump 후 커밋.
99
99
  2. `git tag cli-v$(node -p "require('./package.json').version")` → `git push origin HEAD --tags`.
100
- 3. 워크플로가 `secrets.NPM_TOKEN`으로 `npm publish --provenance` 실행.
100
+ 3. 워크플로가 `secrets.NPM_TOKEN`으로 `npm publish` 실행. (private GitHub repo라 `--provenance` 불가)
101
101
 
102
102
  태그 규칙: `cli-v{semver}` (예: `cli-v0.5.15`).
103
103
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pleumrouter",
3
- "version": "0.5.22",
3
+ "version": "0.5.24",
4
4
  "description": "Official PleumRouter terminal CLI — launch any coding agent against PleumRouter.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,10 +1,10 @@
1
1
  // Shell 자동완성 스크립트 출력. 명령·도구는 정적, 모델명은 `pleum models`에서 동적으로 채운다.
2
2
  // 설치: pleum completion zsh >> ~/.zshrc · pleum completion bash >> ~/.bashrc
3
3
  import { t } from "./i18n.mjs";
4
- import { RECIPES, MANUAL } from "./recipes.mjs";
4
+ import { launchToolNames } from "./recipes.mjs";
5
5
 
6
6
  const COMMANDS = "login logout lang launch wire unwire run models top balance usage health version upgrade completion help";
7
- const TOOLS = [...Object.keys(RECIPES), ...Object.keys(MANUAL)].join(" ");
7
+ const TOOLS = launchToolNames().join(" ");
8
8
 
9
9
  function bash() {
10
10
  return `# pleum bash completion — eval "$(pleum completion bash)" (${t("or add to ~/.bashrc")})
package/src/launch.mjs CHANGED
@@ -3,7 +3,16 @@ import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join, delimiter, dirname, resolve } from "node:path";
5
5
  import { requireAuth } from "./config.mjs";
6
- import { RECIPES, MANUAL, codexToml } from "./recipes.mjs";
6
+ import {
7
+ RECIPES,
8
+ MANUAL,
9
+ LAUNCH_ALIASES,
10
+ resolveLaunchTool,
11
+ codexToml,
12
+ piModelsJson,
13
+ crushJson,
14
+ mergeAgySettings,
15
+ } from "./recipes.mjs";
7
16
  import { t } from "./i18n.mjs";
8
17
  import { sanitizeTerminalText } from "./ansi.mjs";
9
18
 
@@ -72,28 +81,35 @@ export function launchHelp() {
72
81
  const ok = onPath(r.bin) ? t("✓ installed") : `${t("✗ not installed —")} ${r.install}`;
73
82
  console.log(` ${name.padEnd(11)} ${r.label.padEnd(13)} ${ok}`);
74
83
  }
75
- console.log(`\n${t("Manual setup (needs a provider definition file):")}`);
76
- for (const [name, m] of Object.entries(MANUAL)) {
77
- const ok = onPath(m.bin) ? t("✓ installed") : `${t("✗ not installed —")} ${m.install}`;
78
- console.log(` ${name.padEnd(11)} ${m.label.padEnd(13)} ${ok}`);
84
+ if (Object.keys(MANUAL).length) {
85
+ console.log(`\n${t("Manual setup (needs a provider definition file):")}`);
86
+ for (const [name, m] of Object.entries(MANUAL)) {
87
+ const ok = onPath(m.bin) ? t("✓ installed") : `${t("✗ not installed —")} ${m.install}`;
88
+ console.log(` ${name.padEnd(11)} ${m.label.padEnd(13)} ${ok}`);
89
+ }
79
90
  }
91
+ const aliases = Object.entries(LAUNCH_ALIASES).map(([from, to]) => `${from} → ${to}`).join(", ");
92
+ console.log(`\n${t("Aliases: {list}", { list: aliases })}`);
80
93
  console.log(`\n${t("Example: pleum launch claude --model claude-sonnet-5")}`);
81
94
  }
82
95
 
83
96
  // exit:false면 process.exit 대신 자식 종료 코드를 반환 — 런처 메뉴의 실행 후 복귀용.
84
97
  export async function launch(tool, model, passthrough, { exit = true } = {}) {
85
98
  const key = await requireAuth();
86
- const r = RECIPES[tool];
99
+ const resolved = resolveLaunchTool(tool);
100
+ const r = RECIPES[resolved];
87
101
 
88
102
  if (!r) {
89
- if (MANUAL[tool]) {
90
- printManual(tool, key);
103
+ if (MANUAL[resolved]) {
104
+ printManual(resolved, key);
91
105
  return;
92
106
  }
107
+ const aliases = Object.entries(LAUNCH_ALIASES).map(([from, to]) => `${from} → ${to}`).join(", ");
93
108
  console.error(
94
109
  t("Unsupported tool: {tool}", { tool }) +
95
110
  `\n ${t("Auto-run:")} ${Object.keys(RECIPES).join(", ")}` +
96
- `\n ${t("Manual setup:")} ${Object.keys(MANUAL).join(", ")}`,
111
+ (Object.keys(MANUAL).length ? `\n ${t("Manual setup:")} ${Object.keys(MANUAL).join(", ")}` : "") +
112
+ `\n ${t("Aliases: {list}", { list: aliases })}`,
97
113
  );
98
114
  process.exit(1);
99
115
  }
@@ -102,11 +118,15 @@ export async function launch(tool, model, passthrough, { exit = true } = {}) {
102
118
 
103
119
  // Codex는 격리 CODEX_HOME에 config.toml을 써서 사용자 ~/.codex를 보존한다.
104
120
  if (r.type === "codex-home") {
105
- const home = join(homedir(), ".pleum", "codex-home");
106
- mkdirSync(home, { recursive: true, mode: 0o700 }); // 사용자 데이터 디렉토리 권한 제한(L11)
107
- writeFileSync(join(home, "config.toml"), codexToml(model), { mode: 0o600 });
108
- env.CODEX_HOME = home;
121
+ env.CODEX_HOME = writeIsolatedHome("codex-home", "config.toml", codexToml(model));
122
+ }
123
+ if (r.type === "pi-home") {
124
+ env.PI_CODING_AGENT_DIR = writeIsolatedHome("pi-home", "models.json", piModelsJson(model));
109
125
  }
126
+ if (r.type === "crush-home") {
127
+ env.CRUSH_GLOBAL_CONFIG = writeIsolatedHome("crush-home", "crush.json", crushJson(model));
128
+ }
129
+ if (r.type === "agy-settings") ensureAgySettings();
110
130
 
111
131
  const args = [...r.args(model), ...passthrough];
112
132
  if (r.note) console.log(`ℹ ${t(r.note)}`);
@@ -144,6 +164,21 @@ export async function launch(tool, model, passthrough, { exit = true } = {}) {
144
164
  });
145
165
  }
146
166
 
167
+ function writeIsolatedHome(name, filename, contents) {
168
+ const home = join(homedir(), ".pleum", name);
169
+ mkdirSync(home, { recursive: true, mode: 0o700 });
170
+ writeFileSync(join(home, filename), contents, { mode: 0o600 });
171
+ return home;
172
+ }
173
+
174
+ function ensureAgySettings() {
175
+ const dir = join(homedir(), ".gemini", "antigravity-cli");
176
+ const path = join(dir, "settings.json");
177
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
178
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
179
+ writeFileSync(path, `${JSON.stringify(mergeAgySettings(existing), null, 2)}\n`, { mode: 0o600 });
180
+ }
181
+
147
182
  function printManual(tool, key) {
148
183
  const m = MANUAL[tool];
149
184
  console.log(`${t("{label} needs a provider definition in a file, so this only shows setup — it won't auto-run.", { label: m.label })}\n`);
@@ -20,6 +20,15 @@ export const ES = {
20
20
  "Nous Research's general-purpose agent": "Agente de propósito general de Nous Research",
21
21
  "Alibaba's terminal coding agent": "Agente de codificación de terminal de Alibaba",
22
22
  "GitHub's terminal coding agent": "Agente de codificación de terminal de GitHub",
23
+ "Google's Gemini CLI": "CLI de Gemini de Google",
24
+ "Google Antigravity CLI (agy)": "CLI de Google Antigravity (agy)",
25
+ "Pi coding agent (picode is a Pi package)": "Agente Pi (picode es un paquete de Pi)",
26
+ "Charmbracelet terminal coding agent": "Agente de terminal Charmbracelet",
27
+ "MiniMax's autonomous coding agent": "Agente de codificación autónomo de MiniMax",
28
+ "Xiaomi's terminal coding agent with persistent memory": "Agente de codificación de terminal de Xiaomi con memoria persistente",
29
+ "Autonomous personal AI coding agent": "Agente personal autónomo de codificación por IA",
30
+ "Terminal AI coding engine for complex tasks": "Motor de codificación de IA en terminal para tareas complejas",
31
+ "Local code execution & pair programmer": "Ejecución de código local y programador en pareja",
23
32
  "Available: {list}": "Disponibles: {list}",
24
33
  "Balance": "Saldo",
25
34
  "Balance is below the auto-charge threshold ({threshold} ₩).": "El saldo está por debajo del umbral de recarga automática ({threshold} ₩).",
@@ -62,6 +71,13 @@ export const ES = {
62
71
  "Necesita GitHub Copilot CLI 2026-04 o posterior para el enrutamiento de proveedores BYOK.",
63
72
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
64
73
  "Gemini CLI usa el formato nativo generateContent de Google — establece el modelo tras el inicio con `/model google/<id>`.",
74
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
75
+ "Escribe modelProvider=gemini en ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY sola se ignora. Solo familia Gemini.",
76
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
77
+ "Usa un ~/.pleum/pi-home aislado (PI_CODING_AGENT_DIR). No toca ~/.pi.",
78
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
79
+ "Usa un ~/.pleum/crush-home aislado (CRUSH_GLOBAL_CONFIG). No toca crush.json.",
80
+ "Aliases: {list}": "Alias: {list}",
65
81
  "Gateway": "Gateway",
66
82
  "Gateway connection failed: {error}": "Error de conexión al gateway: {error}",
67
83
  "Gateway connectivity · login status": "Conectividad del gateway · estado de la sesión",
@@ -20,6 +20,15 @@ export const JA = {
20
20
  "Nous Research's general-purpose agent": "Nous Research の汎用エージェント",
21
21
  "Alibaba's terminal coding agent": "Alibaba のターミナルコーディングエージェント",
22
22
  "GitHub's terminal coding agent": "GitHub のターミナルコーディングエージェント",
23
+ "Google's Gemini CLI": "Google Gemini CLI",
24
+ "Google Antigravity CLI (agy)": "Google Antigravity CLI (agy)",
25
+ "Pi coding agent (picode is a Pi package)": "Pi コーディングエージェント (picode は Pi パッケージ)",
26
+ "Charmbracelet terminal coding agent": "Charmbracelet ターミナルコーディングエージェント",
27
+ "MiniMax's autonomous coding agent": "MiniMax の自律型コーディングエージェント",
28
+ "Xiaomi's terminal coding agent with persistent memory": "永続メモリを搭載した Xiaomi のターミナルコーディングエージェント",
29
+ "Autonomous personal AI coding agent": "個人向け自律型 AI コーディングエージェント",
30
+ "Terminal AI coding engine for complex tasks": "複雑なタスク向けターミナル AI コーディングエンジン",
31
+ "Local code execution & pair programmer": "ローカルコード実行&ペアプログラマー",
23
32
  "Available: {list}": "利用可能: {list}",
24
33
  "Balance": "残高",
25
34
  "Balance is below the auto-charge threshold ({threshold} ₩).": "残高が自動チャージのしきい値 ({threshold} ₩) を下回っています。",
@@ -63,6 +72,13 @@ export const JA = {
63
72
  "BYOK プロバイダールーティングに対応した 2026-04 以降の GitHub Copilot CLI が必要です。",
64
73
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
65
74
  "Gemini CLI は Google のネイティブ generateContent 形式を使用します — 起動後に `/model google/<id>` でモデルを設定してください。",
75
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
76
+ "~/.gemini/antigravity-cli/settings.json に modelProvider=gemini を書き込みます。GEMINI_API_KEY だけでは無視されます。Gemini 系のみです。",
77
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
78
+ "隔離した ~/.pleum/pi-home (PI_CODING_AGENT_DIR) だけを使います。~/.pi は触れません。",
79
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
80
+ "隔離した ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG) だけを使います。crush.json は触れません。",
81
+ "Aliases: {list}": "エイリアス: {list}",
66
82
  "Gateway": "ゲートウェイ",
67
83
  "Gateway connection failed: {error}": "ゲートウェイ接続に失敗しました: {error}",
68
84
  "Gateway connectivity · login status": "ゲートウェイ接続 · ログイン状態",
@@ -52,6 +52,13 @@ export const KO = {
52
52
  "BYOK provider 라우팅을 지원하는 2026-04 이후 버전의 GitHub Copilot CLI가 필요합니다.",
53
53
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
54
54
  "Gemini CLI는 Google의 네이티브 generateContent 포맷을 사용합니다 — 실행 후 `/model google/<id>`로 모델을 설정하세요.",
55
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
56
+ "~/.gemini/antigravity-cli/settings.json 에 modelProvider=gemini 를 씁니다. GEMINI_API_KEY만 있으면 무시됩니다. Gemini 계열만 됩니다.",
57
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
58
+ "격리된 ~/.pleum/pi-home (PI_CODING_AGENT_DIR)만 씁니다. ~/.pi는 건드리지 않습니다.",
59
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
60
+ "격리된 ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG)만 씁니다. crush.json은 건드리지 않습니다.",
61
+ "Aliases: {list}": "별칭: {list}",
55
62
 
56
63
  // completion.mjs
57
64
  "Usage: pleum completion <zsh|bash>": "사용법: pleum completion <zsh|bash>",
@@ -146,6 +153,15 @@ export const KO = {
146
153
  "Nous Research's general-purpose agent": "Nous Research의 범용 에이전트",
147
154
  "Alibaba's terminal coding agent": "Alibaba의 터미널 코딩 에이전트",
148
155
  "GitHub's terminal coding agent": "GitHub의 터미널 코딩 에이전트",
156
+ "Google's Gemini CLI": "Google Gemini CLI",
157
+ "Google Antigravity CLI (agy)": "Google Antigravity CLI (agy)",
158
+ "Pi coding agent (picode is a Pi package)": "Pi 코딩 에이전트 (picode는 Pi 패키지)",
159
+ "Charmbracelet terminal coding agent": "Charmbracelet 터미널 코딩 에이전트",
160
+ "MiniMax's autonomous coding agent": "MiniMax의 자율 코딩 에이전트",
161
+ "Xiaomi's terminal coding agent with persistent memory": "영구 메모리를 탑재한 Xiaomi의 터미널 코딩 에이전트",
162
+ "Autonomous personal AI coding agent": "개인용 자율 AI 코딩 에이전트",
163
+ "Terminal AI coding engine for complex tasks": "복잡한 작업을 위한 터미널 AI 코딩 엔진",
164
+ "Local code execution & pair programmer": "로컬 코드 실행 및 페어 프로그래머",
149
165
  "Log in": "로그인",
150
166
  "Log in via browser and save the API key": "브라우저로 로그인하고 API 키를 저장합니다",
151
167
  "Chat with a model": "모델과 채팅",
@@ -20,6 +20,15 @@ export const ZH = {
20
20
  "Nous Research's general-purpose agent": "Nous Research 的通用代理",
21
21
  "Alibaba's terminal coding agent": "阿里巴巴的终端编程代理",
22
22
  "GitHub's terminal coding agent": "GitHub 的终端编程代理",
23
+ "Google's Gemini CLI": "Google Gemini CLI",
24
+ "Google Antigravity CLI (agy)": "Google Antigravity CLI (agy)",
25
+ "Pi coding agent (picode is a Pi package)": "Pi 编程代理(picode 是 Pi 包)",
26
+ "Charmbracelet terminal coding agent": "Charmbracelet 终端编程代理",
27
+ "MiniMax's autonomous coding agent": "MiniMax 自主编程代理",
28
+ "Xiaomi's terminal coding agent with persistent memory": "带持久记忆的 Xiaomi 终端编程代理",
29
+ "Autonomous personal AI coding agent": "个人自主 AI 编程代理",
30
+ "Terminal AI coding engine for complex tasks": "用于复杂任务的终端 AI 编程引擎",
31
+ "Local code execution & pair programmer": "本地代码执行与结对编程器",
23
32
  "Available: {list}": "可用:{list}",
24
33
  "Balance": "余额",
25
34
  "Balance is below the auto-charge threshold ({threshold} ₩).": "余额低于自动充值阈值({threshold} ₩)。",
@@ -61,7 +70,14 @@ export const ZH = {
61
70
  "Needs GitHub Copilot CLI 2026-04 or later for BYOK provider routing support.":
62
71
  "需要 2026-04 或更新版本的 GitHub Copilot CLI 才支持 BYOK provider 路由。",
63
72
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
64
- "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.",
73
+ "Gemini CLI 使用 Google 原生 generateContent 格式 — 启动后用 `/model google/<id>` 设置模型。",
74
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
75
+ "会把 modelProvider=gemini 写入 ~/.gemini/antigravity-cli/settings.json。只设 GEMINI_API_KEY 会被忽略。仅 Gemini 系列。",
76
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
77
+ "只用隔离的 ~/.pleum/pi-home(PI_CODING_AGENT_DIR),不改 ~/.pi。",
78
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
79
+ "只用隔离的 ~/.pleum/crush-home(CRUSH_GLOBAL_CONFIG),不改 crush.json。",
80
+ "Aliases: {list}": "别名:{list}",
65
81
  "Gateway": "网关",
66
82
  "Gateway connection failed: {error}": "网关连接失败:{error}",
67
83
  "Gateway connectivity · login status": "网关连通性 · 登录状态",
package/src/menu.mjs CHANGED
@@ -40,6 +40,15 @@ const AGENT_DESC = {
40
40
  hermes: "Nous Research's general-purpose agent",
41
41
  qwen: "Alibaba's terminal coding agent",
42
42
  copilot: "GitHub's terminal coding agent",
43
+ gemini: "Google's Gemini CLI",
44
+ agy: "Google Antigravity CLI (agy)",
45
+ pi: "Pi coding agent (picode is a Pi package)",
46
+ crush: "Charmbracelet terminal coding agent",
47
+ minimax: "MiniMax's autonomous coding agent",
48
+ mimo: "Xiaomi's terminal coding agent with persistent memory",
49
+ openclaw: "Autonomous personal AI coding agent",
50
+ plandex: "Terminal AI coding engine for complex tasks",
51
+ interpreter: "Local code execution & pair programmer",
43
52
  };
44
53
 
45
54
  const autoInstallable = (r) => !/^https?:/.test(r.install); // URL이면 수동 설치 안내만
package/src/recipes.mjs CHANGED
@@ -1,15 +1,19 @@
1
1
  import { ROOT, V1 } from "./config.mjs";
2
2
 
3
- // 도구별 설정의 진실 원천. 출처: frontend .../docs/cookbook/{codex,claude-code,...} + 에이전트별 공식 문서 조사(2026-07).
4
- // type "env" → 환경변수만 세팅하고 바로 실행.
5
- // type "codex-home" → 격리된 CODEX_HOME에 config.toml을 써서 실행(사용자 ~/.codex 보존).
3
+ // 도구별 설정의 진실 원천. 출처: frontend .../docs/cookbook/{codex,claude-code,...} + 에이전트별 공식 문서.
4
+ // type "env" → 환경변수만 세팅하고 바로 실행.
5
+ // type "codex-home" → 격리 CODEX_HOME에 config.toml (사용자 ~/.codex 보존).
6
+ // type "pi-home" → 격리 PI_CODING_AGENT_DIR에 models.json (사용자 ~/.pi 보존).
7
+ // type "crush-home" → 격리 CRUSH_GLOBAL_CONFIG 디렉터리에 crush.json (사용자 crush.json 보존).
8
+ // type "agy-settings" → ~/.gemini/antigravity-cli/settings.json 에 modelProvider=gemini 병합.
9
+ // Antigravity는 이 파일이 없으면 GEMINI_API_KEY를 무시한다. 격리 홈 env가 없다.
6
10
  // note 필드는 t() 키(영어 원문)만 담는다 — 여기서 t()를 호출하면 모듈 로드 시점에 번역이
7
11
  // 고정돼 같은 프로세스 안에서 언어를 바꿔도(Settings) 안내문이 갱신되지 않는다.
8
12
  // 실제 번역은 소비 지점(launch.mjs)에서 매번 t()를 호출해 얻는다.
9
13
  //
10
- // 커스텀 엔드포인트를 지원하지 않아 PleumRouter로 라우팅할 수 없는 도구는 등록하지 않는다(조사 결과, 2026-07):
11
- // Gemini CLI(구글 백엔드 전용, PR#2899 머지 거부) · Cursor CLI(구독 백엔드 전용, 공식 확인) ·
12
- // Amp(Sourcegraph 백엔드 전용) · OpenClaw(멀티채널 게이트웨이 데몬 — 세션형 코딩 CLI가 아님 + 파일설정 전용).
14
+ // 커스텀 엔드포인트가 없어 PleumRouter로 라우팅할 수 없으면 등록하지 않는다:
15
+ // Cursor CLI · Amp · Factory Droid · Kiro(공식 BYOK 없음, aws/kiro#9367) ·
16
+ // Cline CLI(cline auth가 사용자 cred를 디스크에 고정).
13
17
  export const RECIPES = {
14
18
  claude: {
15
19
  label: "Claude Code",
@@ -27,6 +31,8 @@ export const RECIPES = {
27
31
  ANTHROPIC_BASE_URL: ROOT,
28
32
  ANTHROPIC_API_KEY: key,
29
33
  ANTHROPIC_AUTH_TOKEN: key,
34
+ // /model 이 GET {ANTHROPIC_BASE_URL}/v1/models 를 치게 한다. 기본값은 꺼져 있다.
35
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
30
36
  }),
31
37
  args: (m) => (m ? ["--model", `anthropic/${m}`] : []),
32
38
  },
@@ -185,8 +191,151 @@ export const RECIPES = {
185
191
  }),
186
192
  args: (m) => (m ? ["--model", `google/${m}`] : []),
187
193
  },
194
+ agy: {
195
+ label: "Antigravity CLI",
196
+ bin: "agy",
197
+ type: "agy-settings",
198
+ install: "https://antigravity.google/cli/install.sh",
199
+ note: "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.",
200
+ attribution: {
201
+ title: "Antigravity CLI",
202
+ referer: "https://www.antigravity.google/docs/cli/install",
203
+ },
204
+ env: (key) => ({
205
+ GOOGLE_GEMINI_BASE_URL: ROOT,
206
+ GEMINI_API_KEY: key,
207
+ GOOGLE_API_KEY: key,
208
+ }),
209
+ args: (m) => (m ? ["--model", `google/${m}`] : []),
210
+ },
211
+ pi: {
212
+ label: "Pi",
213
+ bin: "pi",
214
+ type: "pi-home",
215
+ install: "npm i -g @mariozechner/pi-coding-agent",
216
+ note: "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.",
217
+ attribution: {
218
+ title: "Pi",
219
+ referer: "https://pi.dev",
220
+ },
221
+ env: (key) => ({ PLEUM_API_KEY: key }),
222
+ args: (m) => ["--model", m || "gpt-5.4-mini"],
223
+ },
224
+ crush: {
225
+ label: "Crush",
226
+ bin: "crush",
227
+ type: "crush-home",
228
+ install: "npm install -g @charmland/crush",
229
+ note: "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.",
230
+ attribution: {
231
+ title: "Crush",
232
+ referer: "https://github.com/charmbracelet/crush",
233
+ },
234
+ env: (key) => ({ PLEUM_API_KEY: key }),
235
+ args: () => [],
236
+ },
237
+ minimax: {
238
+ label: "MiniMax Code",
239
+ bin: "mcode",
240
+ type: "env",
241
+ install: "npm install -g @minimax-ai/code@latest",
242
+ attribution: {
243
+ title: "MiniMax Code",
244
+ referer: "https://github.com/MiniMax-AI/minimax-code",
245
+ },
246
+ env: (key, model) => ({
247
+ OPENAI_BASE_URL: V1,
248
+ OPENAI_API_KEY: key,
249
+ ...(model ? { OPENAI_MODEL: model } : {}),
250
+ }),
251
+ args: (m) => (m ? ["--model", m] : []),
252
+ },
253
+ mimo: {
254
+ label: "MiMo Code",
255
+ bin: "mimo",
256
+ type: "env",
257
+ install: "npm install -g @mimo-ai/cli",
258
+ attribution: {
259
+ title: "MiMo Code",
260
+ referer: "https://github.com/XiaomiMiMo/MiMo-Code",
261
+ },
262
+ env: (key, model) => ({
263
+ OPENAI_BASE_URL: V1,
264
+ OPENAI_API_KEY: key,
265
+ ...(model ? { OPENAI_MODEL: model } : {}),
266
+ }),
267
+ args: (m) => (m ? ["--model", m] : []),
268
+ },
269
+ openclaw: {
270
+ label: "OpenClaw",
271
+ bin: "openclaw",
272
+ type: "env",
273
+ install: "npm install -g openclaw@latest",
274
+ attribution: {
275
+ title: "OpenClaw",
276
+ referer: "https://github.com/openclaw/openclaw",
277
+ },
278
+ env: (key, model) => ({
279
+ OPENAI_BASE_URL: V1,
280
+ OPENAI_API_KEY: key,
281
+ ...(model ? { OPENAI_MODEL: model } : {}),
282
+ }),
283
+ args: (m) => (m ? ["--model", m] : []),
284
+ },
285
+ plandex: {
286
+ label: "Plandex",
287
+ bin: "plandex",
288
+ type: "env",
289
+ install: "curl -sL https://plandex.ai/install.sh | bash",
290
+ attribution: {
291
+ title: "Plandex",
292
+ referer: "https://github.com/plandex-ai/plandex",
293
+ },
294
+ env: (key) => ({
295
+ OPENAI_API_BASE: V1,
296
+ OPENAI_API_KEY: key,
297
+ }),
298
+ args: () => [],
299
+ },
300
+ interpreter: {
301
+ label: "Open Interpreter",
302
+ bin: "interpreter",
303
+ type: "env",
304
+ install: "pip install open-interpreter",
305
+ attribution: {
306
+ title: "Open Interpreter",
307
+ referer: "https://github.com/OpenInterpreter/open-interpreter",
308
+ },
309
+ env: (key) => ({
310
+ OPENAI_API_BASE: V1,
311
+ OPENAI_API_KEY: key,
312
+ }),
313
+ args: (m) => (m ? ["--model", `openai/${m}`] : []),
314
+ },
315
+ };
316
+
317
+ export const LAUNCH_ALIASES = {
318
+ antigravity: "agy",
319
+ picode: "pi",
320
+ ohmypi: "omp",
321
+ "oh-my-pi": "omp",
322
+ qwencode: "qwen",
323
+ "qwen-code": "qwen",
324
+ "minimax-code": "minimax",
325
+ mcode: "minimax",
326
+ mimocode: "mimo",
327
+ "mimo-code": "mimo",
328
+ "open-interpreter": "interpreter",
188
329
  };
189
330
 
331
+ export function resolveLaunchTool(name) {
332
+ return LAUNCH_ALIASES[name] ?? name;
333
+ }
334
+
335
+ export function launchToolNames() {
336
+ return [...Object.keys(RECIPES), ...Object.keys(MANUAL), ...Object.keys(LAUNCH_ALIASES)];
337
+ }
338
+
190
339
  // Codex는 Chat Completions가 제거돼 Responses API(wire_api="responses")만 지원.
191
340
  // 사용자의 ~/.codex/config.toml을 건드리지 않도록 격리 CODEX_HOME에 이 파일을 쓴다.
192
341
  // TOML basic string encoder. 모델명과 base_url은 CLI/env에서 오므로, 따옴표·개행이
@@ -222,22 +371,45 @@ supports_websockets = false
222
371
  `;
223
372
  }
224
373
 
225
- // provider 정의가 파일에 있어야 하는 도구 — 자동 실행 대신 설정 스니펫만 안내.
226
- export const MANUAL = {
227
- crush: {
228
- label: "Crush",
229
- bin: "crush",
230
- install: "npm install -g @charmland/crush",
231
- file: "crush.json",
232
- snippet: () => `{
233
- "providers": {
234
- "pleum": {
235
- "type": "openai-compat",
236
- "base_url": "${V1}",
237
- "api_key": "$PLEUM_API_KEY",
238
- "models": [{ "id": "gpt-5.4-mini", "name": "gpt-5.4-mini" }]
374
+ export function mergeAgySettings(existingJson) {
375
+ let obj = {};
376
+ if (existingJson && String(existingJson).trim()) {
377
+ obj = JSON.parse(existingJson);
378
+ if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
379
+ throw new Error("agy settings.json must be a JSON object");
239
380
  }
240
381
  }
241
- }`,
242
- },
243
- };
382
+ return { ...obj, modelProvider: "gemini" };
383
+ }
384
+
385
+ export function piModelsJson(model) {
386
+ const m = model || "gpt-5.4-mini";
387
+ return `${JSON.stringify({
388
+ providers: {
389
+ pleum: {
390
+ baseUrl: V1,
391
+ api: "openai-completions",
392
+ apiKey: "$PLEUM_API_KEY",
393
+ models: [{ id: m }],
394
+ },
395
+ },
396
+ }, null, 2)}\n`;
397
+ }
398
+
399
+ export function crushJson(model) {
400
+ const m = model || "gpt-5.4-mini";
401
+ return `${JSON.stringify({
402
+ providers: {
403
+ pleum: {
404
+ type: "openai-compat",
405
+ base_url: V1,
406
+ api_key: "$PLEUM_API_KEY",
407
+ models: [{ id: m, name: m }],
408
+ },
409
+ },
410
+ }, null, 2)}\n`;
411
+ }
412
+
413
+ // provider 정의가 파일에 있어야 자동 실행이 안 되는 도구. crush는 격리 CRUSH_GLOBAL_CONFIG로
414
+ // RECIPES로 올렸으니 지금은 비어 있다 — 스니펫-only 도구가 다시 생기면 여기 넣는다.
415
+ export const MANUAL = {};
package/src/wire.mjs CHANGED
@@ -10,7 +10,12 @@ export const WIRE_KEY_NAME = "pleum-desktop";
10
10
  export const WIRE_MARKER_START = "# pleum-wire-start";
11
11
  export const WIRE_MARKER_END = "# pleum-wire-end";
12
12
 
13
- const CLAUDE_ENV_KEYS = ["ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_API_KEY"];
13
+ const CLAUDE_ENV_KEYS = [
14
+ "ANTHROPIC_BASE_URL",
15
+ "ANTHROPIC_AUTH_TOKEN",
16
+ "ANTHROPIC_API_KEY",
17
+ "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY",
18
+ ];
14
19
 
15
20
  export function wirePaths(home = homedir()) {
16
21
  const claude = join(home, ".claude", "settings.json");
@@ -81,6 +86,7 @@ export function wireClaudeSettings(existingJson, { root, key }) {
81
86
  ANTHROPIC_BASE_URL: root,
82
87
  ANTHROPIC_AUTH_TOKEN: key,
83
88
  ANTHROPIC_API_KEY: key,
89
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
84
90
  };
85
91
  return `${JSON.stringify(obj, null, 2)}\n`;
86
92
  }