fix(team): preserve sync file locks and splash layout
This commit is contained in:
parent
f6e95f5b2f
commit
651cfa1846
2 changed files with 56 additions and 15 deletions
|
|
@ -99,16 +99,50 @@ function releaseLock(lockPath: string): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sleepSync(ms: number): void {
|
||||||
|
const deadline = Date.now() + ms;
|
||||||
|
while (Date.now() < deadline) {
|
||||||
|
// Synchronous callers need the same cross-process lock as controller writes.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLockOptions(options: FileLockOptions): Required<FileLockOptions> {
|
||||||
|
return {
|
||||||
|
acquireTimeoutMs: options.acquireTimeoutMs ?? ACQUIRE_TIMEOUT_MS,
|
||||||
|
staleTimeoutMs: options.staleTimeoutMs ?? STALE_TIMEOUT_MS,
|
||||||
|
retryIntervalMs: options.retryIntervalMs ?? RETRY_INTERVAL_MS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withFileLockSync<T>(
|
||||||
|
filePath: string,
|
||||||
|
fn: () => T,
|
||||||
|
options: FileLockOptions = {}
|
||||||
|
): T {
|
||||||
|
const resolvedOptions = resolveLockOptions(options);
|
||||||
|
const lockPath = `${filePath}.lock`;
|
||||||
|
const deadline = Date.now() + resolvedOptions.acquireTimeoutMs;
|
||||||
|
|
||||||
|
while (!tryAcquire(lockPath, resolvedOptions)) {
|
||||||
|
if (Date.now() >= deadline) {
|
||||||
|
throw new Error(`File lock timeout: ${filePath}`);
|
||||||
|
}
|
||||||
|
sleepSync(Math.min(resolvedOptions.retryIntervalMs, Math.max(0, deadline - Date.now())));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return fn();
|
||||||
|
} finally {
|
||||||
|
releaseLock(lockPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function withFileLock<T>(
|
export async function withFileLock<T>(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
fn: () => Promise<T>,
|
fn: () => Promise<T>,
|
||||||
options: FileLockOptions = {}
|
options: FileLockOptions = {}
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const resolvedOptions = {
|
const resolvedOptions = resolveLockOptions(options);
|
||||||
acquireTimeoutMs: options.acquireTimeoutMs ?? ACQUIRE_TIMEOUT_MS,
|
|
||||||
staleTimeoutMs: options.staleTimeoutMs ?? STALE_TIMEOUT_MS,
|
|
||||||
retryIntervalMs: options.retryIntervalMs ?? RETRY_INTERVAL_MS,
|
|
||||||
};
|
|
||||||
const lockPath = `${filePath}.lock`;
|
const lockPath = `${filePath}.lock`;
|
||||||
const deadline = Date.now() + resolvedOptions.acquireTimeoutMs;
|
const deadline = Date.now() + resolvedOptions.acquireTimeoutMs;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -237,10 +237,17 @@
|
||||||
animation: splash-tagline-type 1.05s steps(28, end) 0.22s forwards;
|
animation: splash-tagline-type 1.05s steps(28, end) 0.22s forwards;
|
||||||
will-change: clip-path;
|
will-change: clip-path;
|
||||||
}
|
}
|
||||||
#splash-status-row {
|
#splash-loading {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: min(320px, 78vw);
|
width: min(320px, 78vw);
|
||||||
margin-top: 14px;
|
margin-top: 24px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
#splash-status-row {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
@ -274,9 +281,8 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#splash-hint {
|
#splash-hint {
|
||||||
width: min(320px, 78vw);
|
width: 100%;
|
||||||
min-height: 15px;
|
min-height: 15px;
|
||||||
margin-top: 6px;
|
|
||||||
font-family:
|
font-family:
|
||||||
ui-sans-serif,
|
ui-sans-serif,
|
||||||
system-ui,
|
system-ui,
|
||||||
|
|
@ -347,7 +353,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
width: min(240px, 64vw);
|
width: min(240px, 64vw);
|
||||||
height: 3px;
|
height: 3px;
|
||||||
margin-top: 10px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
|
@ -611,12 +616,14 @@
|
||||||
<div id="splash-copy">
|
<div id="splash-copy">
|
||||||
<div id="splash-text">Agent Teams AI</div>
|
<div id="splash-text">Agent Teams AI</div>
|
||||||
<div id="splash-tagline"><span>Get more done by doing less.</span></div>
|
<div id="splash-tagline"><span>Get more done by doing less.</span></div>
|
||||||
<div id="splash-status-row">
|
<div id="splash-loading">
|
||||||
<div id="splash-status" aria-live="polite">Preparing workspace...</div>
|
<div id="splash-progress" aria-hidden="true"><div id="splash-progress-bar"></div></div>
|
||||||
<div id="splash-elapsed">0s</div>
|
<div id="splash-status-row">
|
||||||
|
<div id="splash-status" aria-live="polite">Preparing workspace...</div>
|
||||||
|
<div id="splash-elapsed">0s</div>
|
||||||
|
</div>
|
||||||
|
<div id="splash-hint" aria-live="polite"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="splash-hint" aria-live="polite"></div>
|
|
||||||
<div id="splash-progress" aria-hidden="true"><div id="splash-progress-bar"></div></div>
|
|
||||||
<div id="splash-timeline" aria-label="Startup steps"></div>
|
<div id="splash-timeline" aria-label="Startup steps"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue