zigmini 0.8.0-beta.7 → 0.9.0-beta.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 +27 -5
- package/README.md +27 -5
- package/assets/agent/gateway-request-rules.md +3 -1
- package/assets/android/zigmini.apk +0 -0
- package/assets/android/zigmini.apk.json +3 -3
- package/assets/build.json +1 -1
- package/docs/manual.ko.md +57 -4
- package/docs/manual.md +55 -4
- package/host/omo-bridge/ascii-json.ts +5 -0
- package/host/omo-bridge/bridge.ts +50 -44
- package/host/omo-check/patches/omo-5.1.44-compaction-port.patch +2630 -0
- package/host/omo-check/patches/omo-5.1.44-gateway-port.manifest.json +329 -0
- package/host/omo-check/patches/omo-5.1.44-provider-port.patch +440 -0
- package/host/omo-check/patches/omo-5.1.44-rpc-port.patch +778 -0
- package/host/omo-check/patches/omo-5.1.48-compaction-port.patch +2629 -0
- package/host/omo-check/patches/omo-5.1.48-gateway-port.manifest.json +355 -0
- package/host/omo-check/patches/omo-5.1.48-provider-port.patch +440 -0
- package/host/omo-check/patches/omo-5.1.48-rpc-port.patch +799 -0
- package/host/omo-check/patches/omo-5.1.48-runtime-compat.patch +31 -0
- package/package.json +1 -1
- package/src/cli/setup.ts +6 -2
- package/src/decisions.ts +32 -13
- package/src/engine/compat.json +4 -4
- package/src/engine/install.ts +68 -20
- package/src/engine/rpc-socket.ts +36 -0
- package/src/engine/schema-guard.ts +34 -2
- package/src/hub.ts +57 -31
- package/src/main.ts +22 -7
- package/src/notification-history.ts +56 -0
- package/src/omo/client.ts +16 -3
- package/src/omo/frame-socket.ts +330 -0
- package/src/protocol.ts +2 -1
- package/src/security-allow.ts +126 -0
- package/src/security-check.ts +355 -37
- package/src/store.ts +165 -5
- package/src/update/handoff-system.ts +22 -0
- package/src/update/handoff.ts +10 -2
- package/src/update/helper.ts +11 -1
- package/src/update/request.ts +15 -2
- package/src/work.ts +21 -7
- package/web/DESIGN.md +39 -0
- package/web/app.js +198 -69
- package/web/decisions.d.ts +1 -1
- package/web/decisions.js +3 -0
- package/web/diagram.d.ts +28 -0
- package/web/diagram.js +228 -0
- package/web/index.html +13 -0
- package/web/security-inbox.d.ts +35 -0
- package/web/security-inbox.js +237 -0
- package/web/style.css +31 -1
package/README.ko.md
CHANGED
|
@@ -70,7 +70,7 @@ zigmini는 에이전트를 권한 프리셋 `full-access`로 실행합니다.
|
|
|
70
70
|
```sh
|
|
71
71
|
zigmini --version
|
|
72
72
|
```
|
|
73
|
-
- 결과가 숫자로 시작하는 버전 한 줄(예: `0.
|
|
73
|
+
- 결과가 숫자로 시작하는 버전 한 줄(예: `0.9.0-beta.1`)이면 15번으로 갑니다.
|
|
74
74
|
- 결과가 `zsh: command not found: zigmini`이면 Bun 전역 명령 폴더가 PATH에 없습니다. 10번으로 갑니다.
|
|
75
75
|
10. 지금 터미널 창에서 Bun 전역 명령 폴더를 PATH에 넣습니다. 이 명령은 지금 터미널 창에만 적용되고 파일을 바꾸지 않습니다.
|
|
76
76
|
```sh
|
|
@@ -95,9 +95,9 @@ zigmini는 에이전트를 권한 프리셋 `full-access`로 실행합니다.
|
|
|
95
95
|
14. 9번 명령을 다시 실행합니다.
|
|
96
96
|
- 결과가 숫자로 시작하는 버전 한 줄이면 15번으로 갑니다.
|
|
97
97
|
- 결과가 여전히 `zsh: command not found: zigmini`이면 설치를 멈춥니다. `ls "$(bun pm bin -g)"`를 실행합니다. 결과에 `zigmini`가 없으면 8번의 `bun add -g zigmini` 출력에서 `error:`로 시작하는 줄을 확인합니다.
|
|
98
|
-
15. 에이전트 엔진 `omo-ai`를 전역 설치합니다. 필수 단계입니다. `omo-ai` 설치와 모델 계정 로그인이 없으면 게이트웨이가 동작하지 않습니다. 15~18번을 건너뛰면 `zigmini setup`이 전역 `omo`를 직접 설치하고 로그인 단계에서 기다리지만, 로그인은 직접 해야 합니다. 그래도 여기의 단계를 먼저 합니다. 버전은 게이트웨이가 고정한 stock 버전에 맞춥니다(`src/engine/compat.json`의 `omo`,
|
|
98
|
+
15. 에이전트 엔진 `omo-ai`를 전역 설치합니다. 필수 단계입니다. `omo-ai` 설치와 모델 계정 로그인이 없으면 게이트웨이가 동작하지 않습니다. 15~18번을 건너뛰면 `zigmini setup`이 전역 `omo`를 직접 설치하고 로그인 단계에서 기다리지만, 로그인은 직접 해야 합니다. 그래도 여기의 단계를 먼저 합니다. 버전은 게이트웨이가 고정한 stock 버전에 맞춥니다(`src/engine/compat.json`의 `omo`, 0.9.0-beta.2는 5.1.48이며 설치된 릴리스는 자체 고정값을 유지합니다).
|
|
99
99
|
```sh
|
|
100
|
-
bun add -g omo-ai@5.1.
|
|
100
|
+
bun add -g omo-ai@5.1.48
|
|
101
101
|
```
|
|
102
102
|
16. 셸이 `omo` 명령을 찾는지 확인합니다. 결과는 버전 한 줄이어야 합니다.
|
|
103
103
|
```sh
|
|
@@ -118,7 +118,7 @@ zigmini는 에이전트를 권한 프리셋 `full-access`로 실행합니다.
|
|
|
118
118
|
1. 언어를 묻습니다: `Language / 언어 [1] English [2] 한국어`. 그 뒤의 안내는 모두 고른 언어로 나옵니다. 고른 언어는 `~/.zigmini/language`에 저장되고 `zigmini setup --access`, `zigmini pair`, `zigmini start`·`restart`의 준비 대기도 이 언어를 씁니다. `--lang en|ko`로 묻지 않고 정할 수 있습니다. 묻지 않는 설치(`--non-interactive`)는 저장된 언어, 없으면 시스템 언어(`LANG`이 `ko…`이면 한국어), 그것도 없으면 영어를 씁니다.
|
|
119
119
|
2. 권한 안내를 보여 주고 동의를 받습니다.
|
|
120
120
|
3. 홈 폴더 `~/.zigmini`를 만듭니다. 이 설치의 규칙 파일(`~/.zigmini/rules.md`)이 없으면 빈 파일을 만듭니다. 아래 "에이전트 규칙"을 보세요.
|
|
121
|
-
4. 에이전트 엔진(omo-ai)의 고정 버전을 내려받아 해시를 확인하고 패치합니다. 버전은 패키지가 정합니다(`src/engine/compat.json`, 이 문서를 쓸 때 엔진 5.1.
|
|
121
|
+
4. 에이전트 엔진(omo-ai)의 고정 버전을 내려받아 해시를 확인하고 패치합니다. 버전은 패키지가 정합니다(`src/engine/compat.json`, 이 문서를 쓸 때 엔진 5.1.48). 엔진 라이선스 요약을 보여 주고 동의(`y`)를 받습니다.
|
|
122
122
|
5. 전역 `omo` 명령을 `bun add -g omo-ai@<버전>`으로 설치합니다. 버전은 고정 엔진과 같은 stock 버전입니다. 전역 `omo`가 이미 그 버전이면 설치를 건너뜁니다. 다른 버전이면 `<버전>으로 덮어쓸까요? (y/N)`를 묻습니다. 묻지 않는 설치는 있는 버전을 유지합니다. 설치에 실패하면 고정 엔진의 긴 명령을 로그인 명령으로 보여 줍니다.
|
|
123
123
|
6. 에이전트 폴더와 모델 계정 로그인 상태를 확인합니다. 기본 에이전트 폴더는 `~/.omo/agent`이고, 같은 폴더를 쓰는 다른 OMO와 로그인 정보를 같이 씁니다. 로그인하지 않았으면 아래 상자를 보이고 기다립니다.
|
|
124
124
|
```text
|
|
@@ -195,6 +195,28 @@ F0:AB:B3:B2:DC:70:1A:45:19:85:69:7D:58:68:F5:FB:4F:AC:33:4F:D8:6A:68:DD:04:5C:53
|
|
|
195
195
|
|
|
196
196
|
사용자 매뉴얼([한국어](docs/manual.ko.md), [English](docs/manual.md))은 웹 설정 창 위쪽의 "매뉴얼" 버튼과, 앱 헤더의 상태 태그를 짧게 눌러 여는 연결 화면의 "매뉴얼" 버튼으로 봅니다.
|
|
197
197
|
|
|
198
|
+
## 0.9.0-beta.2의 새 기능
|
|
199
|
+
|
|
200
|
+
0.9.0-beta.2(2026-10-29 KST, Android `versionCode` 28)는 0.8.0-beta.7에서 업데이트하기 위한 핫픽스입니다. 엔진 `5.1.48-zgw`(OMO 5.1.48, 패치 개정 2, senpi 2026.10.25-2, 대화 스키마 10)를 선택합니다. 전체 노트는 [docs/releases/0.9.0-beta.2.ko.md](docs/releases/0.9.0-beta.2.ko.md)에 있습니다.
|
|
201
|
+
|
|
202
|
+
- **0.8.0-beta.7에서 업데이트 복구:** 0.9.0-beta.1은 이미 스키마 10으로 바뀐 대화 저장소에서 시작하지 못했습니다. 이 릴리스는 스키마 10을 읽고 세션, 등록, 설정, 기록을 유지합니다.
|
|
203
|
+
- **시간 초과 대신 분명한 거부:** 엔진이 공유 대화 저장소를 읽지 못하는 업데이트는 아무것도 설치하기 전에 거부합니다. 강제 업데이트도 거부하며 웹 설정에 오류를 보여 줍니다.
|
|
204
|
+
- **더 안정적인 엔진 연결:** 게이트웨이가 엔진 브리지 연결에 Bun WebSocket 클라이언트를 더 쓰지 않습니다. 이 클라이언트는 긴 세션에서 연결을 끊을 수 있었습니다.
|
|
205
|
+
- **앱:** 동작 변화가 없습니다. `versionCode` 28은 이 릴리스 표시입니다.
|
|
206
|
+
|
|
207
|
+
## 0.9.0-beta.1의 새 기능
|
|
208
|
+
|
|
209
|
+
0.9.0-beta.1(2026-10-28 KST, Android `versionCode` 27)은 엔진 `5.1.44-zgw`(OMO 5.1.44, senpi 2026.10.21-3, 대화 스키마 7)를 선택합니다. 전체 노트는 [docs/releases/0.9.0-beta.1.ko.md](docs/releases/0.9.0-beta.1.ko.md)에 있습니다.
|
|
210
|
+
|
|
211
|
+
- **채팅 도식:** 웹과 Android 앱이 답변의 `zigmini-diagram` 블록을 그립니다.
|
|
212
|
+
- **보안 확인함과 정확값 허용:** 보안검사로 보류한 항목이 웹과 앱의 확인함에 나옵니다. 보내기를 고르면 게이트웨이가 표시한 정확값을 설치별 목록에 기억합니다. 기억한 값은 설정에서 보고 지웁니다.
|
|
213
|
+
- **알림 설정:** 웹 설정이나 앱 설정 화면에서 "확인함 알림만"과 "모든 답장 알림" 중 하나를 고릅니다. "모든 답장 알림" 저장의 HTTP 500 오류를 고쳤습니다.
|
|
214
|
+
- **화면 수정:** 웹 이동 버튼이 메시지를 가리지 않고, Android 헤더에 모델 선택기 자리가 남습니다.
|
|
215
|
+
|
|
216
|
+
릴리스 발행은 맥과 폰을 바꾸지 않습니다. 맥은 "업데이트" 또는 `zigmini update`를 실행할 때까지 설치된 게이트웨이와 엔진을 유지합니다. 폰은 새 APK를 설치할 때까지 기존 앱을 유지합니다.
|
|
217
|
+
|
|
218
|
+
남은 문제: 버그 80(Android 참조 밀기, 요구사항만 기록), 버그 184(Android 음성 버튼 잘림, 미확정), 버그 205(Git 객체 검사 범위만 바꿈, 과거 14922건은 기록으로 남고 GitHub 지원 요청은 보류), 버그 212(변이 기록 작성기 미수정). 진단 보고와 수신 서버는 이번 릴리스에 없습니다.
|
|
219
|
+
|
|
198
220
|
## 완료 답변 통계
|
|
199
221
|
|
|
200
222
|
웹 채팅과 새 Android 앱은 완료 답변 아래에 엔진이 측정한 TPS(초당 토큰 수),
|
|
@@ -299,7 +321,7 @@ zigmini update <버전> # 지정한 버전으로 업데이트
|
|
|
299
321
|
```sh
|
|
300
322
|
zigmini setup --access lan # 또는 tailscale, domain --domain https://<도메인>
|
|
301
323
|
```
|
|
302
|
-
- **설치 뒤 에이전트가 답하지 않습니다.** 모델 계정에 로그인하지 않았습니다(예: 로그인 단계에서 `s`를 입력함). 설치 15~18번(`bun add -g omo-ai@5.1.
|
|
324
|
+
- **설치 뒤 에이전트가 답하지 않습니다.** 모델 계정에 로그인하지 않았습니다(예: 로그인 단계에서 `s`를 입력함). 설치 15~18번(`bun add -g omo-ai@5.1.48`, `omo`, `/login`)을 합니다. 그 뒤 `zigmini setup`을 다시 실행합니다. "모델 계정: 로그인됨"이 나오고, 이미 끝난 단계는 유지됩니다. `--agent-dir`을 쓰면 `OMO_CODING_AGENT_DIR=<경로> omo`로 실행해야 그 폴더에 로그인됩니다.
|
|
303
325
|
- 오래된 DB 백업, 쓰지 않는 엔진 버전, 업데이트에서 남은 파일은 웹 화면 설정의 "정리"에서 확인하고, 고른 항목만 지웁니다.
|
|
304
326
|
|
|
305
327
|
## 제거
|
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ The gateway runs the agent with the permission preset `full-access`. The agent d
|
|
|
70
70
|
```sh
|
|
71
71
|
zigmini --version
|
|
72
72
|
```
|
|
73
|
-
- If the result is one version line that starts with a number (for example `0.
|
|
73
|
+
- If the result is one version line that starts with a number (for example `0.9.0-beta.1`), go to step 15.
|
|
74
74
|
- If the result is `zsh: command not found: zigmini`, the Bun global command folder is not in PATH. Go to step 10.
|
|
75
75
|
10. Put the Bun global command folder in PATH in the current terminal window. This command applies only to the current terminal window and changes no file.
|
|
76
76
|
```sh
|
|
@@ -95,9 +95,9 @@ The gateway runs the agent with the permission preset `full-access`. The agent d
|
|
|
95
95
|
14. Run the command of step 9 again.
|
|
96
96
|
- If the result is one version line that starts with a number, go to step 15.
|
|
97
97
|
- If the result is still `zsh: command not found: zigmini`, stop the installation. Run `ls "$(bun pm bin -g)"`. If the result does not contain `zigmini`, look for lines that start with `error:` in the output of `bun add -g zigmini` in step 8.
|
|
98
|
-
15. Install the agent engine `omo-ai` globally. This step is required: without the `omo-ai` installation and the model account login, the gateway does not work. If you skip steps 15 to 18, `zigmini setup` installs the global `omo` itself and stops at the login step, but you must still log in. Do the steps here anyway. Use the stock version that the gateway pins (`omo` in `src/engine/compat.json`; 5.1.
|
|
98
|
+
15. Install the agent engine `omo-ai` globally. This step is required: without the `omo-ai` installation and the model account login, the gateway does not work. If you skip steps 15 to 18, `zigmini setup` installs the global `omo` itself and stops at the login step, but you must still log in. Do the steps here anyway. Use the stock version that the gateway pins (`omo` in `src/engine/compat.json`; 5.1.48 in 0.9.0-beta.2; installed releases keep their own pin).
|
|
99
99
|
```sh
|
|
100
|
-
bun add -g omo-ai@5.1.
|
|
100
|
+
bun add -g omo-ai@5.1.48
|
|
101
101
|
```
|
|
102
102
|
16. Check that the shell finds the `omo` command. The result must be one version line.
|
|
103
103
|
```sh
|
|
@@ -118,7 +118,7 @@ The gateway runs the agent with the permission preset `full-access`. The agent d
|
|
|
118
118
|
1. It asks for the language: `Language / 언어 [1] English [2] 한국어`. All later messages use this language. The choice is saved in `~/.zigmini/language` and used by `zigmini setup --access`, `zigmini pair` and the ready wait of `zigmini start` and `restart`. `--lang en|ko` picks it without the question. Without a question (`--non-interactive`) the saved language is used, else the system language (`LANG`, `ko…` means Korean), else English.
|
|
119
119
|
2. It shows the permission notice and asks for your consent.
|
|
120
120
|
3. It creates the home folder `~/.zigmini`. If the rules file of this installation (`~/.zigmini/rules.md`) does not exist, it creates an empty one. See "Agent rules" below.
|
|
121
|
-
4. It downloads the pinned version of the agent engine (omo-ai), checks its hash and patches it. The package decides the version (`src/engine/compat.json`; engine 5.1.
|
|
121
|
+
4. It downloads the pinned version of the agent engine (omo-ai), checks its hash and patches it. The package decides the version (`src/engine/compat.json`; engine 5.1.48 at the time of writing). It shows a summary of the engine license and asks for your consent (`y`).
|
|
122
122
|
5. It installs the global `omo` command with `bun add -g omo-ai@<version>`, the same stock version as the pinned engine. If the global `omo` already has this version, it skips the installation. If it has another version, it asks `Replace it with <version>? (y/N)`; without a question it keeps the installed one. If the installation fails, it shows the long command of the pinned engine for the login instead.
|
|
123
123
|
6. It checks the agent folder and the login state of the model account. The default agent folder is `~/.omo/agent`. Other OMO installations that use the same folder share the login data. If you are not logged in, it shows this box and waits:
|
|
124
124
|
```text
|
|
@@ -195,6 +195,28 @@ F0:AB:B3:B2:DC:70:1A:45:19:85:69:7D:58:68:F5:FB:4F:AC:33:4F:D8:6A:68:DD:04:5C:53
|
|
|
195
195
|
|
|
196
196
|
The user manual ([English](docs/manual.md), [한국어](docs/manual.ko.md)) opens from the "매뉴얼" (Manual) button at the top of the web settings dialog, and in the app from the "매뉴얼" button on the connection screen that a short press on the header status tag opens.
|
|
197
197
|
|
|
198
|
+
## What's new in 0.9.0-beta.2
|
|
199
|
+
|
|
200
|
+
Release 0.9.0-beta.2 (2026-10-29 KST, Android `versionCode` 28) is a hotfix for updating from 0.8.0-beta.7. It selects engine `5.1.48-zgw` (OMO 5.1.48, patch revision 2, senpi 2026.10.25-2, thread schema 10). Full notes: [docs/releases/0.9.0-beta.2.md](docs/releases/0.9.0-beta.2.md).
|
|
201
|
+
|
|
202
|
+
- **Update from 0.8.0-beta.7 works again:** 0.9.0-beta.1 could not start against a thread store that had already moved to schema 10. This release reads schema 10 and keeps sessions, registrations, settings and history.
|
|
203
|
+
- **Clear refusal instead of a timeout:** an update whose engine can't read the shared thread store is refused before anything is installed, with an error in the web settings, even with force.
|
|
204
|
+
- **Steadier engine connection:** the gateway no longer uses Bun's WebSocket client for the engine bridge, which could drop the connection in long sessions.
|
|
205
|
+
- **App:** no behavior change; `versionCode` 28 only marks this release.
|
|
206
|
+
|
|
207
|
+
## What's new in 0.9.0-beta.1
|
|
208
|
+
|
|
209
|
+
Release 0.9.0-beta.1 (2026-10-28 KST, Android `versionCode` 27) selects engine `5.1.44-zgw` (OMO 5.1.44, senpi 2026.10.21-3, thread schema 7). Full notes: [docs/releases/0.9.0-beta.1.md](docs/releases/0.9.0-beta.1.md).
|
|
210
|
+
|
|
211
|
+
- **Diagrams in chat:** the web and the Android app draw `zigmini-diagram` blocks in answers.
|
|
212
|
+
- **Security inbox and exact-value consent:** held security-check items appear in the web and app inbox. When you send one, the gateway remembers the exact displayed values for that installation. You can review and remove them in the settings.
|
|
213
|
+
- **Notification settings:** choose "확인함 알림만" (Inbox notifications only) or "모든 답장 알림" (All reply notifications) in the web settings or the app settings screen. Saving "All reply notifications" no longer fails with HTTP 500.
|
|
214
|
+
- **UI fixes:** the web scroll buttons no longer cover messages, and the Android header keeps room for the model picker.
|
|
215
|
+
|
|
216
|
+
Publishing a release doesn't change your Mac or phone. The Mac keeps its installed gateway and engine until you run "업데이트" (Update) or `zigmini update`, and the phone keeps its app until you install the new APK.
|
|
217
|
+
|
|
218
|
+
Still open: bug 80 (Android reference swipe, only requirements recorded), bug 184 (Android voice button clipping, unconfirmed), bug 205 (only the Git-object scope changed; the 14922 historical findings stay recorded and the GitHub support request stays deferred) and bug 212 (the mutation-record writer is not fixed). Diagnostic reports and the receiver server aren't part of this release.
|
|
219
|
+
|
|
198
220
|
## Completed-answer statistics
|
|
199
221
|
|
|
200
222
|
The web chat and updated Android app show available engine measurements below a
|
|
@@ -300,7 +322,7 @@ Use these workarounds.
|
|
|
300
322
|
```sh
|
|
301
323
|
zigmini setup --access lan # or tailscale, or domain --domain https://<도메인>
|
|
302
324
|
```
|
|
303
|
-
- **After the installation the agent does not answer.** The model account is not logged in (for example you typed `s` at the login step). Do install steps 15 to 18 (`bun add -g omo-ai@5.1.
|
|
325
|
+
- **After the installation the agent does not answer.** The model account is not logged in (for example you typed `s` at the login step). Do install steps 15 to 18 (`bun add -g omo-ai@5.1.48`, `omo`, `/login`). Then run `zigmini setup` again. It shows "Model accounts: logged in" and keeps the steps that are already done. If you use `--agent-dir`, start `omo` with `OMO_CODING_AGENT_DIR=<경로> omo` so that it logs in to that folder.
|
|
304
326
|
- Old DB backups, unused engine versions and files left by updates appear in "정리" (Clean up) in the web screen settings. Only the items you select are deleted.
|
|
305
327
|
|
|
306
328
|
## Uninstall
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
사용자에게 보여 주는
|
|
1
|
+
사용자에게 보여 주는 모든 답변과 설명은 ASD-STE100의 명확한 기술문서 작성 원칙을 따른다. 짧은 능동문을 쓰고, 한 문장에는 한 가지 내용만 담는다. 주체·대상·행동을 명확히 밝히고, 같은 대상에는 같은 용어를 쓴다.
|
|
2
|
+
|
|
3
|
+
흐름이나 구조를 설명할 때 도식이 도움이 되면 `docs/manual.md`의 `zigmini-diagram` 형식으로 함께 표시한다.
|
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"versionCode":
|
|
3
|
-
"versionName": "0.
|
|
4
|
-
"sha256": "
|
|
2
|
+
"versionCode": 28,
|
|
3
|
+
"versionName": "0.9.0-beta.2",
|
|
4
|
+
"sha256": "3588a9b0124a4d483d84038edf86f68cf3d96e0d071d5eb93ff556908507e012"
|
|
5
5
|
}
|
package/assets/build.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"commit":"
|
|
1
|
+
{"commit":"78cb593c5a3f2599a4196e877f2d07d3e90fd506"}
|
package/docs/manual.ko.md
CHANGED
|
@@ -92,6 +92,44 @@ README의 '명령' 절에 따르면 채팅에서 `/usage`를 보내면 게이트
|
|
|
92
92
|
|
|
93
93
|
### 2.3 첨부와 참조
|
|
94
94
|
|
|
95
|
+
#### 흐름도와 구조도
|
|
96
|
+
|
|
97
|
+
웹과 Android 채팅은 완성된 `zigmini-diagram` 코드 블록을 도식으로 표시합니다.
|
|
98
|
+
좁은 화면에서도 글자를 줄이지 않습니다. 도식 안을 스크롤하면 화면 밖의 노드를 볼 수 있습니다.
|
|
99
|
+
원문 표시를 열면 원래 JSON을 선택하고 복사할 수 있습니다.
|
|
100
|
+
메시지를 참조하면 이미지나 로컬 경로가 아니라 원래 본문을 전달합니다.
|
|
101
|
+
기록 조회와 재연결도 같은 저장 본문을 사용합니다. 업로드나 외부 렌더러는 필요하지 않습니다.
|
|
102
|
+
|
|
103
|
+
에이전트는 답변 본문에 아래처럼 완성된 블록을 넣습니다.
|
|
104
|
+
|
|
105
|
+
````markdown
|
|
106
|
+
```zigmini-diagram
|
|
107
|
+
{"version":1,"title":"요청 흐름","nodes":[{"id":"request","label":"요청"},{"id":"gateway","label":"게이트웨이"},{"id":"reply","label":"답변"}],"edges":[{"from":"request","to":"gateway"},{"from":"gateway","to":"reply"}]}
|
|
108
|
+
```
|
|
109
|
+
````
|
|
110
|
+
|
|
111
|
+
지원 형식은 순환 없는 작은 방향 그래프입니다. Mermaid나 임의 SVG·HTML은 지원하지 않습니다.
|
|
112
|
+
갈라지는 연결로 구조를 표현할 수 있습니다.
|
|
113
|
+
노드는 위에서 아래로 배치합니다. 같은 깊이의 노드는 입력 순서로 배치합니다.
|
|
114
|
+
|
|
115
|
+
- 최상위 필수 키는 `version: 1`, `nodes`, `edges`입니다. `title`은 선택 항목입니다.
|
|
116
|
+
- 노드에는 `id`와 `label`만 넣습니다. 노드는 1~16개입니다.
|
|
117
|
+
ID는 서로 달라야 하며 `[A-Za-z][A-Za-z0-9_-]{0,31}` 형식을 사용합니다.
|
|
118
|
+
이름은 공백만이 아닌 일반 텍스트로 쓰며 유니코드 코드 포인트 48개까지입니다.
|
|
119
|
+
- 연결에는 기존 노드의 ID인 `from`과 `to`만 넣습니다. 연결은 0~24개입니다.
|
|
120
|
+
중복 연결, 자기 자신으로의 연결, 순환은 지원하지 않습니다.
|
|
121
|
+
- 선택 제목은 공백만이 아닌 일반 텍스트로 쓰며 유니코드 코드 포인트 80개까지입니다.
|
|
122
|
+
제목과 이름에는 제어 문자 U+0000~U+001F와 U+007F를 넣을 수 없습니다.
|
|
123
|
+
- JSON은 UTF-16 코드 단위 16,384개까지입니다. 다른 키는 지원하지 않습니다.
|
|
124
|
+
들여쓰기 없는 백틱 세 개와 위 예시의 정확한 언어 이름을 사용합니다.
|
|
125
|
+
|
|
126
|
+
미완성·잘못된 형식·미지원 블록은 원문으로 표시합니다.
|
|
127
|
+
스트리밍 중 블록이 완성되고 형식이 맞으면 도식으로 표시할 수 있습니다.
|
|
128
|
+
도식 내용은 스크립트 실행, URL 로드, 파일 읽기를 할 수 없습니다.
|
|
129
|
+
이 기능이 없는 이전 클라이언트는 코드 블록으로 표시합니다.
|
|
130
|
+
짧은 매 요청 규칙은 흐름이나 구조 설명에 도움이 될 때만 도식을 사용하도록 안내합니다.
|
|
131
|
+
모든 답변에 도식을 요구하지 않습니다.
|
|
132
|
+
|
|
95
133
|
#### 이미지
|
|
96
134
|
|
|
97
135
|
- "이미지" 버튼을 눌러 이미지를 고릅니다.
|
|
@@ -176,9 +214,16 @@ README의 '명령' 절에 따르면 채팅에서 `/usage`를 보내면 게이트
|
|
|
176
214
|
#### 보안검사
|
|
177
215
|
|
|
178
216
|
- "보안검사 켜짐/꺼짐"은 보내는 메시지의 검사를 켜고 끕니다. 기본은 켜짐이고 게이트웨이가 설정을 기억합니다.
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
217
|
+
- 메시지나 확인함 답에 허용하지 않은 값이 탐지되면 게이트웨이가 에이전트에 보내기 전에 보류합니다. 확인함과 채팅에는 같은 보안 항목이 보입니다. 항목은 기억할 전체 값과 종류를 보여 줍니다.
|
|
218
|
+
- 보내기를 선택하면 원래 메시지를 전송하고 표시한 정확한 값을 이 게이트웨이에 기억합니다. 다음부터 같은 값은 다시 묻지 않습니다. 다른 값은 계속 확인합니다. 이미 보류한 메시지를 자동으로 보내지는 않습니다.
|
|
219
|
+
- 취소는 이번 전송만 중단합니다. 기존 작업은 중단하지 않습니다. 보류한 내용이 확인함 답이면 원래 질문은 답하지 않은 상태로 남고 답변 입력란도 유지합니다.
|
|
220
|
+
- 웹은 같은 대화의 입력창이 비어 있을 때만 취소한 메시지의 글을 되돌립니다. 새로 작성한 초안은 바꾸지 않습니다. Android는 취소한 메시지를 입력창에 되돌리지 않습니다. 전송 결과가 미확인이면 자동 복구하거나 다시 보내지 않습니다.
|
|
221
|
+
- 요청을 처리하는 동안 선택 버튼을 잠급니다. HTTP 응답, 실시간 이벤트, 재연결 결과로 같은 항목을 갱신합니다. 미확인은 전달을 확인하지 못했다는 뜻입니다. 다시 보내지 말고 상태 확인을 사용합니다.
|
|
222
|
+
- 값을 기억한 뒤 전송합니다. 저장에 실패하면 보내지 않습니다. 저장한 뒤 전송에 실패하거나 결과를 확인하지 못하면 값은 기억된 상태로 남습니다. 결과는 기억 여부와 전송 상태를 따로 알립니다.
|
|
223
|
+
- 허용 목록은 이 설치에만 비공개로 저장하며 게이트웨이를 다시 시작해도 유지합니다. 웹 설정의 보안검사 구역에서 목록을 봅니다. Android는 매뉴얼 버튼이 있는 게이트웨이 설정 화면에서 허용 값 목록을 엽니다.
|
|
224
|
+
- 항목마다 전체 값, 종류, 승인 시각, 승인한 클라이언트를 보여 줍니다. 개별 삭제나 전체 삭제를 하면 다음 전송부터 해당 값을 다시 검사합니다. 삭제는 이미 승인한 전송을 취소하거나 보낸 메시지를 지우지 않습니다.
|
|
225
|
+
- 구형 앱의 보내기는 한 번만 전송하며 값을 기억하지 않습니다. 값을 기억하려면 새 화면의 전송·기억 선택이 필요합니다. 미승인 원문은 일반 질문 기록이나 대화 이력에 저장하지 않습니다. 게이트웨이를 다시 시작하면 보류 원문은 사라집니다.
|
|
226
|
+
- 이 목록은 메시지와 답변의 전송 검사에만 적용합니다. 저장소와 릴리스 보안검사는 바꾸지 않습니다.
|
|
182
227
|
|
|
183
228
|
#### 업데이트
|
|
184
229
|
|
|
@@ -194,10 +239,17 @@ README의 '명령' 절에 따르면 채팅에서 `/usage`를 보내면 게이트
|
|
|
194
239
|
| 확인함에 답을 기다리는 질문이 있음 | 버튼이 꺼집니다. "답을 기다리는 질문이 N개 있습니다" 안내와 "확인함 열기" 버튼이 보입니다. 질문에 답한 뒤 업데이트할 수 있습니다. |
|
|
195
240
|
| 권한 안내가 바뀜 | 새 안내문이 보입니다. 입력란에 동의를 입력해야 진행합니다. |
|
|
196
241
|
| 공유 데이터 형식 변경 | 변경 내용을 보여 주고 확인을 받습니다. |
|
|
242
|
+
| 저장된 데이터 형식이 대상 엔진의 지원 범위보다 새로움 | 설치 전에 업데이트를 멈춥니다. 오류에 저장 형식과 지원 형식이 나옵니다. 확인하거나 강제로 실행해도 이 거부를 우회할 수 없습니다. |
|
|
197
243
|
| 진행 중인 작업이 있음 | 게이트웨이가 바쁘다는 안내가 보입니다. |
|
|
198
244
|
|
|
199
245
|
업데이트하면 게이트웨이와 브리지가 다시 시작됩니다. 그동안 연결과 진행 중인 대화가 잠시 끊길 수 있습니다. 진행 상황이 영역 아래에 보입니다. 끝나면 페이지를 새로 고칩니다.
|
|
200
246
|
|
|
247
|
+
공유 데이터 형식이 더 새로우면 그 형식을 지원하는 엔진 릴리스가 필요합니다.
|
|
248
|
+
저장 형식 번호를 낮추거나 에이전트 폴더를 초기화하지 마세요.
|
|
249
|
+
새 세대가 시작을 거부하면 업데이트가 그 이유를 표시합니다.
|
|
250
|
+
기존 실행을 아직 바꾸지 않았다면 기존 세대를 유지합니다.
|
|
251
|
+
에이전트가 연결됐다는 사실만으로 공유 thread 저장소의 정상 동작을 판단할 수는 없습니다.
|
|
252
|
+
|
|
201
253
|
#### 기기 등록
|
|
202
254
|
|
|
203
255
|
- 폰 등록 QR이 보입니다. QR 하나는 10분 동안 한 번만 쓸 수 있습니다.
|
|
@@ -329,10 +381,11 @@ Ctrl 단축키는 Cmd, Alt, Shift와 함께 누르면 동작하지 않습니다.
|
|
|
329
381
|
### 4.6 알림
|
|
330
382
|
|
|
331
383
|
- 등록한 게이트웨이마다 알림 연결이 하나씩 있습니다.
|
|
332
|
-
- 웹
|
|
384
|
+
- 웹 설정에서 앱 알림 수준을 고릅니다. 앱에서는 헤더의 상태 태그를 짧게 눌러 기존 설정창을 엽니다. 두 선택기는 매뉴얼 위에 있으며 작업 상태 화면에는 없습니다.
|
|
333
385
|
- "확인함 알림만"이 기본값입니다. 기존 확인 요청 알림을 유지합니다.
|
|
334
386
|
- "모든 답장 알림"은 확인 요청 알림과 main·작업 에이전트의 개별 완성 답장을 알립니다. 사용자 메시지와 스트리밍 조각은 알리지 않습니다.
|
|
335
387
|
- 두 화면은 같은 게이트웨이 저장값을 바꿉니다. 앱은 현재 게이트웨이만 바꿉니다. 설정을 바꿔도 과거 답장을 다시 알리지 않습니다.
|
|
388
|
+
- 저장에 실패하면 마지막으로 확인된 선택을 유지합니다. "다시 시도"는 저장값을 다시 읽습니다. 읽기가 끝나면 원하는 항목을 다시 고릅니다.
|
|
336
389
|
- 일정과 명시적 알림은 이 선택과 별개입니다. 확인 요청 알림의 기존 웹 화면 표시 여부 규칙도 유지합니다.
|
|
337
390
|
- 답장 알림을 누르면 보낸 게이트웨이의 채팅을 엽니다. 같은 답장을 재전송해도 다시 알리지 않습니다.
|
|
338
391
|
- 알림은 소리 없이 진동만 합니다.
|
package/docs/manual.md
CHANGED
|
@@ -92,6 +92,42 @@ The README's Commands section says that sending `/usage` in chat makes the gatew
|
|
|
92
92
|
|
|
93
93
|
### 2.3 Attachments and references
|
|
94
94
|
|
|
95
|
+
#### Flow and structure diagrams
|
|
96
|
+
|
|
97
|
+
Web and Android chat render complete `zigmini-diagram` fenced blocks as diagrams.
|
|
98
|
+
Labels remain readable on narrow screens: scroll inside the diagram to reach
|
|
99
|
+
off-screen nodes. Open its source control to select and copy the original JSON.
|
|
100
|
+
Referencing the message sends its original text, not an image or a local path.
|
|
101
|
+
History and reconnect use the same stored text. No upload or external renderer is needed.
|
|
102
|
+
|
|
103
|
+
For agents, use this complete block in the assistant's message body:
|
|
104
|
+
|
|
105
|
+
````markdown
|
|
106
|
+
```zigmini-diagram
|
|
107
|
+
{"version":1,"title":"Request flow","nodes":[{"id":"request","label":"Request"},{"id":"gateway","label":"Gateway"},{"id":"reply","label":"Reply"}],"edges":[{"from":"request","to":"gateway"},{"from":"gateway","to":"reply"}]}
|
|
108
|
+
```
|
|
109
|
+
````
|
|
110
|
+
|
|
111
|
+
The supported format is a small directed acyclic graph, not Mermaid or arbitrary
|
|
112
|
+
SVG/HTML. Branching edges express a structure. Nodes appear top-to-bottom; nodes
|
|
113
|
+
at the same depth follow their input order.
|
|
114
|
+
|
|
115
|
+
- Root keys: required `version: 1`, `nodes`, `edges`; optional `title`.
|
|
116
|
+
- Each node has only `id` and `label`. Use 1–16 nodes with unique IDs matching
|
|
117
|
+
`[A-Za-z][A-Za-z0-9_-]{0,31}`. Labels are nonblank plain text, at most 48 Unicode code points.
|
|
118
|
+
- Each edge has only `from` and `to`, naming existing nodes. Use 0–24 edges.
|
|
119
|
+
Duplicate edges, self-links and cycles are not supported.
|
|
120
|
+
- The optional title is nonblank plain text, at most 80 Unicode code points.
|
|
121
|
+
Titles and labels cannot contain control characters U+0000–U+001F or U+007F.
|
|
122
|
+
- JSON is at most 16,384 UTF-16 code units. Extra keys are not supported.
|
|
123
|
+
Use unindented triple backticks and the exact language name shown above.
|
|
124
|
+
|
|
125
|
+
Unfinished, invalid or unsupported blocks remain visible as source. When a streamed
|
|
126
|
+
block becomes complete and valid, chat can render it. Diagram text cannot run
|
|
127
|
+
scripts, load URLs or read files. An older client displays the code block instead.
|
|
128
|
+
The concise per-request instruction asks for a diagram only when it helps explain
|
|
129
|
+
a flow or structure; it does not require diagrams in every reply.
|
|
130
|
+
|
|
95
131
|
#### Images
|
|
96
132
|
|
|
97
133
|
- Click 이미지 (image) to pick images.
|
|
@@ -176,9 +212,16 @@ In a name field, Enter saves and Esc cancels. Leaving the field also saves. Name
|
|
|
176
212
|
#### Security check (보안검사)
|
|
177
213
|
|
|
178
214
|
- "보안검사 켜짐/꺼짐" (security check on/off) turns the check of your messages on or off. It is on by default and the gateway keeps the choice.
|
|
179
|
-
- When a message
|
|
180
|
-
-
|
|
181
|
-
-
|
|
215
|
+
- When a message or inbox answer contains a detected value that is not allowed, the gateway holds it before sending it to the agent. The same security item appears in the inbox and chat. It shows the complete values to be remembered and their types.
|
|
216
|
+
- Approving sends the original message and remembers the displayed exact values on this gateway. The same values do not prompt again. A different value still needs approval. Existing held messages do not send automatically.
|
|
217
|
+
- Cancel stops only this transmission. It does not stop the existing work. If the held transmission is an inbox answer, the original question remains unanswered and its answer form stays available.
|
|
218
|
+
- On web, a cancelled message can restore its text only in an empty input box of the same conversation. A newer draft is never replaced. Android does not restore a cancelled message to its input box. A transmission with an unconfirmed result is not automatically restored or sent again.
|
|
219
|
+
- Controls lock while the request is processing. HTTP replies, live events and reconnects update the same item. An unconfirmed result means the client has no proof of delivery; use the status check instead of sending again.
|
|
220
|
+
- Remembering happens before dispatch. If saving fails, the message is not sent. If saving succeeds but dispatch fails or cannot be confirmed, the values remain remembered and the result reports both facts.
|
|
221
|
+
- The allowed-value list is private to this installation and survives gateway restarts. Web settings show the list under Security check. On Android, open the gateway settings screen that contains Manual, then open the allowed values.
|
|
222
|
+
- Each entry shows its full value, type, approval time and approving client. Remove one entry or clear the list to check those values again on future transmissions. Removal does not cancel a transmission already approved or erase a sent message.
|
|
223
|
+
- An older app's send-only approval still sends once without remembering values. Remembering requires the new send-and-remember choice. Unapproved original messages are not saved in ordinary question records or history; pending originals are lost on gateway restart.
|
|
224
|
+
- This list affects runtime message and answer checking only. It does not change repository or release security checks.
|
|
182
225
|
|
|
183
226
|
#### Update (업데이트)
|
|
184
227
|
|
|
@@ -194,10 +237,17 @@ The update button is held, or asks for more, in these cases.
|
|
|
194
237
|
| Inbox questions are waiting for you | The button is disabled. A line says N questions are waiting, with a 확인함 열기 (open inbox) button. Answer them, then update. |
|
|
195
238
|
| The permission notice changed | The new notice shows. Type the consent word (동의) in the field to go on. |
|
|
196
239
|
| Shared data format change | The changes are shown and you confirm them. |
|
|
240
|
+
| Stored data is newer than the target engine supports | The update stops before installation. The error names the stored and supported formats. Confirmation or forcing the update cannot bypass this refusal. |
|
|
197
241
|
| Work is running | A notice says the gateway is busy. |
|
|
198
242
|
|
|
199
243
|
An update restarts the gateway and the bridge. Connections and running chats may drop for a moment. Progress shows below. When it's done, reload the page.
|
|
200
244
|
|
|
245
|
+
A newer shared data format needs a compatible engine release. Do not lower the
|
|
246
|
+
stored format number or reset the agent folder. If a new generation refuses to
|
|
247
|
+
start, the update reports that reason and keeps the old running generation when
|
|
248
|
+
it has not been changed. A connected agent alone does not prove that its shared
|
|
249
|
+
thread storage works.
|
|
250
|
+
|
|
201
251
|
#### Device registration (기기 등록)
|
|
202
252
|
|
|
203
253
|
- A phone registration QR shows. Each QR works once, for 10 minutes.
|
|
@@ -329,10 +379,11 @@ Tap the Zig face to open it. Slot states include running, waiting for input, wai
|
|
|
329
379
|
### 4.6 Notifications
|
|
330
380
|
|
|
331
381
|
- Each registered gateway has its own notification connection.
|
|
332
|
-
- Open web settings
|
|
382
|
+
- Open web settings to choose the app notification level. In the app, short press the header status tag to open the existing settings screen. Both choices are above Manual, not in work status.
|
|
333
383
|
- "확인함 알림만" (Inbox notifications only) is the default. It keeps the existing confirmation notifications.
|
|
334
384
|
- "모든 답장 알림" (All reply notifications) includes confirmation notifications and each completed main/work agent reply. User messages and streaming fragments do not alert.
|
|
335
385
|
- Both screens edit the same saved gateway value. The app edits only its active gateway. Changing the value does not replay old replies.
|
|
386
|
+
- If saving fails, the last confirmed choice stays selected. Retry reloads the saved value. Select the desired choice after reloading.
|
|
336
387
|
- Schedule reminders and explicit notifications are independent of this choice. The existing visible-web rule for confirmation notifications still applies.
|
|
337
388
|
- Reply notification taps open chat on the sending gateway. Replayed delivery of the same reply does not alert again.
|
|
338
389
|
- Notifications vibrate with no sound.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Bug 220: Bun 1.4.2 rejects large multibyte JSON frames in the RPC regression.
|
|
2
|
+
// JSON escapes preserve every UTF-16 code unit while keeping wire text ASCII.
|
|
3
|
+
export function asciiJson(line: string): string {
|
|
4
|
+
return line.replace(/[^\x00-\x7f]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
5
|
+
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
|
-
import type { ServerWebSocket,
|
|
4
|
+
import type { ServerWebSocket, Subprocess } from "bun";
|
|
5
|
+
import type { Socket } from "node:net";
|
|
5
6
|
import { bounded, maintenanceAuth, MaintenanceError } from "../../src/maintenance.ts";
|
|
6
7
|
import { runtimePaths } from "../../src/runtime/paths.ts";
|
|
7
8
|
import { installKind } from "../../src/runtime/install-kind.ts";
|
|
8
9
|
import { loadNotice, startRefusal } from "../../src/consent/consent.ts";
|
|
9
10
|
import { engine, engineSearchPath } from "../../src/engine/index.ts";
|
|
11
|
+
import { connectEngineSocket } from "../../src/engine/rpc-socket.ts";
|
|
10
12
|
import { bunInstallDir, guardMode, guardStart } from "../../src/engine/schema-guard.ts";
|
|
11
13
|
import { bridgeMayRun, readRegistry } from "../../src/runtime/slots.ts";
|
|
12
14
|
import { BridgeMaintenance } from "./maintenance.ts";
|
|
15
|
+
import { asciiJson } from "./ascii-json.ts";
|
|
13
16
|
|
|
14
17
|
const paths = runtimePaths();
|
|
15
18
|
const home = paths.home;
|
|
@@ -39,6 +42,7 @@ if (slotMode) {
|
|
|
39
42
|
const agentDir = process.env.ZGW_OMO_AGENT_DIR ?? join(process.env.HOME ?? ".", ".omo", "agent");
|
|
40
43
|
// Bug 73: a shared store newer than the pinned engine keeps the bridge down (exit 0: launchd does not restart it).
|
|
41
44
|
const schemaGuard = guardStart({ agentDir, run: paths.run, mode: guardMode(process.env),
|
|
45
|
+
origin: { slot: slotMode ? paths.slot : null, engine: engineRoot ? basename(engineRoot) : null },
|
|
42
46
|
supported: () => engine.supportedThreadSchema(home, engineRoot), bunInstall: bunInstallDir(process.env), now: () => new Date() });
|
|
43
47
|
if (schemaGuard.log !== null) console.error(`${new Date().toISOString()} ${schemaGuard.log}`);
|
|
44
48
|
if (schemaGuard.refuse) process.exit(0);
|
|
@@ -124,7 +128,8 @@ type Link = {
|
|
|
124
128
|
readonly identity: string | null;
|
|
125
129
|
readonly epoch: string;
|
|
126
130
|
readonly previousFailure: string | null;
|
|
127
|
-
upstream: Socket
|
|
131
|
+
upstream: Socket | null;
|
|
132
|
+
upstreamBlocked: boolean;
|
|
128
133
|
frontend: ServerWebSocket<Link> | null;
|
|
129
134
|
connecting: boolean;
|
|
130
135
|
ready: boolean;
|
|
@@ -150,7 +155,7 @@ const links = new Set<Link>();
|
|
|
150
155
|
maintenance = new BridgeMaintenance(paths.maintenanceFile,
|
|
151
156
|
() => ({ generation: nativeGeneration, binary: nativeBinary }),
|
|
152
157
|
() => omo.exitCode === null && [...links].filter((link) => link.nativeGeneration === nativeGeneration).every((link) =>
|
|
153
|
-
!link.connecting && !link.activeOut && link.out.length === 0 && link.pending.size === 0 &&
|
|
158
|
+
!link.connecting && !link.upstreamBlocked && !link.activeOut && link.out.length === 0 && link.pending.size === 0 &&
|
|
154
159
|
!link.frontendBlocked && !link.frontendPending && link.frontendOut.length === 0 &&
|
|
155
160
|
link.pendingUiWrites.size === 0 && !link.untrackedWrite && link.ui.size === 0),
|
|
156
161
|
async () => {
|
|
@@ -191,8 +196,9 @@ function fail(link: Link, reason: string): void {
|
|
|
191
196
|
link.failure = reason;
|
|
192
197
|
logBridgeError(`omo bridge: requester ${link.epoch} lost: ${reason}`);
|
|
193
198
|
link.frontend?.close(1011, reason);
|
|
194
|
-
link.upstream?.
|
|
199
|
+
link.upstream?.destroy();
|
|
195
200
|
link.upstream = null;
|
|
201
|
+
link.upstreamBlocked = false;
|
|
196
202
|
link.activeOut = null;
|
|
197
203
|
link.out = [];
|
|
198
204
|
link.outBytes = 0;
|
|
@@ -234,6 +240,7 @@ function flushFrontend(link: Link): void {
|
|
|
234
240
|
}
|
|
235
241
|
|
|
236
242
|
function deliver(link: Link, line: string): void {
|
|
243
|
+
line = asciiJson(line);
|
|
237
244
|
const ws = link.frontend;
|
|
238
245
|
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
239
246
|
if (link.frontendBlocked || link.frontendPending || link.frontendOut.length > 0) {
|
|
@@ -314,17 +321,20 @@ function receive(link: Link, line: string): void {
|
|
|
314
321
|
|
|
315
322
|
const encoder = new TextEncoder();
|
|
316
323
|
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
// cannot parse it, and the next command glued to the cut-off tail is lost too.
|
|
324
|
+
// net.Socket accepts the complete chunk; false means wait for drain before writing the next one.
|
|
325
|
+
// Keep later commands in the bounded bridge queue instead of the socket's unbounded writable queue.
|
|
320
326
|
function flush(link: Link): void {
|
|
321
327
|
const upstream = link.upstream;
|
|
322
|
-
if (!upstream) return;
|
|
328
|
+
if (!upstream || link.upstreamBlocked) return;
|
|
329
|
+
if (!link.activeOut) {
|
|
330
|
+
link.activeOut = link.out.shift() ?? null;
|
|
331
|
+
if (link.activeOut) link.outBytes -= link.activeOut.length;
|
|
332
|
+
}
|
|
323
333
|
while (link.activeOut) {
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
if (
|
|
327
|
-
link.
|
|
334
|
+
const writable = upstream.write(link.activeOut);
|
|
335
|
+
link.activeOut = null;
|
|
336
|
+
if (!writable) {
|
|
337
|
+
link.upstreamBlocked = true;
|
|
328
338
|
return;
|
|
329
339
|
}
|
|
330
340
|
link.activeOut = link.out.shift() ?? null;
|
|
@@ -334,7 +344,7 @@ function flush(link: Link): void {
|
|
|
334
344
|
|
|
335
345
|
function forward(link: Link, line: string): void {
|
|
336
346
|
const chunk = encoder.encode(`${line}\n`);
|
|
337
|
-
if (!link.activeOut) {
|
|
347
|
+
if (!link.activeOut && !link.upstreamBlocked) {
|
|
338
348
|
link.activeOut = chunk;
|
|
339
349
|
flush(link);
|
|
340
350
|
return;
|
|
@@ -401,6 +411,7 @@ const server = Bun.serve<Link>({
|
|
|
401
411
|
epoch: randomUUID(),
|
|
402
412
|
previousFailure: existing?.failure ?? null,
|
|
403
413
|
upstream: null,
|
|
414
|
+
upstreamBlocked: false,
|
|
404
415
|
frontend: null,
|
|
405
416
|
connecting: false,
|
|
406
417
|
ready: false,
|
|
@@ -440,42 +451,37 @@ const server = Bun.serve<Link>({
|
|
|
440
451
|
if (link.upstream || link.connecting) return;
|
|
441
452
|
link.connecting = true;
|
|
442
453
|
try {
|
|
443
|
-
const upstream = await
|
|
444
|
-
unix: socketPath,
|
|
445
|
-
socket: {
|
|
446
|
-
data(_s, chunk) {
|
|
447
|
-
if (link.failure) return;
|
|
448
|
-
const text = link.decoder.decode(chunk, { stream: true });
|
|
449
|
-
let start = 0;
|
|
450
|
-
let at = text.indexOf("\n");
|
|
451
|
-
while (at >= 0) {
|
|
452
|
-
const line = `${link.buffer.join("")}${text.slice(start, at)}`.replace(/\r$/, "");
|
|
453
|
-
link.buffer = [];
|
|
454
|
-
if (line) receive(link, line);
|
|
455
|
-
start = at + 1;
|
|
456
|
-
at = text.indexOf("\n", start);
|
|
457
|
-
}
|
|
458
|
-
if (start < text.length) link.buffer.push(text.slice(start));
|
|
459
|
-
},
|
|
460
|
-
drain() {
|
|
461
|
-
flush(link);
|
|
462
|
-
},
|
|
463
|
-
close() {
|
|
464
|
-
fail(link, "omo_requester_disconnected");
|
|
465
|
-
},
|
|
466
|
-
error() {
|
|
467
|
-
fail(link, "omo_requester_error");
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
});
|
|
454
|
+
const upstream = await connectEngineSocket({ socketPath, binary: nativeBinary, agentDir });
|
|
471
455
|
if (link.failure || shuttingDown) {
|
|
472
|
-
upstream.
|
|
456
|
+
upstream.destroy();
|
|
473
457
|
return;
|
|
474
458
|
}
|
|
459
|
+
upstream.on("data", (chunk: Buffer) => {
|
|
460
|
+
if (link.failure) return;
|
|
461
|
+
const text = link.decoder.decode(chunk, { stream: true });
|
|
462
|
+
let start = 0;
|
|
463
|
+
let at = text.indexOf("\n");
|
|
464
|
+
while (at >= 0) {
|
|
465
|
+
const line = `${link.buffer.join("")}${text.slice(start, at)}`.replace(/\r$/, "");
|
|
466
|
+
link.buffer = [];
|
|
467
|
+
if (line) receive(link, line);
|
|
468
|
+
start = at + 1;
|
|
469
|
+
at = text.indexOf("\n", start);
|
|
470
|
+
}
|
|
471
|
+
if (start < text.length) link.buffer.push(text.slice(start));
|
|
472
|
+
});
|
|
473
|
+
upstream.on("drain", () => {
|
|
474
|
+
link.upstreamBlocked = false;
|
|
475
|
+
flush(link);
|
|
476
|
+
});
|
|
477
|
+
upstream.on("close", () => fail(link, "omo_requester_disconnected"));
|
|
478
|
+
upstream.on("error", () => fail(link, "omo_requester_error"));
|
|
475
479
|
link.upstream = upstream;
|
|
476
480
|
flush(link);
|
|
477
|
-
} catch {
|
|
478
|
-
|
|
481
|
+
} catch (error) {
|
|
482
|
+
const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
483
|
+
fail(link, code === "auth_failed" || code === "auth_required" || code === "unsafe_key_record" ||
|
|
484
|
+
code === "host_identity_unproven" ? code : "omo_requester_unavailable");
|
|
479
485
|
} finally {
|
|
480
486
|
link.connecting = false;
|
|
481
487
|
}
|