Skip to content

Commit 5a15a97

Browse files
committed
feat: 재현 가능한 Node 게스트를 제품화한다
Node 22.22.0 게스트를 Python과 Linux의 공통 수명주기 및 서명 image 계약에 연결한다. V86 실행 자산을 engine 생성 전에 검증하고 공급망 증거와 장기 원장을 갱신한다. 검증: npm test, Edge와 Chrome 설치 제품 및 Web Computer와 V86 게이트
1 parent 99e7c91 commit 5a15a97

49 files changed

Lines changed: 2215 additions & 114 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,10 @@ jobs:
230230
env:
231231
PYPROC_BROWSER: /usr/bin/google-chrome
232232
PYPROC_GATE_TIMEOUT: "600000"
233+
# exact packed package가 Python, Linux, source-pinned Node를 한 signed computer로 옮기고
234+
# 변조 Node image를 active swap 전에 거절하는지 같은 x86 asset lane에서 반복한다.
235+
- run: npm run test:node-guest
236+
env:
237+
PYPROC_BROWSER: /usr/bin/google-chrome
238+
PYPROC_GATE_TIMEOUT: "900000"
239+
PYPROC_NODE_GUEST_HEADED: "0"

CHANGELOG.md

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,68 @@ happen only on an explicit maintainer decision; the Unreleased section accumulat
1414

1515
### Added
1616

17-
- **A reproducible data engine now ships beside the default core engine.** `pyproc/wasi` exposes its exact
18-
manifest and a profile-selected package-owned catalog. The static `pyproc.data/2` module executes float64
19-
buffer addition and dot products with `wasm-simd128`, and its verified facade survives process clone and
20-
Machine image revival. The exact data profile now also builds NumPy 2.5.1 from its official sdist as 13
21-
static modules, installs its verified Python layer from the package-owned multi-wheel catalog, and preserves
22-
it through process clone and Machine image revival. SciPy, pandas, Polars, and arbitrary native wheels remain
23-
explicitly unsupported.
17+
No unreleased public changes.
18+
19+
## 0.0.23 - 2026-08-16
20+
21+
### Added
22+
23+
- **The installed package is now the default engine entrance.** Root `open()` and `boot()`, `createWebComputer`,
24+
Control, and the package-owned engine catalog converge on one exact installed product without deep imports.
25+
- **Browser eyes and hands now carry bounded proof through document change.** Paged semantic inventory,
26+
durable locators, action convergence, occlusion handling, document replacement, 20-cycle resource cleanup,
27+
and a headed hardware receipt for compute and rendered pixels run through exact packed-product gates.
28+
- **A reproducible data engine ships beside the default core engine.** `pyproc/wasi` exposes its exact manifest
29+
and a profile-selected package-owned catalog. The static `pyproc.data/2` module executes float64 SIMD oracles.
30+
NumPy 2.5.1 is built from its exact sdist as 13 static modules and survives process clone and Machine image
31+
revival. The build-sealed thread capability reports the actual worker-process boundary. SciPy, pandas,
32+
Polars, shared-memory threads, and arbitrary native wheels remain explicit absences.
33+
- **Browser storage failures are explicit and recoverable where bytes still exist.** Persistence receipts,
34+
quota rollback, eviction witnesses, and OPFS generation recovery distinguish best-effort durability from
35+
silent first boot while preserving the last valid state.
36+
- **Source-pinned ripgrep and local Git are resident Machine tools.** Isolated WASI workers run ripgrep 15.1.0
37+
over bounded snapshots and libgit2 1.9.7 over compare-and-swap KernelVfs transactions. Main and cloned Python
38+
kernels call the same argv-only catalog through `pyprocTools` and receive the same receipts.
39+
- **An optional source-pinned Node guest joins Python and Linux in one Web Computer.** `createWebComputer({ node })`
40+
creates `nodeOs` and `nodeDisk`, verifies Node source identity and every declared V86 boot asset before engine
41+
construction, and carries all configured guests in one signed `.webmachine`. The exact packed product runs
42+
Node crypto and filesystem work, rejects a changed boot image before active replacement, and restores all
43+
three guests. A separate reproducible Buildroot release carries the image, source, configuration, SBOM,
44+
complete legal material, and two-build byte-identity receipt.
45+
46+
### Fixed
47+
48+
- Successful Control startup now clears its timeout timer immediately, so a completed product lifecycle does
49+
not wait for an obsolete process timer.
50+
- Chromium profile cleanup now waits for Windows pending deletion to converge and reports a structured failure
51+
if the exact temporary profile remains, closing a Chrome-only race after an otherwise clean 20-cycle run.
52+
- V86 inspection reports readiness only after the engine event. A pre-ready timeout no longer calls unsafe
53+
partial-instance stop or destroy methods, while a ready instance retains the full drain, flush, and destroy
54+
path.
55+
- V86 verified asset descriptors are closed and type-exact. The default Node loader accepts only same-origin
56+
credential-free URLs, refuses redirects and unbounded response bodies, enforces declared lengths, hashes
57+
independent bytes, and returns `WEB_MACHINE_ASSET_INTEGRITY` on mismatch.
58+
- `WebComputer.bootAll()` now rolls every partial guest back to `stopped` when one configured guest fails. If
59+
cleanup also fails, an `AggregateError` retains the original boot failure and every cleanup failure.
60+
- The TypeScript V86 block mode now names the implemented `ata` and `filesystem` values rather than stale names.
61+
- V86 text display accepts the engine's transient 256-row boot geometry and reports only genuinely invalid
62+
updates as errors, so a normal Linux boot returns `errors: 0` instead of accumulating clipped VGA cells.
63+
64+
### Compatibility
65+
66+
- The root remains exactly six exports and no package subpath or executable is added. The `node` option is
67+
additive on `createWebComputer`; `WEB_COMPUTER_MACHINE_IDS` now lists the optional `nodeOs` identifier.
68+
- The npm package retains zero runtime dependencies. V86, firmware, and optional guest images remain external,
69+
digest-pinned assets rather than bundled package bytes.
70+
- Existing Python-only and Python plus Linux Web Computers keep their machine IDs, device names, lifecycle,
71+
image format, and durability behavior.
72+
73+
한국어 요약: 설치 패키지를 엔진 기본 진입점으로 통일하고, 문서 교체와 가림을 다시 관찰해 안전하게
74+
수렴하는 브라우저 눈과 팔, 실제 hardware 결과 영수증, 무잔류 수명주기를 제품화했다. 별도 재현 data
75+
engine과 NumPy 2.5.1, 정확한 thread 경계, 저장소 quota와 축출 영수증, 상주 ripgrep과 local Git 및 같은
76+
Python 도구 영수증을 추가했다. 선택적 Node guest는 Python과 Linux 옆에서 같은 Web Computer, 독립 disk,
77+
서명 image 계약을 쓰며 source와 boot asset을 실행 전에 검증한다. npm package는 계속 runtime 의존성 0이고
78+
V86, firmware, guest image는 별도 digest-pinned 공급망으로 남는다.
2479

