@alivault/pico 0.13.0 → 0.14.1

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.
Files changed (32) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{index-CW1T9dk-.js → index-DBVxXDtw.js} +4 -4
  3. package/.output/public/assets/routes-yUxD2q-l.js +2194 -0
  4. package/.output/public/assets/styles-CUVz7jV9.css +2 -0
  5. package/.output/public/screenshots/pico-ios-dark.png +0 -0
  6. package/.output/public/screenshots/pico-ios-light.png +0 -0
  7. package/.output/public/screenshots/pico-macos-dark.jpg +0 -0
  8. package/.output/public/screenshots/pico-macos-light.jpg +0 -0
  9. package/.output/server/_chunks/ssr-renderer.mjs +3 -3
  10. package/.output/server/_libs/crossws.mjs +2929 -0
  11. package/.output/server/_libs/h3+rou3+srvx.mjs +7 -1
  12. package/.output/server/_libs/unctx.mjs +1 -1
  13. package/.output/server/_routes/api/terminal/[id]/ws.mjs +8874 -0
  14. package/.output/server/_ssr/{router-l__gf5Y7.mjs → router-CN4JMm2A.mjs} +1074 -316
  15. package/.output/server/_ssr/{routes-DySm_W8N.mjs → routes-BsQ26pbY.mjs} +738 -189
  16. package/.output/server/_ssr/{routes-CuWg5WTI.mjs → routes-DbjuMLFI.mjs} +19 -10
  17. package/.output/server/_ssr/ssr.mjs +3 -3
  18. package/.output/server/_ssr/start-jIczsXlR.mjs +4 -0
  19. package/.output/server/{_tanstack-start-manifest_v-CVoKKKc_.mjs → _tanstack-start-manifest_v-Bs4g_j1_.mjs} +7 -4
  20. package/.output/server/index.mjs +644 -565
  21. package/.output/server/node_modules/node-pty/build/Makefile +2 -2
  22. package/.output/server/node_modules/node-pty/build/pty.target.mk +14 -14
  23. package/README.md +68 -6
  24. package/bin/pico.mjs +103 -1
  25. package/package.json +4 -4
  26. package/public/screenshots/pico-ios-dark.png +0 -0
  27. package/public/screenshots/pico-ios-light.png +0 -0
  28. package/public/screenshots/pico-macos-dark.jpg +0 -0
  29. package/public/screenshots/pico-macos-light.jpg +0 -0
  30. package/.output/public/assets/routes-2LboeUx4.js +0 -2194
  31. package/.output/public/assets/styles-B1ITYh-r.css +0 -2
  32. package/.output/server/_ssr/start-Captdpmb.mjs +0 -4
