From f106def875b06404a8a952f18f49a6e32d3df675 Mon Sep 17 00:00:00 2001 From: YEONGUK LEE Date: Mon, 12 Oct 2026 10:21:42 +0900 Subject: [PATCH] chore(release): prepare 0.3.4 stable patch --- PARITY.md | 10 ++- README.ja.md | 14 ++-- README.ko.md | 14 ++-- README.md | 14 ++-- README.zh-CN.md | 14 ++-- RELEASING.md | 18 ++++ VALIDATION.md | 16 ++++ docs/api.md | 8 +- docs/design/typescript-skill-compatibility.md | 2 +- docs/guide.ja.md | 30 +++---- docs/guide.ko.md | 34 ++++---- docs/guide.md | 33 ++++---- docs/guide.zh-CN.md | 30 +++---- docs/shorthand.md | 2 +- package-lock.json | 4 +- package.json | 2 +- scripts/release.mjs | 8 +- src/protocol.ts | 2 +- src/skill-metadata.ts | 2 +- test/integration-multihost-updates.test.ts | 14 ++-- test/release.test.ts | 16 ++-- test/replies.test.ts | 2 +- test/reply-agent-binding.test.ts | 2 +- test/skill-metadata.test.ts | 6 +- test/ssh-hosts.test.ts | 4 +- test/transport.test.ts | 8 +- test/updates.test.ts | 84 +++++++++---------- 27 files changed, 216 insertions(+), 177 deletions(-) diff --git a/PARITY.md b/PARITY.md index a0fba57..70be06e 100644 --- a/PARITY.md +++ b/PARITY.md @@ -1,8 +1,8 @@ # Python compatibility and npm migration -Updated 2026-10-09 KST. Python reference: **1.0.2**, +Updated 2026-10-12 KST. Python reference: **1.0.2**, [`47c2371`](https://github.com/abruption/session-peer/tree/47c23713d0a2a3c11ebde6186afd8c43489b8b65/session_peer_core). -This matrix describes the **TS 0.3.3 source/package contract**: the 0.2.0 unified listing, safe home selection, +This matrix describes the **TS 0.3.4 source/package contract**: the 0.2.0 unified listing, safe home selection, read-only doctor, text/help/positional input, Unicode 14.0.0 name matching and separately managed TS skill guidance (#16/#17/#18/#19/#25), the [0.2.1 reliability and hardening](#021-reliability-and-hardening) changes, and the @@ -15,6 +15,12 @@ between releases, operators should also align reviewed commits; the wire does no verify a source commit. `referenceVersion: "1.0.2"` identifies the comparison baseline, not complete parity. See [VALIDATION.md](VALIDATION.md) for dated evidence. +## 0.3.4 patch preparation — 2026-10-12 KST + +[PR #134](https://github.com/abruption/session-peer-ts/pull/134) corrects Windows-destination PowerShell forwarding of native exit codes for version preflight and wire requests. A missing native exit fails with 1; complete validated abnormal responses retain independent submission facts. Strict response limits, exact-version SSH, host-key checks and no automatic resend remain unchanged. This is not delivery or ACK evidence. + +The four guides pin independently published TS skill 0.2.0 source `8482cf644507c682915e35c85aa3384c53864906`. The 0.3.4 candidate accepts only the same two exact reviewed profiles as public 0.3.3; 0.3.5 and unreviewed versions remain incompatible. Python reference 1.0.2 is unchanged. Draft 0.4 handoff/app-server/observation and Side Session are excluded. + ## Feature and command matrix **Implemented** means the stated bounded behavior exists. **Partial** means a diff --git a/README.ja.md b/README.ja.md index 49703d9..aa21058 100644 --- a/README.ja.md +++ b/README.ja.md @@ -15,7 +15,7 @@ -**文書・ソースのバージョン: 0.3.3。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](VALIDATION.md)、リリース手順は [RELEASING.md](RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.2 と 0.3.3 は互いの SSH 事前確認を拒否します。 +**文書・ソースのバージョン: 0.3.4。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](VALIDATION.md)、リリース手順は [RELEASING.md](RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.3 と 0.3.4 は互いの SSH 事前確認を拒否します。 **実行中の Claude Code・Codex セッションを見つけ、ローカルまたは SSH でメッセージを送信します。** Python 不要の Node.js クライアントです。パッケージ名とコマンド名は `session-peer` です。 @@ -56,15 +56,15 @@ SSH は既存の鍵・ホスト信頼設定と両端の**同じバージョン `Get-Command session-peer -All` で PATH を確認してください。利用する実装を選び、 別の管理ツールのファイルを `--force` で上書きしないでください。 -**0.3.3 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](docs/guide.ja.md#ソースからビルド)を使います。 +**0.3.4 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](docs/guide.ja.md#ソースからビルド)を使います。 ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` -期待する出力: `session-peer 0.3.3 (typescript)`。分離インストール・ソースビルド・ +期待する出力: `session-peer 0.3.4 (typescript)`。分離インストール・ソースビルド・ Windows・削除の手順は以下の詳細ガイドを参照してください。 任意の `sp` 短縮名は 0.3.0 以降に含まれ(0.2.1 以前にはありません)、自動では有効になりません。 @@ -73,12 +73,12 @@ Windows・削除の手順は以下の詳細ガイドを参照してください ### Update npm で導入したクライアントは npm で更新します。タグと対象バージョンを確認してから -正確なバージョンを指定してください。以下は古い npm 版を公開済みの 0.3.3 に更新する例です。 +正確なバージョンを指定してください。以下は古い npm 版を選択した 0.3.4 に更新する例です。 CLI は更新をインストールせず、`session-peer update --check` は確認のみ行います。 ```sh npm view session-peer dist-tags -npm install --global --ignore-scripts session-peer@0.3.3 +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` diff --git a/README.ko.md b/README.ko.md index 6b8773c..a31bb6c 100644 --- a/README.ko.md +++ b/README.ko.md @@ -20,7 +20,7 @@ Python을 설치하지 않아도 Node.js에서 실행되는 TypeScript 클라이 -**문서·소스 기준 버전: 0.3.3.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](VALIDATION.md), 릴리스 절차는 [RELEASING.md](RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.2와 0.3.3은 서로의 SSH 사전 확인을 거부합니다. +**문서·소스 기준 버전: 0.3.4.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](VALIDATION.md), 릴리스 절차는 [RELEASING.md](RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.3과 0.3.4는 서로의 SSH 사전 확인을 거부합니다. ## 시연 @@ -65,15 +65,15 @@ PowerShell에서는 `Get-Command session-peer -All`로 PATH(명령 검색 경로 실행 파일을 찾는지 확인하세요. 사용할 구현을 선택하고 다른 패키지 관리자가 설치한 파일을 `--force`로 덮어쓰지 마세요. 설치와 갱신은 사용자가 npm으로 직접 수행합니다. -**0.3.3 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](docs/guide.ko.md#소스에서-빌드)를 사용하세요. +**0.3.4 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](docs/guide.ko.md#소스에서-빌드)를 사용하세요. ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` -예상 결과는 `session-peer 0.3.3 (typescript)`입니다. 별도 경로에 설치하거나, +예상 결과는 `session-peer 0.3.4 (typescript)`입니다. 별도 경로에 설치하거나, 소스에서 빌드하거나, Windows에서 설치·제거하는 방법은 [사용자 가이드](docs/guide.ko.md#설치)를 참고하세요. 선택 사항인 `sp` 단축 명령은 0.3.0 이상에 포함되며(0.2.1 이하에는 없음) 자동으로 @@ -84,13 +84,13 @@ session-peer --version ### 업데이트 npm으로 설치한 클라이언트는 사용자가 직접 npm으로 업데이트합니다. 사용 가능한 배포 태그 -(dist-tag)와 대상 버전을 확인한 뒤 해당 버전을 지정해 설치하세요. 아래 예시는 이전 npm 설치본을 공개된 0.3.3으로 +(dist-tag)와 대상 버전을 확인한 뒤 해당 버전을 지정해 설치하세요. 아래 예시는 이전 npm 설치본을 선택한 0.3.4로 업데이트합니다. `session-peer` CLI는 업데이트를 설치하지 않습니다. `session-peer update --check`는 새 버전이 있는지 확인할 뿐입니다. ```sh npm view session-peer dist-tags -npm install --global --ignore-scripts session-peer@0.3.3 +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` diff --git a/README.md b/README.md index e233562..28fa041 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ -**Documentation and source version: 0.3.3.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](VALIDATION.md); release procedures are in [RELEASING.md](RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.2 and 0.3.3 refuse each other’s SSH preflight. +**Documentation and source version: 0.3.4.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](VALIDATION.md); release procedures are in [RELEASING.md](RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.3 and 0.3.4 refuse each other’s SSH preflight. **Find and message running Claude Code and Codex sessions, locally or over SSH.** A Node.js client that runs without Python. Package and command: `session-peer`. @@ -56,15 +56,15 @@ Check `type -a session-peer` on macOS/Linux or `Get-Command session-peer -All` in PowerShell before installing. Choose the intended PATH entry; do not use `--force` to overwrite another manager’s files. -**Version 0.3.3 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](docs/guide.md#build-from-source). +**Version 0.3.4 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](docs/guide.md#build-from-source). ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` -Expected: `session-peer 0.3.3 (typescript)`. For isolated installation, source +Expected: `session-peer 0.3.4 (typescript)`. For isolated installation, source builds, Windows and removal, see the detailed guide below. Optional `sp` shorthand ships with 0.3.0 and later (not 0.2.1 or earlier) and is @@ -74,12 +74,12 @@ never enabled automatically. See [explicit activation and collisions](docs/short Use npm for an npm-managed installation. Check the available tags and review the target version, then install that exact version; this example updates an -older npm installation to the published 0.3.3 release. The CLI does not install updates; +older npm installation to the selected 0.3.4 release. The CLI does not install updates; `session-peer update --check` only reports them. ```sh npm view session-peer dist-tags -npm install --global --ignore-scripts session-peer@0.3.3 +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` diff --git a/README.zh-CN.md b/README.zh-CN.md index 7d9d042..1c3ba09 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -15,7 +15,7 @@ -**文档和源码版本:0.3.3。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](VALIDATION.md),发布流程见 [RELEASING.md](RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.2 与 0.3.3 会拒绝彼此的 SSH 预检。 +**文档和源码版本:0.3.4。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](VALIDATION.md),发布流程见 [RELEASING.md](RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.3 与 0.3.4 会拒绝彼此的 SSH 预检。 **发现正在运行的 Claude Code 和 Codex 会话,并在本机或通过 SSH 发送消息。** 这是不需要 Python 的 Node.js 客户端。包名与命令名均为 `session-peer`。 @@ -54,15 +54,15 @@ session-peer send --to CLAUDE_PID --message '请检查 API 契约并回复。' - `Get-Command session-peer -All` 检查 PATH。选择所需实现,不要用 `--force` 覆盖其他安装管理器的文件。 -**安装 0.3.3:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](docs/guide.zh-CN.md#从源码构建)。 +**安装 0.3.4:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](docs/guide.zh-CN.md#从源码构建)。 ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` -预期输出:`session-peer 0.3.3 (typescript)`。隔离安装、源码构建、Windows 和 +预期输出:`session-peer 0.3.4 (typescript)`。隔离安装、源码构建、Windows 和 卸载步骤见下方详细指南。 可选的 `sp` 简写包含在 0.3.0 及更高版本中(0.2.1 及更早版本没有),不会自动启用。 @@ -71,11 +71,11 @@ session-peer --version ### Update 使用 npm 更新由 npm 管理的安装。先检查标签并审阅目标版本,再安装指定版本。 -下面示例将旧 npm 安装更新至已公开发布的 0.3.3。CLI 不会安装更新,`session-peer update --check` 只做检查。 +下面示例将旧 npm 安装更新至所选的 0.3.4。CLI 不会安装更新,`session-peer update --check` 只做检查。 ```sh npm view session-peer dist-tags -npm install --global --ignore-scripts session-peer@0.3.3 +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` diff --git a/RELEASING.md b/RELEASING.md index 7c396ed..b5265d0 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,6 +9,24 @@ the historical 0.3.2 settings-read exception is recorded below. The workflow uses OIDC and no npm token. The protected GitHub environment requires a reviewer and permits deployments from `main` only. +## 0.3.4 stable preparation — 2026-10-12 KST + +The patch integrates [PR #134](https://github.com/abruption/session-peer-ts/pull/134), merged as `c210ee963741f77153df10038c28e228ea7a902f`: Windows native exit forwarding and independently published TS skill guidance. Draft #106–#109 and #82 are excluded. Preparation is not staging or publication approval. + +Read-only registry baseline at this checkpoint: `latest=0.3.3`, `preview=0.1.0-preview.1`, and 0.3.4 absent. Previous stable integrity: +`sha512-5OQ7eUlR5Oy3RHFRoR3FuBkV7LT72dfjfBEbWRCY5wYP7Dz8TmN2jlRPpo2SFHPAwosFc0WIt8Iis/vyHckxRg==`. +Previous stable attestation metadata: `https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.3.3`. +The immutable public 0.3.3 source is `c0ed30273b9e9c0049df86ae3c38486a94289527`. Historical records below are retained. + +1. Review and merge this preparation, then require successful exact-main CI/release gate, CodeQL and Windows Node 22/24 for the resulting 40-character main SHA. +2. Fresh authenticated reads must verify ownership, pending npm stages, stage-only Trusted Publisher mapping/permissions, package MFA and protected GitHub npm environment/reviewer/main-only restrictions. Recheck registry tags, previous integrity, provenance content/source and target absence. This preparation performs no authenticated publication settings audit; prior approvals/exceptions do not carry forward. +3. Build and validate the exact-source archive, reproducible packing, isolated installation/removal and every packaged Markdown document. Apply the [artifact documentation gate](#documentation-gate-for-the-next-release); installation examples and runtime/banner/archive must agree on 0.3.4. Preserve historical/fixture and real-effect evidence boundaries. +4. Obtain separate approval naming the exact merged main SHA, `session-peer@0.3.4`, `stable-stage` and eventual `latest` promotion to 0.3.4. +5. Only then dispatch `publish.yml` with `source_sha=`, `version=0.3.4`, `mode=stable-stage`, `confirmation=session-peer@0.3.4 stable-stage`. Review the retained Actions artifact before protected environment approval. Verify staged bytes and provenance, then obtain separate final npm 2FA approval. Never retry an uncertain upload or broaden publisher permissions. +6. After publication, verify public integrity/provenance/signatures and isolated version/install/removal, and append a dated public record. Leave preview unchanged. Tags/GitHub Releases/fleet installation require their own decisions. + +SSH endpoints must both use exactly 0.3.4 when testing this package; public 0.3.3 refuses its banner. Managers/operators own upgrades. No implicit provisioning, postinstall, automatic resend, delivery/ACK support or 0.4 feature promotion is introduced. TS skill metadata/full0.3.2, published pin8482cf6 and Python1.0.2 reference remain independent and unchanged. + ## 0.3.3 candidate preparation — #128 (2026-10-09 KST) This dated checkpoint records preparation only. The candidate integrated reviewed diff --git a/VALIDATION.md b/VALIDATION.md index 8fd2a52..0aa303e 100644 --- a/VALIDATION.md +++ b/VALIDATION.md @@ -1,5 +1,21 @@ # Validation record — updated 2026-10-12 KST +## 0.3.4 preparation checkpoint — 2026-10-12 KST + +Base main: `c210ee963741f77153df10038c28e228ea7a902f`, merged [PR #134](https://github.com/abruption/session-peer-ts/pull/134). Exact base main CLI [38191285491](https://github.com/abruption/session-peer-ts/actions/runs/38191285491) and CodeQL [38191285293](https://github.com/abruption/session-peer-ts/actions/runs/38191285293) completed successfully, including Windows Node 22/24. Draft 0.4 PR #106–#109 and experimental #82 are excluded. This is preparation, not approval to stage or publish. + +Candidate source/package/banner/installation examples are 0.3.4. The registry read at this checkpoint was `latest=0.3.3`, `preview=0.1.0-preview.1`, 0.3.4 absent; previous stable integrity is the public value recorded below. This read does not establish authenticated pending stages, package MFA or Trusted Publisher settings. + +Local macOS arm64, Node 24.16.0/npm 11.15.0: + +- `npm ci --ignore-scripts`, `npm run build`, `npm run test:types`: PASS. +- `npm test`: 190 total, 185 PASS, 0 FAIL, 5 Windows-only SKIP. The first preparation run had 183 PASS/2 FAIL/5 SKIP because two update-notice regexes retained the old current/latest pair. Updating those expectations to 0.3.4/0.3.5 produced the successful final run; no production logic was changed to satisfy them. +- Python conformance uses the existing verified `47c23713d0a2a3c11ebde6186afd8c43489b8b65` reference. The Python1.0.2 parity pin is unchanged. +- `npm run test:package`: PASS, reproducible package and isolated installation/removal, bash/zsh shorthand contract and lifecycle. No global installation. +- `node scripts/check-repository.mjs`, `git diff --check`: PASS. `npm audit`: zero vulnerabilities. + +The physical Windows 24-case synthetic fixture evidence below belongs to the PR #134 correction at version 0.3.3, not to a new physical 0.3.4 installation, live queue/injection or ACK experiment. Preparation will additionally validate its exact committed artifact and current-head CI before review completion; their source/hash results belong in the PR record and are not fabricated here. Publication/environment approval/final npm2FA/tag/fleet operations are not performed by these checks. + ## Public 0.3.3 checkpoint — rechecked 2026-10-12 KST The earlier candidate/preparation sections below are dated history, not the diff --git a/docs/api.md b/docs/api.md index 278999c..87600d1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,18 +1,18 @@ # API reference -This reference describes source/package version **0.3.3 candidate (unpublished at the 2026-10-09 KST preparation checkpoint)**. Public npm `latest` verified at that checkpoint is **0.3.2**, with unchanged `preview=0.1.0-preview.1`. The 0.3.3 install example is conditional on separately approved publication; check `npm view session-peer@0.3.3 version` first. Before publication, use the reviewed candidate source artifact. +This reference describes source/package version **0.3.4**. Check registry availability and review the selected release before installation. Dated release evidence is recorded in [VALIDATION.md](../VALIDATION.md); before publication use a reviewed source artifact. [Back to README](../README.md) · [User guide](guide.md) The package exposes a small ESM API with TypeScript declarations. Install it -locally with `npm install --ignore-scripts session-peer@0.3.3` before importing it. +locally with `npm install --ignore-scripts session-peer@0.3.4` before importing it. Importing the package does not execute the CLI or submit a message. ```js import { VERSION, VERSION_LINE, reply, envelope } from 'session-peer'; -console.log(VERSION); // 0.3.3 -console.log(VERSION_LINE); // session-peer 0.3.3 (typescript) +console.log(VERSION); // 0.3.4 +console.log(VERSION_LINE); // session-peer 0.3.4 (typescript) const address = 'session-peer://v1/reply?agent=claude&session=12345&transport=local'; console.log(reply(address)); // { to: '12345' } console.log(envelope('Please review and reply.', true, address)); diff --git a/docs/design/typescript-skill-compatibility.md b/docs/design/typescript-skill-compatibility.md index 7f1a1e9..b3af2cd 100644 --- a/docs/design/typescript-skill-compatibility.md +++ b/docs/design/typescript-skill-compatibility.md @@ -81,4 +81,4 @@ The dated proposal/review above preceded publication. Public npm 0.3.3 uses sour Final metadata is skill `0.2.0`, implementation `typescript`, minimum `0.1.0`, full `0.3.2`, policy `probe-help`. Four-language guidance now pins that published source. This publication checkpoint does not turn `metadata_only` into instruction authenticity, execution authority, full platform qualification, consumption or ACK. Python metadata and the Python 1.0.2 parity/reference pin remain unchanged. -The explicit 0.3.4 candidate row preserves both exact published profiles for a future patch version. No runtime version bump, skill metadata change, new skill publication, installation or future-range compatibility is included. +The explicit 0.3.4 candidate row preserves both exact published profiles for a future patch version. The subsequent 0.3.4 release preparation bumps the candidate runtime version only. No skill metadata change, new skill publication, installation or future-range compatibility is included. diff --git a/docs/guide.ja.md b/docs/guide.ja.md index 1905f8b..8bd935a 100644 --- a/docs/guide.ja.md +++ b/docs/guide.ja.md @@ -4,13 +4,13 @@ [English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md) -**文書・ソースのバージョン: 0.3.3。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](../VALIDATION.md)、リリース手順は [RELEASING.md](../RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.2 と 0.3.3 は互いの SSH 事前確認を拒否します。 +**文書・ソースのバージョン: 0.3.4。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](../VALIDATION.md)、リリース手順は [RELEASING.md](../RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.3 と 0.3.4 は互いの SSH 事前確認を拒否します。 -0.3.3 の詳細ガイドです([0.3.1 の信頼性の修正](../PARITY.md#031-reliability-fixes))。0.3.0 は複数ホスト SSH、送信者情報と返信経路、更新確認、任意の `sp` 短縮名を追加しました([0.3.0 の変更点](../PARITY.md#030-remote-usability-and-maintenance))。以前の公開記録は [0.3.1 公開リリースの証拠](../VALIDATION.md#public-031--2026-10-04-kst) にあります。以下の 0.1.0 の説明は旧版との比較です。 +0.3.4 の詳細ガイドです([0.3.1 の信頼性の修正](../PARITY.md#031-reliability-fixes))。0.3.0 は複数ホスト SSH、送信者情報と返信経路、更新確認、任意の `sp` 短縮名を追加しました([0.3.0 の変更点](../PARITY.md#030-remote-usability-and-maintenance))。以前の公開記録は [0.3.1 公開リリースの証拠](../VALIDATION.md#public-031--2026-10-04-kst) にあります。以下の 0.1.0 の説明は旧版との比較です。 -## 移行時の境界 (0.3.3) +## 移行時の境界 (0.3.4) [0.3.2 の公開記録](../VALIDATION.md#public-032--2026-10-07-kst)はリリースのソースと成果物を固定しています。SSH の事前確認は TS のバージョン表示の完全一致を検査しますが、コミットハッシュは検査しません。ソースビルドのレビュー済みコミットは運用者が別途揃える必要があります。 @@ -19,7 +19,7 @@ - `--host` を繰り返すと要求順の配列を返します。ホスト別の実行中の失敗は終了コード 1 に集約しますが、実行前のコマンド全体の拒否(重複ホストや空のメッセージなど)は配列出力でも終了コード 2 を維持します。公開 0.3.2 は SSH の終了 255 やタイムアウト後に完全な応答の証拠も失う場合があります。0.3.3 は要求に一致する完全な事実を保持し、ローカル終了コード 1 と `sshTransport` を返します。不完全・不正な証拠は unknown のままです。unknown の場合は自動再送しません。 - 0.2.0 で導入した基本 doctor は Codex を実行せず inbox に提出しません。0.3.0 以降の明示的な `--check-return-route` は、利用できる非ローカル宛先に対してのみ、時間を制限した逆方向 SSH の無操作コマンドを実行します。自分自身への経路は SSH なしで `verified`、`transport:local`、`self_route_normalized` を返し、ホストがない場合は接続確認前に失敗します。`--check-reply-to` は未対応です。verified はローカル経路の正規化または 1 回の SSH 無操作コマンドの成功であり、ACK の認証や一般的な逆方向アクセスの保証ではありません。 - 同じ端末の Reply-To をローカルに正規化するには、明示した OS ユーザーの一致、認識できる正確なローカル名、明示的な SSH オプションがないことが必要です。送信者の証拠が競合した場合は識別情報を省き、Claude の名前が使えない場合は PID を使います。 -- npm、Python、リモートホスト、スキルは別管理です。TS は自己更新やリモート更新を提供しません。通知は既定で無効で、無効化指定が優先します。明示的な `update --check` はレジストリを照会します。従来のスキルメタデータは `0.1.0` のままで、#118 の後継 Draft は導入用の固定値や公開済み互換性検査の証拠ではありません。 +- npm、Python、リモートホスト、スキルは別管理です。TS は自己更新やリモート更新を提供しません。通知は既定で無効で、無効化指定が優先します。明示的な `update --check` はレジストリを照会します。公開 TS 0.3.3 は、レビュー済みの旧 `0.1.0` と公開済み `0.2.0` スキルの厳密なプロファイルを受け入れます。この 0.3.4 候補も同じ有限判定表を維持します。インストール節では、別途公開されたスキルのソースを固定しています。 - Windows の検証は公開 0.3.1、Windows 10 x64、管理者アカウント、PowerShell 5.1、Node 24.16.0 で行いました。オフライン導入、ラッパー・ネイティブロック、運用者の doctor・存在しない宛先の dry-run、ローカル fixture 31 件を検証しました。Node 22 はバージョン表示とネイティブロックの確認のみです。非管理者、他のアーキテクチャ、F6 のランタイム検査、以前の Linux 実験の最新規則は未検証です。0.3.2 の実機 ACK やデプロイ補助ツールの検証ではありません。 [移行の詳細](../PARITY.md#ordinary-cli-migration-notes--117)と [Windows の検証記録](../VALIDATION.md#windows-ssh-destination-f5--2026-10-07-kst)を参照してください。Python 1.0.2 のテスト基準は維持します。比較対象の公開 CLI はどちらも一般のネイティブ ACK/wait、エンドツーエンドの重複防止、Side Session を提供しません。Relay/MCP はこの比較の対象外です。 @@ -40,7 +40,7 @@ 実行中の **Claude Code と Codex セッション**に、ローカルまたは SSH 経由でメッセージを送る TypeScript クライアントです。Node.js で動作し、Python は不要です。 -**0.3.3 の利用ガイドです。**パッケージ名は `session-peer`、CLI コマンドは **`session-peer`**。Relay サーバーやホスティングサービスは提供しません。 +**0.3.4 の利用ガイドです。**パッケージ名は `session-peer`、CLI コマンドは **`session-peer`**。Relay サーバーやホスティングサービスは提供しません。 ## 機能と範囲 @@ -57,7 +57,7 @@ Relay 通信、MCP、wake/resume、Antigravity、自動更新は未実装です ビルド後の `node dist/cli.js list --json` は Claude/Codex をまとめて表示し、`list --agent codex --json` は既知の home を検索します。公開 npm **0.1.0** では引き続き agent と Codex 一覧の home を明示します。0.2.0 でも明示的なコマンド形式を利用できます。 -対象は既定の `~/.codex`、`CODEX_HOME`、macOS Orca 直下のアカウント home、JSON 配列 `SESSION_PEER_CODEX_HOMES` のみです。`--codex-home` は Codex 一覧を固定して無関係な設定エラーを回避し、`--agent claude` は Codex 探索を省略します。同じ home の別名は統合し、異なる home の同じ UUID は保持します。送信には各行の `codexHome` を使ってください。任意 home の不在はエラーではなく、明示した home の不在・不正は読み取れた行を保持して終了コード 1 を返します。一覧は writer を選択せず、メッセージを提出しません。[一覧契約](../PARITY.md#source-unified-listing-contract--16--020)に順序・診断・`--all`・SSH を記載しています。0.2.0の Codex 送信は `--codex-home` 省略時に一意で安定した live writer を選択します。明示 home でも既知の競合 home をすべて検査します。非アクティブなキュー送信には保存済みスレッド、全候補の非アクティブ検証、および `--codex-home HOME --allow-inactive-codex-home` が必要で、wake/resume は実行しません。Dry-run は提出しません。JSON に診断 `codexHomeResolution` と、ネイティブ出力にある場合のみ `queueId` を追加しますが、消費確認ではありません。[選択契約](../PARITY.md#source-codex-home-selection--17--020)を参照してください。公開 **0.1.0** には明示 live home が必要で、非アクティブ許可オプションはありません。SSH の両端には同じ TypeScript クライアントのバージョン(0.3.3)が必要です。 +対象は既定の `~/.codex`、`CODEX_HOME`、macOS Orca 直下のアカウント home、JSON 配列 `SESSION_PEER_CODEX_HOMES` のみです。`--codex-home` は Codex 一覧を固定して無関係な設定エラーを回避し、`--agent claude` は Codex 探索を省略します。同じ home の別名は統合し、異なる home の同じ UUID は保持します。送信には各行の `codexHome` を使ってください。任意 home の不在はエラーではなく、明示した home の不在・不正は読み取れた行を保持して終了コード 1 を返します。一覧は writer を選択せず、メッセージを提出しません。[一覧契約](../PARITY.md#source-unified-listing-contract--16--020)に順序・診断・`--all`・SSH を記載しています。0.2.0の Codex 送信は `--codex-home` 省略時に一意で安定した live writer を選択します。明示 home でも既知の競合 home をすべて検査します。非アクティブなキュー送信には保存済みスレッド、全候補の非アクティブ検証、および `--codex-home HOME --allow-inactive-codex-home` が必要で、wake/resume は実行しません。Dry-run は提出しません。JSON に診断 `codexHomeResolution` と、ネイティブ出力にある場合のみ `queueId` を追加しますが、消費確認ではありません。[選択契約](../PARITY.md#source-codex-home-selection--17--020)を参照してください。公開 **0.1.0** には明示 live home が必要で、非アクティブ許可オプションはありません。SSH の両端には同じ TypeScript クライアントのバージョン(0.3.4)が必要です。 ### 0.2.0 CLI の使いやすさ @@ -80,15 +80,15 @@ macOS / Linux / Windows native、Node **22.x の 22.13 以上、または 24.x** Node 22.x の 22.13 以上または 24.x を使用し、インストールする正確なバージョンの公開状況を確認してください。Python CLI も同じコマンド名を使うため、既存のインストールがあれば先に PATH を確認してください。 -**0.3.3 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](#ソースからビルド)を使います。 +**0.3.4 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](#ソースからビルド)を使います。 ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` -期待値は `session-peer 0.3.3 (typescript)`。 +期待値は `session-peer 0.3.4 (typescript)`。 1. `session-peer list --agent claude --json` で宛先を探し、正確な PID を選びます。 2. `session-peer send --to CLAUDE_PID --message 'Please reply after checking.' --dry-run --json` で提出せずに確認します。 @@ -107,7 +107,7 @@ npm run build node dist/cli.js --version npm pack --ignore-scripts # 任意のグローバルインストール前に PATH の既存コマンドを確認 -npm install --global --ignore-scripts ./session-peer-0.3.3.tgz +npm install --global --ignore-scripts ./session-peer-0.3.4.tgz session-peer --version ``` @@ -117,7 +117,7 @@ session-peer --version インストール前後に `type -a session-peer` と `command -v session-peer` を確認します。他の実装も同名コマンドを提供するため、PATH 上の一つを選ぶか `node /absolute/path/dist/cli.js` を使ってください。`--force` で他の管理ツールのファイルを上書きしないでください。Python パッケージ・スキル・サービスを自動変更しません。削除は `npm uninstall --global session-peer` を使い、PATH を再確認します。 -Windows PowerShell では `Get-Command session-peer -All` で既存コマンドを確認します。Python CLI を置換せずに試すには、`npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts` の後、`npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.3.tgz` を実行し、`& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` で確認します。同じ prefix の `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` で削除します。 +Windows PowerShell では `Get-Command session-peer -All` で既存コマンドを確認します。Python CLI を置換せずに試すには、`npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts` の後、`npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.4.tgz` を実行し、`& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` で確認します。同じ prefix の `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` で削除します。 ## 使い方 @@ -208,13 +208,13 @@ Python は開発時の互換検証基準のみです(v1.0.2、`47c23713d0a2a3c ## npm リリース -公開 **0.3.2** は 2026-10-07 KST に検証されました([固定された公開記録](../VALIDATION.md#public-032--2026-10-07-kst))。以下のレジストリ照会で 0.3.3 の現在の公開状況と npm タグを確認してください。日付付きの準備記録は、その後の公開結果を証明しません。 +公開 **0.3.2** は 2026-10-07 KST に検証されました([固定された公開記録](../VALIDATION.md#public-032--2026-10-07-kst))。以下のレジストリ照会で 0.3.4 の現在の公開状況と npm タグを確認してください。日付付きの準備記録は、その後の公開結果を証明しません。 0.2.1 は 2026-09-29 KST、0.2.0 は 2026-09-28 KST に公開・検証されました。 [0.2.1 公開記録](../VALIDATION.md#public-021--2026-09-29-kst)を参照し(以降のリリースはそれぞれ日付付きの記録を追加します)、導入前に正確なバージョンと現在のタグを確認してください。 ```sh -npm view session-peer@0.3.3 version dist.integrity +npm view session-peer@0.3.4 version dist.integrity npm view session-peer dist-tags ``` @@ -256,7 +256,7 @@ session-peer update --check --channel preview --output-format text 実行中の CLI のパスからインストールの管理者を確実に特定できた場合にだけ示します。 対象は、自身の `session-peer` ランチャーがこのパッケージを指す npm グローバル prefix (既定、Homebrew、nvm、nvm-windows、fnm。例: -`npm install --global --ignore-scripts session-peer@0.3.3`)、マニフェストで +`npm install --global --ignore-scripts session-peer@0.3.4`)、マニフェストで `session-peer` を宣言している pnpm・Yarn・Bun のグローバルストア、Volta、npx キャッシュ です。プロジェクトへのインストール(`npm_project`、`pnpm_project`)、ソースチェック アウト(`source`)、それ以外(`unknown`)では `updateCommand: null` と `guidance` の文だけを diff --git a/docs/guide.ko.md b/docs/guide.ko.md index 0a22c39..a3ef90b 100644 --- a/docs/guide.ko.md +++ b/docs/guide.ko.md @@ -4,13 +4,13 @@ [English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md) -**문서·소스 기준 버전: 0.3.3.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](../VALIDATION.md), 릴리스 절차는 [RELEASING.md](../RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.2와 0.3.3은 서로의 SSH 사전 확인을 거부합니다. +**문서·소스 기준 버전: 0.3.4.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](../VALIDATION.md), 릴리스 절차는 [RELEASING.md](../RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.3과 0.3.4는 서로의 SSH 사전 확인을 거부합니다. -이 가이드는 0.3.3을 다룹니다([0.3.1 신뢰성 수정](../PARITY.md#031-reliability-fixes)). 0.3.0에는 여러 호스트를 통한 SSH 전송, 발신자 정보와 회신 경로, 업데이트 확인, 선택 기능인 `sp` 단축 명령이 추가됐습니다([0.3.0 변경 사항](../PARITY.md#030-remote-usability-and-maintenance)). [0.3.1 공개 릴리스 기록](../VALIDATION.md#public-031--2026-10-04-kst)은 이전 버전의 발행 내역입니다. 아래에서 0.1.0을 언급할 때는 이전 버전을 설명합니다. +이 가이드는 0.3.4를 다룹니다([0.3.1 신뢰성 수정](../PARITY.md#031-reliability-fixes)). 0.3.0에는 여러 호스트를 통한 SSH 전송, 발신자 정보와 회신 경로, 업데이트 확인, 선택 기능인 `sp` 단축 명령이 추가됐습니다([0.3.0 변경 사항](../PARITY.md#030-remote-usability-and-maintenance)). [0.3.1 공개 릴리스 기록](../VALIDATION.md#public-031--2026-10-04-kst)은 이전 버전의 발행 내역입니다. 아래에서 0.1.0을 언급할 때는 이전 버전을 설명합니다. -## 마이그레이션 경계 (0.3.3) +## 마이그레이션 경계 (0.3.4) [0.3.2 공개 기록](../VALIDATION.md#public-032--2026-10-07-kst)은 릴리스 소스와 산출물을 고정합니다. SSH 사전 확인은 정확히 같은 TS 버전 표시를 검사하며 커밋 해시는 검사하지 않습니다. 소스 빌드의 검토 커밋 일치는 운영자가 별도로 확인해야 합니다. @@ -19,7 +19,7 @@ - 반복 `--host`는 요청 순서대로 배열을 반환합니다. 호스트별 실행 단계의 실패는 종료코드 1로 집계하지만, 실행 전 명령 전체 거부(호스트 중복이나 빈 메시지 등)는 배열 출력에서도 종료코드 2를 유지합니다. 공개 0.3.2는 SSH 종료 255나 시간 초과 후 완전한 응답의 근거도 잃을 수 있습니다. 0.3.3은 요청에 맞는 완전한 사실을 보존하며 로컬 종료코드 1과 `sshTransport`를 반환합니다. 불완전하거나 잘못된 근거는 unknown으로 남습니다. unknown이면 자동 재전송하지 않습니다. - 0.2.0에 도입된 기본 doctor는 Codex를 실행하거나 inbox에 제출하지 않습니다. 0.3.0부터 명시적 `--check-return-route`는 사용할 수 있는 비로컬 대상에 한해 제한된 역방향 SSH 무동작 명령을 실행합니다. 자기 경로는 SSH 없이 `verified`, `transport:local`, `self_route_normalized`를 반환하며, 호스트가 없으면 접속 확인 전에 실패합니다. `--check-reply-to`는 지원하지 않습니다. verified는 로컬 경로 정규화 또는 한 번의 SSH 무동작 명령 성공을 의미하며, ACK 인증이나 일반적인 역방향 접근 보장이 아닙니다. - 같은 기기의 Reply-To를 로컬로 바꾸려면 명시된 OS 사용자가 일치하고 정확한 로컬 이름으로 확인되며 명시적 SSH 옵션이 없어야 합니다. 발신자 근거가 충돌하면 신원을 생략하고, Claude 이름을 사용할 수 없으면 PID를 사용합니다. -- npm, Python, 원격 호스트, 스킬은 각각 별도로 관리합니다. TS는 자체·원격 업데이트를 제공하지 않습니다. 알림은 기본 꺼짐이며 비활성화 설정이 우선합니다. 명시적 `update --check`는 레지스트리를 조회합니다. 기존 스킬 메타데이터는 `0.1.0`이며 #118의 후속 Draft는 설치 고정값이나 공개된 호환성 검사기의 근거가 아닙니다. +- npm, Python, 원격 호스트, 스킬은 각각 별도로 관리합니다. TS는 자체·원격 업데이트를 제공하지 않습니다. 알림은 기본 꺼짐이며 비활성화 설정이 우선합니다. 명시적 `update --check`는 레지스트리를 조회합니다. 공개 TS 0.3.3은 검토된 기존 `0.1.0` 및 공개된 `0.2.0` 스킬의 정확한 프로필을 허용하며, 이 0.3.4 후보도 같은 유한 판정표를 유지합니다. 설치 절에는 별도로 공개된 스킬 소스를 고정했습니다. - Windows 검증은 공개 0.3.1, Windows 10 x64, 관리자 계정, PowerShell 5.1, Node 24.16.0에서 수행했습니다. 오프라인 설치, 래퍼·네이티브 잠금, 운영자의 doctor·없는 대상 dry-run과 로컬 fixture 31개가 검증됐습니다. Node 22는 버전 표시·네이티브 잠금 확인만 수행했습니다. 비관리자, 다른 아키텍처, F6 런타임 검사, 이전 Linux 실험의 최신 규칙 검증은 남아 있습니다. 이는 0.3.2 실기 ACK나 배포 도우미 검증이 아닙니다. [마이그레이션 상세](../PARITY.md#ordinary-cli-migration-notes--117)와 [Windows 검증 기록](../VALIDATION.md#windows-ssh-destination-f5--2026-10-07-kst)을 참고하세요. Python 1.0.2 테스트 기준은 유지합니다. 비교 대상 공개 CLI 모두 일반 네이티브 ACK/wait, 종단 간 중복 방지, Side Session을 제공하지 않습니다. Relay/MCP는 이 비교 범위에서 제외합니다. @@ -92,7 +92,7 @@ JSON에는 정제된 `codexHomeResolution`이 포함되며, 네이티브 응답 `queueId`도 포함됩니다. 둘 다 메시지를 읽거나 처리했음을 확인하지 않습니다. [홈 선택 계약](../PARITY.md#source-codex-home-selection--17--020)을 참고하세요. 공개 **0.1.0**은 여전히 실행 중인 홈을 명시해야 하며 비활성 세션 허용 옵션이 없습니다. -SSH를 사용할 때는 발신·수신 양쪽에 같은 TypeScript 클라이언트 버전(0.3.3)을 설치해야 +SSH를 사용할 때는 발신·수신 양쪽에 같은 TypeScript 클라이언트 버전(0.3.4)을 설치해야 합니다. @@ -132,20 +132,20 @@ Claude Code에는 받은 메시지함에 접근할 수 있는 실행 중 TUI(터 Node 22.x의 22.13 이상 또는 24.x를 사용하세요. Python CLI도 같은 `session-peer` 명령 이름을 쓰므로, 설치 전에 PATH(명령 검색 경로)에서 어떤 실행 파일을 찾는지 -확인하세요. 아래 명령은 공개된 0.3.3을 npm으로 설치하는 예입니다. +확인하세요. 아래 명령은 선택한 0.3.4를 npm으로 설치하는 예입니다. CLI 자체는 패키지를 설치하거나 업데이트하지 않습니다. -**0.3.3 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](#소스에서-빌드)를 사용하세요. +**0.3.4 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](#소스에서-빌드)를 사용하세요. ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 -session-peer --version # session-peer 0.3.3 (typescript) +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 +session-peer --version # session-peer 0.3.4 (typescript) session-peer list --agent claude --json session-peer send --to CLAUDE_PID --message 'Please review the API contract.' --dry-run --json ``` -예상 결과는 `session-peer 0.3.3 (typescript)`입니다. +예상 결과는 `session-peer 0.3.4 (typescript)`입니다. 1. 세션을 조회하고 `CLAUDE_PID`를 대상으로 선택한 정확한 PID로 바꾸세요. 2. `--dry-run`으로 검증합니다. 이 단계에서는 메시지를 제출하지 않습니다. @@ -173,11 +173,11 @@ npm run build node dist/cli.js --version npm pack --ignore-scripts # Optional global install: first check which session-peer your PATH selects. -npm install --global --ignore-scripts ./session-peer-0.3.3.tgz +npm install --global --ignore-scripts ./session-peer-0.3.4.tgz session-peer --version ``` -0.3.3에서 예상되는 결과는 `session-peer 0.3.3 (typescript)`입니다. 로컬 빌드 +0.3.4에서 예상되는 결과는 `session-peer 0.3.4 (typescript)`입니다. 로컬 빌드 산출물을 선택하려면 `./...tgz` 경로를 그대로 두세요. 이후 버전에 이 명령을 사용할 때는 먼저 체크아웃한 소스의 버전을 확인하세요. @@ -198,7 +198,7 @@ Python CLI를 덮어쓰지 않으려면 격리 prefix에 설치·제거할 수 npm ci --ignore-scripts npm run build npm pack --ignore-scripts -npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.3.tgz +npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.4.tgz & "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version # Later: npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer ``` @@ -441,7 +441,7 @@ Windows x64/Node 24에서 실제 TUI로 확인한 ACK는 별도의 일회성 증 ## npm 릴리스 -아래 레지스트리 조회로 0.3.3의 현재 제공 여부와 npm 태그를 확인하세요. 날짜가 기록된 준비 근거는 이후 발행 결과를 입증하지 않습니다. +아래 레지스트리 조회로 0.3.4의 현재 제공 여부와 npm 태그를 확인하세요. 날짜가 기록된 준비 근거는 이후 발행 결과를 입증하지 않습니다. 0.3.2는 2026-10-07 KST에, 0.3.1은 2026-10-04 KST에 공개·검증했습니다. [0.3.2 공개 기록](../VALIDATION.md#public-032--2026-10-07-kst)과 @@ -452,7 +452,7 @@ Windows x64/Node 24에서 실제 TUI로 확인한 ACK는 별도의 일회성 증 설치 전에는 정확한 버전과 현재 npm 태그를 확인하세요. ```sh -npm view session-peer@0.3.3 version dist.integrity +npm view session-peer@0.3.4 version dist.integrity npm view session-peer dist-tags ``` @@ -507,7 +507,7 @@ session-peer update --check --channel preview --output-format text `updateCommand`는 실행 중인 CLI 경로로 설치 주체를 확인할 수 있을 때 사용자에게 안내합니다. 해당하는 경우는 자체 `session-peer` 실행기가 이 패키지를 가리키는 npm 전역 prefix(기본, Homebrew, nvm, -nvm-windows, fnm. 예: `npm install --global --ignore-scripts session-peer@0.3.3`), +nvm-windows, fnm. 예: `npm install --global --ignore-scripts session-peer@0.3.4`), 매니페스트에 `session-peer`가 선언된 pnpm·Yarn·Bun 전역 저장소, Volta, npx 캐시입니다. 프로젝트 설치(`npm_project`, `pnpm_project`), 소스 체크아웃(`source`), 그 밖의 경우(`unknown`)에는 `updateCommand: null`과 `guidance` 문장만 돌려주므로, 관련 없는 현재 diff --git a/docs/guide.md b/docs/guide.md index 482d9aa..bcf8eb4 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -4,13 +4,13 @@ [English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md) -**Documentation and source version: 0.3.3.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](../VALIDATION.md); release procedures are in [RELEASING.md](../RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.2 and 0.3.3 refuse each other’s SSH preflight. +**Documentation and source version: 0.3.4.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](../VALIDATION.md); release procedures are in [RELEASING.md](../RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.3 and 0.3.4 refuse each other’s SSH preflight. -This guide covers version 0.3.3 ([0.3.1 reliability fixes](../PARITY.md#031-reliability-fixes)). Version 0.3.0 introduced multi-host SSH, sender context and return routes, update checks and the optional `sp` shorthand ([0.3.0 changes](../PARITY.md#030-remote-usability-and-maintenance)). [Public 0.3.1 release evidence](../VALIDATION.md#public-031--2026-10-04-kst) records a prior publication. References to 0.1.0 below describe the older release. +This guide covers version 0.3.4 ([0.3.1 reliability fixes](../PARITY.md#031-reliability-fixes)). Version 0.3.0 introduced multi-host SSH, sender context and return routes, update checks and the optional `sp` shorthand ([0.3.0 changes](../PARITY.md#030-remote-usability-and-maintenance)). [Public 0.3.1 release evidence](../VALIDATION.md#public-031--2026-10-04-kst) records a prior publication. References to 0.1.0 below describe the older release. -## Migration boundaries (0.3.3) +## Migration boundaries (0.3.4) The [public 0.3.2 record](../VALIDATION.md#public-032--2026-10-07-kst) fixes the release source and artifact. SSH preflight checks the exact TS version banner, @@ -40,8 +40,7 @@ not a commit hash; operators must align reviewed source commits separately. omits identity; an unusable Claude name falls back to PID. - npm, Python, remote hosts and skills have separate owners. TS has no self- or remote updater; notices default off and opt-out wins. Explicit `update --check` - still queries the registry. The legacy skill metadata remains `0.1.0`; - #118's Draft successor is not an install pin or a compatible published guard. + still queries the registry. Public TS 0.3.3 accepts both exact reviewed legacy `0.1.0` and published `0.2.0` skill profiles; this 0.3.4 candidate preserves that finite matrix. The installation section pins the independently published skill source. - Windows qualification used published 0.3.1 on Windows 10 x64, an administrator account, PowerShell 5.1 and Node 24.16.0: offline provisioning, wrapper/native locking, operator doctor/nonexistent-target dry-run and 31 local fixtures. @@ -74,7 +73,7 @@ Relay/MCP are outside this migration comparison. **Find and message running Claude Code and Codex sessions, locally or over SSH.** This TypeScript client runs on Node.js without Python. -This guide describes **version 0.3.3**. The npm package and CLI command are both **`session-peer`**. This +This guide describes **version 0.3.4**. The npm package and CLI command are both **`session-peer`**. This project does not provide a Relay server or hosted service. ## Quick start @@ -82,12 +81,12 @@ project does not provide a Relay server or hosted service. Use Node **22.13+ within 22.x or 24.x**. Check which `session-peer` your PATH selects if the Python CLI is already installed; both packages use that command. -**Version 0.3.3 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](#build-from-source). +**Version 0.3.4 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](#build-from-source). ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 -session-peer --version # session-peer 0.3.3 (typescript) +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 +session-peer --version # session-peer 0.3.4 (typescript) session-peer list --agent claude --json session-peer send --to CLAUDE_PID --message 'Please review the API contract.' --dry-run --json ``` @@ -154,7 +153,7 @@ submits nothing. JSON adds sanitized `codexHomeResolution` and, when supplied by native queue output, `queueId`; neither confirms consumption. See the [selection contract](../PARITY.md#source-codex-home-selection--17--020). Published **0.1.0** still requires an explicit live home and has no inactive opt-in. -On SSH, both ends need the same TypeScript client version (0.3.3). +On SSH, both ends need the same TypeScript client version (0.3.4). ## Requirements @@ -178,11 +177,11 @@ npm run build node dist/cli.js --version npm pack --ignore-scripts # Optional global install: first check which session-peer your PATH selects. -npm install --global --ignore-scripts ./session-peer-0.3.3.tgz +npm install --global --ignore-scripts ./session-peer-0.3.4.tgz session-peer --version ``` -Expected for version 0.3.3: `session-peer 0.3.3 (typescript)`. Keep the `./...tgz` path to +Expected for version 0.3.4: `session-peer 0.3.4 (typescript)`. Keep the `./...tgz` path to select the locally built artifact. Check the checkout version before using these commands for a later release. @@ -202,7 +201,7 @@ On Windows PowerShell, inspect competing commands with npm ci --ignore-scripts npm run build npm pack --ignore-scripts -npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.3.tgz +npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.4.tgz & "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version # Later: npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer ``` @@ -465,7 +464,7 @@ separate approval. Licensed under [MIT](../LICENSE). ## Package release -Public **0.3.2** was verified on 2026-10-07 KST ([immutable checkpoint](../VALIDATION.md#public-032--2026-10-07-kst)). Check current 0.3.3 availability and npm tags with the registry queries below; dated preparation evidence does not establish a later publication outcome. +Public **0.3.2** was verified on 2026-10-07 KST ([immutable checkpoint](../VALIDATION.md#public-032--2026-10-07-kst)). Check current 0.3.4 availability and npm tags with the registry queries below; dated preparation evidence does not establish a later publication outcome. Version 0.2.1 was published and verified on 2026-09-29 KST and 0.2.0 on 2026-09-28 KST; see the [0.2.1 public release record](../VALIDATION.md#public-021--2026-09-29-kst). @@ -473,7 +472,7 @@ Later releases add their own dated records. Check the exact version and current tags before installing: ```sh -npm view session-peer@0.3.3 version dist.integrity +npm view session-peer@0.3.4 version dist.integrity npm view session-peer dist-tags ``` @@ -525,7 +524,7 @@ and reports `current`, `latest`, `channel` (`latest` by default, or `preview`), installation's owner is positively identified from the running CLI's path: an npm global prefix whose own `session-peer` launcher points at this package (default, Homebrew, nvm, nvm-windows and fnm prefixes), for example -`npm install --global --ignore-scripts session-peer@0.3.3`; a pnpm, Yarn or Bun +`npm install --global --ignore-scripts session-peer@0.3.4`; a pnpm, Yarn or Bun global store whose manifest declares `session-peer`; Volta; or the npx cache. Project installs (`npm_project`, `pnpm_project`), source checkouts (`source`) and anything else (`unknown`) get `updateCommand: null` and a `guidance` diff --git a/docs/guide.zh-CN.md b/docs/guide.zh-CN.md index 15f53d9..6784f3b 100644 --- a/docs/guide.zh-CN.md +++ b/docs/guide.zh-CN.md @@ -4,13 +4,13 @@ [English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md) -**文档和源码版本:0.3.3。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](../VALIDATION.md),发布流程见 [RELEASING.md](../RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.2 与 0.3.3 会拒绝彼此的 SSH 预检。 +**文档和源码版本:0.3.4。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](../VALIDATION.md),发布流程见 [RELEASING.md](../RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.3 与 0.3.4 会拒绝彼此的 SSH 预检。 -这是 0.3.3 详细指南([0.3.1 可靠性修复](../PARITY.md#031-reliability-fixes))。0.3.0 新增了多主机 SSH、发送者信息与回复路由、更新检查以及可选的 `sp` 简写([0.3.0 变更](../PARITY.md#030-remote-usability-and-maintenance))。此前的发布记录见 [0.3.1 公开发布证据](../VALIDATION.md#public-031--2026-10-04-kst);下文 0.1.0 的说明用于旧版本对比。 +这是 0.3.4 详细指南([0.3.1 可靠性修复](../PARITY.md#031-reliability-fixes))。0.3.0 新增了多主机 SSH、发送者信息与回复路由、更新检查以及可选的 `sp` 简写([0.3.0 变更](../PARITY.md#030-remote-usability-and-maintenance))。此前的发布记录见 [0.3.1 公开发布证据](../VALIDATION.md#public-031--2026-10-04-kst);下文 0.1.0 的说明用于旧版本对比。 -## 迁移边界 (0.3.3) +## 迁移边界 (0.3.4) [0.3.2 公开记录](../VALIDATION.md#public-032--2026-10-07-kst)固定了发布源码和产物。SSH 预检验证 TS 版本标识完全一致,不检查提交哈希;源码构建使用的已审核提交需要由操作人员另行对齐。 @@ -19,7 +19,7 @@ - 重复 `--host` 返回按请求顺序排列的数组。主机分别执行期间发生的失败汇总为退出码 1;执行前的命令整体拒绝(如主机重复或消息为空)在数组输出中仍保持退出码 2。公开 0.3.2 在 SSH 退出 255 或超时后,也可能丢失完整响应的证据。0.3.3 保留与请求匹配的完整事实,并返回本地退出码 1 和 `sshTransport`。不完整或无效证据仍为 unknown。unknown 时不得自动重发。 - 0.2.0 引入的基础 doctor 不执行 Codex,也不向 inbox 提交。0.3.0 起,显式 `--check-return-route` 仅对可用的非本地目标执行有时间限制的反向 SSH 无操作命令。同机自身路由无需 SSH 即返回 `verified`、`transport:local`、`self_route_normalized`;缺少主机时在任何探测前失败。`--check-reply-to` 不受支持。verified 表示本地路由归一化或一次 SSH 无操作命令成功,不是 ACK 认证或通用反向访问保证。 - 将同机 Reply-To 归一化为本地路径,必须明确指定匹配的 OS 用户、使用可识别的准确本机名称,且没有显式 SSH 选项。发送者证据冲突时省略身份;Claude 名称不可用时使用 PID。 -- npm、Python、远程主机、技能分别管理。TS 不提供自身更新或远程更新。通知默认关闭,禁用设置优先。显式 `update --check` 仍会查询注册表。现有技能元数据保持 `0.1.0`;#118 的后续 Draft 不是安装固定来源,也不是已发布兼容性检查器的证据。 +- npm、Python、远程主机、技能分别管理。TS 不提供自身更新或远程更新。通知默认关闭,禁用设置优先。显式 `update --check` 仍会查询注册表。公开 TS 0.3.3 接受经过审核的旧 `0.1.0` 和已发布 `0.2.0` 技能的精确配置;此 0.3.4 候选保留相同的有限判定表。安装部分固定了独立发布的技能源码。 - Windows 验证使用公开 0.3.1、Windows 10 x64、管理员账号、PowerShell 5.1 和 Node 24.16.0。验证范围包括离线安装、包装器与原生锁、操作端 doctor 和不存在目标的 dry-run,以及 31 项本地 fixture。Node 22 仅确认版本标识和原生锁。非管理员、其他架构、F6 运行时校验、此前 Linux 实验的最新规则仍未验证。这不是 0.3.2 实机 ACK 或部署辅助工具的验证。 请参阅[迁移详情](../PARITY.md#ordinary-cli-migration-notes--117)和 [Windows 验证记录](../VALIDATION.md#windows-ssh-destination-f5--2026-10-07-kst)。Python 1.0.2 测试基准保持不变。对比的两个公开 CLI 都没有通用原生 ACK/wait、端到端去重或 Side Session 支持。Relay/MCP 不在此次迁移对比范围内。 @@ -40,7 +40,7 @@ 向正在运行的 **Claude Code 和 Codex 会话**发送消息,支持本机和跨机器 SSH。这是运行于 Node.js 的 TypeScript 客户端,不需要 Python。 -**这是 0.3.3 使用指南。**包名为 `session-peer`,CLI 命令为 **`session-peer`**。本项目不提供 Relay 服务器或托管服务。 +**这是 0.3.4 使用指南。**包名为 `session-peer`,CLI 命令为 **`session-peer`**。本项目不提供 Relay 服务器或托管服务。 ## 功能与边界 @@ -57,7 +57,7 @@ 构建后,`node dist/cli.js list --json` 合并列出 Claude/Codex,`list --agent codex --json` 查找已知 home。公开 npm **0.1.0** 仍要求显式 agent,以及 Codex 列表所需的 home;0.2.0 也支持显式命令格式。 -范围仅限默认 `~/.codex`、`CODEX_HOME`、macOS Orca 下的直接账户 home,以及 JSON 数组 `SESSION_PEER_CODEX_HOMES`。`--codex-home` 固定 Codex 列表并绕过无关配置错误;`--agent claude` 完全跳过 Codex 探索。同一 home 的别名合并,不同 home 的相同 UUID 保留。发送时使用每行的 `codexHome`。可选 home 缺失不是错误;显式 home 缺失或无效会保留成功读取的行,并返回退出码 1。列表不会选择 writer 或提交消息。[列表契约](../PARITY.md#source-unified-listing-contract--16--020)说明排序、诊断、`--all` 和 SSH 行为。0.2.0的 Codex 发送在省略 `--codex-home` 时选择唯一且稳定的 live writer;显式 home 仍会检查所有已知竞争 home。非活动队列提交需要已保存的线程、所有候选均确认非活动,以及 `--codex-home HOME --allow-inactive-codex-home`,不会执行 wake/resume。Dry-run 不提交。JSON 新增诊断 `codexHomeResolution`,且仅在原生输出提供时包含 `queueId`;两者均不代表消费确认。请参阅[选择契约](../PARITY.md#source-codex-home-selection--17--020)。公开 **0.1.0** 仍需要显式 live home,且没有非活动许可选项。SSH 两端需要相同的 TypeScript 客户端版本(0.3.3)。 +范围仅限默认 `~/.codex`、`CODEX_HOME`、macOS Orca 下的直接账户 home,以及 JSON 数组 `SESSION_PEER_CODEX_HOMES`。`--codex-home` 固定 Codex 列表并绕过无关配置错误;`--agent claude` 完全跳过 Codex 探索。同一 home 的别名合并,不同 home 的相同 UUID 保留。发送时使用每行的 `codexHome`。可选 home 缺失不是错误;显式 home 缺失或无效会保留成功读取的行,并返回退出码 1。列表不会选择 writer 或提交消息。[列表契约](../PARITY.md#source-unified-listing-contract--16--020)说明排序、诊断、`--all` 和 SSH 行为。0.2.0的 Codex 发送在省略 `--codex-home` 时选择唯一且稳定的 live writer;显式 home 仍会检查所有已知竞争 home。非活动队列提交需要已保存的线程、所有候选均确认非活动,以及 `--codex-home HOME --allow-inactive-codex-home`,不会执行 wake/resume。Dry-run 不提交。JSON 新增诊断 `codexHomeResolution`,且仅在原生输出提供时包含 `queueId`;两者均不代表消费确认。请参阅[选择契约](../PARITY.md#source-codex-home-selection--17--020)。公开 **0.1.0** 仍需要显式 live home,且没有非活动许可选项。SSH 两端需要相同的 TypeScript 客户端版本(0.3.4)。 ### 0.2.0 CLI 易用性 @@ -79,15 +79,15 @@ macOS、Linux 或 Windows native;Node **22.x 中的 22.13 及以上,或 24.x 使用 Node 22.x 的 22.13 及以上或 24.x,并确认要安装的确切版本是否已发布。Python CLI 也使用同一个命令名;若已有安装,请先检查 PATH。 -**安装 0.3.3:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](#从源码构建)。 +**安装 0.3.4:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](#从源码构建)。 ```sh -npm view session-peer@0.3.3 version dist.integrity -npm install --global --ignore-scripts session-peer@0.3.3 +npm view session-peer@0.3.4 version dist.integrity +npm install --global --ignore-scripts session-peer@0.3.4 session-peer --version ``` -预期输出:`session-peer 0.3.3 (typescript)`。 +预期输出:`session-peer 0.3.4 (typescript)`。 1. 用 `session-peer list --agent claude --json` 查找目标并选择准确 PID。 2. 用 `session-peer send --to CLAUDE_PID --message 'Please reply after checking.' --dry-run --json` 验证,不提交。 @@ -106,7 +106,7 @@ npm run build node dist/cli.js --version npm pack --ignore-scripts # 可选:全局安装前先检查 PATH 选择的现有命令 -npm install --global --ignore-scripts ./session-peer-0.3.3.tgz +npm install --global --ignore-scripts ./session-peer-0.3.4.tgz session-peer --version ``` @@ -116,7 +116,7 @@ session-peer --version 安装前后用 `type -a session-peer` 和 `command -v session-peer` 检查实际执行项。其他实现也可能提供同名命令;请选择 PATH 上的一种,或显式运行 `node /absolute/path/dist/cli.js`。不要用 `--force` 覆盖其他管理器的文件。本包不会自动安装、删除或调整 Python 包、技能或服务。卸载使用 `npm uninstall --global session-peer`,随后再次检查 PATH。 -在 Windows PowerShell 中用 `Get-Command session-peer -All` 检查已有命令。为了不替换 Python CLI,可先执行 `npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts`,再用 `npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.3.tgz` 安装到隔离目录。以 `& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` 验证,并可用 `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` 卸载。 +在 Windows PowerShell 中用 `Get-Command session-peer -All` 检查已有命令。为了不替换 Python CLI,可先执行 `npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts`,再用 `npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.3.4.tgz` 安装到隔离目录。以 `& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` 验证,并可用 `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` 卸载。 ## 使用 @@ -207,14 +207,14 @@ Python 只用作开发时的兼容性基准(v1.0.2,`47c23713d0a2a3c11ebde618 ## npm 版本 -公开 **0.3.2** 于 2026-10-07 KST 完成验证([不可变公开记录](../VALIDATION.md#public-032--2026-10-07-kst))。请用下方注册表查询确认 0.3.3 的当前可用性和 npm 标签;带日期的准备证据不能证明后续发布结果。 +公开 **0.3.2** 于 2026-10-07 KST 完成验证([不可变公开记录](../VALIDATION.md#public-032--2026-10-07-kst))。请用下方注册表查询确认 0.3.4 的当前可用性和 npm 标签;带日期的准备证据不能证明后续发布结果。 0.2.1 已于 2026-09-29 KST、0.2.0 已于 2026-09-28 KST 公开并验证。请参阅 [0.2.1 公开记录](../VALIDATION.md#public-021--2026-09-29-kst)(之后的版本各自追加带日期的记录), 安装前核对准确版本与当前标签。 ```sh -npm view session-peer@0.3.3 version dist.integrity +npm view session-peer@0.3.4 version dist.integrity npm view session-peer dist-tags ``` @@ -253,7 +253,7 @@ dist-tag(3 秒超时,不重试),并报告 `current`、`latest`、`channe `ahead`)、`managedBy`、`updateCommand` 和 `guidance`。只有根据当前 CLI 路径能确切识别 安装的管理方时才给出命令:其自身 `session-peer` 启动器指向本包的 npm 全局 prefix (默认、Homebrew、nvm、nvm-windows、fnm,例如 -`npm install --global --ignore-scripts session-peer@0.3.3`),清单中声明了 +`npm install --global --ignore-scripts session-peer@0.3.4`),清单中声明了 `session-peer` 的 pnpm、Yarn、Bun 全局存储,Volta,或 npx 缓存。项目安装 (`npm_project`、`pnpm_project`)、源码检出(`source`)以及其他情况(`unknown`)只返回 `updateCommand: null` 和一句 `guidance`,因此不会给出可能修改无关当前目录的命令。它只报告 diff --git a/docs/shorthand.md b/docs/shorthand.md index a00f6e2..b07127d 100644 --- a/docs/shorthand.md +++ b/docs/shorthand.md @@ -1,6 +1,6 @@ # Optional `sp` shorthand -This reference describes source/package version 0.3.3. Check registry availability before using a version-pinned npm installation; before publication use a reviewed source artifact. The shorthand forwards to the explicitly selected CLI and does not install or upgrade it. +This reference describes source/package version 0.3.4. Check registry availability before using a version-pinned npm installation; before publication use a reviewed source artifact. The shorthand forwards to the explicitly selected CLI and does not install or upgrade it. [README](../README.md) · [User guide](guide.md) diff --git a/package-lock.json b/package-lock.json index ef0173d..7516fe7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "session-peer", - "version": "0.3.3", + "version": "0.3.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "session-peer", - "version": "0.3.3", + "version": "0.3.4", "license": "MIT", "os": [ "darwin", diff --git a/package.json b/package.json index 01aca96..94dd49d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "session-peer", - "version": "0.3.3", + "version": "0.3.4", "private": false, "publishConfig": { "registry": "https://registry.npmjs.org/", diff --git a/scripts/release.mjs b/scripts/release.mjs index bb0db92..b40d8b3 100644 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -8,9 +8,9 @@ import { fileURLToPath } from 'node:url'; export const repository = 'abruption/session-peer-ts'; export const registry = 'https://registry.npmjs.org'; -const stableVersion = '0.3.3'; -const previousStable = '0.3.2'; -const previousStableIntegrity = 'sha512-VNlzBqELXLFI8uCopfEdYrfweIacHeP6dMK32/4vwQJy8fUIKC64c5WCE3IwQVeCTKGMz9mXSxhzZnXmSGMuuA=='; +const stableVersion = '0.3.4'; +const previousStable = '0.3.3'; +const previousStableIntegrity = 'sha512-5OQ7eUlR5Oy3RHFRoR3FuBkV7LT72dfjfBEbWRCY5wYP7Dz8TmN2jlRPpo2SFHPAwosFc0WIt8Iis/vyHckxRg=='; const verifiedPreview = '0.1.0-preview.1'; const verifiedPreviewIntegrity = 'sha512-h4SMvrQ/LWA9osd4EHIs9rSTqv1u+S3MXQAmn+yG/gZ9+7NwYMutq+Oa5K/0ggIq61Wfwdh11Cv+5YdEffiNMA=='; export const packageFiles = ['UNICODE-LICENSE.txt', 'dist/casefold.js', 'dist/casefold.d.ts', 'dist/help.js', 'dist/help.d.ts', 'dist/output.js', 'dist/output.d.ts', 'CONTRIBUTING.md', 'LICENSE', 'PARITY.md', 'README.ja.md', 'README.ko.md', @@ -30,7 +30,7 @@ export function validatePackage(pkg) { assert.deepEqual(pkg.exports, { '.': { types: './dist/index.d.ts', import: './dist/index.js' } }); assert.equal(pkg.private, false); assert.ok(pkg.version === stableVersion || /^\d+\.\d+\.\d+-preview\.\d+$/.test(pkg.version), - 'only reviewed preview versions and 0.3.3 stable are supported'); + 'only reviewed preview versions and 0.3.4 stable are supported'); assert.deepEqual(pkg.bin, { 'session-peer': 'dist/cli.js' }); assert.equal(pkg.repository.url, `git+https://github.com/${repository}.git`); assert.deepEqual(pkg.publishConfig, { registry: `${registry}/`, access: 'public', diff --git a/src/protocol.ts b/src/protocol.ts index 25d539c..7fa5021 100644 --- a/src/protocol.ts +++ b/src/protocol.ts @@ -1,7 +1,7 @@ import { isIPv6 } from 'node:net'; import { Refusal } from './discovery.js'; import { uuid } from './writer.js'; -export const VERSION = '0.3.3'; +export const VERSION = '0.3.4'; export const VERSION_LINE = `session-peer ${VERSION} (typescript)`; // Return the OpenSSH destination: [user@]name, or an IPv6 literal with any // brackets removed (`ssh` does not strip `[...]` outside ssh:// URIs). Zone IDs diff --git a/src/skill-metadata.ts b/src/skill-metadata.ts index 8a8a3a0..27c145e 100644 --- a/src/skill-metadata.ts +++ b/src/skill-metadata.ts @@ -13,7 +13,7 @@ export const SKILL_METADATA_LIMIT = 65536; const keys = ['version', 'runtime-implementation', 'runtime-min-version', 'runtime-full-version', 'runtime-capability-policy'] as const; // Finite reviewed profiles: public 0.3.2 is legacy-only; public 0.3.3 accepts // both profiles. The explicit 0.3.4 row is an unreleased patch proposal, not -// generic future-version support. Compiled VERSION remains 0.3.3 here. +// generic future-version support. Compiled VERSION is 0.3.4 in this candidate. const matrix: Record = { '0.3.2': ['0.1.0'], '0.3.3': ['0.1.0', '0.2.0'], '0.3.4': ['0.1.0', '0.2.0'] }; diff --git a/test/integration-multihost-updates.test.ts b/test/integration-multihost-updates.test.ts index 1d99855..8a2d00a 100644 --- a/test/integration-multihost-updates.test.ts +++ b/test/integration-multihost-updates.test.ts @@ -21,7 +21,7 @@ async function fixture(t: TestContext) { const requests: string[] = []; const server = createServer((request, response) => { requests.push(request.url ?? ''); - response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify({ latest: '0.3.4' })); + response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify({ latest: '0.3.5' })); }); server.listen(0, '127.0.0.1'); await once(server, 'listening'); t.after(async () => { @@ -38,7 +38,7 @@ const args=process.argv.slice(2); if(args.includes('-G')){console.log('hostname x\\nuser config-user\\nport 22');process.exit(0);} const dest=args[args.indexOf('--')+1]; if((process.env.FAKE_DOWN||'').split(',').includes(dest)){console.error('ssh: connect to host: Connection refused');process.exit(255);} -if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.3 (typescript)');process.exit(0);} +if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.4 (typescript)');process.exit(0);} const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input:fs.readFileSync(0,'utf8'),encoding:'utf8', env:{...process.env,SESSION_PEER_CACHE_DIR:${JSON.stringify(receiver)},SESSION_PEER_UPDATE_NOTICE:'1'}}); // FAKE_INJECT: a receiver response carrying a forged client-only field. @@ -58,7 +58,7 @@ process.stdout.write(out);process.exit(r.status);`, { mode: 0o700 }); }; const seed = (age = 0) => { mkdirSync(cache, { recursive: true, mode: 0o700 }); - writeFileSync(file, JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.4', checkedAt: Date.now() - age }), { mode: 0o600 }); + writeFileSync(file, JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.5', checkedAt: Date.now() - age }), { mode: 0o600 }); }; const settled = async (count: number) => { for (let i = 0; i < 200 && !(requests.length >= count && existsSync(file) && !existsSync(lock)); i++) await delay(50); @@ -86,7 +86,7 @@ test('repeated --host keeps the request-order array exactly; the notice is clien // One destination stays flat and gets the additive client field. const single = await f.call(['list', '--agent', 'claude', '--host', 'alpha', '--json']); const flat = JSON.parse(single.stdout); - assert.equal(Array.isArray(flat), false); assert.equal(flat.host, 'alpha'); assert.equal(flat.clientUpdate.latest, '0.3.4'); + assert.equal(Array.isArray(flat), false); assert.equal(flat.host, 'alpha'); assert.equal(flat.clientUpdate.latest, '0.3.5'); await delay(500); assert.deepEqual(f.requests, [], 'fresh cache: no refresh'); assert.equal(existsSync(f.receiver), false); }); @@ -98,7 +98,7 @@ test('each invocation schedules at most one refresh regardless of host count; re for (const item of JSON.parse(first.stdout)) assert.equal('clientUpdate' in item, false); await f.settled(1); assert.equal(f.requests.length, 1, 'one refresh for three hosts'); - assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.4'); + assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.5'); f.seed(25 * 3600_000); await f.call(f.args(['--output-format', 'text'])); await f.settled(2); @@ -118,7 +118,7 @@ test('failure policy: per-host results (including failed ones) may carry the adv const on = await f.call(['list', '--agent', 'claude', '--host', 'alpha', '--json'], down); const offValue = JSON.parse(off.stdout), { clientUpdate, ...onValue } = JSON.parse(on.stdout); assert.equal(offValue.ok, false); assert.equal(offValue.error, 'ssh_unreachable'); - assert.equal(on.code, off.code); assert.deepEqual(onValue, offValue); assert.equal(clientUpdate.latest, '0.3.4'); + assert.equal(on.code, off.code); assert.deepEqual(onValue, offValue); assert.equal(clientUpdate.latest, '0.3.5'); // Mixed array: element shape and exit code unchanged; text gets one stderr line for the invocation. const mixedOff = await f.call(f.args(['--json', '--no-update-notice']), down), mixedOn = await f.call(f.args(['--json']), down); assert.equal(mixedOn.code, mixedOff.code); assert.deepEqual(JSON.parse(mixedOn.stdout), JSON.parse(mixedOff.stdout)); @@ -152,7 +152,7 @@ test('clientUpdate is client-local: a remote-supplied value is dropped', posix, assert.equal(localRun.code, 0); const { clientUpdate: own, ...local } = JSON.parse(localRun.stdout); accepted(local, 'alpha'); - assert.equal(own.source, 'npm_registry_cache'); assert.equal(own.latest, '0.3.4'); assert.notEqual(own.command, 'fixture-only'); + assert.equal(own.source, 'npm_registry_cache'); assert.equal(own.latest, '0.3.5'); assert.notEqual(own.command, 'fixture-only'); const text = await f.call(f.args(['--output-format', 'text', '--no-update-notice']), inject); assert.equal(text.code, 0); assert.doesNotMatch(text.stdout, /fixture-only|Error:/); assert.equal(text.stderr, ''); assert.deepEqual(text.stdout.match(/^Host: .+$/gm), hosts.map(item => `Host: ${item}`)); diff --git a/test/release.test.ts b/test/release.test.ts index 05d4a99..a60cf33 100644 --- a/test/release.test.ts +++ b/test/release.test.ts @@ -49,13 +49,13 @@ test('release gate rejects moved main, absent/failed/pending CI and missing envi assert.throws(() => validateGitHubGate(sha, main, runs, { name: 'npm', protection_rules: [{ type: 'required_reviewers', reviewers: [] }] })); }); -test('0.3.3 stable staging requires verified 0.3.2 and unchanged preview/tag baselines', () => { +test('0.3.4 stable staging requires verified 0.3.3 and unchanged preview/tag baselines', () => { const previewDist = { integrity: 'sha512-h4SMvrQ/LWA9osd4EHIs9rSTqv1u+S3MXQAmn+yG/gZ9+7NwYMutq+Oa5K/0ggIq61Wfwdh11Cv+5YdEffiNMA==', attestations: { url: 'https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.1.0-preview.1' } }; - const stableDist = { integrity: 'sha512-VNlzBqELXLFI8uCopfEdYrfweIacHeP6dMK32/4vwQJy8fUIKC64c5WCE3IwQVeCTKGMz9mXSxhzZnXmSGMuuA==', - attestations: { url: 'https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.3.2' } }; - const current = { name: pkg.name, versions: { '0.3.2': { dist: stableDist }, '0.1.0-preview.1': { dist: previewDist } }, - 'dist-tags': { latest: '0.3.2', preview: '0.1.0-preview.1' } }; + const stableDist = { integrity: 'sha512-5OQ7eUlR5Oy3RHFRoR3FuBkV7LT72dfjfBEbWRCY5wYP7Dz8TmN2jlRPpo2SFHPAwosFc0WIt8Iis/vyHckxRg==', + attestations: { url: 'https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.3.3' } }; + const current = { name: pkg.name, versions: { '0.3.3': { dist: stableDist }, '0.1.0-preview.1': { dist: previewDist } }, + 'dist-tags': { latest: '0.3.3', preview: '0.1.0-preview.1' } }; validateRegistryState(pkg, 'stable-stage', current); assert.throws(() => validateRegistryState(pkg, 'stable-stage', null)); assert.throws(() => validateRegistryState(pkg, 'trusted-stage', current)); @@ -71,10 +71,10 @@ test('0.3.3 stable staging requires verified 0.3.2 and unchanged preview/tag bas versions: { ...current.versions, '0.1.0-preview.1': { dist: { ...previewDist, attestations: undefined } } } })); const preview = { ...pkg, version: '0.1.0-preview.2', publishConfig: { ...pkg.publishConfig, tag: 'preview' } }; assert.throws(() => validateRegistryState(preview, 'trusted-stage', { ...current, 'dist-tags': { latest: '0.1.0-preview.0' } }), /latest_points_to_prerelease/); - assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.3.2': { dist: { ...stableDist, integrity: 'tampered' } } } }), /verified_stable_changed/); - assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.3.2': { dist: { ...stableDist, attestations: undefined } } } })); + assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.3.3': { dist: { ...stableDist, integrity: 'tampered' } } } }), /verified_stable_changed/); + assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.3.3': { dist: { ...stableDist, attestations: undefined } } } })); validateRegistryState(preview, 'trusted-stage', { ...current, - 'dist-tags': { latest: '0.3.2', preview: '0.1.0-preview.1' } }); + 'dist-tags': { latest: '0.3.3', preview: '0.1.0-preview.1' } }); }); test('release artifact binds version, allowlist, commit, SHA-256 and registry integrity', () => { diff --git a/test/replies.test.ts b/test/replies.test.ts index 484587d..6bfb68e 100644 --- a/test/replies.test.ts +++ b/test/replies.test.ts @@ -141,7 +141,7 @@ if(args.at(-1)==='exit 0'){const mode=process.env.FAKE_PROBE; if(mode==='timeout'){console.error('ssh: connect to host origin port 22: Operation timed out SECRET-SENTINEL');process.exit(255);} console.error('SECRET-SENTINEL');process.exit(1);} if((process.env.FAKE_DOWN||'').split(',').includes(dest)){console.error('Connection refused');process.exit(255);} -if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.3 (typescript)');process.exit(0);} +if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.4 (typescript)');process.exit(0);} const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:{...process.env,SESSION_PEER_TAILSCALE:'off'}}); process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 }); writeFileSync(join(path, 'tailscale'), `#!${process.execPath} diff --git a/test/reply-agent-binding.test.ts b/test/reply-agent-binding.test.ts index 5e4eb67..679a857 100644 --- a/test/reply-agent-binding.test.ts +++ b/test/reply-agent-binding.test.ts @@ -45,7 +45,7 @@ function fixture(t: TestContext) { const fs=require('node:fs'),{spawnSync}=require('node:child_process'); fs.appendFileSync(${JSON.stringify(sshLog)},'1'); if(process.argv.includes('-G')){console.log('user fixture-user');process.exit(0);} -if(process.argv.at(-1).endsWith('--version')){console.log('session-peer 0.3.3 (typescript)');process.exit(0);} +if(process.argv.at(-1).endsWith('--version')){console.log('session-peer 0.3.4 (typescript)');process.exit(0);} const input=fs.readFileSync(0,'utf8'); const result=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env}); process.stdout.write(result.stdout);process.exit(result.status);`, { mode: 0o700 }); diff --git a/test/skill-metadata.test.ts b/test/skill-metadata.test.ts index 1522acf..58aae9a 100644 --- a/test/skill-metadata.test.ts +++ b/test/skill-metadata.test.ts @@ -28,7 +28,7 @@ test('finite runtime/profile matrix preserves published 0.3.2 and 0.3.3 profiles const legacy = fixtureText(), successor = fixtureText({ ...fields, version: '0.2.0', 'runtime-full-version': '0.3.2' }); assert.deepEqual(validateSkillMetadata(legacy, '0.3.2'), compatible); assert.deepEqual(validateSkillMetadata(successor, '0.3.2'), incompatible); - assert.equal(VERSION, '0.3.3'); + assert.equal(VERSION, '0.3.4'); for (const text of [legacy, successor]) { assert.deepEqual(validateSkillMetadata(text, '0.3.3'), compatible); assert.deepEqual(validateSkillMetadata(text), compatible); @@ -40,9 +40,9 @@ test('finite runtime/profile matrix preserves published 0.3.2 and 0.3.3 profiles } }); -test('unreleased 0.3.4 patch row permits only the two exact published profiles', () => { +test('0.3.4 patch row permits only the two exact published profiles', () => { const successor = { ...fields, version: '0.2.0', 'runtime-full-version': '0.3.2' }; - assert.equal(VERSION, '0.3.3'); + assert.equal(VERSION, '0.3.4'); for (const values of [fields, successor]) { assert.deepEqual(validateSkillMetadata(fixtureText(values), '0.3.4'), compatible); for (const key of Object.keys(values)) { diff --git a/test/ssh-hosts.test.ts b/test/ssh-hosts.test.ts index 0dfd438..06369bd 100644 --- a/test/ssh-hosts.test.ts +++ b/test/ssh-hosts.test.ts @@ -32,7 +32,7 @@ fs.appendFileSync(${JSON.stringify(log)},JSON.stringify({args,input})+'\\n'); const dest=args[args.indexOf('--')+1],list=k=>(process.env[k]||'').split(','); if(args.includes('-G')){if(process.env.FAKE_G==='fail'||list('FAKE_G_FAIL').includes(dest))process.exit(255);console.log('hostname x\\nuser config-user\\nport 22');process.exit(0);} if(list('FAKE_DOWN').includes(dest)){console.error('ssh: connect to host: Connection refused SECRET-SENTINEL');process.exit(255);} -if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.3 (typescript)');process.exit(0);} +if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.4 (typescript)');process.exit(0);} if(list('FAKE_LOSS').includes(dest))process.exit(255); const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env}); if(list('FAKE_MALFORMED_TARGET').includes(dest)){const value=JSON.parse(r.stdout);value.target.agent={toString:null};r.stdout=JSON.stringify(value);} @@ -339,7 +339,7 @@ const proxy=args.find(a=>a.startsWith('ProxyCommand=')); if(proxy){const cmd=proxy.slice(13).replace(/%(.)/g,(m,c)=>c==='%'?'%':c==='h'?name:c==='p'?port:c==='n'?dest:c==='r'?'config-user':m); if(spawnSync('/bin/sh',['-c','exec '+cmd],{stdio:'inherit'}).status!==0)process.exit(255);} fs.appendFileSync(${JSON.stringify(log)},JSON.stringify({hop:false,args,input})+'\\n'); -if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.3 (typescript)');process.exit(0);} +if(args.at(-1).endsWith('--version')){console.log('session-peer 0.3.4 (typescript)');process.exit(0);} const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env}); process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 }); const env = { PATH: bin + delimiter + f.env.PATH }; diff --git a/test/transport.test.ts b/test/transport.test.ts index 9ef1399..7489e62 100644 --- a/test/transport.test.ts +++ b/test/transport.test.ts @@ -167,20 +167,20 @@ test('SSH preflight reports allowlisted causes without leaking stderr or sending test('SSH request framing: no message in remote command; response loss never retries', async t => { const { path, env } = setup(t), messages = await inbox(t, path); const ssh = join(path, 'ssh'); - writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process'); const a=process.argv.slice(2);if(a.at(-1).endsWith('--version')){console.log('session-peer 0.3.3 (typescript)');process.exit(0);}if(a.at(-1).includes('SECRET-SENTINEL'))process.exit(99);const input=require('node:fs').readFileSync(0,'utf8');const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 }); + writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process'); const a=process.argv.slice(2);if(a.at(-1).endsWith('--version')){console.log('session-peer 0.3.4 (typescript)');process.exit(0);}if(a.at(-1).includes('SECRET-SENTINEL'))process.exit(99);const input=require('node:fs').readFileSync(0,'utf8');const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 }); const result = await invoke(['send', '--host', 'fixture', '--to', String(process.pid), '--message', 'SECRET-SENTINEL', '--no-from'], { ...env, PATH: path + delimiter + env.PATH }); assert.equal(result.status, 'posted'); assert.equal(messages.length, 1); // Valid JSON mixed with banner/trailing output is not a verified response. for (const command of ['send', 'list']) for (const prefix of [true, false]) { const response = JSON.stringify({ schemaVersion: 1, command, host: 'fixture', ok: true, status: 'posted', submitted: true, consumptionConfirmed: false }); const stdout = prefix ? 'untrusted banner\n' + response : response + '\ntrailing junk'; - writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.3.3 (typescript)');else console.log(${JSON.stringify(stdout)});`, { mode: 0o700 }); + writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.3.4 (typescript)');else console.log(${JSON.stringify(stdout)});`, { mode: 0o700 }); const unverified = await invoke([command, '--host', 'fixture', ...(command === 'send' ? ['--to', 'fixture', '--message', 'not-provable'] : [])], { ...env, PATH: path + delimiter + env.PATH }); assert.equal(unverified.status, command === 'send' ? 'unknown' : 'refused'); assert.equal(unverified.submitted, command === 'send' ? null : false); assert.equal(unverified.retryAllowed, false); } - writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.3.3 (typescript)');else process.exit(255);`, { mode: 0o700 }); + writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.3.4 (typescript)');else process.exit(255);`, { mode: 0o700 }); const lost = await invoke(['send', '--host', 'fixture', '--to', 'fixture', '--message', 'lost'], { ...env, PATH: path + delimiter + env.PATH }); assert.equal(lost.status, 'unknown'); assert.equal(lost.retryAllowed, false); assert.equal(messages.length, 1); }); @@ -329,7 +329,7 @@ test('a saved inactive home needs opt-in and still rejects relocated or unreadab test('SSH preflight accepts a verified version despite noisy stderr and propagates remote exit codes', async t => { const { path, env } = setup(t), messages = await inbox(t, path); const ssh = join(path, 'ssh'); - writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process');if(process.argv.at(-1).endsWith('--version')){console.error('curl: (7) Failed to connect: Connection refused');console.log('session-peer 0.3.3 (typescript)');process.exit(0);}const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input:require('node:fs').readFileSync(0,'utf8'),encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 }); + writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process');if(process.argv.at(-1).endsWith('--version')){console.error('curl: (7) Failed to connect: Connection refused');console.log('session-peer 0.3.4 (typescript)');process.exit(0);}const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input:require('node:fs').readFileSync(0,'utf8'),encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 }); const remoteEnv = { ...env, PATH: path + delimiter + env.PATH }; // #55 + #49/#51 over SSH: a flag-like body travels as --message= and posts literally. const sent = await invoke(['send', '--host', 'fixture', '--to', String(process.pid), '--message=--dry-run', '--no-from'], remoteEnv); diff --git a/test/updates.test.ts b/test/updates.test.ts index 01c6b7f..5572965 100644 --- a/test/updates.test.ts +++ b/test/updates.test.ts @@ -16,7 +16,7 @@ import { acquireLock, cacheDirectory, checkUpdate, compareVersions, manager, ref const cli = resolve('dist/cli.js'); const SENTINEL = 'REGISTRY-BODY-SENTINEL'; type Reply = { status?: number; body?: string; wait?: number }; -async function fixture(t: TestContext, reply: Reply = { body: JSON.stringify({ latest: '0.3.4', preview: '0.4.0-preview.1' }) }) { +async function fixture(t: TestContext, reply: Reply = { body: JSON.stringify({ latest: '0.3.5', preview: '0.4.0-preview.1' }) }) { const root = mkdtempSync(join(process.env.TASK_TEMP ?? tmpdir(), 'codex-updates-')); mkdirSync(join(root, '.claude/sessions'), { recursive: true }); const requests: string[] = []; @@ -82,11 +82,11 @@ test('package-manager guidance requires a positively identified owner; uncertain read: path => { if (!(path in files)) throw new Error('ENOENT'); return files[path]!; }, real: path => { if (path in links) return links[path]!; if (path in files) return path; throw new Error('ENOENT'); } }); - const declared = JSON.stringify({ dependencies: { 'session-peer': '0.3.3' } }); + const declared = JSON.stringify({ dependencies: { 'session-peer': '0.3.4' } }); const posixGlobal = (prefix: string) => probe({ [`${prefix}/lib/node_modules/session-peer/dist/cli.js`]: '' }, { [`${prefix}/bin/session-peer`]: `${prefix}/lib/node_modules/session-peer/dist/cli.js` }); const windowsGlobal = (prefix: string) => probe({ [`${prefix}/session-peer.cmd`]: '@ECHO off\r\nnode "%dp0%\\node_modules\\session-peer\\dist\\cli.js" %*\r\n' }); - const cmd = { npm: 'npm install --global --ignore-scripts session-peer@0.3.4' }; + const cmd = { npm: 'npm install --global --ignore-scripts session-peer@0.3.5' }; const cases: [string, NodeJS.Platform, Probe, string, string | null][] = [ ['/usr/local/lib/node_modules/session-peer/dist/updates.js', 'linux', posixGlobal('/usr/local'), 'npm', cmd.npm], ['/home/u/.nvm/versions/node/v24.16.0/lib/node_modules/session-peer/dist/updates.js', 'darwin', posixGlobal('/home/u/.nvm/versions/node/v24.16.0'), 'npm', cmd.npm], @@ -100,27 +100,27 @@ test('package-manager guidance requires a positively identified owner; uncertain ['/home/u/.nvm/versions/node/v24.16.0/lib/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null], ['/home/u/.local/lib/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/home/u/.local/lib/node_modules/session-peer/dist/cli.js': '' }, { '/home/u/.local/bin/session-peer': '/home/u/.local/share/pipx/venvs/session-peer/bin/session-peer' }), 'unknown', null], - ['/home/u/.volta/tools/image/packages/session-peer/lib/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'volta', 'volta install session-peer@0.3.4'], - ['C:/Users/u/AppData/Local/Volta/tools/image/packages/session-peer/node_modules/session-peer/dist/updates.js', 'win32', probe({}), 'volta', 'volta install session-peer@0.3.4'], + ['/home/u/.volta/tools/image/packages/session-peer/lib/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'volta', 'volta install session-peer@0.3.5'], + ['C:/Users/u/AppData/Local/Volta/tools/image/packages/session-peer/node_modules/session-peer/dist/updates.js', 'win32', probe({}), 'volta', 'volta install session-peer@0.3.5'], ['C:\\work\\app\\node_modules\\session-peer\\dist\\updates.js', 'win32', probe({ 'C:/work/app/package.json': declared }), 'npm_project', null], ['/work/app/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': declared }), 'npm_project', null], ['/work/app/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': JSON.stringify({ dependencies: { other: '1' } }) }), 'unknown', null], ['/work/app/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null], - ['/home/u/.local/share/pnpm/global/5/node_modules/.pnpm/session-peer@0.3.3/node_modules/session-peer/dist/updates.js', 'linux', - probe({ '/home/u/.local/share/pnpm/global/5/package.json': declared }), 'pnpm', 'pnpm add --global --ignore-scripts session-peer@0.3.4'], - ['/work/app/node_modules/.pnpm/session-peer@0.3.3/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': declared }), 'pnpm_project', null], - ['/work/app/node_modules/.pnpm/session-peer@0.3.3/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null], - ['/home/u/.config/yarn/global/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/home/u/.config/yarn/global/package.json': declared }), 'yarn', 'yarn global add --ignore-scripts session-peer@0.3.4'], + ['/home/u/.local/share/pnpm/global/5/node_modules/.pnpm/session-peer@0.3.4/node_modules/session-peer/dist/updates.js', 'linux', + probe({ '/home/u/.local/share/pnpm/global/5/package.json': declared }), 'pnpm', 'pnpm add --global --ignore-scripts session-peer@0.3.5'], + ['/work/app/node_modules/.pnpm/session-peer@0.3.4/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': declared }), 'pnpm_project', null], + ['/work/app/node_modules/.pnpm/session-peer@0.3.4/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null], + ['/home/u/.config/yarn/global/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/home/u/.config/yarn/global/package.json': declared }), 'yarn', 'yarn global add --ignore-scripts session-peer@0.3.5'], ['/home/u/.config/yarn/global/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null], - ['/home/u/.bun/install/global/node_modules/session-peer/dist/updates.js', 'darwin', probe({ '/home/u/.bun/install/global/package.json': declared }), 'bun', 'bun add --global --ignore-scripts session-peer@0.3.4'], - ['/home/u/.npm/_npx/abc123/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'npx', 'npx --yes --ignore-scripts session-peer@0.3.4 --version'], + ['/home/u/.bun/install/global/node_modules/session-peer/dist/updates.js', 'darwin', probe({ '/home/u/.bun/install/global/package.json': declared }), 'bun', 'bun add --global --ignore-scripts session-peer@0.3.5'], + ['/home/u/.npm/_npx/abc123/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'npx', 'npx --yes --ignore-scripts session-peer@0.3.5 --version'], ['/src/session-peer-ts/dist/updates.js', 'linux', probe({ '/src/session-peer-ts/package.json': JSON.stringify({ name: 'session-peer' }) }), 'source', null], ['/src/other/dist/updates.js', 'linux', probe({}), 'unknown', null], ['/opt/unrelated/node_modules/other/updates.js', 'linux', probe({}), 'unknown', null], ]; for (const [path, platform, files, owner, command] of cases) { assert.equal(manager(path, platform, files), owner, `${platform} ${path}`); - assert.equal(upgradeCommand(owner as Manager, '0.3.4'), command, path); + assert.equal(upgradeCommand(owner as Manager, '0.3.5'), command, path); if (command === null) assert.ok(upgradeGuidance(owner as Manager).length > 0); } }); @@ -157,7 +157,7 @@ test('update --check reports npm dist-tags, writes a private atomic cache and le assert.deepEqual(f.requests, ['/-/package/session-peer/dist-tags']); const v = r.value; assert.equal(v.command, 'update'); assert.equal(v.ok, true); assert.equal(v.package, 'session-peer'); - assert.equal(v.current, '0.3.3'); assert.equal(v.latest, '0.3.4'); assert.equal(v.channel, 'latest'); assert.equal(v.distTag, 'latest'); + assert.equal(v.current, '0.3.4'); assert.equal(v.latest, '0.3.5'); assert.equal(v.channel, 'latest'); assert.equal(v.distTag, 'latest'); assert.equal(v.source, 'npm_registry'); assert.equal(v.status, 'update_available'); assert.equal(v.outdated, true); assert.equal(v.updated, false); assert.equal(v.referenceVersion, '1.0.2'); assert.equal(v.cache, 'written'); // The test runs from a checkout; installed-path guidance is covered above and in package-smoke. @@ -168,31 +168,31 @@ test('update --check reports npm dist-tags, writes a private atomic cache and le assert.deepEqual([readFileSync(skill), readFileSync(python)], before); const cached = JSON.parse(readFileSync(f.file, 'utf8')); assert.deepEqual(Object.keys(cached).sort(), ['channel', 'checkedAt', 'latest', 'package', 'schemaVersion', 'source']); - assert.equal(cached.latest, '0.3.4'); + assert.equal(cached.latest, '0.3.5'); assert.deepEqual(readdirSync(f.cache), ['npm-update.json']); if (process.platform !== 'win32') { assert.equal(statSync(f.cache).mode & 0o777, 0o700); assert.equal(statSync(f.file).mode & 0o777, 0o600); } const text = await f.call(['update', '--check', '--output-format', 'text']); - assert.equal(text.code, 0); assert.match(text.stdout, /0\.3\.3 \(typescript\): 0\.3\.4 available on npm dist-tag latest/); + assert.equal(text.code, 0); assert.match(text.stdout, /0\.3\.4 \(typescript\): 0\.3\.5 available on npm dist-tag latest/); assert.match(text.stdout, /source checkout/); assert.equal(text.stderr, ''); - f.state.reply = { body: JSON.stringify({ latest: '0.3.3' }) }; + f.state.reply = { body: JSON.stringify({ latest: '0.3.4' }) }; const current = await f.call(['update', '--check', '--json']); assert.equal(current.value.status, 'up_to_date'); assert.equal(current.value.outdated, false); assert.equal(current.value.updateCommand, null); }); test('preview channel accepts prereleases, compares separately and does not replace the stable cache', async t => { const f = await fixture(t); - f.seed('0.3.3'); + f.seed('0.3.4'); const before = readFileSync(f.file, 'utf8'); const ahead = await f.call(['update', '--check', '--channel', 'preview', '--json']); assert.equal(ahead.code, 0); assert.equal(ahead.value.latest, '0.4.0-preview.1'); assert.equal(ahead.value.channel, 'preview'); assert.equal(ahead.value.status, 'update_available'); assert.equal(ahead.value.outdated, true); assert.equal(ahead.value.cache, 'not_applicable'); - f.state.reply = { body: JSON.stringify({ latest: '0.3.3', preview: '0.1.0-preview.1' }) }; + f.state.reply = { body: JSON.stringify({ latest: '0.3.4', preview: '0.1.0-preview.1' }) }; const older = await f.call(['update', '--check', '--channel=preview', '--json']); assert.equal(older.value.status, 'ahead'); assert.equal(older.value.outdated, false); assert.equal(older.value.updateCommand, null); assert.equal(readFileSync(f.file, 'utf8'), before); - f.state.reply = { body: JSON.stringify({ latest: '0.3.3' }) }; + f.state.reply = { body: JSON.stringify({ latest: '0.3.4' }) }; const missing = await f.call(['update', '--check', '--channel', 'preview', '--json']); assert.equal(missing.code, 1); assert.equal(missing.value.error, 'dist_tag_missing'); }); @@ -205,7 +205,7 @@ test('invalid, failed and slow registry responses refuse without printing bodies [{ body: JSON.stringify({ latest: 9, note: SENTINEL }) }, 'registry_response_invalid'], [{ body: `[${JSON.stringify(SENTINEL)}]` }, 'registry_response_invalid'], [{ body: `${SENTINEL} not json` }, 'registry_response_invalid'], - [{ body: JSON.stringify({ latest: '0.3.4', padding: SENTINEL.repeat(4000) }) }, 'registry_response_invalid'], + [{ body: JSON.stringify({ latest: '0.3.5', padding: SENTINEL.repeat(4000) }) }, 'registry_response_invalid'], [{ status: 500, body: SENTINEL }, 'registry_http_error'], [{ status: 302, body: SENTINEL }, 'registry_unreachable'], ]; @@ -217,7 +217,7 @@ test('invalid, failed and slow registry responses refuse without printing bodies const text = await f.call(['update', '--check', '--output-format=text']); assert.match(text.stdout, new RegExp(`Error: ${error}`)); assert.equal(text.stdout.includes(SENTINEL), false); } - f.state.reply = { body: JSON.stringify({ latest: '0.3.4' }), wait: 8000 }; + f.state.reply = { body: JSON.stringify({ latest: '0.3.5' }), wait: 8000 }; const started = Date.now(); const slow = await f.call(['update', '--check', '--json']); assert.equal(slow.value.error, 'registry_timeout'); assert.ok(Date.now() - started < 7000, 'bounded timeout'); @@ -264,7 +264,7 @@ test('update without --check refuses with ownership guidance and no network; opt test('cached notices are off by default, opt-in additive, stderr-only in text and opt-out wins', async t => { const f = await fixture(t); - f.seed('0.3.4'); + f.seed('0.3.5'); const plain = await f.call(['list', '--json']); assert.equal(plain.code, 0); assert.equal('clientUpdate' in plain.value, false); assert.equal(plain.stderr, ''); const on = { SESSION_PEER_UPDATE_NOTICE: '1' }; @@ -273,14 +273,14 @@ test('cached notices are off by default, opt-in additive, stderr-only in text an const { clientUpdate, ...rest } = json.value; assert.deepEqual(rest, plain.value); assert.deepEqual(Object.keys(clientUpdate).sort(), ['channel', 'checkedAt', 'command', 'current', 'guidance', 'latest', 'managedBy', 'schemaVersion', 'source', 'status']); - assert.equal(clientUpdate.status, 'available'); assert.equal(clientUpdate.current, '0.3.3'); assert.equal(clientUpdate.latest, '0.3.4'); + assert.equal(clientUpdate.status, 'available'); assert.equal(clientUpdate.current, '0.3.4'); assert.equal(clientUpdate.latest, '0.3.5'); assert.equal(clientUpdate.source, 'npm_registry_cache'); assert.match(clientUpdate.checkedAt, /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ$/); const textOff = await f.call(['list', '--output-format', 'text']); const textOn = await f.call(['list', '--output-format', 'text'], { SESSION_PEER_UPDATE_NOTICE: 'yes' }); assert.equal(textOn.stdout, textOff.stdout); assert.equal(textOn.code, textOff.code); - assert.match(textOn.stderr, /^Update available: session-peer 0\.3\.3 -> 0\.3\.4 \(npm dist-tag latest\)\. This is a source checkout; update it with git and rebuild\.\n$/); + assert.match(textOn.stderr, /^Update available: session-peer 0\.3\.4 -> 0\.3\.5 \(npm dist-tag latest\)\. This is a source checkout; update it with git and rebuild\.\n$/); const doctor = await f.call(['doctor', '--json'], on); - assert.equal(doctor.value.clientUpdate.latest, '0.3.4'); assert.equal(doctor.value.capabilities.updateCheck, true); assert.equal(doctor.value.capabilities.selfUpdate, false); + assert.equal(doctor.value.clientUpdate.latest, '0.3.5'); assert.equal(doctor.value.capabilities.updateCheck, true); assert.equal(doctor.value.capabilities.selfUpdate, false); const refusal = await f.call(['send', '--to', 'codex:bad', '--json', 'hi'], on); assert.equal(refusal.code, 2); assert.equal('clientUpdate' in refusal.value, false); for (const [args, extra] of [[['list', '--json', '--no-update-notice'], on], [['list', '--json'], { ...on, SESSION_PEER_NO_UPDATE_NOTICE: 'TRUE' }], @@ -288,7 +288,7 @@ test('cached notices are off by default, opt-in additive, stderr-only in text an const r = await f.call(args, extra); assert.equal(r.code, 0); assert.equal(r.stderr, ''); assert.equal(r.value?.clientUpdate, undefined); } - f.seed('0.3.3'); + f.seed('0.3.4'); assert.equal('clientUpdate' in (await f.call(['list', '--json'], on)).value, false); // Python's own update.json in the same directory is a different release stream. writeFileSync(join(f.cache, 'update.json'), JSON.stringify({ schemaVersion: 1, latest: '9.9.9', checkedAt: Math.floor(Date.now() / 1000) })); @@ -304,26 +304,26 @@ test('missing, expired and invalid caches refresh once in the background without assert.deepEqual(first.value, baseline.value); assert.equal(first.code, baseline.code); assert.equal(first.stderr, ''); await f.settled(1); assert.equal(f.requests.length, 1); - assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.4'); + assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.5'); if (process.platform !== 'win32') assert.equal(statSync(f.file).mode & 0o777, 0o600); - assert.equal((await f.call(['list', '--json'], on)).value.clientUpdate.latest, '0.3.4'); + assert.equal((await f.call(['list', '--json'], on)).value.clientUpdate.latest, '0.3.5'); assert.equal(f.requests.length, 1, 'fresh cache does not refresh'); - f.seed('0.3.4', 25 * 3600_000); + f.seed('0.3.5', 25 * 3600_000); assert.equal('clientUpdate' in (await f.call(['list', '--json'], on)).value, false, 'expired cache is not shown'); await f.settled(2); assert.equal(f.requests.length, 2); for (const content of ['not json', JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.0-rc.1', checkedAt: Date.now() }), - JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.4', checkedAt: Date.now() + 3600_000 }), 'x'.repeat(5000)]) { + JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.5', checkedAt: Date.now() + 3600_000 }), 'x'.repeat(5000)]) { const count: number = f.requests.length; writeFileSync(f.file, content); const r = await f.call(['list', '--json'], on); assert.equal(r.code, 0); assert.equal('clientUpdate' in r.value, false); await f.settled(count + 1); assert.equal(f.requests.length, count + 1); - assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.4'); + assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.5'); } }); test('concurrent invocations perform one bounded refresh; locks and failures back off', async t => { - const f = await fixture(t, { body: JSON.stringify({ latest: '0.3.4' }), wait: 1000 }); + const f = await fixture(t, { body: JSON.stringify({ latest: '0.3.5' }), wait: 1000 }); const on = { SESSION_PEER_UPDATE_NOTICE: '1' }; const results = await Promise.all(Array.from({ length: 8 }, () => f.call(['list', '--json'], on))); for (const r of results) { assert.equal(r.code, 0); assert.equal('clientUpdate' in r.value, false); } @@ -341,7 +341,7 @@ test('concurrent invocations perform one bounded refresh; locks and failures bac await f.settled(2); assert.equal(f.requests.length, 2); // Offline/failed refresh records a backoff without a version and never alters output. f.state.reply = { status: 503, body: SENTINEL }; - f.seed('0.3.4', 25 * 3600_000); + f.seed('0.3.5', 25 * 3600_000); const offline = await f.call(['list', '--json'], on); assert.equal(offline.code, 0); assert.equal(offline.stdout.includes(SENTINEL), false); await f.settled(3); assert.equal(f.requests.length, 3); @@ -360,7 +360,7 @@ test('remote wire requests never read, refresh or report client notices', async const on = { SESSION_PEER_UPDATE_NOTICE: '1' }; const wire = await f.call(['--stdio-request'], on, JSON.stringify({ schemaVersion: 1, args: ['list', '--json'] })); assert.equal(wire.code, 0); assert.equal('clientUpdate' in wire.value, false); - f.seed('0.3.4'); + f.seed('0.3.5'); const seeded = await f.call(['--stdio-request'], on, JSON.stringify({ schemaVersion: 1, args: ['doctor', '--json'] })); assert.equal('clientUpdate' in seeded.value, false); await delay(1000); @@ -371,7 +371,7 @@ test('remote wire requests never read, refresh or report client notices', async test('over SSH the local client adds its own notice; the receiver never reads or refreshes', { skip: process.platform === 'win32' && 'POSIX fake ssh' }, async t => { const f = await fixture(t); - f.seed('0.3.4'); + f.seed('0.3.5'); const bin = join(f.root, 'bin'), receiver = join(f.root, 'receiver-cache'); mkdirSync(bin); writeFileSync(join(bin, 'ssh'), `#!${process.execPath}\nconst {spawnSync}=require('node:child_process');const flag=process.argv.at(-1).endsWith('--version')?'--version':'--stdio-request';` + @@ -380,7 +380,7 @@ test('over SSH the local client adds its own notice; the receiver never reads or const on = { SESSION_PEER_UPDATE_NOTICE: '1', PATH: `${bin}${delimiter}${process.env.PATH}` }; const json = await f.call(['list', '--host', 'audit', '--json'], on); assert.equal(json.code, 0, json.stdout); assert.equal(json.value.host, 'audit'); - assert.equal(json.value.clientUpdate.latest, '0.3.4'); assert.equal(json.stderr, ''); + assert.equal(json.value.clientUpdate.latest, '0.3.5'); assert.equal(json.stderr, ''); const text = await f.call(['list', '--host', 'audit', '--output-format', 'text'], on); assert.equal(text.code, 0); assert.equal(text.stderr.match(/Update available/g)?.length, 1); const off = await f.call(['list', '--host', 'audit', '--json', '--no-update-notice'], on); @@ -430,9 +430,9 @@ test('explicit and background cache writes are serialized by the same lock and n const locked = await f.call(['update', '--check', '--json']); assert.equal(locked.code, 0); assert.equal(locked.value.latest, '0.3.9'); assert.equal(locked.value.cache, 'skipped_locked'); assert.equal(existsSync(f.file), false); - f.state.reply = { body: JSON.stringify({ latest: '0.3.4' }) }; + f.state.reply = { body: JSON.stringify({ latest: '0.3.5' }) }; await refreshCache({ ...f.env, SESSION_PEER_UPDATE_LOCK_TOKEN: background }); - assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.4'); assert.equal(existsSync(f.lock), false); + assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.5'); assert.equal(existsSync(f.lock), false); f.state.reply = { body: JSON.stringify({ latest: '0.3.9' }) }; const written = await f.call(['update', '--check', '--json']); assert.equal(written.value.cache, 'written'); assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.9'); @@ -440,7 +440,7 @@ test('explicit and background cache writes are serialized by the same lock and n writeFileSync(f.file, JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.9', checkedAt: Date.now() + 60_000 })); const newer = readFileSync(f.file, 'utf8'); const late = acquireLock(env)!; - f.state.reply = { body: JSON.stringify({ latest: '0.3.4' }) }; + f.state.reply = { body: JSON.stringify({ latest: '0.3.5' }) }; await refreshCache({ ...f.env, SESSION_PEER_UPDATE_LOCK_TOKEN: late }); assert.equal(readFileSync(f.file, 'utf8'), newer); assert.equal(existsSync(f.lock), false); const skipped = await f.call(['update', '--check', '--json']); @@ -465,7 +465,7 @@ test('concurrent lock acquisition across processes yields one owner, and none fo }); test('lock I/O failures: a failed token write removes only its own new lock; an unverifiable lock maps to cache failed', async t => { - const f = await fixture(t, { body: JSON.stringify({ latest: '0.3.4' }) }); + const f = await fixture(t, { body: JSON.stringify({ latest: '0.3.5' }) }); const env = { ...f.env }; t.after(() => { t.mock.restoreAll(); syncBuiltinESMExports(); }); const token = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; @@ -487,7 +487,7 @@ test('lock I/O failures: a failed token write removes only its own new lock; an syncBuiltinESMExports(); const result = await checkUpdate('latest', env); t.mock.restoreAll(); syncBuiltinESMExports(); - assert.equal(result.cache, 'failed'); assert.equal(result.latest, '0.3.4'); assert.equal(existsSync(f.file), false); + assert.equal(result.cache, 'failed'); assert.equal(result.latest, '0.3.5'); assert.equal(existsSync(f.file), false); // Release failed too, so the lock is left for documented manual recovery. assert.ok(existsSync(f.lock)); });