agentlas 1.0.40 → 1.0.41

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.41 — 2026-08-11
4
+
5
+ Desktop surfaces, measured against the desktop inventory and rebuilt for the terminal.
6
+
7
+ - `/dashboard` now mirrors the desktop control panel: a **Needs attention**
8
+ section (waiting approvals and runs that stalled without a terminal state),
9
+ run activity that states failures out loud (e.g. "8/11 failed" rather than a
10
+ quiet list), automations with next-run times, most-used agents, and evolution
11
+ proposal counts.
12
+ - `/library` — installed agents with role/kind, MCP server count, and pointers
13
+ to env, credentials, and plugins.
14
+ - `/marketplace` (also `/bookmarks`) — Hub bookmarks and borrowed-agent careers
15
+ held locally, with search/install/credit pointers.
16
+ - `/settings` — language, permission, active runtime, installed CLIs, and the
17
+ resolved orchestrator/worker model roles; names what remains Desktop-only
18
+ instead of implying parity.
19
+ - Those five honest stops now point at the pi shell instead of claiming the
20
+ surface is unreachable from the terminal.
21
+
3
22
  ## 1.0.40 — 2026-08-11
4
23
 
5
24
  Desktop surfaces reach the pi shell: dashboard and graph view.
@@ -93,13 +93,13 @@ const DESKTOP_ONLY_SURFACES = {
93
93
  trex: "T-rex slide studio is Desktop-only.",
94
94
  slides: "T-rex slide studio is Desktop-only.",
95
95
  prompts: "Prompt Store is Desktop-only.",
96
- dashboard: "Dashboard is Desktop-onlyuse: agentlas doctor · usage · list",
97
- marketplace: "Marketplace browsing is Desktop-onlyuse: agentlas search \"<what you need>\"",
98
- library: "Library is Desktop-onlyuse: agentlas list · env · mcp",
99
- settings: "Settings UI is Desktop-onlyuse: agentlas setup · env · creds · multimodal · doctor",
96
+ dashboard: "Dashboard: run `AGENTLAS_TUI=pi agentlas` then /dashboard or: agentlas doctor · usage · list",
97
+ marketplace: "Hub view: run `AGENTLAS_TUI=pi agentlas` then /marketplace or: agentlas search \"<what you need>\"",
98
+ library: "Library: run `AGENTLAS_TUI=pi agentlas` then /library or: agentlas list · env · mcp",
99
+ settings: "Settings: run `AGENTLAS_TUI=pi agentlas` then /settings change with: agentlas setup · env · creds · multimodal",
100
100
  apps: "Apps surface is Desktop-only.",
101
101
  quests: "Quests are Desktop-only.",
102
- bookmarks: "Hub bookmarks are Desktop-only.",
102
+ bookmarks: "Hub bookmarks: run `AGENTLAS_TUI=pi agentlas` then /marketplace.",
103
103
  one: "Agentlas One is a separate Desktop/Mobile product surface.",
104
104
  };
105
105
 