2580
## 0.0.22 - 2026-08-15
2681

README.ko.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,22 @@ try {
194194
storage를 조립한다. 기본 Python guest는 root `boot()``open()`이 쓰는 것과 같은 `KernelMachine`
195195
Machine image 계약을 사용한다.
196196

197+
선택적 Linux와 Node guest는 V86 constructor와 guest manifest를 주입하면 같은 lifecycle에 합류한다.
198+
Node manifest는 정확한 runtime version, source revision, source URL, source SHA-256을 밝히고, boot image의
199+
byte length와 SHA-256을 기술해야 한다. pyproc은 emulator를 만들기 전에 그 byte를 가져와 검증하고 `inspect()`에 검증 영수증을
200+
남긴다. 변조된 image는 가져온 Machine이 active computer를 교체하기 전에
201+
`WEB_MACHINE_ASSET_INTEGRITY`로 거절한다. Linux와 Node manifest는 BIOS, kernel image, VGA BIOS를 모두
202+
같은 검증 descriptor 경로로 선언할 수 있다.
203+
guest마다 독립 block device를 받고, signed `.webmachine`
204+
봉투는 설정된 guest 전체를 함께 나른다. V86, firmware, 선택적 guest image는 npm package에 포함하지
205+
않는다. `bootAll()`은 전체 수렴 계약이다. 설정된 guest 하나라도 실패하면 모든 boot 시도의 결론을
206+
기다린 뒤 부분 부팅된 guest를 전부 종료하고 거절한다.
207+
208+
버전이 붙은 외부 자산 정본은 [`scripts/assetCatalog.json`](scripts/assetCatalog.json)이다. exact packed
209+
기준 여정은 [Node guest 제품 gate](https://github.com/eddmpython/pyproc/blob/main/tests/browser/nodeGuestProduct.mjs)에 있고, 전체 manifest,
210+
source identity, permission, inspect 계약은 [API reference](skills/reference-pyproc-api/references/api.md)
211+
`createWebComputer` 절에 있다.
212+
197213
## Package subpath
198214

199215
| Subpath | 계약 |

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,22 @@ try {
213213
and optional durable storage. The default Python guest uses the same `KernelMachine` and Machine image
214214
contract as root `boot()` and `open()`.
215215

216+
Optional Linux and Node guests join that same lifecycle when a V86 constructor and guest manifest are supplied.
217+
A Node manifest must name the exact runtime version, source revision, source URL, and source SHA-256, and must
218+
describe its boot image with byte length and SHA-256. pyproc fetches and verifies those bytes before constructing the emulator, reports the
219+
verified asset through `inspect()`, and rejects a changed image with `WEB_MACHINE_ASSET_INTEGRITY` before an
220+
imported Machine can replace the active one. Linux and Node manifests can declare the BIOS, kernel image, and
221+
VGA BIOS through the same verified descriptor path.
222+
Each guest receives its own block device while the signed
223+
`.webmachine` envelope carries all configured guests together. V86, firmware, and optional guest images are not
224+
bundled into the npm package. `bootAll()` is all-or-clean: if any configured guest fails, it waits for every boot
225+
attempt and shuts down every partial guest before rejecting.
226+
227+
The versioned external-asset SSOT is [`scripts/assetCatalog.json`](scripts/assetCatalog.json). The exact packed
228+
reference journey is [the Node guest product gate](https://github.com/eddmpython/pyproc/blob/main/tests/browser/nodeGuestProduct.mjs), and the complete
229+
manifest, source identity, permission, and inspection contract is documented under `createWebComputer` in the
230+
[API reference](skills/reference-pyproc-api/references/api.md).
231+
216232
## Package subpaths
217233

218234
| Subpath | Contract |

apps/webComputer/assetCatalog.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"schemaVersion": 1,
33
"catalogId": "web-computer-development-assets-v1",
4-
"createdAt": "2026-07-19T00:00:00Z",
4+
"createdAt": "2026-08-16T00:00:00Z",
55
"channel": "development",
66
"redistribution": "disabled",
77
"promotionRequires": [

apps/webComputer/assetProvenance.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ export const WEB_COMPUTER_ASSET_PROVENANCE = Object.freeze({
77
policyVersion: 3,
88
catalogId: "web-computer-development-assets-v1",
99
sourceCatalogId: "web-machine-execution-assets-v1",
10-
sbomDigest: "sha256:a6f1fc94c08b75b6c709270445e6f3145636fd8216f840bf51adede8a91ac98f",
10+
sbomDigest: "sha256:e4617058b174d40cc08554c1d2804ba4b926b78731456ea00056068f07f2a0c8",
1111
});
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# 긴 페이지 screenshot 경계 오류 정규화
2+
3+
## 접수 판정
4+
5+
- 관찰한 호출 제품과 목적: eddmpython의 전체 블로그 시각 검수에서 긴 글의 처음부터 푸터까지 증거를
6+
남기려 했다
7+
- pyproc 소유라고 판정한 근거: 정확히 고정한 `pyproc@0.0.21`의 공개 JavaScript Control SDK에서
8+
`screenshot` action을 호출했고, 문서가 약속한 `BROWSER_AUTOMATION_SCREENSHOT_BOUNDS` 대신
9+
Chromium 원문 오류가 노출됐다
10+
- 검색한 중복 후보와 차이: `mainPlan`, `skills`, `tests/attempts`, Git 이력에서 screenshot과 bounds를
11+
검색했다. 크기 제한과 오류 코드는 문서에 있지만 실제 content bounds 초과를 재현한 이니셔티브는 없다
12+
- 현재 직렬 대기열 위치: `9-agentComputerStandardReadiness` 다음인 10번 대기열이다
13+
14+
## 정확한 환경
15+
16+
- 관찰 시각과 시간대: 2026-08-16 00:16부터 00:32, Asia/Seoul
17+
- 소비 저장소 revision: `f3a9012fa7b184d7fcbe48e2cf56b63ca27ba958`, 작업 중인 블로그 변경 포함
18+
- 해석된 pyproc 버전, 패키지 무결성 또는 소스 SHA: npm `pyproc@0.0.21`, integrity
19+
`sha512-Sv+1mlGW+VJ/utmSutBycjP/U0jodl/cZdwW31R/ECq4HyMIBHtIOjFiKVMscueV6q56yZgFSWyGEs+g7HnZJg==`
20+
- 운영체제, 브라우저 이름과 정확한 버전, 런타임 버전: Windows 11 Home 10.0.26200,
21+
Microsoft Edge 151.0.4129.86, Node.js는 소비 저장소의 현재 실행 환경
22+
- 사용한 공개 명령 또는 공개 API: `PyProcControlClient.start`, `openTarget`, `attachSession`,
23+
`client.act([{ kind: "screenshot", fullPage: true }])`, `Runtime.evaluate`, `deleteArtifact`
24+
- 권한 manifest와 관련 설정의 비밀 제거 요약: `nativeCdp`, 로컬 합성 페이지 origin 하나,
25+
screenshot과 `Runtime.evaluate`, viewport 1440 x 1000 및 390 x 844, 외부 효과 승인
26+
27+
## 목적과 시작 상태
28+
29+
- 사용자가 달성하려던 결과: 독립형 긴 글의 데스크톱과 모바일 전체 화면을 실제 픽셀로 검수한다
30+
- 대상 URL 범위, viewport, 페이지 상태, 사전 조건: 로컬 프로덕션 빌드의 합성 블로그 글 한 개,
31+
document height 37,894 CSS px와 41,605 CSS px, 폰트와 이미지 로딩 완료 상태
32+
- 읽기, 외부 효과, 비가역 작업의 승인 경계: 로컬 페이지 탐색과 lazy hydration만 외부 효과로 승인했다.
33+
screenshot은 읽기이며 artifact는 소비 즉시 삭제했다
34+
35+
## 실행 기록
36+
37+
| 순서 | 목적 | 실행한 공개 명령 또는 API | 입력 경계 | 관찰 결과 | 증거 |
38+
|---|---|---|---|---|---|
39+
| 1 | 긴 페이지 전체 증거 | `screenshot` action | `fullPage: true`, 두 viewport | 두 경우 모두 raw CSS bounds 오류 | report SHA-256 `56D7DC83774E5CD41E17A17476D7BC796E6F036CBF9BC7E46BEBB37AFFE4194B` |
40+
| 2 | 절대 clip 우회 확인 | `screenshot` action | 12,000px 높이 clip을 y축으로 이동 | y축 상단이 한계를 넘자 raw clip 오류 | report SHA-256 `88D271F73E031AACCCA358493A05EE64D9C42D2055A7A8CBD357398D175B35FA` |
41+
| 3 | viewport scroll 우회 확인 | `Runtime.evaluate`, `screenshot` action | viewport 단위 scroll 뒤 일반 screenshot | 데스크톱 38장, 모바일 50장으로 통과 | report SHA-256 `40F29E05D19F1FC30F3828CF41724E325AF9D8FDA45964C477483B98F5DF2E7D` |
42+
43+
## 첫 불일치
44+
45+
- 처음 기대와 달라진 단계: 첫 `fullPage: true` screenshot action
46+
- 기대 결과: content height가 문서의 32,768 CSS px 제한을 넘으면
47+
`BROWSER_AUTOMATION_SCREENSHOT_BOUNDS`로 측정값과 제한을 반환한다
48+
- 실제 결과: `browser screenshot content height is outside the supported CSS bounds`라는 provider 원문이
49+
action 오류로 노출됐다
50+
- 반환 코드, 오류 계약, 완료된 효과의 범위: 문서화한 오류 코드와 측정 metadata가 없었다.
51+
screenshot artifact는 생성되지 않았고 앞선 상단 screenshot만 완료됐다
52+
53+
## 재현성
54+
55+
- 최소 합성 재현 fixture 또는 절차: 높이 33,000 CSS px를 넘는 정적 문서를 열고
56+
`client.act``fullPage: true` screenshot을 호출한다
57+
- 반복 횟수와 성공, 실패 횟수: fullPage는 두 viewport에서 0회 성공, 2회 실패했다.
58+
절대 clip 우회도 두 viewport에서 0회 성공, 2회 실패했다
59+
- 브라우저 재시작, 새 profile, viewport 등 바꿔 본 조건: viewport마다 Control client와 profile을 새로
60+
시작했고 1440 x 1000 및 390 x 844에서 같은 종류의 실패를 확인했다
61+
- 재현하지 못한 조건과 남은 불확실성: content height가 32,768 CSS px 이하인 페이지는 같은 설치본에서
62+
통과했다. 정확한 Chromium 내부 최대값과 device scale별 차이는 별도 probe가 필요하다
63+
64+
## 증거
65+
66+
- 비식별화한 로그 또는 artifact 경로와 digest: 소비 저장소 밖 `visual/<run-id>/visual-report.json` 세 개의
67+
SHA-256을 실행 기록에 남겼다. 원본 페이지와 screenshot은 반입하지 않았다
68+
- 스크린샷의 대상 상태, viewport, 촬영 단계와 digest: 실패 실행은 상단 화면 뒤 전체 화면 단계에서
69+
중단됐다. 성공 우회는 1440 x 1000과 390 x 844 viewport의 순서 있는 화면 조각을 남겼다
70+
- DOM, 네트워크, console, 실행 receipt 가운데 판정에 사용한 것: document scrollHeight, viewport,
71+
action 오류 문자열, 생성된 screenshot 수, report digest를 사용했다
72+
- 증거가 입증하는 범위와 입증하지 못하는 범위: 공개 action이 긴 content bounds에서 문서화한 오류로
73+
정규화되지 않는다는 점을 입증한다. Chromium 버전 전체의 물리 상한은 입증하지 않는다
74+
75+
## 시도한 대응
76+
77+
- 시도한 진단과 변경: 12,000px 절대 clip으로 나눈 뒤, 실제 viewport를 scroll하고 일반 screenshot을
78+
찍는 방식으로 바꿨다
79+
- 각 시도의 결과: 절대 clip은 y축 좌표가 CSS bounds를 넘어 실패했고 viewport scroll은 두 viewport에서
80+
마지막 푸터까지 통과했다
81+
- 소비 저장소 우회가 근본 해결이 아닌 이유: 호출자가 브라우저별 물리 상한을 추측해야 하고 raw provider
82+
오류에서 재시도 가능성과 권장 분할 크기를 복원해야 한다
83+
84+
## 영향과 안전 경계
85+
86+
- 영향을 받는 제품 흐름과 빈도: 긴 문서, 보고서, 무한 목록을 전체 screenshot으로 검수하는 모든
87+
Native CDP 소비 흐름에서 content height가 상한을 넘을 때 발생한다
88+
- 심각도와 사용자가 보게 되는 실패: 시각 증거가 중간에서 빠지고 문서화되지 않은 문자열에 의존하게 되는
89+
중간 심각도의 검수 실패다
90+
- 데이터, 권한, 외부 효과, 재시도 위험: screenshot 자체는 읽기다. scroll 기반 대응은 lazy loader와
91+
observer를 실행할 수 있으므로 별도 외부 효과 승인이 필요하다
92+
- 기존 호환성 또는 공개 표면에 미치는 영향: 기존 action 입력은 유지할 수 있고 실패 오류의 정규화만으로
93+
최소 호환 개선이 가능하다
94+
95+
## 제안하는 pyproc 계약
96+
97+
- pyproc이 소유해야 할 동작: `fullPage`의 실제 content bounds를 CDP capture 전에 측정하고 제한을 넘으면
98+
`BROWSER_AUTOMATION_SCREENSHOT_BOUNDS`에 측정 width, height, 최대 dimension, 최대 area를 담는다.
99+
provider 원문은 공개 오류 message의 정본이 되지 않는다
100+
- 공개 표면 변경 여부와 비목표: 새 root export 없이 기존 action 오류 detail을 보강한다.
101+
자동 scroll과 여러 artifact 반환은 한 action이 한 screenshot을 반환하는 계약을 바꾸므로 이번 최소 수정의
102+
비목표다
103+
- 가장 작은 수용 테스트와 음성 시험: 32,768 CSS px 이하 fixture는 한 artifact로 GREEN,
104+
32,769 CSS px fixture는 정확한 code와 측정 detail로 RED가 되어야 한다. raw provider 오류가 노출되면
105+
음성 시험이 실패해야 한다
106+
- 브라우저 실측 시나리오: Windows Edge와 CI Chromium에서 짧은 문서, dimension 초과 문서,
107+
area 초과 문서, absolute clip의 x와 y 초과를 각각 public Control 설치본으로 실행한다
108+
- 완료 조건, 지속 문서 승격 위치, 계획과 attempt 삭제 조건: contract test와 installed browser gate가
109+
통과하고 browser automation troubleshooting에 detail schema와 scroll 대응 경계를 반영한다.
110+
완료와 같은 사이클에 이 계획과 대응 attempt를 삭제한다
111+
112+
## 다음 행동
113+
114+
- 선행 조건: 9번 이니셔티브의 현재 직렬 작업을 끝낸다
115+
- 첫 probe: 32,767px부터 32,769px까지 합성 문서와 scale 및 area 조합을 설치본에서 측정한다
116+
- 예상 수정 소유 영역: Native CDP screenshot bounds 정규화, action error detail, contract 및 installed
117+
browser gate, browser automation reference

0 commit comments

Comments
 (0)