agentlas 0.5.2 → 0.6.0
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 +48 -6
- package/bin/agentlas.cjs +55 -8
- package/engine/agentlas-api-agent.cjs +1 -1
- package/engine/agentlas-banner.cjs +40 -56
- package/engine/agentlas-capabilities.cjs +3 -0
- package/engine/agentlas-cloud-runtime.cjs +65 -11
- package/engine/agentlas-composer.cjs +112 -44
- package/engine/agentlas-doctor.cjs +40 -12
- package/engine/agentlas-i18n.cjs +136 -12
- package/engine/agentlas-input.cjs +118 -19
- package/engine/agentlas-native-host.cjs +381 -83
- package/engine/agentlas-parity.cjs +315 -45
- package/engine/agentlas-permissions.cjs +90 -0
- package/engine/agentlas-repl.cjs +239 -70
- package/engine/agentlas-tasks.cjs +111 -0
- package/engine/agentlas-tools.cjs +174 -12
- package/engine/agentlas-ui.cjs +352 -23
- package/engine/agentlas.cjs +2819 -351
- package/engine/semver.cjs +64 -0
- package/package.json +1 -1
- package/test/bootstrap-race.cjs +47 -0
- package/test/capture-runtime-guard.cjs +122 -0
- package/test/cloud-asset-restore.cjs +423 -0
- package/test/cloud-cas-client.cjs +333 -0
- package/test/cloud-owner-restore.cjs +183 -0
- package/test/cloud-runtime-paths.cjs +40 -0
- package/test/cloud-save-publish.cjs +453 -0
- package/test/credential-env-regression.cjs +52 -0
- package/test/login-loopback-security.cjs +115 -0
- package/test/mcp-config-isolation.cjs +36 -0
- package/test/permission-mapping.cjs +180 -0
- package/test/route-regression.cjs +121 -0
- package/test/run-api-regression.cjs +322 -0
- package/test/runtime-env-protection.cjs +45 -0
- package/test/semver-precedence.cjs +39 -0
- package/test/smoke.sh +20 -0
- package/test/sqlite-driver-probe.cjs +22 -0
- package/test/terminal-ui-regression.cjs +472 -0
- package/test/timeout-regression.cjs +218 -0
- package/test/tool-workspace-boundary.cjs +165 -0
- package/test/update-safety.cjs +376 -0
|
@@ -20,12 +20,28 @@ function codexHome() {
|
|
|
20
20
|
return process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* 에러 텍스트에서 OAuth 자원 메타데이터로 명시된 호스트만 추출한다.
|
|
25
|
+
* stderr의 모든 URL을 수집하면 도움말·문서 링크와 관련된 정상 플러그인까지
|
|
26
|
+
* 수리 대상이 될 수 있다. 자동 수리는 구조화된 증거가 있을 때만 실행한다.
|
|
27
|
+
*/
|
|
24
28
|
function extractHosts(error) {
|
|
25
29
|
const hosts = new Set();
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
const addUrlHost = (rawUrl) => {
|
|
31
|
+
try {
|
|
32
|
+
hosts.add(new URL(rawUrl.replace(/[\]}>),.;]+$/g, "")).hostname.toLowerCase());
|
|
33
|
+
} catch {
|
|
34
|
+
/* 잘못된 메타데이터 URL은 자동 수리 증거로 쓰지 않음 */
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const patterns = [
|
|
38
|
+
/resource_metadata(?:_url)?\s*[:=]\s*\\?["']?(https?:\/\/[^\s"'\\)>,]+)/gi,
|
|
39
|
+
/(https?:\/\/[^\s"'\\)>,]+\/\.well-known\/oauth-protected-resource(?:[/?#][^\s"'\\)>,]*)?)/gi,
|
|
40
|
+
];
|
|
41
|
+
for (const re of patterns) {
|
|
42
|
+
let m;
|
|
43
|
+
while ((m = re.exec(error || "")) !== null) addUrlHost(m[1]);
|
|
44
|
+
}
|
|
29
45
|
return [...hosts];
|
|
30
46
|
}
|
|
31
47
|
|
|
@@ -47,7 +63,7 @@ function classifyFailure(error) {
|
|
|
47
63
|
return { kind: "codex-config-invalid", hosts: [], badServer: extractBadMcpServer(text) };
|
|
48
64
|
}
|
|
49
65
|
if (/no response for \d+s|auto-aborted/i.test(text)) return { kind: "timeout", hosts: [] };
|
|
50
|
-
if (/CLI exit
|
|
66
|
+
if (/(?:CLI exit|exited with code)\s+[1-9]\d*/i.test(text)) return { kind: "cli-exit", hosts: extractHosts(text) };
|
|
51
67
|
return { kind: "unknown", hosts: [] };
|
|
52
68
|
}
|
|
53
69
|
|
|
@@ -89,7 +105,9 @@ function findOauthPluginsByHost(hosts) {
|
|
|
89
105
|
} catch {
|
|
90
106
|
continue;
|
|
91
107
|
}
|
|
92
|
-
|
|
108
|
+
// 호스트가 정확히 같을 때만 자동 수리한다. 부모/자식 도메인 관계만으로는
|
|
109
|
+
// 어느 플러그인이 실패했는지 증명할 수 없다.
|
|
110
|
+
if (hosts.includes(host)) {
|
|
93
111
|
// cache 디렉토리 "openai-curated-remote"는 config 키에선 "openai-curated".
|
|
94
112
|
hits.push({ pluginKey: `${plugin}@${marketplace.replace(/-remote$/, "")}`, host });
|
|
95
113
|
}
|
|
@@ -111,12 +129,22 @@ function disableCodexPlugin(pluginKey) {
|
|
|
111
129
|
const original = fs.readFileSync(configPath, "utf8");
|
|
112
130
|
const header = `[plugins."${pluginKey}"]`;
|
|
113
131
|
let next;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
133
|
+
const headerMatch = new RegExp(`^[ \\t]*${escapeRegExp(header)}[ \\t]*(?:#.*)?\\r?$`, "m").exec(original);
|
|
134
|
+
if (headerMatch) {
|
|
135
|
+
const headerLineEnd = headerMatch.index + headerMatch[0].length;
|
|
136
|
+
const newlineIndex = original.indexOf("\n", headerLineEnd);
|
|
137
|
+
const bodyStart = newlineIndex === -1 ? original.length : newlineIndex + 1;
|
|
138
|
+
const remaining = original.slice(bodyStart);
|
|
139
|
+
const nextSection = /^[ \t]*\[[^\r\n]+\][ \t]*(?:#.*)?\r?$/m.exec(remaining);
|
|
140
|
+
const sectionEnd = nextSection ? bodyStart + nextSection.index : original.length;
|
|
141
|
+
const section = original.slice(headerMatch.index, sectionEnd);
|
|
142
|
+
const replacedSection = section.replace(
|
|
143
|
+
/^([ \t]*enabled[ \t]*=[ \t]*)true([ \t]*(?:#.*)?)(?=\r?$)/m,
|
|
144
|
+
"$1false$2",
|
|
145
|
+
);
|
|
146
|
+
if (replacedSection === section) return false; // 이미 false거나 해당 섹션에 enabled=true가 없음
|
|
147
|
+
next = original.slice(0, headerMatch.index) + replacedSection + original.slice(sectionEnd);
|
|
120
148
|
} else {
|
|
121
149
|
next = `${original.trimEnd()}\n\n${header}\nenabled = false\n`;
|
|
122
150
|
}
|
package/engine/agentlas-i18n.cjs
CHANGED
|
@@ -16,6 +16,14 @@ const STRINGS = {
|
|
|
16
16
|
"banner.help": "for commands",
|
|
17
17
|
"banner.quit": "to quit",
|
|
18
18
|
"banner.interrupt": "to interrupt",
|
|
19
|
+
"banner.product": "Agent OS terminal",
|
|
20
|
+
"banner.session": "%s · %s · %s",
|
|
21
|
+
"banner.location": "%s · / commands · Shift-Tab permissions",
|
|
22
|
+
"status.title": "Session",
|
|
23
|
+
"status.runtime": "runtime",
|
|
24
|
+
"status.agent": "agent",
|
|
25
|
+
"status.directory": "directory",
|
|
26
|
+
"status.permission": "permission",
|
|
19
27
|
// picker
|
|
20
28
|
"picker.agents": "Agents",
|
|
21
29
|
"picker.companies": "Companies",
|
|
@@ -34,6 +42,15 @@ const STRINGS = {
|
|
|
34
42
|
"stalled": "stream stalled — interrupted (idle timeout)",
|
|
35
43
|
"thinkingWith": "thinking with %s",
|
|
36
44
|
"spinnerStop": "ctrl-c to stop",
|
|
45
|
+
"runtime.thinking": "thinking…",
|
|
46
|
+
"runtime.reasoning": "reasoning…",
|
|
47
|
+
"runtime.starting": "starting %s…",
|
|
48
|
+
"runtime.failed": "failed to run %s: %s",
|
|
49
|
+
"runtime.exited": "%s exited with code %s",
|
|
50
|
+
"runtime.noOutput": "%s returned no output",
|
|
51
|
+
"runtime.rateLimit": "%s rate limit reached",
|
|
52
|
+
"runtime.doctor": "Runtime Doctor: %s",
|
|
53
|
+
"runtime.doctorRetry": "Repair complete — retrying the same request once.",
|
|
37
54
|
"noKey": "No %s API key. Add one in the app (Settings → BYOK), or switch with /runtime.",
|
|
38
55
|
"runtimeSet": "runtime → %s",
|
|
39
56
|
"runtimeNotInstalled": "%s CLI is not installed.",
|
|
@@ -47,6 +64,10 @@ const STRINGS = {
|
|
|
47
64
|
"permSet": "permission → %s",
|
|
48
65
|
"permUsage": "usage: /permission read|write|full",
|
|
49
66
|
"permCurrent": "current permission: %s",
|
|
67
|
+
"permCycleHint": "Shift-Tab to cycle",
|
|
68
|
+
"permCycleConfirm": "permission → %s",
|
|
69
|
+
"permFullArm": "FULL ACCESS IS UNRESTRICTED — press Shift-Tab again within 5 seconds to confirm",
|
|
70
|
+
"permFullConfirm": "FULL ACCESS ON FOR THIS SESSION — approvals and sandboxing are bypassed",
|
|
50
71
|
"sideUsage": "usage: /side <question>",
|
|
51
72
|
"sideNeedsSubject": "pick an agent first, or type the task normally so Agentlas can auto-route it",
|
|
52
73
|
"sideStart": "side question; this answer will not be saved to chat context",
|
|
@@ -66,8 +87,10 @@ const STRINGS = {
|
|
|
66
87
|
"market.loggedIn": "cloud session: signed in",
|
|
67
88
|
"market.loggedOut": "cloud session: signed out — sign in via the app (or set AGENTLAS_SESSION) to install cloud agents",
|
|
68
89
|
"mcp.none": "(no MCP servers configured — add them in the app)",
|
|
69
|
-
"mcp.
|
|
70
|
-
"mcp.
|
|
90
|
+
"mcp.playwright": "Playwright browser",
|
|
91
|
+
"mcp.fullOnly": "full only",
|
|
92
|
+
"mcp.wired": "%s server(s) available only in full-access turns (incl. playwright)",
|
|
93
|
+
"mcp.usage": "add/remove MCP servers in the app; read/write never inject external MCP tools",
|
|
71
94
|
"help.mcp": "list configured MCP servers and which are wired",
|
|
72
95
|
"resume.title": "Resume — recent sessions",
|
|
73
96
|
"resume.none": "(no saved sessions yet — they're saved after each turn)",
|
|
@@ -88,12 +111,27 @@ const STRINGS = {
|
|
|
88
111
|
"team.usage": "usage: /team · /team <agent> <claude-code|codex|gemini|auto>",
|
|
89
112
|
"team.set": "%s → %s",
|
|
90
113
|
"routedImage": "routed to %s for image support",
|
|
114
|
+
"usageBar": "tokens",
|
|
115
|
+
"config.title": "Engine auto engagement — explicit on/off (default: off)",
|
|
116
|
+
"config.storm": "Stormbreaker auto-engage on direct-routed real work",
|
|
117
|
+
"config.network": "hep-network auto-borrow on direct-routed real work",
|
|
118
|
+
"config.usage": "usage: /config storm|network on|off · explicit /storm and /network always work",
|
|
119
|
+
"config.set": "%s → %s",
|
|
120
|
+
"config.autoEngage": "auto-engaging %s (opted in via /config — turn off with /config %s off)",
|
|
121
|
+
"help.config": "explicit on/off for Stormbreaker/hep-network auto engagement",
|
|
91
122
|
"guard.imageWarn": "this agent works with images, but %s can't generate them — try /runtime gemini (or codex), or /team to pin one",
|
|
92
123
|
"multimodal.title": "Multimodal fallback",
|
|
93
124
|
"multimodal.set": "multimodal %s → %s",
|
|
94
125
|
"multimodal.usage": "set with /multimodal set <image|video|audio> <provider-id>",
|
|
95
126
|
// help rows
|
|
96
127
|
"help.talk": "talk to the current agent/company — streaming + tools",
|
|
128
|
+
"help.help": "show commands, shortcuts, and common flows",
|
|
129
|
+
"help.title": "Help",
|
|
130
|
+
"help.intro": "Run local agents with explicit runtime, permission, files, shell, and history controls.",
|
|
131
|
+
"help.commands": "Commands",
|
|
132
|
+
"skills.title": "Skills",
|
|
133
|
+
"permissions.title": "Permissions",
|
|
134
|
+
"permissions.current": "Current",
|
|
97
135
|
"help.skills": "list slash-command skills",
|
|
98
136
|
"help.agents": "list installed agents",
|
|
99
137
|
"help.team": "view/assign each agent's LLM (auto-routed by capability)",
|
|
@@ -114,7 +152,6 @@ const STRINGS = {
|
|
|
114
152
|
"help.multimodal": "show/set image, video, and audio fallback providers",
|
|
115
153
|
"help.import": "import a local folder (agent or team)",
|
|
116
154
|
"help.market": "browse/install marketplace agents",
|
|
117
|
-
"help.install": "install a cloud agent by slug",
|
|
118
155
|
"help.clear": "clear the chat and redraw",
|
|
119
156
|
"help.storm": "run a force-robust Stormbreaker pipeline on a goal",
|
|
120
157
|
"help.build": "build/repair/package an agent or team (Hephaestus)",
|
|
@@ -149,6 +186,31 @@ const STRINGS = {
|
|
|
149
186
|
"help.tab": "autocomplete commands, agents, runtimes, paths",
|
|
150
187
|
"help.arrows": "browse persisted input history",
|
|
151
188
|
"help.ctrlc": "interrupt a running turn, or press twice when idle to quit",
|
|
189
|
+
"help.shiftTab": "cycle read → write → full permission",
|
|
190
|
+
"help.ctrlT": "show or hide the real runtime task/todo list during a turn",
|
|
191
|
+
"palette.title": "Slash commands",
|
|
192
|
+
"palette.search": "type to search",
|
|
193
|
+
"palette.category": "category: %s",
|
|
194
|
+
"palette.examples": "examples: %s",
|
|
195
|
+
"palette.controls": "↑↓ move · Enter run · Tab complete · Esc close",
|
|
196
|
+
"tasks.title": "Tasks",
|
|
197
|
+
"tasks.show": "Ctrl-T show",
|
|
198
|
+
"tasks.hide": "Ctrl-T hide",
|
|
199
|
+
"tasks.pending": "pending",
|
|
200
|
+
"tasks.progress": "in progress",
|
|
201
|
+
"tasks.done": "done",
|
|
202
|
+
"tasks.failed": "failed",
|
|
203
|
+
"category.Help": "Help",
|
|
204
|
+
"category.Session": "Session",
|
|
205
|
+
"category.Discovery": "Discovery",
|
|
206
|
+
"category.Knowledge": "Knowledge",
|
|
207
|
+
"category.Routing": "Routing",
|
|
208
|
+
"category.Settings": "Settings",
|
|
209
|
+
"category.Files": "Files",
|
|
210
|
+
"category.Context": "Context",
|
|
211
|
+
"category.Engine": "Engine",
|
|
212
|
+
"category.Hub": "Hub",
|
|
213
|
+
"category.Health": "Health",
|
|
152
214
|
"compact.noop": "context is already compact (%s message(s))",
|
|
153
215
|
"compact.done": "compacted context: %s → %s message(s)",
|
|
154
216
|
// onboarding wizard
|
|
@@ -159,9 +221,9 @@ const STRINGS = {
|
|
|
159
221
|
"wiz.runtimeInstalled": "installed",
|
|
160
222
|
"wiz.runtimeMissing": "not installed",
|
|
161
223
|
"wiz.permQ": "How much should agents be allowed to do by default?",
|
|
162
|
-
"wiz.permRead": "read —
|
|
163
|
-
"wiz.permWrite": "write —
|
|
164
|
-
"wiz.permFull": "full —
|
|
224
|
+
"wiz.permRead": "read — inspect only; runtime is read-only/plan",
|
|
225
|
+
"wiz.permWrite": "write — edit this workspace in the runtime sandbox; no external MCP (recommended)",
|
|
226
|
+
"wiz.permFull": "full — unrestricted; bypass runtime approvals and sandboxing",
|
|
165
227
|
"wiz.pick": "Enter a number › ",
|
|
166
228
|
"wiz.saved": "All set. You can change any of this later with /runtime, /permission.",
|
|
167
229
|
"wiz.changeLang": "Tip: re-run setup anytime with agentlas setup",
|
|
@@ -170,6 +232,14 @@ const STRINGS = {
|
|
|
170
232
|
"banner.help": "명령 보기",
|
|
171
233
|
"banner.quit": "종료",
|
|
172
234
|
"banner.interrupt": "턴 중단",
|
|
235
|
+
"banner.product": "Agent OS 터미널",
|
|
236
|
+
"banner.session": "%s · %s · %s",
|
|
237
|
+
"banner.location": "%s · / 명령 · Shift-Tab 권한",
|
|
238
|
+
"status.title": "세션",
|
|
239
|
+
"status.runtime": "런타임",
|
|
240
|
+
"status.agent": "에이전트",
|
|
241
|
+
"status.directory": "작업 폴더",
|
|
242
|
+
"status.permission": "권한",
|
|
173
243
|
"picker.agents": "에이전트",
|
|
174
244
|
"picker.companies": "회사",
|
|
175
245
|
"picker.none": "(아직 없음 — Agentlas 앱에서 에이전트를 설치하거나 /import <경로>)",
|
|
@@ -186,6 +256,15 @@ const STRINGS = {
|
|
|
186
256
|
"stalled": "응답이 지연되어 중단했습니다 (idle timeout)",
|
|
187
257
|
"thinkingWith": "%s로 생각 중",
|
|
188
258
|
"spinnerStop": "ctrl-c로 중단",
|
|
259
|
+
"runtime.thinking": "생각 중…",
|
|
260
|
+
"runtime.reasoning": "추론 중…",
|
|
261
|
+
"runtime.starting": "%s 시작 중…",
|
|
262
|
+
"runtime.failed": "%s 실행 실패: %s",
|
|
263
|
+
"runtime.exited": "%s 종료 코드 %s",
|
|
264
|
+
"runtime.noOutput": "%s가 출력 없이 종료됨",
|
|
265
|
+
"runtime.rateLimit": "%s 사용량 한도에 도달함",
|
|
266
|
+
"runtime.doctor": "런타임 진단: %s",
|
|
267
|
+
"runtime.doctorRetry": "자동 수리 완료 — 같은 요청을 한 번 다시 시도합니다.",
|
|
189
268
|
"noKey": "%s API 키가 없습니다. 앱 설정 → BYOK에서 등록하거나 /runtime으로 전환하세요.",
|
|
190
269
|
"runtimeSet": "런타임 → %s",
|
|
191
270
|
"runtimeNotInstalled": "%s CLI가 설치돼 있지 않습니다.",
|
|
@@ -199,6 +278,10 @@ const STRINGS = {
|
|
|
199
278
|
"permSet": "권한 → %s",
|
|
200
279
|
"permUsage": "사용법: /permission read|write|full",
|
|
201
280
|
"permCurrent": "현재 권한: %s",
|
|
281
|
+
"permCycleHint": "Shift-Tab으로 변경",
|
|
282
|
+
"permCycleConfirm": "권한 → %s",
|
|
283
|
+
"permFullArm": "무제한 권한은 승인과 샌드박스를 우회합니다 — 5초 안에 Shift-Tab을 다시 눌러 확인",
|
|
284
|
+
"permFullConfirm": "이 세션에서만 무제한 권한 켜짐 — 승인과 샌드박스를 우회함",
|
|
202
285
|
"sideUsage": "사용법: /side <질문>",
|
|
203
286
|
"sideNeedsSubject": "먼저 에이전트를 고르거나, 그냥 할 일을 입력해서 Agentlas가 자동 라우팅하게 하세요.",
|
|
204
287
|
"sideStart": "사이드 질문입니다. 이 답변은 메인 대화 맥락에 저장하지 않습니다.",
|
|
@@ -218,8 +301,10 @@ const STRINGS = {
|
|
|
218
301
|
"market.loggedIn": "클라우드 세션: 로그인됨",
|
|
219
302
|
"market.loggedOut": "클라우드 세션: 로그아웃 — 클라우드 설치는 앱에서 로그인(또는 AGENTLAS_SESSION) 필요",
|
|
220
303
|
"mcp.none": "(설정된 MCP 서버 없음 — 앱에서 추가)",
|
|
221
|
-
"mcp.
|
|
222
|
-
"mcp.
|
|
304
|
+
"mcp.playwright": "Playwright 브라우저",
|
|
305
|
+
"mcp.fullOnly": "full 전용",
|
|
306
|
+
"mcp.wired": "무제한 권한 턴에서만 %s개 서버 사용 가능 (playwright 포함)",
|
|
307
|
+
"mcp.usage": "MCP 서버 추가/삭제는 앱에서; read/write에는 외부 MCP 도구를 주입하지 않음",
|
|
223
308
|
"help.mcp": "설정된 MCP 서버와 연결 상태 보기",
|
|
224
309
|
"resume.title": "이어하기 — 최근 세션",
|
|
225
310
|
"resume.none": "(저장된 세션 없음 — 각 턴 후 저장됩니다)",
|
|
@@ -240,11 +325,26 @@ const STRINGS = {
|
|
|
240
325
|
"team.usage": "사용법: /team · /team <에이전트> <claude-code|codex|gemini|auto>",
|
|
241
326
|
"team.set": "%s → %s",
|
|
242
327
|
"routedImage": "이미지 지원을 위해 %s로 라우팅",
|
|
328
|
+
"usageBar": "토큰",
|
|
329
|
+
"config.title": "엔진 자동 개입 — 명시적 on/off (기본: off)",
|
|
330
|
+
"config.storm": "직답 라우팅된 실작업에 Stormbreaker 자동 개입",
|
|
331
|
+
"config.network": "직답 라우팅된 실작업에 hep-network 자동 빌림",
|
|
332
|
+
"config.usage": "사용법: /config storm|network on|off · /storm·/network 명시 실행은 항상 가능",
|
|
333
|
+
"config.set": "%s → %s",
|
|
334
|
+
"config.autoEngage": "%s 자동 개입 (/config로 켜져 있음 — 해제: /config %s off)",
|
|
335
|
+
"help.config": "Stormbreaker/hep-network 자동 개입 명시적 on/off",
|
|
243
336
|
"guard.imageWarn": "이 에이전트는 이미지 작업인데 %s는 이미지 생성 불가 — /runtime gemini(또는 codex), 또는 /team으로 고정",
|
|
244
337
|
"multimodal.title": "멀티모달 fallback",
|
|
245
338
|
"multimodal.set": "멀티모달 %s → %s",
|
|
246
339
|
"multimodal.usage": "/multimodal set <image|video|audio> <provider-id> 로 변경",
|
|
247
340
|
"help.talk": "현재 에이전트/회사와 대화 — 스트리밍 + 툴",
|
|
341
|
+
"help.help": "명령·단축키·주요 사용법 보기",
|
|
342
|
+
"help.title": "도움말",
|
|
343
|
+
"help.intro": "런타임·권한·파일·셸·기록을 직접 통제하며 로컬 에이전트를 실행합니다.",
|
|
344
|
+
"help.commands": "명령",
|
|
345
|
+
"skills.title": "스킬",
|
|
346
|
+
"permissions.title": "권한",
|
|
347
|
+
"permissions.current": "현재",
|
|
248
348
|
"help.skills": "slash 명령 스킬 목록",
|
|
249
349
|
"help.agents": "설치된 에이전트 목록",
|
|
250
350
|
"help.team": "에이전트별 LLM 보기/지정 (능력 기반 자동)",
|
|
@@ -265,7 +365,6 @@ const STRINGS = {
|
|
|
265
365
|
"help.multimodal": "이미지·영상·음성 fallback provider 보기/변경",
|
|
266
366
|
"help.import": "로컬 폴더(에이전트/팀) 임포트",
|
|
267
367
|
"help.market": "마켓플레이스 에이전트 보기/설치",
|
|
268
|
-
"help.install": "slug로 클라우드 에이전트 설치",
|
|
269
368
|
"help.clear": "대화 비우고 다시 그리기",
|
|
270
369
|
"help.storm": "목표를 Stormbreaker 견고 파이프라인으로 실행",
|
|
271
370
|
"help.build": "에이전트/팀 빌드·수리·패키징 (Hephaestus)",
|
|
@@ -299,6 +398,31 @@ const STRINGS = {
|
|
|
299
398
|
"help.tab": "명령·에이전트·런타임·경로 자동완성",
|
|
300
399
|
"help.arrows": "세션 간 저장된 입력 기록 탐색",
|
|
301
400
|
"help.ctrlc": "실행 중인 턴 중단, 유휴 시 두 번 누르면 종료",
|
|
401
|
+
"help.shiftTab": "read → write → full 권한 순환",
|
|
402
|
+
"help.ctrlT": "실행 중 실제 런타임 작업 목록 열기/접기",
|
|
403
|
+
"palette.title": "슬래시 명령",
|
|
404
|
+
"palette.search": "입력해서 검색",
|
|
405
|
+
"palette.category": "분류: %s",
|
|
406
|
+
"palette.examples": "예시: %s",
|
|
407
|
+
"palette.controls": "↑↓ 이동 · Enter 실행 · Tab 완성 · Esc 닫기",
|
|
408
|
+
"tasks.title": "작업",
|
|
409
|
+
"tasks.show": "Ctrl-T 열기",
|
|
410
|
+
"tasks.hide": "Ctrl-T 접기",
|
|
411
|
+
"tasks.pending": "대기",
|
|
412
|
+
"tasks.progress": "진행 중",
|
|
413
|
+
"tasks.done": "완료",
|
|
414
|
+
"tasks.failed": "실패",
|
|
415
|
+
"category.Help": "도움말",
|
|
416
|
+
"category.Session": "세션",
|
|
417
|
+
"category.Discovery": "탐색",
|
|
418
|
+
"category.Knowledge": "지식",
|
|
419
|
+
"category.Routing": "라우팅",
|
|
420
|
+
"category.Settings": "설정",
|
|
421
|
+
"category.Files": "파일",
|
|
422
|
+
"category.Context": "맥락",
|
|
423
|
+
"category.Engine": "엔진",
|
|
424
|
+
"category.Hub": "허브",
|
|
425
|
+
"category.Health": "점검",
|
|
302
426
|
"compact.noop": "이미 충분히 compact 상태입니다 (%s개 메시지)",
|
|
303
427
|
"compact.done": "맥락 compact 완료: %s → %s개 메시지",
|
|
304
428
|
"wiz.welcome": "Agentlas에 오신 걸 환영합니다 — 먼저 설정할게요.",
|
|
@@ -308,9 +432,9 @@ const STRINGS = {
|
|
|
308
432
|
"wiz.runtimeInstalled": "설치됨",
|
|
309
433
|
"wiz.runtimeMissing": "미설치",
|
|
310
434
|
"wiz.permQ": "에이전트가 기본적으로 어디까지 할 수 있게 할까요?",
|
|
311
|
-
"wiz.permRead": "read —
|
|
312
|
-
"wiz.permWrite": "write —
|
|
313
|
-
"wiz.permFull": "full —
|
|
435
|
+
"wiz.permRead": "read — 조회만; 런타임 read-only/plan",
|
|
436
|
+
"wiz.permWrite": "write — 런타임 샌드박스 안에서 작업 공간 편집; 외부 MCP 없음 (권장)",
|
|
437
|
+
"wiz.permFull": "full — 무제한; 런타임 승인과 샌드박스 우회",
|
|
314
438
|
"wiz.pick": "번호 입력 › ",
|
|
315
439
|
"wiz.saved": "완료. 나중에 /runtime, /permission으로 언제든 바꿀 수 있어요.",
|
|
316
440
|
"wiz.changeLang": "팁: 언제든 agentlas setup 으로 다시 설정",
|
|
@@ -11,6 +11,7 @@ const fs = require("node:fs");
|
|
|
11
11
|
const os = require("node:os");
|
|
12
12
|
const path = require("node:path");
|
|
13
13
|
const readline = require("node:readline");
|
|
14
|
+
const i18n = require("./agentlas-i18n.cjs");
|
|
14
15
|
|
|
15
16
|
function userDataDir() {
|
|
16
17
|
const override = process.env.AGENTLAS_USER_DATA_DIR;
|
|
@@ -88,11 +89,12 @@ const SLASH_COMMAND_META = [
|
|
|
88
89
|
{ command: "/permission", description: "Set read/write/full permission", category: "Settings", usage: "/permission full", detail: "No argument shows what read, write, and full mean.", aliases: ["/perm"] },
|
|
89
90
|
{ command: "/permissions", description: "Show or set current permission", category: "Settings", usage: "/permissions", detail: "Codex-style permission screen for Agentlas read/write/full." },
|
|
90
91
|
{ command: "/setup", description: "Run first-time setup again", category: "Settings", usage: "/setup", detail: "Re-run language, runtime, and default permission setup in-place." },
|
|
92
|
+
{ command: "/config", description: "Explicit on/off for Stormbreaker and hep-network auto engagement", category: "Settings", usage: "/config [storm|network] [on|off]", detail: "Both engines default to off — no automatic activation. Turn one on to let direct-routed real-work prompts engage it; /storm and /network stay available explicitly either way.", examples: ["/config", "/config storm on", "/config network off"], aliases: ["/toggles"] },
|
|
91
93
|
{ command: "/cwd", description: "Show or change the working folder", category: "Files", usage: "/cwd <path>", detail: "Change the folder used for tools, file mentions, and local commands." },
|
|
92
94
|
{ command: "/memory", description: "Show the memory injected into this run", category: "Context", usage: "/memory", detail: "Print the project memory that Agentlas adds to agent turns." },
|
|
93
95
|
{ command: "/side", description: "Ask a side question without saving it to chat context", category: "Context", usage: "/side <question>", detail: "Runs a one-off answer using current context, then returns without appending to chat history.", aliases: ["/btw"] },
|
|
94
96
|
{ command: "/multimodal", description: "Show or set image, video, and audio fallback providers", category: "Settings", usage: "/multimodal", detail: "Inspect or change fallback providers for media work." },
|
|
95
|
-
{ command: "/mcp", description: "List configured MCP servers", category: "Settings", usage: "/mcp", detail: "Show MCP servers
|
|
97
|
+
{ command: "/mcp", description: "List configured MCP servers", category: "Settings", usage: "/mcp", detail: "Show MCP servers available only during explicit full-access turns." },
|
|
96
98
|
{ command: "/diff", description: "Show the current git diff", category: "Files", usage: "/diff", detail: "Print the working-tree diff for the current cwd." },
|
|
97
99
|
{ command: "/history", description: "Show recent inputs", category: "Session", usage: "/history", detail: "Show persisted terminal input history." },
|
|
98
100
|
{ command: "/resume", description: "Resume a recent runtime session", category: "Session", usage: "/resume [n]", detail: "List recent agent/runtime sessions and continue one (restores the native session thread)." },
|
|
@@ -102,7 +104,6 @@ const SLASH_COMMAND_META = [
|
|
|
102
104
|
{ command: "/clear", description: "Clear the chat and redraw", category: "Session", usage: "/clear", detail: "Clear local conversation state and redraw the Agentlas banner." },
|
|
103
105
|
{ command: "/import", description: "Import a local agent or team folder", category: "Files", usage: "/import <path>", detail: "Install a local agent or team into Agentlas." },
|
|
104
106
|
{ command: "/marketplace", description: "Browse/install marketplace agents", category: "Routing", usage: "/marketplace", detail: "Show how to install agents from the Agentlas cloud marketplace or a local folder.", aliases: ["/market"] },
|
|
105
|
-
{ command: "/install", description: "Install a cloud agent by slug", category: "Routing", usage: "/install <slug>", detail: "Download and install an agent from the Agentlas cloud marketplace by slug." },
|
|
106
107
|
{ command: "/storm", description: "Run a force-robust Stormbreaker pipeline on a goal", category: "Engine", usage: "/storm <goal> [--research]", detail: "Route the goal through Hephaestus Stormbreaker and execute the verified pipeline; --research grounds it with Research Engine evidence." },
|
|
107
108
|
{ command: "/swarm", description: "Fan out an emergent agent swarm on a goal", category: "Engine", usage: "/swarm <goal> [--parallel N]", detail: "Parallel workers share a blackboard and spawn subtasks with ## Spawn; a synthesizer merges results into one answer." },
|
|
108
109
|
{ command: "/build", description: "Build/repair/package an agent or team (Hephaestus)", category: "Engine", usage: "/build <what to build>", detail: "Runs Hephaestus hep-build natively — deep interview, scaffolding, packaging." },
|
|
@@ -120,19 +121,80 @@ const SLASH_COMMANDS = SLASH_COMMAND_META.flatMap((entry) => [entry.command].con
|
|
|
120
121
|
const RUNTIME_SPECS = ["claude-code", "codex", "gemini", "anthropic", "openai", "google", "ollama", "upstage"];
|
|
121
122
|
const PERM_LEVELS = ["read", "write", "full"];
|
|
122
123
|
|
|
124
|
+
const HELP_KEY_BY_COMMAND = {
|
|
125
|
+
"/help": "help.help",
|
|
126
|
+
"/status": "help.status",
|
|
127
|
+
"/skills": "help.skills",
|
|
128
|
+
"/career-graph": "help.careerGraph",
|
|
129
|
+
"/ontology": "help.ontology",
|
|
130
|
+
"/agents": "help.agents",
|
|
131
|
+
"/team": "help.team",
|
|
132
|
+
"/agent": "help.agent",
|
|
133
|
+
"/firms": "help.firms",
|
|
134
|
+
"/firm": "help.firms",
|
|
135
|
+
"/runtime": "help.runtime",
|
|
136
|
+
"/model": "help.model",
|
|
137
|
+
"/effort": "help.effort",
|
|
138
|
+
"/permission": "help.permission",
|
|
139
|
+
"/permissions": "help.permissions",
|
|
140
|
+
"/setup": "help.setup",
|
|
141
|
+
"/config": "help.config",
|
|
142
|
+
"/cwd": "help.cwd",
|
|
143
|
+
"/memory": "help.memory",
|
|
144
|
+
"/side": "help.side",
|
|
145
|
+
"/multimodal": "help.multimodal",
|
|
146
|
+
"/mcp": "help.mcp",
|
|
147
|
+
"/diff": "help.diff",
|
|
148
|
+
"/history": "help.history",
|
|
149
|
+
"/resume": "help.resume",
|
|
150
|
+
"/compact": "help.compact",
|
|
151
|
+
"/cost": "help.cost",
|
|
152
|
+
"/keybindings": "help.keybindings",
|
|
153
|
+
"/clear": "help.clear",
|
|
154
|
+
"/import": "help.import",
|
|
155
|
+
"/marketplace": "help.market",
|
|
156
|
+
"/install": "help.install",
|
|
157
|
+
"/storm": "help.storm",
|
|
158
|
+
"/swarm": "help.swarm",
|
|
159
|
+
"/build": "help.build",
|
|
160
|
+
"/route": "help.route",
|
|
161
|
+
"/research": "help.research",
|
|
162
|
+
"/search": "help.search",
|
|
163
|
+
"/network": "help.network",
|
|
164
|
+
"/browser": "help.browser",
|
|
165
|
+
"/connect": "help.connect",
|
|
166
|
+
"/doctor": "help.doctor",
|
|
167
|
+
"/exit": "help.exit",
|
|
168
|
+
};
|
|
169
|
+
|
|
123
170
|
function uniqStartsWith(cands, token) {
|
|
124
171
|
const hits = cands.filter((c) => c.startsWith(token));
|
|
125
172
|
return hits.length ? hits : cands;
|
|
126
173
|
}
|
|
127
174
|
|
|
128
|
-
function
|
|
175
|
+
function localizeSlashEntry(entry, lang) {
|
|
176
|
+
const helpKey = HELP_KEY_BY_COMMAND[entry.command];
|
|
177
|
+
const description = helpKey ? i18n.t(lang, helpKey) : entry.description;
|
|
178
|
+
const category = entry.category ? i18n.t(lang, `category.${entry.category}`) : entry.category;
|
|
179
|
+
return {
|
|
180
|
+
...entry,
|
|
181
|
+
description,
|
|
182
|
+
category,
|
|
183
|
+
// English keeps the longer authored detail. Other languages must not fall back to
|
|
184
|
+
// an English paragraph under an otherwise-localized command palette.
|
|
185
|
+
detail: lang === "en" ? entry.detail : description,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function slashCommandEntries(lang = "en") {
|
|
129
190
|
const rows = [];
|
|
130
|
-
for (const
|
|
191
|
+
for (const rawEntry of SLASH_COMMAND_META) {
|
|
192
|
+
const entry = localizeSlashEntry(rawEntry, lang);
|
|
131
193
|
rows.push({ ...entry, aliasOf: null });
|
|
132
194
|
for (const alias of entry.aliases || []) {
|
|
133
195
|
rows.push({
|
|
134
196
|
command: alias,
|
|
135
|
-
description: `Alias for ${entry.command}`,
|
|
197
|
+
description: lang === "ko" ? `${entry.command} 별칭` : `Alias for ${entry.command}`,
|
|
136
198
|
category: entry.category,
|
|
137
199
|
usage: alias + (entry.usage && entry.usage.includes(" ") ? entry.usage.slice(entry.usage.indexOf(" ")) : ""),
|
|
138
200
|
detail: entry.detail,
|
|
@@ -152,11 +214,11 @@ function slashCommandQuery(line) {
|
|
|
152
214
|
return value;
|
|
153
215
|
}
|
|
154
216
|
|
|
155
|
-
function slashCommandSuggestions(line, limit = 12) {
|
|
217
|
+
function slashCommandSuggestions(line, limit = 12, lang = "en") {
|
|
156
218
|
const query = slashCommandQuery(line);
|
|
157
219
|
if (query == null) return [];
|
|
158
220
|
const q = query.toLowerCase();
|
|
159
|
-
const entries = slashCommandEntries();
|
|
221
|
+
const entries = slashCommandEntries(lang);
|
|
160
222
|
const starts = entries.filter((entry) => entry.command.toLowerCase().startsWith(q));
|
|
161
223
|
const contains = entries.filter(
|
|
162
224
|
(entry) =>
|
|
@@ -167,9 +229,9 @@ function slashCommandSuggestions(line, limit = 12) {
|
|
|
167
229
|
}
|
|
168
230
|
|
|
169
231
|
function padVisible(value, width) {
|
|
170
|
-
const
|
|
171
|
-
if (
|
|
172
|
-
return value + " ".repeat(width -
|
|
232
|
+
const current = visibleWidthLite(value);
|
|
233
|
+
if (current >= width) return value;
|
|
234
|
+
return value + " ".repeat(width - current);
|
|
173
235
|
}
|
|
174
236
|
|
|
175
237
|
function stripAnsiLite(value) {
|
|
@@ -179,8 +241,40 @@ function stripAnsiLite(value) {
|
|
|
179
241
|
|
|
180
242
|
function truncateVisible(value, width) {
|
|
181
243
|
const clean = stripAnsiLite(value);
|
|
182
|
-
if (clean
|
|
183
|
-
|
|
244
|
+
if (visibleWidthLite(clean) <= width) return value;
|
|
245
|
+
let out = "";
|
|
246
|
+
let used = 0;
|
|
247
|
+
const room = Math.max(0, width - 1);
|
|
248
|
+
for (const ch of clean) {
|
|
249
|
+
const cells = cellWidthLite(ch);
|
|
250
|
+
if (used + cells > room) break;
|
|
251
|
+
out += ch;
|
|
252
|
+
used += cells;
|
|
253
|
+
}
|
|
254
|
+
return out + "…";
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function cellWidthLite(ch) {
|
|
258
|
+
const cp = ch.codePointAt(0);
|
|
259
|
+
if (cp < 0x20) return 0;
|
|
260
|
+
return (
|
|
261
|
+
(cp >= 0x1100 && cp <= 0x115f) ||
|
|
262
|
+
(cp >= 0x2e80 && cp <= 0x303e) ||
|
|
263
|
+
(cp >= 0x3041 && cp <= 0x33ff) ||
|
|
264
|
+
(cp >= 0x3400 && cp <= 0x4dbf) ||
|
|
265
|
+
(cp >= 0x4e00 && cp <= 0x9fff) ||
|
|
266
|
+
(cp >= 0xac00 && cp <= 0xd7a3) ||
|
|
267
|
+
(cp >= 0xf900 && cp <= 0xfaff) ||
|
|
268
|
+
(cp >= 0xfe30 && cp <= 0xfe4f) ||
|
|
269
|
+
(cp >= 0xff00 && cp <= 0xff60) ||
|
|
270
|
+
(cp >= 0x1f300 && cp <= 0x1faff)
|
|
271
|
+
) ? 2 : 1;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function visibleWidthLite(value) {
|
|
275
|
+
let width = 0;
|
|
276
|
+
for (const ch of stripAnsiLite(value)) width += cellWidthLite(ch);
|
|
277
|
+
return width;
|
|
184
278
|
}
|
|
185
279
|
|
|
186
280
|
function renderSlashPalette(rows, selectedIndex, opts = {}) {
|
|
@@ -194,32 +288,35 @@ function renderSlashPalette(rows, selectedIndex, opts = {}) {
|
|
|
194
288
|
inverse: (s) => String(s),
|
|
195
289
|
};
|
|
196
290
|
const c = { ...fallbackColors, ...(opts.colors || {}) };
|
|
291
|
+
const lang = opts.lang || "en";
|
|
197
292
|
const commandWidth = Math.min(24, Math.max(16, rows.reduce((n, row) => Math.max(n, row.command.length), 0) + 2));
|
|
198
293
|
const descWidth = Math.max(12, columns - commandWidth - 8);
|
|
199
294
|
const lineWidth = Math.min(columns - 1, commandWidth + descWidth + 5);
|
|
200
295
|
const selected = rows[Math.max(0, Math.min(selectedIndex, rows.length - 1))] || rows[0];
|
|
201
296
|
const out = [
|
|
202
|
-
c.faint(
|
|
297
|
+
c.faint(truncateVisible(`${i18n.t(lang, "palette.title")} ${i18n.t(lang, "palette.search")}`, lineWidth)),
|
|
203
298
|
c.faint("─".repeat(lineWidth)),
|
|
204
299
|
];
|
|
205
300
|
rows.forEach((row, index) => {
|
|
206
301
|
const command = padVisible(row.command, commandWidth);
|
|
207
302
|
const desc = truncateVisible(row.description, descWidth);
|
|
208
303
|
const body = " " + c.blue(command) + c.text(desc);
|
|
209
|
-
out.push(index === selectedIndex ? c.inverse(body
|
|
304
|
+
out.push(index === selectedIndex ? c.inverse(padVisible(body, lineWidth)) : body);
|
|
210
305
|
});
|
|
211
306
|
out.push(c.faint("─".repeat(lineWidth)));
|
|
212
307
|
if (selected) {
|
|
213
308
|
const usage = truncateVisible(selected.usage || selected.command, lineWidth - 2);
|
|
214
309
|
const detail = truncateVisible(selected.detail || selected.description || "", lineWidth - 2);
|
|
215
|
-
const category = selected.category ?
|
|
216
|
-
|
|
310
|
+
const category = selected.category ? i18n.t(lang, "palette.category", selected.category) : "";
|
|
311
|
+
const categoryRoom = Math.max(0, lineWidth - visibleWidthLite(usage) - 3);
|
|
312
|
+
const categoryText = categoryRoom > 0 ? truncateVisible(category, categoryRoom) : "";
|
|
313
|
+
out.push(" " + c.text(usage) + (categoryText ? c.dim(" " + categoryText) : ""));
|
|
217
314
|
if (detail) out.push(" " + c.dim(detail));
|
|
218
315
|
if (selected.examples && selected.examples.length) {
|
|
219
|
-
out.push(" " +
|
|
316
|
+
out.push(c.dim(truncateVisible(" " + i18n.t(lang, "palette.examples", selected.examples.slice(0, 2).join(" | ")), lineWidth)));
|
|
220
317
|
}
|
|
221
318
|
}
|
|
222
|
-
out.push(c.dim("
|
|
319
|
+
out.push(c.dim(truncateVisible(" " + i18n.t(lang, "palette.controls"), lineWidth)));
|
|
223
320
|
return out.join("\n");
|
|
224
321
|
}
|
|
225
322
|
|
|
@@ -244,7 +341,7 @@ function attachSlashPalette(rl, opts = {}) {
|
|
|
244
341
|
|
|
245
342
|
function rows() {
|
|
246
343
|
if (!state.enabled) return [];
|
|
247
|
-
return slashCommandSuggestions(rl.line || "");
|
|
344
|
+
return slashCommandSuggestions(rl.line || "", 12, opts.lang || (opts.ui && opts.ui.lang) || "en");
|
|
248
345
|
}
|
|
249
346
|
function active() {
|
|
250
347
|
return rows().length > 0 && state.dismissedForLine !== (rl.line || "");
|
|
@@ -276,6 +373,7 @@ function attachSlashPalette(rl, opts = {}) {
|
|
|
276
373
|
const body = renderSlashPalette(list, state.selected, {
|
|
277
374
|
columns: stream.columns || process.stdout.columns || 88,
|
|
278
375
|
colors,
|
|
376
|
+
lang: opts.lang || (opts.ui && opts.ui.lang) || "en",
|
|
279
377
|
});
|
|
280
378
|
stream.write("\x1b7\x1b[E\x1b[0J" + body + "\x1b8");
|
|
281
379
|
state.visible = true;
|
|
@@ -365,6 +463,7 @@ function isAbsolutePathTask(line) {
|
|
|
365
463
|
const value = String(line || "").trim();
|
|
366
464
|
if (!value.startsWith("/")) return false;
|
|
367
465
|
const first = value.split(/\s+/)[0] || "";
|
|
466
|
+
if (first === "/") return false;
|
|
368
467
|
if (!first || SLASH_COMMANDS.includes(first)) return false;
|
|
369
468
|
if (!path.isAbsolute(first)) return false;
|
|
370
469
|
if (fs.existsSync(first)) return true;
|