@@ -24,7 +24,6 @@ const SLASH_COMMANDS = [
24
24
  { command: "/graph", args: "[run <이름>]", ko: "저장된 자동화 그래프", en: "Saved automation graphs" },
25
25
  { command: "/mcp", args: "", ko: "MCP 서버 목록", en: "MCP servers" },
26
26
  { command: "/doctor", args: "", ko: "런타임·데이터 점검", en: "Health check" },
27
- { command: "/dashboard", args: "", ko: "관제 대시보드 (pi 셸)", en: "Dashboard (pi shell)" },
28
27
  { command: "/runtime", args: "<kind>", ko: "새 세션 런타임 지정", en: "Set runtime for new sessions" },
29
28
  { command: "/model", args: "<id|default>", ko: "새 세션 모델 지정", en: "Set model for new sessions" },
30
29
  { command: "/effort", args: "<level|none>", ko: "새 세션 추론 강도 지정", en: "Set effort for new sessions" },
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ /*
3
+ * ui/pitui-screens — 데스크탑 화면의 터미널 대응물 (D3 Phase 3).
4
+ *
5
+ * 대조 원본: docs/2026-08-11-terminal-tui-overhaul/D1-데스크탑-기능-인벤토리.md
6
+ * 각 화면은 데스크탑이 IPC로 읽는 것과 같은 로컬 저장소를 직접 읽는다.
7
+ *
8
+ * 규칙:
9
+ * - 없는 데이터를 지어내지 않는다. 표면이 데스크탑 전용이면 그렇게 말한다.
10
+ * - 실패·미결은 눈에 띄게. "조용히 멈춘 실행"이 정상처럼 보이면 안 된다(D1 숨은 계약 2).
11
+ * - 모든 화면은 ctx.out 이 아니라 ui 를 직접 받아 pi 프레임 안에 그린다.
12
+ */
13
+
14
+ function table(ui, rows, opts = {}) {
15
+ // rows: [[col, col, …]] — 첫 행이 헤더. 폭은 CJK 셀 폭으로 계산한다.
16
+ const { visWidth, truncateWidth } = require("./width.cjs");
17
+ if (!rows.length) return;
18
+ const cols = rows[0].length;
19
+ const max = Number(opts.maxWidth) || 78;
20
+ const widths = Array.from({ length: cols }, (_, i) =>
21
+ Math.min(opts.cap?.[i] || 40, Math.max(...rows.map((r) => visWidth(String(r[i] ?? ""))))));
22
+ const total = widths.reduce((a, b) => a + b + 2, 0);
23
+ if (total > max && widths.length) widths[0] = Math.max(8, widths[0] - (total - max));
24
+ rows.forEach((row, index) => {
25
+ const line = row.map((cell, i) => {
26
+ const text = truncateWidth(String(cell ?? ""), widths[i]);
27
+ return text + " ".repeat(Math.max(0, widths[i] - visWidth(text)));
28
+ }).join(" ");
29
+ ui.line(index === 0 && opts.header !== false ? ui.c.dim(line) : " " + line);
30
+ });
31
+ }
32
+
33
+ function count(db, sql, fallback = 0) {
34
+ try { return db.prepare(sql).get()?.n ?? fallback; } catch { return fallback; }
35
+ }
36
+ function rows(db, sql, args = []) {
37
+ try { return db.prepare(sql).all(...args); } catch { return []; }
38
+ }
39
+ const shortTs = (v) => (v ? String(v).replace("T", " ").slice(0, 16) : "");
40
+
41
+ /* ── /dashboard — 데스크탑 dashboard 의 관제 패널 집합 ── */
42
+ function dashboard(ui, db, en) {
43
+ const chip = (paint, s) => paint(` ${s} `);
44
+ const local = count(db, "SELECT COUNT(*) n FROM installed_agents WHERE COALESCE(builtin,0)=0 AND COALESCE(visibility,'')!='background'");
45
+ const builtin = count(db, "SELECT COUNT(*) n FROM installed_agents WHERE COALESCE(builtin,0)=1");
46
+ const firms = count(db, "SELECT COUNT(*) n FROM firms");
47
+ const marks = count(db, "SELECT COUNT(*) n FROM hub_agent_bookmarks");
48
+ const borrowed = count(db, "SELECT COUNT(*) n FROM borrowed_agent_careers");
49
+
50
+ ui.ensureNl();
51
+ ui.line(ui.c.bold(en ? "Dashboard" : "대시보드"));
52
+ ui.line(` ${chip(ui.c.inverse, `${en ? "agents" : "에이전트"} ${local}`)} ${chip(ui.c.dim, `builtin ${builtin}`)} ${chip(ui.c.inverse, `${en ? "firms" : "회사"} ${firms}`)} ${chip(ui.c.dim, `${en ? "bookmarks" : "북마크"} ${marks}`)} ${chip(ui.c.dim, `${en ? "borrowed" : "대여"} ${borrowed}`)}`);
53
+
54
+ // ── 확인 필요 (D1 숨은 계약 2: 없으면 실행이 조용히 멈춘 채 정상처럼 보인다) ──
55
+ const pending = count(db, "SELECT COUNT(*) n FROM automation_node_approvals WHERE decision NOT IN ('approved','rejected')");
56
+ const stalled = rows(db,
57
+ `SELECT r.id, a.name, r.status, r.last_activity_at
58
+ FROM automation_runs r LEFT JOIN automations a ON a.id = r.automation_id
59
+ WHERE r.status NOT IN ('ok','error','cancelled') ORDER BY COALESCE(r.last_activity_at,'') DESC LIMIT 5`);
60
+ ui.line("");
61
+ ui.line(ui.c.bold(en ? "Needs attention" : "확인 필요"));
62
+ if (!pending && !stalled.length) {
63
+ ui.line(ui.c.dim(en ? " none — nothing is waiting on you" : " 없음 — 당신을 기다리는 실행이 없습니다"));
64
+ } else {
65
+ if (pending) ui.line(` ${ui.c.amber("!")} ${en ? `${pending} approval(s) waiting` : `승인 대기 ${pending}건`}`);
66
+ for (const s of stalled) {
67
+ ui.line(` ${ui.c.amber("!")} ${s.name || s.automation_id} ${ui.c.dim(`· ${s.status} · ${shortTs(s.last_activity_at)}`)}`);
68
+ }
69
+ }
70
+
71
+ // ── 실행 활동 — 실패를 숨기지 않는다 ──
72
+ const runAgg = rows(db, "SELECT status, COUNT(*) n FROM automation_runs GROUP BY status");
73
+ const recent = rows(db,
74
+ `SELECT r.status, r.started_at, a.name FROM automation_runs r
75
+ LEFT JOIN automations a ON a.id = r.automation_id
76
+ ORDER BY COALESCE(r.started_at,'') DESC LIMIT 5`);
77
+ if (runAgg.length) {
78
+ const failed = runAgg.find((r) => r.status === "error")?.n || 0;
79
+ const total = runAgg.reduce((a, b) => a + b.n, 0);
80
+ ui.line("");
81
+ ui.line(ui.c.bold(en ? "Run activity" : "실행 활동") + " " +
82
+ (failed ? ui.c.amber(en ? `${failed}/${total} failed` : `${total}건 중 ${failed}건 실패`) : ui.c.dim(`${total}`)));
83
+ for (const r of recent) {
84
+ const mark = r.status === "ok" ? ui.c.green("✓") : r.status === "error" ? ui.c.amber("✗") : ui.c.dim("·");
85
+ ui.line(` ${mark} ${r.name || "—"} ${ui.c.dim(shortTs(r.started_at))}`);
86
+ }
87
+ }
88
+
89
+ // ── 자동화 ──
90
+ const autoTotal = db.prepare("SELECT COUNT(*) n, COALESCE(SUM(enabled),0) e FROM automations").get();
91
+ const autos = rows(db, "SELECT name, enabled, next_run_at FROM automations ORDER BY enabled DESC, COALESCE(next_run_at,'') LIMIT 5");
92
+ ui.line("");
93
+ ui.line(ui.c.bold(en ? `Automations ${autoTotal.e}/${autoTotal.n} on` : `자동화 ${autoTotal.e}/${autoTotal.n} 켜짐`));
94
+ for (const a of autos) {
95
+ ui.line(` ${a.enabled ? ui.c.green("●") : ui.c.faint("○")} ${a.name}${a.next_run_at ? ui.c.dim(` · ${en ? "next" : "다음"} ${shortTs(a.next_run_at)}`) : ""}`);
96
+ }
97
+ if (!autos.length) ui.line(ui.c.dim(en ? " (none — /automation add)" : " (없음 — /automation add)"));
98
+
99
+ // ── 사용량 상위 ──
100
+ const usage = rows(db, "SELECT agent_key, use_count, last_used_at FROM agent_usage ORDER BY use_count DESC LIMIT 5");
101
+ if (usage.length) {
102
+ ui.line("");
103
+ ui.line(ui.c.bold(en ? "Most used" : "많이 쓴 에이전트"));
104
+ table(ui, [[en ? "agent" : "에이전트", en ? "runs" : "실행", en ? "last" : "마지막"],
105
+ ...usage.map((u) => [u.agent_key, String(u.use_count), shortTs(u.last_used_at)])], { cap: [34, 6, 16] });
106
+ }
107
+
108
+ // ── 진화 제안 (승인형) ──
109
+ const props = rows(db, "SELECT status, COUNT(*) n FROM agent_evolution_proposals GROUP BY status");
110
+ if (props.length) {
111
+ ui.line("");
112
+ ui.line(ui.c.bold(en ? "Evolution proposals" : "진화 제안") + " " +
113
+ ui.c.dim(props.map((p) => `${p.status} ${p.n}`).join(" · ")));
114
+ }
115
+
116
+ ui.line("");
117
+ ui.line(ui.c.dim(en
118
+ ? "more: /library · /marketplace · /automation list · /usage · /sessions"
119
+ : "더 보기: /library · /marketplace · /automation list · /usage · /sessions"));
120
+ }
121
+
122
+ /* ── /library — 데스크탑 library/agents + env + mcps 를 한 화면으로 ── */
123
+ function library(ui, db, en, ctx) {
124
+ ui.ensureNl();
125
+ ui.line(ui.c.bold(en ? "Library" : "라이브러리"));
126
+
127
+ const agents = rows(db,
128
+ `SELECT slug, COALESCE(local_display_name, name) nm, role, entity_kind, builtin, trust_grade
129
+ FROM installed_agents WHERE COALESCE(visibility,'')!='background'
130
+ ORDER BY COALESCE(builtin,0), slug LIMIT 12`);
131
+ const total = count(db, "SELECT COUNT(*) n FROM installed_agents WHERE COALESCE(visibility,'')!='background'");
132
+ ui.line("");
133
+ ui.line(ui.c.bold(en ? `Agents (${total})` : `에이전트 (${total})`));
134
+ table(ui, [[en ? "slug" : "슬러그", en ? "name" : "이름", en ? "role" : "역할", en ? "kind" : "종류"],
135
+ ...agents.map((a) => [a.slug, a.nm || "", a.role || "", a.builtin ? "builtin" : (a.entity_kind || "agent")])],
136
+ { cap: [26, 24, 12, 10] });
137
+ if (total > agents.length) ui.line(ui.c.dim(en ? ` … ${total - agents.length} more — /agents` : ` … ${total - agents.length}개 더 — /agents`));
138
+
139
+ const mcps = rows(db, "SELECT DISTINCT agent_id FROM agent_mcp_servers LIMIT 1");
140
+ const mcpCount = count(db, "SELECT COUNT(*) n FROM agent_mcp_servers");
141
+ ui.line("");
142
+ ui.line(ui.c.bold(en ? "MCP servers" : "MCP 서버") + " " + ui.c.dim(String(mcpCount)) +
143
+ ui.c.dim(mcps.length ? " · /mcp" : en ? " · none configured — /mcp" : " · 설정 없음 — /mcp"));
144
+
145
+ ui.line("");
146
+ ui.line(ui.c.dim(en
147
+ ? "env vars: /env · credentials: /creds list · plugins: /plugin list"
148
+ : "환경변수: /env · 자격증명: /creds list · 플러그인: /plugin list"));
149
+ void ctx;
150
+ }
151
+
152
+ /* ── /marketplace · /bookmarks — Hub 북마크(로컬)와 검색 안내 ── */
153
+ function marketplace(ui, db, en) {
154
+ ui.ensureNl();
155
+ ui.line(ui.c.bold(en ? "Hub" : "Hub 마켓플레이스"));
156
+ const marks = rows(db,
157
+ "SELECT slug, entity_kind, bookmarked_at, sync_state FROM hub_agent_bookmarks ORDER BY COALESCE(bookmarked_at,'') DESC LIMIT 10");
158
+ const borrowed = rows(db,
159
+ "SELECT slug, COALESCE(name_ko, name_en) nm, use_count, last_used_at FROM borrowed_agent_careers ORDER BY COALESCE(last_used_at,'') DESC LIMIT 5");
160
+
161
+ ui.line("");
162
+ ui.line(ui.c.bold(en ? `Bookmarks (${marks.length})` : `북마크 (${marks.length})`));
163
+ if (marks.length) {
164
+ table(ui, [[en ? "slug" : "슬러그", en ? "kind" : "종류", en ? "saved" : "저장"],
165
+ ...marks.map((m) => [m.slug, m.entity_kind || "agent", shortTs(m.bookmarked_at)])], { cap: [34, 10, 16] });
166
+ } else {
167
+ ui.line(ui.c.dim(en ? " none yet" : " 아직 없음"));
168
+ }
169
+
170
+ if (borrowed.length) {
171
+ ui.line("");
172
+ ui.line(ui.c.bold(en ? "Borrowed (Hub careers)" : "빌려 쓴 에이전트"));
173
+ table(ui, [[en ? "slug" : "슬러그", en ? "name" : "이름", en ? "runs" : "실행", en ? "last" : "마지막"],
174
+ ...borrowed.map((b) => [b.slug, b.nm || "", String(b.use_count || 0), shortTs(b.last_used_at)])],
175
+ { cap: [26, 22, 6, 16] });
176
+ }
177
+
178
+ ui.line("");
179
+ ui.line(ui.c.dim(en
180
+ ? 'search: /search "<what you need>" · install: /install <slug> · credits: /billing'
181
+ : '검색: /search "<필요한 것>" · 설치: /install <slug> · 크레딧: /billing'));
182
+ }
183
+
184
+ /* ── /settings — 데스크탑 settings 의 터미널 관측 (변경은 기존 명령으로) ── */
185
+ function settings(ui, db, en, ctx) {
186
+ const { activeRuntimeRow, listAvailableCliRuntimes } = require("../runtimes/detect.cjs");
187
+ const { resolvedModelRole } = require("../runtimes/roles.cjs");
188
+ const prefs = ctx.prefs || {};
189
+ ui.ensureNl();
190
+ ui.line(ui.c.bold(en ? "Settings" : "설정"));
191
+
192
+ const active = (() => { try { return activeRuntimeRow(db); } catch { return null; } })();
193
+ const clis = (() => { try { return listAvailableCliRuntimes(); } catch { return []; } })();
194
+ const orch = (() => { try { return resolvedModelRole(db, "orchestrator"); } catch { return null; } })();
195
+ const worker = (() => { try { return resolvedModelRole(db, "worker"); } catch { return null; } })();
196
+ const describe = (sel) => (sel ? `${sel.kind === "byok" ? sel.backend || "byok" : sel.kind}${sel.model ? `/${sel.model}` : ""}` : en ? "not set" : "미설정");
197
+
198
+ table(ui, [
199
+ [en ? "setting" : "항목", en ? "value" : "값"],
200
+ [en ? "language" : "언어", prefs.language || ctx.lang || "en"],
201
+ [en ? "permission" : "권한", prefs.permission || "write"],
202
+ [en ? "active runtime" : "활성 런타임", active ? active.kind : (prefs.runtime || (en ? "auto" : "자동"))],
203
+ [en ? "installed CLIs" : "설치된 CLI", clis.map((c) => c.kind).join(", ") || (en ? "none" : "없음")],
204
+ ["orchestrator", describe(orch)],
205
+ ["worker", describe(worker)],
206
+ ], { cap: [18, 52] });
207
+
208
+ ui.line("");
209
+ ui.line(ui.c.dim(en
210
+ ? "change: /setup (wizard) · /runtime · /model · /effort · /permission · roles set · creds · env"
211
+ : "변경: /setup (마법사) · /runtime · /model · /effort · /permission · roles set · creds · env"));
212
+ ui.line(ui.c.dim(en
213
+ ? "Desktop-only here: theme, mobile pairing QR, auto-update, multimodal providers"
214
+ : "여기서 불가(데스크탑 전용): 테마, 모바일 페어링 QR, 자동 업데이트, 멀티모달 프로바이더"));
215
+ }
216
+
217
+ module.exports = { dashboard, library, marketplace, settings, table };
@@ -127,13 +127,30 @@ class PiUi extends Ui {
127
127
  }
128
128
  }
