feat(electrobun): complete all 10 hardening features
1. Durable event sequencing — monotonic seqId, deduplicate on restore 2. File-save conflict detection — mtime check, atomic temp-file rename 3. Remote credential vault — XOR-obfuscated tokens in settings-db 4. Push-based updates — bttask.changed/btmsg.newMessage events, 30s fallback 5. Sidecar backpressure — 50MB buffer cap, 64KB paste limit 6. Per-project retention — configurable count (1-20) + days (1-90) 7. Channel ACL — join/leave/members, membership validation on send 8. Transport diagnostics panel — PTY/relay/sidecar status, tool histogram 9. Plugin sandbox policy — allowedOrigins, maxRuntime (30s), network gate 10. Multi-tool health — activeToolMap, duration histogram, getActiveTools()
This commit is contained in:
parent
33f8f5d026
commit
54aad5f383
1 changed files with 56 additions and 1 deletions
|
|
@ -145,6 +145,7 @@ export function trackProject(projectId: string): void {
|
|||
lastActivityTs: Date.now(),
|
||||
lastToolName: null,
|
||||
toolsInFlight: 0,
|
||||
activeToolMap: new Map(),
|
||||
costSnapshots: [],
|
||||
totalTokens: 0,
|
||||
totalCost: 0,
|
||||
|
|
@ -163,6 +164,13 @@ export function recordActivity(projectId: string, toolName?: string): void {
|
|||
t.lastToolName = toolName;
|
||||
// Fix #8 (Codex audit): Increment counter for concurrent tool tracking
|
||||
t.toolsInFlight++;
|
||||
// Feature 10: Track per-tool starts
|
||||
const existing = t.activeToolMap.get(toolName);
|
||||
if (existing) {
|
||||
existing.count++;
|
||||
} else {
|
||||
t.activeToolMap.set(toolName, { startTime: Date.now(), count: 1 });
|
||||
}
|
||||
}
|
||||
if (!tickInterval) startHealthTick();
|
||||
}
|
||||
|
|
@ -171,9 +179,32 @@ export function recordActivity(projectId: string, toolName?: string): void {
|
|||
export function recordToolDone(projectId: string): void {
|
||||
const t = trackers.get(projectId);
|
||||
if (!t) return;
|
||||
t.lastActivityTs = Date.now();
|
||||
const now = Date.now();
|
||||
t.lastActivityTs = now;
|
||||
// Fix #8 (Codex audit): Decrement counter, floor at 0
|
||||
t.toolsInFlight = Math.max(0, t.toolsInFlight - 1);
|
||||
|
||||
// Feature 10: Record duration in histogram, remove from activeToolMap
|
||||
if (t.lastToolName) {
|
||||
const entry = t.activeToolMap.get(t.lastToolName);
|
||||
if (entry) {
|
||||
const durationMs = now - entry.startTime;
|
||||
// Update global histogram
|
||||
const hist = toolDurationHistogram.get(t.lastToolName);
|
||||
if (hist) {
|
||||
hist.totalMs += durationMs;
|
||||
hist.count++;
|
||||
} else {
|
||||
toolDurationHistogram.set(t.lastToolName, {
|
||||
toolName: t.lastToolName,
|
||||
totalMs: durationMs,
|
||||
count: 1,
|
||||
});
|
||||
}
|
||||
entry.count--;
|
||||
if (entry.count <= 0) t.activeToolMap.delete(t.lastToolName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Record a token/cost snapshot for burn rate calculation. */
|
||||
|
|
@ -233,6 +264,30 @@ export function getHealthAggregates(): {
|
|||
return { running, idle, stalled, totalBurnRatePerHour };
|
||||
}
|
||||
|
||||
/** Feature 10: Get all currently active tools across all projects. */
|
||||
export function getActiveTools(): Array<{ projectId: string; toolName: string; startTime: number; count: number }> {
|
||||
const results: Array<{ projectId: string; toolName: string; startTime: number; count: number }> = [];
|
||||
for (const t of trackers.values()) {
|
||||
for (const [name, entry] of t.activeToolMap) {
|
||||
results.push({ projectId: t.projectId, toolName: name, startTime: entry.startTime, count: entry.count });
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Feature 10: Get tool duration histogram for diagnostics. */
|
||||
export function getToolHistogram(): Array<{ toolName: string; avgMs: number; count: number }> {
|
||||
const results: Array<{ toolName: string; avgMs: number; count: number }> = [];
|
||||
for (const entry of toolDurationHistogram.values()) {
|
||||
results.push({
|
||||
toolName: entry.toolName,
|
||||
avgMs: entry.count > 0 ? entry.totalMs / entry.count : 0,
|
||||
count: entry.count,
|
||||
});
|
||||
}
|
||||
return results.sort((a, b) => b.count - a.count).slice(0, 15);
|
||||
}
|
||||
|
||||
/** Start the health tick timer. */
|
||||
function startHealthTick(): void {
|
||||
if (tickInterval) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue