@event4u/agent-switch 2.1.0 → 2.3.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 +166 -73
- package/dist/activity.js +99 -0
- package/dist/args.js +23 -3
- package/dist/auth-status.js +159 -0
- package/dist/balance.js +55 -0
- package/dist/browsers.js +90 -0
- package/dist/daemon.js +95 -42
- package/dist/desktop-sessions.js +115 -0
- package/dist/desktop-settings.js +90 -0
- package/dist/dir-resolve.js +178 -0
- package/dist/doctor.js +239 -69
- package/dist/gui-settings.js +67 -0
- package/dist/history.js +20 -0
- package/dist/hooks.js +21 -0
- package/dist/index.js +879 -68
- package/dist/launchers.js +228 -0
- package/dist/locks.js +61 -0
- package/dist/mappings.js +48 -9
- package/dist/pools.js +157 -0
- package/dist/profiles.js +181 -67
- package/dist/protection.js +100 -0
- package/dist/rebind.js +115 -12
- package/dist/service.js +40 -9
- package/dist/sessions.js +82 -13
- package/dist/share.js +92 -13
- package/dist/shellenv.js +122 -21
- package/dist/single-load.js +5 -0
- package/dist/token-freshen.js +11 -0
- package/package.json +1 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GUI preferences that must outlive the webview's `localStorage` — the Pet
|
|
3
|
+
* intro once-flag (D36) and the launch-at-login preference. The GUI can only
|
|
4
|
+
* reach the filesystem through this CLI, so it reads and writes them with
|
|
5
|
+
* `agent-switch gui-settings`; `doctor` reads the autostart preference from the
|
|
6
|
+
* same file.
|
|
7
|
+
*
|
|
8
|
+
* Additive file (`<ROOT>/gui-settings.json`): absent keys mean "never decided",
|
|
9
|
+
* so an upgrade starts with nothing stored and the GUI migrates its old
|
|
10
|
+
* `localStorage` flags in once.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { ROOT, ensureRoot } from "./profiles.js";
|
|
15
|
+
export const GUI_SETTINGS_FILE = path.join(ROOT, "gui-settings.json");
|
|
16
|
+
function normalizeAutostart(raw) {
|
|
17
|
+
const r = (raw ?? {});
|
|
18
|
+
const at = typeof r.at === "string" ? r.at : "";
|
|
19
|
+
if (r.state === "enabled" && (r.by === "first-run" || r.by === "user"))
|
|
20
|
+
return { state: "enabled", by: r.by, at };
|
|
21
|
+
if (r.state === "disabled" && r.by === "user")
|
|
22
|
+
return { state: "disabled", by: "user", at };
|
|
23
|
+
return { state: "unset" };
|
|
24
|
+
}
|
|
25
|
+
export function readGuiSettings(file = GUI_SETTINGS_FILE) {
|
|
26
|
+
let raw = {};
|
|
27
|
+
try {
|
|
28
|
+
raw = JSON.parse(fs.readFileSync(file, "utf8")) ?? {};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* absent / unparsable → nothing decided */
|
|
32
|
+
}
|
|
33
|
+
const d = raw.autostartDegraded;
|
|
34
|
+
return {
|
|
35
|
+
petWelcomed: raw.petWelcomed === true,
|
|
36
|
+
autostart: normalizeAutostart(raw.autostart),
|
|
37
|
+
autostartDegraded: d && typeof d === "object" && typeof d.reason === "string"
|
|
38
|
+
? { at: typeof d.at === "string" ? d.at : "", reason: d.reason }
|
|
39
|
+
: null,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function writeGuiSettings(next, file = GUI_SETTINGS_FILE) {
|
|
43
|
+
if (file === GUI_SETTINGS_FILE)
|
|
44
|
+
ensureRoot();
|
|
45
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
46
|
+
fs.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", { mode: 0o600 });
|
|
47
|
+
fs.renameSync(tmp, file);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Apply one update. Guards the two invariants the roadmap pins: the pet intro
|
|
51
|
+
* flag only ever goes false → true, and a first-run default never replaces a
|
|
52
|
+
* stored choice (only `by: "user"` may overwrite one).
|
|
53
|
+
*/
|
|
54
|
+
export function applyGuiSetting(cur, u, now = new Date()) {
|
|
55
|
+
const at = now.toISOString();
|
|
56
|
+
if (u.key === "pet-welcomed")
|
|
57
|
+
return { ...cur, petWelcomed: true };
|
|
58
|
+
if (u.key === "autostart-degraded") {
|
|
59
|
+
return { ...cur, autostartDegraded: u.reason ? { at, reason: u.reason } : null };
|
|
60
|
+
}
|
|
61
|
+
if (u.state === "disabled" && u.by !== "user")
|
|
62
|
+
throw new Error("only the user can turn launch-at-login off");
|
|
63
|
+
if (u.by === "first-run" && cur.autostart.state !== "unset")
|
|
64
|
+
return cur;
|
|
65
|
+
const autostart = u.state === "enabled" ? { state: "enabled", by: u.by, at } : { state: "disabled", by: "user", at };
|
|
66
|
+
return { ...cur, autostart, autostartDegraded: null };
|
|
67
|
+
}
|
package/dist/history.js
CHANGED
|
@@ -56,3 +56,23 @@ export function seriesFor(samples, windowKey) {
|
|
|
56
56
|
.map((s) => s.windows.find((w) => w.key === windowKey)?.utilization)
|
|
57
57
|
.filter((u) => typeof u === "number");
|
|
58
58
|
}
|
|
59
|
+
/** The last snapshot read for a profile, kept unthrottled next to the history:
|
|
60
|
+
* `dir` trusts usage only while it is minutes old, and the history keeps at
|
|
61
|
+
* most one sample an hour. Holds the snapshot only, never a token. */
|
|
62
|
+
export const LATEST_FILE = "usage-latest.json";
|
|
63
|
+
export function writeLatestSnapshot(file, snapshot) {
|
|
64
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
65
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
66
|
+
fs.writeFileSync(tmp, JSON.stringify(snapshot) + "\n", { mode: 0o600 });
|
|
67
|
+
fs.renameSync(tmp, file);
|
|
68
|
+
}
|
|
69
|
+
export function readLatestSnapshot(file) {
|
|
70
|
+
try {
|
|
71
|
+
const snapshot = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
72
|
+
const capturedMs = Date.parse(snapshot?.capturedAt);
|
|
73
|
+
return Array.isArray(snapshot?.windows) && !isNaN(capturedMs) ? { snapshot, capturedMs } : null;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/hooks.js
CHANGED
|
@@ -122,6 +122,27 @@ export function readEvents(file) {
|
|
|
122
122
|
return [];
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Sessions waiting on the user: their last hook event is `Notification` (a
|
|
127
|
+
* permission prompt, or a long wait for input) and the transcript has not been
|
|
128
|
+
* written since — a reply or an approval writes to it. `Stop` alone is not
|
|
129
|
+
* waiting: it ends every turn.
|
|
130
|
+
*/
|
|
131
|
+
export function waitingSessions(events, mtimeMs) {
|
|
132
|
+
const last = new Map();
|
|
133
|
+
for (const e of events)
|
|
134
|
+
if (e.sessionId)
|
|
135
|
+
last.set(e.sessionId, e);
|
|
136
|
+
// sessionId → when it started waiting.
|
|
137
|
+
const out = new Map();
|
|
138
|
+
for (const [id, e] of last) {
|
|
139
|
+
const written = mtimeMs(id);
|
|
140
|
+
// Two seconds of slack: the notification and the transcript line that led to it land together.
|
|
141
|
+
if (e.event === "Notification" && written !== null && Date.parse(e.at) + 2000 >= written)
|
|
142
|
+
out.set(id, e.at);
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
125
146
|
/** Append one event, trimming the ring to EVENT_RING_CAP. Tolerant of a
|
|
126
147
|
* malformed existing file (starts fresh). */
|
|
127
148
|
export function appendEvent(file, rec) {
|