fraim-hub 2.0.295 → 2.0.297
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.
|
@@ -35,11 +35,19 @@ const hub_runtime_file_1 = require("./hub-runtime-file");
|
|
|
35
35
|
const project_fraim_paths_1 = require("../core/utils/project-fraim-paths");
|
|
36
36
|
const version_utils_1 = require("../cli/utils/version-utils");
|
|
37
37
|
const process_liveness_1 = require("./process-liveness");
|
|
38
|
+
// Issue #1415 (follow-up): confirmed live - a kill attempt can fail for a reason other than "the
|
|
39
|
+
// process is already gone." An orphan Hub started from an elevated (Administrator) context could
|
|
40
|
+
// not be terminated by this call running as a standard user - Windows refuses cross-privilege
|
|
41
|
+
// termination, taskkill exits non-zero with "Access is denied," and that failure used to vanish
|
|
42
|
+
// into this catch block with zero trace. There is nothing this function can do about that
|
|
43
|
+
// permission gap (escalating would mean a UAC prompt, defeating the "seamless" goal this whole
|
|
44
|
+
// fix exists for) - but the failure must be visible instead of silently indistinguishable from a
|
|
45
|
+
// successful kill, or a real remaining orphan reads as "the sweep just didn't run."
|
|
38
46
|
function killPid(pid) {
|
|
39
47
|
try {
|
|
40
48
|
if (process.platform === 'win32') {
|
|
41
49
|
// /T kills the entire process tree; /F forces termination without prompting.
|
|
42
|
-
(0, child_process_1.execFileSync)('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: '
|
|
50
|
+
(0, child_process_1.execFileSync)('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'pipe' });
|
|
43
51
|
}
|
|
44
52
|
else {
|
|
45
53
|
// tree-kill sends SIGTERM to the process group so child Electron helper processes
|
|
@@ -47,8 +55,11 @@ function killPid(pid) {
|
|
|
47
55
|
(0, tree_kill_1.default)(pid, 'SIGTERM');
|
|
48
56
|
}
|
|
49
57
|
}
|
|
50
|
-
catch {
|
|
51
|
-
|
|
58
|
+
catch (error) {
|
|
59
|
+
const detail = error instanceof Error && 'stderr' in error
|
|
60
|
+
? String(error.stderr ?? error.message)
|
|
61
|
+
: String(error);
|
|
62
|
+
console.warn(`[fraim] could not kill pid ${pid} (may already be gone, or may lack permission - e.g. a process started elevated): ${detail.trim()}`);
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
65
|
function isPortFree(port, timeoutMs = 500) {
|
|
@@ -3328,9 +3328,17 @@ class AiHubServer {
|
|
|
3328
3328
|
conversationRecordFromRun(run) {
|
|
3329
3329
|
const lastUpdatedAt = run.updatedAt || new Date().toISOString();
|
|
3330
3330
|
const stages = deriveStages(run, run.projectPath);
|
|
3331
|
+
const rawScope = run.scope
|
|
3332
|
+
?? run.invokedArea;
|
|
3333
|
+
const recordScope = rawScope === 'manager' || rawScope === 'company' ? rawScope : 'project';
|
|
3334
|
+
const recordProjectPath = recordScope === 'manager'
|
|
3335
|
+
? conversation_store_1.MANAGER_SCOPE_KEY
|
|
3336
|
+
: recordScope === 'company'
|
|
3337
|
+
? conversation_store_1.COMPANY_SCOPE_KEY
|
|
3338
|
+
: path_1.default.resolve(run.projectPath);
|
|
3331
3339
|
const record = {
|
|
3332
3340
|
id: run.conversationId || run.id,
|
|
3333
|
-
projectPath:
|
|
3341
|
+
projectPath: recordProjectPath,
|
|
3334
3342
|
title: run.conversationTitle || run.jobTitle || run.jobId,
|
|
3335
3343
|
jobId: run.jobId,
|
|
3336
3344
|
jobTitle: run.jobTitle || run.jobId,
|
|
@@ -3392,9 +3400,7 @@ class AiHubServer {
|
|
|
3392
3400
|
sourceTrigger: run.sourceTrigger,
|
|
3393
3401
|
// Issue #708: carry the invocation scope so the record lands in (and is keyed to)
|
|
3394
3402
|
// the right bucket. Falls back to the legacy client `invokedArea` when present.
|
|
3395
|
-
scope:
|
|
3396
|
-
?? run.invokedArea
|
|
3397
|
-
?? 'project',
|
|
3403
|
+
scope: recordScope,
|
|
3398
3404
|
run: {
|
|
3399
3405
|
stages,
|
|
3400
3406
|
currentPhase: run.currentPhase || null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-hub",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.297",
|
|
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.297",
|
|
215
215
|
"mongodb": "^7.0.0",
|
|
216
216
|
"node-cron": "4.2.1",
|
|
217
217
|
"node-edge-tts": "^1.2.10",
|
package/public/ai-hub/script.js
CHANGED
|
@@ -8515,6 +8515,12 @@ function convNeedsPolling(conv) {
|
|
|
8515
8515
|
}
|
|
8516
8516
|
|
|
8517
8517
|
function switchToConversation(id) {
|
|
8518
|
+
const conv = findConversation(id);
|
|
8519
|
+
const scope = convScope(conv);
|
|
8520
|
+
const targetArea = scope === 'manager' || scope === 'company' ? scope : 'projects';
|
|
8521
|
+
if (conv && tf.area !== targetArea) {
|
|
8522
|
+
tfShowArea(targetArea);
|
|
8523
|
+
}
|
|
8518
8524
|
state.activeId = id;
|
|
8519
8525
|
persistActiveConversationSelection(id);
|
|
8520
8526
|
// Issue #1065: a find session belongs to one thread. srchOpenResult re-opens it with the search
|
|
@@ -8522,23 +8528,23 @@ function switchToConversation(id) {
|
|
|
8522
8528
|
if (typeof convFindClose === 'function') convFindClose();
|
|
8523
8529
|
// For Company/Manager: move the shared .page into this area's conv-host and
|
|
8524
8530
|
// switch from the info view to the full coaching panel.
|
|
8525
|
-
if (
|
|
8526
|
-
tfEnsurePageInArea(
|
|
8527
|
-
const host = document.getElementById(
|
|
8528
|
-
const info = document.getElementById(
|
|
8531
|
+
if (targetArea === 'company' || targetArea === 'manager') {
|
|
8532
|
+
tfEnsurePageInArea(targetArea);
|
|
8533
|
+
const host = document.getElementById(targetArea + '-conv-host');
|
|
8534
|
+
const info = document.getElementById(targetArea + '-info-view');
|
|
8529
8535
|
if (host) host.hidden = false;
|
|
8530
8536
|
if (info) info.hidden = true;
|
|
8531
8537
|
// #1397: an area selection represents every explicit destination, not only
|
|
8532
8538
|
// the Info view. Poll-driven renders must preserve this conversation until
|
|
8533
8539
|
// the user chooses another destination or the conversation disappears.
|
|
8534
|
-
if (tf.areaSelection) tf.areaSelection[
|
|
8540
|
+
if (tf.areaSelection) tf.areaSelection[targetArea] = id;
|
|
8535
8541
|
}
|
|
8536
8542
|
renderRail();
|
|
8537
8543
|
renderActive();
|
|
8538
|
-
hydrateConversationBody(state.projectPath, id).catch((error) =>
|
|
8544
|
+
hydrateConversationBody(conv ? convBucketKey(conv) : state.projectPath, id).catch((error) =>
|
|
8539
8545
|
console.warn('Could not hydrate switched conversation body:', error));
|
|
8540
|
-
const
|
|
8541
|
-
if (convNeedsPolling(
|
|
8546
|
+
const active = activeConversation();
|
|
8547
|
+
if (convNeedsPolling(active)) {
|
|
8542
8548
|
startPolling();
|
|
8543
8549
|
} else if (state.pollHandle) {
|
|
8544
8550
|
window.clearInterval(state.pollHandle);
|