mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
e0aee7451b
* feat: add PLANNOTATOR_DATA_DIR env var to customize data directory * fix: update missed hardcoded paths to use PLANNOTATOR_DATA_DIR OpenCode plugin and VS Code extension still used hardcoded ~/.plannotator paths, causing the IPC registry and plan backing file to diverge from the server when PLANNOTATOR_DATA_DIR is set. Also exports data-dir from @plannotator/shared and documents the new env var in AGENTS.md. Co-authored-by: Chris Werner Rau <14326070+cwrau@users.noreply.github.com> Co-authored-by: João O. Santos <34689526+Joao-O-Santos@users.noreply.github.com> * fix: vendor data-dir.ts into Pi extension and rewrite imports The Pi extension copies shared/server modules into generated/ at build time. Without vendoring data-dir.ts and rewriting the parent-relative imports, typecheck fails on all generated files that import getPlannotatorDataDir. * refactor: eliminate duplicated data-dir logic and clean up call sites - VS Code extension: replace inlined getPlannotatorDataDir() copy with import from the canonical packages/shared/data-dir.ts (esbuild bundles it, so no runtime dependency needed) - storage.ts: hoist repeated getPlannotatorDataDir() calls to a module-level DATA_DIR constant, matching the pattern config.ts uses - data-dir.ts: remove inaccurate docstring claim about relative path resolution (the code does not call resolve()) - improvement-hooks.ts: hoist to DATA_DIR constant, clarify comments on the two-level hook lookup (hooks/ subdir vs root fallback) * fix: resolve relative PLANNOTATOR_DATA_DIR to absolute path A relative value like ./data would break readArchivedPlan's path traversal guard, which compares a resolve()'d absolute path against the still-relative planDir prefix. Always return an absolute path so all callers get consistent path shapes. * fix: use @plannotator/shared/data-dir imports in server package Switch from relative ../shared/data-dir imports to the package export, matching the convention every other server file follows. Update Pi vendor script sed rules to match the new import style. * fix: use package imports in server and respect data dir in compound skill Server modules: switch from relative ../shared/data-dir imports to @plannotator/shared/data-dir, matching the convention every other server file follows. Update Pi vendor script sed rules to match. Compound skill: update hardcoded ~/.plannotator paths to check PLANNOTATOR_DATA_DIR first, so the skill reads plans and writes the improvement hook to the correct location when users set a custom data directory. Co-authored-by: Chris Werner Rau <14326070+cwrau@users.noreply.github.com> Co-authored-by: João O. Santos <34689526+Joao-O-Santos@users.noreply.github.com> * fix: remove remaining hardcoded ~/.plannotator assumptions - Settings UI: replace hardcoded path in label and placeholder with generic text that doesn't assume a specific data directory - quickLabels: update agent tip to reference PLANNOTATOR_DATA_DIR so the agent checks the correct plans directory - codex-review: hoist getPlannotatorDataDir() to module-level DATA_DIR constant, eliminating redundant per-call resolution in debugLog() - Tests: make submit-plan and storage tests resilient to PLANNOTATOR_DATA_DIR being set in the environment - Install scripts (sh, ps1, cmd): check PLANNOTATOR_DATA_DIR before falling back to ~/.plannotator for config.json attestation lookup * fix: expand tilde in install script and update test assertions install.sh: PLANNOTATOR_DATA_DIR set to ~/... stays literal inside double quotes, so the config file check silently failed. Add case statement to expand ~ the same way the runtime data-dir.ts does. install.test.ts: update three assertions that checked for hardcoded ~/.plannotator paths — now verify PLANNOTATOR_DATA_DIR awareness instead. * docs: add PLANNOTATOR_DATA_DIR to env var reference with VS Code note Document the new env var on the marketing site's environment variables reference page. Include a footnote about ensuring VS Code inherits the variable when launched from the Dock. --------- Co-authored-by: Michael Ramos <mdramos8@gmail.com> Co-authored-by: Chris Werner Rau <14326070+cwrau@users.noreply.github.com> Co-authored-by: João O. Santos <34689526+Joao-O-Santos@users.noreply.github.com>
112 lines
2.4 KiB
TypeScript
112 lines
2.4 KiB
TypeScript
/**
|
|
* Session Registry
|
|
*
|
|
* Tracks active Plannotator server sessions in ~/.plannotator/sessions/
|
|
* so users can discover and reopen closed browser tabs.
|
|
*/
|
|
|
|
import { join } from "path";
|
|
import {
|
|
mkdirSync,
|
|
writeFileSync,
|
|
readFileSync,
|
|
readdirSync,
|
|
unlinkSync,
|
|
existsSync,
|
|
} from "fs";
|
|
import { getPlannotatorDataDir } from "@plannotator/shared/data-dir";
|
|
|
|
export interface SessionInfo {
|
|
pid: number;
|
|
port: number;
|
|
url: string;
|
|
mode: "plan" | "review" | "annotate" | "archive" | "goal-setup";
|
|
project: string;
|
|
startedAt: string;
|
|
label: string;
|
|
}
|
|
|
|
function getSessionsDir(): string {
|
|
const dir = join(getPlannotatorDataDir(), "sessions");
|
|
mkdirSync(dir, { recursive: true });
|
|
return dir;
|
|
}
|
|
|
|
function sessionPath(pid: number): string {
|
|
return join(getSessionsDir(), `${pid}.json`);
|
|
}
|
|
|
|
/**
|
|
* Check if a process is still alive.
|
|
*/
|
|
function isAlive(pid: number): boolean {
|
|
try {
|
|
process.kill(pid, 0);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register the current server session.
|
|
*/
|
|
export function registerSession(info: SessionInfo): void {
|
|
writeFileSync(sessionPath(info.pid), JSON.stringify(info, null, 2), "utf-8");
|
|
}
|
|
|
|
/**
|
|
* Unregister the current process's session. No-op if not found.
|
|
*/
|
|
export function unregisterSession(pid: number = process.pid): void {
|
|
const filePath = sessionPath(pid);
|
|
try {
|
|
if (existsSync(filePath)) unlinkSync(filePath);
|
|
} catch {
|
|
// Ignore delete failures
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List all active sessions. Automatically removes stale entries.
|
|
*/
|
|
export function listSessions(): SessionInfo[] {
|
|
const dir = getSessionsDir();
|
|
const active: SessionInfo[] = [];
|
|
|
|
let entries: string[];
|
|
try {
|
|
entries = readdirSync(dir);
|
|
} catch {
|
|
return [];
|
|
}
|
|
|
|
for (const entry of entries) {
|
|
if (!entry.endsWith(".json")) continue;
|
|
|
|
const filePath = join(dir, entry);
|
|
try {
|
|
const data: SessionInfo = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
|
|
if (isAlive(data.pid)) {
|
|
active.push(data);
|
|
} else {
|
|
// Stale session — clean up
|
|
try {
|
|
unlinkSync(filePath);
|
|
} catch {}
|
|
}
|
|
} catch {
|
|
// Corrupt file — remove it
|
|
try {
|
|
unlinkSync(filePath);
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
// Sort by most recent first
|
|
return active.sort(
|
|
(a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
|
|
);
|
|
}
|