deel-local-cli 1.6.2 → 1.7.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.en.md +115 -23
- package/README.md +114 -24
- package/bin/deel.js +21 -3
- package/package.json +2 -2
- package/src/acp/serve.js +25 -2
- package/src/agent/loop.js +68 -5
- package/src/backend/adapter.js +211 -4
- package/src/backend/ctxsize.js +16 -2
- package/src/backend/detect.js +104 -6
- package/src/backend/price.js +197 -0
- package/src/backend/scanui.js +2 -1
- package/src/backend/vision.js +26 -4
- package/src/commands.js +80 -6
- package/src/completion.js +1 -0
- package/src/i18n/en.js +6 -0
- package/src/i18n/ja.js +6 -0
- package/src/i18n/ko.js +6 -0
- package/src/i18n/zh.js +6 -0
- package/src/oneshot.js +28 -4
- package/src/providers/anthropic.js +48 -0
- package/src/providers/bedrock.js +102 -0
- package/src/providers/custom.js +65 -0
- package/src/providers/gemini.js +64 -0
- package/src/providers/index.js +121 -0
- package/src/providers/openai.js +68 -0
- package/src/repl.js +118 -7
- package/src/report.js +3 -2
- package/src/safety/runmode.js +123 -0
- package/src/safety/secrets.js +61 -0
- package/src/setup.js +215 -12
- package/src/tools/index.js +46 -2
- package/src/tools/todo.js +28 -0
- package/src/ui/histline.js +67 -0
- package/src/ui/status.js +88 -3
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 위/아래로 지난 입력을 뒤질 때, 치던 글을 붙잡아 둔다.
|
|
3
|
+
*
|
|
4
|
+
* ── 무슨 일이 났었나 ────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* 「채팅치다가 윗키 누르면 기존에 쓰던 내용 사라지고, 아랫키 눌러도 복구
|
|
7
|
+
* 안 되는 오류」
|
|
8
|
+
*
|
|
9
|
+
* 반쯤 쓰던 글이 통째로 없어진다. 되돌릴 길도 없다. 긴 요청을 몇 줄 쓰다가
|
|
10
|
+
* 아까 뭐라고 보냈더라 하고 위를 한 번 눌렀을 뿐인데 처음부터 다시 써야 한다.
|
|
11
|
+
*
|
|
12
|
+
* ── 왜 이걸 우리가 갖나 ─────────────────────────────────────────────────
|
|
13
|
+
*
|
|
14
|
+
* 위/아래 화살표는 readline 이 처리한다. 그런데 그 동작이 **Node 판마다
|
|
15
|
+
* 다르다.**
|
|
16
|
+
*
|
|
17
|
+
* 새 판: 치던 글을 앞글자로 삼아 이력을 찾는다. 아래로 다 내려오면 그
|
|
18
|
+
* 앞글자를 도로 넣어 준다 — 치던 글이 살아 있다.
|
|
19
|
+
* 옛 판: 그냥 이력으로 갈아끼운다. 아래로 다 내려오면 **빈 줄**을 준다 —
|
|
20
|
+
* 치던 글은 그대로 사라진다.
|
|
21
|
+
*
|
|
22
|
+
* deel 은 Node 20 부터 돈다. 쓰는 사람의 Node 가 몇 판이냐에 따라 글이
|
|
23
|
+
* 날아가기도 하고 안 날아가기도 하는 것은, 우리가 고칠 수 있는 결함이다.
|
|
24
|
+
* 그래서 이 동작만큼은 readline 에 안 맡기고 우리가 갖는다.
|
|
25
|
+
*
|
|
26
|
+
* ── 어떻게 ──────────────────────────────────────────────────────────────
|
|
27
|
+
*
|
|
28
|
+
* 이력을 안 뒤지는 동안(이력자리 -1)의 줄을 계속 적어 둔다. 화살표를 눌러
|
|
29
|
+
* 다시 이력 밖(-1)으로 나오면 적어 둔 것을 돌려준다.
|
|
30
|
+
*
|
|
31
|
+
* 이미 제대로 하는 판에서는 돌려주는 값이 readline 이 넣어 둔 것과 **같다.**
|
|
32
|
+
* 그래서 덧씌워도 화면은 그대로다. 안 하는 판에서만 글이 되살아난다. 판을
|
|
33
|
+
* 알아내서 갈라지지 않는다 — 알아낼 필요가 없게 만든 것이 요점이다.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export function 이력지킴이() {
|
|
37
|
+
let 치던것 = '';
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
/*
|
|
41
|
+
* 이력을 안 뒤지는 동안의 줄을 적어 둔다.
|
|
42
|
+
*
|
|
43
|
+
* 뒤지는 중(이력자리 0 이상)에는 안 적는다. 적으면 위로 올라간 이력이
|
|
44
|
+
* 「치던 글」로 둔갑해서, 아래로 내려왔을 때 이력이 되돌아온다.
|
|
45
|
+
*/
|
|
46
|
+
적기(줄, 이력자리) {
|
|
47
|
+
if ((이력자리 ?? -1) === -1) 치던것 = String(줄 ?? '');
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
* 화살표가 이력 밖으로 나왔으면 돌려줄 글, 아니면 null.
|
|
52
|
+
*
|
|
53
|
+
* 화살표일 때만이다. 아무 키에나 돌려주면 치고 있는 글을 계속 덮어쓴다.
|
|
54
|
+
*/
|
|
55
|
+
되돌릴것(키이름, 이력자리) {
|
|
56
|
+
if (키이름 !== 'up' && 키이름 !== 'down') return null;
|
|
57
|
+
if ((이력자리 ?? -1) !== -1) return null;
|
|
58
|
+
return 치던것;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/** 줄을 보냈다 — 붙잡고 있던 것을 놓는다. */
|
|
62
|
+
비우기() { 치던것 = ''; },
|
|
63
|
+
|
|
64
|
+
/** 지금 붙잡고 있는 글 (검사용). */
|
|
65
|
+
지금() { return 치던것; },
|
|
66
|
+
};
|
|
67
|
+
}
|
package/src/ui/status.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// 화면 그리기는 ansi.js 만 쓴다. 반입 심사에 새로 설명할 것이 늘지 않게 하려는 뜻이다.
|
|
5
5
|
import { c, 눈금게이지, width, clip, cols, mark } from './ansi.js';
|
|
6
6
|
import { 마지막할당량, 할당량말, 아슬아슬한가 } from '../backend/quota.js';
|
|
7
|
+
import { 세션요금, 돈셈, 돈말 } from '../backend/price.js';
|
|
7
8
|
import { PROFILES } from '../agent/effort.js';
|
|
8
9
|
import { get as workMode, canWrite, 보일이름 } from '../agent/modes.js';
|
|
9
10
|
import { isLocalHost, isOffline } from '../safety/network.js';
|
|
@@ -11,6 +12,7 @@ import { 프록시고르기, 프록시설정 } from '../backend/proxy.js';
|
|
|
11
12
|
import { COMPACT_AT, FOLD_AT } from '../agent/compact.js';
|
|
12
13
|
import { 말, 언어 } from '../i18n/index.js';
|
|
13
14
|
import { 표시 as 승인표시, 고르기 as 승인고르기 } from './approve.js';
|
|
15
|
+
import { VERSION } from '../version.js';
|
|
14
16
|
|
|
15
17
|
// 저장소 주소. package.json 의 repository 와 같아야 한다 — 검사가 지킨다.
|
|
16
18
|
export const GITHUB = 'https://github.com/jysvai/deel-local-cli';
|
|
@@ -45,6 +47,29 @@ export const SEGMENTS = {
|
|
|
45
47
|
short: (s) => `${경계표(s)} ${c.gray(clip(base(s.root), 12))}`,
|
|
46
48
|
},
|
|
47
49
|
|
|
50
|
+
/*
|
|
51
|
+
* 판 번호.
|
|
52
|
+
*
|
|
53
|
+
* 「무슨 판을 쓰고 있나」 는 화면 어디에도 안 떴다 — `deel --version` 을 따로
|
|
54
|
+
* 쳐야 알았다. 그런데 그 질문이 나오는 자리는 대개 뭔가 이상할 때고, 그때
|
|
55
|
+
* 사람은 화면을 캡처해서 보낸다. 캡처에 판 번호가 없으면 무슨 판 이야기인지
|
|
56
|
+
* 부터 다시 물어야 한다. 켤 때 머리말에도 적지만 그건 스크롤에 밀린다.
|
|
57
|
+
*
|
|
58
|
+
* **조각을 따로 둔다.** 처음에는 폴더 이름(dir) 안에 글자로 붙였는데, 그러면
|
|
59
|
+
* 여덟 칸이 늘어난 채로 「자리에 맞나」 를 재게 되어 114칸 아래 터미널에서
|
|
60
|
+
* 온 줄이 짧은 꼴로 바뀌었다 — 승인 방식이 `위험만 확인` 에서 `위험만` 이
|
|
61
|
+
* 됐다. 이 파일이 이미 정해 둔 원칙에 걸린다: **안전 표시를 흐리게 만드는
|
|
62
|
+
* 표시는 손해다.**
|
|
63
|
+
*
|
|
64
|
+
* 조각으로 떼어 두면 자리가 모자랄 때 이것부터 뺄 수 있다(아래 사다리).
|
|
65
|
+
* 판 번호는 밀려도 되는 것이고, 승인 방식은 아니다.
|
|
66
|
+
*/
|
|
67
|
+
ver: {
|
|
68
|
+
get desc() { return 말('seg.ver'); },
|
|
69
|
+
make: () => c.gray(VERSION),
|
|
70
|
+
short: () => null,
|
|
71
|
+
},
|
|
72
|
+
|
|
48
73
|
/*
|
|
49
74
|
* 지금 어느 대화 갈래인가.
|
|
50
75
|
*
|
|
@@ -177,6 +202,26 @@ export const SEGMENTS = {
|
|
|
177
202
|
: null,
|
|
178
203
|
},
|
|
179
204
|
|
|
205
|
+
/*
|
|
206
|
+
* 여태 쓴 돈.
|
|
207
|
+
*
|
|
208
|
+
* 요금을 모르면 **아무것도 안 그린다.** 여기에 '요금 모름' 을 적으면
|
|
209
|
+
* 로컬로만 쓰는 사람 화면에 평생 그 다섯 글자가 서 있게 된다 — 로컬은
|
|
210
|
+
* 원래 공짜라 그 자리는 영원히 안 채워진다. 모른다는 말은 물어본 자리
|
|
211
|
+
* (/cost)에서 하고, 상태줄은 아는 것만 그린다.
|
|
212
|
+
*
|
|
213
|
+
* 반대로 아는데 안 그리면 안 된다. 바깥 API 는 몇 턴 만에 몇 달러가
|
|
214
|
+
* 나가고, 그걸 화면 밖에 두면 다 쓴 뒤에나 안다.
|
|
215
|
+
*/
|
|
216
|
+
cost: {
|
|
217
|
+
get desc() { return 말('seg.cost'); },
|
|
218
|
+
make: (s) => {
|
|
219
|
+
const 돈 = 돈셈(s.usage, 세션요금(s));
|
|
220
|
+
if (!돈 || !(돈.달러 > 0)) return null;
|
|
221
|
+
return c.gray(돈말(돈.달러));
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
|
|
180
225
|
/*
|
|
181
226
|
* 이번 대화에서 **내 폴더에 무슨 일이 있었나** — 다른 축이라 따로 묶는다.
|
|
182
227
|
*
|
|
@@ -246,12 +291,12 @@ export const SEGMENTS = {
|
|
|
246
291
|
* 그러면 눈이 세 번만 멈춘다.
|
|
247
292
|
*/
|
|
248
293
|
export const SEGMENT_GROUPS = [
|
|
249
|
-
['dir', 'thread', 'model'],
|
|
294
|
+
['dir', 'ver', 'thread', 'model'],
|
|
250
295
|
['ctx', 'grade'],
|
|
251
296
|
['work', 'think', 'mode'],
|
|
252
297
|
// 내 폴더에 무슨 일이 있었나. 아무 일도 없으면 이 덩이는 아예 안 선다.
|
|
253
298
|
['edits', 'verify', 'undoable'],
|
|
254
|
-
['tok'],
|
|
299
|
+
['tok', 'cost'],
|
|
255
300
|
];
|
|
256
301
|
|
|
257
302
|
// 옛 이름. 조각 이름을 직접 넘기던 자리(검사·설정)가 그대로 돌게 남긴다.
|
|
@@ -309,6 +354,24 @@ function 경계표(s) {
|
|
|
309
354
|
return c.hgreen('⌂');
|
|
310
355
|
}
|
|
311
356
|
|
|
357
|
+
/*
|
|
358
|
+
* 실행 모드 한 조각 — `⌂ 이 안 (기본)`.
|
|
359
|
+
*
|
|
360
|
+
* 모드를 안 심어 준 화면(옛 호출부·검사)에서는 아무것도 안 그린다. 모르는
|
|
361
|
+
* 것을 기본값으로 지어내면, 봉인으로 켠 줄 알았는데 화면은 「이 안」 이라고
|
|
362
|
+
* 하는 자리가 생긴다 — 안 보여 주느니만 못하다.
|
|
363
|
+
*
|
|
364
|
+
* 색은 여기서 칠한다. 모드 표(safety/runmode.js)는 자물쇠지 화면이 아니다.
|
|
365
|
+
*/
|
|
366
|
+
const 모드색 = { default: c.hgreen, online: c.hyellow, offline: c.hgreen };
|
|
367
|
+
|
|
368
|
+
function 모드칸(s) {
|
|
369
|
+
const m = s?.실행모드;
|
|
370
|
+
if (!m?.영) return '';
|
|
371
|
+
const 칠 = 모드색[m.영] ?? c.gray;
|
|
372
|
+
return ` ${칠(m.글자)} ${c.gray(말(`head.mode.${m.영}`))}`;
|
|
373
|
+
}
|
|
374
|
+
|
|
312
375
|
function base(p) {
|
|
313
376
|
const parts = String(p ?? '').split(/[\\/]/).filter(Boolean);
|
|
314
377
|
return parts[parts.length - 1] ?? '~';
|
|
@@ -367,6 +430,15 @@ export function statusLine(session, { segments = null, max = cols() - 2 } = {})
|
|
|
367
430
|
* 덜 급한 자리를 접는 편이 낫다.
|
|
368
431
|
*/
|
|
369
432
|
let parts = 그리기(false);
|
|
433
|
+
/*
|
|
434
|
+
* 자리가 모자라면 **판 번호부터** 뺀다. 접기보다도 먼저다.
|
|
435
|
+
*
|
|
436
|
+
* 여기 있는 것 중에 지금 안 봐도 되는 것은 판 번호 하나뿐이다. 나머지는
|
|
437
|
+
* 전부 「지금 어떤 상태냐」 를 말한다. 순서를 안 두고 그냥 접기부터 들어가면
|
|
438
|
+
* 판 번호 여덟 칸 때문에 온 줄이 짧은 꼴로 바뀌고, 그 바람에 승인 방식이
|
|
439
|
+
* `위험만 확인` 에서 `위험만` 이 된다 — 이 파일이 여러 번 적어 둔 그 손해다.
|
|
440
|
+
*/
|
|
441
|
+
if (!맞나(parts) && !segments) parts = 그리기(false, new Set(['ver']));
|
|
370
442
|
if (!맞나(parts)) parts = 그리기(true);
|
|
371
443
|
|
|
372
444
|
/*
|
|
@@ -470,7 +542,20 @@ export function headerLines(session, found, 상자쓰나 = true) {
|
|
|
470
542
|
const 자리 = (글) => `${글}${' '.repeat(Math.max(1, 이름칸 - width(글)))}`;
|
|
471
543
|
const 빈자리 = ' '.repeat(이름칸);
|
|
472
544
|
const lines = [
|
|
473
|
-
|
|
545
|
+
/*
|
|
546
|
+
* 판 번호와 실행 모드를 첫 줄에 같이 놓는다.
|
|
547
|
+
*
|
|
548
|
+
* 여태 판 번호는 화면 어디에도 안 떴다 — `deel --version` 을 따로 쳐야
|
|
549
|
+
* 알았다. 그런데 그 질문이 나오는 자리는 대개 뭔가 이상할 때고, 그때
|
|
550
|
+
* 사람은 화면을 캡처해서 보낸다. 캡처에 판 번호가 없으면 무슨 판
|
|
551
|
+
* 이야기인지부터 다시 물어야 한다.
|
|
552
|
+
*
|
|
553
|
+
* 실행 모드도 같은 줄이다. 「지금 나가나 안 나가나」 는 아래 `보냄` 줄이
|
|
554
|
+
* 주소로 말해 주지만, 그건 **어디로** 이지 **나가도 되나** 가 아니다.
|
|
555
|
+
* 봉인(--offline)으로 켠 사람은 그 사실을 첫 줄에서 봐야 한다.
|
|
556
|
+
*/
|
|
557
|
+
`${c.hcyan(c.bold('deel'))} ${c.gray(VERSION)}${모드칸(session)}`
|
|
558
|
+
+ ` ${c.gray(말(`head.spec.${['ollama', 'anthropic'].includes(conn.kind) ? conn.kind : 'openai'}`))}`,
|
|
474
559
|
'',
|
|
475
560
|
/*
|
|
476
561
|
* 모델 줄에 급을 같이 적는다.
|