@rubytech/create-maxy-lite 0.1.14 → 0.1.16
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/lib/orchestrate.mjs +26 -19
- package/lib/paths.mjs +8 -1
- package/package.json +1 -1
- package/payload/webchat/server.mjs +17 -0
package/lib/orchestrate.mjs
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
// All external effects go through injected ctx primitives so the sequence,
|
|
16
16
|
// guards and idempotency are unit-testable; index.mjs wires the real ones.
|
|
17
17
|
|
|
18
|
-
import { PATHS,
|
|
18
|
+
import { PATHS, ptyLoadCommand, GUEST_BUILD_ENV } from './paths.mjs'
|
|
19
19
|
|
|
20
|
-
export const STEP_NAMES = ['termux-deps', 'proot', 'ubuntu', 'node', 'toolchain', 'vault-bind', 'npm-app']
|
|
20
|
+
export const STEP_NAMES = ['termux-deps', 'proot', 'ubuntu', 'node', 'toolchain', 'vault-bind', 'app-code', 'npm-app']
|
|
21
21
|
|
|
22
22
|
/** Throw unless the command result is a clean exit; the message carries the command's stderr. */
|
|
23
23
|
function ensureOk(r) {
|
|
@@ -111,20 +111,32 @@ const STEPS = [
|
|
|
111
111
|
ensureOk(c.run(`proot-distro login ${PATHS.distro} --bind ${PATHS.vaultHost}:${PATHS.vaultGuest} -- test -d ${PATHS.vaultGuest}`))
|
|
112
112
|
},
|
|
113
113
|
},
|
|
114
|
+
{
|
|
115
|
+
// The app code — payload (server.mjs, validator, schema, skills), the skills
|
|
116
|
+
// symlinks, and the launcher — is cheap to (re)lay and must land on EVERY run,
|
|
117
|
+
// including a re-install of an already-converged device. It lived inside the
|
|
118
|
+
// guarded npm-app step, so once node-pty/claude/launcher were present npm-app
|
|
119
|
+
// skipped and a changed server.mjs never reached the device (a new build had
|
|
120
|
+
// "no effect"). Always-run (`done: () => false`); all three ops overwrite
|
|
121
|
+
// idempotently. Ordered before npm-app because installDeps builds in the dir
|
|
122
|
+
// this lays.
|
|
123
|
+
name: 'app-code',
|
|
124
|
+
done: () => false,
|
|
125
|
+
run: async (c) => {
|
|
126
|
+
c.layPayload()
|
|
127
|
+
c.linkSkills()
|
|
128
|
+
c.writeLauncher()
|
|
129
|
+
},
|
|
130
|
+
},
|
|
114
131
|
{
|
|
115
132
|
name: 'npm-app',
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
// the guard and the step re-installs.
|
|
124
|
-
done: (c) =>
|
|
125
|
-
c.runIn(ptyLoadCommand()).code === 0 &&
|
|
126
|
-
c.runIn('command -v claude').code === 0 &&
|
|
127
|
-
c.existsHost(launcherPath()),
|
|
133
|
+
// The heavy, network-bound installs only. Converged when node-pty's native
|
|
134
|
+
// module LOADS and claude is present (the load-check is `require("node-pty")`,
|
|
135
|
+
// the same condition installDeps asserts post-build, so a present-but-
|
|
136
|
+
// unloadable pty.node fails the guard and the step rebuilds). The launcher +
|
|
137
|
+
// payload are handled by the always-run app-code step, so they are not part of
|
|
138
|
+
// this guard.
|
|
139
|
+
done: (c) => c.runIn(ptyLoadCommand()).code === 0 && c.runIn('command -v claude').code === 0,
|
|
128
140
|
run: async (c) => {
|
|
129
141
|
ensureOk(await c.runInStream(`npm install -g @anthropic-ai/claude-code@${c.pins.claudeCode}`))
|
|
130
142
|
// Pin ttyd to the manifest version via its release binary; apt would float.
|
|
@@ -133,12 +145,7 @@ const STEPS = [
|
|
|
133
145
|
`curl -fsSL -o /usr/local/bin/ttyd https://github.com/tsl0922/ttyd/releases/download/${c.pins.ttyd}/ttyd.aarch64 && chmod +x /usr/local/bin/ttyd`,
|
|
134
146
|
),
|
|
135
147
|
)
|
|
136
|
-
c.layPayload()
|
|
137
|
-
// The payload now carries the skills tree into the app dir; link each skill
|
|
138
|
-
// into the on-device personal-skills source so `claude` discovers them.
|
|
139
|
-
c.linkSkills()
|
|
140
148
|
await c.installDeps()
|
|
141
|
-
c.writeLauncher()
|
|
142
149
|
},
|
|
143
150
|
},
|
|
144
151
|
]
|
package/lib/paths.mjs
CHANGED
|
@@ -183,7 +183,14 @@ export function launcherScript({
|
|
|
183
183
|
port = PATHS.webchatPort,
|
|
184
184
|
distro = PATHS.distro,
|
|
185
185
|
} = {}) {
|
|
186
|
-
|
|
186
|
+
// Kill any prior relay before starting so `maxy-lite` always runs the freshly
|
|
187
|
+
// installed server.mjs, never a stale process still holding the port (which made
|
|
188
|
+
// a new build appear to "have no effect"). The relay writes its pid to
|
|
189
|
+
// <vault>/.maxy-lite/relay.pid; kill via the bash builtin (no external tools),
|
|
190
|
+
// and a best-effort pkill catches a relay started before pid-file support. The
|
|
191
|
+
// relay's own EADDRINUSE handler makes any remaining conflict loud.
|
|
192
|
+
const killPrior = `P=$(cat ${vaultGuest}/.maxy-lite/relay.pid 2>/dev/null); [ -n "$P" ] && kill "$P" 2>/dev/null; pkill -f "node server.mjs" 2>/dev/null; sleep 1`
|
|
193
|
+
const inner = `${killPrior}; cd ${webchatDir} && ${webchatRunEnv({ vaultGuest, port })} exec node server.mjs`
|
|
187
194
|
return `#!${prefix}/bin/bash
|
|
188
195
|
# maxy-lite launcher — starts the on-device web chat relay.
|
|
189
196
|
# Enters the glibc Ubuntu layer with the shared-storage vault bound in, then runs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rubytech/create-maxy-lite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Install maxy-lite on an Android phone: orchestrates proot-distro Ubuntu, glibc Node, claude, the web-chat relay, the vault and its bind-mount — run via npx in bare Termux.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -34,6 +34,7 @@ const CLAUDE_BIN = process.env.LITE_CLAUDE_BIN || 'claude'
|
|
|
34
34
|
const PROJECTS_DIR = path.join(os.homedir(), '.claude', 'projects')
|
|
35
35
|
const STATE_DIR = process.env.LITE_STATE_DIR || path.join(AGENT_HOME, '.maxy-lite')
|
|
36
36
|
const SESSION_FILE = path.join(STATE_DIR, 'session-id')
|
|
37
|
+
const PID_FILE = path.join(STATE_DIR, 'relay.pid') // so the launcher can kill a prior relay before starting a fresh one
|
|
37
38
|
const TAIL_MS = 250 // JSONL poll interval
|
|
38
39
|
const QUIET_MS = 1500 // silence that ends a turn for op=done
|
|
39
40
|
const NO_REPLY_MS = 8000 // a turn with NO transcript growth this long = swallowed; surface why
|
|
@@ -261,6 +262,22 @@ wss.on('connection', (ws) => {
|
|
|
261
262
|
})
|
|
262
263
|
})
|
|
263
264
|
|
|
265
|
+
// A stale relay still holding the port is the usual cause of "new build installed
|
|
266
|
+
// but old code serving". Make it loud instead of a silent stale-serve.
|
|
267
|
+
server.on('error', (e) => {
|
|
268
|
+
log('listen-error', { code: e.code, port: PORT })
|
|
269
|
+
process.exit(1)
|
|
270
|
+
})
|
|
264
271
|
server.listen(PORT, '127.0.0.1', () => {
|
|
272
|
+
// Record our pid so the next launch can kill this relay and take over cleanly,
|
|
273
|
+
// and emit a build marker: op=relay-start proves THIS server.mjs (not a stale
|
|
274
|
+
// one) is the live process.
|
|
275
|
+
try {
|
|
276
|
+
fs.mkdirSync(STATE_DIR, { recursive: true })
|
|
277
|
+
fs.writeFileSync(PID_FILE, String(process.pid))
|
|
278
|
+
} catch {
|
|
279
|
+
/* pid file is best-effort */
|
|
280
|
+
}
|
|
281
|
+
log('relay-start', { pid: process.pid, port: PORT })
|
|
265
282
|
log('listening', { port: PORT, sessionId, home: AGENT_HOME })
|
|
266
283
|
})
|