@@ -353,8 +353,8 @@ ifeq ($(strip $(foreach prefix,$(NO_LOAD),\
353
353
  endif
354
354
 
355
355
  quiet_cmd_regen_makefile = ACTION Regenerating $@
356
- cmd_regen_makefile = cd $(srcdir); /home/runner/setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/home/runner/.cache/node-gyp/22.22.3" "-Dnode_gyp_dir=/home/runner/setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp" "-Dnode_lib_file=/home/runner/.cache/node-gyp/22.22.3/<(target_arch)/node.lib" "-Dmodule_root_dir=/home/runner/work/pico/pico/node_modules/.pnpm/node-pty@1.1.0/node_modules/node-pty" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/home/runner/work/pico/pico/node_modules/.pnpm/node-pty@1.1.0/node_modules/node-pty/build/config.gypi -I/home/runner/setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp/addon.gypi -I/home/runner/.cache/node-gyp/22.22.3/include/node/common.gypi "--toplevel-dir=." binding.gyp
357
- Makefile: $(srcdir)/../../../../../../../../.cache/node-gyp/22.22.3/include/node/common.gypi $(srcdir)/../../../../../../../../setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp/addon.gypi $(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api/except.gypi $(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api/node_addon_api.gyp $(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api/noexcept.gypi $(srcdir)/binding.gyp $(srcdir)/build/config.gypi
356
+ cmd_regen_makefile = cd $(srcdir); /home/runner/setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/home/runner/.cache/node-gyp/22.23.1" "-Dnode_gyp_dir=/home/runner/setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp" "-Dnode_lib_file=/home/runner/.cache/node-gyp/22.23.1/<(target_arch)/node.lib" "-Dmodule_root_dir=/home/runner/work/pico/pico/node_modules/.pnpm/node-pty@1.1.0/node_modules/node-pty" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/home/runner/work/pico/pico/node_modules/.pnpm/node-pty@1.1.0/node_modules/node-pty/build/config.gypi -I/home/runner/setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp/addon.gypi -I/home/runner/.cache/node-gyp/22.23.1/include/node/common.gypi "--toplevel-dir=." binding.gyp
357
+ Makefile: $(srcdir)/../../../../../../../../.cache/node-gyp/22.23.1/include/node/common.gypi $(srcdir)/../../../../../../../../setup-pnpm/node_modules/.pnpm/pnpm@10.33.0/node_modules/pnpm/dist/node_modules/node-gyp/addon.gypi $(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api/except.gypi $(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api/node_addon_api.gyp $(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api/noexcept.gypi $(srcdir)/binding.gyp $(srcdir)/build/config.gypi
358
358
  $(call do_cmd,regen_makefile)
359
359
 
360
360
  # "all" is a concatenation of the "all" targets from all the included
@@ -42,13 +42,13 @@ CFLAGS_CC_Debug := \
42
42
  -std=gnu++17
43
43
 
44
44
  INCS_Debug := \
45
- -I/home/runner/.cache/node-gyp/22.22.3/include/node \
46
- -I/home/runner/.cache/node-gyp/22.22.3/src \
47
- -I/home/runner/.cache/node-gyp/22.22.3/deps/openssl/config \
48
- -I/home/runner/.cache/node-gyp/22.22.3/deps/openssl/openssl/include \
49
- -I/home/runner/.cache/node-gyp/22.22.3/deps/uv/include \
50
- -I/home/runner/.cache/node-gyp/22.22.3/deps/zlib \
51
- -I/home/runner/.cache/node-gyp/22.22.3/deps/v8/include \
45
+ -I/home/runner/.cache/node-gyp/22.23.1/include/node \
46
+ -I/home/runner/.cache/node-gyp/22.23.1/src \
47
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/openssl/config \
48
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/openssl/openssl/include \
49
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/uv/include \
50
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/zlib \
51
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/v8/include \
52
52
  -I$(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api
53
53
 
54
54
  DEFS_Release := \
@@ -89,13 +89,13 @@ CFLAGS_CC_Release := \
89
89
  -std=gnu++17
90
90
 
91
91
  INCS_Release := \
92
- -I/home/runner/.cache/node-gyp/22.22.3/include/node \
93
- -I/home/runner/.cache/node-gyp/22.22.3/src \
94
- -I/home/runner/.cache/node-gyp/22.22.3/deps/openssl/config \
95
- -I/home/runner/.cache/node-gyp/22.22.3/deps/openssl/openssl/include \
96
- -I/home/runner/.cache/node-gyp/22.22.3/deps/uv/include \
97
- -I/home/runner/.cache/node-gyp/22.22.3/deps/zlib \
98
- -I/home/runner/.cache/node-gyp/22.22.3/deps/v8/include \
92
+ -I/home/runner/.cache/node-gyp/22.23.1/include/node \
93
+ -I/home/runner/.cache/node-gyp/22.23.1/src \
94
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/openssl/config \
95
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/openssl/openssl/include \
96
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/uv/include \
97
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/zlib \
98
+ -I/home/runner/.cache/node-gyp/22.23.1/deps/v8/include \
99
99
  -I$(srcdir)/../../../node-addon-api@7.1.1/node_modules/node-addon-api
100
100
 
101
101
  OBJS := \
package/README.md CHANGED
@@ -1,16 +1,30 @@
1
1
  # Pico
2
2
 
3
- Pico is a local, keyboard-friendly browser workspace for Pi coding-agent sessions.
3
+ Pico is a local, keyboard-friendly workspace for Pi coding-agent sessions, available in the browser and as a native SwiftUI app for macOS and iOS.
4
4
 
5
5
  It gives you a persistent session browser, a live conversation shell, git tools, and project-aware prompt helpers in one app.
6
6
 
7
+ ## Browser workspace
8
+
7
9
  ![Pico workspace showing the session browser, conversation shell, composer, and git tools](public/screenshots/pico-workspace.png)
8
10
 
9
11
  ![Pico workspace showing project-aware context and coding-agent workflow](public/screenshots/pico-workspace-alt.png)
10
12
 
11
- | Dark mobile | Light mobile |
12
- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
13
- | ![Pico mobile session view in dark mode](public/screenshots/pico-mobile.png) | ![Pico mobile session view in light mode](public/screenshots/pico-mobile-alt.png) |
13
+ | Dark mobile browser | Light mobile browser |
14
+ | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
15
+ | ![Pico mobile browser in dark mode](public/screenshots/pico-mobile.png) | ![Pico mobile browser in light mode](public/screenshots/pico-mobile-alt.png) |
16
+
17
+ ## Native Apple clients
18
+
19
+ Pico also includes a shared SwiftUI client with platform-native layouts for macOS and iOS. Both clients connect to the same local Pico server, stream live session updates, manage conversations, and provide native Git and project-file workflows.
20
+
21
+ | macOS dark | macOS light |
22
+ | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
23
+ | ![Pico native macOS client in dark mode](public/screenshots/pico-macos-dark.jpg) | ![Pico native macOS client in light mode](public/screenshots/pico-macos-light.jpg) |
24
+
25
+ | iOS dark | iOS light |
26
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
27
+ | ![Pico native iOS client in dark mode](public/screenshots/pico-ios-dark.png) | ![Pico native iOS client in light mode](public/screenshots/pico-ios-light.png) |
14
28
 
15
29
  ## What Pico gives you
16
30
 
@@ -27,7 +41,7 @@ It gives you a persistent session browser, a live conversation shell, git tools,
27
41
 
28
42
  ## Built on Pi
29
43
 
30
- Pico runs Pi locally through the bundled `@earendil-works/pi-coding-agent` SDK dependency, pinned to `0.74.0` for reproducible installs. You do not need a separate global Pi install for normal use.
44
+ Pico runs Pi locally through the bundled `@earendil-works/pi-coding-agent` SDK dependency, pinned to `0.80.6` for reproducible installs. You do not need a separate global Pi install for normal use.
31
45
 
32
46
  If you intentionally want to test Pico against a different Pi SDK checkout or install, set:
33
47
 
@@ -43,7 +57,7 @@ pnpm update:pi
43
57
 
44
58
  ## Getting started
45
59
 
46
- Run Pico without cloning the repo:
60
+ Run Pico without cloning the repo (Node.js 22.19.0 or newer is required):
47
61
 
48
62
  ```bash
49
63
  npx @alivault/pico
@@ -94,6 +108,53 @@ Then open:
94
108
  http://localhost:3141
95
109
  ```
96
110
 
111
+ ## Developing the native macOS and iOS app
112
+
113
+ The native SwiftUI client lives in `apps/apple/Pico` and builds for both macOS and iOS. It connects to an already-running Pico server over HTTP JSON and SSE; it does not embed the Pi runtime. The native clients are currently built from source and are not distributed through the npm package.
114
+
115
+ Open the shared project in Xcode:
116
+
117
+ ```bash
118
+ open apps/apple/Pico/Pico.xcodeproj
119
+ ```
120
+
121
+ Select the `Pico` scheme and a macOS, iPhone, or iPad destination.
122
+
123
+ Build and launch the macOS app:
124
+
125
+ ```bash
126
+ xcodebuild \
127
+ -project apps/apple/Pico/Pico.xcodeproj \
128
+ -scheme Pico \
129
+ -destination 'platform=macOS' \
130
+ -derivedDataPath /tmp/pico-macos-build \
131
+ build
132
+
133
+ open /tmp/pico-macos-build/Build/Products/Debug/Pico.app
134
+ ```
135
+
136
+ The macOS client defaults to `http://localhost:3141`. Keep Pico on a trusted local machine or network; the server does not currently provide remote pairing or token authentication.
137
+
138
+ Run the shared native tests on macOS:
139
+
140
+ ```bash
141
+ xcodebuild \
142
+ -project apps/apple/Pico/Pico.xcodeproj \
143
+ -scheme Pico \
144
+ -destination 'platform=macOS' \
145
+ test
146
+ ```
147
+
148
+ Run the iOS tests with an installed simulator:
149
+
150
+ ```bash
151
+ xcodebuild \
152
+ -project apps/apple/Pico/Pico.xcodeproj \
153
+ -scheme Pico \
154
+ -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
155
+ test
156
+ ```
157
+
97
158
  ## Development commands
98
159
 
99
160
  ```bash
@@ -122,6 +183,7 @@ The release script verifies a clean, up-to-date `main`, runs checks and build, b
122
183
  Pico is built with:
123
184
 
124
185
  - TanStack Start, Router, Query, Store, Hotkeys, and Pacer
186
+ - Native SwiftUI for macOS and iOS
125
187
  - React 19
126
188
  - TypeScript
127
189
  - Vite+ and Nitro
package/bin/pico.mjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { spawn, spawnSync } from "node:child_process"
4
4
  import { existsSync, readFileSync } from "node:fs"
5
+ import { createServer } from "node:net"
5
6
  import process from "node:process"
6
7
  import { dirname, join } from "node:path"
7
8
  import { fileURLToPath } from "node:url"
@@ -9,6 +10,34 @@ import { fileURLToPath } from "node:url"
9
10
  const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)))
10
11
  const packageJsonPath = join(packageRoot, "package.json")
11
12
  const serverEntry = join(packageRoot, ".output", "server", "index.mjs")
13
+ const minimumNodeVersion = [22, 19, 0]
14
+ const minimumNodeVersionLabel = "22.19.0"
15
+ const portSearchLimit = 100
16
+
17
+ function nodeVersionMeetsMinimum(version) {
18
+ const parts = version.split(".").map((part) => Number(part))
19
+
20
+ for (let index = 0; index < minimumNodeVersion.length; index += 1) {
21
+ const actual = Number.isFinite(parts[index]) ? parts[index] : 0
22
+ const required = minimumNodeVersion[index]
23
+ if (actual > required) return true
24
+ if (actual < required) return false
25
+ }
26
+
27
+ return true
28
+ }
29
+
30
+ function ensureSupportedNodeVersion() {
31
+ if (nodeVersionMeetsMinimum(process.versions.node)) return
32
+
33
+ console.error(
34
+ `Pico requires Node.js >=${minimumNodeVersionLabel}; current version is ${process.versions.node}.`
35
+ )
36
+ console.error("Upgrade Node.js and run Pico again.")
37
+ process.exit(1)
38
+ }
39
+
40
+ ensureSupportedNodeVersion()
12
41
 
13
42
  function readPackageMetadata() {
14
43
  try {
@@ -41,7 +70,7 @@ Commands:
41
70
  update Update the globally installed Pico package to latest
42
71
 
43
72
  Options:
44
- --port <port> Port to listen on (default: 3141)
73
+ --port <port> Port to listen on (default: 3141; tries next free port)
45
74
  --host <host> Host to bind to
46
75
  --open Open Pico in your browser (default)
47
76
  --no-open Do not open a browser window
@@ -209,6 +238,67 @@ function browserHostFor(host) {
209
238
  return host
210
239
  }
211
240
 
241
+ function normalizePort(port) {
242
+ const portNumber = Number(port)
243
+ if (!Number.isInteger(portNumber) || portNumber < 0 || portNumber > 65_535) {
244
+ console.error(`Invalid port: ${port}`)
245
+ process.exit(1)
246
+ }
247
+ return portNumber
248
+ }
249
+
250
+ function canRetryPortError(error) {
251
+ return error?.code === "EADDRINUSE"
252
+ }
253
+
254
+ function isPortAvailable(port, host) {
255
+ return new Promise((resolve, reject) => {
256
+ const probe = createServer()
257
+ const listenOptions = host ? { port, host } : { port }
258
+
259
+ probe.once("error", (error) => {
260
+ if (canRetryPortError(error)) {
261
+ resolve(false)
262
+ return
263
+ }
264
+ reject(error)
265
+ })
266
+ probe.once("listening", () => {
267
+ probe.close((error) => {
268
+ if (error) {
269
+ reject(error)
270
+ return
271
+ }
272
+ resolve(true)
273
+ })
274
+ })
275
+ probe.listen(listenOptions)
276
+ })
277
+ }
278
+
279
+ async function resolveAvailablePort(port, host) {
280
+ const requestedPort = normalizePort(port)
281
+ if (requestedPort === 0) return String(requestedPort)
282
+
283
+ const lastPort = Math.min(65_535, requestedPort + portSearchLimit - 1)
284
+
285
+ for (let candidate = requestedPort; candidate <= lastPort; candidate += 1) {
286
+ if (await isPortAvailable(candidate, host)) {
287
+ if (candidate !== requestedPort) {
288
+ console.warn(
289
+ `Port ${requestedPort} is not available; using ${candidate} instead.`
290
+ )
291
+ }
292
+ return String(candidate)
293
+ }
294
+ }
295
+
296
+ console.error(
297
+ `No available port found between ${requestedPort} and ${lastPort}.`
298
+ )
299
+ process.exit(1)
300
+ }
301
+
212
302
  async function waitForServer(url, timeoutMs = 15_000) {
213
303
  const startedAt = Date.now()
214
304
 
@@ -254,6 +344,18 @@ if (!existsSync(serverEntry)) {
254
344
  process.exit(1)
255
345
  }
256
346
 
347
+ try {
348
+ options.port = await resolveAvailablePort(options.port, options.host)
349
+ } catch (error) {
350
+ console.error(
351
+ `Could not check port ${options.port}${
352
+ options.host ? ` on ${options.host}` : ""
353
+ }.`
354
+ )
355
+ console.error(error?.message ?? String(error))
356
+ process.exit(1)
357
+ }
358
+
257
359
  const url = `http://${browserHostFor(options.host)}:${options.port}`
258
360
 
259
361
  console.log(`Starting Pico at ${url}`)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alivault/pico",
3
- "version": "0.13.0",
4
- "description": "A local, keyboard-friendly browser workspace for Pi coding-agent sessions.",
3
+ "version": "0.14.1",
4
+ "description": "A local workspace for Pi coding-agent sessions with browser, macOS, and iOS clients.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "bin": {
@@ -25,7 +25,7 @@
25
25
  "url": "https://github.com/alivault/pico/issues"
26
26
  },
27
27
  "engines": {
28
- "node": ">=20.6.0"
28
+ "node": ">=22.19.0"
29
29
  },
30
30
  "publishConfig": {
31
31
  "access": "public"
@@ -44,7 +44,7 @@
44
44
  "update:pi": "pnpm update @earendil-works/pi-coding-agent --latest"
45
45
  },
46
46
  "dependencies": {
47
- "@earendil-works/pi-coding-agent": "0.74.0",
47
+ "@earendil-works/pi-coding-agent": "0.80.6",
48
48
  "node-pty": "^1.1.0",
49
49
  "tslib": "^2.8.1"
50
50
  },