deel-local-cli 1.17.1 → 1.17.3
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 +5 -3
- package/README.md +5 -3
- package/package.json +1 -1
- package/src/backend/adapter.js +21 -4
- package/src/backend/probe.js +27 -1
- package/src/commands.js +12 -0
- package/src/tools/hwpxwrite.js +38 -4
package/README.ko.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
|
|
22
22
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
|
|
23
|
-
[](docs/ko/develop.md)
|
|
24
24
|
|
|
25
25
|
[](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
|
|
26
26
|
[](package.json)
|
|
@@ -268,7 +268,7 @@ deel --offline
|
|
|
268
268
|
무엇이 어디로 갈 수 있는지는 켤 때 화면 맨 위에 늘 적혀 있습니다.
|
|
269
269
|
|
|
270
270
|
```
|
|
271
|
-
deel 1.17.
|
|
271
|
+
deel 1.17.3 ⌂ 이 안
|
|
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,
|
|
1188
|
+
npm test 전체 검증 (7,861항목 — 몇몇은 터미널에 따라 갈립니다)
|
|
1189
1189
|
npm run coverage 검사가 소스의 어디를 밟았는지
|
|
1190
1190
|
npm run verify 반입·통신 검증만
|
|
1191
1191
|
npm run bench 편집 성공률 측정
|
|
@@ -1254,6 +1254,8 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
|
|
|
1254
1254
|
|
|
1255
1255
|
| 판 | 무엇이 바뀌었나 |
|
|
1256
1256
|
|---|---|
|
|
1257
|
+
| [1.17.3](docs/ko/releases/1.17.md#1173) | 「같은 대화다」 를 적는 표준 이름이 없습니다 — 아는 이름을 다 답니다 |
|
|
1258
|
+
| [1.17.2](docs/ko/releases/1.17.md#1172) | 다른 눈으로 한 번 더 — 한글이 못 여는 hwpx · 이름 없이 나가던 요청 둘 |
|
|
1257
1259
|
| [1.17.1](docs/ko/releases/1.17.md#1171) | 내 PC 에서만 초록이던 것 둘 — 지운 폴더에 쓰기 · 소스에 박힌 날 NUL |
|
|
1258
1260
|
| **[1.17.0](docs/ko/releases/1.17.md#1170)** | 사내에 들여놓을 때 막히던 자리들 · 믿는 폴더 · mTLS · `doctor` · 훅 · 서브에이전트 · hwpx 쓰기 |
|
|
1259
1261
|
| **[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
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
|
|
22
22
|
[](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
|
|
23
|
-
[](docs/en/develop.md)
|
|
24
24
|
|
|
25
25
|
[](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
|
|
26
26
|
[](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.
|
|
342
|
+
deel 1.17.3 ⌂ 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,
|
|
1261
|
+
npm test Full suite (7,861 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,8 @@ so one run tells you everything.
|
|
|
1313
1313
|
|
|
1314
1314
|
| Version | What changed |
|
|
1315
1315
|
|---|---|
|
|
1316
|
+
| [1.17.3](docs/en/releases/1.17.md#1173) | No standard name for "same conversation" — it sends every name it knows |
|
|
1317
|
+
| [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
1318
|
| [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
1319
|
| **[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
1320
|
| **[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
package/src/backend/adapter.js
CHANGED
|
@@ -51,20 +51,37 @@ export function endpoint(shape) {
|
|
|
51
51
|
* 이름이 있으면 게이트웨이가 그 대화를 첫 요청이 닿은 창구에 붙여 둔다
|
|
52
52
|
* (LiteLLM 의 session affinity). 그러면 캐시가 한자리에 쌓인다.
|
|
53
53
|
*
|
|
54
|
-
* ── 왜 이름을
|
|
54
|
+
* ── 왜 이름을 넷으로 적나 ───────────────────────────────────────────────
|
|
55
|
+
*
|
|
56
|
+
* 「이 요청들은 한 대화다」 를 적는 **표준 이름이 없다.** 게이트웨이마다
|
|
57
|
+
* 읽는 이름이 다르고, 같은 게이트웨이도 판마다 다르다. 그래서 창구를 가려
|
|
58
|
+
* 이름을 고르는 대신 **아는 이름을 다 단다.** 머리는 모르면 무시되므로
|
|
59
|
+
* 이름이 늘어도 안 붙던 곳이 안 붙게 되는 일은 없다 — 이 방식을 고른
|
|
60
|
+
* 까닭이 처음부터 그것이었다.
|
|
55
61
|
*
|
|
56
62
|
* x-litellm-session-id 문서에 이름이 그대로 적힌 칸이다.
|
|
63
|
+
* x-litellm-trace-id 같은 문서가 **맞바꿔 쓸 수 있다**고 적어 둔 이름이다.
|
|
64
|
+
* 기록(SpendLogs)이 세션 칸을 trace 쪽에서만 읽던
|
|
65
|
+
* 시기가 있어서, 낮은 판에서는 이 이름만 걸린다.
|
|
66
|
+
* x-session-id 이름 없는 일반형. 앞의 둘을 모르는 중계기·기록기가
|
|
67
|
+
* 이것만 보는 일이 있다.
|
|
57
68
|
* x-deel-session-id `x-<우리이름>-session-id` 라는 **일반 규칙**으로도
|
|
58
69
|
* 읽어 주는 게이트웨이가 있다. 우리 이름으로 적어 두면
|
|
59
70
|
* 남의 도구 이름을 사칭하지 않고도 그 규칙에 걸린다.
|
|
60
71
|
*
|
|
61
|
-
* 값은
|
|
62
|
-
* (backend/wire.js 의 세션이름짓기).
|
|
72
|
+
* 값은 넷 다 같다. 대화 번호 하나뿐이고 경로·주소·열쇠는 안 들어간다
|
|
73
|
+
* (backend/wire.js 의 세션이름짓기). 그러니 이름이 늘어도 **밖으로 나가는
|
|
74
|
+
* 것은 한 글자도 안 는다.**
|
|
63
75
|
*/
|
|
64
76
|
function 세션머리(세션이름) {
|
|
65
77
|
const 이름 = String(세션이름 ?? '').trim();
|
|
66
78
|
if (!이름) return {};
|
|
67
|
-
return {
|
|
79
|
+
return {
|
|
80
|
+
'x-litellm-session-id': 이름,
|
|
81
|
+
'x-litellm-trace-id': 이름,
|
|
82
|
+
'x-session-id': 이름,
|
|
83
|
+
'x-deel-session-id': 이름,
|
|
84
|
+
};
|
|
68
85
|
}
|
|
69
86
|
|
|
70
87
|
/** 이 규격이 더 요구하는 머리. 없으면 빈 것. */
|
package/src/backend/probe.js
CHANGED
|
@@ -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
|
-
|
|
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(알림.밖으로
|
package/src/tools/hwpxwrite.js
CHANGED
|
@@ -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="
|
|
187
|
-
|
|
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}"
|
|
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
|
|