@alivault/pico 0.13.0 → 0.14.0
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/.output/nitro.json +1 -1
- package/.output/public/assets/{index-CW1T9dk-.js → index-DBVxXDtw.js} +4 -4
- package/.output/public/assets/routes-yUxD2q-l.js +2194 -0
- package/.output/public/assets/styles-CUVz7jV9.css +2 -0
- package/.output/public/screenshots/pico-ios-dark.png +0 -0
- package/.output/public/screenshots/pico-ios-light.png +0 -0
- package/.output/public/screenshots/pico-macos-dark.jpg +0 -0
- package/.output/public/screenshots/pico-macos-light.jpg +0 -0
- package/.output/server/_chunks/ssr-renderer.mjs +3 -3
- package/.output/server/_libs/crossws.mjs +2929 -0
- package/.output/server/_libs/h3+rou3+srvx.mjs +7 -1
- package/.output/server/_libs/unctx.mjs +1 -1
- package/.output/server/_routes/api/terminal/[id]/ws.mjs +8874 -0
- package/.output/server/_ssr/{router-l__gf5Y7.mjs → router-CN4JMm2A.mjs} +1074 -316
- package/.output/server/_ssr/{routes-DySm_W8N.mjs → routes-BsQ26pbY.mjs} +738 -189
- package/.output/server/_ssr/{routes-CuWg5WTI.mjs → routes-DbjuMLFI.mjs} +19 -10
- package/.output/server/_ssr/ssr.mjs +3 -3
- package/.output/server/_ssr/start-jIczsXlR.mjs +4 -0
- package/.output/server/{_tanstack-start-manifest_v-CVoKKKc_.mjs → _tanstack-start-manifest_v-Bs4g_j1_.mjs} +7 -4
- package/.output/server/index.mjs +621 -542
- package/.output/server/node_modules/node-pty/build/Makefile +2 -2
- package/.output/server/node_modules/node-pty/build/pty.target.mk +14 -14
- package/README.md +68 -6
- package/bin/pico.mjs +103 -1
- package/package.json +4 -4
- package/public/screenshots/pico-ios-dark.png +0 -0
- package/public/screenshots/pico-ios-light.png +0 -0
- package/public/screenshots/pico-macos-dark.jpg +0 -0
- package/public/screenshots/pico-macos-light.jpg +0 -0
- package/.output/public/assets/routes-2LboeUx4.js +0 -2194
- package/.output/public/assets/styles-B1ITYh-r.css +0 -2
- 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.
|
|
357
|
-
Makefile: $(srcdir)/../../../../../../../../.cache/node-gyp/22.
|
|
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.
|
|
46
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
47
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
48
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
49
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
50
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
51
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
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.
|
|
93
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
94
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
95
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
96
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
97
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
98
|
-
-I/home/runner/.cache/node-gyp/22.
|
|
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
|
|
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
|

|
|
8
10
|
|
|
9
11
|

|
|
10
12
|
|
|
11
|
-
| Dark mobile
|
|
12
|
-
|
|
|
13
|
-
|  |  |
|
|
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
|
+
|  |  |
|
|
24
|
+
|
|
25
|
+
| iOS dark | iOS light |
|
|
26
|
+
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
27
|
+
|  |  |
|
|
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.
|
|
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.
|
|
4
|
-
"description": "A local
|
|
3
|
+
"version": "0.14.0",
|
|
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": ">=
|
|
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.
|
|
47
|
+
"@earendil-works/pi-coding-agent": "0.80.6",
|
|
48
48
|
"node-pty": "^1.1.0",
|
|
49
49
|
"tslib": "^2.8.1"
|
|
50
50
|
},
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|