pi-proxy-guard 0.5.1 → 0.6.0

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 CHANGED
@@ -73,6 +73,7 @@ Node switching is intentionally left to the proxy client (chain-proxy friendly).
73
73
  | `PI_PROXY_GUARD_PAUSED_REPAIRS` | `3` | active repair attempts per down-episode before passive watch |
74
74
  | `PI_PROXY_GUARD_NOTIFY` | `macos,bark,webhook` | channels; `0`/`off` disables |
75
75
  | `PI_PROXY_GUARD_NOTIFY_RESUME` | `0` | `1` also pushes when the session resumes |
76
+ | `PI_PROXY_GUARD_NOTIFY_FINISH_MS` | `0` | `>0` pushes "Pi finished" only for clean completes lasting ≥ this long — unlike pi-bark, never fires on error settles; e.g. `120000` |
76
77
  | `PI_PROXY_GUARD_NOTIFY_COOLDOWN_MS` | `600000` | min gap between pushes |
77
78
  | `PI_PROXY_GUARD_BARK` | _unset_ | Bark URL incl. device key |
78
79
  | `PI_PROXY_GUARD_WEBHOOK` | _unset_ | generic JSON webhook |
@@ -91,6 +91,10 @@ const NOTIFY_COOLDOWN_MS = Number(process.env.PI_PROXY_GUARD_NOTIFY_COOLDOWN_MS
91
91
  const BARK = process.env.PI_PROXY_GUARD_BARK ?? "";
92
92
  const WEBHOOK = process.env.PI_PROXY_GUARD_WEBHOOK ?? "";
93
93
  const PUSH_TITLE = "pi proxy-guard";
94
+ /** Push a "Pi finished" notification only for clean completes lasting >=
95
+ * this long. 0 disables. Beats pi-bark's blanket agent_settled push:
96
+ * outcome-aware (no false "finished" on error settles) + duration gate. */
97
+ const NOTIFY_FINISH_MS = Number(process.env.PI_PROXY_GUARD_NOTIFY_FINISH_MS ?? 0);
94
98
  const LOG_FILE =
95
99
  process.env.PI_PROXY_GUARD_LOG !== ""
96
100
  ? (process.env.PI_PROXY_GUARD_LOG ?? join(homedir(), ".pi", "agent", "proxy-guard.log"))
@@ -165,6 +169,9 @@ export default function (pi: ExtensionAPI) {
165
169
  let ticking = false;
166
170
  let lastRepairAt = 0;
167
171
  let lastNotifyAt = 0;
172
+ let runStartedAt = 0;
173
+ /** outcome of the most recent settle boundary; agent_settled has none. */
174
+ let lastSettleOutcome = "";
168
175
  /** Session-scoped watchdog + captured context. */
169
176
  let watchdog: ReturnType<typeof setInterval> | undefined;
170
177
  let sessionCtx: ExtensionContext | undefined;
@@ -195,6 +202,16 @@ export default function (pi: ExtensionAPI) {
195
202
  return;
196
203
  }
197
204
  lastNotifyAt = Date.now();
205
+ pushNow(body);
206
+ }
207
+
208
+ /** Duration-gated finish push; shares channels, unthrottled. */
209
+ function finishNotify(body: string): void {
210
+ if (NOTIFY_CHANNELS.length === 0 || NOTIFY_FINISH_MS <= 0) return;
211
+ pushNow(body);
212
+ }
213
+
214
+ function pushNow(body: string): void {
198
215
  // Machine + cwd prefix, same format as @herbertgao/pi-bark, so pushes
199
216
  // from pi-sync'd machines are attributable at a glance.
200
217
  const full = `\ud83d\udcbb ${hostname()}\n\ud83d\udcc1 ${process.cwd()}\n${body}`;
@@ -358,14 +375,25 @@ export default function (pi: ExtensionAPI) {
358
375
 
359
376
  pi.on("agent_start", () => {
360
377
  runActive = true;
378
+ runStartedAt = Date.now();
361
379
  pausedByUs = false; // whoever started the run, a fresh attempt is underway
362
380
  });
363
- pi.on("agent_settled", () => {
381
+ pi.on("agent_settled", (_event, ctx) => {
364
382
  runActive = false;
383
+ // Genuine completion (not a failure disguised as "finished", like
384
+ // pi-bark's blanket settle push) that took a while — worth notifying;
385
+ // quick interactive turns are not.
386
+ if (NOTIFY_FINISH_MS > 0 && lastSettleOutcome === "completed" && runStartedAt > 0 && ctx.isIdle()) {
387
+ const mins = (Date.now() - runStartedAt) / 60_000;
388
+ if (Date.now() - runStartedAt >= NOTIFY_FINISH_MS) {
389
+ finishNotify(`Pi finished — run took ${mins.toFixed(1)}min`);
390
+ }
391
+ }
365
392
  });
366
393
 
367
394
  pi.on("agent_before_settle", async (event, ctx) => {
368
395
  // Clean settle resets both counters: a new incident deserves a full budget.
396
+ lastSettleOutcome = event.outcome;
369
397
  if (event.outcome !== "error") {
370
398
  repairs = [];
371
399
  consecutiveErrors = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-proxy-guard",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Auto-recover pi sessions when the proxy drops: connectivity check, Shadowrocket restart/node-switch via Shortcuts or URL scheme, watchdog auto-resume.",
5
5
  "license": "MIT",
6
6
  "repository": {