centralu 0.1.0-beta.43 → 0.1.0-beta.44

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.
@@ -270,7 +270,8 @@ export async function runDetachedChild({ spec, ...opts }) {
270
270
  *
271
271
  * A stop that went through is remembered in the data folder (`markStopped`, #1143), so a linked hub does not start the
272
272
  * host again by itself; the next host that starts here removes the mark. Not for a hub's own stop, which starts the
273
- * host again itself (an update, a rollback): the hub runs it with `CENTRALU_STOP_BY=hub` (links/tunnel.ts `stopCommand`)
273
+ * host again itself (an update, a rollback): the hub runs it with `CENTRALU_STOP_BY=hub` (links/tunnel.ts `stopCommand`).
274
+ * With `CENTRALU_STOP_FOR=update` too (#1618), the host is asked to list the turns it ends, which the next host continues
274
275
  */
275
276
  export async function runStop({
276
277
  env,
@@ -283,7 +284,7 @@ export async function runStop({
283
284
  write = (line) => process.stdout.write(line),
284
285
  }) {
285
286
  const dataDir = serveDataDir(env, home)
286
- const stopped = await stopServedHost({ dataDir, entry, platform, timeoutMs, graceMs, kill })
287
+ const stopped = await stopServedHost({ dataDir, entry, platform, forUpdate: env.CENTRALU_STOP_FOR === 'update', timeoutMs, graceMs, kill })
287
288
  if (stopped.ok && env.CENTRALU_STOP_BY !== 'hub') markStopped(dataDir, 'stop')
288
289
  return answerLine(write, 'stop', stopped)
289
290
  }
@@ -23,9 +23,11 @@ import { isAlive, say, sleep, waitUntil } from './serve-util.mjs'
23
23
  * with this folder's token: a stale pid is never signalled.
24
24
  *
25
25
  * `{ ok, wasRunning, how?, message? }`, the `--stop` line's `stop`. With `ifIdle` (#1224, the hand-over) the host stops only
26
- * when nothing runs there, and `{ ok: false, busy: true }` says it did not.
26
+ * when nothing runs there, and `{ ok: false, busy: true }` says it did not. `forUpdate` (#1618): a hub's update stops it, and
27
+ * the host writes the turns it ends for the next one (`host.stop { forUpdate }`); a host that does not take the request is
28
+ * ended as any is, and lists nothing.
27
29
  */
28
- export async function stopServedHost({ dataDir, entry, platform = process.platform, ifIdle = false, timeoutMs = 30_000, graceMs = 15_000, kill = (pid, sig) => process.kill(pid, sig) }) {
30
+ export async function stopServedHost({ dataDir, entry, platform = process.platform, ifIdle = false, forUpdate = false, timeoutMs = 30_000, graceMs = 15_000, kill = (pid, sig) => process.kill(pid, sig) }) {
29
31
  const state = readServeState(dataDir)
30
32
  if (!state) return { ok: true, wasRunning: false, message: `no centralu serve has run for ${dataDir}` }
31
33
  const port = state.port ?? DEFAULT_SERVE_PORT
@@ -41,8 +43,8 @@ export async function stopServedHost({ dataDir, entry, platform = process.platfo
41
43
  const launcherPid = launcher && hostPid && launcher.hostPid === hostPid ? launcher.pid : null
42
44
  const down = async () => !(await probe()).running && !(hostPid && isAlive(hostPid))
43
45
 
44
- let asked = await askHostToStop({ port, token: state.token, protocolVersion: first.protocolVersion ?? installed, prove, ifIdle })
45
- if (!asked.ok && asked.protocolVersion !== undefined) asked = await askHostToStop({ port, token: state.token, protocolVersion: asked.protocolVersion, prove, ifIdle })
46
+ let asked = await askHostToStop({ port, token: state.token, protocolVersion: first.protocolVersion ?? installed, prove, ifIdle, forUpdate })
47
+ if (!asked.ok && asked.protocolVersion !== undefined) asked = await askHostToStop({ port, token: state.token, protocolVersion: asked.protocolVersion, prove, ifIdle, forUpdate })
46
48
  // `ifIdle` and it has work running (#1224): it was left as it is
47
49
  if (asked.busy) return { ok: false, wasRunning: true, busy: true, message: asked.message }
48
50
  let how = 'asked'
@@ -367,9 +367,9 @@ export function askHostRpc({ port, token, protocolVersion, prove = false, method
367
367
  * `{ ok: true }` once the host took it, `{ ok: false, message, protocolVersion? }` otherwise; the
368
368
  * last names the host's protocol when the hello was refused for it, so the caller can ask again.
369
369
  * `ifIdle` (#1224): only if nothing runs there, checked and acted on by the host in one step; `{ ok: false, busy: true }`
370
- * when something does.
370
+ * when something does. `forUpdate` (#1618): a hub's update stops it, and the host lists the turns it ends for the next one.
371
371
  */
372
- export function askHostToStop({ port, token, protocolVersion, prove = false, ifIdle = false, timeoutMs = 5000, WebSocketImpl = globalThis.WebSocket }) {
372
+ export function askHostToStop({ port, token, protocolVersion, prove = false, ifIdle = false, forUpdate = false, timeoutMs = 5000, WebSocketImpl = globalThis.WebSocket }) {
373
373
  if (typeof WebSocketImpl !== 'function') return Promise.resolve({ ok: false, message: `Node ${process.version} has no WebSocket to ask with` })
374
374
  // With `prove`, the token goes out only to a host that proved it holds it (#838)
375
375
  return helloExchange({
@@ -380,7 +380,7 @@ export function askHostToStop({ port, token, protocolVersion, prove = false, ifI
380
380
  unproven: () => done({ ok: false, message: 'what answers on the port did not prove it holds the token' }),
381
381
  gone: () => done({ ok: false, message: 'the connection closed' }),
382
382
  message: (m, ws) => {
383
- if (m.kind === 'hello_ok') ws.send(JSON.stringify({ kind: 'rpc', id: 'stop', method: 'host.stop', params: ifIdle ? { ifIdle: true } : {} }))
383
+ if (m.kind === 'hello_ok') ws.send(JSON.stringify({ kind: 'rpc', id: 'stop', method: 'host.stop', params: { ...(ifIdle ? { ifIdle: true } : {}), ...(forUpdate ? { forUpdate: true } : {}) } }))
384
384
  else if (m.kind === 'res' && m.id === 'stop' && m.ok && m.result?.ok === false) {
385
385
  // `ifIdle` and something runs there (#1224): nothing was changed
386
386
  done({ ok: false, busy: true, message: 'it has work running', activity: m.result.busy ?? null, calls: m.result.calls ?? 0 })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "centralu",
3
- "version": "0.1.0-beta.43",
3
+ "version": "0.1.0-beta.44",
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.43",
42
- "@centralu/linux-arm64": "0.1.0-beta.43",
43
- "@centralu/linux-x64": "0.1.0-beta.43",
44
- "@centralu/win32-x64": "0.1.0-beta.43"
41
+ "@centralu/darwin-arm64": "0.1.0-beta.44",
42
+ "@centralu/linux-arm64": "0.1.0-beta.44",
43
+ "@centralu/linux-x64": "0.1.0-beta.44",
44
+ "@centralu/win32-x64": "0.1.0-beta.44"
45
45
  }
46
46
  }