19420cc01d
- Asana REST API client for task polling, section management, and commenting - Pi CLI executor for running tasks with specialist agent teams - Task loop: polls Asana To Do/Pi Worker sections, executes tasks, reports results - Improvement loop: rotates through 8 improvement categories (code quality, security, docs, testing, etc) - Health server on :8787 - File-based mutex locks with stale lock detection - Docker + docker-compose deployment config - deploy.sh for one-command deployment to cr-server-new via SSH/Incus
48 lines
1.1 KiB
TypeScript
48 lines
1.1 KiB
TypeScript
import { writeFileSync, unlinkSync, readFileSync, existsSync } from "fs";
|
|
import { join } from "path";
|
|
|
|
export class FileLock {
|
|
private lockPath: string;
|
|
|
|
constructor(name: string, dir: string = "/tmp") {
|
|
this.lockPath = join(dir, `pi-worker-${name}.lock`);
|
|
}
|
|
|
|
acquire(): boolean {
|
|
// Check for stale lock first
|
|
if (existsSync(this.lockPath)) {
|
|
try {
|
|
const content = readFileSync(this.lockPath, "utf-8");
|
|
const lockTime = parseInt(content);
|
|
if (Date.now() - lockTime > 15 * 60 * 1000) {
|
|
// Stale lock, remove it
|
|
try { unlinkSync(this.lockPath); } catch {}
|
|
} else {
|
|
return false;
|
|
}
|
|
} catch {
|
|
// Corrupted lock file, remove it
|
|
try { unlinkSync(this.lockPath); } catch {}
|
|
}
|
|
}
|
|
|
|
// Atomic create
|
|
try {
|
|
writeFileSync(this.lockPath, Date.now().toString(), { flag: 'wx' });
|
|
return true;
|
|
} catch {
|
|
return false; // Another process got it first
|
|
}
|
|
}
|
|
|
|
release() {
|
|
try {
|
|
unlinkSync(this.lockPath);
|
|
} catch {}
|
|
}
|
|
|
|
isLocked(): boolean {
|
|
return existsSync(this.lockPath);
|
|
}
|
|
}
|