pleumrouter 0.5.47 → 0.5.49

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pleumrouter",
3
- "version": "0.5.47",
3
+ "version": "0.5.49",
4
4
  "description": "Official PleumRouter terminal CLI — launch any coding agent against PleumRouter.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -49,13 +49,14 @@ _pleum() {
49
49
  _pleum "$@"`;
50
50
  }
51
51
 
52
- export function completion(shell) {
52
+ export function completion(shell, platform = process.platform) {
53
53
  if (shell === "bash") return void console.log(bash());
54
54
  if (shell === "zsh") return void console.log(zsh());
55
55
  console.error(
56
56
  t("Usage: pleum completion <zsh|bash>") + "\n" +
57
57
  ` zsh: eval "$(pleum completion zsh)" (${t("or add to ~/.zshrc")})\n` +
58
- ` bash: eval "$(pleum completion bash)" (${t("or add to ~/.bashrc")})`,
58
+ ` bash: eval "$(pleum completion bash)" (${t("or add to ~/.bashrc")})` +
59
+ (platform === "win32" ? `\n${t("PowerShell and cmd completion are not supported; use Git Bash or WSL.")}` : ""),
59
60
  );
60
61
  process.exit(1);
61
62
  }
package/src/install.mjs CHANGED
@@ -82,9 +82,12 @@ function runCurlPipe(pipe, spawnImpl, resolve) {
82
82
  export async function runInstallCommand(install, {
83
83
  spawnImpl = spawn,
84
84
  resolve = resolveLaunchCommand,
85
+ platform = process.platform,
85
86
  } = {}) {
86
87
  const parsed = parseInstallCommand(install);
87
88
  if (parsed.manual) return { manual: true, reason: parsed.reason, code: null };
89
+ // Windows엔 sh/bash가 없다 — curl|sh는 시도하지 않고 수동 안내로 넘긴다.
90
+ if (parsed.pipe && platform === "win32") return { manual: true, reason: "windows-pipe", code: null };
88
91
  if (parsed.pipe) {
89
92
  return { manual: false, code: await runCurlPipe(parsed.pipe, spawnImpl, resolve) };
90
93
  }
@@ -102,6 +102,7 @@ export function rowsFor(s, width = terminalWidth()) {
102
102
  // 화살표 이동 · 타이핑 필터 · Enter 선택 · Esc/Ctrl+C 취소(null).
103
103
  // 비TTY면 즉시 null — 호출자가 기존 번호 피커 등으로 폴백한다.
104
104
  // process.exit는 절대 호출하지 않는다(취소 처리는 호출자 몫).
105
+ // `{ header: true }` 항목은 묶음 제목 — 커서가 건너뛰고 선택되지 않으며 필터 중엔 숨긴다.
105
106
  export async function select(items, {
106
107
  message = t("Select"),
107
108
  formatItem = String,
@@ -118,6 +119,16 @@ export async function select(items, {
118
119
  let cursor = Math.max(0, Math.min(initialCursor, Math.max(0, (items?.length ?? 1) - 1)));
119
120
  let windowStart = 0;
120
121
  let lastRows = 0;
122
+ const isHeader = (it) => it?.header === true;
123
+ const poolOf = () => {
124
+ const pool = matches(items, query);
125
+ return query ? pool.filter((it) => !isHeader(it)) : pool;
126
+ };
127
+ // from에서 dir 방향 첫 비제목 위치. 없으면 fallback(현재 위치 유지).
128
+ const seek = (pool, from, dir, fallback) => {
129
+ for (let i = from; i >= 0 && i < pool.length; i += dir) if (!isHeader(pool[i])) return i;
130
+ return fallback;
131
+ };
121
132
 
122
133
  const erase = () => {
123
134
  if (!lastRows) return;
@@ -126,8 +137,9 @@ export async function select(items, {
126
137
  };
127
138
 
128
139
  const render = () => {
129
- const pool = matches(items, query);
140
+ const pool = poolOf();
130
141
  if (cursor >= pool.length) cursor = Math.max(0, pool.length - 1);
142
+ if (isHeader(pool[cursor])) cursor = seek(pool, cursor, 1, seek(pool, cursor, -1, cursor));
131
143
  if (cursor < windowStart) {
132
144
  windowStart = cursor;
133
145
  } else if (cursor >= windowStart + limit) {
@@ -146,6 +158,7 @@ export async function select(items, {
146
158
  rawLines.push(
147
159
  ...shown.map((it, idx) => {
148
160
  const itemIndex = windowStart + idx;
161
+ if (isHeader(it)) return ` ${style.gray(formatItem(it))}`;
149
162
  return itemIndex === cursor
150
163
  ? `${style.plum("❯")} ${style.bold(formatItem(it))}`
151
164
  : ` ${style.dim(formatItem(it))}`;
@@ -180,15 +193,15 @@ export async function select(items, {
180
193
  for (const key of feed(chunk)) {
181
194
  if (key.type === "ctrlc" || key.type === "escape") return finish(null);
182
195
  if (key.type === "enter") {
183
- const pool = matches(items, query);
196
+ const pool = poolOf();
184
197
  const safeCursor = Math.max(0, Math.min(cursor, pool.length - 1));
198
+ if (isHeader(pool[safeCursor])) continue;
185
199
  return finish(pool[safeCursor] ?? null);
186
200
  }
187
201
  if (key.type === "up") {
188
- cursor = Math.max(0, cursor - 1);
202
+ cursor = seek(poolOf(), cursor - 1, -1, cursor);
189
203
  } else if (key.type === "down") {
190
- const pool = matches(items, query);
191
- cursor = Math.min(Math.max(0, pool.length - 1), cursor + 1);
204
+ cursor = seek(poolOf(), cursor + 1, 1, cursor);
192
205
  } else if (key.type === "backspace") {
193
206
  query = query.slice(0, -1);
194
207
  cursor = 0;
@@ -193,6 +193,8 @@ export const ES = {
193
193
  "Update CLI": "Actualizar la CLI",
194
194
  "Usage": "Uso",
195
195
  "Usage: pleum completion <zsh|bash>": "Uso: pleum completion <zsh|bash>",
196
+ "{tool}: wire needs a zsh/bash shell and is not supported on Windows. Use `pleum launch {tool}` instead.": "{tool}: wire necesita un shell zsh/bash y no es compatible con Windows. Usa `pleum launch {tool}` en su lugar.",
197
+ "PowerShell and cmd completion are not supported; use Git Bash or WSL.": "El autocompletado para PowerShell y cmd no es compatible; usa Git Bash o WSL.",
196
198
  "Usage: pleum launch <tool> [--model id] [-- tool args]\n": "Uso: pleum launch <tool> [--model id] [-- tool args]\n",
197
199
  "Usage: pleum models <model-id> --info": "Uso: pleum models <model-id> --info",
198
200
  "Usage: pleum run <model> [\"prompt\"]\nModel list: pleum models": "Uso: pleum run <model> [\"prompt\"]\nLista de modelos: pleum models",
@@ -258,6 +260,7 @@ export const ES = {
258
260
  "Team": "Equipo",
259
261
  "Personal": "Personal",
260
262
  "Personal Credit": "Crédito personal",
263
+ "Invited workspaces": "Espacios de trabajo invitados",
261
264
  "Credit workspace": "Espacio de crédito",
262
265
  "Credit Balance:": "Saldo de crédito:",
263
266
  "credits": "créditos",
@@ -191,6 +191,8 @@ export const JA = {
191
191
  "Update CLI": "CLIを更新",
192
192
  "Usage": "使用状況",
193
193
  "Usage: pleum completion <zsh|bash>": "使い方: pleum completion <zsh|bash>",
194
+ "{tool}: wire needs a zsh/bash shell and is not supported on Windows. Use `pleum launch {tool}` instead.": "{tool}: wire は zsh/bash シェルが必要なため Windows では使えません。代わりに `pleum launch {tool}` を使ってください。",
195
+ "PowerShell and cmd completion are not supported; use Git Bash or WSL.": "PowerShell・cmd の補完には対応していません。Git Bash か WSL を使ってください。",
194
196
  "Usage: pleum launch <tool> [--model id] [-- tool args]\n": "使い方: pleum launch <tool> [--model id] [-- tool args]\n",
195
197
  "Usage: pleum models <model-id> --info": "使い方: pleum models <model-id> --info",
196
198
  "Usage: pleum run <model> [\"prompt\"]\nModel list: pleum models": "使い方: pleum run <model> [\"prompt\"]\nモデル一覧: pleum models",
@@ -258,6 +260,7 @@ export const JA = {
258
260
  "Team": "チーム",
259
261
  "Personal": "個人",
260
262
  "Personal Credit": "個人クレジット",
263
+ "Invited workspaces": "招待されたワークスペース",
261
264
  "Credit workspace": "クレジットワークスペース",
262
265
  "Credit Balance:": "クレジット残高:",
263
266
  "credits": "クレジット",
@@ -65,6 +65,8 @@ export const KO = {
65
65
 
66
66
  // completion.mjs
67
67
  "Usage: pleum completion <zsh|bash>": "사용법: pleum completion <zsh|bash>",
68
+ "{tool}: wire needs a zsh/bash shell and is not supported on Windows. Use `pleum launch {tool}` instead.": "{tool}: wire는 zsh/bash 셸이 필요해 Windows에서 지원하지 않습니다. 대신 `pleum launch {tool}`을 사용하세요.",
69
+ "PowerShell and cmd completion are not supported; use Git Bash or WSL.": "PowerShell·cmd 자동완성은 지원하지 않습니다. Git Bash나 WSL을 사용하세요.",
68
70
  "or add to ~/.zshrc": "또는 ~/.zshrc에 추가",
69
71
  "or add to ~/.bashrc": "또는 ~/.bashrc에 추가",
70
72
  "or add _pleum to fpath": "또는 fpath에 _pleum로 추가",
@@ -317,6 +319,7 @@ export const KO = {
317
319
  "Team": "팀",
318
320
  "Personal": "개인",
319
321
  "Personal Credit": "개인 크레딧",
322
+ "Invited workspaces": "초대받은 워크스페이스",
320
323
  "Credit workspace": "크레딧 워크스페이스",
321
324
  "Credit Balance:": "크레딧 잔액:",
322
325
  "credits": "크레딧",
@@ -193,6 +193,8 @@ export const ZH = {
193
193
  "Update CLI": "更新 CLI",
194
194
  "Usage": "用量",
195
195
  "Usage: pleum completion <zsh|bash>": "用法:pleum completion <zsh|bash>",
196
+ "{tool}: wire needs a zsh/bash shell and is not supported on Windows. Use `pleum launch {tool}` instead.": "{tool}:wire 需要 zsh/bash shell,不支持 Windows。请改用 `pleum launch {tool}`。",
197
+ "PowerShell and cmd completion are not supported; use Git Bash or WSL.": "不支持 PowerShell 和 cmd 自动补全,请使用 Git Bash 或 WSL。",
196
198
  "Usage: pleum launch <tool> [--model id] [-- tool args]\n": "用法:pleum launch <tool> [--model id] [-- tool args]\n",
197
199
  "Usage: pleum models <model-id> --info": "用法:pleum models <model-id> --info",
198
200
  "Usage: pleum run <model> [\"prompt\"]\nModel list: pleum models": "用法:pleum run <model> [\"prompt\"]\n模型列表:pleum models",
@@ -258,6 +260,7 @@ export const ZH = {
258
260
  "Team": "团队",
259
261
  "Personal": "个人",
260
262
  "Personal Credit": "个人额度",
263
+ "Invited workspaces": "受邀的工作区",
261
264
  "Credit workspace": "额度工作区",
262
265
  "Credit Balance:": "额度余额:",
263
266
  "credits": "额度",
package/src/wire.mjs CHANGED
@@ -895,7 +895,13 @@ function originalValues(tool, paths, state, text) {
895
895
  return fromText ?? pickPrevious(tool, readText(paths[`${tool}Bak`])) ?? {};
896
896
  }
897
897
 
898
- export function applyToolWiring(tool, paths, { key, model, models = [], billingOnly = false, activeContext } = {}) {
898
+ // 셸 rc 래퍼 없이 설정 파일만으로 배선되는 도구. 나머지는 zsh/bash rc가 필요해 Windows에서 효과가 없다.
899
+ const FILE_WIRED_TOOLS = new Set(["claude", "codex", "opencode", "aider", "crush", "pi"]);
900
+
901
+ export function applyToolWiring(tool, paths, { key, model, models = [], billingOnly = false, activeContext, platform = process.platform } = {}) {
902
+ if (platform === "win32" && !FILE_WIRED_TOOLS.has(tool)) {
903
+ throw new Error(t("{tool}: wire needs a zsh/bash shell and is not supported on Windows. Use `pleum launch {tool}` instead.", { tool }));
904
+ }
899
905
  const state = readWiredState(paths);
900
906
  const mode = billingOnly ? "billing" : (models.length > 0 ? "list" : (model ? "custom" : "billing"));
901
907
  let previous; // claude·agy가 덮어쓰는 사용자 원래 값 — unwire가 복원한다
package/src/workspace.mjs CHANGED
@@ -56,15 +56,24 @@ export function workspaceLabel(context) {
56
56
  return t("Personal");
57
57
  }
58
58
 
59
+ // 서버가 소유 공간(role=owner)을 먼저 보낸다. 소유=개인 크레딧 아래, 나머지=초대받은 공간 묶음.
60
+ export function splitCandidates(candidates = []) {
61
+ return {
62
+ owned: candidates.filter((c) => c.role === "owner"),
63
+ invited: candidates.filter((c) => c.role !== "owner"),
64
+ };
65
+ }
66
+
59
67
  export function formatWorkspaceList(context, candidates = []) {
60
68
  const activeId = context?.type === "workspace" ? String(context.workspace_id) : null;
61
- const lines = [`${t("Credit workspace")}: ${workspaceLabel(context)}`];
62
- for (const candidate of candidates) {
69
+ const { owned, invited } = splitCandidates(candidates);
70
+ const line = (candidate) => {
63
71
  const mark = String(candidate.workspace_id) === activeId ? style.bold(style.plum("▸ ")) : " ";
64
- lines.push(
65
- `${mark}${candidate.workspace_id} ${sanitizeTerminalText(candidate.name)} (${sanitizeTerminalText(candidate.role)})`,
66
- );
67
- }
72
+ return `${mark}${candidate.workspace_id} ${sanitizeTerminalText(candidate.name)} (${sanitizeTerminalText(candidate.role)})`;
73
+ };
74
+ const lines = [`${t("Credit workspace")}: ${workspaceLabel(context)}`];
75
+ if (owned.length) lines.push(`${t("Personal Credit")}:`, ...owned.map(line));
76
+ if (invited.length) lines.push(`${t("Invited workspaces")}:`, ...invited.map(line));
68
77
  return lines.join("\n");
69
78
  }
70
79
 
@@ -118,6 +127,15 @@ export async function selectAndBindWorkspace({
118
127
  const active = data?.active_context ?? getActiveContext();
119
128
  const activeId = active?.type === "workspace" ? String(active.workspace_id) : null;
120
129
 
130
+ const { owned, invited } = splitCandidates(candidates);
131
+ const option = (c, title) => ({
132
+ id: String(c.workspace_id),
133
+ type: "workspace",
134
+ workspace_id: c.workspace_id,
135
+ name: c.name,
136
+ title,
137
+ label: `${activeId === String(c.workspace_id) ? "✓ " : " "} └ ${title}`,
138
+ });
121
139
  const options = [
122
140
  {
123
141
  id: "personal",
@@ -125,20 +143,17 @@ export async function selectAndBindWorkspace({
125
143
  title: t("Personal Credit"),
126
144
  label: `${activeId ? " " : "✓ "}${t("Personal Credit")}`,
127
145
  },
128
- ...candidates.map((c) => ({
129
- id: String(c.workspace_id),
130
- type: "workspace",
131
- workspace_id: c.workspace_id,
132
- name: c.name,
133
- title: `${sanitizeTerminalText(c.name)} (${sanitizeTerminalText(c.role)})`,
134
- label: `${activeId === String(c.workspace_id) ? "✓ " : " "}${sanitizeTerminalText(c.name)} (${sanitizeTerminalText(c.role)})`,
135
- })),
146
+ ...owned.map((c) => option(c, sanitizeTerminalText(c.name))),
147
+ ...(invited.length
148
+ ? [{ id: "header:invited", header: true, label: ` ${t("Invited workspaces")}` }]
149
+ : []),
150
+ ...invited.map((c) => option(c, `${sanitizeTerminalText(c.name)} (${sanitizeTerminalText(c.role)})`)),
136
151
  ];
137
152
 
138
153
  const picked = await selectFn(options, {
139
154
  message: t("Select billing workspace for this agent"),
140
155
  formatItem: (x) => x.label,
141
- limit: 10,
156
+ limit: 12,
142
157
  });
143
158
 
144
159
  if (!picked) return active;