fraim-hub 2.0.292 → 2.0.293
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.
|
@@ -65,6 +65,20 @@ function parseArgs(argv) {
|
|
|
65
65
|
}
|
|
66
66
|
return { projectPath, preferredPort, runtimeId };
|
|
67
67
|
}
|
|
68
|
+
// Issue #1415 (follow-up): the orphan sweep inside reconcileRunningHub hits real system ports
|
|
69
|
+
// directly (not scoped to FRAIM_USER_DIR), so a test proving it fires must not point it at the
|
|
70
|
+
// real 43091-43200 range - that could kill whatever Hub is genuinely running on the machine the
|
|
71
|
+
// test happens to execute on. Not used in any real launch path; mirrors the existing
|
|
72
|
+
// FRAIM_HUB_SKIP_MATERIALIZE / FRAIM_INSTALLER_LIFECYCLE_TEST kill-switch pattern.
|
|
73
|
+
function resolveOrphanScanPortRangeOverride() {
|
|
74
|
+
const raw = process.env.FRAIM_HUB_ORPHAN_SCAN_PORT_RANGE;
|
|
75
|
+
if (!raw)
|
|
76
|
+
return hub_instance_reconciliation_1.DEFAULT_HUB_ORPHAN_SCAN_PORT_RANGE;
|
|
77
|
+
const [start, end] = raw.split('-').map(Number);
|
|
78
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start > end)
|
|
79
|
+
return hub_instance_reconciliation_1.DEFAULT_HUB_ORPHAN_SCAN_PORT_RANGE;
|
|
80
|
+
return { start, end };
|
|
81
|
+
}
|
|
68
82
|
function applyUserDataOverride() {
|
|
69
83
|
const userDataDir = process.env.FRAIM_AI_HUB_USER_DATA_DIR;
|
|
70
84
|
if (!userDataDir)
|
|
@@ -570,7 +584,7 @@ async function bootstrap() {
|
|
|
570
584
|
// regardless of FRAIM_USER_DIR isolation, so a test double (FAKE_HOST=1) skips it for the same
|
|
571
585
|
// reason it skips the lock — it must never disturb a real Hub already running on this machine.
|
|
572
586
|
if (!skipSingleInstance) {
|
|
573
|
-
await (0, hub_instance_reconciliation_1.reconcileRunningHub)({ restart: true, keepRunning: false }, options.runtimeId);
|
|
587
|
+
await (0, hub_instance_reconciliation_1.reconcileRunningHub)({ restart: true, keepRunning: false }, options.runtimeId, resolveOrphanScanPortRangeOverride());
|
|
574
588
|
}
|
|
575
589
|
// macOS reads the dock icon from the app bundle once packaged, but an
|
|
576
590
|
// unpackaged `npm run hub:desktop` / `npx fraim-hub` run shows Electron's
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_HUB_ORPHAN_SCAN_PORT_RANGE = void 0;
|
|
6
7
|
exports.killPid = killPid;
|
|
7
8
|
exports.isPortFree = isPortFree;
|
|
8
9
|
exports.waitForPortFree = waitForPortFree;
|
|
@@ -109,10 +110,13 @@ function fetchHubPid(port) {
|
|
|
109
110
|
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
exports.DEFAULT_HUB_ORPHAN_SCAN_PORT_RANGE = { start: 43091, end: 43200 };
|
|
114
|
+
// `portRange` is injectable (default: the real Hub port range) so a test can point this at an
|
|
115
|
+
// isolated range instead - the scan hits real system ports directly, independent of any
|
|
116
|
+
// FRAIM_USER_DIR isolation, so exercising it against the real range in a test would risk killing
|
|
117
|
+
// whatever Hub is genuinely running on this machine at the time.
|
|
118
|
+
async function scanAndKillOrphanHubs(excludePid, portRange = exports.DEFAULT_HUB_ORPHAN_SCAN_PORT_RANGE) {
|
|
119
|
+
for (let port = portRange.start; port <= portRange.end; port++) {
|
|
116
120
|
const version = await fetchRunningHubVersion(port);
|
|
117
121
|
if (!version)
|
|
118
122
|
continue; // port not a Hub
|
|
@@ -127,8 +131,8 @@ async function scanAndKillOrphanHubs(excludePid) {
|
|
|
127
131
|
// The one entry point both launch paths call before starting their own server: reads
|
|
128
132
|
// hub-runtime.json for `runtimeId`, confirms the recorded pid is actually alive and answering
|
|
129
133
|
// (not just present in a stale file), and - when the decision is `replace` - kills it, waits for
|
|
130
|
-
// its port to free
|
|
131
|
-
async function reconcileRunningHub(flags, runtimeId = 'hub') {
|
|
134
|
+
// its port to free. The orphan sweep (#921) then runs independently of that decision - see below.
|
|
135
|
+
async function reconcileRunningHub(flags, runtimeId = 'hub', orphanScanPortRange = exports.DEFAULT_HUB_ORPHAN_SCAN_PORT_RANGE) {
|
|
132
136
|
const running = (0, hub_runtime_file_1.readHubRuntimeFile)((0, project_fraim_paths_1.getUserFraimDirPath)(), runtimeId);
|
|
133
137
|
const confirmedVersion = running ? await fetchRunningHubVersion(running.port) : null;
|
|
134
138
|
const live = !!(running && confirmedVersion && (0, process_liveness_1.isPidAlive)(running.pid));
|
|
@@ -140,17 +144,27 @@ async function reconcileRunningHub(flags, runtimeId = 'hub') {
|
|
|
140
144
|
restart: flags.restart,
|
|
141
145
|
noRestart: flags.keepRunning,
|
|
142
146
|
});
|
|
147
|
+
let replacedPid = null;
|
|
143
148
|
if (decision.action === 'replace' && effective) {
|
|
144
149
|
console.log(`Replacing running Hub (v${effective.version}, pid ${effective.pid}) - ${decision.reason}`);
|
|
145
150
|
killPid(effective.pid);
|
|
146
151
|
await waitForPortFree(effective.port);
|
|
147
|
-
|
|
148
|
-
// never registered in hub-runtime.json (e.g. older versions started via bare npx).
|
|
149
|
-
if (runtimeId === 'hub') {
|
|
150
|
-
await scanAndKillOrphanHubs(effective.pid);
|
|
151
|
-
}
|
|
152
|
+
replacedPid = effective.pid;
|
|
152
153
|
}
|
|
153
154
|
else if (decision.action === 'focus-existing' && effective) {
|
|
154
155
|
console.log(`A Hub (v${effective.version}) is already running - focusing it. Use --restart to replace it.`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Issue #1415 (follow-up): confirmed live - a Hub started via a different launch path (so it
|
|
159
|
+
// was never registered in hub-runtime.json at all, e.g. an older `npx fraim-hub` build) is only
|
|
160
|
+
// ever caught here. The bug this closes: the *registered* Hub can perfectly well have already
|
|
161
|
+
// quit on its own (a clean shutdown, or - observed live - electron-updater's own native update
|
|
162
|
+
// silently restarting it) by the time this reconciliation runs. That collapses the decision to
|
|
163
|
+
// 'launch-fresh', and the sweep used to live *inside* the 'replace' branch above, so a real,
|
|
164
|
+
// still-running orphan on the standard port range was never even looked for. The sweep must run
|
|
165
|
+
// whenever there is no live-and-focusable Hub for this runtimeId, not only when one was found
|
|
166
|
+
// and replaced - "no other launch path registered a Hub" is not evidence "no Hub is running."
|
|
167
|
+
if (runtimeId === 'hub') {
|
|
168
|
+
await scanAndKillOrphanHubs(replacedPid, orphanScanPortRange);
|
|
155
169
|
}
|
|
156
170
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-hub",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.293",
|
|
4
4
|
"description": "FRAIM Hub local companion package.",
|
|
5
5
|
"author": "Sid Mathur <sid.mathur@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/mathursrus/FRAIM#readme",
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"electron-updater": "^6.8.9",
|
|
212
212
|
"express": "^5.2.1",
|
|
213
213
|
"extract-zip": "^2.0.1",
|
|
214
|
-
"fraim": "2.0.
|
|
214
|
+
"fraim": "2.0.293",
|
|
215
215
|
"mongodb": "^7.0.0",
|
|
216
216
|
"node-cron": "4.2.1",
|
|
217
217
|
"node-edge-tts": "^1.2.10",
|