nearly-cli 0.1.12 → 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.
@@ -70,10 +70,30 @@ const freed = async (base) => (await identify(base)) === null;
70
70
  // 'stood-down' it exited when asked (0.1.8 and later)
71
71
  // 'ended' older build, idle, so we closed it
72
72
  // 'stuck' ours by every test, but we could not end it
73
- export async function reclaim({ port, base, root, kill = process.kill.bind(process) }) {
73
+ const parts = (v) => String(v || '').split('-')[0].split('.').map(Number);
74
+ function newer(a, b) {
75
+ const x = parts(a), y = parts(b);
76
+ if (x.some(Number.isNaN) || y.some(Number.isNaN)) return false;
77
+ for (let i = 0; i < 3; i++) if ((x[i] || 0) !== (y[i] || 0)) return (x[i] || 0) > (y[i] || 0);
78
+ return false;
79
+ }
80
+
81
+ // Whether the server on the port is good enough to keep: this build, the same
82
+ // version run from somewhere else, or a newer one.
83
+ //
84
+ // This used to compare install folders. A second `npx nearly-cli` never shares a
85
+ // folder with the runtime install, so it closed a server with live sessions and
86
+ // announced that it had closed "an older" one. And an old hook pinned to a past
87
+ // release would have replaced a newer server with itself.
88
+ export function keep(serverVersion, ourVersion) {
89
+ if (!serverVersion || !ourVersion) return false;
90
+ return serverVersion === ourVersion || newer(serverVersion, ourVersion);
91
+ }
92
+
93
+ export async function reclaim({ port, base, root, version, kill = process.kill.bind(process) }) {
74
94
  const who = await identify(base);
75
95
  if (!who) return { outcome: 'free' };
76
- if (who.root && who.root === root) return { outcome: 'ours', who };
96
+ if ((who.root && who.root === root) || keep(who.version, version)) return { outcome: 'ours', who };
77
97
  if (who.waiting > 0) return { outcome: 'busy', who };
78
98
 
79
99
  // The polite path. A build that understands this will refuse if it is busy.
@@ -335,7 +335,7 @@ const render = {
335
335
  <div class="col"><span class="lbl">${esc(askedFor)}</span><div class="body">${esc(s.task)}</div></div>
336
336
  <div class="col"><span class="lbl">Agent reported</span><div class="body">${esc(s.report || '(no final message)')}</div></div>
337
337
  </div>
338
- ${s.notDone.length ? `<div class="notdone">${s.notDone.map((n) => `<div class="row">${T(n.by === 'policy' ? 'never' : 'deny', n.by === 'policy' ? 'blocked by policy' : saidNo)}<span class="s" title="${esc(n.what)}">${esc(n.tool)} · ${esc(n.what)}</span></div>`).join('')}</div>` : ''}
338
+ ${s.notDone.length ? `<div class="notdone">${s.notDone.map((n) => `<div class="row">${T(n.by === 'policy' ? 'never' : 'deny', n.by === 'policy' ? 'blocked by policy' : n.by === 'timeout' ? 'nobody answered' : saidNo)}<span class="s" title="${esc(n.what)}">${esc(n.tool)} · ${esc(n.what)}</span></div>`).join('')}</div>` : ''}
339
339
  <div class="sub">${s.head ? `Worktree now at <span class="mono">${esc(s.head)}</span>` : ''}${s.turns ? ` · ${s.turns} turns` : ''}${s.cost != null ? ` · est. $${Number(s.cost).toFixed(2)} of subscription` : ''}</div>`,
340
340
 
341
341
  credits: () => `