@narumitw/pi-analytics 0.49.5 → 0.49.6
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 +1 -1
- package/package.json +2 -2
- package/src/menu.ts +49 -19
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ Pi exposes HTTP responses and final assistant failures, not every provider-SDK t
|
|
|
96
96
|
|
|
97
97
|
The command accepts no arguments. TUI mode uses the full dashboard; RPC mode adapts the same standard screens to dialogs. Print and JSON modes reject the interactive command observably instead of writing ad hoc protocol output.
|
|
98
98
|
|
|
99
|
-
The root menu contains Change time range, Skills, Tools, Provider reliability, Response cycles, Data & privacy, and Close. Skills and Tools are searchable browse views with details and model breakdowns. Escape goes Back from nested screens and closes the root. Ctrl+C closes the menu.
|
|
99
|
+
The root menu contains Change time range, Skills, Tools, Provider reliability, Response cycles, Data & privacy, and Close. Skills and Tools are searchable browse views with details and model breakdowns. Escape goes Back from nested screens and closes the root. Ctrl+C closes the menu. Data deletion uses Pi TUI Kit's standalone confirmation: Back keeps the dashboard open, Ctrl+C closes it in TUI mode, and cancellation never clears data.
|
|
100
100
|
|
|
101
101
|
## 🔐 Local data and privacy
|
|
102
102
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-analytics",
|
|
3
|
-
"version": "0.49.
|
|
3
|
+
"version": "0.49.6",
|
|
4
4
|
"description": "Local-first usage analytics for Pi models, skills, tools, and reliability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@narumitw/pi-tui-kit": "^0.
|
|
35
|
+
"@narumitw/pi-tui-kit": "^0.51.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@earendil-works/pi-coding-agent": "*"
|
package/src/menu.ts
CHANGED
|
@@ -28,6 +28,11 @@ export interface AnalyticsMenuState {
|
|
|
28
28
|
result: AnalyticsLoadResult;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export interface AnalyticsMenuOptions {
|
|
32
|
+
runConfirmation: typeof import("@narumitw/pi-tui-kit")["runConfirmation"];
|
|
33
|
+
isCurrent(): boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
31
36
|
type Screen = "main" | "range" | "skills" | "tools" | "reliability" | "responses" | "privacy";
|
|
32
37
|
type Action = "setRange" | "clearData";
|
|
33
38
|
|
|
@@ -38,7 +43,11 @@ const RANGE_LABELS: Record<TimeRangeId, string> = {
|
|
|
38
43
|
all: "All time",
|
|
39
44
|
};
|
|
40
45
|
|
|
41
|
-
export function createAnalyticsMenu(
|
|
46
|
+
export function createAnalyticsMenu(
|
|
47
|
+
source: AnalyticsMenuDataSource,
|
|
48
|
+
now: () => number = Date.now,
|
|
49
|
+
options?: AnalyticsMenuOptions,
|
|
50
|
+
) {
|
|
42
51
|
let rangeId: TimeRangeId = "7d";
|
|
43
52
|
let cachedState: AnalyticsMenuState | undefined;
|
|
44
53
|
const loadState = async (signal: AbortSignal): Promise<AnalyticsMenuState> => {
|
|
@@ -119,27 +128,45 @@ export function createAnalyticsMenu(source: AnalyticsMenuDataSource, now: () =>
|
|
|
119
128
|
},
|
|
120
129
|
clearData: async ({ ctx, state, signal }) => {
|
|
121
130
|
if (state.result.kind !== "ready") return { kind: "stay" };
|
|
131
|
+
if (!options) throw new Error("Analytics confirmation is unavailable");
|
|
122
132
|
const count = state.result.snapshot.overview.responseCycles;
|
|
123
|
-
const
|
|
124
|
-
"Delete analytics data?",
|
|
125
|
-
`This will clear all local analytics history from:\n\n${safeDisplayText(state.path)}\n\nThe selected range currently shows ${count} response cycles. Other running Pi processes may add new records afterward.`,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
const confirmation = await options.runConfirmation(ctx, {
|
|
134
|
+
title: "Delete analytics data?",
|
|
135
|
+
message: `This will clear all local analytics history from:\n\n${safeDisplayText(state.path)}\n\nThe selected range currently shows ${count} response cycles. Other running Pi processes may add new records afterward.`,
|
|
136
|
+
confirmLabel: "Delete data",
|
|
137
|
+
cancelLabel: "Keep data",
|
|
138
|
+
signal,
|
|
139
|
+
isCurrent: options.isCurrent,
|
|
140
|
+
// Keep the dashboard's existing domain-level error route as the only notifier.
|
|
141
|
+
onError: () => undefined,
|
|
142
|
+
});
|
|
143
|
+
if (signal.aborted || !options.isCurrent()) return { kind: "close" };
|
|
144
|
+
if (confirmation.kind === "closed") {
|
|
145
|
+
return confirmation.reason === "close" ? { kind: "close" } : { kind: "stay" };
|
|
146
|
+
}
|
|
147
|
+
if (confirmation.kind === "stale") return { kind: "close" };
|
|
148
|
+
if (confirmation.kind === "unsupported") {
|
|
149
|
+
throw new Error(`Analytics confirmation is unavailable in ${confirmation.mode} mode`);
|
|
150
|
+
}
|
|
151
|
+
if (confirmation.kind === "error") throw confirmation.error;
|
|
152
|
+
|
|
129
153
|
const result = await source.clearAll(signal);
|
|
130
154
|
cachedState = undefined;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
ctx.ui.notify(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
const isCurrent = options.isCurrent();
|
|
156
|
+
if (isCurrent) {
|
|
157
|
+
try {
|
|
158
|
+
ctx.ui.notify("Cleared local analytics data.", "info");
|
|
159
|
+
if (result.cleanupIncomplete) {
|
|
160
|
+
ctx.ui.notify(
|
|
161
|
+
"Some obsolete analytics files are still in use. Stop other Pi processes and clear again to remove them.",
|
|
162
|
+
"warning",
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// The host can dispose the current UI after the ownership check.
|
|
138
167
|
}
|
|
139
|
-
} catch {
|
|
140
|
-
// Session replacement can invalidate the UI after the generation switch.
|
|
141
168
|
}
|
|
142
|
-
return signal.aborted ? { kind: "close" } : { kind: "to", screen: "main" };
|
|
169
|
+
return signal.aborted || !isCurrent ? { kind: "close" } : { kind: "to", screen: "main" };
|
|
143
170
|
},
|
|
144
171
|
},
|
|
145
172
|
};
|
|
@@ -158,9 +185,12 @@ export async function showAnalyticsMenu(
|
|
|
158
185
|
source: AnalyticsMenuDataSource,
|
|
159
186
|
options: { signal: AbortSignal; isCurrent: () => boolean },
|
|
160
187
|
): Promise<void> {
|
|
161
|
-
const { runMenu, runTask } = await import("@narumitw/pi-tui-kit");
|
|
188
|
+
const { runConfirmation, runMenu, runTask } = await import("@narumitw/pi-tui-kit");
|
|
162
189
|
if (options.signal.aborted || !options.isCurrent()) return;
|
|
163
|
-
const controller = createAnalyticsMenu(source
|
|
190
|
+
const controller = createAnalyticsMenu(source, Date.now, {
|
|
191
|
+
runConfirmation,
|
|
192
|
+
isCurrent: options.isCurrent,
|
|
193
|
+
});
|
|
164
194
|
const loading = await runTask(ctx, {
|
|
165
195
|
label: "Loading local analytics…",
|
|
166
196
|
signal: options.signal,
|