centralu 0.1.0-beta.47 → 0.1.0-beta.48
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/bin/serve-detach.mjs +3 -1
- package/bin/serve-handover.mjs +3 -2
- package/bin/serve-window.mjs +32 -1
- package/package.json +5 -5
package/bin/serve-detach.mjs
CHANGED
|
@@ -267,6 +267,8 @@ export async function runDetachedChild({ spec, ...opts }) {
|
|
|
267
267
|
* A stop that went through is remembered in the data folder (`markStopped`, #1143), so a linked hub does not start the
|
|
268
268
|
* host again by itself; the next host that starts here removes the mark. Not for a hub's own stop, which starts the
|
|
269
269
|
* host again itself (an update, a rollback): the hub runs it with `CENTRALU_STOP_BY=hub` (links/tunnel.ts `stopCommand`).
|
|
270
|
+
* The window's "Quit completely" on this host runs it with `CENTRALU_STOP_BY=quit` (#1466), so the mark says the person quit
|
|
271
|
+
* Centralu there, as the app's own host's does, not that they ran `serve --stop`.
|
|
270
272
|
* With `CENTRALU_STOP_FOR=update` too (#1618), the host is asked to list the turns it ends, which the next host continues
|
|
271
273
|
*/
|
|
272
274
|
export async function runStop({
|
|
@@ -281,6 +283,6 @@ export async function runStop({
|
|
|
281
283
|
}) {
|
|
282
284
|
const dataDir = serveDataDir(env, home)
|
|
283
285
|
const stopped = await stopServedHost({ dataDir, entry, platform, forUpdate: env.CENTRALU_STOP_FOR === 'update', timeoutMs, graceMs, kill })
|
|
284
|
-
if (stopped.ok && env.CENTRALU_STOP_BY !== 'hub') markStopped(dataDir, 'stop')
|
|
286
|
+
if (stopped.ok && env.CENTRALU_STOP_BY !== 'hub') markStopped(dataDir, env.CENTRALU_STOP_BY === 'quit' ? 'quit' : 'stop')
|
|
285
287
|
return answerLine(write, 'stop', stopped)
|
|
286
288
|
}
|
package/bin/serve-handover.mjs
CHANGED
|
@@ -26,7 +26,7 @@ import { isAlive, say, sleep, waitUntil } from './serve-util.mjs'
|
|
|
26
26
|
* And only a host `serve` started (`startedByServe`, #1722): a lock held by the app's or the keeper's host is theirs,
|
|
27
27
|
* whatever answers on the port, so `--stop` says whose it is and leaves it.
|
|
28
28
|
*
|
|
29
|
-
* `{ ok, wasRunning, how?, message? }`, the `--stop` line's `stop`. With `ifIdle` (#1224, the hand-over) the host stops only
|
|
29
|
+
* `{ ok, wasRunning, how?, heldByOther?, message? }`, the `--stop` line's `stop`. With `ifIdle` (#1224, the hand-over) the host stops only
|
|
30
30
|
* when nothing runs there, and `{ ok: false, busy: true }` says it did not. `forUpdate` (#1618): a hub's update stops it, and
|
|
31
31
|
* the host writes the turns it ends for the next one (`host.stop { forUpdate }`); a host that does not take the request is
|
|
32
32
|
* ended as any is, and lists nothing.
|
|
@@ -45,7 +45,8 @@ export async function stopServedHost({ dataDir, entry, platform = process.platfo
|
|
|
45
45
|
// The folder is held, but not by a serve (#1722): the app's or the keeper's host, whatever answered on the port
|
|
46
46
|
const other = foreignHolder(dataDir)
|
|
47
47
|
const whose = other ? `. It belongs to another Centralu host (pid ${other.pid}), ${holderWho(other.by)}; not stopping it` : ''
|
|
48
|
-
|
|
48
|
+
// `heldByOther`: the folder is held by a host that is not this serve's, which this left alone (the window's quit says so rather than claim a stop)
|
|
49
|
+
return { ok: true, wasRunning: false, ...(other ? { heldByOther: true } : {}), message: `centralu serve is not running for ${dataDir}${whose}` }
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
const hostPid = lockHolderPid(dataDir)
|
package/bin/serve-window.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { join } from 'node:path'
|
|
8
8
|
import { pairServe, readServeState } from './serve-files.mjs'
|
|
9
9
|
import { DEFAULT_SERVE_PORT } from './serve-port.mjs'
|
|
10
|
-
import { probeHost, protocolVersionOf } from './serve-host.mjs'
|
|
10
|
+
import { askHostRpc, probeHost, protocolVersionOf } from './serve-host.mjs'
|
|
11
11
|
import { serveFolder } from './serve-run.mjs'
|
|
12
12
|
import { runDetach } from './serve-detach.mjs'
|
|
13
13
|
import { startInSignIn } from './serve-autostart.mjs'
|
|
@@ -27,6 +27,9 @@ import { say, sleep } from './serve-util.mjs'
|
|
|
27
27
|
* (`moveIntoSignIn`, serve-handover.mjs).
|
|
28
28
|
* - stdin is the window (it runs this with stdin a pipe): when it ends, this exits 0 and leaves the host running,
|
|
29
29
|
* since it serves the hub's link too.
|
|
30
|
+
* - A line `{"op":"system-ending"}` on that stdin (`"cancelled":true` calls it off) is the window saying the system is ending, a
|
|
31
|
+
* shutdown, restart or sign-out (#1970): the host this runs is no child of the window and hears nothing of it, so this tells it by
|
|
32
|
+
* `host.systemEnding`, with the window's full credential. A sign-out (`"logoff":true`) is not told to a host in a network logon, which it does not end.
|
|
30
33
|
* - The host stops answering for `misses` polls in a row (the hand-over's gap, or it ended): this exits 1, and the
|
|
31
34
|
* window's supervisor starts this again, which finds the new host or starts one.
|
|
32
35
|
*
|
|
@@ -45,6 +48,7 @@ export async function runWindow({
|
|
|
45
48
|
signIn = startInSignIn,
|
|
46
49
|
pollMs = 5_000,
|
|
47
50
|
misses = 3,
|
|
51
|
+
ask = askHostRpc,
|
|
48
52
|
}) {
|
|
49
53
|
const { dataDir } = serveFolder({ env, home, cliPath, platform })
|
|
50
54
|
const protocolVersion = protocolVersionOf(entry) ?? 0
|
|
@@ -82,6 +86,16 @@ export async function runWindow({
|
|
|
82
86
|
const state = readServeState(dataDir)
|
|
83
87
|
write(`${JSON.stringify({ ready: true, port: state.port ?? DEFAULT_SERVE_PORT, token, db: join(dataDir, 'store.db') })}\n`)
|
|
84
88
|
|
|
89
|
+
const tellEnding = async (line) => {
|
|
90
|
+
const op = parseLine(line)
|
|
91
|
+
if (op?.op !== 'system-ending') return
|
|
92
|
+
// A sign-out does not end a host the ssh logon of a hub started: it runs on
|
|
93
|
+
if (op.logoff === true && op.cancelled !== true && (await probe()).logon === 'network') return
|
|
94
|
+
const { port, proofKey } = readServeState(dataDir) ?? {}
|
|
95
|
+
const sent = await ask({ port: port ?? DEFAULT_SERVE_PORT, token, proofKey, protocolVersion, method: 'host.systemEnding', params: op.cancelled === true ? { cancelled: true } : {} })
|
|
96
|
+
if (!sent) say('the host did not take the notice that the system is ending')
|
|
97
|
+
}
|
|
98
|
+
|
|
85
99
|
return await new Promise((resolve) => {
|
|
86
100
|
let done = false
|
|
87
101
|
const finish = (code) => {
|
|
@@ -89,6 +103,9 @@ export async function runWindow({
|
|
|
89
103
|
done = true
|
|
90
104
|
resolve(code)
|
|
91
105
|
}
|
|
106
|
+
// One after the other: the call-off must not overtake the notice it calls off
|
|
107
|
+
let told = Promise.resolve()
|
|
108
|
+
stdin.on('data', lineReader((line) => void (told = told.then(() => tellEnding(line)).catch(() => {}))))
|
|
92
109
|
stdin.once('end', () => finish(0))
|
|
93
110
|
stdin.once('close', () => finish(0))
|
|
94
111
|
stdin.resume()
|
|
@@ -114,3 +131,17 @@ function parseLine(text) {
|
|
|
114
131
|
return null
|
|
115
132
|
}
|
|
116
133
|
}
|
|
134
|
+
|
|
135
|
+
/** A `data` handler that gives `onLine` each complete line of the stream, however its chunks cut them */
|
|
136
|
+
function lineReader(onLine) {
|
|
137
|
+
let rest = ''
|
|
138
|
+
return (chunk) => {
|
|
139
|
+
rest += String(chunk)
|
|
140
|
+
let i
|
|
141
|
+
while ((i = rest.indexOf('\n')) >= 0) {
|
|
142
|
+
onLine(rest.slice(0, i))
|
|
143
|
+
rest = rest.slice(i + 1)
|
|
144
|
+
}
|
|
145
|
+
if (rest.length > 64 * 1024) rest = ''
|
|
146
|
+
}
|
|
147
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "centralu",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.48",
|
|
4
4
|
"description": "Run, watch and steer several Claude Code and Codex CLI sessions from one window",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ijun17",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"win32"
|
|
39
39
|
],
|
|
40
40
|
"optionalDependencies": {
|
|
41
|
-
"@centralu/darwin-arm64": "0.1.0-beta.
|
|
42
|
-
"@centralu/linux-arm64": "0.1.0-beta.
|
|
43
|
-
"@centralu/linux-x64": "0.1.0-beta.
|
|
44
|
-
"@centralu/win32-x64": "0.1.0-beta.
|
|
41
|
+
"@centralu/darwin-arm64": "0.1.0-beta.48",
|
|
42
|
+
"@centralu/linux-arm64": "0.1.0-beta.48",
|
|
43
|
+
"@centralu/linux-x64": "0.1.0-beta.48",
|
|
44
|
+
"@centralu/win32-x64": "0.1.0-beta.48"
|
|
45
45
|
}
|
|
46
46
|
}
|