update report h6
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
// Mirrors packages/casan-harness/scripts/bash/casan-paths.sh: walk UP from a start dir
|
||||
// for a marker (`.specify` state dir OR `packages/casan-harness`), so the API reads the
|
||||
// real harness telemetry whether launched from the repo, a workspace subdir, or a bundle.
|
||||
// Every path is overridable by the same CASAN_DASHBOARD_* env vars the legacy
|
||||
// dashboard-server.py honors, so console + dashboard read identical sources.
|
||||
// Canonical CASAN_TELEMETRY_* variables are shared with harness writers; legacy
|
||||
// dashboard/control-panel names remain accepted so upgrades do not split sources.
|
||||
import { existsSync, statSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -27,35 +27,74 @@ export const APP_ROOT = process.env.CASAN_APP_ROOT
|
||||
? resolve(process.env.CASAN_APP_ROOT)
|
||||
: findAppRoot(HERE);
|
||||
|
||||
const env = (k: string, fallback: string) => (process.env[k] ? resolve(process.env[k]!) : join(APP_ROOT, fallback));
|
||||
const env = (keys: string[], fallback: string) => {
|
||||
const configured = keys.map((key) => process.env[key]).find((value): value is string => Boolean(value));
|
||||
return configured ? resolve(configured) : join(APP_ROOT, fallback);
|
||||
};
|
||||
|
||||
// Runtime telemetry (regenerated) + generated reports. Names match the harness layout.
|
||||
export const PATHS = {
|
||||
metrics: env('CASAN_DASHBOARD_METRICS', '.specify/logs/cost/metrics.jsonl'),
|
||||
providerUsage: env('CASAN_CP_PROVIDER_USAGE', '.specify/logs/level5/provider-usage.jsonl'),
|
||||
audit: env('CASAN_CP_AUDIT', '.specify/logs/audit/audit.jsonl'),
|
||||
auditHead: env('CASAN_CP_AUDIT_HEAD', '.specify/logs/audit/audit-head.txt'),
|
||||
security: env('CASAN_CP_SECURITY', '.specify/logs/audit/security.jsonl'),
|
||||
toolRegistry: env('CASAN_CP_TOOL_REGISTRY', '.specify/logs/level5/tool-registry.jsonl'),
|
||||
actionGate: env('CASAN_CP_ACTION_GATE', '.specify/logs/level5/action-gate.jsonl'),
|
||||
fallback: env('CASAN_CP_FALLBACK', '.specify/logs/level5/fallback.jsonl'),
|
||||
incidents: env('CASAN_CP_INCIDENTS', '.specify/logs/level5/incidents.jsonl'),
|
||||
alerts: env('CASAN_DASHBOARD_ALERTS', '.specify/agentops/alerts.log'),
|
||||
traceDir: env('CASAN_CP_TRACE_DIR', '.specify/logs/trace'),
|
||||
traceEventDir: env('CASAN_CP_TRACE_EVENT_DIR', '.specify/logs/trace-events'),
|
||||
traceability: env('CASAN_CP_TRACEABILITY', 'docs/output/casan/traceability-matrix.json'),
|
||||
drift: env('CASAN_CP_DRIFT', 'docs/output/casan/level5-evidence/09-drift-report.json'),
|
||||
businessKpi: env('CASAN_CP_KPI', 'docs/output/casan/level5-evidence/14-business-kpi-report.json'),
|
||||
benignFp: env('CASAN_CP_BENIGN_FP', 'docs/output/casan/benign-fp-report.json'),
|
||||
scoringReport: env('CASAN_CP_SCORING_REPORT', 'docs/output/casan/phase3-real-run-scoring.md'),
|
||||
approvalInbox: env('CASAN_CP_APPROVAL_INBOX', '.specify/level5/approval-inbox.json'),
|
||||
chatAudit: env('CASAN_CP_CHAT_AUDIT', '.specify/logs/chat/chat-turns.jsonl'),
|
||||
delegationPolicy: env('CASAN_CP_DELEGATION_POLICY', 'packages/casan-harness/config/delegation-policy.yaml'),
|
||||
selfImprove: env('CASAN_CP_SELF_IMPROVE', 'packages/casan-harness/scripts/bash/self-improve.py'),
|
||||
evidencePacks: env('CASAN_CP_EVIDENCE_PACKS', 'docs/output/casan/evidence-packs'),
|
||||
metrics: env(['CASAN_TELEMETRY_METRICS_LOG', 'CASAN_DASHBOARD_METRICS'], '.specify/logs/cost/metrics.jsonl'),
|
||||
providerUsage: env(['CASAN_TELEMETRY_PROVIDER_LOG', 'CASAN_CP_PROVIDER_USAGE'], '.specify/logs/level5/provider-usage.jsonl'),
|
||||
alerts: env(['CASAN_TELEMETRY_ALERTS_LOG', 'CASAN_DASHBOARD_ALERTS'], '.specify/agentops/alerts.log'),
|
||||
audit: env(['CASAN_CP_AUDIT'], '.specify/logs/audit/audit.jsonl'),
|
||||
auditHead: env(['CASAN_CP_AUDIT_HEAD'], '.specify/logs/audit/audit-head.txt'),
|
||||
security: env(['CASAN_CP_SECURITY'], '.specify/logs/audit/security.jsonl'),
|
||||
toolRegistry: env(['CASAN_CP_TOOL_REGISTRY'], '.specify/logs/level5/tool-registry.jsonl'),
|
||||
actionGate: env(['CASAN_CP_ACTION_GATE'], '.specify/logs/level5/action-gate.jsonl'),
|
||||
fallback: env(['CASAN_CP_FALLBACK'], '.specify/logs/level5/fallback.jsonl'),
|
||||
incidents: env(['CASAN_CP_INCIDENTS'], '.specify/logs/level5/incidents.jsonl'),
|
||||
traceDir: env(['CASAN_CP_TRACE_DIR'], '.specify/logs/trace'),
|
||||
traceEventDir: env(['CASAN_CP_TRACE_EVENT_DIR'], '.specify/logs/trace-events'),
|
||||
traceability: env(['CASAN_CP_TRACEABILITY'], 'docs/output/casan/traceability-matrix.json'),
|
||||
drift: env(['CASAN_CP_DRIFT'], 'docs/output/casan/level5-evidence/09-drift-report.json'),
|
||||
businessKpi: env(['CASAN_CP_KPI'], 'docs/output/casan/level5-evidence/14-business-kpi-report.json'),
|
||||
benignFp: env(['CASAN_CP_BENIGN_FP'], 'docs/output/casan/benign-fp-report.json'),
|
||||
scoringReport: env(['CASAN_CP_SCORING_REPORT'], 'docs/output/casan/phase3-real-run-scoring.md'),
|
||||
approvalInbox: env(['CASAN_CP_APPROVAL_INBOX'], '.specify/level5/approval-inbox.json'),
|
||||
chatAudit: env(['CASAN_CP_CHAT_AUDIT'], '.specify/logs/chat/chat-turns.jsonl'),
|
||||
delegationPolicy: env(['CASAN_CP_DELEGATION_POLICY'], 'packages/casan-harness/config/delegation-policy.yaml'),
|
||||
selfImprove: env(['CASAN_CP_SELF_IMPROVE'], 'packages/casan-harness/scripts/bash/self-improve.py'),
|
||||
evidencePacks: env(['CASAN_CP_EVIDENCE_PACKS'], 'docs/output/casan/evidence-packs'),
|
||||
};
|
||||
|
||||
export const STALE_AFTER_S = Number(process.env.CASAN_DASHBOARD_STALE_S ?? 3600);
|
||||
const configuredStaleAfter = Number(process.env.CASAN_DASHBOARD_STALE_S ?? 3600);
|
||||
export const STALE_AFTER_S = Number.isFinite(configuredStaleAfter) && configuredStaleAfter >= 0
|
||||
? configuredStaleAfter
|
||||
: 3600;
|
||||
|
||||
export interface SourceFreshness {
|
||||
source: 'metrics' | 'provider' | 'alerts';
|
||||
path: string;
|
||||
present: boolean;
|
||||
updated_at: string | null;
|
||||
age_s: number | null;
|
||||
stale: boolean;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export function sourceFreshness(
|
||||
source: SourceFreshness['source'],
|
||||
path: string,
|
||||
required: boolean,
|
||||
nowMs = Date.now(),
|
||||
): SourceFreshness {
|
||||
try {
|
||||
const mtime = statSync(path).mtime;
|
||||
const age = Math.max(0, Math.floor((nowMs - mtime.getTime()) / 1000));
|
||||
return { source, path, present: true, updated_at: mtime.toISOString(), age_s: age, stale: age > STALE_AFTER_S, required };
|
||||
} catch {
|
||||
return { source, path, present: false, updated_at: null, age_s: null, stale: required, required };
|
||||
}
|
||||
}
|
||||
|
||||
export function telemetryFreshness(nowMs = Date.now()): SourceFreshness[] {
|
||||
return [
|
||||
sourceFreshness('metrics', PATHS.metrics, true, nowMs),
|
||||
sourceFreshness('provider', PATHS.providerUsage, false, nowMs),
|
||||
sourceFreshness('alerts', PATHS.alerts, false, nowMs),
|
||||
];
|
||||
}
|
||||
|
||||
// Freshness of the primary metrics feed (mtime), used by /healthz + `stale` flags.
|
||||
export function metricsAgeSeconds(): number | null {
|
||||
|
||||
Reference in New Issue
Block a user