pi-profile-switch 0.12.0 → 0.12.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/package.json +1 -1
- package/src/startup-notifier.ts +4 -51
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-profile-switch",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Named profiles for Pi: reference skills, extensions, MCP servers, and tools per workflow, switched without restarting. Install: npm install -g pi-profile-switch (not pi install).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
package/src/startup-notifier.ts
CHANGED
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
* (`workspaceDir`, i.e. `getProfileSwitchDir()`).
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import {
|
|
25
|
-
import { mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
24
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
26
25
|
import path from "node:path";
|
|
27
26
|
|
|
28
27
|
import { isRecord } from "./json-file.ts";
|
|
@@ -61,9 +60,6 @@ const MAX_ANNOUNCEMENTS = 50;
|
|
|
61
60
|
const MAX_ID_LENGTH = 128;
|
|
62
61
|
const MAX_MESSAGE_LENGTH = 400;
|
|
63
62
|
const MAX_ACTION_LENGTH = 200;
|
|
64
|
-
/** A pending display claim older than this is considered abandoned (the
|
|
65
|
-
* presenting process died mid-display) and may be reclaimed. */
|
|
66
|
-
const CLAIM_STALE_MS = 10 * 60 * 1000;
|
|
67
63
|
const HISTORY_SCHEMA_VERSION = 1;
|
|
68
64
|
|
|
69
65
|
export interface NoticeSurface {
|
|
@@ -276,7 +272,7 @@ function parseNpmLatest(text: string): string {
|
|
|
276
272
|
}
|
|
277
273
|
|
|
278
274
|
// ---------------------------------------------------------------------------
|
|
279
|
-
// Global history
|
|
275
|
+
// Global history
|
|
280
276
|
// ---------------------------------------------------------------------------
|
|
281
277
|
|
|
282
278
|
function notificationsDir(workspaceDir: string): string {
|
|
@@ -316,41 +312,6 @@ async function recordDisplayed(dir: string, key: string): Promise<void> {
|
|
|
316
312
|
await atomicWriteJson(path.join(dir, "displayed.json"), { schemaVersion: HISTORY_SCHEMA_VERSION, keys: [...keys] });
|
|
317
313
|
}
|
|
318
314
|
|
|
319
|
-
function claimFile(dir: string, key: string): string {
|
|
320
|
-
const digest = createHash("sha256").update(key).digest("hex");
|
|
321
|
-
return path.join(dir, "claims", `${digest}.json`);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/** Exclusive per-key claim around synchronous presentation: concurrent
|
|
325
|
-
* launches (same process or separate ones) cannot both display the same
|
|
326
|
-
* notice. Abandoned claims go stale and become reclaimable. */
|
|
327
|
-
async function tryAcquireClaim(dir: string, key: string, nowMs: number): Promise<boolean> {
|
|
328
|
-
const file = claimFile(dir, key);
|
|
329
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
330
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
331
|
-
try {
|
|
332
|
-
const handle = await open(file, "wx");
|
|
333
|
-
try {
|
|
334
|
-
await handle.writeFile(JSON.stringify({ key, at: nowMs }));
|
|
335
|
-
} finally {
|
|
336
|
-
await handle.close();
|
|
337
|
-
}
|
|
338
|
-
return true;
|
|
339
|
-
} catch (error) {
|
|
340
|
-
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
341
|
-
const existing = await readJsonQuiet(file);
|
|
342
|
-
const at = isRecord(existing) && typeof existing.at === "number" ? existing.at : 0;
|
|
343
|
-
if (nowMs - at <= CLAIM_STALE_MS) return false;
|
|
344
|
-
await rm(file, { force: true });
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
async function releaseClaim(dir: string, key: string): Promise<void> {
|
|
351
|
-
await rm(claimFile(dir, key), { force: true });
|
|
352
|
-
}
|
|
353
|
-
|
|
354
315
|
// ---------------------------------------------------------------------------
|
|
355
316
|
// Candidate evaluation and presentation
|
|
356
317
|
// ---------------------------------------------------------------------------
|
|
@@ -425,16 +386,8 @@ function report(surface: NoticeSurface, message: string): void {
|
|
|
425
386
|
}
|
|
426
387
|
|
|
427
388
|
async function present(dir: string, candidate: Candidate, surface: NoticeSurface, nowMs: number): Promise<void> {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
surface.display(candidate.message, candidate.level);
|
|
431
|
-
await recordDisplayed(dir, candidate.key);
|
|
432
|
-
} catch (error) {
|
|
433
|
-
// A failed presentation releases its claim so a later launch retries.
|
|
434
|
-
await releaseClaim(dir, candidate.key);
|
|
435
|
-
throw error;
|
|
436
|
-
}
|
|
437
|
-
await releaseClaim(dir, candidate.key);
|
|
389
|
+
surface.display(candidate.message, candidate.level);
|
|
390
|
+
await recordDisplayed(dir, candidate.key);
|
|
438
391
|
}
|
|
439
392
|
|
|
440
393
|
// ---------------------------------------------------------------------------
|