agentlas 1.0.39 → 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 +33 -0
- package/engine/commands/index.cjs +5 -5
- package/engine/ui/pitui-screens.cjs +217 -0
- package/engine/ui/pitui-shell.cjs +58 -1
- package/engine/ui/repl.cjs +8 -8
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
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
|
+
|
|
22
|
+
## 1.0.40 — 2026-08-11
|
|
23
|
+
|
|
24
|
+
Desktop surfaces reach the pi shell: dashboard and graph view.
|
|
25
|
+
|
|
26
|
+
- `/dashboard` (pi shell): agents/firms/telegram badges + automations panel
|
|
27
|
+
(enabled state, last run) from the local store — first of the thirteen
|
|
28
|
+
Desktop-only honest stops to be lifted.
|
|
29
|
+
- `/graph show <name>` (pi shell): the automation graph renders as Unicode
|
|
30
|
+
box art (grok-mermaid) — condition branches, loop-back edges, and Korean
|
|
31
|
+
labels align correctly. No canvas emulation; editing stays declarative.
|
|
32
|
+
- First-run onboarding now runs before the pi shell starts (sequential
|
|
33
|
+
readline → pi-tui, no stdin contention), so `AGENTLAS_TUI=pi` works for
|
|
34
|
+
brand-new installs too.
|
|
35
|
+
|
|
3
36
|
## 1.0.39 — 2026-08-11
|
|
4
37
|
|
|
5
38
|
pi-tui shell increment 2 + architecture mirror sync.
|
|
@@ -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
|
|
97
|
-
marketplace: "
|
|
98
|
-
library: "Library
|
|
99
|
-
settings: "Settings
|
|
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
|
|
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
|
|
|
@@ -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
|
-
|
|
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 = {}) {
|
|
@@ -232,6 +249,46 @@ async function startPiShell(ctx, opts = {}) {
|
|
|
232
249
|
ui.line(palette.renderPalette(ctx.lang));
|
|
233
250
|
return;
|
|
234
251
|
}
|
|
252
|
+
// 그래프 보기 (Phase 4) — 캔버스를 흉내내지 않는다: mermaid → 유니코드 박스 아트.
|
|
253
|
+
if (cmd === "graph" && rest[0] === "show" && rest[1]) {
|
|
254
|
+
const name = rest.slice(1).join(" ");
|
|
255
|
+
const row = db.prepare(
|
|
256
|
+
"SELECT name, graph_json FROM automations WHERE name = ? AND graph_json IS NOT NULL").get(name);
|
|
257
|
+
if (row) {
|
|
258
|
+
try {
|
|
259
|
+
const g = JSON.parse(row.graph_json);
|
|
260
|
+
const esc = (s) => String(s || "").replace(/["[\]{}|<>]/g, " ").trim().slice(0, 28) || "·";
|
|
261
|
+
const lines = ["flowchart TD"];
|
|
262
|
+
for (const n of g.nodes || []) {
|
|
263
|
+
const label = esc(n.label || n.id);
|
|
264
|
+
lines.push(n.type === "condition" ? ` ${n.id}{${label}}` : ` ${n.id}[${label}]`);
|
|
265
|
+
}
|
|
266
|
+
for (const e of g.edges || []) {
|
|
267
|
+
const lbl = e.sourceHandle === "true" ? "|참|" : e.sourceHandle === "false" ? "|거짓|" : "";
|
|
268
|
+
lines.push(` ${e.source} -->${lbl} ${e.target}`);
|
|
269
|
+
}
|
|
270
|
+
const { render, toAnsi } = require("grok-mermaid");
|
|
271
|
+
const art = toAnsi(render(lines.join("\n")));
|
|
272
|
+
ui.ensureNl();
|
|
273
|
+
ui.line(ui.c.bold(row.name));
|
|
274
|
+
for (const rowLine of (Array.isArray(art) ? art : String(art).split("\n"))) ui.line(rowLine);
|
|
275
|
+
ui.line("");
|
|
276
|
+
return;
|
|
277
|
+
} catch { /* 렌더 실패 → 아래 클래식 폴스루가 텍스트로 보여준다 */ }
|
|
278
|
+
}
|
|
279
|
+
}
|
|
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; }
|
|
291
|
+
}
|
|
235
292
|
// 세션 관찰/전환 (증분 2b) — 기본 REPL과 같은 orch/renderer 배선
|
|
236
293
|
if (cmd === "sessions" || cmd === "tree") {
|
|
237
294
|
require("./repl.cjs").printSessions(shellCtx, orch);
|
package/engine/ui/repl.cjs
CHANGED
|
@@ -86,14 +86,6 @@ function pickProjectController(db, cwd = process.cwd()) {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
async function startRepl(ctx, opts = {}) {
|
|
89
|
-
/*
|
|
90
|
-
* 실험 셸 opt-in (D3 Phase 2 증분 1): AGENTLAS_TUI=pi + TTY 일 때만.
|
|
91
|
-
* 기본 REPL 이 정본이며, 온보딩 전(prefs.onboarded=false)에는 켜지 않는다 —
|
|
92
|
-
* 마법사는 아직 readline 계약이다(Phase 2-2 에서 이전).
|
|
93
|
-
*/
|
|
94
|
-
if (process.env.AGENTLAS_TUI === "pi" && process.stdin.isTTY && ctx.prefs && ctx.prefs.onboarded) {
|
|
95
|
-
return require("./pitui-shell.cjs").startPiShell(ctx, opts);
|
|
96
|
-
}
|
|
97
89
|
// 마법사가 언어를 바꾸면 이 뒤의 문구도 따라가야 한다 — 아래 온보딩 블록에서 갱신한다.
|
|
98
90
|
let en = ctx.lang === "en";
|
|
99
91
|
const ui = ctx.uiInstance;
|
|
@@ -132,6 +124,14 @@ async function startRepl(ctx, opts = {}) {
|
|
|
132
124
|
}
|
|
133
125
|
}
|
|
134
126
|
|
|
127
|
+
/*
|
|
128
|
+
* 실험 셸 opt-in (D3 Phase 2): AGENTLAS_TUI=pi + TTY.
|
|
129
|
+
* 온보딩 마법사(위 readline 블록)가 먼저 끝난 뒤 진입한다 — 순차 실행이라
|
|
130
|
+
* stdin 경합이 없고, 첫 실행 사용자도 pi 셸을 쓸 수 있다(증분 2c).
|
|
131
|
+
*/
|
|
132
|
+
if (process.env.AGENTLAS_TUI === "pi" && process.stdin.isTTY) {
|
|
133
|
+
return require("./pitui-shell.cjs").startPiShell(ctx, opts);
|
|
134
|
+
}
|
|
135
135
|
const orch = new Orchestrator({ db, lang: ctx.lang });
|
|
136
136
|
const renderer = new Renderer(ui);
|
|
137
137
|
let permission = permissions.normalize(opts.permission || (ctx.prefs && ctx.prefs.permission) || "write");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlas",
|
|
3
|
-
"version": "1.0.
|
|
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"
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"author": "Agentlas (https://agentlas.cloud)",
|
|
51
51
|
"license": "Apache-2.0",
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@earendil-works/pi-tui": "0.84.1"
|
|
53
|
+
"@earendil-works/pi-tui": "0.84.1",
|
|
54
|
+
"grok-mermaid": "0.2.2"
|
|
54
55
|
}
|
|
55
56
|
}
|