deel-local-cli 1.7.0 → 1.8.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.ko.md +1182 -0
- package/README.md +815 -690
- package/bin/deel.js +23 -0
- package/package.json +3 -3
- package/src/acp/map.js +1 -1
- package/src/agent/loop.js +16 -5
- package/src/agent/session.js +15 -9
- package/src/backend/adapter.js +71 -5
- package/src/backend/retry.js +3 -0
- package/src/backend/scanui.js +1 -1
- package/src/commands.js +63 -34
- package/src/completion.js +20 -2
- package/src/i18n/en.js +154 -1
- package/src/i18n/index.js +22 -0
- package/src/i18n/ja.js +146 -1
- package/src/i18n/ko.js +167 -1
- package/src/i18n/zh.js +146 -1
- package/src/oneshot.js +11 -2
- package/src/pack/selfpack.js +1 -1
- package/src/repl.js +80 -26
- package/src/reset.js +397 -0
- package/src/safety/audit.js +5 -2
- package/src/safety/authcmd.js +305 -0
- package/src/safety/keystore.js +53 -0
- package/src/safety/undo.js +3 -2
- package/src/tools/desc.en.js +19 -14
- package/src/tools/index.js +112 -43
- package/src/tools/jobs.js +17 -13
- package/src/tools/lsp.js +5 -4
- package/src/tools/outline.js +7 -3
- package/src/tools/task.js +10 -6
- package/src/tools/todo.js +9 -2
- package/src/tools/verify.js +9 -3
- package/src/tools/webfetch.js +2 -1
- package/src/ui/export.js +1 -1
- package/README.en.md +0 -1117
package/bin/deel.js
CHANGED
|
@@ -14,6 +14,7 @@ import { parseSize } from '../src/backend/ctxsize.js';
|
|
|
14
14
|
import { runSessions } from '../src/agent/sessionui.js';
|
|
15
15
|
import { acp } from '../src/acp/serve.js';
|
|
16
16
|
import { runCompletion } from '../src/completion.js';
|
|
17
|
+
import { runReset } from '../src/reset.js';
|
|
17
18
|
|
|
18
19
|
const MIN_NODE = 20;
|
|
19
20
|
|
|
@@ -138,6 +139,7 @@ function help() {
|
|
|
138
139
|
say(` ${c.cyan('deel setup')} 연결 설정 (주소·키·모델)`);
|
|
139
140
|
say(` ${c.cyan('deel status')} 연결 상태 보기`);
|
|
140
141
|
say(` ${c.cyan('deel diagnose')} 저장된 연결로 진단 다시 돌리기`);
|
|
142
|
+
say(` ${c.cyan('deel reset')} 설정·기억·기록 지우기 ${c.gray('(그냥 치면 보여만 줍니다)')}`);
|
|
141
143
|
say(` ${c.cyan('deel completion <셸>')} 탭 완성 스크립트 ${c.gray('(bash · zsh · powershell)')}`);
|
|
142
144
|
say(` ${c.cyan('deel --version')} 판 번호 ${c.gray('(-v 도 됩니다)')}`);
|
|
143
145
|
say('');
|
|
@@ -155,6 +157,19 @@ function help() {
|
|
|
155
157
|
say(` ${c.gray('에디터 설정에 이 명령을 적어 두면 그 안에서 deel 이 돕니다.')}`);
|
|
156
158
|
say(` ${c.gray('승인 창·모드 고르개·고친 파일 링크가 에디터 것으로 그려집니다.')}`);
|
|
157
159
|
say('');
|
|
160
|
+
say(` ${c.bold('되돌아가기')} ${c.gray('— 지우려고 다시 깔지 않아도 됩니다')}`);
|
|
161
|
+
say('');
|
|
162
|
+
say(` ${c.cyan('deel reset')} 무엇이 얼마나 있는지 보여 주고 묻습니다 ${c.gray('(아무것도 안 지웁니다)')}`);
|
|
163
|
+
say(` ${c.gray('deel reset model')} 연결·프로필·잠긴 열쇠`);
|
|
164
|
+
say(` ${c.gray('deel reset memory')} 기억 (.deel/memory.md)`);
|
|
165
|
+
say(` ${c.gray('deel reset sessions')} 대화 기록`);
|
|
166
|
+
say(` ${c.gray('deel reset learned')} 배운 것 (이 PC + 이 폴더)`);
|
|
167
|
+
say(` ${c.gray('deel reset plugins')} 설치한 플러그인`);
|
|
168
|
+
say(` ${c.gray('deel reset all')} 위 전부 ${c.gray('— 플러그인·되돌리기·감사기록은 빼고')}`);
|
|
169
|
+
say(` ${c.gray('--hard')} ${c.yellow('되돌리기 스냅샷·감사기록까지')}. all 에서만 씁니다`);
|
|
170
|
+
say(` ${c.gray('--yes')} 안 묻고 지웁니다 (스크립트용)`);
|
|
171
|
+
say(` ${c.gray('.deel/mcp.json · .deelignore · DEEL.md 는 사람이 적은 것이라 어떤 길로도 안 건드립니다.')}`);
|
|
172
|
+
say('');
|
|
158
173
|
say(` ${c.bold('사내 반입')}`);
|
|
159
174
|
say('');
|
|
160
175
|
say(` ${c.cyan('deel audit')} 의존성·네트워크 호출 자리 심사서 ${c.gray('(사람이 읽는 글)')}`);
|
|
@@ -318,6 +333,14 @@ async function main() {
|
|
|
318
333
|
case 'sessions':
|
|
319
334
|
case 'ls':
|
|
320
335
|
return runSessions(flags);
|
|
336
|
+
/*
|
|
337
|
+
* 초기화.
|
|
338
|
+
*
|
|
339
|
+
* setup 과 달리 **설정을 안 읽는다.** 초기화를 찾는 까닭이 대개 설정이
|
|
340
|
+
* 깨져서인데, 읽고 시작하면 그 자리에서 못 넘어간다.
|
|
341
|
+
*/
|
|
342
|
+
case 'reset':
|
|
343
|
+
return runReset(args, flags);
|
|
321
344
|
/*
|
|
322
345
|
* 탭 완성 스크립트를 낸다 (src/completion.js).
|
|
323
346
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deel-local-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "로컬 모델·사내 게이트웨이 전용 코딩 에이전트 CLI — 외부 의존성 0개 / Zero-dependency coding agent CLI for local LLMs and private gateways",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"bin",
|
|
15
15
|
"src",
|
|
16
16
|
"README.md",
|
|
17
|
-
"README.
|
|
17
|
+
"README.ko.md",
|
|
18
18
|
"LICENSE"
|
|
19
19
|
],
|
|
20
20
|
"keywords": [
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"verify": "node test/no-bundle.test.js && node test/network.test.js && node test/web.test.js",
|
|
52
52
|
"bench": "node test/edit-bench.js",
|
|
53
53
|
"demo": "node test/demo.js",
|
|
54
|
-
"check": "node --check bin/deel.js && node --check src/repl.js && node --check src/oneshot.js && node --check src/commands.js && node --check src/agent/loop.js && node --check src/agent/session.js && node --check src/agent/effort.js && node --check src/agent/modes.js && node --check src/agent/route.js && node --check src/agent/askcheck.js && node --check src/agent/asks.js && node --check src/agent/models.js && node --check src/agent/grade.js && node --check src/agent/mention.js && node --check src/agent/project.js && node --check src/tools/encoding.js && node --check src/tools/excel-com.js && node --check src/tools/excel.js && node --check src/tools/xlsx.js && node --check src/tools/docs.js && node --check src/tools/convert.js && node --check src/tools/pdf.js && node --check src/tools/clipboard.js && node --check src/agent/compact.js && node --check src/agent/store.js && node --check src/agent/sessionui.js && node --check src/backend/adapter.js && node --check src/backend/ctxsize.js && node --check src/backend/probe.js && node --check src/backend/detect.js && node --check src/backend/http.js && node --check src/backend/retry.js && node --check src/backend/proxy.js && node --check src/backend/azure.js && node --check src/backend/vision.js && node --check src/backend/quota.js && node --check src/backend/price.js && node --check src/backend/scan.js && node --check src/backend/scanui.js && node --check src/tools/index.js && node --check src/tools/desc.en.js && node --check src/tools/lsp.js && node --check src/lsp/rpc.js && node --check src/lsp/servers.js && node --check src/lsp/client.js && node --check src/lsp/diag.js && node --check src/tools/fsutil.js && node --check src/tools/ignore.js && node --check src/tools/spawn.js && node --check src/tools/fastgrep.js && node --check src/tools/edit-match.js && node --check src/tools/webfetch.js && node --check src/tools/todo.js && node --check src/tools/jobs.js && node --check src/tools/shell.js && node --check src/skills/discover.js && node --check src/plugins/manage.js && node --check src/pack/zip.js && node --check src/pack/tar.js && node --check src/pack/selfpack.js && node --check src/safety/guard.js && node --check src/safety/undo.js && node --check src/safety/audit.js && node --check src/safety/keystore.js && node --check src/safety/policy.js && node --check src/safety/network.js && node --check src/safety/runmode.js && node --check src/providers/index.js && node --check src/providers/openai.js && node --check src/providers/anthropic.js && node --check src/providers/gemini.js && node --check src/providers/bedrock.js && node --check src/providers/custom.js && node --check src/setup.js && node --check src/report.js && node --check src/config.js && node --check src/completion.js && node --check src/ui/diff.js && node --check src/ui/ansi.js && node --check src/ui/status.js && node --check src/ui/level.js && node --check src/ui/prompt.js && node --check src/ui/spinner.js && node --check src/ui/motion.js && node --check src/ui/office.js && node --check src/ui/notify.js && node --check src/ui/intro.js && node --check src/ui/banner.js && node --check src/ui/export.js && node --check src/i18n/index.js && node --check src/i18n/ko.js && node --check src/i18n/en.js && node --check src/i18n/ja.js && node --check src/i18n/zh.js && node --check src/preview/serve.js && node --check src/ui/working.js && node --check src/ui/inputbox.js && node --check src/ui/wrap.js && node --check src/ui/pastechip.js && node --check src/ui/histline.js && node --check src/ui/md.js && node --check src/agent/threads.js && node --check src/agent/evolve.js && node --check src/agent/pins.js && node --check src/agent/card.js && node --check src/agent/preset.js && node --check src/agent/evidence.js && node --check src/agent/commit.js && node --check src/agent/review.js && node --check src/acp/jsonrpc.js && node --check src/acp/map.js && node --check src/acp/serve.js && node --check src/pack/sbom.js && node --check src/safety/secrets.js && echo OK",
|
|
54
|
+
"check": "node --check bin/deel.js && node --check src/repl.js && node --check src/oneshot.js && node --check src/commands.js && node --check src/agent/loop.js && node --check src/agent/session.js && node --check src/agent/effort.js && node --check src/agent/modes.js && node --check src/agent/route.js && node --check src/agent/askcheck.js && node --check src/agent/asks.js && node --check src/agent/models.js && node --check src/agent/grade.js && node --check src/agent/mention.js && node --check src/agent/project.js && node --check src/tools/encoding.js && node --check src/tools/excel-com.js && node --check src/tools/excel.js && node --check src/tools/xlsx.js && node --check src/tools/docs.js && node --check src/tools/convert.js && node --check src/tools/pdf.js && node --check src/tools/clipboard.js && node --check src/agent/compact.js && node --check src/agent/store.js && node --check src/agent/sessionui.js && node --check src/backend/adapter.js && node --check src/backend/ctxsize.js && node --check src/backend/probe.js && node --check src/backend/detect.js && node --check src/backend/http.js && node --check src/backend/retry.js && node --check src/backend/proxy.js && node --check src/backend/azure.js && node --check src/backend/vision.js && node --check src/backend/quota.js && node --check src/backend/price.js && node --check src/backend/scan.js && node --check src/backend/scanui.js && node --check src/tools/index.js && node --check src/tools/desc.en.js && node --check src/tools/lsp.js && node --check src/lsp/rpc.js && node --check src/lsp/servers.js && node --check src/lsp/client.js && node --check src/lsp/diag.js && node --check src/tools/fsutil.js && node --check src/tools/ignore.js && node --check src/tools/spawn.js && node --check src/tools/fastgrep.js && node --check src/tools/edit-match.js && node --check src/tools/webfetch.js && node --check src/tools/todo.js && node --check src/tools/jobs.js && node --check src/tools/shell.js && node --check src/skills/discover.js && node --check src/plugins/manage.js && node --check src/pack/zip.js && node --check src/pack/tar.js && node --check src/pack/selfpack.js && node --check src/safety/guard.js && node --check src/safety/undo.js && node --check src/safety/audit.js && node --check src/safety/keystore.js && node --check src/safety/authcmd.js && node --check src/safety/policy.js && node --check src/safety/network.js && node --check src/safety/runmode.js && node --check src/providers/index.js && node --check src/providers/openai.js && node --check src/providers/anthropic.js && node --check src/providers/gemini.js && node --check src/providers/bedrock.js && node --check src/providers/custom.js && node --check src/setup.js && node --check src/report.js && node --check src/config.js && node --check src/completion.js && node --check src/ui/diff.js && node --check src/ui/ansi.js && node --check src/ui/status.js && node --check src/ui/level.js && node --check src/ui/prompt.js && node --check src/ui/spinner.js && node --check src/ui/motion.js && node --check src/ui/office.js && node --check src/ui/notify.js && node --check src/ui/intro.js && node --check src/ui/banner.js && node --check src/ui/export.js && node --check src/i18n/index.js && node --check src/i18n/ko.js && node --check src/i18n/en.js && node --check src/i18n/ja.js && node --check src/i18n/zh.js && node --check src/preview/serve.js && node --check src/ui/working.js && node --check src/ui/inputbox.js && node --check src/ui/wrap.js && node --check src/ui/pastechip.js && node --check src/ui/histline.js && node --check src/ui/md.js && node --check src/agent/threads.js && node --check src/agent/evolve.js && node --check src/agent/pins.js && node --check src/agent/card.js && node --check src/agent/preset.js && node --check src/agent/evidence.js && node --check src/agent/commit.js && node --check src/agent/review.js && node --check src/acp/jsonrpc.js && node --check src/acp/map.js && node --check src/acp/serve.js && node --check src/pack/sbom.js && node --check src/safety/secrets.js && node --check src/version.js && node --check src/agent/budget.js && node --check src/agent/memory.js && node --check src/agent/recall.js && node --check src/agent/salvage.js && node --check src/backend/learn.js && node --check src/backend/mcp.js && node --check src/tools/task.js && node --check src/tools/outline.js && node --check src/tools/verify.js && node --check src/ui/approve.js && node --check src/ui/complete.js && node --check src/ui/screen.js && node --check src/reset.js && echo OK",
|
|
55
55
|
"coverage": "node test/coverage.mjs",
|
|
56
56
|
"prepublishOnly": "npm run check && npm test"
|
|
57
57
|
},
|
package/src/acp/map.js
CHANGED
|
@@ -55,7 +55,7 @@ export function 도구갈래(이름) {
|
|
|
55
55
|
*/
|
|
56
56
|
export function 도구이름표(이름, 인자) {
|
|
57
57
|
const a = 인자 ?? {};
|
|
58
|
-
const 첫 = a.file_path ?? a.pattern ?? a.path ?? a.url ?? a.name ?? a.목적
|
|
58
|
+
const 첫 = a.file_path ?? a.pattern ?? a.path ?? a.url ?? a.name ?? a.purpose ?? a.목적
|
|
59
59
|
?? (a.command ? String(a.command).replace(/\s+/g, ' ') : null)
|
|
60
60
|
?? (Array.isArray(a.files) && a.files.length
|
|
61
61
|
? `${a.files[0]?.file_path ?? '?'}${a.files.length > 1 ? ` 외 ${a.files.length - 1}개` : ''}`
|
package/src/agent/loop.js
CHANGED
|
@@ -215,7 +215,8 @@ export async function* run(session, ctx, userText, { signal = null, 깊이 = 0,
|
|
|
215
215
|
// 파일 하나가 아니라 **그 파일** 을 센다. 이름만 세면 서로 다른 파일 세 개를
|
|
216
216
|
// 고치다 실패한 것이 한 덩어리로 뭉쳐 턴이 죽는다. 다섯 군데 중 두 군데만
|
|
217
217
|
// 고쳐 놓고 '헛돌고 있어 멈췄습니다' 가 되는 것이 그 모습이다.
|
|
218
|
-
const 어디 = call.args?.file_path ?? call.args?.path ?? call.args?.pattern ?? call.args?.command
|
|
218
|
+
const 어디 = call.args?.file_path ?? call.args?.path ?? call.args?.pattern ?? call.args?.command
|
|
219
|
+
?? call.args?.purpose ?? call.args?.목적 ?? '';
|
|
219
220
|
const 서명 = `${call.name}|${이유}|${String(어디).slice(0, 200)}`;
|
|
220
221
|
const n = (막힘.get(서명) ?? 0) + 1;
|
|
221
222
|
막힘.set(서명, n);
|
|
@@ -531,6 +532,16 @@ export async function* run(session, ctx, userText, { signal = null, 깊이 = 0,
|
|
|
531
532
|
think: thinkFor(conn, think),
|
|
532
533
|
maxTokens,
|
|
533
534
|
signal,
|
|
535
|
+
/*
|
|
536
|
+
* 열쇠받기(safety/authcmd.js)가 걸린 연결에서 쓴다.
|
|
537
|
+
*
|
|
538
|
+
* 두 갈래를 위층으로 올린다 — 「부를까요」 를 사람에게 묻는 길과,
|
|
539
|
+
* 「지금 사내 로그인을 기다리는 중」 을 화면에 적는 길. 여기서 화면을
|
|
540
|
+
* 직접 만지지 않는 것은, 이 루프가 대화 화면 말고 oneshot·ACP 에서도
|
|
541
|
+
* 돌기 때문이다. 그쪽에는 물어볼 사람이 없다.
|
|
542
|
+
*/
|
|
543
|
+
열쇠물어보기: session.열쇠물어보기 ?? null,
|
|
544
|
+
onAuth: (것) => { session.onAuth?.(것); },
|
|
534
545
|
});
|
|
535
546
|
|
|
536
547
|
let msg;
|
|
@@ -945,8 +956,8 @@ export async function* run(session, ctx, userText, { signal = null, 깊이 = 0,
|
|
|
945
956
|
*/
|
|
946
957
|
if (실행할것.length === 1 && 실행할것[0].name === 'Task') {
|
|
947
958
|
const call = 실행할것[0];
|
|
948
|
-
const 목적 = String(call.args
|
|
949
|
-
const 할일 = String(call.args
|
|
959
|
+
const 목적 = String(call.args?.purpose ?? call.args?.목적 ?? '').trim() || '이름 없는 작업';
|
|
960
|
+
const 할일 = String(call.args?.task ?? call.args?.할일 ?? '').trim();
|
|
950
961
|
|
|
951
962
|
// 할 일이 비면 하위는 아무것도 모른 채로 시작한다. 하위는 이 대화를
|
|
952
963
|
// 못 보므로, 여기서 통과시키면 걸음만 태우고 빈손으로 돌아온다.
|
|
@@ -959,7 +970,7 @@ export async function* run(session, ctx, userText, { signal = null, 깊이 = 0,
|
|
|
959
970
|
continue;
|
|
960
971
|
}
|
|
961
972
|
|
|
962
|
-
const 자식모드 = 하위모드(call.args
|
|
973
|
+
const 자식모드 = 하위모드(call.args?.mode ?? call.args?.모드, 모드.id);
|
|
963
974
|
|
|
964
975
|
/*
|
|
965
976
|
* ── 다른 모델에게 떼어 주기 ─────────────────────────────────────
|
|
@@ -975,7 +986,7 @@ export async function* run(session, ctx, userText, { signal = null, 깊이 = 0,
|
|
|
975
986
|
let 자식conn = conn;
|
|
976
987
|
let 자리닫기 = null;
|
|
977
988
|
let 모델알림 = null;
|
|
978
|
-
const 부른모델 = String(call.args
|
|
989
|
+
const 부른모델 = String(call.args?.model ?? call.args?.모델 ?? '').trim();
|
|
979
990
|
if (부른모델) {
|
|
980
991
|
const 찾음 = 프로필찾기(부른모델);
|
|
981
992
|
if (!찾음.ok) {
|
package/src/agent/session.js
CHANGED
|
@@ -9,7 +9,7 @@ import { 매김, 급말, 값 as 급값, 지켜본것 } from './grade.js';
|
|
|
9
9
|
import { 지문 } from './project.js';
|
|
10
10
|
import { 프롬프트토막 as 기억토막 } from './memory.js';
|
|
11
11
|
import { 못박기 } from './pins.js';
|
|
12
|
-
import { 언어,
|
|
12
|
+
import { 언어, 지시말, 말 as 옮긴말 } from '../i18n/index.js';
|
|
13
13
|
import { 셸안내 } from '../tools/shell.js';
|
|
14
14
|
|
|
15
15
|
// 토큰 추정 — 정확한 토크나이저 없이 대략만 센다.
|
|
@@ -630,15 +630,21 @@ export class Session {
|
|
|
630
630
|
const 기억 = this.memory ? estimateTokens(this.memory) : 0;
|
|
631
631
|
const 기억줄 = this.memory ? this.memory.split('\n').filter((l) => l.startsWith('- ')).length : 0;
|
|
632
632
|
|
|
633
|
+
/*
|
|
634
|
+
* 이름은 화면에 그대로 나간다(`/context`). 그래서 여기서 말 표를 거친다.
|
|
635
|
+
* 여기만 한국어로 두면 영어로 켠 사람의 컨텍스트 표가 통째로 한국어가
|
|
636
|
+
* 되는데, `/lang` 은 그 사이에도 100% 라고 답한다 — 표에 없는 글은
|
|
637
|
+
* 세지지 못하기 때문이다. test/langleak.test.js 가 이 자리를 지킨다.
|
|
638
|
+
*/
|
|
633
639
|
const rows = [
|
|
634
|
-
{ label: '
|
|
635
|
-
{ label: this.rules ?
|
|
636
|
-
{ label:
|
|
637
|
-
{ label: '
|
|
638
|
-
{ label:
|
|
639
|
-
{ label: '
|
|
640
|
-
{ label: '
|
|
641
|
-
{ label:
|
|
640
|
+
{ label: 옮긴말('ctx.system'), n: sys },
|
|
641
|
+
{ label: this.rules ? 옮긴말('ctx.rules', { 이름: this.rules.name }) : 옮긴말('ctx.rulesNone'), n: rules },
|
|
642
|
+
{ label: 옮긴말('ctx.memory', { n: 기억줄 }), n: 기억 },
|
|
643
|
+
{ label: 옮긴말('ctx.learned'), n: this.배움요약 ? estimateTokens(this.배움요약) : 0 },
|
|
644
|
+
{ label: 옮긴말('ctx.skills', { 실림: listed.length, 전체: this.skills.length }), n: skills },
|
|
645
|
+
{ label: 옮긴말('ctx.tools'), n: 도구 },
|
|
646
|
+
{ label: 옮긴말('ctx.history'), n: history },
|
|
647
|
+
{ label: 옮긴말('ctx.toolResults', { n: this.filesRead.size }), n: files },
|
|
642
648
|
];
|
|
643
649
|
const used = rows.reduce((a, r) => a + r.n, 0);
|
|
644
650
|
const total = this.conn.ctx ?? 32768;
|
package/src/backend/adapter.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// 진단(probe)과 에이전트 루프가 같은 함수를 쓴다.
|
|
3
3
|
import { req, headersFor, serverMessage, Aborted } from './http.js';
|
|
4
4
|
import { 할당량기억 } from './quota.js';
|
|
5
|
+
import { 열쇠 as 열쇠받아오기, 쓸수있나 } from '../safety/authcmd.js';
|
|
6
|
+
import { 말 } from '../i18n/index.js';
|
|
5
7
|
import { 다시부를지, 기다리기, 정책고르기 } from './retry.js';
|
|
6
8
|
|
|
7
9
|
/*
|
|
@@ -33,9 +35,9 @@ export function 더할머리(shape) {
|
|
|
33
35
|
* 지장이 없어서 아무도 안 고치고, 그 화면을 믿고 남에게 설명하게 된다.
|
|
34
36
|
*/
|
|
35
37
|
export function 규격이름(shape) {
|
|
36
|
-
if (shape === 'ollama') return '
|
|
37
|
-
if (shape === 'anthropic') return '
|
|
38
|
-
return '
|
|
38
|
+
if (shape === 'ollama') return 말('head.spec.ollama');
|
|
39
|
+
if (shape === 'anthropic') return 말('head.spec.anthropic');
|
|
40
|
+
return 말('head.spec.openai');
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/**
|
|
@@ -301,14 +303,61 @@ export function toolMessage(shape, { callId, name, content }) {
|
|
|
301
303
|
: { role: 'tool', tool_call_id: callId, content: String(content) };
|
|
302
304
|
}
|
|
303
305
|
|
|
306
|
+
/*
|
|
307
|
+
* 이번 요청에 실을 머리말.
|
|
308
|
+
*
|
|
309
|
+
* 열쇠받기가 걸려 있으면 여기서 받아 온다. 요청 **직전**에 받는 것이
|
|
310
|
+
* 중요하다 — 판을 켤 때 한 번 받아 두면 세 시간짜리 대화의 두 시간째에
|
|
311
|
+
* 죽어 있고, 그 401 은 「열쇠가 틀렸다」 와 화면에서 구별이 안 된다.
|
|
312
|
+
*
|
|
313
|
+
* 못 받으면 **던지지 않는다.** 원래 열쇠(있으면)로 그냥 간다. 여기서
|
|
314
|
+
* 막아 버리면 열쇠받기 설정 한 줄이 잘못된 것으로 멀쩡히 붙던 연결까지
|
|
315
|
+
* 안 붙는다. 못 받았다는 것은 부르는 쪽이 onAuth 로 듣고 화면에 적는다.
|
|
316
|
+
*/
|
|
317
|
+
async function 머리말짓기(conn, opts, { 다시 = false } = {}) {
|
|
318
|
+
const 설정 = conn.열쇠받기 ?? null;
|
|
319
|
+
const 판단 = 쓸수있나(설정, { auth: conn.auth });
|
|
320
|
+
if (!설정 || !판단.된다) {
|
|
321
|
+
if (설정 && 판단.왜) opts.onAuth?.({ ok: false, 왜: 판단.왜, 안부름: true });
|
|
322
|
+
return headersFor(conn.auth, conn.key ?? '', 더할머리(conn.kind));
|
|
323
|
+
}
|
|
324
|
+
const r = await 열쇠받아오기(설정, {
|
|
325
|
+
다시, signal: opts.signal ?? null,
|
|
326
|
+
물어보기: opts.열쇠물어보기 ?? null,
|
|
327
|
+
알림: opts.onAuth ? (것) => opts.onAuth(것) : null,
|
|
328
|
+
});
|
|
329
|
+
if (!r.ok) {
|
|
330
|
+
opts.onAuth?.({ ...r, ok: false });
|
|
331
|
+
return headersFor(conn.auth, conn.key ?? '', 더할머리(conn.kind));
|
|
332
|
+
}
|
|
333
|
+
if (!r.그대로) opts.onAuth?.({ ok: true, 만료: r.만료, ms: r.ms });
|
|
334
|
+
return headersFor(conn.auth, r.token, { ...더할머리(conn.kind), ...r.headers });
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/*
|
|
338
|
+
* 401 을 맞았을 때 열쇠를 새로 받고 한 번만 다시 부를까.
|
|
339
|
+
*
|
|
340
|
+
* retry.js 는 401 을 안 다시 부른다 — 열쇠가 틀린 것은 백 번 불러도
|
|
341
|
+
* 같기 때문이다. 그 말은 지금도 맞다. 다른 것은 **열쇠를 바꿀 수 있을
|
|
342
|
+
* 때**뿐이다. 그때는 같은 열쇠로 다시 부르는 것이 아니라 새 열쇠로
|
|
343
|
+
* 부르는 것이라, 「불러 봐야 같다」 에 해당하지 않는다.
|
|
344
|
+
*
|
|
345
|
+
* 한 번만이다. 두 번째 401 은 진짜로 권한이 없는 것이고, 그때 더 부르면
|
|
346
|
+
* 로그인 명령만 되풀이해서 띄우게 된다.
|
|
347
|
+
*/
|
|
348
|
+
function 열쇠다시받을까(conn, status, 이미) {
|
|
349
|
+
return !이미 && Number(status) === 401 && !!conn.열쇠받기;
|
|
350
|
+
}
|
|
351
|
+
|
|
304
352
|
// 한 번에 받기.
|
|
305
353
|
export async function chat(conn, opts) {
|
|
306
354
|
const body = buildBody(conn.kind, { model: conn.model, ctx: conn.ctx ?? null, ...opts });
|
|
307
355
|
const 정책 = 정책고르기(conn, opts);
|
|
356
|
+
let 열쇠다시받음 = false;
|
|
308
357
|
for (let 시도 = 1; ; 시도++) {
|
|
309
358
|
const r = await req(요청주소(conn), {
|
|
310
359
|
method: 'POST',
|
|
311
|
-
headers:
|
|
360
|
+
headers: await 머리말짓기(conn, opts),
|
|
312
361
|
body,
|
|
313
362
|
timeout: opts.timeout ?? 300000,
|
|
314
363
|
signal: opts.signal ?? null,
|
|
@@ -317,6 +366,14 @@ export async function chat(conn, opts) {
|
|
|
317
366
|
// 429 를 맞고 나서야 아는 것과, 맞기 전에 아는 것은 사람이 할 일이 다르다.
|
|
318
367
|
할당량기억(r.headers);
|
|
319
368
|
if (r.ok) return extractMessage(conn.kind, r.json);
|
|
369
|
+
// 열쇠가 늙어서 막힌 것이면 새로 받고 한 번만 다시. 시도 수는 안 올린다 —
|
|
370
|
+
// 서버가 막은 것이 아니라 우리 열쇠가 낡았던 것이라 물러설 까닭이 없다.
|
|
371
|
+
if (열쇠다시받을까(conn, r.status, 열쇠다시받음)) {
|
|
372
|
+
열쇠다시받음 = true;
|
|
373
|
+
await 머리말짓기(conn, opts, { 다시: true });
|
|
374
|
+
시도 -= 1;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
320
377
|
// 잠깐 막힌 것이면 기다렸다 다시 부른다 (backend/retry.js 머리말).
|
|
321
378
|
// 한 번에 받는 길은 제너레이터가 아니라 화면에 말을 못 걸어서, 부르는 쪽이
|
|
322
379
|
// 준 onBackoff 로 알린다. 안 줬으면 조용히 기다린다.
|
|
@@ -379,10 +436,11 @@ export async function* chatStream(conn, opts) {
|
|
|
379
436
|
const body = buildBody(conn.kind, { model: conn.model, ctx: conn.ctx ?? null, ...opts, stream: true });
|
|
380
437
|
const 정책 = 정책고르기(conn, opts);
|
|
381
438
|
let r;
|
|
439
|
+
let 열쇠다시받음 = false;
|
|
382
440
|
for (let 시도 = 1; ; 시도++) {
|
|
383
441
|
r = await req(요청주소(conn), {
|
|
384
442
|
method: 'POST',
|
|
385
|
-
headers:
|
|
443
|
+
headers: await 머리말짓기(conn, opts),
|
|
386
444
|
body,
|
|
387
445
|
timeout: opts.timeout ?? 300000,
|
|
388
446
|
stream: true,
|
|
@@ -391,6 +449,14 @@ export async function* chatStream(conn, opts) {
|
|
|
391
449
|
할당량기억(r.headers ?? r.res?.headers);
|
|
392
450
|
if (r.ok && r.res?.body) break;
|
|
393
451
|
const 거절 = await 거절읽기(r);
|
|
452
|
+
// 위 chat() 과 같은 규칙. 몸을 먼저 읽고(거절읽기) 나서 다시 부른다 —
|
|
453
|
+
// 안 읽은 몸을 두고 다음 요청을 보내면 연결이 남는다.
|
|
454
|
+
if (열쇠다시받을까(conn, 거절.status, 열쇠다시받음)) {
|
|
455
|
+
열쇠다시받음 = true;
|
|
456
|
+
await 머리말짓기(conn, opts, { 다시: true });
|
|
457
|
+
시도 -= 1;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
394
460
|
// 잠깐 막힌 것이면 알리고, 기다렸다, 다시 부른다. 머리말도 못 받은 자리라
|
|
395
461
|
// 화면에 흘러간 글이 없다 — 그래서 여기서만 다시 부르고, 아래 읽기 도중에
|
|
396
462
|
// 끊긴 것은 다시 안 부른다 (backend/retry.js 머리말).
|
package/src/backend/retry.js
CHANGED
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
// · 세 번까지. 그 뒤로는 사실대로 말한다. 열 번 두드리는 것은 할당량을 더 빨리
|
|
18
18
|
// 태우는 짓이다.
|
|
19
19
|
// · 400·401·403·404 는 다시 불러 봐야 같다. 한 번만 부르고 서버가 한 말을 보여 준다.
|
|
20
|
+
// 401 에 딱 하나 예외가 있는데, 그것도 여기가 아니라 backend/adapter.js 가 한다 —
|
|
21
|
+
// 열쇠받기(safety/authcmd.js)가 걸려 있으면 **같은 열쇠로 다시 부르는 것이 아니라**
|
|
22
|
+
// 새 열쇠를 받아서 부르는 것이라, 「불러 봐야 같다」 에 해당하지 않는다. 딱 한 번이다.
|
|
20
23
|
// · ECONNREFUSED 는 서버가 꺼진 것이고, 시간 초과는 5분을 또 기다릴 일이 아니다.
|
|
21
24
|
// 둘 다 안 부른다.
|
|
22
25
|
//
|
package/src/backend/scanui.js
CHANGED
|
@@ -67,7 +67,7 @@ export async function runScan(flags = {}) {
|
|
|
67
67
|
const wRun = Math.max(10, ...found.map((f) => width(f.runtime)));
|
|
68
68
|
for (const f of found) {
|
|
69
69
|
const 자리 = `${f.host}:${f.port}`;
|
|
70
|
-
const 규격 =
|
|
70
|
+
const 규격 = 규격이름(f.kind);
|
|
71
71
|
const 표시 = f.guessed ? c.gray(' (추정)') : '';
|
|
72
72
|
say(` ${c.hcyan('◆')} ${c.bold(pad(f.runtime, wRun))}${표시} ${c.gray(pad(자리, 22))}${c.gray(pad(규격, 14))}${c.gray(f.ms + 'ms')}`);
|
|
73
73
|
if (f.locked) {
|
package/src/commands.js
CHANGED
|
@@ -13,6 +13,7 @@ import { 지금모드, 바깥인가, 나갈수있나 } from './safety/runmode.js
|
|
|
13
13
|
import { 주소가리기 } from './safety/secrets.js';
|
|
14
14
|
import { pick, confirm } from './ui/prompt.js';
|
|
15
15
|
import { load, save, resolveKey, upsert, 열쇠보관, configPath } from './config.js';
|
|
16
|
+
import { 지금상태 as 지금열쇠상태 } from './safety/authcmd.js';
|
|
16
17
|
import { 제공자고르기 } from './providers/index.js';
|
|
17
18
|
import { 종, 알릴만한초 } from './ui/notify.js';
|
|
18
19
|
import { 말, 언어, 언어들, 언어정하기, 언어고르기, 옮긴만큼, 지시말, 지시말정하기, 지시말따로정했나 } from './i18n/index.js';
|
|
@@ -22,7 +23,7 @@ import { chat, 규격이름 } from './backend/adapter.js';
|
|
|
22
23
|
import { 알림채움 } from './backend/retry.js';
|
|
23
24
|
import { 프록시고르기, 프록시설정 } from './backend/proxy.js';
|
|
24
25
|
import { 정한셸 } from './tools/shell.js';
|
|
25
|
-
import { TOOLS } from './tools/index.js';
|
|
26
|
+
import { TOOLS, 영어설명 } from './tools/index.js';
|
|
26
27
|
import { 둘러보기, 프로젝트갈래 } from './lsp/servers.js';
|
|
27
28
|
import { 지금것들 } from './lsp/client.js';
|
|
28
29
|
import { 보고서적기 } from './ui/export.js';
|
|
@@ -863,9 +864,20 @@ export async function handle(line, session, ctx) {
|
|
|
863
864
|
case 'serve': return await 미리보기(session, ctx, arg), { handled: true };
|
|
864
865
|
|
|
865
866
|
case 'tools': {
|
|
866
|
-
rule('
|
|
867
|
+
rule(말('scr.tools'), 70);
|
|
868
|
+
/*
|
|
869
|
+
* 원본 schema.description 을 그대로 찍으면 안 된다.
|
|
870
|
+
*
|
|
871
|
+
* 그 글은 한국어다. 영어 표(tools/desc.en.js)는 **모델에게 줄 때만**
|
|
872
|
+
* 갈아 끼우고 있어서, 영어로 켠 사람의 `/tools` 는 스물한 줄이 통째로
|
|
873
|
+
* 한국어로 나왔다. `/lang` 은 그 사이에도 100% 라고 답했다 — 이 글들은
|
|
874
|
+
* 애초에 말 표에 들어간 적이 없어서 셈에 안 잡혔기 때문이다.
|
|
875
|
+
*
|
|
876
|
+
* 여기서는 **화면 말**(언어())로 고른다. 모델에게 주는 글의 말과는
|
|
877
|
+
* 다른 축이다 — 영어로 시키고 한국어로 보는 조합이 실제로 쓰인다.
|
|
878
|
+
*/
|
|
867
879
|
for (const [n, t] of Object.entries(TOOLS)) {
|
|
868
|
-
say(` ${c.cyan(pad(n, 8))} ${c.gray(t.schema.description)}`);
|
|
880
|
+
say(` ${c.cyan(pad(n, 8))} ${c.gray(영어설명(t.schema, n, 언어()).description)}`);
|
|
869
881
|
}
|
|
870
882
|
say('');
|
|
871
883
|
return { handled: true };
|
|
@@ -873,14 +885,14 @@ export async function handle(line, session, ctx) {
|
|
|
873
885
|
|
|
874
886
|
case 'cost': {
|
|
875
887
|
const mins = ((Date.now() - session.startedAt) / 60000).toFixed(1);
|
|
876
|
-
rule('
|
|
877
|
-
say(` ${c.gray(pad('
|
|
878
|
-
say(` ${c.gray(pad('
|
|
879
|
-
say(` ${c.gray(pad('
|
|
880
|
-
say(` ${c.gray(pad('
|
|
888
|
+
rule(말('scr.session'), 70);
|
|
889
|
+
say(` ${c.gray(pad(말('cost.calls'), 14))} ${말('unit.calls', { n: session.usage.calls })}`);
|
|
890
|
+
say(` ${c.gray(pad(말('cost.tokIn'), 14))} ${session.usage.in.toLocaleString()}`);
|
|
891
|
+
say(` ${c.gray(pad(말('cost.tokOut'), 14))} ${session.usage.out.toLocaleString()}`);
|
|
892
|
+
say(` ${c.gray(pad(말('cost.toolMs'), 14))} ${말('unit.sec', { n: (session.usage.ms / 1000).toFixed(1) })}`);
|
|
881
893
|
// 서버가 잠깐 막아 다시 부른 횟수. 0 이면 안 적는다 — 없는 일을 줄로 남기면 표만 길어진다.
|
|
882
|
-
if (session.usage.retries) say(` ${c.gray(pad(말('cost.retries'), 14))} ${session.usage.retries}
|
|
883
|
-
say(` ${c.gray(pad('
|
|
894
|
+
if (session.usage.retries) say(` ${c.gray(pad(말('cost.retries'), 14))} ${말('unit.calls', { n: session.usage.retries })}`);
|
|
895
|
+
say(` ${c.gray(pad(말('cost.elapsed'), 14))} ${말('unit.min', { n: mins })}`);
|
|
884
896
|
/*
|
|
885
897
|
* 돈.
|
|
886
898
|
*
|
|
@@ -897,7 +909,7 @@ export async function handle(line, session, ctx) {
|
|
|
897
909
|
const 요금값 = 세션요금(session);
|
|
898
910
|
const 쓴돈 = 돈셈(session.usage, 요금값);
|
|
899
911
|
if (쓴돈) {
|
|
900
|
-
say(` ${c.gray(pad('
|
|
912
|
+
say(` ${c.gray(pad(말('cost.money'), 14))} ${c.bold(돈말(쓴돈.달러))}`
|
|
901
913
|
+ ` ${c.gray(`(${어디서온값(요금값)})`)}`);
|
|
902
914
|
} else if (바깥인가(session.conn.base) && (session.usage.in || session.usage.out)) {
|
|
903
915
|
const 붙은곳 = session.제공자 ? 제공자고르기(session.제공자) : null;
|
|
@@ -917,8 +929,8 @@ export async function handle(line, session, ctx) {
|
|
|
917
929
|
const 남은것 = 마지막할당량();
|
|
918
930
|
if (남은것) {
|
|
919
931
|
const 말줄 = 할당량말(남은것);
|
|
920
|
-
say(` ${c.gray(pad('
|
|
921
|
-
+ ` ${c.gray(`(${Math.round((Date.now() - 남은것.때) / 1000)}
|
|
932
|
+
say(` ${c.gray(pad(말('cost.quota'), 14))} ${아슬아슬한가(남은것) ? c.yellow(말줄) : 말줄}`
|
|
933
|
+
+ ` ${c.gray(`(${말('cost.quotaAge', { 초: Math.round((Date.now() - 남은것.때) / 1000) })})`)}`);
|
|
922
934
|
}
|
|
923
935
|
say('');
|
|
924
936
|
return { handled: true };
|
|
@@ -926,29 +938,46 @@ export async function handle(line, session, ctx) {
|
|
|
926
938
|
|
|
927
939
|
case 'status': {
|
|
928
940
|
const k = session.conn;
|
|
929
|
-
rule('
|
|
930
|
-
say(` ${c.gray(pad('
|
|
931
|
-
say(` ${c.gray(pad('
|
|
941
|
+
rule(말('scr.conn'), 70);
|
|
942
|
+
say(` ${c.gray(pad(말('status.kind'), 10))} ${규격이름(k.kind)}`);
|
|
943
|
+
say(` ${c.gray(pad(말('status.url'), 10))} ${k.base}`);
|
|
932
944
|
// 프록시를 거치면 어느 것을, 어디서 읽었는지(env · config)까지. 안 거치면 줄 자체가 없다.
|
|
933
945
|
// 적어 놨는데 못 쓰는 것(socks5 등)이면 그 까닭을 — 조용히 직접 가면 사람은 프록시를 탄 줄 안다.
|
|
934
946
|
const 프록시 = 프록시고르기(k.base);
|
|
935
|
-
if (프록시) say(` ${c.gray(pad('
|
|
936
|
-
else if (프록시설정().탈) say(` ${c.gray(pad('
|
|
937
|
-
say(` ${c.gray(pad('
|
|
938
|
-
say(` ${c.gray(pad('
|
|
947
|
+
if (프록시) say(` ${c.gray(pad(말('status.proxy'), 10))} ${프록시.url} ${c.gray(`(${프록시.출처})`)}`);
|
|
948
|
+
else if (프록시설정().탈) say(` ${c.gray(pad(말('status.proxy'), 10))} ${c.yellow(말('status.proxyOff'))} ${c.gray(`— ${프록시설정().탈}`)}`);
|
|
949
|
+
say(` ${c.gray(pad(말('status.model'), 10))} ${k.model}`);
|
|
950
|
+
say(` ${c.gray(pad(말('status.root'), 10))} ${session.root}`);
|
|
939
951
|
// 명령이 어느 셸에서 도는지. "ls 가 왜 안 되지" 의 답이 이 줄에 있다.
|
|
940
|
-
say(` ${c.gray(pad('
|
|
952
|
+
say(` ${c.gray(pad(말('status.shell'), 10))} ${정한셸().표시}`);
|
|
941
953
|
// 열쇠를 어디에 두고 있나. 사내 심사에서 제일 먼저 묻는 것이라
|
|
942
954
|
// 「어딘가 잠겨 있겠지」 로 두지 않고 지금 상태를 그대로 적는다.
|
|
943
|
-
|
|
944
|
-
|
|
955
|
+
/*
|
|
956
|
+
* 열쇠를 어디에 두고 있나 — 또는 **어디서 받아 오나.**
|
|
957
|
+
*
|
|
958
|
+
* 받아 오는 연결에서는 「보관」 을 적으면 거짓말이 된다. 우리는 그
|
|
959
|
+
* 열쇠를 갖고 있지 않다. 사내 심사에서 제일 먼저 묻는 자리라
|
|
960
|
+
* 있는 그대로 적는다 (safety/authcmd.js).
|
|
961
|
+
*/
|
|
962
|
+
if (session.conn.열쇠받기) {
|
|
963
|
+
const 상태 = 지금열쇠상태();
|
|
964
|
+
const 남은말 = 상태
|
|
965
|
+
? 말('auth.leftMin', { 분: Math.round(상태.남은초 / 60) })
|
|
966
|
+
: 말('auth.notYet');
|
|
967
|
+
say(` ${c.gray(pad(말('status.keyStore'), 10))} `
|
|
968
|
+
+ `${말(session.conn.열쇠받기.곳 === '정책' ? 'auth.fromPolicy' : 'auth.from')} ${c.gray(`· ${남은말}`)}`);
|
|
969
|
+
say(` ${c.gray(pad('', 10))} ${c.gray(clip(session.conn.열쇠받기.명령, 56))}`);
|
|
970
|
+
} else if (k.auth !== 'none') {
|
|
971
|
+
say(` ${c.gray(pad(말('status.keyStore'), 10))} ${열쇠보관(load())}`);
|
|
972
|
+
}
|
|
973
|
+
say(` ${c.gray(pad(말('status.rules'), 10))} ${session.rules ? session.rules.name : 말('status.noRules')}`);
|
|
945
974
|
const caps = [
|
|
946
|
-
k.tools ? c.green('
|
|
947
|
-
k.streaming ? c.green('
|
|
948
|
-
k.json ? c.green('
|
|
949
|
-
k.think ? c.green('
|
|
975
|
+
k.tools ? c.green(말('status.capTools')) : c.red(말('status.capTools')),
|
|
976
|
+
k.streaming ? c.green(말('status.capStream')) : c.gray(말('status.capStream')),
|
|
977
|
+
k.json ? c.green(말('status.capSchema')) : c.gray(말('status.capSchema')),
|
|
978
|
+
k.think ? c.green(말('status.capThink')) : c.gray(말('status.capThink')),
|
|
950
979
|
].join(c.gray(' · '));
|
|
951
|
-
say(` ${c.gray(pad('
|
|
980
|
+
say(` ${c.gray(pad(말('status.caps'), 10))} ${caps}`);
|
|
952
981
|
say('');
|
|
953
982
|
return { handled: true };
|
|
954
983
|
}
|
|
@@ -2219,8 +2248,8 @@ function 배움명령(session, ctx, arg = '') {
|
|
|
2219
2248
|
function showContext(session) {
|
|
2220
2249
|
const b = session.breakdown();
|
|
2221
2250
|
say('');
|
|
2222
|
-
rule('
|
|
2223
|
-
say(` ${c.bold(session.conn.model)} ${c.gray('·')} ${b.total.toLocaleString()}
|
|
2251
|
+
rule(말('scr.context'), 70);
|
|
2252
|
+
say(` ${c.bold(session.conn.model)} ${c.gray('·')} ${말('unit.tokens', { n: b.total.toLocaleString() })}`);
|
|
2224
2253
|
say('');
|
|
2225
2254
|
say(` ${bar(b.used, b.total, 32)} ${b.used.toLocaleString()} / ${b.total.toLocaleString()} ${c.gray(`${Math.round((b.used / b.total) * 100)}%`)}`);
|
|
2226
2255
|
say('');
|
|
@@ -2229,9 +2258,9 @@ function showContext(session) {
|
|
|
2229
2258
|
say(` ${c.gray(pad(r.label, 26))} ${pad(r.n.toLocaleString(), 8, 'right')}`);
|
|
2230
2259
|
}
|
|
2231
2260
|
say(` ${c.gray('─'.repeat(35))}`);
|
|
2232
|
-
say(` ${c.gray(pad('
|
|
2261
|
+
say(` ${c.gray(pad(말('ctx.left'), 26))} ${pad(b.left.toLocaleString(), 8, 'right')}`);
|
|
2233
2262
|
say('');
|
|
2234
|
-
say(` ${c.gray(
|
|
2263
|
+
say(` ${c.gray(`/compact ${말('ctx.compactHint')} /clear ${말('ctx.clearHint')}`)}`);
|
|
2235
2264
|
/*
|
|
2236
2265
|
* 추정이라고만 적어 두면 사람은 얼마나 믿어야 할지 모른다. 서버가 알려 준
|
|
2237
2266
|
* 실제값에 맞춰 가고 있으면 그 사실을 적는다 — '추정' 과 '맞춰 본 추정' 은
|
|
@@ -2239,9 +2268,9 @@ function showContext(session) {
|
|
|
2239
2268
|
*/
|
|
2240
2269
|
if (b.보정잰것 > 0) {
|
|
2241
2270
|
const 차이 = Math.round((b.보정 - 1) * 100);
|
|
2242
|
-
say(` ${c.gray(
|
|
2271
|
+
say(` ${c.gray(말('ctx.calibrated', { 부호: 차이 >= 0 ? '+' : '', 퍼센트: 차이, 번: b.보정잰것 }))}`);
|
|
2243
2272
|
} else {
|
|
2244
|
-
say(` ${c.gray('
|
|
2273
|
+
say(` ${c.gray(말('ctx.estimate'))}`);
|
|
2245
2274
|
}
|
|
2246
2275
|
say('');
|
|
2247
2276
|
}
|
package/src/completion.js
CHANGED
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { MODES } from './agent/modes.js';
|
|
25
25
|
import { LEVELS, PROFILES } from './agent/effort.js';
|
|
26
|
+
// 승인 모드(auto·confirm·strict). agent/modes.js 의 MODES 와 헷갈리기 쉬운데,
|
|
27
|
+
// 저쪽은 `--work` 가 받는 '무슨 일을 하는 중인가' 다. 실제로 한 번 뒤바뀌어
|
|
28
|
+
// 있었다 — `--mode` 를 누르면 code·plan 이 나오고 `--work` 는 폴더를 냈다.
|
|
29
|
+
import { 차례 as 승인모드 } from './ui/approve.js';
|
|
26
30
|
|
|
27
31
|
/** deel 이 받는 명령. bin/deel.js 의 switch 와 같아야 한다(검사가 지킨다). */
|
|
28
32
|
export const 명령들 = [
|
|
@@ -39,6 +43,7 @@ export const 명령들 = [
|
|
|
39
43
|
{ 이름: 'scan', 뜻: '게이트웨이에 어떤 모델이 있나 훑는다', en: 'scan the gateway for available models' },
|
|
40
44
|
{ 이름: 'sessions', 뜻: '지난 대화 목록', en: 'list past conversations' },
|
|
41
45
|
{ 이름: 'ls', 뜻: 'sessions 와 같다', en: 'same as sessions' },
|
|
46
|
+
{ 이름: 'reset', 뜻: '설정·기억·기록을 지운다 (그냥 치면 보여만 준다)', en: 'wipe settings, memory and records (bare: just shows)' },
|
|
42
47
|
{ 이름: 'completion', 뜻: '탭 완성 스크립트를 낸다', en: 'print a tab-completion script' },
|
|
43
48
|
{ 이름: 'version', 뜻: '판 번호', en: 'version number' },
|
|
44
49
|
{ 이름: 'help', 뜻: '도움말', en: 'help' },
|
|
@@ -55,8 +60,20 @@ export const 숨은명령 = ['-p'];
|
|
|
55
60
|
*/
|
|
56
61
|
export const 깃발들 = [
|
|
57
62
|
{ 이름: '--root', 뜻: '작업 폴더', en: 'working folder', 값: '폴더' },
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
/*
|
|
64
|
+
* 이 둘은 한 번 뒤바뀌어 있었다.
|
|
65
|
+
*
|
|
66
|
+
* `--mode` 는 **승인 모드**(auto·confirm·strict)인데 작업 모드 목록이
|
|
67
|
+
* 붙어 있었고, `--work` 는 **작업 모드**인데 「살림 폴더」 라고 적혀 폴더를
|
|
68
|
+
* 완성해 주고 있었다. 그래서 `deel --mode <탭>` 을 누르면 받지도 않는
|
|
69
|
+
* code·plan 이 나왔다.
|
|
70
|
+
*
|
|
71
|
+
* 검사도 이걸 못 잡았다 — 완성 목록이 agent/modes.js 에서 온 것이 맞나만
|
|
72
|
+
* 봤기 때문이다. 베낀 자리가 맞는지는 봤는데, 베껴 온 곳이 맞는지는 안 봤다.
|
|
73
|
+
* 지금은 도움말에 적힌 값과 맞춰 본다 (test/completion.test.js).
|
|
74
|
+
*/
|
|
75
|
+
{ 이름: '--work', 뜻: '무슨 일을 하는 중인가', en: 'what kind of work this is', 값: Object.keys(MODES) },
|
|
76
|
+
{ 이름: '--mode', 뜻: '무엇까지 물어보지 않고 할까', en: 'how much it may do without asking', 값: 승인모드 },
|
|
60
77
|
{ 이름: '--level', 뜻: '화면을 얼마나 자세히', en: 'how much detail on screen', 값: null },
|
|
61
78
|
{ 이름: '--ctx', 뜻: '창 크기 (예: 32k)', en: 'context window size (e.g. 32k)', 값: null },
|
|
62
79
|
{ 이름: '--max-tokens', 뜻: '한 번에 뱉을 최대 토큰', en: 'max tokens per reply', 값: null },
|
|
@@ -65,6 +82,7 @@ export const 깃발들 = [
|
|
|
65
82
|
{ 이름: '--online', 뜻: '묻지 않고 바깥으로 나간다', en: 'go outside without asking', 값: false },
|
|
66
83
|
{ 이름: '--offline', 뜻: '바깥으로 아예 안 나간다', en: 'never reach the network', 값: false },
|
|
67
84
|
{ 이름: '--yes', 뜻: '물어보지 않고 진행', en: 'do not ask, just proceed', 값: false },
|
|
85
|
+
{ 이름: '--hard', 뜻: 'reset all 에서 되돌리기·감사기록까지', en: 'with reset all: undo snapshots and the audit log too', 값: false },
|
|
68
86
|
{ 이름: '--json', 뜻: '결과를 JSON 으로', en: 'output JSON', 값: false },
|
|
69
87
|
{ 이름: '--quiet', 뜻: '군말 없이', en: 'no chatter', 값: false },
|
|
70
88
|
{ 이름: '--tui', 뜻: '입력 상자를 켠다', en: 'force the input box on', 값: false },
|