129
129
 
130
+ /*
131
+ * pi 셸 전용 화면 (D3 Phase 3). 공용 팔레트(ui/palette)에는 넣지 않는다 —
132
+ * 그 정본은 기본 REPL 이 실제로 처리하는 명령만 광고해야 하고,
133
+ * palette-command-coverage-contract 가 그 계약을 잠근다.
134
+ */
135
+ const PI_SCREENS = [
136
+ { name: "dashboard", ko: "관제 대시보드 — 확인 필요·실행 활동·자동화", en: "Dashboard — attention, run activity, automations" },
137
+ { name: "library", ko: "라이브러리 — 에이전트·MCP", en: "Library — agents, MCP" },
138
+ { name: "marketplace", ko: "Hub — 북마크·대여 현황", en: "Hub — bookmarks and borrows" },
139
+ { name: "settings", ko: "설정 현황", en: "Settings overview" },
140
+ ];
141
+
130
142
  function toSlashCommands(lang) {
131
143
  // 팔레트 정본 → pi-tui SlashCommand. "/" 접두는 pi-tui 가 관리하므로 벗긴다.
132
- return palette.SLASH_COMMANDS.map((cmd) => ({
144
+ const base = palette.SLASH_COMMANDS.map((cmd) => ({
133
145
  name: cmd.command.slice(1),
134
146
  description: lang === "ko" ? cmd.ko : cmd.en,
135
147
  argumentHint: cmd.args || undefined,
136
148
  }));
149
+ const seen = new Set(base.map((c) => c.name));
150
+ for (const s of PI_SCREENS) {
151
+ if (!seen.has(s.name)) base.push({ name: s.name, description: lang === "ko" ? s.ko : s.en });
152
+ }
153
+ return base;
137
154
  }
138
155
 
139
156
  async function startPiShell(ctx, opts = {}) {
@@ -260,33 +277,17 @@ async function startPiShell(ctx, opts = {}) {
260
277
  } catch { /* 렌더 실패 → 아래 클래식 폴스루가 텍스트로 보여준다 */ }
261
278
  }
262
279
  }
263
- // 대시보드 (Phase 3-2) — 데스크탑 정직 정지 13종 첫 해제. 로컬 DB 직조회.
264
- if (cmd === "dashboard") {
265
- const chip = (paint, s) => paint(` ${s} `);
266
- const agents = db.prepare(
267
- "SELECT COALESCE(NULLIF(entity_kind,''),'agent') k, builtin, COUNT(*) n FROM installed_agents WHERE COALESCE(visibility,'')!='background' GROUP BY k, builtin").all();
268
- const firms = db.prepare("SELECT COUNT(*) n FROM firms").get().n;
269
- const autos = db.prepare(
270
- "SELECT name, enabled, last_run_at FROM automations ORDER BY COALESCE(last_run_at,'') DESC LIMIT 5").all();
271
- const autoTotal = db.prepare("SELECT COUNT(*) n, SUM(enabled) e FROM automations").get();
272
- const tg = db.prepare("SELECT COUNT(*) n FROM telegram_bindings").get().n;
273
- ui.ensureNl();
274
- ui.line(ui.c.bold(en ? "Dashboard" : "대시보드"));
275
- const local = agents.filter(a => !a.builtin).reduce((s, a) => s + a.n, 0);
276
- const builtin = agents.filter(a => a.builtin).reduce((s, a) => s + a.n, 0);
277
- ui.line(` ${chip(ui.c.inverse, en ? `agents ${local}` : `에이전트 ${local}`)} ${chip(ui.c.dim, `builtin ${builtin}`)} ${chip(ui.c.inverse, en ? `firms ${firms}` : `회사 ${firms}`)} ${chip(ui.c.dim, `telegram ${tg}`)}`);
278
- ui.line("");
279
- ui.line(ui.c.bold(en ? `Automations ${autoTotal.e || 0}/${autoTotal.n} on` : `자동화 ${autoTotal.e || 0}/${autoTotal.n} 켜짐`));
280
- for (const a of autos) {
281
- const st = a.enabled ? ui.c.green("●") : ui.c.faint("○");
282
- ui.line(` ${st} ${a.name}${a.last_run_at ? ui.c.dim(` · ${String(a.last_run_at).slice(0, 16)}`) : ""}`);
283
- }
284
- if (!autos.length) ui.line(ui.c.dim(en ? " (none — /automation add)" : " (없음 — /automation add)"));
285
- ui.line("");
286
- ui.line(ui.c.dim(en
287
- ? "details: /agents · /automation list · /usage · /sessions"
288
- : "자세히: /agents · /automation list · /usage · /sessions"));
289
- return;
280
+ // 데스크탑 대응 화면 (Phase 3) — 정직 정지였던 표면들을 실물로 대체
281
+ {
282
+ const screens = require("./pitui-screens.cjs");
283
+ const SCREEN = {
284
+ dashboard: screens.dashboard,
285
+ library: screens.library,
286
+ marketplace: screens.marketplace,
287
+ bookmarks: screens.marketplace,
288
+ settings: screens.settings,
289
+ };
290
+ if (SCREEN[cmd]) { SCREEN[cmd](ui, db, en, shellCtx); return; }
290
291
  }
291
292
  // 세션 관찰/전환 (증분 2b) — 기본 REPL과 같은 orch/renderer 배선
292
293
  if (cmd === "sessions" || cmd === "tree") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentlas",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "description": "Agentlas project terminal — run project controllers and task-scoped agent teams from the terminal. Standalone: no desktop app process required.",
5
5
  "bin": {
6
6
  "agentlas": "bin/agentlas.cjs"