@codemieai/code 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/agents/core/session/BaseSessionAdapter.d.ts +5 -2
- package/dist/agents/core/session/BaseSessionAdapter.d.ts.map +1 -1
- package/dist/agents/core/types.d.ts +11 -0
- package/dist/agents/core/types.d.ts.map +1 -1
- package/dist/agents/plugins/claude/claude.plugin.js +3 -3
- package/dist/agents/plugins/claude/claude.session.d.ts +13 -0
- package/dist/agents/plugins/claude/claude.session.d.ts.map +1 -1
- package/dist/agents/plugins/claude/claude.session.js +122 -42
- package/dist/agents/plugins/claude/claude.session.js.map +1 -1
- package/dist/agents/plugins/claude/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/agents/plugins/claude/plugin/skills/codemie-analytics/scripts/analytics-cli.js +12 -8
- package/dist/agents/plugins/claude/plugin/skills/msgraph/SKILL.md +95 -8
- package/dist/agents/plugins/claude/plugin/skills/msgraph/scripts/msgraph.js +333 -2
- package/dist/agents/plugins/claude/plugin/statusline.mjs +11 -3
- package/dist/agents/plugins/claude/session/claude-file-operation.d.ts +38 -0
- package/dist/agents/plugins/claude/session/claude-file-operation.d.ts.map +1 -0
- package/dist/agents/plugins/claude/session/claude-file-operation.js +67 -0
- package/dist/agents/plugins/claude/session/claude-file-operation.js.map +1 -0
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.d.ts +0 -4
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.d.ts.map +1 -1
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.js +7 -56
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.js.map +1 -1
- package/dist/bin/proxy-daemon.js +54 -5
- package/dist/bin/proxy-daemon.js.map +1 -1
- package/dist/cli/commands/analytics/aggregator.d.ts +1 -1
- package/dist/cli/commands/analytics/aggregator.d.ts.map +1 -1
- package/dist/cli/commands/analytics/aggregator.js +84 -23
- package/dist/cli/commands/analytics/aggregator.js.map +1 -1
- package/dist/cli/commands/analytics/cost/cost-calculator.d.ts +10 -0
- package/dist/cli/commands/analytics/cost/cost-calculator.d.ts.map +1 -0
- package/dist/cli/commands/analytics/cost/cost-calculator.js +24 -0
- package/dist/cli/commands/analytics/cost/cost-calculator.js.map +1 -0
- package/dist/cli/commands/analytics/cost/cost-enricher.d.ts +24 -0
- package/dist/cli/commands/analytics/cost/cost-enricher.d.ts.map +1 -0
- package/dist/cli/commands/analytics/cost/cost-enricher.js +152 -0
- package/dist/cli/commands/analytics/cost/cost-enricher.js.map +1 -0
- package/dist/cli/commands/analytics/cost/pricing.d.ts +23 -0
- package/dist/cli/commands/analytics/cost/pricing.d.ts.map +1 -0
- package/dist/cli/commands/analytics/cost/pricing.js +82 -0
- package/dist/cli/commands/analytics/cost/pricing.js.map +1 -0
- package/dist/cli/commands/analytics/cost/pricing.json +975 -0
- package/dist/cli/commands/analytics/cost/types.d.ts +51 -0
- package/dist/cli/commands/analytics/cost/types.d.ts.map +1 -0
- package/dist/cli/commands/analytics/cost/types.js +8 -0
- package/dist/cli/commands/analytics/cost/types.js.map +1 -0
- package/dist/cli/commands/analytics/cost/usage-readers.d.ts +40 -0
- package/dist/cli/commands/analytics/cost/usage-readers.d.ts.map +1 -0
- package/dist/cli/commands/analytics/cost/usage-readers.js +165 -0
- package/dist/cli/commands/analytics/cost/usage-readers.js.map +1 -0
- package/dist/cli/commands/analytics/data-loader.d.ts +11 -0
- package/dist/cli/commands/analytics/data-loader.d.ts.map +1 -1
- package/dist/cli/commands/analytics/data-loader.js +7 -0
- package/dist/cli/commands/analytics/data-loader.js.map +1 -1
- package/dist/cli/commands/analytics/exporter.d.ts.map +1 -1
- package/dist/cli/commands/analytics/exporter.js +2 -2
- package/dist/cli/commands/analytics/exporter.js.map +1 -1
- package/dist/cli/commands/analytics/index.d.ts.map +1 -1
- package/dist/cli/commands/analytics/index.js +107 -10
- package/dist/cli/commands/analytics/index.js.map +1 -1
- package/dist/cli/commands/analytics/native-loader.d.ts +48 -0
- package/dist/cli/commands/analytics/native-loader.d.ts.map +1 -0
- package/dist/cli/commands/analytics/native-loader.js +220 -0
- package/dist/cli/commands/analytics/native-loader.js.map +1 -0
- package/dist/cli/commands/analytics/report/assets/chart.umd.js +14 -0
- package/dist/cli/commands/analytics/report/assets/codemie-bundle.css +1 -0
- package/dist/cli/commands/analytics/report/client/app.js +676 -0
- package/dist/cli/commands/analytics/report/payload-builder.d.ts +15 -0
- package/dist/cli/commands/analytics/report/payload-builder.d.ts.map +1 -0
- package/dist/cli/commands/analytics/report/payload-builder.js +107 -0
- package/dist/cli/commands/analytics/report/payload-builder.js.map +1 -0
- package/dist/cli/commands/analytics/report/report-generator.d.ts +47 -0
- package/dist/cli/commands/analytics/report/report-generator.d.ts.map +1 -0
- package/dist/cli/commands/analytics/report/report-generator.js +102 -0
- package/dist/cli/commands/analytics/report/report-generator.js.map +1 -0
- package/dist/cli/commands/analytics/report/template.html +217 -0
- package/dist/cli/commands/analytics/report/types.d.ts +55 -0
- package/dist/cli/commands/analytics/report/types.d.ts.map +1 -0
- package/dist/cli/commands/analytics/report/types.js +6 -0
- package/dist/cli/commands/analytics/report/types.js.map +1 -0
- package/dist/cli/commands/analytics/types.d.ts +13 -0
- package/dist/cli/commands/analytics/types.d.ts.map +1 -1
- package/dist/cli/commands/proxy/daemon-manager.d.ts +5 -0
- package/dist/cli/commands/proxy/daemon-manager.d.ts.map +1 -1
- package/dist/cli/commands/proxy/daemon-manager.js +25 -9
- package/dist/cli/commands/proxy/daemon-manager.js.map +1 -1
- package/dist/cli/commands/proxy/health-check.d.ts +16 -0
- package/dist/cli/commands/proxy/health-check.d.ts.map +1 -0
- package/dist/cli/commands/proxy/health-check.js +81 -0
- package/dist/cli/commands/proxy/health-check.js.map +1 -0
- package/dist/cli/commands/proxy/index.d.ts.map +1 -1
- package/dist/cli/commands/proxy/index.js +54 -4
- package/dist/cli/commands/proxy/index.js.map +1 -1
- package/dist/cli/commands/proxy/watcher.d.ts +31 -0
- package/dist/cli/commands/proxy/watcher.d.ts.map +1 -0
- package/dist/cli/commands/proxy/watcher.js +97 -0
- package/dist/cli/commands/proxy/watcher.js.map +1 -0
- package/dist/cli/commands/skills/setup/sync-plugin.d.ts +15 -0
- package/dist/cli/commands/skills/setup/sync-plugin.d.ts.map +1 -0
- package/dist/cli/commands/skills/setup/sync-plugin.js +63 -0
- package/dist/cli/commands/skills/setup/sync-plugin.js.map +1 -0
- package/dist/providers/plugins/sso/proxy/proxy-errors.js +2 -2
- package/dist/providers/plugins/sso/proxy/proxy-errors.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/proxy-types.d.ts +6 -0
- package/dist/providers/plugins/sso/proxy/proxy-types.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts +1 -0
- package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.js +39 -3
- package/dist/providers/plugins/sso/proxy/sso.proxy.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js +14 -4
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.d.ts.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.js +5 -0
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-types.d.ts +2 -0
- package/dist/providers/plugins/sso/session/processors/metrics/metrics-types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/copy-plugins.js +39 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the embedded {@link ReportPayload} from the aggregated analytics
|
|
3
|
+
* hierarchy plus the report-time cost index. Pure — the caller stamps
|
|
4
|
+
* `generatedAt` so this stays deterministic and unit-testable.
|
|
5
|
+
*/
|
|
6
|
+
import type { RootAnalytics } from '../types.js';
|
|
7
|
+
import type { SessionCostIndex, CostSummary } from '../cost/types.js';
|
|
8
|
+
import type { ReportPayload } from './types.js';
|
|
9
|
+
export interface PayloadContext {
|
|
10
|
+
rangeLabel: string;
|
|
11
|
+
projectFilter: string;
|
|
12
|
+
generatedAt: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function buildPayload(root: RootAnalytics, costIndex: SessionCostIndex, summary: CostSummary, ctx: PayloadContext): ReportPayload;
|
|
15
|
+
//# sourceMappingURL=payload-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-builder.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/analytics/report/payload-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAiB,MAAM,kBAAkB,CAAC;AAErF,OAAO,KAAK,EAAE,aAAa,EAAmC,MAAM,YAAY,CAAC;AAEjF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,cAAc,GAClB,aAAa,CAuGf"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the embedded {@link ReportPayload} from the aggregated analytics
|
|
3
|
+
* hierarchy plus the report-time cost index. Pure — the caller stamps
|
|
4
|
+
* `generatedAt` so this stays deterministic and unit-testable.
|
|
5
|
+
*/
|
|
6
|
+
import { emptyUsage } from '../cost/cost-calculator.js';
|
|
7
|
+
export function buildPayload(root, costIndex, summary, ctx) {
|
|
8
|
+
const sessions = [];
|
|
9
|
+
const agents = new Set();
|
|
10
|
+
// Per-agent coverage over the DEDUPED set so "which tools are included" stays
|
|
11
|
+
// consistent with the headline session count (not the larger raw scan).
|
|
12
|
+
const coverageMap = new Map();
|
|
13
|
+
// The aggregator places a session under EVERY branch it touched, each carrying
|
|
14
|
+
// the full (duplicated) session metrics. Dedupe by sessionId so the flat record
|
|
15
|
+
// list — the single source of truth for the client — counts each session once.
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
for (const project of root.projects) {
|
|
18
|
+
for (const branch of project.branches) {
|
|
19
|
+
for (const s of branch.sessions) {
|
|
20
|
+
if (seen.has(s.sessionId)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
seen.add(s.sessionId);
|
|
24
|
+
const cost = costIndex.get(s.sessionId);
|
|
25
|
+
agents.add(s.agentName);
|
|
26
|
+
const cov = coverageMap.get(s.agentName) ?? { agentName: s.agentName, total: 0, priced: 0, withLog: 0 };
|
|
27
|
+
cov.total += 1;
|
|
28
|
+
if (cost?.hadLog) {
|
|
29
|
+
cov.withLog += 1;
|
|
30
|
+
}
|
|
31
|
+
if (cost?.priced) {
|
|
32
|
+
cov.priced += 1;
|
|
33
|
+
}
|
|
34
|
+
coverageMap.set(s.agentName, cov);
|
|
35
|
+
sessions.push({
|
|
36
|
+
sessionId: s.sessionId,
|
|
37
|
+
agentName: s.agentName,
|
|
38
|
+
provider: s.provider,
|
|
39
|
+
project: project.projectPath,
|
|
40
|
+
// The session's dominant branch — so a session that touched several branches is
|
|
41
|
+
// attributed to where it did the most work, not whichever branch iterates first.
|
|
42
|
+
branch: s.primaryBranch ?? branch.branchName,
|
|
43
|
+
startTime: s.startTime,
|
|
44
|
+
durationMs: s.duration,
|
|
45
|
+
turns: s.totalTurns,
|
|
46
|
+
fileOps: s.totalFileOperations,
|
|
47
|
+
linesAdded: s.totalLinesAdded,
|
|
48
|
+
linesRemoved: s.totalLinesRemoved,
|
|
49
|
+
linesModified: s.totalLinesModified,
|
|
50
|
+
netLines: s.netLinesChanged,
|
|
51
|
+
toolCallsTotal: s.totalToolCalls,
|
|
52
|
+
toolCallsSuccess: s.successfulToolCalls,
|
|
53
|
+
toolCallsFailure: s.failedToolCalls,
|
|
54
|
+
models: s.models.map((m) => m.model),
|
|
55
|
+
languages: s.languages.map((l) => l.language),
|
|
56
|
+
tools: s.tools,
|
|
57
|
+
tokens: cost?.tokens ?? emptyUsage(),
|
|
58
|
+
costUSD: cost?.costUSD ?? 0,
|
|
59
|
+
perModelCost: cost?.perModel ?? [],
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Derive headline totals from the deduped records so every KPI the client can
|
|
65
|
+
// sum exactly equals the headline (no double-counting, no enricher/hierarchy drift).
|
|
66
|
+
let durationMs = 0;
|
|
67
|
+
let turns = 0;
|
|
68
|
+
let files = 0;
|
|
69
|
+
let netLines = 0;
|
|
70
|
+
let toolCallsTotal = 0;
|
|
71
|
+
let toolCallsSuccess = 0;
|
|
72
|
+
let totalCostUSD = 0;
|
|
73
|
+
let pricedSessions = 0;
|
|
74
|
+
for (const r of sessions) {
|
|
75
|
+
durationMs += r.durationMs;
|
|
76
|
+
turns += r.turns;
|
|
77
|
+
files += r.fileOps;
|
|
78
|
+
netLines += r.netLines;
|
|
79
|
+
toolCallsTotal += r.toolCallsTotal;
|
|
80
|
+
toolCallsSuccess += r.toolCallsSuccess;
|
|
81
|
+
totalCostUSD += r.costUSD;
|
|
82
|
+
if (costIndex.get(r.sessionId)?.priced) {
|
|
83
|
+
pricedSessions += 1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const meta = {
|
|
87
|
+
generatedAt: ctx.generatedAt,
|
|
88
|
+
rangeLabel: ctx.rangeLabel,
|
|
89
|
+
agents: [...agents],
|
|
90
|
+
projectFilter: ctx.projectFilter,
|
|
91
|
+
totals: {
|
|
92
|
+
sessions: sessions.length,
|
|
93
|
+
durationMs,
|
|
94
|
+
turns,
|
|
95
|
+
files,
|
|
96
|
+
netLines,
|
|
97
|
+
toolCallsTotal,
|
|
98
|
+
toolSuccessRate: toolCallsTotal ? Math.round((toolCallsSuccess / toolCallsTotal) * 1000) / 10 : 0,
|
|
99
|
+
totalCostUSD,
|
|
100
|
+
pricedSessions,
|
|
101
|
+
},
|
|
102
|
+
unpricedModels: summary.unpricedModels,
|
|
103
|
+
coverage: [...coverageMap.values()].sort((a, b) => b.total - a.total),
|
|
104
|
+
};
|
|
105
|
+
return { meta, sessions };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=payload-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-builder.js","sourceRoot":"","sources":["../../../../../src/cli/commands/analytics/report/payload-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembles the self-contained HTML report: inline the design-system CSS, embed
|
|
3
|
+
* the analytics payload as valid JS, and inline the client app. No server, no
|
|
4
|
+
* external data files — the result opens anywhere.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReportPayload } from './types.js';
|
|
7
|
+
/** Pure string assembly — unit-testable without fs. */
|
|
8
|
+
export declare function renderReportHtml(input: {
|
|
9
|
+
template: string;
|
|
10
|
+
css: string;
|
|
11
|
+
clientJs: string;
|
|
12
|
+
payload: ReportPayload;
|
|
13
|
+
chartJs?: string;
|
|
14
|
+
}): string;
|
|
15
|
+
/** Reads vendored assets next to this module and writes the self-contained report. */
|
|
16
|
+
export declare function generateReport(payload: ReportPayload, outputPath: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Writes the report payload as a standalone JSON file — the exact cost-enriched
|
|
19
|
+
* dataset embedded in the HTML report ({ meta, sessions }). Plain JSON.stringify:
|
|
20
|
+
* the `<` escaping used by renderReportHtml is defense for inline-<script> embedding
|
|
21
|
+
* only and must NOT be applied to a .json file on disk.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateReportJson(payload: ReportPayload, outputPath: string): void;
|
|
24
|
+
export declare function getDefaultReportPath(cwd: string): string;
|
|
25
|
+
export declare function getDefaultReportJsonPath(cwd: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Permission / read-only fs errors that a *different output directory* can resolve.
|
|
28
|
+
* Seen when the report defaults to a cwd that is a drive root (Windows `D:\`) or a
|
|
29
|
+
* read-only / write-protected volume (removable, network, BitLocker-locked, etc.).
|
|
30
|
+
*/
|
|
31
|
+
export declare function isUnwritableLocationError(err: unknown): boolean;
|
|
32
|
+
/** Result of {@link writeReportWithFallback}: where the file actually landed. */
|
|
33
|
+
export interface ReportWriteResult {
|
|
34
|
+
/** The path the report was finally written to. */
|
|
35
|
+
path: string;
|
|
36
|
+
/** Set only when the preferred path was unwritable and we relocated. */
|
|
37
|
+
relocatedFrom?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Writes a report via `write(path)`. If the preferred location is unwritable
|
|
41
|
+
* (drive root, read-only volume) AND `allowFallback` is true, retries the same
|
|
42
|
+
* filename under the user's home dir, then the OS temp dir, and reports where it
|
|
43
|
+
* landed. With `allowFallback` false (an explicit `--report-output`) or for any
|
|
44
|
+
* non-permission error, the original error propagates unchanged.
|
|
45
|
+
*/
|
|
46
|
+
export declare function writeReportWithFallback(write: (path: string) => void, preferredPath: string, allowFallback: boolean): ReportWriteResult;
|
|
47
|
+
//# sourceMappingURL=report-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-generator.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/analytics/report/report-generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,uDAAuD;AACvD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAkBT;AAED,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAQ/E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAGnF;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK5D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAG/D;AAED,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC7B,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,OAAO,GACrB,iBAAiB,CAmBnB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembles the self-contained HTML report: inline the design-system CSS, embed
|
|
3
|
+
* the analytics payload as valid JS, and inline the client app. No server, no
|
|
4
|
+
* external data files — the result opens anywhere.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { homedir, tmpdir } from 'node:os';
|
|
8
|
+
import { basename, dirname, join } from 'node:path';
|
|
9
|
+
import { getDirname } from '../../../../utils/paths.js';
|
|
10
|
+
const HERE = getDirname(import.meta.url); // dist/.../analytics/report at runtime
|
|
11
|
+
/** Pure string assembly — unit-testable without fs. */
|
|
12
|
+
export function renderReportHtml(input) {
|
|
13
|
+
// Escape EVERY `<` as the JS/JSON string escape `<`. `<` only appears inside
|
|
14
|
+
// JSON string values (never as JSON structure), so this round-trips through JSON.parse,
|
|
15
|
+
// while making it impossible for embedded data to emit `</script>`, `<!--`, or `<script`
|
|
16
|
+
// and break out of the inline <script> block (defense-in-depth against HTML injection).
|
|
17
|
+
const safeData = JSON.stringify(input.payload).replace(/</g, '\\u003c');
|
|
18
|
+
// IMPORTANT: use FUNCTION replacements. A string replacement would interpret `$`
|
|
19
|
+
// patterns ($&, $', $`, $$, $n) in CSS/JS/JSON content (e.g. app.js contains `'$'`),
|
|
20
|
+
// corrupting the output and breaking the embedded script. Functions are not subject to that.
|
|
21
|
+
// Inject the trusted CSS and client JS FIRST, then the (data-derived) payload LAST, so no
|
|
22
|
+
// later replace can scan or mis-target a sentinel string that happens to appear in the data.
|
|
23
|
+
return input.template
|
|
24
|
+
.replace('/* __CODEMIE_CSS__ */', () => input.css)
|
|
25
|
+
.replace('/* __CHARTJS__ */', () => input.chartJs ?? '')
|
|
26
|
+
.replace('/* __CLIENT_APP__ */', () => input.clientJs)
|
|
27
|
+
// Replace the comment AND its ` null` fallback so the assignment becomes
|
|
28
|
+
// `window.__ANALYTICS__ = {…};` (valid), and the un-injected template stays valid too.
|
|
29
|
+
.replace('/*__ANALYTICS_DATA__*/ null', () => safeData);
|
|
30
|
+
}
|
|
31
|
+
/** Reads vendored assets next to this module and writes the self-contained report. */
|
|
32
|
+
export function generateReport(payload, outputPath) {
|
|
33
|
+
const template = readFileSync(join(HERE, 'template.html'), 'utf-8');
|
|
34
|
+
const css = readFileSync(join(HERE, 'assets', 'codemie-bundle.css'), 'utf-8');
|
|
35
|
+
const chartJs = readFileSync(join(HERE, 'assets', 'chart.umd.js'), 'utf-8');
|
|
36
|
+
const clientJs = readFileSync(join(HERE, 'client', 'app.js'), 'utf-8');
|
|
37
|
+
const html = renderReportHtml({ template, css, chartJs, clientJs, payload });
|
|
38
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
39
|
+
writeFileSync(outputPath, html, 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Writes the report payload as a standalone JSON file — the exact cost-enriched
|
|
43
|
+
* dataset embedded in the HTML report ({ meta, sessions }). Plain JSON.stringify:
|
|
44
|
+
* the `<` escaping used by renderReportHtml is defense for inline-<script> embedding
|
|
45
|
+
* only and must NOT be applied to a .json file on disk.
|
|
46
|
+
*/
|
|
47
|
+
export function generateReportJson(payload, outputPath) {
|
|
48
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
49
|
+
writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf-8');
|
|
50
|
+
}
|
|
51
|
+
export function getDefaultReportPath(cwd) {
|
|
52
|
+
const date = new Date().toISOString().split('T')[0];
|
|
53
|
+
return join(cwd, `codemie-analytics-${date}.html`);
|
|
54
|
+
}
|
|
55
|
+
export function getDefaultReportJsonPath(cwd) {
|
|
56
|
+
const date = new Date().toISOString().split('T')[0];
|
|
57
|
+
// `.report.json` (not `.json`) so the default never collides with `--export json`,
|
|
58
|
+
// which writes the cost-less analytics tree to `codemie-analytics-<date>.json`.
|
|
59
|
+
return join(cwd, `codemie-analytics-${date}.report.json`);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Permission / read-only fs errors that a *different output directory* can resolve.
|
|
63
|
+
* Seen when the report defaults to a cwd that is a drive root (Windows `D:\`) or a
|
|
64
|
+
* read-only / write-protected volume (removable, network, BitLocker-locked, etc.).
|
|
65
|
+
*/
|
|
66
|
+
export function isUnwritableLocationError(err) {
|
|
67
|
+
const code = err?.code;
|
|
68
|
+
return code === 'EPERM' || code === 'EACCES' || code === 'EROFS';
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Writes a report via `write(path)`. If the preferred location is unwritable
|
|
72
|
+
* (drive root, read-only volume) AND `allowFallback` is true, retries the same
|
|
73
|
+
* filename under the user's home dir, then the OS temp dir, and reports where it
|
|
74
|
+
* landed. With `allowFallback` false (an explicit `--report-output`) or for any
|
|
75
|
+
* non-permission error, the original error propagates unchanged.
|
|
76
|
+
*/
|
|
77
|
+
export function writeReportWithFallback(write, preferredPath, allowFallback) {
|
|
78
|
+
try {
|
|
79
|
+
write(preferredPath);
|
|
80
|
+
return { path: preferredPath };
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (!allowFallback || !isUnwritableLocationError(err))
|
|
84
|
+
throw err;
|
|
85
|
+
const name = basename(preferredPath);
|
|
86
|
+
for (const dir of [homedir(), tmpdir()]) {
|
|
87
|
+
const candidate = join(dir, name);
|
|
88
|
+
if (candidate === preferredPath)
|
|
89
|
+
continue;
|
|
90
|
+
try {
|
|
91
|
+
write(candidate);
|
|
92
|
+
return { path: candidate, relocatedFrom: preferredPath };
|
|
93
|
+
}
|
|
94
|
+
catch (inner) {
|
|
95
|
+
if (!isUnwritableLocationError(inner))
|
|
96
|
+
throw inner;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=report-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-generator.js","sourceRoot":"","sources":["../../../../../src/cli/commands/analytics/report/report-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>CodeMie Analytics</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* __CODEMIE_CSS__ */
|
|
9
|
+
|
|
10
|
+
/* ===== report-specific layout ===== */
|
|
11
|
+
html, body { height: 100%; }
|
|
12
|
+
|
|
13
|
+
/* CodeMie-style ambient brand gradient layered over the solid page background.
|
|
14
|
+
Per-theme: a deeper purple→blue wash in dark, a soft tint in light. */
|
|
15
|
+
:root, :root.dark {
|
|
16
|
+
--report-page-bg:
|
|
17
|
+
radial-gradient(118% 88% at 100% 0%, rgba(132, 83, 210, 0.16), transparent 58%),
|
|
18
|
+
radial-gradient(100% 72% at 0% 100%, rgba(0, 120, 194, 0.12), transparent 55%),
|
|
19
|
+
var(--color-bg-page);
|
|
20
|
+
}
|
|
21
|
+
:root.light, .light {
|
|
22
|
+
--report-page-bg:
|
|
23
|
+
radial-gradient(118% 88% at 100% 0%, rgba(132, 83, 210, 0.14), transparent 60%),
|
|
24
|
+
radial-gradient(100% 72% at 0% 100%, rgba(0, 122, 255, 0.13), transparent 58%),
|
|
25
|
+
var(--color-bg-page);
|
|
26
|
+
}
|
|
27
|
+
body { margin: 0; padding: 0; overflow: hidden; background: var(--report-page-bg); background-attachment: fixed; }
|
|
28
|
+
.shell { display: flex; height: 100vh; background: transparent; }
|
|
29
|
+
|
|
30
|
+
/* sidebar — frosted over the gradient so the rail stays distinct without a hard fill.
|
|
31
|
+
A CodeMie-brand radial glow (purple→blue) is anchored at the bottom-left corner, as
|
|
32
|
+
in the product chrome. Layered as a fixed background (background-attachment: scroll is
|
|
33
|
+
the default for a scroll container, so the glow stays pinned to the corner, not the
|
|
34
|
+
scrolled content). The first `background:` is the no-color-mix fallback. */
|
|
35
|
+
.side {
|
|
36
|
+
position: relative;
|
|
37
|
+
width: 232px; flex-shrink: 0;
|
|
38
|
+
background: var(--color-bg-card);
|
|
39
|
+
background:
|
|
40
|
+
radial-gradient(135% 80% at -10% 116%, rgba(132, 83, 210, 0.34) 0%, rgba(34, 151, 246, 0.12) 44%, transparent 72%),
|
|
41
|
+
color-mix(in srgb, var(--color-bg-card) 82%, transparent);
|
|
42
|
+
backdrop-filter: blur(8px);
|
|
43
|
+
border-right: 1px solid var(--color-border-structural);
|
|
44
|
+
display: flex; flex-direction: column;
|
|
45
|
+
padding: 16px 12px; overflow-y: auto;
|
|
46
|
+
}
|
|
47
|
+
:root.light .side, .light .side {
|
|
48
|
+
background: var(--color-bg-card);
|
|
49
|
+
background:
|
|
50
|
+
radial-gradient(135% 80% at -10% 116%, rgba(132, 83, 210, 0.18) 0%, rgba(34, 151, 246, 0.09) 46%, transparent 74%),
|
|
51
|
+
color-mix(in srgb, var(--color-bg-card) 90%, transparent);
|
|
52
|
+
}
|
|
53
|
+
.side .brand { display: flex; align-items: center; gap: 10px; padding: 4px 8px 14px; }
|
|
54
|
+
.side .logo { width: 30px; height: 30px; border-radius: 9px; background: linear-gradient(135deg, #2297F6, #7C5CFC); display: flex; align-items: center; justify-content: center; font-weight: 700; color: #fff; font-size: 15px; }
|
|
55
|
+
.nav-group { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-muted); font-weight: 600; margin: 14px 8px 6px; }
|
|
56
|
+
.nav-i { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: 8px; color: var(--color-text-muted); font-size: 13.5px; cursor: pointer; user-select: none; }
|
|
57
|
+
.nav-i:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
|
|
58
|
+
.nav-i.active { background: var(--color-bg-tertiary); color: var(--color-text-primary); font-weight: 600; }
|
|
59
|
+
.nav-i .ic { width: 16px; text-align: center; opacity: .85; }
|
|
60
|
+
.nav-i .lock { margin-left: auto; }
|
|
61
|
+
.side-foot { margin-top: auto; font-size: 11px; color: var(--color-text-muted); padding: 10px 8px 4px; line-height: 1.5; }
|
|
62
|
+
|
|
63
|
+
/* main + topbar */
|
|
64
|
+
.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; background: transparent; }
|
|
65
|
+
.topbar { min-height: 52px; border-bottom: 1px solid var(--color-border-structural); display: flex; align-items: center; gap: 10px; padding: 10px 22px; flex-wrap: wrap; background: var(--color-bg-page); background: color-mix(in srgb, var(--color-bg-page) 70%, transparent); backdrop-filter: blur(8px); }
|
|
66
|
+
.scroll { flex: 1; overflow-y: auto; padding: 22px; background: transparent; }
|
|
67
|
+
.fl-label { font-size: 11px; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: .04em; }
|
|
68
|
+
.seg { display: inline-flex; border: 1px solid var(--color-border-structural); border-radius: 8px; overflow: hidden; }
|
|
69
|
+
.seg button { background: transparent; border: none; color: var(--color-text-muted); padding: 5px 11px; font-size: 12.5px; cursor: pointer; font-family: inherit; }
|
|
70
|
+
.seg button.on { background: var(--color-bg-tertiary); color: var(--color-text-primary); font-weight: 600; }
|
|
71
|
+
.chip-tog { border: 1px solid var(--color-border-structural); border-radius: 999px; padding: 4px 11px; font-size: 12.5px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; color: var(--color-text-primary); user-select: none; }
|
|
72
|
+
.chip-tog .dot { width: 9px; height: 9px; border-radius: 50%; }
|
|
73
|
+
.chip-tog.off { opacity: .4; }
|
|
74
|
+
.live { margin-left: auto; font-size: 11px; color: var(--color-success); display: flex; align-items: center; gap: 6px; }
|
|
75
|
+
.live::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--color-success); }
|
|
76
|
+
|
|
77
|
+
/* KPIs */
|
|
78
|
+
.kpi-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; margin: 4px 0 16px; }
|
|
79
|
+
@media (max-width: 1100px) { .kpi-grid { grid-template-columns: repeat(3, 1fr); } }
|
|
80
|
+
.kpi { background: var(--color-bg-card); border: 1px solid var(--color-border-structural); border-radius: 12px; padding: 15px; }
|
|
81
|
+
.kpi-label { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--color-text-muted); font-weight: 600; }
|
|
82
|
+
.kpi-value { font-size: 24px; font-weight: 700; color: var(--color-text-primary); margin-top: 6px; line-height: 1.1; }
|
|
83
|
+
.kpi-sub { font-size: 12px; color: var(--color-text-muted); margin-top: 4px; }
|
|
84
|
+
.kpi.soon { opacity: .6; }
|
|
85
|
+
/* token-usage KPI row on Overview — input / output / cache write / cache read / total */
|
|
86
|
+
.kpi-section-label { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-muted); font-weight: 600; margin: 4px 2px 8px; }
|
|
87
|
+
.kpi-grid-tokens { grid-template-columns: repeat(5, 1fr); margin-top: 0; margin-bottom: 18px; }
|
|
88
|
+
@media (max-width: 1100px) { .kpi-grid-tokens { grid-template-columns: repeat(2, 1fr); } }
|
|
89
|
+
/* a hairline brand accent so the token tiles read as a related group */
|
|
90
|
+
.kpi-grid-tokens .kpi { border-top: 2px solid color-mix(in srgb, #7C5CFC 55%, var(--color-border-structural)); }
|
|
91
|
+
.kpi-value.muted { color: var(--color-text-muted); }
|
|
92
|
+
|
|
93
|
+
/* grids + charts */
|
|
94
|
+
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
95
|
+
.grid-32 { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; }
|
|
96
|
+
@media (max-width: 1000px) { .grid-2, .grid-32 { grid-template-columns: 1fr; } }
|
|
97
|
+
.chart-box { position: relative; height: 250px; }
|
|
98
|
+
.mb16 { margin-bottom: 16px; }
|
|
99
|
+
.bar-row { display: flex; align-items: center; gap: 10px; margin: 7px 0; font-size: 13px; }
|
|
100
|
+
.bar-row .nm { width: 110px; color: var(--color-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
101
|
+
.bar-track { flex: 1; background: var(--color-bg-tertiary); border-radius: 6px; height: 18px; overflow: hidden; }
|
|
102
|
+
.bar-fill { height: 100%; border-radius: 6px; }
|
|
103
|
+
.bar-row .vl { width: 80px; text-align: right; color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
|
|
104
|
+
|
|
105
|
+
/* agent compare cards */
|
|
106
|
+
.agent-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; margin-bottom: 18px; }
|
|
107
|
+
.acard { background: var(--color-bg-card); border: 1px solid var(--color-border-structural); border-radius: 12px; padding: 16px; border-top: 3px solid var(--ac, #2297F6); }
|
|
108
|
+
.acard h3 { margin: 0 0 2px; font-size: 15px; display: flex; align-items: center; gap: 8px; text-transform: capitalize; }
|
|
109
|
+
.acard .pill { width: 10px; height: 10px; border-radius: 50%; background: var(--ac, #2297F6); }
|
|
110
|
+
.acard .mini { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; }
|
|
111
|
+
.acard .mini .l { font-size: 10.5px; color: var(--color-text-muted); text-transform: uppercase; }
|
|
112
|
+
.acard .mini .v { font-size: 18px; font-weight: 700; color: var(--color-text-primary); }
|
|
113
|
+
|
|
114
|
+
/* heatmap */
|
|
115
|
+
.heatmap { display: grid; grid-template-columns: auto repeat(24, 1fr); gap: 3px; font-size: 9px; max-width: 760px; }
|
|
116
|
+
.heat-cell { aspect-ratio: 1; border-radius: 3px; background: var(--color-bg-tertiary); }
|
|
117
|
+
.heat-lbl { color: var(--color-text-muted); font-size: 10px; display: flex; align-items: center; }
|
|
118
|
+
.heat-hr { color: var(--color-text-muted); text-align: center; }
|
|
119
|
+
|
|
120
|
+
.view-title { margin: 0 0 4px; }
|
|
121
|
+
.view-sub { margin: 0 0 18px; font-size: 13px; color: var(--color-text-muted); }
|
|
122
|
+
.empty { color: var(--color-text-muted); padding: 40px; text-align: center; }
|
|
123
|
+
.search-input { width: 280px; }
|
|
124
|
+
.clickable { cursor: pointer; }
|
|
125
|
+
.drill { background: var(--color-bg-secondary); }
|
|
126
|
+
|
|
127
|
+
/* table alignment — the design-system `.table thead tr th` (text-align:left, higher
|
|
128
|
+
specificity) was overriding `.td-number` on numeric <th>, so numeric headers stayed
|
|
129
|
+
left-aligned while their cells right-aligned. These higher-specificity rules re-align
|
|
130
|
+
each header with its column values so headers line up over the content. */
|
|
131
|
+
.table thead tr th.td-number { text-align: right; }
|
|
132
|
+
.table thead tr th:not(.td-number) { text-align: left; }
|
|
133
|
+
.table tbody tr td:not(.td-number) { text-align: left; }
|
|
134
|
+
|
|
135
|
+
/* date range picker */
|
|
136
|
+
.date-range { display: inline-flex; align-items: center; gap: 6px; }
|
|
137
|
+
.date-input { background: var(--color-bg-input, var(--color-bg-card)); border: 1px solid var(--color-border-structural); color: var(--color-text-primary); border-radius: 7px; padding: 4px 8px; font-size: 12.5px; font-family: inherit; height: 30px; color-scheme: dark; }
|
|
138
|
+
:root.light .date-input, .light .date-input { color-scheme: light; }
|
|
139
|
+
.date-sep { color: var(--color-text-muted); font-size: 12px; }
|
|
140
|
+
.date-clear { background: transparent; border: 1px solid var(--color-border-structural); color: var(--color-text-muted); border-radius: 7px; height: 30px; width: 28px; cursor: pointer; font-size: 11px; line-height: 1; }
|
|
141
|
+
.date-clear:hover { color: var(--color-text-primary); background: var(--color-bg-tertiary); }
|
|
142
|
+
|
|
143
|
+
/* coverage table (Cost view) */
|
|
144
|
+
.cov-ok { color: var(--color-success); }
|
|
145
|
+
.cov-warn { color: var(--color-warning, #F5A534); }
|
|
146
|
+
|
|
147
|
+
/* theme switch — bottom-left, gradient track per design-system tokens */
|
|
148
|
+
.theme-switch { display: flex; align-items: center; gap: 8px; margin: 8px 4px 2px; padding: 8px 10px; border-radius: 10px; cursor: pointer; user-select: none; color: var(--color-text-muted); font-size: 12.5px; outline: none; }
|
|
149
|
+
.theme-switch:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
|
|
150
|
+
.theme-switch:focus-visible { box-shadow: 0 0 0 2px var(--color-border-focus, #2297F6); }
|
|
151
|
+
.theme-switch .ts-track { position: relative; width: 40px; height: 22px; border-radius: 999px; flex-shrink: 0; background: var(--gradient-switch-off, linear-gradient(to right, #BBB, #666)); transition: background var(--transition-base, 200ms ease); }
|
|
152
|
+
.theme-switch .ts-thumb { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.45); transition: transform var(--transition-base, 200ms ease); }
|
|
153
|
+
.theme-switch .ts-icon { font-size: 13px; opacity: .55; }
|
|
154
|
+
.theme-switch .ts-text { min-width: 34px; }
|
|
155
|
+
/* dark = default: moon active, thumb left, "off" gradient */
|
|
156
|
+
.theme-switch .ts-moon { opacity: 1; }
|
|
157
|
+
/* light theme: thumb slides right, sun active, "on" gradient */
|
|
158
|
+
.light .theme-switch .ts-track { background: var(--gradient-switch-on, linear-gradient(to right, #007AFF, #007AFF)); }
|
|
159
|
+
.light .theme-switch .ts-thumb { transform: translateX(18px); }
|
|
160
|
+
.light .theme-switch .ts-moon { opacity: .55; }
|
|
161
|
+
.light .theme-switch .ts-sun { opacity: 1; }
|
|
162
|
+
</style>
|
|
163
|
+
</head>
|
|
164
|
+
<body>
|
|
165
|
+
<div class="shell">
|
|
166
|
+
<aside class="side">
|
|
167
|
+
<div class="brand"><div class="logo">C</div><div style="font-weight:700;font-size:15px;">Analytics</div></div>
|
|
168
|
+
<div class="nav-group">Insights</div>
|
|
169
|
+
<div class="nav-i" data-view="overview"><span class="ic">▢</span>Overview</div>
|
|
170
|
+
<div class="nav-i" data-view="agents"><span class="ic">⊞</span>Agents · Compare</div>
|
|
171
|
+
<div class="nav-i" data-view="projects"><span class="ic">▤</span>Projects</div>
|
|
172
|
+
<div class="nav-i" data-view="toolsmodels"><span class="ic">⚙</span>Tools & Models</div>
|
|
173
|
+
<div class="nav-i" data-view="activity"><span class="ic">▦</span>Activity</div>
|
|
174
|
+
<div class="nav-group">Spend</div>
|
|
175
|
+
<div class="nav-i" data-view="cost"><span class="ic">$</span>Cost</div>
|
|
176
|
+
<div class="nav-group">Raw</div>
|
|
177
|
+
<div class="nav-i" data-view="sessions"><span class="ic">≣</span>Sessions</div>
|
|
178
|
+
<div class="side-foot" id="side-foot"></div>
|
|
179
|
+
<div class="theme-switch" id="theme-switch" role="button" tabindex="0" aria-label="Toggle light and dark theme" title="Toggle light / dark theme">
|
|
180
|
+
<span class="ts-track"><span class="ts-thumb"></span></span>
|
|
181
|
+
<span class="ts-icon ts-moon">☾</span>
|
|
182
|
+
<span class="ts-text">Dark</span>
|
|
183
|
+
<span class="ts-icon ts-sun">☀</span>
|
|
184
|
+
</div>
|
|
185
|
+
</aside>
|
|
186
|
+
|
|
187
|
+
<div class="main">
|
|
188
|
+
<div class="topbar">
|
|
189
|
+
<span class="fl-label">Range</span>
|
|
190
|
+
<div class="seg" id="range-seg">
|
|
191
|
+
<button data-range="today">Today</button>
|
|
192
|
+
<button data-range="7d">7d</button>
|
|
193
|
+
<button data-range="30d">30d</button>
|
|
194
|
+
<button data-range="90d">90d</button>
|
|
195
|
+
<button data-range="all" class="on">All</button>
|
|
196
|
+
</div>
|
|
197
|
+
<span class="fl-label" style="margin-left:8px;">Dates</span>
|
|
198
|
+
<div class="date-range">
|
|
199
|
+
<input type="date" id="date-from" class="date-input" aria-label="From date" />
|
|
200
|
+
<span class="date-sep">→</span>
|
|
201
|
+
<input type="date" id="date-to" class="date-input" aria-label="To date" />
|
|
202
|
+
<button id="date-clear" class="date-clear" title="Clear date range" type="button">✕</button>
|
|
203
|
+
</div>
|
|
204
|
+
<span class="fl-label" style="margin-left:8px;">Agents</span>
|
|
205
|
+
<span id="agent-chips" style="display:inline-flex;gap:6px;flex-wrap:wrap;"></span>
|
|
206
|
+
<select class="select" id="project-select" style="height:30px;font-size:12.5px;margin-left:8px;max-width:230px;"></select>
|
|
207
|
+
<span class="live">filtering client-side · instant</span>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="scroll" id="view-root"></div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<script>window.__ANALYTICS__ = /*__ANALYTICS_DATA__*/ null;</script>
|
|
214
|
+
<script>/* __CHARTJS__ */</script>
|
|
215
|
+
<script>/* __CLIENT_APP__ */</script>
|
|
216
|
+
</body>
|
|
217
|
+
</html>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The embedded report payload — the single data object baked into the HTML
|
|
3
|
+
* report. The client app reads only this and computes every view from it.
|
|
4
|
+
*/
|
|
5
|
+
import type { TokenUsage, ModelCost, AgentCoverage } from '../cost/types.js';
|
|
6
|
+
import type { ToolStats } from '../types.js';
|
|
7
|
+
/** One flat record per session — the client aggregates everything from these. */
|
|
8
|
+
export interface ReportSessionRecord {
|
|
9
|
+
sessionId: string;
|
|
10
|
+
agentName: string;
|
|
11
|
+
provider: string;
|
|
12
|
+
project: string;
|
|
13
|
+
branch: string;
|
|
14
|
+
startTime: number;
|
|
15
|
+
durationMs: number;
|
|
16
|
+
turns: number;
|
|
17
|
+
fileOps: number;
|
|
18
|
+
linesAdded: number;
|
|
19
|
+
linesRemoved: number;
|
|
20
|
+
linesModified: number;
|
|
21
|
+
netLines: number;
|
|
22
|
+
toolCallsTotal: number;
|
|
23
|
+
toolCallsSuccess: number;
|
|
24
|
+
toolCallsFailure: number;
|
|
25
|
+
models: string[];
|
|
26
|
+
languages: string[];
|
|
27
|
+
tools: ToolStats[];
|
|
28
|
+
tokens: TokenUsage;
|
|
29
|
+
costUSD: number;
|
|
30
|
+
perModelCost: ModelCost[];
|
|
31
|
+
}
|
|
32
|
+
export interface ReportMeta {
|
|
33
|
+
generatedAt: string;
|
|
34
|
+
rangeLabel: string;
|
|
35
|
+
agents: string[];
|
|
36
|
+
projectFilter: string;
|
|
37
|
+
totals: {
|
|
38
|
+
sessions: number;
|
|
39
|
+
durationMs: number;
|
|
40
|
+
turns: number;
|
|
41
|
+
files: number;
|
|
42
|
+
netLines: number;
|
|
43
|
+
toolCallsTotal: number;
|
|
44
|
+
toolSuccessRate: number;
|
|
45
|
+
totalCostUSD: number;
|
|
46
|
+
pricedSessions: number;
|
|
47
|
+
};
|
|
48
|
+
unpricedModels: string[];
|
|
49
|
+
coverage: AgentCoverage[];
|
|
50
|
+
}
|
|
51
|
+
export interface ReportPayload {
|
|
52
|
+
meta: ReportMeta;
|
|
53
|
+
sessions: ReportSessionRecord[];
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/analytics/report/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,SAAS,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/cli/commands/analytics/report/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -11,6 +11,8 @@ export interface ModelStats {
|
|
|
11
11
|
model: string;
|
|
12
12
|
calls: number;
|
|
13
13
|
percentage: number;
|
|
14
|
+
tokens?: import('./cost/types.js').TokenUsage;
|
|
15
|
+
costUSD?: number;
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* Tool usage statistics
|
|
@@ -55,6 +57,8 @@ export interface SessionAnalytics {
|
|
|
55
57
|
agentName: string;
|
|
56
58
|
provider: string;
|
|
57
59
|
workingDirectory: string;
|
|
60
|
+
/** The branch the session did the most work on (modal of its deltas' gitBranch). */
|
|
61
|
+
primaryBranch: string;
|
|
58
62
|
startTime: number;
|
|
59
63
|
endTime: number;
|
|
60
64
|
duration: number;
|
|
@@ -73,6 +77,8 @@ export interface SessionAnalytics {
|
|
|
73
77
|
files: FileOperationSummary[];
|
|
74
78
|
languages: LanguageStats[];
|
|
75
79
|
formats: LanguageStats[];
|
|
80
|
+
tokens?: import('./cost/types.js').TokenUsage;
|
|
81
|
+
costUSD?: number;
|
|
76
82
|
}
|
|
77
83
|
/**
|
|
78
84
|
* Branch-level analytics
|
|
@@ -167,5 +173,12 @@ export interface AnalyticsOptions {
|
|
|
167
173
|
verbose?: boolean;
|
|
168
174
|
export?: 'json' | 'csv';
|
|
169
175
|
output?: string;
|
|
176
|
+
report?: boolean;
|
|
177
|
+
open?: boolean;
|
|
178
|
+
reportOutput?: string;
|
|
179
|
+
/** Report serialization selector (default 'html'). 'json' writes the cost-enriched payload; 'both' writes html + json. */
|
|
180
|
+
reportFormat?: 'html' | 'json' | 'both';
|
|
181
|
+
/** When false (via --no-scan-native), skip native-log discovery and use tracked sessions only. */
|
|
182
|
+
scanNative?: boolean;
|
|
170
183
|
}
|
|
171
184
|
//# sourceMappingURL=types.d.ts.map
|