OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note commits / b2203ab

Migrate OTA: pear-runtime (Hyperdrive) → pgh content-addressed updates (0.2.1)
hypernote's pear-runtime OTA was doubly-dead (no top-level `upgrade` key, and pear-runtime wasn't even in node_modules) but the code + the CI patch-pear-runtime step remained. Replace with a backgrounded `pgh app update hypernote` subprocess (detached, best-effort, skipped on --no-updates) and wire the pgh publish path. Mirrors the godot-keet / hyperident migration (hyper-fleeky-dev #81). hypernote-server.js: - replace the import('pear-'+'runtime')/new PearRuntime block with startBackgroundUpdate logic (resolve ~/.local/bin/pgh or store bare+entry, spawn detached). Bare-only app, so spawn via a direct bare-subprocess import (process comes from bare-node-runtime/global). Verified under bare: spawn=function, process.argv works. package.json (0.2.0 → 0.2.1): - add a pgh block (bin hypernote, entrypoint hypernote-server.js, runtime bare, pgh.upgrade=pgh://<registry>/hypernote). - declare bare-subprocess ^5.2.2 (was only transitively present — audited that every directly-imported bare-* is now a declared dep, per the bare-process lesson from hyperident). .pear-git/ci.yaml: - drop the patch-pear-runtime step + the pear-runtime/bare-sidecar cleanup lines; add `pgh publish`. Keep the copy-deps rsync (preserves the postinstall lib0 patch + bare-* prebuilds). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
b2203ab3b35f128418d1c441d1cc2c70961d354c
by plankatron <fleeky@gmail.com> · 2026-06-20T20:35:14.000Z

Changed files (4) +50 -35

.pear-git/ci.yaml +6 -8
……@@ -1,5 +1,8 @@
11steps:
22 - name: copy-deps
3 # Copy the pre-built node_modules (carries the postinstall lib0 patch and
4 # bare-* native prebuilds) rather than npm install, so the patched tree
5 # ships intact.
36 run: |
47 if [ -d /storage/projects/code/hypercore-kb/hyper-fleeky/hypernote/node_modules ]; then
58 SRC=/storage/projects/code/hypercore-kb/hyper-fleeky/hypernote/node_modules
……@@ -10,12 +13,7 @@ steps:
1013 fi
1114 echo "Copying node_modules from $SRC"
1215 cp -r "$SRC" .
13 rm -rf node_modules/pear-runtime/node_modules
14 rm -rf node_modules/bare-sidecar
1516 timeout: 120
16 - name: patch-pear-runtime
17 run: |
18 sed -i "s/require('bare-sidecar')/require('bare-' + 'sidecar')/" node_modules/pear-runtime/index.js 2>/dev/null || true
19 sed -i "/await fsx.rm(this._next)/s/^/\/\//" node_modules/pear-runtime/index.js 2>/dev/null || true
20 sed -i "/await fsx.rm(staged)/s/^/\/\//" node_modules/pear-runtime/index.js 2>/dev/null || true
21 timeout: 30
17 - name: pgh-publish
18 run: pgh publish . --accept-conflict "automated CI publish; dep version splits inherited from per-app lockfiles across the pgh ecosystem"
19 timeout: 300
hypernote-server.js +32 -24
……@@ -24,6 +24,7 @@ import http from 'bare-http1'
2424import fs from 'bare-fs'
2525import path from 'bare-path'
2626import os from 'bare-os'
27import subprocess from 'bare-subprocess'
2728import Corestore from 'corestore'
2829import Hyperbee from 'hyperbee'
2930import Hyperdrive from 'hyperdrive'
……@@ -71,35 +72,42 @@ process.on('unhandledRejection', (reason) => {
7172 console.error('[WARN] Unhandled rejection:', reason && reason.message ? reason.message : reason)
7273})
7374
74// OTA update bootstrap — pear-runtime checks for updates when `upgrade` key is in package.json
75// Background OTA via pgh — content-addressed store, NO Hyperdrive/blobs.
76// `pgh app update <name>` resolves the latest release from package.json's
77// pgh.upgrade, fetches any new tree closure into the content store, and
78// ATOMICALLY materializes it into the install dir (scratch build + swap),
79// safe to run while the server is live; the next start picks up the new tree.
80// Replaces the former pear-runtime Hyperdrive updater, which kept an unbounded
81// append-only blob corestore under ~/.config/hypernote/pear-runtime.
7582const isPear = typeof Pear !== 'undefined'
76if (!isPear) {
83if (!isPear && !process.argv.includes('--no-updates')) {
7784 try {
7885 const appDir = path.dirname(new URL(import.meta.url).pathname)
7986 const pkg = JSON.parse(fs.readFileSync(path.join(appDir, 'package.json'), 'utf-8'))
80 if (pkg.upgrade) {
81 import('pear-' + 'runtime').then(({ default: PearRuntime }) => {
82 const runtime = new PearRuntime({
83 dir: path.join(os.homedir(), '.config', 'hypernote'),
84 app: appDir,
85 version: pkg.version || '0.2.0',
86 upgrade: pkg.upgrade
87 })
88 runtime.on('updated', async () => {
89 console.log('[OTA] Update available, applying...')
90 try {
91 await runtime.applyUpdate()
92 if (runtime.applied) {
93 console.log('[OTA] Update applied — exiting for restart')
94 if (globalThis._hnServer) try { await globalThis._hnServer.close() } catch {}
95 if (globalThis._hnHttpServer) try { globalThis._hnHttpServer.close() } catch {}
96 setTimeout(() => process.exit(0), 1000)
97 }
98 } catch (err) {
99 console.error('[OTA] Failed to apply:', err.message)
100 }
87 const upgrade = pkg.pgh && pkg.pgh.upgrade
88 if (upgrade) {
89 const appName = (pkg.pgh && pkg.pgh.bin) || pkg.name || 'hypernote'
90 // Resolve the pgh CLI: prefer ~/.local/bin/pgh, else the store-installed
91 // bare runtime + entry. Bail if pgh isn't on this machine.
92 let argv = null
93 const launcher = path.join(os.homedir(), '.local', 'bin', 'pgh')
94 if (fs.existsSync(launcher)) argv = [launcher]
95 else {
96 const storeRoot = process.env.PGH_STORE_ROOT || path.join(os.homedir(), '.local', 'share', 'store')
97 const bareBin = path.join(storeRoot, 'apps', 'pgh', 'runtime', 'bare')
98 const entry = path.join(storeRoot, 'apps', 'pgh', 'bin', 'pgh.bare.js')
99 if (fs.existsSync(bareBin) && fs.existsSync(entry)) argv = [bareBin, entry]
100 }
101 if (argv) {
102 // Detached + best-effort: never blocks the server. pgh's atomic
103 // materialize leaves the live install intact if interrupted.
104 const proc = subprocess.spawn(argv[0], [...argv.slice(1), 'app', 'update', appName], {
105 stdio: 'ignore',
106 detached: true
101107 })
102 }).catch(() => {})
108 proc.on('error', () => {})
109 if (proc.unref) proc.unref()
110 }
103111 }
104112 } catch {}
105113}
package-lock.json +3 -2
……@@ -1,12 +1,12 @@
11{
22 "name": "hypernote",
3 "version": "0.2.0",
3 "version": "0.2.1",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "hypernote",
9 "version": "0.2.0",
9 "version": "0.2.1",
1010 "hasInstallScript": true,
1111 "dependencies": {
1212 "@noble/ed25519": "^2.0.0",
……@@ -22,6 +22,7 @@
2222 "bare-path": "^3.0.0",
2323 "bare-performance": "^2.0.0",
2424 "bare-readline": "^1.2.2",
25 "bare-subprocess": "^5.2.2",
2526 "blind-pairing": "^2.3.1",
2627 "corestore": "^7.8.0",
2728 "hyperbee": "^2.27.3",
package.json +9 -1
……@@ -1,12 +1,19 @@
11{
22 "name": "hypernote",
3 "version": "0.2.0",
3 "version": "0.2.1",
44 "description": "P2P note-taking app built on Hypercore",
55 "type": "module",
66 "main": "hypernote-server.js",
77 "bin": {
88 "hn": "./bin/hn"
99 },
10 "pgh": {
11 "bin": "hypernote",
12 "entrypoint": "hypernote-server.js",
13 "type": "terminal",
14 "runtime": "bare",
15 "upgrade": "pgh://1dd36bdfa9cb7f7d84adbd07f017e7200f7f90a3a546cd0761fac2a1f8a13e86/hypernote"
16 },
1017 "scripts": {
1118 "test": "bare node_modules/.bin/brittle test/unit/*.test.js",
1219 "start": "bare hypernote-server.js serve",
……@@ -26,6 +33,7 @@
2633 "bare-path": "^3.0.0",
2734 "bare-performance": "^2.0.0",
2835 "bare-readline": "^1.2.2",
36 "bare-subprocess": "^5.2.2",
2937 "blind-pairing": "^2.3.1",
3038 "corestore": "^7.8.0",
3139 "hyperbee": "^2.27.3",