mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-08 19:19:42 +00:00
* fix(cursor): close ACP list-models race and false exit 143 window Register the agent-acp-active guard before spawn, hold it until stdio close (not bare exit), record the ACP child PID, and align lock/cache home with resolveHapiHomeDir so runner and session children agree. Richer exit attribution distinguishes live-PID transport disruption from confirmed child death. Fixes residual #1472 after #835. Co-authored-by: Cursor <cursoragent@cursor.com> * test(cursor): isolate ACP guard teardown from ~/.hapi Reset afterEach under the temp HAPI_HOME only, and restore the isolated home before teardown in the unset-HAPI_HOME case, so tests cannot wipe a live agent-acp-active lock. Use distinct child PIDs in registration tests. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): publish ACP lock pid before count Fail-closed reservation order: write pids/<hostPid> before count so concurrent reconcile cannot treat a mid-register lock as stale and clear it for list-models. Keep a short mtime grace only when pids/ is missing (mkdir gap). Regression covers mid-publish readers. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): keep empty pids/ ACP reservation fail-closed Between mkdir(pids) and the host pid writeFile, reconcile could see liveCount=0 and clear the lock. Keep that window when count is still absent and the lock is fresh; re-scan for pids published mid-reconcile. Regression hooks the mkdir/write gap. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): keep ACP lock across last-unregister publish race Write a short-lived registering marker before pids/count so empty pids with leftover count cannot erase a concurrent mid-addLockPid reservation. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): use per-pid ACP registering markers Crash/reboot must not pin list-models forever on a bare registering file; prune dead owners and only keep live registrar PIDs. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import type { CursorModelsResponse } from '@hapi/protocol/apiTypes';
|
|
import { resolveHapiHomeDir } from '@/configuration';
|
|
|
|
function getHapiHomeDir(): string {
|
|
return resolveHapiHomeDir();
|
|
}
|
|
|
|
function getSharedCachePath(): string {
|
|
return join(getHapiHomeDir(), 'cache', 'cursor-models.json');
|
|
}
|
|
|
|
function isUsableModelsResponse(response: CursorModelsResponse | null): response is CursorModelsResponse {
|
|
return Boolean(
|
|
response?.success
|
|
&& (response.availableModels?.length ?? 0) > 0
|
|
);
|
|
}
|
|
|
|
/** Cross-process catalog for New Session while an ACP lock blocks `agent --list-models`. */
|
|
export function readSharedCursorModelsCache(): CursorModelsResponse | null {
|
|
const path = getSharedCachePath();
|
|
if (!existsSync(path)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(readFileSync(path, 'utf8')) as CursorModelsResponse;
|
|
return isUsableModelsResponse(parsed) ? parsed : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function writeSharedCursorModelsCache(response: CursorModelsResponse): void {
|
|
if (!isUsableModelsResponse(response)) {
|
|
return;
|
|
}
|
|
|
|
const path = getSharedCachePath();
|
|
try {
|
|
mkdirSync(dirname(path), { recursive: true });
|
|
writeFileSync(path, JSON.stringify(response), 'utf8');
|
|
} catch {
|
|
// Best effort — in-process cache still works in the session child.
|
|
}
|
|
}
|
|
|
|
export function _resetSharedCursorModelsCacheForTests(): void {
|
|
const path = getSharedCachePath();
|
|
if (existsSync(path)) {
|
|
rmSync(path, { force: true });
|
|
}
|
|
}
|