deel-local-cli 1.7.0 → 1.10.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 +1166 -0
- package/README.md +1223 -1107
- package/bin/deel.js +78 -12
- package/package.json +4 -4
- package/src/acp/map.js +1 -1
- package/src/acp/serve.js +40 -3
- package/src/agent/compact.js +314 -296
- package/src/agent/effort.js +27 -7
- package/src/agent/evidence.js +2 -0
- package/src/agent/filemem.js +141 -0
- package/src/agent/grade.js +51 -1
- package/src/agent/loop.js +218 -40
- package/src/agent/session.js +136 -14
- package/src/agent/store.js +186 -9
- package/src/agent/threads.js +26 -1
- package/src/backend/adapter.js +272 -23
- package/src/backend/learn.js +24 -0
- package/src/backend/mcp.js +96 -5
- package/src/backend/probe.js +131 -61
- package/src/backend/quota.js +29 -5
- package/src/backend/retry.js +3 -0
- package/src/backend/scanui.js +1 -1
- package/src/backend/toolfit.js +325 -0
- package/src/commands.js +170 -61
- package/src/completion.js +20 -2
- package/src/config.js +47 -2
- package/src/i18n/en.js +210 -3
- package/src/i18n/index.js +40 -0
- package/src/i18n/ja.js +204 -3
- package/src/i18n/ko.js +270 -3
- package/src/i18n/zh.js +204 -3
- package/src/lsp/client.js +49 -5
- package/src/oneshot.js +92 -5
- package/src/pack/sbom.js +30 -4
- package/src/pack/selfpack.js +26 -10
- package/src/pack/sheet.en.js +288 -0
- package/src/pack/tar.js +65 -2
- package/src/plugins/manage.js +46 -9
- package/src/repl.js +344 -61
- package/src/reset.js +397 -0
- package/src/safety/audit.js +95 -8
- package/src/safety/authcmd.js +316 -0
- package/src/safety/guard.js +143 -0
- package/src/safety/keystore.js +114 -38
- package/src/safety/undo.js +25 -6
- package/src/tools/desc.en.js +19 -14
- package/src/tools/index.js +349 -75
- package/src/tools/jobs.js +175 -42
- 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 +42 -7
- package/src/tools/webfetch.js +94 -10
- package/src/ui/export.js +1 -1
- package/src/ui/md.js +201 -5
- package/src/ui/motion.js +0 -1
- package/src/ui/pastechip.js +50 -3
- package/src/ui/pick.js +115 -0
- package/src/ui/screen.js +23 -3
- package/README.en.md +0 -1117
package/src/i18n/ko.js
CHANGED
|
@@ -25,6 +25,30 @@ export const ko = {
|
|
|
25
25
|
'cmd.model.arg': '[이름|list|models|카드]',
|
|
26
26
|
'cmd.think.desc': '추론 강도 (off/low/medium/high/max)',
|
|
27
27
|
'cmd.think.arg': '<수준>',
|
|
28
|
+
|
|
29
|
+
// ── /think 화면 ────────────────────────────────────────────────────
|
|
30
|
+
// 표의 틀만 옮기고 알맹이(단계 이름)를 안 옮기면 아무것도 안 읽힌다.
|
|
31
|
+
// 단계 이름·배분 이름은 agent/effort.js 가 들고 있다 — modes.js 와 같은 방식.
|
|
32
|
+
'run.noTools': '도구 호출이 확인되지 않았습니다 — deel diagnose 로 점검하세요',
|
|
33
|
+
'think.effort': '추론 강도',
|
|
34
|
+
'think.profile': '배분',
|
|
35
|
+
'think.detail': '자세히',
|
|
36
|
+
'think.harder': '더 세게',
|
|
37
|
+
'think.faster': '더 빠르게',
|
|
38
|
+
'think.eg': '예',
|
|
39
|
+
'think.col.stage': '단계',
|
|
40
|
+
'think.col.level': '강도',
|
|
41
|
+
'think.col.cap': '출력상한',
|
|
42
|
+
'think.col.when': '언제',
|
|
43
|
+
'think.cap.head': '출력 상한은',
|
|
44
|
+
'think.cap.tail': ' 안에서 나눕니다 —',
|
|
45
|
+
'think.cap.you': '(직접 정하신 값)',
|
|
46
|
+
'think.cap.server': '(서버에서 알아낸 값)',
|
|
47
|
+
'think.cap.guess': '(모르는 값이라 기본값)',
|
|
48
|
+
'think.ctx': '컨텍스트',
|
|
49
|
+
'think.ctx.used': ' · 지금 찬 양 ',
|
|
50
|
+
'think.nomodel': '이 연결은 모델 층 강도 조절이 적용되지 않습니다.',
|
|
51
|
+
'think.nomodel.note': '출력 상한만 단계별로 적용됩니다.',
|
|
28
52
|
'cmd.mode.desc': '승인 정책 — 얼마나 물어보나 (auto/confirm/strict)',
|
|
29
53
|
'cmd.mode.arg': '<모드>',
|
|
30
54
|
'cmd.work.desc': '작업 모드 — 무슨 일을 하는 중인가 (종합이면 저절로)',
|
|
@@ -98,8 +122,10 @@ export const ko = {
|
|
|
98
122
|
'consult.failed': '못 물어봤습니다 — {왜}',
|
|
99
123
|
'consult.empty': '빈 답이 왔습니다. 그 모델이 도구 없이 답을 못 내는 것일 수 있습니다.',
|
|
100
124
|
'consult.added': '이 답을 대화에도 넣었습니다 — 누가 한 말인지 표를 달아서.',
|
|
125
|
+
// ── 연결 진단 ─────────────────────────────────────────────────────────
|
|
126
|
+
'probe.codexOnly': '{모델} 계열은 이 문(/chat/completions)으로는 안 나옵니다 — OpenAI 가 Responses API(/responses)로만 내주는 모델인데, deel 은 아직 그 문을 못 씁니다. 지금은 다른 모델을 고르세요.',
|
|
101
127
|
// ── 켤 때 머리말 ──────────────────────────────────────────────────────
|
|
102
|
-
'head.spec.ollama': 'Ollama 규격',
|
|
128
|
+
'head.spec.ollama': 'Ollama 자체 규격',
|
|
103
129
|
'head.spec.openai': 'OpenAI 호환 규격',
|
|
104
130
|
'head.spec.anthropic': 'Anthropic 규격',
|
|
105
131
|
'head.mode.default': '이 안 (기본)',
|
|
@@ -170,6 +196,7 @@ export const ko = {
|
|
|
170
196
|
'undo.alsoTalk': '대화도 {n}개 걷어냈습니다 — 모델이 되돌린 코드를 아직 있는 줄 알면 그 위에 이어 일합니다.',
|
|
171
197
|
'undo.repaired': '(짝 없는 도구 호출 {n}개도 같이 정리)',
|
|
172
198
|
'undo.saidWas': '시켰던 말:',
|
|
199
|
+
'undo.failed': '{n}개는 못 되돌렸습니다 — 그 파일은 고쳐진 그대로입니다. 기록을 남겨 뒀으니 /undo 를 다시 해 보세요.',
|
|
173
200
|
'undo.talkKept': '대화는 그대로 둡니다 — 그 턴의 말이 이미 접혀 없어졌습니다.',
|
|
174
201
|
'undo.talkKeptWhy': '모델이 되돌린 것을 아직 있는 줄 알 수 있으니, 이어 시킬 때 한 번 짚어 주세요.',
|
|
175
202
|
|
|
@@ -282,19 +309,259 @@ export const ko = {
|
|
|
282
309
|
'level.developer.hint': '전부 직접 만짐',
|
|
283
310
|
|
|
284
311
|
// ── 되묻기 상자 (Ask 도구) ────────────────────────────────────────────
|
|
312
|
+
/*
|
|
313
|
+
* 계획 상자.
|
|
314
|
+
*
|
|
315
|
+
* 여태 이 줄은 한국어가 글자 그대로 박혀 있었다 — `/lang en` 을 켠
|
|
316
|
+
* 사람이 계획을 승인하는 자리에서만 한글을 봤다. 하필 「무엇을 치면
|
|
317
|
+
* 되나」 를 말하는 줄이라, 못 읽으면 그 자리에서 막힌다.
|
|
318
|
+
*
|
|
319
|
+
* 셋을 한 줄에 보여 준다. ⏎ 하나로 진행되는 것을 맨 앞에 둔다 —
|
|
320
|
+
* 그게 열에 아홉이고, 여기서 뭘 더 치게 하면 사람은 애초에 계획
|
|
321
|
+
* 같은 걸 안 보려 든다.
|
|
322
|
+
*/
|
|
323
|
+
'plan.ask': '이대로 진행할까요?',
|
|
324
|
+
'plan.hint': '⏎ 진행 · y 한마디 붙여 진행 · 3 그 단계에 한마디 · n 취소',
|
|
325
|
+
'plan.cancelled': '그만뒀습니다. 계획은 위에 남아 있으니 이어서 말씀하셔도 됩니다.',
|
|
326
|
+
'plan.going': '계획대로 진행합니다.',
|
|
327
|
+
'plan.goingWith': '한마디를 얹어 진행합니다 — {말}',
|
|
328
|
+
'plan.replan': '그 점을 반영해 계획을 다시 냅니다.',
|
|
329
|
+
'plan.replanStep': '{단계}단계에 대한 말을 반영해 계획을 다시 냅니다.',
|
|
285
330
|
'ask.title': '물어봅니다',
|
|
286
|
-
|
|
331
|
+
/*
|
|
332
|
+
* 「번호 + 하고 싶은 말」 을 여기서 한 줄로 알려 준다.
|
|
333
|
+
*
|
|
334
|
+
* 되는 줄 모르면 아무도 안 쓴다. 그렇다고 세 줄로 적으면 물음보다
|
|
335
|
+
* 안내가 길어져서 정작 물음을 안 읽는다. 보기 하나로 보여 준다 —
|
|
336
|
+
* 보기 하나가 설명 세 줄보다 잘 읽힌다.
|
|
337
|
+
*/
|
|
338
|
+
'ask.pickHint': '숫자로 고르세요. 하고 싶은 말이 있으면 뒤에 붙이면 됩니다 (2 파일은 나눠서)',
|
|
287
339
|
'ask.freeHint': '답을 쳐 주세요',
|
|
288
|
-
'ask.pickPrompt': '1-{끝} 또는 직접 입력',
|
|
340
|
+
'ask.pickPrompt': '1-{끝} (뒤에 의견을 붙여도 됩니다) 또는 직접 입력',
|
|
289
341
|
'ask.freePrompt': '답',
|
|
342
|
+
// 물어보는 도중에 ESC 를 눌렀다. 물음이 화면에 걸린 채로 남으면 안 된다.
|
|
343
|
+
'ask.stopped': '멈췄습니다 — 답하지 않은 것으로 넘어갑니다.',
|
|
290
344
|
|
|
291
345
|
// ── 잠깐 막혔을 때 (backend/retry.js) ─────────────────────────────────
|
|
292
346
|
'loop.backoff': '서버가 잠시 막았습니다 ({무엇}) — {초}초 뒤 다시 부릅니다 ({n}/{max})',
|
|
293
347
|
'cost.retries': '다시 부름',
|
|
294
348
|
|
|
349
|
+
/*
|
|
350
|
+
* ── 세는 말 ───────────────────────────────────────────────────────────
|
|
351
|
+
*
|
|
352
|
+
* 도구 결과 한 줄(`└ 1개 파일 · 3건 · rg`)은 이 조각들을 ` · ` 로 이어
|
|
353
|
+
* 만든다. 통짜 문장으로 안 두는 까닭은, 같은 조각이 열두 자리에서 다른
|
|
354
|
+
* 순서로 섞이기 때문이다 — 통짜로 두면 열쇠가 마흔 개가 되고, 그러면
|
|
355
|
+
* 새 자리를 만들 때마다 표를 네 번 고쳐야 해서 결국 아무도 안 고친다.
|
|
356
|
+
*
|
|
357
|
+
* 영어는 단수·복수를 갈라야 해서 {n} 하나로는 모자란 자리가 있다.
|
|
358
|
+
* 그런 자리는 열쇠를 둘로 나눠 뒀다(files/file1 처럼). 부르는 쪽에서
|
|
359
|
+
* n === 1 을 보고 고른다 — 한국어 표에서는 두 열쇠가 같은 값이라
|
|
360
|
+
* 한국어 화면은 한 글자도 안 바뀐다.
|
|
361
|
+
*/
|
|
362
|
+
'unit.files': '{n}개 파일',
|
|
363
|
+
'unit.files1': '1개 파일',
|
|
364
|
+
'unit.hits': '{n}건',
|
|
365
|
+
'unit.hits1': '1건',
|
|
366
|
+
'unit.lines': '{n}줄',
|
|
367
|
+
'unit.lines1': '1줄',
|
|
368
|
+
'unit.spots': '{n}군데',
|
|
369
|
+
'unit.spots1': '1군데',
|
|
370
|
+
'unit.places': '{n}곳',
|
|
371
|
+
'unit.places1': '1곳',
|
|
372
|
+
'unit.count': '{n}개',
|
|
373
|
+
'unit.count1': '1개',
|
|
374
|
+
'unit.chars': '{n}자',
|
|
375
|
+
'unit.kchars': '{n}k자',
|
|
376
|
+
'unit.calls': '{n}회',
|
|
377
|
+
'unit.sec': '{n}초',
|
|
378
|
+
'unit.min': '{n}분',
|
|
379
|
+
|
|
380
|
+
// ── 도구 결과 한 줄 ──────────────────────────────────────────────────
|
|
381
|
+
'sum.done': '완료',
|
|
382
|
+
'sum.ok': '성공',
|
|
383
|
+
'sum.exitCode': '종료코드 {code}',
|
|
384
|
+
'sum.killedBy': '{시그널} 로 죽음',
|
|
385
|
+
'sum.tooMuchOut': '출력이 너무 많아 받다 말았습니다',
|
|
386
|
+
'sum.cantRun': '못 돌렸습니다 ({왜})',
|
|
387
|
+
'sum.partial': '일부만',
|
|
388
|
+
'sum.cut': '일부 잘림',
|
|
389
|
+
'sum.notAllSeen': '다 못 봄',
|
|
390
|
+
'sum.filesFailed': '{n}개 실패',
|
|
391
|
+
'sum.spotsFailed': '{n}군데 실패',
|
|
392
|
+
'sum.moved': '{n}개 옮김',
|
|
393
|
+
'sum.total': '전체 {줄}',
|
|
394
|
+
'sum.addedLines': '+{줄}',
|
|
395
|
+
'sum.noVision': '이 모델은 못 봄',
|
|
396
|
+
'sum.emptyAnswer': '빈 답',
|
|
397
|
+
'sum.unread': '못 읽음 {n}개',
|
|
398
|
+
'sum.doneOf': '{끝}/{전체} 완료',
|
|
399
|
+
'sum.justDone': '방금 {n}개',
|
|
400
|
+
'sum.noChange': '그대로 (바뀐 것 없음)',
|
|
401
|
+
'sum.verified': '확인 {n}개',
|
|
402
|
+
'sum.readAs': '{쓴것} 로 바꿔 읽음',
|
|
403
|
+
'sum.encGuess': '{인코딩} (추정)',
|
|
404
|
+
'sum.linesOf': '{준}/{전체}줄',
|
|
405
|
+
'sum.countOf': '{n}/{전체}개',
|
|
406
|
+
'sum.remembered': '기억했습니다 ({n}줄)',
|
|
407
|
+
'sum.rememberFull': '자리가 차서 오래된 것을 뺐습니다',
|
|
408
|
+
'sum.recall': '지난 대화에서 {전체}건 중 {맞음}건',
|
|
409
|
+
'sum.jobStarted': '{n}번으로 띄움',
|
|
410
|
+
'sum.jobRunning': '도는중',
|
|
411
|
+
'sum.jobEndedAlready': '이미 끝남',
|
|
412
|
+
'sum.jobNotStopped': '안 끝났습니다 — 아직 살아 있습니다',
|
|
413
|
+
'sum.jobStopped': '끝냄',
|
|
414
|
+
|
|
415
|
+
// Edit 이 어떤 느슨함으로 자리를 찾았나 (tools/edit-match.js 의 TIERS).
|
|
416
|
+
'tier.exact': '정확히 일치',
|
|
417
|
+
'tier.trail': '줄 끝 공백·줄바꿈 차이 무시',
|
|
418
|
+
'tier.indent': '들여쓰기 차이 무시',
|
|
419
|
+
'tier.space': '모든 공백 차이 무시',
|
|
420
|
+
|
|
421
|
+
'sum.image': '그림',
|
|
422
|
+
'sum.imageDropped': '그림 {크기} 생략',
|
|
423
|
+
|
|
424
|
+
'sum.broken': '탈 {n}개',
|
|
425
|
+
'sum.unverified': '못 확인 {n}개',
|
|
426
|
+
'lsp.noDef': '정의를 못 찾았습니다. Grep 으로 찾아보세요.',
|
|
427
|
+
'lsp.defs': '정의 {n}',
|
|
428
|
+
'lsp.noRefs': '쓰는 자리가 없습니다.',
|
|
429
|
+
'lsp.refs': '쓰는 자리 {자리} · 파일 {파일}',
|
|
430
|
+
|
|
431
|
+
'sum.nothingToCheck': '확인할 것이 없었습니다',
|
|
432
|
+
'job.no': '{n}번',
|
|
433
|
+
'job.stop': '끝내기',
|
|
434
|
+
'more.files': '외 {n}개',
|
|
435
|
+
'more.spots': '외 {n}군데',
|
|
436
|
+
|
|
437
|
+
// ── 도구 오류 ────────────────────────────────────────────────────────
|
|
438
|
+
'err.noSuchFile': '파일이 없습니다: {경로}',
|
|
439
|
+
'err.alreadyThere': '이미 있습니다: {경로}',
|
|
440
|
+
'err.overwriteHint': '덮어쓰려면 overwrite: true 를 주세요.',
|
|
441
|
+
|
|
442
|
+
// ── 대화 화면 ────────────────────────────────────────────────────────
|
|
443
|
+
'run.ctxProbe': '모델에 걸린 컨텍스트 길이를 확인하는 중…',
|
|
444
|
+
'run.noStream': '스트리밍이 없어 응답이 한 번에 나옵니다',
|
|
445
|
+
'run.hintHelp': '/help 명령 목록',
|
|
446
|
+
'run.hintEsc': 'ESC 중단',
|
|
447
|
+
'run.hintQuit': 'Ctrl+C 중단·끝내기',
|
|
448
|
+
// 줄 화면에는 상자가 없다. 눌린 것이 닿았다는 신호를 여기 한 줄로 낸다.
|
|
449
|
+
'run.stopping': '멈추는 중…',
|
|
450
|
+
// 탈이 나기 전까지 흘러온 반쪽을 대화에 남겼다. 안 말하면 다시 처음부터 친다.
|
|
451
|
+
'run.errKept': '여기까지 받은 것은 대화에 남겼습니다 — 이어서 말씀하셔도 됩니다.',
|
|
452
|
+
|
|
453
|
+
/*
|
|
454
|
+
* ── 대화창 말고 다른 앞면이 쓰는 한 줄 (한 방 실행 · ACP) ─────────────
|
|
455
|
+
*
|
|
456
|
+
* 대화창은 이 소식들을 저마다 여러 줄로 풀어 쓴다. 한 방 실행과 에디터
|
|
457
|
+
* 붙임에는 그럴 자리가 없어서 한 줄짜리가 따로 있어야 했는데, 그게
|
|
458
|
+
* 없어서 **아예 아무 말도 안 하고 있었다.** 잘린 답이 온전한 답인 척
|
|
459
|
+
* `deel -p` 뒤 스크립트로 넘어가던 것이 그 탓이다.
|
|
460
|
+
*/
|
|
461
|
+
'ev.capped': '답이 {한계} 토큰에서 잘렸습니다 — /out 으로 상한을 올리거나, 파일은 나눠 쓰게 하세요',
|
|
462
|
+
'ev.cutoff': '서버가 끝났다는 말 없이 답을 멈췄습니다 — 중간에서 끊겼을 수 있습니다',
|
|
463
|
+
'ev.nudgeMissed': '시킨 것 {n}가지를 빼놓고 끝내려고 해서 한 번 되밀었습니다',
|
|
464
|
+
'ev.nudgeRead': '읽기만 하고 끝내려고 해서 한 번 되밀었습니다',
|
|
465
|
+
'ev.learnedCtx': '서버가 알려 준 컨텍스트 한계 {한계} 로 맞추고 다시 부릅니다',
|
|
466
|
+
'ev.learnedOut': '서버가 알려 준 답 길이 한계 {한계} 로 맞추고 다시 부릅니다',
|
|
467
|
+
/*
|
|
468
|
+
* 자리가 다 차서 기억을 비운 자리. **오류가 아니다.**
|
|
469
|
+
*
|
|
470
|
+
* 붉게 말하면 사람은 하던 일이 엎어진 줄 알고 /clear 를 친다 — 정작
|
|
471
|
+
* 지금은 시킨 말과 남은 할 일을 들고 그대로 이어 가는 중인데도.
|
|
472
|
+
* 무엇을 버렸고 무엇을 들고 가는지 한 줄로 사실대로 말한다.
|
|
473
|
+
*/
|
|
474
|
+
'ev.reset': '자리가 모자라 앞선 대화 {버린수}개를 비우고 이어갑니다 — 남은 할 일 {할일}개',
|
|
475
|
+
'ev.resetBare': '자리가 모자라 앞선 대화 {버린수}개를 비우고 이어갑니다',
|
|
476
|
+
/*
|
|
477
|
+
* 붙여넣기 표에 쓰는 낱말.
|
|
478
|
+
*
|
|
479
|
+
* 이 말은 화면에 찍고 **다시 찾아 읽는다.** 보낼 때 표를 원문으로
|
|
480
|
+
* 펴야 하기 때문이다. 그래서 낱말을 바꾸면 무늬(pastechip.js 의 표무늬)가
|
|
481
|
+
* 저절로 따라간다 — 네 말을 다 넣어 짓는다. 옛 말로 찍어 둔 표도
|
|
482
|
+
* 계속 알아본다.
|
|
483
|
+
*/
|
|
484
|
+
'paste.chip': '붙여넣기',
|
|
485
|
+
'paste.chipLines': '{n}줄',
|
|
486
|
+
'paste.folded': '{표} — 접어 뒀습니다. 보낼 때 그대로 펴집니다.',
|
|
487
|
+
'paste.pasted': '{n}줄을 붙여넣었습니다 — Enter 로 한 번에 보냅니다',
|
|
488
|
+
'run.thinking': '생각 중…',
|
|
489
|
+
'run.thinkingChars': '생각 중… {n}자',
|
|
490
|
+
'run.turnFoot': '{초}초 · 도구 {n}회 · ↑{입력} ↓{출력}',
|
|
491
|
+
'run.bye': '끝냅니다.',
|
|
492
|
+
'run.byeStats': '모델 호출 {n}회 · 도구 시간 {초}초 · ↑{입력} ↓{출력}',
|
|
493
|
+
'run.notMade': '— 만들어지지 않았습니다',
|
|
494
|
+
'store.notSaving': '이 대화가 파일에 안 적히고 있습니다 ({까닭}) — {n}줄을 잃었습니다. 지금 창을 닫으면 이어할 수 없습니다.',
|
|
495
|
+
|
|
496
|
+
// 생각 중 앞에 붙는 단계 꼬리표 (agent/effort.js 의 STAGES).
|
|
497
|
+
'stage.plan': '첫 판단',
|
|
498
|
+
'stage.work': '이어가기',
|
|
499
|
+
'stage.fix': '막혔을 때',
|
|
500
|
+
|
|
501
|
+
// ── /tools · /status · /cost 화면 ────────────────────────────────────
|
|
502
|
+
'scr.tools': '도구',
|
|
503
|
+
'scr.conn': '연결',
|
|
504
|
+
'scr.session': '이번 세션',
|
|
505
|
+
'status.kind': '규격',
|
|
506
|
+
'status.url': '주소',
|
|
507
|
+
'status.proxy': '프록시',
|
|
508
|
+
'status.proxyOff': '못 씀',
|
|
509
|
+
'status.model': '모델',
|
|
510
|
+
'status.root': '작업 폴더',
|
|
511
|
+
'status.shell': '셸',
|
|
512
|
+
'status.keyStore': '열쇠 보관',
|
|
513
|
+
'status.rules': '규칙',
|
|
514
|
+
'status.noRules': '없음 (/init 으로 만들 수 있습니다)',
|
|
515
|
+
'status.rulesUnread': '{이름} 이 있는데 못 읽었습니다 ({까닭}) — 이 폴더 규칙이 안 걸려 있습니다',
|
|
516
|
+
'status.caps': '지원',
|
|
517
|
+
'status.capTools': '도구',
|
|
518
|
+
'status.capStream': '스트림',
|
|
519
|
+
'status.capSchema': '스키마',
|
|
520
|
+
'status.capThink': '추론',
|
|
521
|
+
'cost.calls': '모델 호출',
|
|
522
|
+
'cost.tokIn': '입력 토큰',
|
|
523
|
+
'cost.tokOut': '출력 토큰',
|
|
524
|
+
'cost.toolMs': '도구 시간',
|
|
525
|
+
'cost.elapsed': '경과',
|
|
526
|
+
'cost.money': '요금',
|
|
527
|
+
'cost.quota': '서버 할당량',
|
|
528
|
+
'cost.quotaAge': '{초}초 전 응답 기준',
|
|
529
|
+
|
|
530
|
+
// ── /context 표 ──────────────────────────────────────────────────────
|
|
531
|
+
'scr.context': '컨텍스트',
|
|
532
|
+
'unit.tokens': '{n} 토큰',
|
|
533
|
+
'ctx.system': '시스템 프롬프트',
|
|
534
|
+
'ctx.rules': '규칙 ({이름})',
|
|
535
|
+
'ctx.rulesNone': '규칙 (없음)',
|
|
536
|
+
'ctx.memory': '기억 ({n}줄)',
|
|
537
|
+
'ctx.learned': '겪어 본 것',
|
|
538
|
+
'ctx.skills': '스킬 목록 ({실림}/{전체}개)',
|
|
539
|
+
'ctx.tools': '도구 정의',
|
|
540
|
+
'ctx.history': '대화 이력',
|
|
541
|
+
'ctx.toolResults': '도구 결과 (파일 {n}개)',
|
|
542
|
+
'ctx.left': '남음',
|
|
543
|
+
'ctx.compactHint': '대화 줄이기',
|
|
544
|
+
'ctx.clearHint': '통째로 비우기',
|
|
545
|
+
'ctx.calibrated': '서버가 알려 준 실제값에 맞춰 {부호}{퍼센트}% 보정했습니다 ({번}번 재봄).',
|
|
546
|
+
'ctx.estimate': '숫자는 추정입니다 — 정확한 토크나이저를 쓰지 않습니다.',
|
|
547
|
+
|
|
548
|
+
// ── 열쇠받기 (safety/authcmd.js) ─────────────────────────────────────
|
|
549
|
+
'auth.waiting': '회사 로그인을 기다리는 중…',
|
|
550
|
+
'auth.got': '열쇠를 받았습니다 — {분}분쯤 씁니다',
|
|
551
|
+
'auth.failed': '열쇠를 못 받았습니다 — {왜}',
|
|
552
|
+
'auth.from': '받아 옴',
|
|
553
|
+
'auth.fromPolicy': '받아 옴 (관리 정책)',
|
|
554
|
+
'auth.leftMin': '{분}분 남음',
|
|
555
|
+
'auth.notYet': '아직 안 받음',
|
|
556
|
+
|
|
557
|
+
// ── 되돌리기 결과 줄 ─────────────────────────────────────────────────
|
|
558
|
+
'undo.wayRestored': '되돌림',
|
|
559
|
+
'undo.wayDeleted': '삭제됨(원래 없던 파일)',
|
|
560
|
+
|
|
295
561
|
// ── 자주 나오는 것 ────────────────────────────────────────────────────
|
|
296
562
|
'common.unknownCommand': '모르는 명령입니다',
|
|
297
563
|
'common.seeHelp': '명령 목록은 /help',
|
|
298
564
|
'common.yes': '네',
|
|
299
565
|
'common.no': '아니요',
|
|
566
|
+
'common.cfgSaveFailed': '설정을 파일에 남기지 못했습니다 ({왜}) — 이번 판에는 먹지만 다시 켜면 돌아갑니다.',
|
|
300
567
|
};
|
package/src/i18n/zh.js
CHANGED
|
@@ -22,6 +22,31 @@ export const zh = {
|
|
|
22
22
|
'cmd.model.arg': '[名称|list|models|card]',
|
|
23
23
|
'cmd.think.desc': '推理力度 (off/low/medium/high/max)',
|
|
24
24
|
'cmd.think.arg': '<级别>',
|
|
25
|
+
|
|
26
|
+
// ── /think 画面 ─────────────────────────────────────────────────────
|
|
27
|
+
// 只译表格的框、不译里面的内容(阶段名),就等于什么都读不懂。
|
|
28
|
+
// 阶段名·分配名在 agent/effort.js 里 — 和 modes.js 一样的做法。
|
|
29
|
+
'run.noTools': '尚未确认工具调用 — 请用 deel diagnose 检查',
|
|
30
|
+
'think.effort': '推理力度',
|
|
31
|
+
'think.profile': '分配',
|
|
32
|
+
'think.detail': '详细',
|
|
33
|
+
'think.harder': '更用力',
|
|
34
|
+
'think.faster': '更快',
|
|
35
|
+
'think.eg': '例',
|
|
36
|
+
'think.col.stage': '阶段',
|
|
37
|
+
'think.col.level': '力度',
|
|
38
|
+
'think.col.cap': '输出上限',
|
|
39
|
+
'think.col.when': '何时',
|
|
40
|
+
'think.cap.head': '输出上限在',
|
|
41
|
+
'think.cap.tail': ' 之内分配 —',
|
|
42
|
+
'think.cap.you': '(您自己定的值)',
|
|
43
|
+
'think.cap.server': '(从服务器读到的值)',
|
|
44
|
+
'think.cap.guess': '(不知道,所以用默认值)',
|
|
45
|
+
'think.ctx': '上下文',
|
|
46
|
+
'think.ctx.used': ' · 已用 ',
|
|
47
|
+
'think.nomodel': '这个连接不会在模型那一层调节推理力度。',
|
|
48
|
+
'think.nomodel.note': '只有各阶段的输出上限会生效。',
|
|
49
|
+
|
|
25
50
|
'cmd.mode.desc': '批准策略 — 先问到什么程度 (auto/confirm/strict)',
|
|
26
51
|
'cmd.mode.arg': '<模式>',
|
|
27
52
|
'cmd.work.desc': '工作模式 — 这是哪一类活儿 (auto 会替你选)',
|
|
@@ -94,7 +119,8 @@ export const zh = {
|
|
|
94
119
|
'consult.failed': '问不成 — {왜}',
|
|
95
120
|
'consult.empty': '返回是空的。那个模型可能不带工具就不回答。',
|
|
96
121
|
'consult.added': '已把那个回答加进对话,并标明是谁说的。',
|
|
97
|
-
'
|
|
122
|
+
'probe.codexOnly': '{모델} 系列无法通过这个接口(/chat/completions)使用 — OpenAI 只通过 Responses API(/responses) 提供该系列,而 deel 还不会说那个接口。请暂时改选别的模型。',
|
|
123
|
+
'head.spec.ollama': 'Ollama 自有规格',
|
|
98
124
|
'head.spec.openai': 'OpenAI 兼容 API',
|
|
99
125
|
'head.spec.anthropic': 'Anthropic API',
|
|
100
126
|
'head.mode.default': '本机内(默认)',
|
|
@@ -158,6 +184,7 @@ export const zh = {
|
|
|
158
184
|
'undo.alsoTalk': '同时撤回了 {n} 条消息 — 如果模型还以为被撤销的代码在,它会继续往上搭。',
|
|
159
185
|
'undo.repaired': '(另外清理了 {n} 个悬空的工具调用)',
|
|
160
186
|
'undo.saidWas': '你当时说的是:',
|
|
187
|
+
'undo.failed': '有 {n} 个没能还原 — 那些文件还是改过的样子。备份留着,请再试一次 /undo。',
|
|
161
188
|
'undo.talkKept': '对话保持原样 — 那一轮早就被折叠了。',
|
|
162
189
|
'undo.talkKeptWhy': '模型可能仍以为撤销掉的工作还在,下一句里提一下。',
|
|
163
190
|
'bell.stateOn': '开',
|
|
@@ -258,15 +285,189 @@ export const zh = {
|
|
|
258
285
|
'level.beginner.hint': '按推荐设置开始',
|
|
259
286
|
'level.developer.name': '开发者',
|
|
260
287
|
'level.developer.hint': '全部自己来',
|
|
288
|
+
'plan.ask': '就按这个做吗?',
|
|
289
|
+
'plan.hint': '⏎ 开始 · y 加一句再开始 · 3 对那一步说一句 · n 取消',
|
|
290
|
+
'plan.cancelled': '停下了。计划还在上面,接着说就行。',
|
|
291
|
+
'plan.going': '按计划开始。',
|
|
292
|
+
'plan.goingWith': '带上你这句话开始 — {말}',
|
|
293
|
+
'plan.replan': '按这一点重新出计划。',
|
|
294
|
+
'plan.replanStep': '按你对第 {단계} 步的说法重新出计划。',
|
|
261
295
|
'ask.title': '有个问题问你',
|
|
262
|
-
'ask.pickHint': '
|
|
296
|
+
'ask.pickHint': '选一个编号。有话要说就写在编号后面 (2 文件分开写)',
|
|
263
297
|
'ask.freeHint': '写下你的回答',
|
|
264
|
-
'ask.pickPrompt': '1-{끝}
|
|
298
|
+
'ask.pickPrompt': '1-{끝}(后面可以加一句话),或者自己写',
|
|
265
299
|
'ask.freePrompt': '回答',
|
|
300
|
+
'ask.stopped': '已停止 — 按没有回答继续。',
|
|
266
301
|
'loop.backoff': '服务端把请求推回来了 ({무엇}) — {초} 秒后再试 ({n}/{max})',
|
|
267
302
|
'cost.retries': '重试',
|
|
303
|
+
// ── 计数词 ────────────────────────────────────────────────────────────
|
|
304
|
+
'unit.files': '{n}个文件',
|
|
305
|
+
'unit.files1': '1个文件',
|
|
306
|
+
'unit.hits': '{n}处匹配',
|
|
307
|
+
'unit.hits1': '1处匹配',
|
|
308
|
+
'unit.lines': '{n}行',
|
|
309
|
+
'unit.lines1': '1行',
|
|
310
|
+
'unit.spots': '{n}处',
|
|
311
|
+
'unit.spots1': '1处',
|
|
312
|
+
'unit.places': '{n}处',
|
|
313
|
+
'unit.places1': '1处',
|
|
314
|
+
'unit.count': '{n}个',
|
|
315
|
+
'unit.count1': '1个',
|
|
316
|
+
'unit.chars': '{n}字',
|
|
317
|
+
'unit.kchars': '{n}k字',
|
|
318
|
+
'unit.calls': '{n}次',
|
|
319
|
+
'unit.sec': '{n}秒',
|
|
320
|
+
'unit.min': '{n}分钟',
|
|
321
|
+
|
|
322
|
+
'sum.done': '完成',
|
|
323
|
+
'sum.ok': '成功',
|
|
324
|
+
'sum.exitCode': '退出码 {code}',
|
|
325
|
+
'sum.killedBy': '被 {시그널} 结束',
|
|
326
|
+
'sum.tooMuchOut': '输出太多,接到一半就停了',
|
|
327
|
+
'sum.cantRun': '没能运行({왜})',
|
|
328
|
+
'sum.partial': '仅部分',
|
|
329
|
+
'sum.cut': '已截断',
|
|
330
|
+
'sum.notAllSeen': '未全部扫描',
|
|
331
|
+
'sum.filesFailed': '{n}个失败',
|
|
332
|
+
'sum.spotsFailed': '{n}处失败',
|
|
333
|
+
'sum.moved': '移动{n}个',
|
|
334
|
+
'sum.total': '共{줄}',
|
|
335
|
+
'sum.addedLines': '+{줄}',
|
|
336
|
+
'sum.noVision': '当前模型看不了图片',
|
|
337
|
+
'sum.emptyAnswer': '空回答',
|
|
338
|
+
'sum.unread': '读不了{n}个',
|
|
339
|
+
'sum.doneOf': '{끝}/{전체} 完成',
|
|
340
|
+
'sum.justDone': '刚完成{n}个',
|
|
341
|
+
'sum.noChange': '没有变化',
|
|
342
|
+
'sum.verified': '核对{n}项',
|
|
343
|
+
'sum.readAs': '用 {쓴것} 转换后读取',
|
|
344
|
+
'sum.encGuess': '{인코딩}(推测)',
|
|
345
|
+
'sum.linesOf': '{준}/{전체}行',
|
|
346
|
+
'sum.countOf': '{n}/{전체}个',
|
|
347
|
+
'sum.remembered': '已记住({n}行)',
|
|
348
|
+
'sum.rememberFull': '位置满了,已移除最旧的',
|
|
349
|
+
'sum.recall': '过往对话 {전체}条中 {맞음}条',
|
|
350
|
+
'sum.jobStarted': '以{n}号启动',
|
|
351
|
+
'sum.jobRunning': '运行中',
|
|
352
|
+
'sum.jobEndedAlready': '已经结束',
|
|
353
|
+
'sum.jobNotStopped': '没有结束 — 还活着',
|
|
354
|
+
'sum.jobStopped': '已结束',
|
|
355
|
+
|
|
356
|
+
'tier.exact': '完全匹配',
|
|
357
|
+
'tier.trail': '忽略行尾空白和换行差异',
|
|
358
|
+
'tier.indent': '忽略缩进差异',
|
|
359
|
+
'tier.space': '忽略所有空白差异',
|
|
360
|
+
|
|
361
|
+
'sum.image': '图片',
|
|
362
|
+
'sum.imageDropped': '省略图片 {크기}',
|
|
363
|
+
|
|
364
|
+
'sum.broken': '{n}处问题',
|
|
365
|
+
'sum.unverified': '{n}项未核对',
|
|
366
|
+
'lsp.noDef': '找不到定义。用 Grep 试试。',
|
|
367
|
+
'lsp.defs': '定义 {n}',
|
|
368
|
+
'lsp.noRefs': '没有任何地方用到。',
|
|
369
|
+
'lsp.refs': '使用 {자리} · 文件 {파일}',
|
|
370
|
+
|
|
371
|
+
'sum.nothingToCheck': '没有可核对的东西',
|
|
372
|
+
'job.no': '{n}号',
|
|
373
|
+
'job.stop': '结束',
|
|
374
|
+
'more.files': '另外{n}个',
|
|
375
|
+
'more.spots': '另外{n}处',
|
|
376
|
+
|
|
377
|
+
'err.noSuchFile': '没有这个文件: {경로}',
|
|
378
|
+
'err.alreadyThere': '已经存在: {경로}',
|
|
379
|
+
'err.overwriteHint': '要覆盖请传 overwrite: true。',
|
|
380
|
+
|
|
381
|
+
'run.ctxProbe': '正在确认模型启动时的上下文长度…',
|
|
382
|
+
'run.noStream': '没有流式输出,回答会一次性出现',
|
|
383
|
+
'run.hintHelp': '/help 命令列表',
|
|
384
|
+
'run.hintEsc': 'ESC 中断',
|
|
385
|
+
'run.hintQuit': 'Ctrl+C 中断·退出',
|
|
386
|
+
'run.stopping': '正在停止…',
|
|
387
|
+
'run.errKept': '出错前收到的内容已经留在对话里 — 可以直接接着说。',
|
|
388
|
+
'ev.capped': '回答在 {한계} 个 token 处被截断 — 用 /out 调高上限,或者让文件分次写',
|
|
389
|
+
'ev.cutoff': '服务端没说结束就停下了 — 回答可能是中途断的',
|
|
390
|
+
'ev.nudgeMissed': '还剩你交代的 {n} 件事没做就要收尾,已经推回去一次',
|
|
391
|
+
'ev.nudgeRead': '只看不动就要收尾,已经推回去一次',
|
|
392
|
+
'ev.learnedCtx': '按服务端给出的上下文上限 {한계} 重新调用',
|
|
393
|
+
'ev.learnedOut': '按服务端给出的回答长度上限 {한계} 重新调用',
|
|
394
|
+
'ev.reset': '位置不够,已清掉前面 {버린수} 条对话继续做 — 还剩 {할일} 件待办',
|
|
395
|
+
'ev.resetBare': '位置不够,已清掉前面 {버린수} 条对话继续做',
|
|
396
|
+
'paste.chip': '粘贴',
|
|
397
|
+
'paste.chipLines': '{n}行',
|
|
398
|
+
'paste.folded': '{표} — 已折起。发送时会原样展开。',
|
|
399
|
+
'paste.pasted': '已粘贴 {n} 行 — 按 Enter 一次发送',
|
|
400
|
+
'run.thinking': '思考中…',
|
|
401
|
+
'run.thinkingChars': '思考中… {n}字',
|
|
402
|
+
'run.turnFoot': '{초}秒 · 工具{n}次 · ↑{입력} ↓{출력}',
|
|
403
|
+
'run.bye': '结束。',
|
|
404
|
+
'run.byeStats': '模型调用{n}次 · 工具耗时{초}秒 · ↑{입력} ↓{출력}',
|
|
405
|
+
'run.notMade': '— 没有被创建',
|
|
406
|
+
'store.notSaving': '这次对话没有写入文件({까닭})— 已丢失 {n} 行。现在关闭窗口就无法继续。',
|
|
407
|
+
|
|
408
|
+
'stage.plan': '先做判断',
|
|
409
|
+
'stage.work': '接着做',
|
|
410
|
+
'stage.fix': '卡住时',
|
|
411
|
+
|
|
412
|
+
'scr.tools': '工具',
|
|
413
|
+
'scr.conn': '连接',
|
|
414
|
+
'scr.session': '本次会话',
|
|
415
|
+
'status.kind': '规格',
|
|
416
|
+
'status.url': '地址',
|
|
417
|
+
'status.proxy': '代理',
|
|
418
|
+
'status.proxyOff': '用不了',
|
|
419
|
+
'status.model': '模型',
|
|
420
|
+
'status.root': '工作目录',
|
|
421
|
+
'status.shell': 'Shell',
|
|
422
|
+
'status.keyStore': '密钥存放',
|
|
423
|
+
'status.rules': '规则',
|
|
424
|
+
'status.noRules': '无(可用 /init 建一个)',
|
|
425
|
+
'status.rulesUnread': '{이름} 存在但读不了({까닭})— 本文件夹的规则没有生效',
|
|
426
|
+
'status.caps': '支持',
|
|
427
|
+
'status.capTools': '工具',
|
|
428
|
+
'status.capStream': '流式',
|
|
429
|
+
'status.capSchema': '结构化',
|
|
430
|
+
'status.capThink': '推理',
|
|
431
|
+
'cost.calls': '模型调用',
|
|
432
|
+
'cost.tokIn': '输入 token',
|
|
433
|
+
'cost.tokOut': '输出 token',
|
|
434
|
+
'cost.toolMs': '工具耗时',
|
|
435
|
+
'cost.elapsed': '已用时',
|
|
436
|
+
'cost.money': '费用',
|
|
437
|
+
'cost.quota': '服务器额度',
|
|
438
|
+
'cost.quotaAge': '基于{초}秒前的响应',
|
|
439
|
+
|
|
440
|
+
'scr.context': '上下文',
|
|
441
|
+
'unit.tokens': '{n} token',
|
|
442
|
+
'ctx.system': '系统提示',
|
|
443
|
+
'ctx.rules': '规则 ({이름})',
|
|
444
|
+
'ctx.rulesNone': '规则 (无)',
|
|
445
|
+
'ctx.memory': '记忆 ({n}行)',
|
|
446
|
+
'ctx.learned': '在这里学到的',
|
|
447
|
+
'ctx.skills': '技能列表 ({실림}/{전체})',
|
|
448
|
+
'ctx.tools': '工具定义',
|
|
449
|
+
'ctx.history': '对话历史',
|
|
450
|
+
'ctx.toolResults': '工具结果 (文件{n}个)',
|
|
451
|
+
'ctx.left': '剩余',
|
|
452
|
+
'ctx.compactHint': '折叠对话',
|
|
453
|
+
'ctx.clearHint': '全部清空',
|
|
454
|
+
'ctx.calibrated': '已按服务器实测值校正 {부호}{퍼센트}%(采样{번}次)。',
|
|
455
|
+
'ctx.estimate': '数字为估算 — 没有使用精确的分词器。',
|
|
456
|
+
|
|
457
|
+
'auth.waiting': '正在等待公司登录…',
|
|
458
|
+
'auth.got': '已取得密钥 — 大约可用{분}分钟',
|
|
459
|
+
'auth.failed': '没能取得密钥 — {왜}',
|
|
460
|
+
'auth.from': '取得',
|
|
461
|
+
'auth.fromPolicy': '取得(管理策略)',
|
|
462
|
+
'auth.leftMin': '剩余{분}分钟',
|
|
463
|
+
'auth.notYet': '尚未取得',
|
|
464
|
+
|
|
465
|
+
'undo.wayRestored': '已还原',
|
|
466
|
+
'undo.wayDeleted': '已删除(原本不存在的文件)',
|
|
467
|
+
|
|
268
468
|
'common.unknownCommand': '不认识的命令',
|
|
269
469
|
'common.seeHelp': '用 /help 看命令列表',
|
|
270
470
|
'common.yes': '是',
|
|
271
471
|
'common.no': '否',
|
|
472
|
+
'common.cfgSaveFailed': '设置没能写入文件({왜})— 本次有效,重开就会恢复原样。',
|
|
272
473
|
};
|
package/src/lsp/client.js
CHANGED
|
@@ -438,11 +438,55 @@ export async function 모두끄기() {
|
|
|
438
438
|
* 안 그러면 사용자 컴퓨터에 언어 서버가 하나씩 쌓인다. 이건 사용자가 나중에
|
|
439
439
|
* 작업 관리자를 열기 전에는 모르는 종류의 탈이라, 반드시 여기서 막아야 한다.
|
|
440
440
|
*/
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
441
|
+
export const 아이들데려가기 = () => {
|
|
442
|
+
for (const v of 풀.values()) { try { v.아이?.kill(); } catch { /* 이미 갔다 */ } }
|
|
443
|
+
풀.clear();
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
process.on('exit', 아이들데려가기);
|
|
447
|
+
|
|
448
|
+
/*
|
|
449
|
+
* ── 치우고 나면 **원래대로 죽어야 한다** ────────────────────────────────
|
|
450
|
+
*
|
|
451
|
+
* 여기는 세 신호에 같은 손을 달아 두고 치우기만 했다. 그런데 Node 는
|
|
452
|
+
* SIGINT·SIGTERM 에 손이 하나라도 달려 있으면 **기본 동작(끝내기)을 안 한다.**
|
|
453
|
+
* 그래서 이 파일이 한 번이라도 불려 들어온 프로그램은 —
|
|
454
|
+
*
|
|
455
|
+
* kill <pid> → 언어 서버는 죽고, deel 은 그대로 산다
|
|
456
|
+
*
|
|
457
|
+
* 유닉스에서 SIGTERM 을 **삼키는** 프로그램이 되어 있었다. 잡 관리자·컨테이너
|
|
458
|
+
* 종료·systemd 가 전부 이 신호로 정리하는데, 그것들이 하나같이 시간을 다
|
|
459
|
+
* 기다린 뒤 SIGKILL 로 때려잡게 된다. 그러면 정작 아이 정리는 못 하고 죽는다.
|
|
460
|
+
*
|
|
461
|
+
* 그래서 치운 다음 우리 손을 떼고 같은 신호를 자신에게 다시 보낸다. 그러면
|
|
462
|
+
* 기본 동작이 돌아 128+n 로 끝난다 — 셸이 기대하는 그 값이다.
|
|
463
|
+
*
|
|
464
|
+
* 다만 **남이 그 신호를 맡고 있으면 안 건드린다.** 한 방 실행은 SIGINT 로
|
|
465
|
+
* 턴만 끊고 살아 있어야 하고(oneshot.js), 대화 화면의 Ctrl+C 는 readline 이
|
|
466
|
+
* 따로 받는다. 우리 것 말고 다른 손이 달려 있으면 그쪽 뜻이 먼저다.
|
|
467
|
+
*/
|
|
468
|
+
/**
|
|
469
|
+
* 치운 다음 그 신호로 진짜 죽어야 하나.
|
|
470
|
+
*
|
|
471
|
+
* 갈림을 함수로 빼 둔 까닭: 윈도우에서는 이 길을 통째로 밟아 볼 수가 없다
|
|
472
|
+
* (SIGTERM 을 보내면 손이 돌기도 전에 Node 가 죽인다). 그런데 갈림 자체가
|
|
473
|
+
* 틀리면 유닉스에서 한 방 실행의 Ctrl+C 가 턴만 끊는 대신 프로그램을 죽인다 —
|
|
474
|
+
* 어느 판에서든 재어 볼 수 있어야 하는 자리다.
|
|
475
|
+
*
|
|
476
|
+
* @param 셈 이 신호에 달린 손의 수. 우리 것 하나만이면 우리가 마지막이다.
|
|
477
|
+
*/
|
|
478
|
+
export function 다시보낼까(신호, 셈 = process.listenerCount(신호)) {
|
|
479
|
+
return Number(셈) <= 1;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
for (const 신호 of ['SIGINT', 'SIGTERM']) {
|
|
483
|
+
const 손 = () => {
|
|
484
|
+
아이들데려가기();
|
|
485
|
+
if (!다시보낼까(신호)) return; // 남이 맡고 있다 — 그쪽 뜻이 먼저다
|
|
486
|
+
process.removeListener(신호, 손);
|
|
487
|
+
try { process.kill(process.pid, 신호); } catch { process.exit(신호 === 'SIGINT' ? 130 : 143); }
|
|
488
|
+
};
|
|
489
|
+
process.on(신호, 손);
|
|
446
490
|
}
|
|
447
491
|
|
|
448
492
|
/**
|