deel-local-cli 1.17.1 → 1.17.2

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 CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  [![Node.js CI](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/test.yml?branch=main&logo=github&logoColor=white&label=Node.js%20CI)](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
22
22
  [![CodeQL](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/codeql.yml?branch=main&logo=github&logoColor=white&label=CodeQL)](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
23
- [![tests](https://img.shields.io/badge/tests-7%2C844%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/ko/develop.md)
23
+ [![tests](https://img.shields.io/badge/tests-7%2C855%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/ko/develop.md)
24
24
 
25
25
  [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
26
26
  [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
@@ -268,7 +268,7 @@ deel --offline
268
268
  무엇이 어디로 갈 수 있는지는 켤 때 화면 맨 위에 늘 적혀 있습니다.
269
269
 
270
270
  ```
271
- deel 1.17.1 ⌂ 이 안
271
+ deel 1.17.2 ⌂ 이 안
272
272
  보냄 이 컴퓨터 안 127.0.0.1:11434 ← 여기 말고는 어디로도 안 갑니다
273
273
  ```
274
274
 
@@ -1185,7 +1185,7 @@ deel stats # 이 폴더에서 실제로 무엇을 했나 (.de
1185
1185
  ## 개발
1186
1186
 
1187
1187
  ```bash
1188
- npm test 전체 검증 (7,844항목 — 몇몇은 터미널에 따라 갈립니다)
1188
+ npm test 전체 검증 (7,855항목 — 몇몇은 터미널에 따라 갈립니다)
1189
1189
  npm run coverage 검사가 소스의 어디를 밟았는지
1190
1190
  npm run verify 반입·통신 검증만
1191
1191
  npm run bench 편집 성공률 측정
@@ -1254,6 +1254,7 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
1254
1254
 
1255
1255
  | 판 | 무엇이 바뀌었나 |
1256
1256
  |---|---|
1257
+ | [1.17.2](docs/ko/releases/1.17.md#1172) | 다른 눈으로 한 번 더 — 한글이 못 여는 hwpx · 이름 없이 나가던 요청 둘 |
1257
1258
  | [1.17.1](docs/ko/releases/1.17.md#1171) | 내 PC 에서만 초록이던 것 둘 — 지운 폴더에 쓰기 · 소스에 박힌 날 NUL |
1258
1259
  | **[1.17.0](docs/ko/releases/1.17.md#1170)** | 사내에 들여놓을 때 막히던 자리들 · 믿는 폴더 · mTLS · `doctor` · 훅 · 서브에이전트 · hwpx 쓰기 |
1259
1260
  | **[1.16.0](docs/ko/releases/1.16.md#1160)** | 깊이 봐 달라는 말을 얕게 받던 자리 · 게이트웨이 캐시 · `doc2md` · Figma `.fig` |
package/README.md CHANGED
@@ -20,7 +20,7 @@ Vendor APIs connect too — **only when you say so**
20
20
 
21
21
  [![Node.js CI](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/test.yml?branch=main&logo=github&logoColor=white&label=Node.js%20CI)](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
22
22
  [![CodeQL](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/codeql.yml?branch=main&logo=github&logoColor=white&label=CodeQL)](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
23
- [![tests](https://img.shields.io/badge/tests-7%2C844%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
23
+ [![tests](https://img.shields.io/badge/tests-7%2C855%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
24
24
 
25
25
  [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
26
26
  [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
@@ -339,7 +339,7 @@ deel --offline
339
339
  The destination is printed at the top of every session:
340
340
 
341
341
  ```
342
- deel 1.17.1 ⌂ inside
342
+ deel 1.17.2 ⌂ inside
343
343
  Sends to this machine 127.0.0.1:11434 ← nowhere else
344
344
  ```
345
345
 
@@ -1258,7 +1258,7 @@ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder tak
1258
1258
  ## Development
1259
1259
 
1260
1260
  ```bash
1261
- npm test Full suite (7,844 checks; a few are TTY-dependent)
1261
+ npm test Full suite (7,855 checks; a few are TTY-dependent)
1262
1262
  npm run coverage Which lines the tests actually execute
1263
1263
  npm run verify Import + network checks only
1264
1264
  npm run bench Edit success rate
@@ -1313,6 +1313,7 @@ so one run tells you everything.
1313
1313
 
1314
1314
  | Version | What changed |
1315
1315
  |---|---|
1316
+ | [1.17.2](docs/en/releases/1.17.md#1172) | A second pair of eyes — an hwpx Hancom cannot open · two requests going out unnamed |
1316
1317
  | [1.17.1](docs/en/releases/1.17.md#1171) | Two things only green on one machine — writing into a deleted directory · raw NUL in the source |
1317
1318
  | **[1.17.0](docs/en/releases/1.17.md#1170)** | What blocked it from getting inside a company · trusted folders · mTLS · `doctor` · hooks · subagents · writing hwpx |
1318
1319
  | **[1.16.0](docs/en/releases/1.16.md#1160)** | A deep request no longer lands in a shallow mode · gateway cache · `doc2md` · Figma `.fig` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deel-local-cli",
3
- "version": "1.17.1",
3
+ "version": "1.17.2",
4
4
  "description": "A coding-agent CLI that runs on local models and private gateways. Zero dependencies, Node 20+.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@ import {
12
12
  assistantMessage, toolMessage,
13
13
  } from './adapter.js';
14
14
  import { 벤더 } from './toolfit.js';
15
+ import { 세션이름짓기 } from './wire.js';
15
16
  import { 말 } from '../i18n/index.js';
16
17
 
17
18
  const READ_TOOL = {
@@ -96,6 +97,19 @@ const SKIPPED = [
96
97
  ['ctx', '컨텍스트 길이'],
97
98
  ];
98
99
 
100
+ /**
101
+ * 이 진단 한 번을 가리키는 번호.
102
+ *
103
+ * agent/store.js 의 대화 번호와 **같은 모양**으로 짓는다(YYYYMMDD-HHMMSS).
104
+ * 모양이 다르면 세션이름짓기 가 안전한 꼴이 아니라고 보고 지문으로 바꾸는데,
105
+ * 지문은 사람이 대시보드에서 「아까 그 진단」 을 못 찾게 만든다.
106
+ */
107
+ function 진단번호(at = new Date()) {
108
+ const 두 = (n) => String(n).padStart(2, '0');
109
+ return `${at.getFullYear()}${두(at.getMonth() + 1)}${두(at.getDate())}`
110
+ + `-${두(at.getHours())}${두(at.getMinutes())}${두(at.getSeconds())}`;
111
+ }
112
+
99
113
  export async function probe(conn, onStep = () => {}) {
100
114
  const { kind: shape, base, auth, model } = conn;
101
115
  const key = conn.key ?? '';
@@ -106,7 +120,19 @@ export async function probe(conn, onStep = () => {}) {
106
120
  * 멀쩡해도 그렇다. 진단 화면은 그 400 을 「연결 실패」 로 적고, 사람은
107
121
  * 열쇠를 다시 받으러 간다 — 여덟 칸이 전부 그 한 줄 때문에 빨개진다.
108
122
  */
109
- const H = () => headersFor(auth, key, 더할머리(shape));
123
+ /*
124
+ * 진단도 **제 이름을 달고 나간다.**
125
+ *
126
+ * 여기서 나가는 것은 여덟아홉 번의 진짜 채팅 완성이다. 이름이 없으면
127
+ * 게이트웨이가 그 하나하나를 새 대화로 연다 — 대시보드에는 정체 모를
128
+ * 단발 요청이 아홉 줄 쌓이고, 그 줄들은 무엇을 하다 생긴 것인지 아무도
129
+ * 모른다. 「붙는지 봤다」 와 「누가 몰래 두드렸다」 가 화면에서 같아진다.
130
+ *
131
+ * 대화가 아니므로 이어받을 이름이 없다. 이 진단 한 번을 한 대화로 묶는
132
+ * 이름을 여기서 짓는다 — 담기는 것은 여전히 번호 하나뿐이다.
133
+ */
134
+ const 진단이름 = 세션이름짓기(진단번호());
135
+ const H = () => headersFor(auth, key, 더할머리(shape, 진단이름));
110
136
  /*
111
137
  * 물음표 뒤를 끝에 남겨야 한다 (adapter.js 의 주소붙이기).
112
138
  *
package/src/commands.js CHANGED
@@ -244,6 +244,18 @@ export async function handle(line, session, ctx) {
244
244
  // 물어보러 가는 자리에도 전선 카드를 달아 준다 (backend/wire.js) —
245
245
  // 없으면 이 한 번만 생각 칸이 짐작으로 나가고, Opus 5 계열에서는 400 이다.
246
246
  전선붙이기(새conn, ctx?.배움);
247
+ /*
248
+ * 대화 이름도 이어받는다.
249
+ *
250
+ * 이건 남에게 한 번 물어보는 것이지 **다른 대화가 아니다.** 안 옮기면
251
+ * 게이트웨이가 이 한 번을 새 대화로 열고, 대시보드에서는 진행 중인
252
+ * 대화와 아무 관계 없는 줄로 떨어져 나간다 — 나중에 「이 답이 어디서
253
+ * 나왔나」 를 따라갈 실이 끊긴다.
254
+ *
255
+ * conn 을 새로 짓는 자리는 언제나 이걸 빠뜨린다. 열쇠받기·vision 을
256
+ * 연결적용()에서 빠뜨렸던 것과 같은 자리다.
257
+ */
258
+ 새conn.세션이름 = session.세션이름 ?? null;
247
259
  const 알림 = 알릴말(session.conn, 새conn);
248
260
  say('');
249
261
  say(알림.밖으로
@@ -183,10 +183,11 @@ const HEADER_XML = `${머리}
183
183
  <hh:head xmlns:hh="http://www.hancom.co.kr/hwpml/2011/head" xmlns:hc="http://www.hancom.co.kr/hwpml/2011/core" version="1.4" secCnt="1">
184
184
  <hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/>
185
185
  <hh:refList>
186
- <hh:fontfaces itemCnt="1">
187
- <hh:fontface lang="HANGUL" fontCnt="1"><hh:font id="0" face="함초롬바탕" type="TTF" isEmbedded="0"><hh:typeInfo familyType="FCAT_GOTHIC" weight="0" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/></hh:font></hh:fontface>
186
+ <hh:fontfaces itemCnt="7">
187
+ ${['HANGUL', 'LATIN', 'HANJA', 'JAPANESE', 'OTHER', 'SYMBOL', 'USER'].map((갈) => `<hh:fontface lang="${갈}" fontCnt="1"><hh:font id="0" face="함초롬바탕" type="TTF" isEmbedded="0"><hh:typeInfo familyType="FCAT_GOTHIC" weight="0" proportion="0" contrast="0" strokeVariation="0" armStyle="0" letterform="0" midline="0" xHeight="0"/></hh:font></hh:fontface>`).join('\n')}
188
188
  </hh:fontfaces>
189
189
  <hh:borderFills itemCnt="1"><hh:borderFill id="1" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0"><hh:slash type="NONE" Crooked="0" isCounter="0"/><hh:backSlash type="NONE" Crooked="0" isCounter="0"/><hh:leftBorder type="NONE" width="0.1 mm" color="#000000"/><hh:rightBorder type="NONE" width="0.1 mm" color="#000000"/><hh:topBorder type="NONE" width="0.1 mm" color="#000000"/><hh:bottomBorder type="NONE" width="0.1 mm" color="#000000"/><hh:diagonal type="SOLID" width="0.1 mm" color="#000000"/></hh:borderFill></hh:borderFills>
190
+ <hh:tabProperties itemCnt="1"><hh:tabPr id="0" autoTabLeft="0" autoTabRight="0"/></hh:tabProperties>
190
191
  <hh:charProperties itemCnt="4">
191
192
  ${[[0, 10], [1, 16], [2, 14], [3, 12]].map(([id, pt]) => `<hh:charPr id="${id}" height="${pt * PT}" textColor="#000000" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"><hh:fontRef hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/><hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/><hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/><hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/><hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${id === 0 ? '' : '<hh:bold/>'}</hh:charPr>`).join('\n')}
192
193
  </hh:charProperties>
@@ -199,11 +200,44 @@ ${[['바탕글', 'Normal', 0], ['개요 1', 'Outline 1', 1], ['개요 2', 'Outli
199
200
  </hh:refList>
200
201
  </hh:head>`;
201
202
 
203
+ /*
204
+ * 구역 속성. 첫 문단의 첫 런 안에 **딱 한 번** 들어간다.
205
+ *
206
+ * 이게 없으면 한글이 파일을 통째로 거절한다 — 조판 엔진을 세울 용지·여백이
207
+ * 없기 때문이다. 우리 읽개(tools/docs.js)는 <hp:t> 만 훑으니 없어도 멀쩡히
208
+ * 읽혔고, 그래서 검사가 전부 초록인 채로 「한글이 못 여는 파일」 을 냈다.
209
+ * 다른 눈(Gemini 3.8 Flash)이 규격을 대조해서 짚어 준 자리다.
210
+ *
211
+ * 값은 A4 세로다. 단위는 HWPUNIT(1/7200인치) —
212
+ * 210mm = 59528 · 297mm = 84188 · 좌우 30mm = 8504 · 위 20mm = 5668 · 아래·머리·꼬리 15mm = 4252
213
+ */
214
+ const SEC_PR = '<hp:secPr id="" textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" tabStopVal="4000" tabStopUnit="HWPUNIT" outlineShapeIDRef="0" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0">'
215
+ + '<hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0" strtnum="0"/>'
216
+ + '<hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/>'
217
+ + '<hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/>'
218
+ + '<hp:lineNumberShape restartType="0" countBy="0" distance="0" startNumber="0"/>'
219
+ + '<hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY">'
220
+ + '<hp:margin header="4252" footer="4252" gutter="0" left="8504" right="8504" top="5668" bottom="4252"/>'
221
+ + '</hp:pagePr>'
222
+ + '<hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="850" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr>'
223
+ + '<hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr>'
224
+ + '<hp:pageBorderFill type="BOTH" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill>'
225
+ + '<hp:pageBorderFill type="EVEN" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill>'
226
+ + '<hp:pageBorderFill type="ODD" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill>'
227
+ + '</hp:secPr>';
228
+
229
+ /*
230
+ * 줄 나눔 캐시(linesegarray)는 **안 적는다.**
231
+ *
232
+ * 전에는 문단마다 같은 값 하나를 박아 넣었다. 글자 수도 줄 수도 안 보고 적는
233
+ * 값이라, 두 줄이 넘는 문단에서는 그 캐시가 사실과 다르다. 규격에서 이 칸은
234
+ * 선택이고, 없으면 한글이 제 엔진으로 다시 잰다. **모르는 것을 지어내
235
+ * 적어 두느니 비워 두는 편이 낫다** — 이 저장소가 값을 다루는 방식 그대로다.
236
+ */
202
237
  const 문단XML = (p, 번호) => {
203
238
  const id = 갈래[p.갈래] ?? 0;
204
239
  return `<hp:p id="${번호}" paraPrIDRef="${id}" styleIDRef="${id}" pageBreak="0" columnBreak="0" merged="0">`
205
- + `<hp:run charPrIDRef="${id}"><hp:t>${esc(p.글)}</hp:t></hp:run>`
206
- + `<hp:linesegarray><hp:lineseg textpos="0" vertpos="0" vertsize="1000" textheight="1000" baseline="850" spacing="600" horzpos="0" horzsize="42520" flags="393216"/></hp:linesegarray>`
240
+ + `<hp:run charPrIDRef="${id}">${번호 === 0 ? SEC_PR : ''}<hp:t>${esc(p.글)}</hp:t></hp:run>`
207
241
  + '</hp:p>';
208
242
  };
209
243