deel-local-cli 1.20.13 → 2.0.1
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 +97 -78
- package/README.md +120 -81
- package/bin/deel.js +398 -32
- package/package.json +2 -1
- package/src/acp/jsonrpc.js +87 -7
- package/src/acp/map.js +23 -3
- package/src/acp/serve.js +333 -25
- package/src/agent/agents.js +110 -12
- package/src/agent/askcheck.js +50 -6
- package/src/agent/asks.js +59 -5
- package/src/agent/budget.js +9 -2
- package/src/agent/card.js +8 -2
- package/src/agent/commit.js +168 -16
- package/src/agent/compact.js +93 -21
- package/src/agent/{/354/213/240/353/242/260/353/217/204.js → confidence.js} +1 -1
- package/src/agent/effort.js +85 -15
- package/src/agent/evidence.js +63 -7
- package/src/agent/evolve.js +132 -24
- package/src/agent/grade.js +12 -3
- package/src/agent/loop.js +408 -81
- package/src/agent/memory.js +176 -29
- package/src/agent/mention.js +22 -5
- package/src/agent/models.js +31 -7
- package/src/agent/modes.js +25 -10
- package/src/agent/outschema.js +381 -56
- package/src/agent/pins.js +33 -2
- package/src/agent/project.js +73 -8
- package/src/agent/recall.js +40 -9
- package/src/agent/{/354/204/261/355/225/234/352/270/260/353/241/235.js → recordshape.js} +19 -8
- package/src/agent/review.js +143 -13
- package/src/agent/route.js +253 -26
- package/src/agent/salvage.js +2 -1
- package/src/agent/session.js +457 -31
- package/src/agent/store.js +232 -22
- package/src/agent/threads.js +52 -3
- package/src/backend/adapter.js +508 -85
- package/src/backend/azure.js +12 -1
- package/src/backend/cachemark.js +31 -5
- package/src/backend/clientcert.js +23 -1
- package/src/backend/ctxsize.js +56 -9
- package/src/backend/detect.js +252 -19
- package/src/backend/http.js +383 -42
- package/src/backend/learn.js +101 -11
- package/src/backend/mcp.js +333 -24
- package/src/backend/price.js +61 -13
- package/src/backend/probe.js +188 -36
- package/src/backend/proxy.js +108 -14
- package/src/backend/quota.js +226 -30
- package/src/backend/retry.js +46 -3
- package/src/backend/scan.js +47 -10
- package/src/backend/scanui.js +57 -7
- package/src/backend/toolfit.js +204 -22
- package/src/backend/vision.js +14 -1
- package/src/backend/wire.js +58 -10
- package/src/cmdnames.js +16 -0
- package/src/commands/common.js +70 -0
- package/src/commands/extend.js +299 -0
- package/src/commands/model.js +851 -0
- package/src/commands/view.js +328 -0
- package/src/commands/work.js +823 -0
- package/src/commands.js +193 -2082
- package/src/completion.js +45 -3
- package/src/config.js +454 -41
- package/src/configexplain.js +127 -15
- package/src/doctor.js +164 -27
- package/src/i18n/en.js +22 -0
- package/src/i18n/index.js +31 -1
- package/src/i18n/ja.js +25 -1
- package/src/i18n/ko.js +25 -2
- package/src/i18n/zh.js +25 -1
- package/src/lsp/client.js +111 -9
- package/src/lsp/servers.js +86 -22
- package/src/oneshot.js +325 -65
- package/src/pack/sbom.js +28 -5
- package/src/pack/selfpack.js +54 -5
- package/src/pack/sheet.en.js +24 -3
- package/src/pack/tar.js +45 -8
- package/src/pack/zip.js +103 -8
- package/src/plugins/manage.js +165 -14
- package/src/preview/serve.js +239 -23
- package/src/providers/bedrock.js +30 -5
- package/src/providers/gemini.js +8 -0
- package/src/providers/index.js +17 -1
- package/src/repl.js +254 -61
- package/src/report.js +7 -2
- package/src/reset.js +151 -25
- package/src/safety/audit.js +108 -8
- package/src/safety/authcmd.js +75 -5
- package/src/safety/guard.js +531 -15
- package/src/safety/hooks.js +172 -20
- package/src/safety/keystore.js +178 -18
- package/src/safety/network.js +209 -6
- package/src/safety/policy.js +377 -36
- package/src/safety/runmode.js +9 -2
- package/src/safety/secrets.js +243 -17
- package/src/safety/shellenv.js +64 -3
- package/src/safety/trust.js +298 -32
- package/src/safety/undo.js +409 -17
- package/src/setup.js +175 -17
- package/src/skills/discover.js +122 -19
- package/src/stats.js +57 -16
- package/src/tools/{/355/231/225/354/235/270/353/262/225.js → checkmethods.js} +26 -1
- package/src/tools/clipboard.js +53 -20
- package/src/tools/convert.js +118 -37
- package/src/tools/desc.en.js +48 -5
- package/src/tools/doc2md.js +36 -4
- package/src/tools/docs.js +94 -18
- package/src/tools/edit-match.js +212 -13
- package/src/tools/encoding.js +525 -28
- package/src/tools/excel-com.js +63 -15
- package/src/tools/excel.js +12 -7
- package/src/tools/fastgrep.js +501 -38
- package/src/tools/fig.js +21 -4
- package/src/tools/fsutil.js +235 -21
- package/src/tools/hwpxwrite.js +80 -19
- package/src/tools/ignore.js +55 -7
- package/src/tools/index.js +1083 -148
- package/src/tools/jobs.js +328 -50
- package/src/tools/kiwi.js +50 -5
- package/src/tools/lsp.js +164 -32
- package/src/tools/outline.js +52 -12
- package/src/tools/pdf.js +367 -59
- package/src/tools/shell.js +12 -4
- package/src/tools/spawn.js +203 -18
- package/src/tools/todo.js +73 -4
- package/src/tools/verify.js +328 -45
- package/src/tools/webfetch.js +194 -25
- package/src/tools/xlsx.js +232 -31
- package/src/ui/ansi.js +209 -15
- package/src/ui/banner.js +2 -1
- package/src/ui/complete.js +46 -4
- package/src/ui/diff.js +4 -2
- package/src/ui/export.js +91 -17
- package/src/ui/inputbox.js +58 -18
- package/src/ui/intro.js +9 -2
- package/src/ui/level.js +32 -7
- package/src/ui/md.js +101 -15
- package/src/ui/motion.js +5 -2
- package/src/ui/notify.js +7 -1
- package/src/ui/office.js +37 -5
- package/src/ui/pastechip.js +3 -2
- package/src/ui/pick.js +85 -12
- package/src/ui/prompt.js +176 -13
- package/src/ui/screen.js +2 -1
- package/src/ui/spinner.js +6 -1
- package/src/ui/status.js +80 -12
- package/src/ui/wrap.js +23 -37
- /package/src/agent/{/353/213/250/352/263/204.js" → phase.js} +0 -0
- /package/src/skills/builtin/{/352/271/212/354/235/264/354/236/210/352/262/214-/353/247/214/353/223/244/352/270/260/SKILL.md" → build-deep/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/260/250/352/267/274/354/260/250/352/267/274-/353/224/224/353/262/204/352/271/205/SKILL.md" → debug-step-by-step/SKILL.md} +0 -0
- /package/src/skills/builtin/{/353/201/235/352/271/214/354/247/200-/355/225/230/352/270/260/SKILL.md" → finish-all/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/212/244/354/212/244/353/241/234-/352/262/200/355/206/240/SKILL.md" → self-review/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/275/224/353/223/234-/354/244/204/354/235/264/352/270/260/SKILL.md" → simplify-code/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/260/224/353/237/254/353/263/264/352/270/260/SKILL.md" → spike/SKILL.md} +0 -0
- /package/src/skills/builtin/{/352/262/200/354/202/254-/353/250/274/354/240/200/SKILL.md" → test-first/SKILL.md} +0 -0
package/src/i18n/ja.js
CHANGED
|
@@ -33,6 +33,7 @@ export const ja = {
|
|
|
33
33
|
'think.harder': 'もっと強く',
|
|
34
34
|
'think.faster': 'もっと速く',
|
|
35
35
|
'think.eg': '例',
|
|
36
|
+
'think.unknown': 'そのような強さはありません: {값}',
|
|
36
37
|
'think.col.stage': '段階',
|
|
37
38
|
'think.col.level': '強さ',
|
|
38
39
|
'think.col.cap': '出力上限',
|
|
@@ -190,6 +191,8 @@ export const ja = {
|
|
|
190
191
|
'undo.failed': '{n}個は戻せませんでした — そのファイルは直したままです。控えは残してあるので /undo をもう一度試してください。',
|
|
191
192
|
'undo.talkKept': '会話はそのままです — そのターンはすでに畳まれていました。',
|
|
192
193
|
'undo.talkKeptWhy': '戻した作業がまだあるとモデルが思っている可能性があるので、次の一言で触れてください。',
|
|
194
|
+
'undo.talkPartial': 'そのうち {n} ターンはすでに要約に畳まれていたため、その会話はそのまま残っています。',
|
|
195
|
+
'undo.notesDropped': 'ただし、そのターンで貼っておいた「今回の依頼」メモ {n} 件は外しました — 戻した作業をもう一度頼まないように。',
|
|
193
196
|
'bell.stateOn': 'オン',
|
|
194
197
|
'bell.stateOff': 'オフ',
|
|
195
198
|
'bell.now': 'ベルは {상태} です。',
|
|
@@ -268,6 +271,7 @@ export const ja = {
|
|
|
268
271
|
'export.nothingRan': '走らせたコマンドはありません。',
|
|
269
272
|
'export.unproven': '裏づけなし',
|
|
270
273
|
'export.allProven': 'ありません — すべての変更に、後から確かめた記録があります。',
|
|
274
|
+
'export.evidenceFailed': '証拠を集められませんでした({왜}) — なので「すべて確認済み」とは書けません。',
|
|
271
275
|
'export.footer': 'deel が作った報告です。外へのリンクはありません — どのネットワークでも開きます。',
|
|
272
276
|
'export.saved': '報告を保存しました:',
|
|
273
277
|
'export.openHint': 'ダブルクリックで開きます。そのままレビューや報告に添えられます。',
|
|
@@ -307,6 +311,7 @@ export const ja = {
|
|
|
307
311
|
'trust.removed': '信頼リストから外しました',
|
|
308
312
|
'trust.notListed': '信頼リストに無いフォルダでした',
|
|
309
313
|
'trust.parentStill': '親フォルダのため、まだ信頼されています',
|
|
314
|
+
'trust.stillTrustedEnv': "このマシンの環境設定のため、まだ信頼されています",
|
|
310
315
|
'stats.title': 'このフォルダでの作業',
|
|
311
316
|
'stats.none': 'まだ記録がありません — このフォルダで deel を使うと残りはじめます。',
|
|
312
317
|
'stats.period': '期間',
|
|
@@ -316,6 +321,7 @@ export const ja = {
|
|
|
316
321
|
'stats.sessions': 'セッション {n} 件',
|
|
317
322
|
'stats.tools': 'ツール',
|
|
318
323
|
'stats.failed': '失敗 {n}',
|
|
324
|
+
'stats.unknown': '不明 {n}',
|
|
319
325
|
'stats.blocked': 'ブロック',
|
|
320
326
|
'stats.undos': '取り消し',
|
|
321
327
|
'stats.masked': '秘密の伏字',
|
|
@@ -324,11 +330,22 @@ export const ja = {
|
|
|
324
330
|
'stats.noCost': 'トークンと料金はこの記録にありません。今の会話ぶんは /cost で見られます。',
|
|
325
331
|
'trust.listTitle': '信頼しているフォルダ',
|
|
326
332
|
'trust.listNone': 'まだどのフォルダも信頼していません。',
|
|
333
|
+
'trust.listIgnoredWide': '範囲が広すぎるため読み込み時に無視されます(そのフォルダで deel trust --off を実行して外してください)',
|
|
327
334
|
'trust.blockedTitle': '信頼しても プロジェクト設定が決められない項目',
|
|
328
335
|
'trust.saveFail': '信頼リストを書けませんでした',
|
|
336
|
+
'trust.refuseWide': 'このフォルダは信頼リストに入れません',
|
|
337
|
+
'trust.refuseWideWhy': 'deel trust はこのフォルダとその下すべてを信頼します。ホームやドライブのルートを信頼すると、下に置いた他人のリポジトリの設定まで読みます',
|
|
338
|
+
'trust.refuseWideHow': '信頼したいリポジトリのフォルダに移動して deel trust を実行してください',
|
|
329
339
|
'trust.why.allow': '承認ルールを広げる項目です。狭める deny はそのまま読みます',
|
|
330
340
|
'trust.why.apiKey': 'リポジトリに書かれた鍵は設定ではなく漏洩です',
|
|
331
341
|
'trust.why.authCmd': '最初のリクエスト前にコマンドが走ります — ツール承認より前なので何も止められません',
|
|
342
|
+
'trust.why.connection': 'このPCのプロファイルの宛先・方式・認証を変えると、このPCの鍵がリポジトリの選んだ先へ送られます',
|
|
343
|
+
'trust.why.online': '「外に出てよい」の許可は人が選んだ場所でしか付きません',
|
|
344
|
+
'trust.why.active': 'リポジトリが加えたプロファイルに接続を切り替える項目です。このPCのプロファイルを選ぶのはそのまま読みます',
|
|
345
|
+
'trust.why.offline': '封印を解く側です。有効にする(true)のはそのまま読みます',
|
|
346
|
+
'trust.why.proxy': 'すべてのリクエストが鍵のヘッダーごとそのプロキシを通ります',
|
|
347
|
+
'trust.why.shellEnv': 'シェルコマンドに秘密の環境変数を渡し戻す項目です',
|
|
348
|
+
'trust.why.profilesNotArray': 'profiles がリスト([ … ])ではないため、まるごと読みませんでした',
|
|
332
349
|
'level.beginner.name': 'かんたん',
|
|
333
350
|
'level.beginner.hint': '勧められた設定のまま始めます',
|
|
334
351
|
'level.developer.name': '開発者',
|
|
@@ -404,6 +421,7 @@ export const ja = {
|
|
|
404
421
|
'tier.trail': '行末の空白・改行の違いを無視',
|
|
405
422
|
'tier.indent': 'インデントの違いを無視',
|
|
406
423
|
'tier.space': 'すべての空白の違いを無視',
|
|
424
|
+
'tier.nfc': 'Unicode 正規化(NFC・NFD)の違いを無視',
|
|
407
425
|
|
|
408
426
|
'sum.image': '画像',
|
|
409
427
|
'sum.imageDropped': '画像 {크기} を省略',
|
|
@@ -413,7 +431,7 @@ export const ja = {
|
|
|
413
431
|
'lsp.noDef': '定義が見つかりません。Grep で探してください。',
|
|
414
432
|
'lsp.defs': '定義 {n}',
|
|
415
433
|
'lsp.noRefs': '使っている場所がありません。',
|
|
416
|
-
'lsp.refs': '使用箇所 {자리} ·
|
|
434
|
+
'lsp.refs': '使用箇所 {자리} · {파일}',
|
|
417
435
|
|
|
418
436
|
'sum.nothingToCheck': '確かめるものがありませんでした',
|
|
419
437
|
'job.no': '{n}番',
|
|
@@ -430,6 +448,9 @@ export const ja = {
|
|
|
430
448
|
'run.hintHelp': '/help コマンド一覧',
|
|
431
449
|
'run.hintEsc': 'ESC 中断',
|
|
432
450
|
'run.hintQuit': 'Ctrl+C 中断・終了',
|
|
451
|
+
'box.more': 'ほか {n} 件',
|
|
452
|
+
'box.queuedHint': 'Enter で今の作業が終わった後に送ります',
|
|
453
|
+
'box.queued': '{n} 件予約済み',
|
|
433
454
|
'run.stopping': '止めています…',
|
|
434
455
|
'run.errKept': 'ここまで届いた分は会話に残しました — このまま続けて話せます。',
|
|
435
456
|
'ev.capped': '答えが {한계} トークンで切れました — /out で上限を上げるか、ファイルは分けて書かせてください',
|
|
@@ -455,6 +476,7 @@ export const ja = {
|
|
|
455
476
|
'paste.pasted': '{n}行を貼り付けました — Enter で一度に送ります',
|
|
456
477
|
'run.thinking': '考え中…',
|
|
457
478
|
'run.thinkingChars': '考え中… {n}文字',
|
|
479
|
+
'run.thoughtChars': '考えた {n}文字',
|
|
458
480
|
'run.bye': '終了します。',
|
|
459
481
|
'run.byeStats': 'モデル呼び出し{n}回 · ツール時間{초}秒 · ↑{입력} ↓{출력}',
|
|
460
482
|
'run.notMade': '— 作られませんでした',
|
|
@@ -501,6 +523,7 @@ export const ja = {
|
|
|
501
523
|
'wire.cacheMarked': '印付け',
|
|
502
524
|
'wire.cacheKey': 'キー',
|
|
503
525
|
'wire.cacheAuto': 'サーバー任せ',
|
|
526
|
+
'wire.learned': '学習',
|
|
504
527
|
'cost.cache': 'キャッシュ',
|
|
505
528
|
'cost.think': '思考',
|
|
506
529
|
'cost.thinkLine': '{토큰} トークン · 応答の {몫}%',
|
|
@@ -518,6 +541,7 @@ export const ja = {
|
|
|
518
541
|
'loop.limitAhead': '直前に制限に当たったので {초} 秒あけてから送ります',
|
|
519
542
|
'net.limit.guardrail': 'ゲートウェイに付いたガードレールの上限です — モデルのトークン上限ではありません。数えているのは毎秒走査する文字量(text unit・1 TU はおよそ 1,000 文字)なので、会話が伸びるほど一回の要求が大きくなり、ある時点から毎回引っかかります。プレフィックスキャッシュはここでは効きません — ガードレールはキャッシュと無関係に毎回全文を走査します。モデルの毎分の要求・トークン上限(RPM・TPM)を上げても動きません。別の API の別の割当です。/compact で会話を縮めると当座はしのげます。根本はゲートウェイ担当者がガードレールの割当を上げるか、入力には掛けないようにすることです。',
|
|
520
543
|
'net.limit.perMinute': '毎分の上限です — 窓が変われば自然に解けます。ここでは待つのが正解です。',
|
|
544
|
+
'net.limit.oneTooBig': 'この一回の要求が毎分の上限より大きいです — 窓が変わってもこの一回は上限を超えたままなので、待っても解けません。指示や添付ファイルを減らす(/compact)、応答の長さ上限を下げる、または上限を上げてください。',
|
|
521
545
|
'cost.quota': 'サーバー残量',
|
|
522
546
|
'cost.quotaAge': '{초}秒前の応答基準',
|
|
523
547
|
|
package/src/i18n/ko.js
CHANGED
|
@@ -203,6 +203,8 @@ export const ko = {
|
|
|
203
203
|
'undo.failed': '{n}개는 못 되돌렸습니다 — 그 파일은 고쳐진 그대로입니다. 기록을 남겨 뒀으니 /undo 를 다시 해 보세요.',
|
|
204
204
|
'undo.talkKept': '대화는 그대로 둡니다 — 그 턴의 말이 이미 접혀 없어졌습니다.',
|
|
205
205
|
'undo.talkKeptWhy': '모델이 되돌린 것을 아직 있는 줄 알 수 있으니, 이어 시킬 때 한 번 짚어 주세요.',
|
|
206
|
+
'undo.talkPartial': '그중 {n}개 턴은 말이 이미 요약에 접혀 있어 대화에 그대로 남았습니다.',
|
|
207
|
+
'undo.notesDropped': '다만 그 턴에서 박아 둔 「이번에 시킨 말」 쪽지 {n}개는 뺐습니다 — 되돌린 일을 다시 시키지 않게.',
|
|
206
208
|
|
|
207
209
|
// ── 종소리 ────────────────────────────────────────────────────────────
|
|
208
210
|
'bell.stateOn': '켜져',
|
|
@@ -288,6 +290,7 @@ export const ko = {
|
|
|
288
290
|
'export.nothingRan': '돌린 명령이 없습니다.',
|
|
289
291
|
'export.unproven': '증명 안 된 것',
|
|
290
292
|
'export.allProven': '없습니다 — 바꾼 것마다 뒤에 돌린 확인이 있습니다.',
|
|
293
|
+
'export.evidenceFailed': '증거를 못 모았습니다 ({왜}) — 「모두 증명됨」 이라고 말할 수 없어 이렇게 적습니다.',
|
|
291
294
|
'export.footer': 'deel 이 만든 보고서입니다. 바깥으로 나가는 주소가 없어 어느 망에서든 열립니다.',
|
|
292
295
|
'export.saved': '보고서를 남겼습니다:',
|
|
293
296
|
'export.openHint': '더블클릭으로 열립니다. 그대로 결재·보고에 첨부하면 됩니다.',
|
|
@@ -330,6 +333,7 @@ export const ko = {
|
|
|
330
333
|
'trust.removed': '믿는 목록에서 뺐습니다',
|
|
331
334
|
'trust.notListed': '믿는 목록에 없던 폴더입니다',
|
|
332
335
|
'trust.parentStill': '그래도 위 폴더 때문에 아직 믿습니다',
|
|
336
|
+
'trust.stillTrustedEnv': "그래도 이 컴퓨터 환경 설정 때문에 아직 믿습니다",
|
|
333
337
|
'stats.title': '이 폴더에서 한 일',
|
|
334
338
|
'stats.none': '아직 기록이 없습니다 — 이 폴더에서 deel 로 무언가 하면 그때부터 남습니다.',
|
|
335
339
|
'stats.period': '기간',
|
|
@@ -339,6 +343,7 @@ export const ko = {
|
|
|
339
343
|
'stats.sessions': '세션 {n}개',
|
|
340
344
|
'stats.tools': '도구',
|
|
341
345
|
'stats.failed': '실패 {n}',
|
|
346
|
+
'stats.unknown': '됐는지 모름 {n}',
|
|
342
347
|
'stats.blocked': '막힘',
|
|
343
348
|
'stats.undos': '되돌리기',
|
|
344
349
|
'stats.masked': '비밀 가림',
|
|
@@ -347,11 +352,22 @@ export const ko = {
|
|
|
347
352
|
'stats.noCost': '토큰과 요금은 이 기록에 없습니다. 이번 대화에서 쓴 것은 /cost 로 보세요.',
|
|
348
353
|
'trust.listTitle': '믿는 폴더',
|
|
349
354
|
'trust.listNone': '아직 아무 폴더도 안 믿습니다.',
|
|
355
|
+
'trust.listIgnoredWide': '넓은 자리라 읽을 때 무시됩니다 (그 폴더에서 deel trust --off 로 빼세요)',
|
|
350
356
|
'trust.blockedTitle': '믿어도 프로젝트가 못 정하는 칸',
|
|
351
357
|
'trust.saveFail': '믿는 목록을 못 적었습니다',
|
|
358
|
+
'trust.refuseWide': '여기는 믿는 폴더로 적지 않습니다',
|
|
359
|
+
'trust.refuseWideWhy': 'deel trust 는 이 폴더와 그 아래 전부를 믿습니다. 집 폴더·드라이브 뿌리를 믿으면 아래에 받아 둔 남의 저장소 설정까지 다 읽습니다',
|
|
360
|
+
'trust.refuseWideHow': '믿을 저장소 폴더로 들어가서 deel trust 를 치세요',
|
|
352
361
|
'trust.why.allow': '승인 규칙을 넓히는 칸입니다. 좁히는 deny 는 그대로 읽습니다',
|
|
353
362
|
'trust.why.apiKey': '열쇠가 저장소에 적혀 있으면 설정이 아니라 유출입니다',
|
|
354
363
|
'trust.why.authCmd': '첫 요청 전에 명령이 돕니다 — 도구 승인보다 앞이라 안 걸립니다',
|
|
364
|
+
'trust.why.connection': '이 PC 프로필의 주소·규격·인증을 바꾸면 이 PC 열쇠가 저장소가 고른 곳으로 갑니다',
|
|
365
|
+
'trust.why.online': '「바깥으로 나가도 된다」 허가는 사람이 고른 자리에서만 붙습니다',
|
|
366
|
+
'trust.why.active': '저장소가 더한 프로필로 연결을 돌리는 칸입니다. 이 PC 프로필을 고르는 것은 읽습니다',
|
|
367
|
+
'trust.why.offline': '봉인을 푸는 쪽입니다. 켜는 것(true)은 그대로 읽습니다',
|
|
368
|
+
'trust.why.proxy': '모든 요청이 열쇠 머리말째 그 프록시를 지납니다',
|
|
369
|
+
'trust.why.shellEnv': '명령 창에 비밀 환경변수를 도로 물려주는 칸입니다',
|
|
370
|
+
'trust.why.profilesNotArray': 'profiles 가 목록([ … ])이 아니라 통째로 안 읽었습니다',
|
|
355
371
|
'level.beginner.name': '쉬움',
|
|
356
372
|
'level.beginner.hint': '권장값으로 바로 시작',
|
|
357
373
|
'level.developer.name': '개발자',
|
|
@@ -365,7 +381,7 @@ export const ko = {
|
|
|
365
381
|
* 사람이 계획을 승인하는 자리에서만 한글을 봤다. 하필 「무엇을 치면
|
|
366
382
|
* 되나」 를 말하는 줄이라, 못 읽으면 그 자리에서 막힌다.
|
|
367
383
|
*
|
|
368
|
-
*
|
|
384
|
+
* 넷을 한 줄에 보여 준다(⏎ · y · 번호 · n). ⏎ 하나로 진행되는 것을 맨 앞에 둔다 —
|
|
369
385
|
* 그게 열에 아홉이고, 여기서 뭘 더 치게 하면 사람은 애초에 계획
|
|
370
386
|
* 같은 걸 안 보려 든다.
|
|
371
387
|
*/
|
|
@@ -465,6 +481,7 @@ export const ko = {
|
|
|
465
481
|
'tier.trail': '줄 끝 공백·줄바꿈 차이 무시',
|
|
466
482
|
'tier.indent': '들여쓰기 차이 무시',
|
|
467
483
|
'tier.space': '모든 공백 차이 무시',
|
|
484
|
+
'tier.nfc': '유니코드 정규화(NFC·NFD) 차이 무시',
|
|
468
485
|
|
|
469
486
|
'sum.image': '그림',
|
|
470
487
|
'sum.imageDropped': '그림 {크기} 생략',
|
|
@@ -474,7 +491,7 @@ export const ko = {
|
|
|
474
491
|
'lsp.noDef': '정의를 못 찾았습니다. Grep 으로 찾아보세요.',
|
|
475
492
|
'lsp.defs': '정의 {n}',
|
|
476
493
|
'lsp.noRefs': '쓰는 자리가 없습니다.',
|
|
477
|
-
'lsp.refs': '쓰는 자리 {자리} ·
|
|
494
|
+
'lsp.refs': '쓰는 자리 {자리} · {파일}',
|
|
478
495
|
|
|
479
496
|
'sum.nothingToCheck': '확인할 것이 없었습니다',
|
|
480
497
|
'job.no': '{n}번',
|
|
@@ -493,6 +510,10 @@ export const ko = {
|
|
|
493
510
|
'run.hintHelp': '/help 명령 목록',
|
|
494
511
|
'run.hintEsc': 'ESC 중단',
|
|
495
512
|
'run.hintQuit': 'Ctrl+C 중단·끝내기',
|
|
513
|
+
// 입력 상자(ui/inputbox.js)에 글자 그대로 박혀 있던 말. 영어로 켠 사람도 이 줄은 한글로 봤다.
|
|
514
|
+
'box.more': '그 밖에 {n}개 더',
|
|
515
|
+
'box.queuedHint': 'Enter 를 치면 지금 일이 끝난 뒤에 보냅니다',
|
|
516
|
+
'box.queued': '{n}건 예약됨',
|
|
496
517
|
// 줄 화면에는 상자가 없다. 눌린 것이 닿았다는 신호를 여기 한 줄로 낸다.
|
|
497
518
|
'run.stopping': '멈추는 중…',
|
|
498
519
|
// 탈이 나기 전까지 흘러온 반쪽을 대화에 남겼다. 안 말하면 다시 처음부터 친다.
|
|
@@ -544,6 +565,7 @@ export const ko = {
|
|
|
544
565
|
'paste.pasted': '{n}줄을 붙여넣었습니다 — Enter 로 한 번에 보냅니다',
|
|
545
566
|
'run.thinking': '생각 중…',
|
|
546
567
|
'run.thinkingChars': '생각 중… {n}자',
|
|
568
|
+
'run.thoughtChars': '생각 {n}자',
|
|
547
569
|
'run.bye': '끝냅니다.',
|
|
548
570
|
'run.byeStats': '모델 호출 {n}회 · 도구 시간 {초}초 · ↑{입력} ↓{출력}',
|
|
549
571
|
'run.notMade': '— 만들어지지 않았습니다',
|
|
@@ -610,6 +632,7 @@ export const ko = {
|
|
|
610
632
|
'loop.limitAhead': '방금 한도에 걸려서 {초}초 띄웠다 보냅니다',
|
|
611
633
|
'net.limit.guardrail': '게이트웨이에 붙은 가드레일 한도입니다 — 모델 토큰 한도가 아닙니다. 재는 것이 초당 훑는 글의 양(text unit, 1 TU 가 1,000자쯤)이라, 대화가 자랄수록 매 요청이 커져 어느 순간부터 계속 걸립니다. 프리픽스 캐시는 여기서 안 듣습니다 — 가드레일은 캐시와 무관하게 매번 전체 글을 훑습니다. 모델의 분당 요청·토큰 한도(RPM·TPM)를 올려도 이건 안 움직입니다: 다른 API 의 다른 할당량입니다. /compact 로 대화를 줄이면 당장 낫고, 근본은 게이트웨이 담당자가 가드레일 할당량을 올리거나 입력에는 안 걸도록 바꾸는 것입니다.',
|
|
612
634
|
'net.limit.perMinute': '분당 한도입니다 — 창이 새로 열리면 저절로 풀립니다. 여기서는 기다리는 것이 맞습니다.',
|
|
635
|
+
'net.limit.oneTooBig': '요청 한 번이 분당 한도보다 큽니다 — 창이 새로 열려도 이 한 번은 여전히 넘어서 기다려도 안 풀립니다. 시킬 말이나 붙인 파일을 줄이거나(/compact), 답 길이 상한을 낮추거나, 한도를 올려야 풀립니다.',
|
|
613
636
|
'cost.quota': '서버 할당량',
|
|
614
637
|
'cost.quotaAge': '{초}초 전 응답 기준',
|
|
615
638
|
|
package/src/i18n/zh.js
CHANGED
|
@@ -33,6 +33,7 @@ export const zh = {
|
|
|
33
33
|
'think.harder': '更用力',
|
|
34
34
|
'think.faster': '更快',
|
|
35
35
|
'think.eg': '例',
|
|
36
|
+
'think.unknown': '没有这样的强度:{값}',
|
|
36
37
|
'think.col.stage': '阶段',
|
|
37
38
|
'think.col.level': '力度',
|
|
38
39
|
'think.col.cap': '输出上限',
|
|
@@ -190,6 +191,8 @@ export const zh = {
|
|
|
190
191
|
'undo.failed': '有 {n} 个没能还原 — 那些文件还是改过的样子。备份留着,请再试一次 /undo。',
|
|
191
192
|
'undo.talkKept': '对话保持原样 — 那一轮早就被折叠了。',
|
|
192
193
|
'undo.talkKeptWhy': '模型可能仍以为撤销掉的工作还在,下一句里提一下。',
|
|
194
|
+
'undo.talkPartial': '其中 {n} 个回合已经被折叠进摘要,那部分对话仍然留着。',
|
|
195
|
+
'undo.notesDropped': '不过那个回合里钉住的「本次要求」便签 {n} 条已经去掉——免得再次要求已撤销的工作。',
|
|
193
196
|
'bell.stateOn': '开',
|
|
194
197
|
'bell.stateOff': '关',
|
|
195
198
|
'bell.now': '提示音现在是{상태}。',
|
|
@@ -268,6 +271,7 @@ export const zh = {
|
|
|
268
271
|
'export.nothingRan': '没有跑过任何命令。',
|
|
269
272
|
'export.unproven': '未经证实',
|
|
270
273
|
'export.allProven': '没有 — 每一处改动后面都有一次检查。',
|
|
274
|
+
'export.evidenceFailed': '没能收集到证据({왜}) — 所以这份报告不能说「全部已验证」。',
|
|
271
275
|
'export.footer': '由 deel 生成的报告。没有外链 — 在任何网络下都能打开。',
|
|
272
276
|
'export.saved': '报告已保存:',
|
|
273
277
|
'export.openHint': '双击打开。可以直接附在评审或汇报里。',
|
|
@@ -307,6 +311,7 @@ export const zh = {
|
|
|
307
311
|
'trust.removed': '已从信任列表中移除',
|
|
308
312
|
'trust.notListed': '该文件夹不在信任列表中',
|
|
309
313
|
'trust.parentStill': '仍因上级文件夹而被信任',
|
|
314
|
+
'trust.stillTrustedEnv': "仍被信任 — 因为本机的环境设置",
|
|
310
315
|
'stats.title': '这个文件夹里做过什么',
|
|
311
316
|
'stats.none': '还没有记录 — 在这个文件夹用过 deel 之后才会开始记。',
|
|
312
317
|
'stats.period': '时间段',
|
|
@@ -316,6 +321,7 @@ export const zh = {
|
|
|
316
321
|
'stats.sessions': '{n} 个会话',
|
|
317
322
|
'stats.tools': '工具',
|
|
318
323
|
'stats.failed': '失败 {n}',
|
|
324
|
+
'stats.unknown': '未知 {n}',
|
|
319
325
|
'stats.blocked': '被拦下',
|
|
320
326
|
'stats.undos': '撤销',
|
|
321
327
|
'stats.masked': '已遮蔽的密钥',
|
|
@@ -324,11 +330,22 @@ export const zh = {
|
|
|
324
330
|
'stats.noCost': '这份记录里没有 token 和费用。本次对话的用量请看 /cost。',
|
|
325
331
|
'trust.listTitle': '已信任的文件夹',
|
|
326
332
|
'trust.listNone': '尚未信任任何文件夹。',
|
|
333
|
+
'trust.listIgnoredWide': '范围过大,读取时会被忽略(在该文件夹中运行 deel trust --off 以移除)',
|
|
327
334
|
'trust.blockedTitle': '即使信任,项目配置也不得设置的字段',
|
|
328
335
|
'trust.saveFail': '无法写入信任列表',
|
|
336
|
+
'trust.refuseWide': '不把这里记为信任文件夹',
|
|
337
|
+
'trust.refuseWideWhy': 'deel trust 会信任此文件夹及其下的全部内容。信任主目录或驱动器根目录,会连其下他人仓库的配置一起读取',
|
|
338
|
+
'trust.refuseWideHow': '请进入要信任的仓库文件夹再运行 deel trust',
|
|
329
339
|
'trust.why.allow': '该字段会放宽审批规则。收紧的 deny 仍会读取',
|
|
330
340
|
'trust.why.apiKey': '写在仓库里的密钥不是配置,而是泄露',
|
|
331
341
|
'trust.why.authCmd': '它在首次请求前执行 — 早于工具审批,因此无法被拦截',
|
|
342
|
+
'trust.why.connection': '改动本机配置的地址、协议或认证,会把本机密钥发往仓库指定的地方',
|
|
343
|
+
'trust.why.online': '「允许连到外部」的许可只能由人亲自授予',
|
|
344
|
+
'trust.why.active': '它会把连接切换到仓库添加的配置。选择本机的配置仍会读取',
|
|
345
|
+
'trust.why.offline': '这是解除封锁的一侧。开启(true)仍会读取',
|
|
346
|
+
'trust.why.proxy': '所有请求连同密钥头都会经过该代理',
|
|
347
|
+
'trust.why.shellEnv': '它会把秘密环境变量重新交给 shell 命令',
|
|
348
|
+
'trust.why.profilesNotArray': 'profiles 不是列表([ … ]),因此整段都未读取',
|
|
332
349
|
'level.beginner.name': '新手',
|
|
333
350
|
'level.beginner.hint': '按推荐设置开始',
|
|
334
351
|
'level.developer.name': '开发者',
|
|
@@ -404,6 +421,7 @@ export const zh = {
|
|
|
404
421
|
'tier.trail': '忽略行尾空白和换行差异',
|
|
405
422
|
'tier.indent': '忽略缩进差异',
|
|
406
423
|
'tier.space': '忽略所有空白差异',
|
|
424
|
+
'tier.nfc': '忽略 Unicode 规范化(NFC/NFD)差异',
|
|
407
425
|
|
|
408
426
|
'sum.image': '图片',
|
|
409
427
|
'sum.imageDropped': '省略图片 {크기}',
|
|
@@ -413,7 +431,7 @@ export const zh = {
|
|
|
413
431
|
'lsp.noDef': '找不到定义。用 Grep 试试。',
|
|
414
432
|
'lsp.defs': '定义 {n}',
|
|
415
433
|
'lsp.noRefs': '没有任何地方用到。',
|
|
416
|
-
'lsp.refs': '使用 {자리} ·
|
|
434
|
+
'lsp.refs': '使用 {자리} · {파일}',
|
|
417
435
|
|
|
418
436
|
'sum.nothingToCheck': '没有可核对的东西',
|
|
419
437
|
'job.no': '{n}号',
|
|
@@ -430,6 +448,9 @@ export const zh = {
|
|
|
430
448
|
'run.hintHelp': '/help 命令列表',
|
|
431
449
|
'run.hintEsc': 'ESC 中断',
|
|
432
450
|
'run.hintQuit': 'Ctrl+C 中断·退出',
|
|
451
|
+
'box.more': '还有 {n} 个',
|
|
452
|
+
'box.queuedHint': '按 Enter,当前任务结束后发送',
|
|
453
|
+
'box.queued': '已排队 {n} 条',
|
|
433
454
|
'run.stopping': '正在停止…',
|
|
434
455
|
'run.errKept': '出错前收到的内容已经留在对话里 — 可以直接接着说。',
|
|
435
456
|
'ev.capped': '回答在 {한계} 个 token 处被截断 — 用 /out 调高上限,或者让文件分次写',
|
|
@@ -455,6 +476,7 @@ export const zh = {
|
|
|
455
476
|
'paste.pasted': '已粘贴 {n} 行 — 按 Enter 一次发送',
|
|
456
477
|
'run.thinking': '思考中…',
|
|
457
478
|
'run.thinkingChars': '思考中… {n}字',
|
|
479
|
+
'run.thoughtChars': '思考了 {n}字',
|
|
458
480
|
'run.bye': '结束。',
|
|
459
481
|
'run.byeStats': '模型调用{n}次 · 工具耗时{초}秒 · ↑{입력} ↓{출력}',
|
|
460
482
|
'run.notMade': '— 没有被创建',
|
|
@@ -501,6 +523,7 @@ export const zh = {
|
|
|
501
523
|
'wire.cacheMarked': '标记',
|
|
502
524
|
'wire.cacheKey': '键',
|
|
503
525
|
'wire.cacheAuto': '服务端',
|
|
526
|
+
'wire.learned': '学到的',
|
|
504
527
|
'cost.cache': '缓存',
|
|
505
528
|
'cost.think': '思考',
|
|
506
529
|
'cost.thinkLine': '{토큰} token · 占输出 {몫}%',
|
|
@@ -518,6 +541,7 @@ export const zh = {
|
|
|
518
541
|
'loop.limitAhead': '刚被限流,间隔 {초} 秒后再发送',
|
|
519
542
|
'net.limit.guardrail': '这是网关上护栏的限额,不是模型的令牌限额。它按每秒扫描的文本量计数(text unit,1 TU 约 1,000 个字符),所以对话越长每次请求越大,过了某个点就会每次都被拦。前缀缓存在这里没有用 — 护栏与缓存无关,每次都会扫描全文。提高模型的每分钟请求/令牌限额(RPM/TPM)不会有任何作用:那是另一个 API 的另一份配额。用 /compact 缩短对话可以先缓解;根本办法是让网关维护者提高护栏配额,或不要对输入应用它。',
|
|
520
543
|
'net.limit.perMinute': '这是每分钟限额 — 窗口滚动后会自行解除,这里等待就是正确做法。',
|
|
544
|
+
'net.limit.oneTooBig': '这一次请求本身就超过了每分钟限额 — 窗口滚动后这一次仍然超出,等待不会解除。请缩短提示或附带的文件(/compact)、调低回答长度上限,或提高限额。',
|
|
521
545
|
'cost.quota': '服务器额度',
|
|
522
546
|
'cost.quotaAge': '基于{초}秒前的响应',
|
|
523
547
|
|
package/src/lsp/client.js
CHANGED
|
@@ -76,6 +76,7 @@ export class 언어서버 {
|
|
|
76
76
|
this.받개 = null;
|
|
77
77
|
this.기다림 = new Map(); // id → {풀기, 시계}
|
|
78
78
|
this.진단 = new Map(); // uri → [진단…]
|
|
79
|
+
this.진단판 = new Map(); // uri → 그 진단이 몇 판 것인가 (진단기다리기 참고)
|
|
79
80
|
this.연것 = new Map(); // uri → 판 번호
|
|
80
81
|
this.준비 = null; // 켜는 중이면 그 약속
|
|
81
82
|
this.죽음 = null; // 못 켠 이유. 차 있으면 다시 안 켠다
|
|
@@ -84,9 +85,24 @@ export class 언어서버 {
|
|
|
84
85
|
this.끄는중 = false;
|
|
85
86
|
this.켜진때 = 0; // 색인이 아직 안 끝났을 만한 때인지 재는 데 쓴다
|
|
86
87
|
this.기다리는진단 = 0; // 진단을 기다리는 중인 파일 수 (#잡기·#놓기 참고)
|
|
88
|
+
this.셸로띄움 = false; // cmd.exe 를 거쳐 띄웠나 — 그러면 나무째 거둔다 (#거두기)
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
/**
|
|
92
|
+
* 이 아이가 아직 살아 있나.
|
|
93
|
+
*
|
|
94
|
+
* `signalCode` 도 같이 본다. 신호로 죽으면 node 는 **exitCode 를 null 로 두고**
|
|
95
|
+
* 신호 이름만 signalCode 에 담는다 — 그러면 종료 코드만 보는 잣대에는 「아직 안
|
|
96
|
+
* 끝났다」 로 보인다. `killed` 도 소용없다. 그건 우리가 `아이.kill()` 을 불렀을
|
|
97
|
+
* 때만 서는 깃발이라, 밖에서 온 것(OOM 킬러 · 남이 보낸 taskkill · 부모 셸이
|
|
98
|
+
* 거둔 것)에는 안 선다.
|
|
99
|
+
*
|
|
100
|
+
* 죽은 서버를 살았다고 하면 다시 안 켜고 그대로 물어본다 — 물음마다 시한까지
|
|
101
|
+
* 기다렸다 빈손으로 오고, 화면에는 「언어 서버가 늦다」 로만 뜬다.
|
|
102
|
+
*/
|
|
103
|
+
살았나() {
|
|
104
|
+
return !!this.아이 && this.아이.exitCode === null && this.아이.signalCode == null && !this.아이.killed;
|
|
105
|
+
}
|
|
90
106
|
|
|
91
107
|
// ── 켜기 ──────────────────────────────────────────────────────────────
|
|
92
108
|
|
|
@@ -124,6 +140,7 @@ export class 언어서버 {
|
|
|
124
140
|
* 그래서 한 겹 더 두른다. 바깥 한 쌍을 떼고 나면 원하던 모양이 남는다.
|
|
125
141
|
*/
|
|
126
142
|
const 몰아쓰기 = `""${실행}" ${this.서버.args.map((a) => `"${a}"`).join(' ')}"`;
|
|
143
|
+
this.셸로띄움 = 셸필요;
|
|
127
144
|
this.아이 = 셸필요
|
|
128
145
|
? spawn(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', 몰아쓰기], {
|
|
129
146
|
cwd: this.뿌리,
|
|
@@ -257,7 +274,31 @@ export class 언어서버 {
|
|
|
257
274
|
this.기다림.clear();
|
|
258
275
|
this.기다리는진단 = 0;
|
|
259
276
|
this.#놓기();
|
|
260
|
-
|
|
277
|
+
this.#거두기();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* 아이를 거둔다 — `.cmd` 로 띄운 것은 **나무째.**
|
|
282
|
+
*
|
|
283
|
+
* 윈도우에서 셸이 필요한 서버는 cmd.exe 를 거쳐 뜬다(#켜기실제). 우리가 쥔 pid 는 cmd 의
|
|
284
|
+
* 것이라 kill() 은 cmd 만 죽이고, 그 안에서 뜬 진짜 서버(node·python)는 부모 없이 남는다.
|
|
285
|
+
* 놀림시계로 끄고 다시 켤 때마다 하나씩 쌓이고, deel 이 끝날 때까지 작업 관리자에만
|
|
286
|
+
* 보인다(2.0.0 6회차 사냥). backend/mcp.js 의 닫기와 같이 taskkill /T 로 나무째 거둔다.
|
|
287
|
+
*/
|
|
288
|
+
#거두기() {
|
|
289
|
+
const 아이 = this.아이;
|
|
290
|
+
if (!아이) return;
|
|
291
|
+
if (this.셸로띄움 && process.platform === 'win32' && 아이.pid && 아이.exitCode === null) {
|
|
292
|
+
const 뒤에 = () => { try { 아이.kill(); } catch { /* 이미 갔다 */ } };
|
|
293
|
+
try {
|
|
294
|
+
const 나무 = spawn('taskkill', ['/pid', String(아이.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
|
|
295
|
+
나무.once('error', 뒤에);
|
|
296
|
+
나무.once('exit', 뒤에);
|
|
297
|
+
나무.unref();
|
|
298
|
+
} catch { 뒤에(); }
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
try { 아이.kill(); } catch { /* 이미 갔다 */ }
|
|
261
302
|
}
|
|
262
303
|
|
|
263
304
|
// ── 주고받기 ──────────────────────────────────────────────────────────
|
|
@@ -277,8 +318,15 @@ export class 언어서버 {
|
|
|
277
318
|
// 서버가 우리에게 묻는 것. 안 답하면 서버가 거기서 멈춰 버리는 것이 있어서
|
|
278
319
|
// (configuration 이 그렇다) 빈 답이라도 반드시 돌려준다.
|
|
279
320
|
if (통.id !== undefined) {
|
|
321
|
+
/*
|
|
322
|
+
* configuration 은 항목마다 **null** 이다 — 「그 설정은 모른다」.
|
|
323
|
+
*
|
|
324
|
+
* 빈 표 `{}` 로 답했었다. 그건 「설정이 있는데 비었다」 라서, 서버가 제 기본값 대신
|
|
325
|
+
* 빈 표를 설정으로 받아 켜 두어야 할 검사를 끈 채로 돌 수 있다(2.0.0 6회차 사냥).
|
|
326
|
+
* VS Code 도 모르는 칸은 null 로 준다.
|
|
327
|
+
*/
|
|
280
328
|
const 값 = 통.method === 'workspace/configuration'
|
|
281
|
-
? (통.params?.items ?? []).map(() =>
|
|
329
|
+
? (통.params?.items ?? []).map(() => null)
|
|
282
330
|
: null;
|
|
283
331
|
this.#보내기({ jsonrpc: '2.0', id: 통.id, result: 값 });
|
|
284
332
|
return;
|
|
@@ -298,6 +346,10 @@ export class 언어서버 {
|
|
|
298
346
|
const 지금판 = this.연것.get(열쇠);
|
|
299
347
|
if (typeof p.version === 'number' && typeof 지금판 === 'number' && p.version < 지금판) return;
|
|
300
348
|
this.진단.set(열쇠, Array.isArray(p.diagnostics) ? p.diagnostics : []);
|
|
349
|
+
// 몇 판 것인지도 적어 둔다. 진단기다리기 가 「이미 온 것이 지금 판 것인가」 를
|
|
350
|
+
// 이것으로 가른다 — 안 적어 두면 옛 판을 지우려다 방금 온 것까지 지운다.
|
|
351
|
+
if (typeof p.version === 'number') this.진단판.set(열쇠, p.version);
|
|
352
|
+
else this.진단판.delete(열쇠);
|
|
301
353
|
}
|
|
302
354
|
return;
|
|
303
355
|
}
|
|
@@ -396,10 +448,23 @@ export class 언어서버 {
|
|
|
396
448
|
*/
|
|
397
449
|
async 진단기다리기(uri, 시한 = 2500) {
|
|
398
450
|
const 끝 = Date.now() + 시한;
|
|
399
|
-
|
|
400
|
-
|
|
451
|
+
/*
|
|
452
|
+
* 진단은 '바뀐 뒤' 것을 봐야 한다. 그래서 기다리기에 앞서 옛 진단을 지운다 —
|
|
453
|
+
* 안 지우면 판을 올려 놓고 **옛 판 답을 지금 판의 답으로** 곧장 내준다.
|
|
454
|
+
*
|
|
455
|
+
* 그런데 여기가 판을 안 보고 통째로 지웠다. 서버가 didChange 를 받고 **이미
|
|
456
|
+
* 답해 놓은** 지금 판 진단까지 같이 버려서, 제대로 온 답이 「안 왔다」(= 확인
|
|
457
|
+
* 못 했다)로 올라갔다. 재 봤다 — 1판을 열고 진단이 도착한 뒤에 기다리면 null 이다.
|
|
458
|
+
*
|
|
459
|
+
* 그래서 **판을 보고** 지운다. 이미 온 것이 지금 판 것이면 그것이 답이다.
|
|
460
|
+
* 판을 안 주는 서버는 진단판 에 아무것도 안 남으므로 여태처럼 지운다 — 그쪽은
|
|
461
|
+
* 옛것인지 알 길이 없어서, 새로 받는 편이 맞다.
|
|
462
|
+
*/
|
|
401
463
|
const 열쇠 = 열쇠주소(uri);
|
|
402
|
-
this
|
|
464
|
+
const 지금판 = this.연것.get(열쇠);
|
|
465
|
+
const 온판 = this.진단판.get(열쇠);
|
|
466
|
+
const 지금판것이와있다 = typeof 지금판 === 'number' && typeof 온판 === 'number' && 온판 >= 지금판;
|
|
467
|
+
if (!지금판것이와있다) this.진단.delete(열쇠);
|
|
403
468
|
this.기다리는진단++;
|
|
404
469
|
this.#잡기();
|
|
405
470
|
try {
|
|
@@ -417,6 +482,14 @@ export class 언어서버 {
|
|
|
417
482
|
// ── 끄기 ──────────────────────────────────────────────────────────────
|
|
418
483
|
|
|
419
484
|
#놀림다시() {
|
|
485
|
+
/*
|
|
486
|
+
* **끄는 중에는 다시 걸지 않는다.**
|
|
487
|
+
*
|
|
488
|
+
* 끄기() 는 맨 앞에서 이 시계를 끄는데, 그 뒤에 나가는 `exit` 알림이 알림() 을
|
|
489
|
+
* 거치면서 여기를 부른다 — 껐는데 껐다 살아나는 시계다. 그렇게 남은 시계는 다
|
|
490
|
+
* 끝난 서버를 붙들고 놀림시한 뒤에 끄기() 를 한 번 더 부른다.
|
|
491
|
+
*/
|
|
492
|
+
if (this.끄는중) return;
|
|
420
493
|
clearTimeout(this.놀림시계);
|
|
421
494
|
this.놀림시계 = setTimeout(() => { this.끄기(); }, 놀림시한);
|
|
422
495
|
this.놀림시계.unref?.();
|
|
@@ -424,6 +497,7 @@ export class 언어서버 {
|
|
|
424
497
|
|
|
425
498
|
async 끄기() {
|
|
426
499
|
clearTimeout(this.놀림시계);
|
|
500
|
+
this.놀림시계 = null;
|
|
427
501
|
if (!this.아이) return;
|
|
428
502
|
this.끄는중 = true;
|
|
429
503
|
this.능력.ready = false;
|
|
@@ -435,9 +509,31 @@ export class 언어서버 {
|
|
|
435
509
|
]);
|
|
436
510
|
void 갔나;
|
|
437
511
|
} catch { /* 끄다 나는 탈은 삼킨다 */ }
|
|
438
|
-
|
|
439
|
-
|
|
512
|
+
this.#거두기();
|
|
513
|
+
/*
|
|
514
|
+
* 기다리던 물음은 **풀어 주고** 비운다.
|
|
515
|
+
*
|
|
516
|
+
* 비우기만 했었다. 그러면 부르던 쪽의 약속은 아무도 안 풀어서 제 시한(길면 수십 초)까지
|
|
517
|
+
* 그 자리에서 멎는다 — 서버는 이미 없는데(2.0.0 6회차 사냥). #무너짐 과 같이 푼다.
|
|
518
|
+
*/
|
|
519
|
+
for (const [, 것] of this.기다림) {
|
|
520
|
+
clearTimeout(것.시계);
|
|
521
|
+
것.풀기({ 오류: '언어 서버를 껐습니다' });
|
|
522
|
+
}
|
|
440
523
|
this.기다림.clear();
|
|
524
|
+
/*
|
|
525
|
+
* 진단을 기다리던 셈도 여기서 접고 놓아 준다 — #무너짐 이 하는 그대로다.
|
|
526
|
+
*
|
|
527
|
+
* 끄기() 만 이 둘을 안 했다. 그러면 기다리던 진단이 있는 채로 끈 뒤에 셈이 1 로
|
|
528
|
+
* 남고, #놓기 는 맨 앞에서 되돌아 나간다. 다음에 다시 켠 아이를 아무도 놓아 주지
|
|
529
|
+
* 않으니 `deel run 한마디` 가 답을 다 내놓고도 프롬프트로 안 돌아온다.
|
|
530
|
+
*
|
|
531
|
+
* 놓기는 **아이를 지우기 전에** 한다. 지운 뒤에는 놓아 줄 대상이 없다 — 이 줄이
|
|
532
|
+
* 아래에 있던 때는 불러도 아무 일도 안 했다.
|
|
533
|
+
*/
|
|
534
|
+
this.기다리는진단 = 0;
|
|
535
|
+
this.#놓기();
|
|
536
|
+
this.아이 = null;
|
|
441
537
|
/*
|
|
442
538
|
* 열어 뒀던 것도 같이 잊는다.
|
|
443
539
|
*
|
|
@@ -447,6 +543,7 @@ export class 언어서버 {
|
|
|
447
543
|
*/
|
|
448
544
|
this.연것.clear();
|
|
449
545
|
this.진단.clear();
|
|
546
|
+
this.진단판.clear();
|
|
450
547
|
this.끄는중 = false;
|
|
451
548
|
}
|
|
452
549
|
}
|
|
@@ -544,8 +641,13 @@ export function 다시보낼까(신호, 셈 = process.listenerCount(신호)) {
|
|
|
544
641
|
|
|
545
642
|
for (const 신호 of ['SIGINT', 'SIGTERM']) {
|
|
546
643
|
const 손 = () => {
|
|
547
|
-
|
|
644
|
+
/*
|
|
645
|
+
* 갈림을 **거두기보다 먼저** 본다. 거두고 나서 갈랐었다 — 그래서 대화 화면의 Ctrl+C 가
|
|
646
|
+
* 턴만 끊고 세션은 사는데 언어 서버는 전부 죽고 풀도 비었다(2.0.0 6회차 사냥). 남이
|
|
647
|
+
* 맡은 신호로 결국 끝나면 'exit' 그물(아이들데려가기)이 그때 거둔다.
|
|
648
|
+
*/
|
|
548
649
|
if (!다시보낼까(신호)) return; // 남이 맡고 있다 — 그쪽 뜻이 먼저다
|
|
650
|
+
아이들데려가기();
|
|
549
651
|
process.removeListener(신호, 손);
|
|
550
652
|
try { process.kill(process.pid, 신호); } catch { process.exit(신호 === 'SIGINT' ? 130 : 143); }
|
|
551
653
|
};
|