Files
ECC/scripts/hooks/session-end-marker.js
T
Gaurav Dubey b98bff4a45 fix(observer): keep project context and leases aligned after remote changes
Integrates #3697 on current main. Prefer the observer project ID, retain a sole legacy ID until the new observer folder exists, and release the ending session lease across same-root aliases. Replace the unsafe live merge hint with read-only project inspection, and validate alias IDs before path use. #3711 independently identified the computed-ID selection gap; its early switch without observer data and unchanged SessionEnd path were not adopted.
2026-10-25 17:54:47 -04:00

74 lines
2.2 KiB
JavaScript
Executable File

#!/usr/bin/env node
'use strict';
/**
* Session end marker hook - performs lightweight observer cleanup and
* outputs stdin to stdout unchanged. Exports run() for in-process execution.
*/
const {
resolveProjectContext,
getAliasContexts,
removeSessionLease,
listSessionLeases,
stopObserverForContext,
resolveSessionId
} = require('../lib/observer-sessions');
const { resolveHookSessionId } = require('../lib/hook-session');
function log(message) {
process.stderr.write(`[SessionEnd] ${message}\n`);
}
function run(rawInput) {
const output = rawInput || '';
const sessionId = resolveSessionId(resolveHookSessionId(output));
if (!sessionId) {
log('No session ID available; skipping observer cleanup');
return output;
}
try {
const observerContext = resolveProjectContext();
// Leases from sessions that started before a remote was added sit under the root's older ids.
const contexts = [observerContext, ...getAliasContexts(observerContext)];
for (const context of contexts) removeSessionLease(context, sessionId);
const remainingLeases = contexts.reduce((count, context) => count + listSessionLeases(context).length, 0);
if (remainingLeases === 0) {
for (const context of contexts) {
if (stopObserverForContext(context)) {
log(`Stopped observer for project ${context.projectId} after final session lease ended`);
} else if (context === observerContext) {
log(`No running observer to stop for project ${context.projectId}`);
}
}
} else {
log(`Retained observer for project ${observerContext.projectId}; ${remainingLeases} session lease(s) remain`);
}
} catch (err) {
log(`Observer cleanup skipped: ${err.message}`);
}
return output;
}
// Legacy CLI execution (when run directly)
if (require.main === module) {
const MAX_STDIN = 1024 * 1024;
let raw = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => {
if (raw.length < MAX_STDIN) {
const remaining = MAX_STDIN - raw.length;
raw += chunk.substring(0, remaining);
}
});
process.stdin.on('end', () => {
process.stdout.write(run(raw));
});
}
module.exports = { run };