@volter/supercode-teams 0.2.94 → 0.2.95
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/workspace/upgrade-watch.mjs +26 -0
- package/bin/workspace.mjs +18 -0
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// An upgrade replaces the code a long-running connector runs (its native binary, this package) under it. Its readers
|
|
2
|
+
// go on answering from the code they loaded, so every reading it serves is the old version's until something
|
|
3
|
+
// restarts it. This watches the files that code came from and says, once, when one of them changed.
|
|
4
|
+
import { statSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const CODE_WATCH_MS = 60_000;
|
|
8
|
+
|
|
9
|
+
const stamp = (file) => {
|
|
10
|
+
try { const s = statSync(file); return `${s.ino}:${s.size}:${s.mtimeMs}`; } catch { return null; }
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Call `onChange(file)` once, the first time one of `files` is replaced; returns a function that stops watching. */
|
|
14
|
+
export function watchCodeChange(files, onChange) {
|
|
15
|
+
const watched = files.filter(Boolean).map((file) => (file instanceof URL ? fileURLToPath(file) : String(file)));
|
|
16
|
+
const first = new Map(watched.map((file) => [file, stamp(file)]));
|
|
17
|
+
const timer = setInterval(() => {
|
|
18
|
+
// A file that is missing for a moment (an installer between removing and writing it) is not yet a change.
|
|
19
|
+
const changed = watched.find((file) => { const now = stamp(file); return now !== null && now !== first.get(file); });
|
|
20
|
+
if (!changed) return;
|
|
21
|
+
clearInterval(timer);
|
|
22
|
+
onChange(changed);
|
|
23
|
+
}, CODE_WATCH_MS);
|
|
24
|
+
timer.unref?.();
|
|
25
|
+
return () => clearInterval(timer);
|
|
26
|
+
}
|
package/bin/workspace.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import { loadOrCreateKeyPair, publicKeyId } from '@volter/teams/identity';
|
|
|
18
18
|
import { duration, parseArgs, positiveInteger, requireArgs, usage } from './workspace/args.mjs';
|
|
19
19
|
import { reserveArtifactDirectory, reserveProtected, writeProtected } from './workspace/files.mjs';
|
|
20
20
|
import { acquireLifecycle, activeSocket, lifecycleHeld, requestStop } from './workspace/lifecycle.mjs';
|
|
21
|
+
import { watchCodeChange } from './workspace/upgrade-watch.mjs';
|
|
21
22
|
import { volterDeviceSignIn } from './workspace/volter-device.mjs';
|
|
22
23
|
import { loginSummary, signInToTeams } from './workspace/volter-login.mjs';
|
|
23
24
|
import { readSharedSignIn, signOut } from '@volter/identity';
|
|
@@ -737,6 +738,23 @@ async function connectorCommand(verb, { flags, rest, output, client, context, te
|
|
|
737
738
|
const stopAll = async () => { await machine.stop().catch(() => {}); await connector.stop(); await core.close(); await largeCore.close(); await slowCore.close(); await liveCore.close(); await lifecycle.close(); state.update(current => current ? ({ ...current, process: undefined }) : null); };
|
|
738
739
|
try { await connector.start({ roots: () => saved.roots }); await machine.start(); } catch (error) { await stopAll(); throw error; }
|
|
739
740
|
output({ version: 1, context: scope(context), data: { machine: saved.machine, source: saved.source, roots: saved.roots.map(({ path: _path, ...item }) => item), socket: machine.socketPath, foreground: true } });
|
|
741
|
+
// Run by a service, the connector moves to an upgrade on its own: its readers would otherwise answer from the code
|
|
742
|
+
// they loaded until something restarted it (after 0.5.51 this Mac's went on reading a Codex turn waiting on an
|
|
743
|
+
// approval as busy). On Windows the service runs a per-version copy, so what an upgrade replaces is the
|
|
744
|
+
// `supercode` on PATH, and the move is the install that re-pins the service (it stops this connector and starts
|
|
745
|
+
// the new copy). Elsewhere it stops and exits non-zero, which every service manager here restarts.
|
|
746
|
+
if (saved.service) {
|
|
747
|
+
const shim = process.platform === 'win32' ? (() => { try { return execFileSync('where', ['supercode'], { encoding: 'utf8', windowsHide: true }).split(/\r?\n/).find(Boolean); } catch { return null; } })() : null;
|
|
748
|
+
watchCodeChange([process.env.SUPERCODE_BIN, new URL('../package.json', import.meta.url), shim], (changed) => {
|
|
749
|
+
process.stderr.write(`supercode teams: ${changed} changed under this connector (an upgrade); moving to the new code\n`);
|
|
750
|
+
if (process.platform === 'win32') {
|
|
751
|
+
spawn('supercode', ['teams', 'connect', '--install', '--context', contextName, '--cwd', path], { shell: true, detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
process.exitCode = 75;
|
|
755
|
+
void stopConnector?.();
|
|
756
|
+
});
|
|
757
|
+
}
|
|
740
758
|
await new Promise(resolveStop => { let stopped = false; stopConnector = async () => { if (stopped) return; stopped = true; await stopAll(); resolveStop(); }; for (const signal of ['SIGINT', 'SIGTERM', ...(process.platform === 'win32' ? ['SIGHUP', 'SIGBREAK'] : [])]) process.once(signal, stopConnector); });
|
|
741
759
|
return;
|
|
742
760
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.95",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
|
|
6
6
|
"license": "MIT",
|