deel-local-cli 1.4.2 → 1.5.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 +997 -908
- package/README.md +988 -907
- package/package.json +60 -60
- package/src/i18n/en.js +1 -0
- package/src/i18n/ko.js +1 -0
- package/src/pack/selfpack.js +9 -3
- package/src/repl.js +73 -4
- package/src/ui/inputbox.js +557 -460
- package/src/ui/motion.js +312 -5
- package/src/ui/office.js +334 -0
- package/src/ui/screen.js +319 -312
- package/src/ui/status.js +1 -0
- package/src/ui/wrap.js +11 -0
package/src/ui/office.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 붙박이 사무실 — 입력 상자 위에 얹히는 방 한 칸.
|
|
3
|
+
*
|
|
4
|
+
* ── 무엇인가 ────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* 로컬 모델은 한 턴에 몇 분이 걸린다. 그 시간을 견디게 하려고 motion.js 가
|
|
7
|
+
* 작은 그림을 돌리는데, 이건 그 생각을 한 뼘 더 밀고 간 것이다 — 지금 무슨
|
|
8
|
+
* 일이 얼마나 돌아가고 있는지를 **방 하나로** 보여 준다.
|
|
9
|
+
*
|
|
10
|
+
* ── 규칙 하나 ───────────────────────────────────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* **화면에 있는 것은 전부 진짜 숫자다.** 꾸미려고 놓은 것은 하나도 없다.
|
|
13
|
+
* 자리가 셋 차 있으면 지금 도는 일이 셋이고, 서랍이 차 있으면 그만큼 파일을
|
|
14
|
+
* 읽은 것이고, 서버 불이 길면 모델을 그만큼 부른 것이다. 이걸 안 지키면
|
|
15
|
+
* 그냥 배경화면이 되고, 지키면 상태줄을 방으로 그린 것이 된다.
|
|
16
|
+
*
|
|
17
|
+
* ── 왜 반칸(▀)인가 ──────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* 한 칸에 픽셀 둘을 넣는다 — 위는 글자색, 아래는 배경색, 글자는 ▀. 세로
|
|
20
|
+
* 해상도가 두 배가 되고 칸이 정사각형에 가까워져 그림이 안 눌린다.
|
|
21
|
+
* motion.js 의 점자와 길이 갈리는 이유는 **색** 이다. 점자는 켜짐/꺼짐뿐이라
|
|
22
|
+
* 사무실이 갈색 죽이 된다.
|
|
23
|
+
*
|
|
24
|
+
* ── 가벼움 ──────────────────────────────────────────────────────────────
|
|
25
|
+
*
|
|
26
|
+
* 한 판을 통째로 보내면 9KB 가 넘는다. 그런데 판이 바뀔 때 실제로 달라지는
|
|
27
|
+
* 것은 **열두 줄 중 한 줄** 뿐이다(벽·창문·서류함·빈 책상은 그대로다).
|
|
28
|
+
* 그래서 상자가 바뀐 줄만 보낸다(inputbox 의 그리기). 그 덕에 사무실을 켜도
|
|
29
|
+
* 터미널로 나가는 양이 켜기 전과 비슷하다.
|
|
30
|
+
*/
|
|
31
|
+
import { 색256가능 } from './ansi.js';
|
|
32
|
+
|
|
33
|
+
const ESC = '\x1b';
|
|
34
|
+
const 투명 = -1;
|
|
35
|
+
|
|
36
|
+
export const 세로 = 24; // 픽셀. 칸으로는 12줄
|
|
37
|
+
export const 줄수 = 세로 / 2;
|
|
38
|
+
const 벽 = 6; // 벽은 눌러 뒀다 — 사람 자리를 벌기 위해
|
|
39
|
+
|
|
40
|
+
// ── 색 (xterm 256) ──────────────────────────────────────────────────────
|
|
41
|
+
const C = {
|
|
42
|
+
바닥: 138, 바닥줄: 95, 벽: 238, 굽도리: 145,
|
|
43
|
+
나무: 137, 나무어둠: 94, 나무진: 58,
|
|
44
|
+
쇠: 145, 쇠어둠: 102, 쇠진: 59, 검정: 235,
|
|
45
|
+
화면: 45, 화면꺼짐: 239, 화면글: 123, 살: 223,
|
|
46
|
+
머리: [52, 240, 94, 130], 옷: [67, 131, 71, 97],
|
|
47
|
+
밤: 17, 별: 226, 낮: 111, 구름: 255,
|
|
48
|
+
종이: 255, 종이어둠: 250, 쪽지: 227, 쪽지끝: 120, 초록: 46,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// ── 픽셀 판 ─────────────────────────────────────────────────────────────
|
|
52
|
+
class 판 {
|
|
53
|
+
constructor(가로, 세로칸) {
|
|
54
|
+
this.가로 = 가로;
|
|
55
|
+
this.세로 = 세로칸;
|
|
56
|
+
this.점 = new Int16Array(가로 * 세로칸).fill(투명);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
찍기(x, y, 색) {
|
|
60
|
+
if (색 === 투명 || 색 == null) return;
|
|
61
|
+
if (x < 0 || y < 0 || x >= this.가로 || y >= this.세로) return;
|
|
62
|
+
this.점[y * this.가로 + x] = 색;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
칠하기(x, y, w, h, 색) {
|
|
66
|
+
for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) this.찍기(x + i, y + j, 색);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
올리기(x, y, 그림, 색표) {
|
|
70
|
+
for (let j = 0; j < 그림.length; j++) {
|
|
71
|
+
const 줄 = 그림[j];
|
|
72
|
+
for (let i = 0; i < 줄.length; i++) {
|
|
73
|
+
const 색 = 색표[줄[i]];
|
|
74
|
+
if (색 !== undefined) this.찍기(x + i, y + j, 색);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 화면에 내보낼 줄들. 색이 안 바뀌면 제어문자를 다시 안 쓴다. */
|
|
80
|
+
줄들() {
|
|
81
|
+
const 결과 = [];
|
|
82
|
+
for (let y = 0; y < this.세로; y += 2) {
|
|
83
|
+
let s = '';
|
|
84
|
+
let 앞위 = null;
|
|
85
|
+
let 앞아래 = null;
|
|
86
|
+
for (let x = 0; x < this.가로; x++) {
|
|
87
|
+
const 위 = this.점[y * this.가로 + x];
|
|
88
|
+
const 아래 = this.점[(y + 1) * this.가로 + x];
|
|
89
|
+
if (위 === 투명 && 아래 === 투명) {
|
|
90
|
+
if (앞위 !== null || 앞아래 !== null) { s += `${ESC}[0m`; 앞위 = null; 앞아래 = null; }
|
|
91
|
+
s += ' ';
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (위 !== 앞위) { s += `${ESC}[38;5;${위 === 투명 ? 0 : 위}m`; 앞위 = 위; }
|
|
95
|
+
if (아래 !== 앞아래) { s += `${ESC}[48;5;${아래 === 투명 ? 0 : 아래}m`; 앞아래 = 아래; }
|
|
96
|
+
s += '▀';
|
|
97
|
+
}
|
|
98
|
+
결과.push(s + `${ESC}[0m`);
|
|
99
|
+
}
|
|
100
|
+
return 결과;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* ── 일하는 자리 18×14 ───────────────────────────────────────────────────
|
|
106
|
+
*
|
|
107
|
+
* 사람과 책상을 따로 그렸더니 반 픽셀씩 어긋나 팔이 책상을 뚫었다. 그래서
|
|
108
|
+
* 한 장으로 그린다 — 한 그림 안에 있으면 어긋날 자리가 없다.
|
|
109
|
+
*
|
|
110
|
+
* 짧은 판이라고 사람을 줄이면 자세가 안 읽힌다(조는 머리가 화면 뒤로 숨는다).
|
|
111
|
+
* 줄여야 할 것은 사람이 아니라 **여백**이다. 그래서 벽을 눌렀다.
|
|
112
|
+
*/
|
|
113
|
+
const 화면줄 = (내용) => ` M${내용.padEnd(10, 'S').slice(0, 10)}M `;
|
|
114
|
+
const 빈 = ' '.repeat(18);
|
|
115
|
+
const 사람 = [
|
|
116
|
+
' hhhhhh ',
|
|
117
|
+
' hhhhhhhh ',
|
|
118
|
+
' hffffffh ',
|
|
119
|
+
' hffffffh ',
|
|
120
|
+
' ffffff ',
|
|
121
|
+
' bbbbbbbbbb ',
|
|
122
|
+
];
|
|
123
|
+
const 책상 = [' MMMMMMMMMMMM ', 'DDDDDDDDDDDDDDDDDD', 'dddddddddddddddddd', 'd d'];
|
|
124
|
+
const 자리짜기 = (위, 화면들) => [...위, ' MMMMMMMMMMMM ', ...화면들.map(화면줄), ...책상];
|
|
125
|
+
|
|
126
|
+
const 자세들 = {
|
|
127
|
+
치기A: 자리짜기(사람, ['GGGGGGGSSS', 'GGGGSSSSSS']),
|
|
128
|
+
치기B: 자리짜기(사람, ['GGGSSSSSSS', 'GGGGGGGSSS']),
|
|
129
|
+
읽기A: 자리짜기([빈, ...사람.slice(0, 5)], ['GGGGGGGGGG', 'SSSSSSSSSS']),
|
|
130
|
+
읽기B: 자리짜기([빈, 빈, ...사람.slice(0, 4)], ['SSSSSSSSSS', 'GGGGGGGGGG']),
|
|
131
|
+
졸기: 자리짜기([빈, 빈, 빈, ' hhhhhh ', ' hhffffffhh ', ' ffffff '],
|
|
132
|
+
['SSSSSSSSSS', 'SSSSSSSSSS']),
|
|
133
|
+
대기: 자리짜기(사람, ['SSSSSSSSSS', 'SSSSSSSSSS']),
|
|
134
|
+
빈자리: 자리짜기([빈, 빈, 빈, 빈, 빈, 빈], ['SSSSSSSSSS', 'SSSSSSSSSS']),
|
|
135
|
+
};
|
|
136
|
+
const 자리폭 = 18;
|
|
137
|
+
const 자리높이 = 14;
|
|
138
|
+
|
|
139
|
+
function 자리색(자세, 머리, 옷) {
|
|
140
|
+
const 꺼짐 = 자세 === '빈자리';
|
|
141
|
+
return {
|
|
142
|
+
h: 머리, f: C.살, b: 옷,
|
|
143
|
+
M: C.검정, S: 꺼짐 ? C.화면꺼짐 : C.화면, G: C.화면글,
|
|
144
|
+
D: 꺼짐 ? C.나무어둠 : C.나무, d: 꺼짐 ? C.나무진 : C.나무어둠,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 방 한 판을 줄들로.
|
|
150
|
+
*
|
|
151
|
+
* @param {object} 상태 아래 상태만들기() 가 만든 것
|
|
152
|
+
* @param {number} 폭 터미널 칸 수
|
|
153
|
+
*/
|
|
154
|
+
export function 사무실줄들(상태, 폭) {
|
|
155
|
+
const {
|
|
156
|
+
일꾼들 = [], 읽은파일 = 0, 고친파일 = 0, 할일 = 0, 끝난일 = 0,
|
|
157
|
+
부른횟수 = 0, 창 = 0, 틱 = 0,
|
|
158
|
+
} = 상태;
|
|
159
|
+
|
|
160
|
+
const 가로 = Math.max(60, 폭);
|
|
161
|
+
const p = new 판(가로, 세로);
|
|
162
|
+
p.칠하기(0, 0, 가로, 벽, C.벽);
|
|
163
|
+
p.칠하기(0, 벽, 가로, 1, C.굽도리);
|
|
164
|
+
p.칠하기(0, 벽 + 1, 가로, 세로 - 벽 - 1, C.바닥);
|
|
165
|
+
|
|
166
|
+
const 밤인가 = 창 > 0.55;
|
|
167
|
+
|
|
168
|
+
// 화이트보드 18×5 — 할 일. 끝난 것은 초록, 남은 것은 노랑.
|
|
169
|
+
{
|
|
170
|
+
const 그림 = ['FFFFFFFFFFFFFFFFFF'];
|
|
171
|
+
for (let r = 0; r < 3; r++) 그림.push(`F${'W'.repeat(16)}F`);
|
|
172
|
+
그림.push('FFFFFFFFFFFFFFFFFF');
|
|
173
|
+
const 총 = Math.min(5, 할일 + 끝난일);
|
|
174
|
+
for (let n = 0; n < 총; n++) {
|
|
175
|
+
const x = 2 + n * 3;
|
|
176
|
+
for (const r of [1, 2, 3]) {
|
|
177
|
+
const 줄 = 그림[r].split('');
|
|
178
|
+
줄[x] = n < 끝난일 ? 'D' : 'N';
|
|
179
|
+
줄[x + 1] = n < 끝난일 ? 'D' : 'N';
|
|
180
|
+
그림[r] = 줄.join('');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
p.올리기(1, 0, 그림, { F: C.쇠어둠, W: C.종이, N: C.쪽지, D: C.쪽지끝 });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 서버 불빛 14×5 — 모델 호출. 오른쪽 벽 끝.
|
|
187
|
+
{
|
|
188
|
+
const 그림 = ['RRRRRRRRRRRRRR'];
|
|
189
|
+
for (let r = 0; r < 3; r++) {
|
|
190
|
+
let 줄 = 'R';
|
|
191
|
+
for (let k = 0; k < 12; k++) 줄 += k < Math.min(12, 부른횟수 * 2) ? 'L' : 'l';
|
|
192
|
+
그림.push(줄 + 'R');
|
|
193
|
+
}
|
|
194
|
+
그림.push('RRRRRRRRRRRRRR');
|
|
195
|
+
p.올리기(가로 - 15, 0, 그림, { R: C.검정, L: C.초록, l: C.쇠진 });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 창문 — 벽에 남는 자리만큼. 컨텍스트가 차면 밤이 된다.
|
|
199
|
+
{
|
|
200
|
+
const 시작 = 21;
|
|
201
|
+
const 남 = 가로 - 15 - 시작;
|
|
202
|
+
const 창수 = Math.max(0, Math.floor(남 / 18));
|
|
203
|
+
for (let i = 0; i < 창수; i++) {
|
|
204
|
+
const 그림 = ['FFFFFFFFFFFFFFFF'];
|
|
205
|
+
for (let r = 0; r < 3; r++) {
|
|
206
|
+
let 줄 = 'F';
|
|
207
|
+
for (let k = 0; k < 14; k++) {
|
|
208
|
+
const h = (i * 37 + r * 19 + k * 11) % 29;
|
|
209
|
+
줄 += k === 7 ? 'F' : (밤인가 ? (h < 5 ? '*' : '.') : (h < 3 ? 'o' : ','));
|
|
210
|
+
}
|
|
211
|
+
그림.push(줄 + 'F');
|
|
212
|
+
}
|
|
213
|
+
그림.push('FFFFFFFFFFFFFFFF');
|
|
214
|
+
p.올리기(시작 + i * 18, 0, 그림, 밤인가
|
|
215
|
+
? { F: C.쇠어둠, '.': C.밤, '*': C.별 }
|
|
216
|
+
: { F: C.쇠어둠, ',': C.낮, o: C.구름 });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 서류함 12×12 — 읽은 파일. 서랍 하나가 일곱쯤.
|
|
221
|
+
{
|
|
222
|
+
const 찬칸 = Math.min(3, Math.ceil(읽은파일 / 7));
|
|
223
|
+
const 그림 = ['CCCCCCCCCCCC'];
|
|
224
|
+
for (let s = 0; s < 3; s++) {
|
|
225
|
+
const 안 = s < 찬칸 ? 'P' : 'c';
|
|
226
|
+
그림.push(`C${안.repeat(4)}HH${안.repeat(4)}C`);
|
|
227
|
+
그림.push(`C${안.repeat(10)}C`);
|
|
228
|
+
그림.push('CCCCCCCCCCCC');
|
|
229
|
+
}
|
|
230
|
+
p.올리기(1, 벽 + 5, 그림.slice(0, 12), { C: C.쇠어둠, c: C.쇠, P: C.종이어둠, H: C.쇠진 });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/*
|
|
234
|
+
* 일하는 자리. **늘 같은 수를 그린다** — 일이 늘 때 자리가 차오르는 것으로
|
|
235
|
+
* 보여야 하는데, 없는 자리를 새로 그리면 방이 늘었다 줄었다 해서 어지럽다.
|
|
236
|
+
*/
|
|
237
|
+
const 왼쪽 = 15;
|
|
238
|
+
const 자리수 = Math.max(1, Math.floor((가로 - 왼쪽 - 2) / (자리폭 + 1)));
|
|
239
|
+
const 짝 = Math.floor(틱 / 5) % 2 === 0;
|
|
240
|
+
for (let i = 0; i < 자리수; i++) {
|
|
241
|
+
const x = 왼쪽 + i * (자리폭 + 1);
|
|
242
|
+
const y = 세로 - 자리높이;
|
|
243
|
+
const 그사람 = 일꾼들[i];
|
|
244
|
+
const 자세 = !그사람 ? '빈자리'
|
|
245
|
+
: 그사람.갈래 === '쓰기' ? (짝 ? '치기A' : '치기B')
|
|
246
|
+
: 그사람.갈래 === '읽기' ? (짝 ? '읽기A' : '읽기B')
|
|
247
|
+
: 그사람.갈래 === '졸기' ? '졸기' : '대기';
|
|
248
|
+
p.올리기(x, y, 자세들[자세], 자리색(자세, C.머리[i % 4], C.옷[i % 4]));
|
|
249
|
+
// 조는 사람 머리 위 z
|
|
250
|
+
if (자세 === '졸기' && 짝) p.칠하기(x + 13, y + 2, 2, 1, C.종이);
|
|
251
|
+
// 고친 파일은 책상 위 종이로
|
|
252
|
+
if (i < 고친파일) p.칠하기(x + 13, y + 9, 4, 1, C.종이);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return p.줄들();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/*
|
|
259
|
+
* ── 켜나 마나 ───────────────────────────────────────────────────────────
|
|
260
|
+
*
|
|
261
|
+
* 기본은 꺼져 있다. 화면 절반을 먹는 것을 묻지도 않고 켜면 안 된다.
|
|
262
|
+
*
|
|
263
|
+
* 켜 달라고 해도 못 켜는 자리가 있다.
|
|
264
|
+
* · 256색이 안 되는 터미널 — 색 번호가 글에 그대로 새어 나온다
|
|
265
|
+
* · 그림을 끈 사람 (DEEL_NO_MOTION) — 화면 읽기 프로그램을 쓰는 자리다
|
|
266
|
+
* · 화면이 낮은 터미널 — 사무실 12줄 + 상자 5줄이면 대화가 들어갈 자리가
|
|
267
|
+
* 없다. 사무실을 보려고 도구를 못 쓰게 되는 것은 앞뒤가 바뀐 것이다.
|
|
268
|
+
*/
|
|
269
|
+
export const 최소높이 = 28;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 사람이 켜 달라고 했나 (화면이 되는지는 안 본다).
|
|
273
|
+
*
|
|
274
|
+
* 켜나() 와 갈라 두는 이유: 상자 안 그림(기사·동물)을 끌지 말지는 **뜻**으로
|
|
275
|
+
* 정해야지 화면 높이로 정하면 안 된다. 창을 줄였다 늘였다 할 때마다 상자 안
|
|
276
|
+
* 그림이 기사였다 돌림표였다 하면 무슨 규칙인지 아무도 모른다.
|
|
277
|
+
*/
|
|
278
|
+
export function 켜달라했나() {
|
|
279
|
+
const v = String(process.env.DEEL_OFFICE ?? '').trim().toLowerCase();
|
|
280
|
+
return v === '1' || v === 'on' || v === '켬' || v === '사무실';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function 켜나(rows = process.stdout.rows) {
|
|
284
|
+
if (!켜달라했나()) return false;
|
|
285
|
+
if (process.env.DEEL_NO_MOTION) return false;
|
|
286
|
+
if (!색256가능()) return false;
|
|
287
|
+
return (rows ?? 0) >= 최소높이;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 세션에서 방 상태를 뽑는다.
|
|
292
|
+
*
|
|
293
|
+
* 여기가 '전부 진짜 숫자' 를 지키는 자리다. 없는 값은 0 으로 두지, 그럴듯한
|
|
294
|
+
* 것으로 채우지 않는다.
|
|
295
|
+
*
|
|
296
|
+
* @param {object} session
|
|
297
|
+
* @param {object} 일감 지금 도는 일 (없으면 null)
|
|
298
|
+
* @param {number} 틱
|
|
299
|
+
*/
|
|
300
|
+
export function 상태만들기(session, 일감, 틱 = 0, 할일들 = [], 도는하위 = 0) {
|
|
301
|
+
const s = session ?? {};
|
|
302
|
+
const 쓴것 = s.usage ?? {};
|
|
303
|
+
const 총토큰 = (쓴것.in ?? 0) + (쓴것.out ?? 0);
|
|
304
|
+
const 창크기 = s.conn?.ctx ?? 0;
|
|
305
|
+
|
|
306
|
+
/*
|
|
307
|
+
* 자리에 앉는 사람들.
|
|
308
|
+
*
|
|
309
|
+
* 지금은 도는 일이 하나(또는 하위 작업만큼)다. 갈래는 일감이 말해 주는
|
|
310
|
+
* 것을 그대로 쓴다 — motion.js 가 쓰는 그 갈래다.
|
|
311
|
+
*/
|
|
312
|
+
const 일꾼들 = [];
|
|
313
|
+
if (일감) {
|
|
314
|
+
const 오래됐나 = 일감.갈래 === '느긋';
|
|
315
|
+
const 갈래 = 오래됐나 ? '졸기'
|
|
316
|
+
: ['읽기', '웹'].includes(일감.갈래) ? '읽기'
|
|
317
|
+
: ['쓰기', '명령', '답'].includes(일감.갈래) ? '쓰기' : '대기';
|
|
318
|
+
// 하위 작업이 돌면 그만큼 자리가 더 찬다. 부모 몫 하나는 늘 있다.
|
|
319
|
+
const 수 = 1 + Math.max(0, 도는하위);
|
|
320
|
+
for (let i = 0; i < 수; i++) 일꾼들.push({ 갈래 });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
일꾼들,
|
|
325
|
+
읽은파일: s.filesRead?.size ?? 0,
|
|
326
|
+
고친파일: s.changes?.size ?? 0,
|
|
327
|
+
끝난일: 할일들.filter((t) => t.state === 'done').length,
|
|
328
|
+
할일: 할일들.filter((t) => t.state !== 'done').length,
|
|
329
|
+
부른횟수: 쓴것.calls ?? 0,
|
|
330
|
+
토큰: 총토큰,
|
|
331
|
+
창: 창크기 ? Math.min(1, 총토큰 / 창크기) : 0,
|
|
332
|
+
틱,
|
|
333
|
+
};
|
|
334
|
+
}
|