pocketshell 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +289 -12
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,19 +1,296 @@
|
|
|
1
|
-
#
|
|
1
|
+
# PocketShell
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
`pocketshell` host-agent CLI as a prebuilt binary.
|
|
3
|
+
> A real terminal in your pocket. End-to-end encrypted. WebRTC peer-to-peer.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
PocketShell turns your phone into a real PTY for the machines you care about — your dev box, a homelab Pi, a fleet of edge servers. Type into your phone, the bytes flow over an encrypted WebRTC data channel directly to your machine. The cloud signals; it does not see your shell.
|
|
6
|
+
|
|
7
|
+
This repository is the **open-source host agent** that runs on the machine you want to reach. The mobile apps (iOS / Android) and the backend control plane that brokers pairing and signaling are closed-source.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌──────────┐ ┌──────────────┐ ┌──────────┐
|
|
11
|
+
│ mobile │ ──── auth · signal ──── │ backend │ ──── auth · signal ──── │ host │
|
|
12
|
+
│ (closed) │ │ (closed) │ │ (this 👋)│
|
|
13
|
+
└────┬─────┘ └──────────────┘ └────┬─────┘
|
|
14
|
+
│ │
|
|
15
|
+
└──── WebRTC datachannel (DTLS) · ED25519-pinned SDP transcript ──── PTY ───┘
|
|
16
|
+
(your terminal traffic; never touches our servers)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The control plane refuses, throttles, or routes connections. Once a mobile↔host session is up, the backend cannot read or forge the terminal/files/agent bytes — they travel over a WebRTC data channel protected by DTLS (the WebRTC stack's standard transport encryption), and the DTLS peer-cert fingerprint is bound into a signed SDP transcript verified against the device's long-term ED25519 pairing key, so a backend that swaps SDPs to MitM is detected before any byte traverses the channel. A second E2E layer (ChaCha20-Poly1305 with X25519-derived session keys) wraps sensitive *signaling* messages that pass through the backend WS. Compromise of our backend does not compromise your shell.
|
|
20
|
+
|
|
21
|
+
> Host↔host direct file transfer (a Pro feature) is anchored to the mobile device as introducer: the phone signs an attestation binding both hosts' long-term public keys to the transfer, and each host verifies it against the mobile pubkey pinned at pairing — so the backend can relay SDP but cannot substitute a peer's identity. See [Security model](#security-model).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## What's in this repo
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
crates/
|
|
29
|
+
host-core/ # library: api client, websocket, pty, crypto, stats, audit, files
|
|
30
|
+
host-agent/ # the `pocketshell` CLI — login, pair, daemon, devices, stats
|
|
31
|
+
packaging/
|
|
32
|
+
debian/ # debian package + systemd --user unit
|
|
33
|
+
homebrew/ # homebrew formula
|
|
34
|
+
macos/ # launchd plist
|
|
35
|
+
mobile/src/locales/ # 12-language UI translation files (community-maintained)
|
|
36
|
+
mobile/src/i18n/ # i18n loader
|
|
37
|
+
LICENSE # Apache-2.0
|
|
38
|
+
NOTICE # required attribution per Apache-2.0 §4(d)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The `mobile/src/locales/` tree is here because translations benefit from being public — the rest of the mobile app source isn't in this repo.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
Pre-built binaries — pick either:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# install script
|
|
51
|
+
brew install cosign # one-time, for Sigstore signature verification
|
|
52
|
+
curl -fsSL https://pocketshell.app/install.sh | bash
|
|
53
|
+
|
|
54
|
+
# npm
|
|
7
55
|
npm i -g pocketshell
|
|
8
|
-
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Install script** detects your OS and arch, fetches the matching
|
|
59
|
+
tarball from the latest [GitHub
|
|
60
|
+
Release](https://github.com/yashagldit/PocketShell/releases), verifies
|
|
61
|
+
its SHA-256 checksum **and the Sigstore cosign keyless signature**
|
|
62
|
+
(pinned to this repo's release workflow identity), then installs to
|
|
63
|
+
`~/.local/bin/pocketshell` (or `/usr/local/bin/pocketshell` if run as
|
|
64
|
+
root). Without cosign installed the script refuses to proceed — the
|
|
65
|
+
SHA-256 alone is same-origin integrity, not authenticity, so a
|
|
66
|
+
compromised release publisher could serve a matching checksum for a
|
|
67
|
+
malicious binary. Set `POCKETSHELL_SKIP_COSIGN=1` only if you've
|
|
68
|
+
verified the artifact out of band.
|
|
69
|
+
|
|
70
|
+
Read the script first if you'd rather not pipe to bash blindly — it's
|
|
71
|
+
served from the static site and intentionally short.
|
|
72
|
+
|
|
73
|
+
**npm** ships the binary inside platform-specific packages
|
|
74
|
+
(`@pocketshell/darwin-arm64`, `@pocketshell/linux-x64-gnu`, etc.)
|
|
75
|
+
selected by npm's `os` / `cpu` / `libc` filters. The packages are
|
|
76
|
+
published from this repo's release workflow via [npm Trusted
|
|
77
|
+
Publishing](https://docs.npmjs.com/trusted-publishers/) (OIDC, no
|
|
78
|
+
long-lived tokens), with build provenance attached automatically.
|
|
79
|
+
|
|
80
|
+
From source (Rust 1.78+):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/yashagldit/PocketShell.git
|
|
84
|
+
cd PocketShell
|
|
85
|
+
cargo install --path crates/host-agent --root ~/.local
|
|
86
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Verify:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pocketshell --version
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Pair and run
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# 1. open the PocketShell app, tap "Pair host" — you get a 9-character code
|
|
99
|
+
pocketshell pair 7H2-9K4-PXM
|
|
100
|
+
|
|
101
|
+
# 2. start the daemon (runs as your user, not root)
|
|
9
102
|
pocketshell daemon start
|
|
103
|
+
|
|
104
|
+
# 3. status & logs
|
|
105
|
+
pocketshell daemon status
|
|
106
|
+
journalctl --user -fu pocketshell-host-agent # linux
|
|
107
|
+
log stream --predicate 'process == "pocketshell"' # macOS
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The daemon connects out to the signaling backend over WSS and waits for a peer offer. When your phone wants a session, the backend signals; the data channel is end-to-end encrypted between phone and host.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## How it works
|
|
115
|
+
|
|
116
|
+
Two planes, deliberately separated. The **control plane** (HTTPS + WebSocket to the backend) carries auth, pairing, presence, SDP offers / answers, and ICE candidates; sensitive signaling messages (file metadata) get an additional ChaCha20-Poly1305 envelope with X25519-derived keys so the backend WS sees only ciphertext. The **data plane** (WebRTC peer connection) carries every byte of your shell — PTY I/O, file chunks, stats samples, agent stdio — directly between phone and host, encrypted by DTLS at the WebRTC layer with the peer cert fingerprint bound into the ED25519-signed SDP.
|
|
117
|
+
|
|
118
|
+
```mermaid
|
|
119
|
+
flowchart TB
|
|
120
|
+
subgraph Mobile["📱 Mobile app (closed source)"]
|
|
121
|
+
direction TB
|
|
122
|
+
M_auth["Email OTP → JWT<br/>(15 min access · 30 day refresh)"]
|
|
123
|
+
M_key["ED25519 device key<br/>Expo SecureStore"]
|
|
124
|
+
M_ui["Terminal · Files · Stats<br/>Agent chat · Alerts · Workspaces"]
|
|
125
|
+
M_ws["Signaling WS client<br/>/ws/mobile"]
|
|
126
|
+
M_rtc["WebRTC peer<br/>(react-native-webrtc)"]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
subgraph Backend["☁️ Backend control plane (closed source)"]
|
|
130
|
+
direction TB
|
|
131
|
+
B_api["FastAPI · /api/v1/*<br/>auth · devices · pairing · sessions<br/>presence · turn · alerts · workspaces"]
|
|
132
|
+
B_ws["ConnectionManager<br/>/ws/mobile · /ws/host<br/>signaling relay only"]
|
|
133
|
+
B_pg[("PostgreSQL<br/>users · hosts · sessions<br/>trusted_devices · audit_log")]
|
|
134
|
+
B_redis[("Redis<br/>presence · live stats<br/>rate-limit · pub/sub relay")]
|
|
135
|
+
B_turn["TURN server<br/>(rotating HMAC creds)"]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
subgraph Host["🖥️ Host agent (this repo)"]
|
|
139
|
+
direction TB
|
|
140
|
+
H_cli["pocketshell CLI<br/>pair · daemon · devices · stats"]
|
|
141
|
+
H_key["ED25519 host key<br/>OS keychain"]
|
|
142
|
+
H_ws["transport.rs<br/>WS client → /ws/host"]
|
|
143
|
+
H_rtc["webrtc_manager.rs<br/>peer-per-mobile-device"]
|
|
144
|
+
H_pty["pty.rs · discovery.rs<br/>tmux / screen / shell"]
|
|
145
|
+
H_stats["stats.rs · files.rs<br/>agent_session.rs · alerts.rs"]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
M_auth -. "HTTPS · JWT" .-> B_api
|
|
149
|
+
M_ws -. "WSS · signaling<br/>session_offer / answer<br/>ice_candidate · stats_offer<br/>files_offer · agent_offer" .-> B_ws
|
|
150
|
+
H_cli -. "HTTPS · pair / refresh" .-> B_api
|
|
151
|
+
H_ws -. "WSS · signaling<br/>session_ack · session_event<br/>alert · host_summary<br/>stats_snapshot" .-> B_ws
|
|
152
|
+
|
|
153
|
+
B_api --- B_pg
|
|
154
|
+
B_ws --- B_redis
|
|
155
|
+
B_api --- B_turn
|
|
156
|
+
|
|
157
|
+
M_rtc <==>|"WebRTC data channels — E2E encrypted, never touches backend<br/><br/><b>terminal</b> (PTY bytes) · <b>stats</b> (JSON ~1 Hz)<br/><b>files</b> (framed JSON + chunks) · <b>agent-{id}</b> (Claude / Codex stdio)<br/><br/>P2P direct · TURN relay only when NAT blocks P2P"| H_rtc
|
|
158
|
+
|
|
159
|
+
B_turn -. "TURN relay path<br/>(opaque to backend)" .-> M_rtc
|
|
160
|
+
B_turn -. "TURN relay path" .-> H_rtc
|
|
161
|
+
|
|
162
|
+
classDef mobile fill:#1e3a5f,stroke:#4a90e2,color:#fff
|
|
163
|
+
classDef backend fill:#3a2f5c,stroke:#9b6dd1,color:#fff
|
|
164
|
+
classDef host fill:#2d4a3e,stroke:#5cb88c,color:#fff
|
|
165
|
+
class M_auth,M_key,M_ui,M_ws,M_rtc mobile
|
|
166
|
+
class B_api,B_ws,B_pg,B_redis,B_turn backend
|
|
167
|
+
class H_cli,H_key,H_ws,H_rtc,H_pty,H_stats host
|
|
10
168
|
```
|
|
11
169
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
170
|
+
### Session establishment (the happy path)
|
|
171
|
+
|
|
172
|
+
```mermaid
|
|
173
|
+
sequenceDiagram
|
|
174
|
+
autonumber
|
|
175
|
+
participant M as 📱 Mobile
|
|
176
|
+
participant B as ☁️ Backend
|
|
177
|
+
participant H as 🖥️ Host daemon
|
|
178
|
+
|
|
179
|
+
Note over M,H: Both sides are already authed and connected to /ws/mobile and /ws/host.
|
|
180
|
+
|
|
181
|
+
M->>B: POST /api/v1/sessions (host_id, purpose=terminal)
|
|
182
|
+
B-->>M: 201 · session_id (state = REQUESTED)
|
|
183
|
+
M->>B: WS session_offer (SDP, signed)
|
|
184
|
+
B->>H: relay session_offer
|
|
185
|
+
H->>H: verify ED25519 sig over SDP · spawn PTY
|
|
186
|
+
H-->>B: WS session_ack { accepted: true }
|
|
187
|
+
B-->>M: relay session_ack → state = APPROVED
|
|
188
|
+
H->>B: WS session_answer (SDP)
|
|
189
|
+
B->>M: relay session_answer
|
|
190
|
+
M-->>H: WS ice_candidate (× N, both directions, via backend)
|
|
191
|
+
H-->>M: WS ice_candidate (× N)
|
|
192
|
+
|
|
193
|
+
rect rgba(92, 184, 140, 0.15)
|
|
194
|
+
Note over M,H: ── WebRTC data channel established ──
|
|
195
|
+
M-->>H: terminal · keystrokes (DTLS over WebRTC datachannel)
|
|
196
|
+
H-->>M: terminal · PTY output
|
|
197
|
+
H-->>M: stats · JSON snapshots ~1 Hz
|
|
198
|
+
M-->>H: files · list / read / write
|
|
199
|
+
Note right of B: Backend sees zero bytes of this traffic.
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
Note over M,H: On host disconnect, state becomes DETACHED. PTY survives — mobile can rejoin.
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### What flows where
|
|
206
|
+
|
|
207
|
+
| Plane | Carrier | Payload |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| Auth | HTTPS `/api/v1/auth/*` | OTP, JWT issue / refresh, host pair / re-auth |
|
|
210
|
+
| Signaling | WSS `/ws/mobile`, `/ws/host` | `session_offer` · `session_answer` · `ice_candidate` · `session_event` · `stats_offer` · `files_offer` · `agent_offer` · `alert` · `host_summary` · `available_sessions` |
|
|
211
|
+
| Presence + lightweight metrics | WSS `/ws/host` | `heartbeat` (host_id, active_sessions, app_version) · `host_summary` (cpu%, ram%) · `stats_snapshot` and `stats_minute_batch` (cpu/mem/disk/load/battery/net IO/temps — **no processes, no command lines, no logged-in users**) |
|
|
212
|
+
| Metrics history (Pro) | HTTPS `/api/v1/presence/hosts/{id}/stats/history*` | minute / 30-min / hourly / daily aggregates served from PostgreSQL — used by the history charts even when no live session is active |
|
|
213
|
+
| **Terminal I/O** | **WebRTC `terminal` channel** | **PTY bytes — never touches backend** |
|
|
214
|
+
| **Live stats (rich)** | **WebRTC `stats` channel** | **Top-50 processes · per-core CPU · logged-in users · network connections · OS info · hostname — never touches backend** |
|
|
215
|
+
| **File ops** | **WebRTC `files` channel** | **Framed JSON + base64 chunks (sentinel `\x00PSFC`)** |
|
|
216
|
+
| **Agent chat** | **WebRTC `agent-{id}` channel** | **Claude / Codex stdio, JSON framed** |
|
|
217
|
+
|
|
218
|
+
The backend is a switchboard — it can refuse a connection, throttle it, or route it through TURN, but once the data channel is up it sees ciphertext at best and nothing at all on direct P2P.
|
|
219
|
+
|
|
220
|
+
**What the backend persists.** To deliver the Pro history-chart feature, the backend stores minute-bucket aggregates of numerical metrics (cpu/mem/disk/uptime/load/battery/net IO/disk IO/max temp + collected-at) per host, rolled up into 30-min/hourly/daily over time. It does **not** persist processes, command lines, hostnames, logged-in users, or network connection lists — those flow only over WebRTC and are dropped from Redis after ~35 minutes. Account deletion removes all of the above; see `app/services/account_deletion_service.py`.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Security model
|
|
225
|
+
|
|
226
|
+
| Layer | Primitive |
|
|
227
|
+
|---|---|
|
|
228
|
+
| Identity | ED25519 long-term host & device keys |
|
|
229
|
+
| Handshake | X25519 ephemeral · ED25519-signed SDP transcript binds DTLS cert fingerprint to the device pairing key |
|
|
230
|
+
| Data plane | WebRTC DTLS (transport encryption — AES-GCM by default, ciphersuite negotiated) |
|
|
231
|
+
| Signaling envelope | ChaCha20-Poly1305 AEAD · per-direction keys (wraps sensitive file/control payloads that pass through the backend WS) |
|
|
232
|
+
| KDF | HKDF-SHA256, domain-separated |
|
|
233
|
+
| Transport | WebRTC P2P · TURN fallback (rotating credentials) |
|
|
234
|
+
| Storage | OS keychain — Apple Keychain · Linux secret-service · Windows DPAPI · 0o600 file fallback for headless Linux |
|
|
235
|
+
|
|
236
|
+
Long-lived secrets (host private key, refresh token) live in the OS keychain via `crates/host-core/src/secret_store.rs`. Short-lived access tokens sit in `state.json` (mode `0o600`).
|
|
237
|
+
|
|
238
|
+
### Host↔host transfer trust model
|
|
239
|
+
|
|
240
|
+
The destination host verifies the source's SDP signature against a public key carried inside a *mobile-signed introducer attestation*, not a backend-relayed field. At transfer-initiation time the mobile app signs `(transfer_id, src_host_id, src_host_pubkey, dst_host_id, dst_host_pubkey, expires_at, nonce)` with its long-term device key; both hosts verify that signature against the mobile pubkey they pinned at pairing. The attestation is short-lived (≤ 5 min), bound to a single `transfer_id`, and both hosts additionally check that their own `host_id`+`public_key` match the attested values. Backend can relay SDP but cannot substitute a peer's identity. See `crates/host-core/src/signaling_crypto.rs` (`verify_host_transfer_attestation`) and `crates/host-core/src/daemon.rs` (`extract_and_verify_mobile_attestation`).
|
|
241
|
+
|
|
242
|
+
### Known gaps
|
|
243
|
+
|
|
244
|
+
- **No per-session approval prompt (by design)** — once a mobile device is trusted, every session offer is accepted immediately. The stolen-and-unlocked-phone case is handled by layered controls instead: app open is gated by device biometric, every paired device shows up in the in-app device list and can be revoked remotely from any other paired device, and sensitive ops are written to the local audit log on the host. Adding a per-session prompt would train users to tap "approve" reflexively and degrade the UX for the common case, so it isn't on the roadmap.
|
|
245
|
+
|
|
246
|
+
Found something? Email `support@pocketshell.app`. Public issues are fine for non-sensitive bugs.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Building & testing
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
cargo build -p host-agent # debug build
|
|
254
|
+
cargo build -p host-agent --release # release
|
|
255
|
+
cargo test -p host-core # core library tests
|
|
256
|
+
cargo test -p host-agent # CLI tests
|
|
257
|
+
RUST_LOG=debug cargo run -p host-agent -- daemon run # run with verbose logs
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Targets supported today: `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `aarch64-apple-darwin`, `x86_64-apple-darwin`. Windows is not yet supported (PRs welcome).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Translations
|
|
265
|
+
|
|
266
|
+
The mobile app currently ships in **12 languages**: English, German, Spanish, French, Hindi, Italian, Japanese, Korean, Portuguese (BR), Russian, Simplified Chinese, Traditional Chinese.
|
|
267
|
+
|
|
268
|
+
Files live under `mobile/src/locales/<lang>/*.json`. To add or improve a language, edit the JSON files and open a PR — no app build required. The strings are loaded by `mobile/src/i18n/index.ts`.
|
|
269
|
+
|
|
270
|
+
If you want a new language added, open an issue and we'll seed the directory.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Contributing
|
|
275
|
+
|
|
276
|
+
PRs are welcome. A few notes:
|
|
277
|
+
|
|
278
|
+
- **Source of truth.** This repo is mirrored from a private monorepo. We accept patches here and apply them upstream. Force-pushes happen on every release — keep your fork rebased rather than merged.
|
|
279
|
+
- **Scope.** Issues for the mobile app or the backend get redirected; this repo is the host agent and the locales.
|
|
280
|
+
- **Style.** Match the surrounding code. `cargo fmt` and `cargo clippy --all-targets -- -D warnings` should pass.
|
|
281
|
+
- **Commits.** Conventional commits preferred (`feat:`, `fix:`, `refactor:`).
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
Licensed under the [Apache License, Version 2.0](./LICENSE).
|
|
288
|
+
|
|
289
|
+
Unless you explicitly state otherwise, any contribution intentionally
|
|
290
|
+
submitted for inclusion in this project shall be licensed as Apache-2.0,
|
|
291
|
+
without any additional terms or conditions, per §5 of the License. See
|
|
292
|
+
also [NOTICE](./NOTICE).
|
|
293
|
+
|
|
294
|
+
---
|
|
17
295
|
|
|
18
|
-
|
|
19
|
-
full docs, see https://pocketshell.app.
|
|
296
|
+
**Links** · [pocketshell.app](https://pocketshell.app) · [Privacy](https://pocketshell.app/privacy) · [Terms](https://pocketshell.app/terms) · [Support](https://pocketshell.app/support)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pocketshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Secure mobile-to-host terminal access — installs the pocketshell host-agent CLI.",
|
|
5
5
|
"homepage": "https://pocketshell.app",
|
|
6
6
|
"repository": {
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"node": ">=14"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@pocketshell/darwin-arm64": "1.0.
|
|
22
|
-
"@pocketshell/linux-x64-gnu": "1.0.
|
|
23
|
-
"@pocketshell/linux-arm64-gnu": "1.0.
|
|
24
|
-
"@pocketshell/linux-x64-musl": "1.0.
|
|
21
|
+
"@pocketshell/darwin-arm64": "1.0.4",
|
|
22
|
+
"@pocketshell/linux-x64-gnu": "1.0.4",
|
|
23
|
+
"@pocketshell/linux-arm64-gnu": "1.0.4",
|
|
24
|
+
"@pocketshell/linux-x64-musl": "1.0.4"
|
|
25
25
|
}
|
|
26
26
|
}
|