@event4u/agent-switch 1.6.1 → 2.0.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 +5 -1
- package/dist/api.js +5 -4
- package/dist/args.js +1 -0
- package/dist/codex-reset.js +2 -2
- package/dist/config-transfer.js +184 -0
- package/dist/credentials.js +6 -4
- package/dist/daemon.js +68 -34
- package/dist/doctor.js +7 -0
- package/dist/gui-launch.js +45 -3
- package/dist/history.js +23 -0
- package/dist/hooks.js +5 -2
- package/dist/index.js +300 -50
- package/dist/keychain.js +14 -0
- package/dist/notifications.js +7 -2
- package/dist/profiles.js +26 -14
- package/dist/rebind-dialog.js +65 -0
- package/dist/rebind.js +715 -0
- package/dist/token-freshen.js +154 -0
- package/dist/tooling.js +324 -0
- package/dist/usage.js +8 -7
- package/package.json +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The limit-dialog model (Phase 4) — the PURE, testable core behind the CLI's
|
|
3
|
+
* interactive `agent-switch rebind` picker. It computes the switch-target rows
|
|
4
|
+
* and the pre-selected best-headroom suggestion; it performs NO switch and does
|
|
5
|
+
* NO I/O. The switch itself is always a user interaction (§ Out of scope,
|
|
6
|
+
* road-to-live-rebind.md): this module only shapes what the user is shown and
|
|
7
|
+
* maps their keystroke to a target. The credential write lives in rebind.ts.
|
|
8
|
+
*
|
|
9
|
+
* The suggestion reuses the usage helper `maxUtilization` — the same ranking
|
|
10
|
+
* `pickSwitchTarget` uses (lowest max-utilization = most headroom); no new
|
|
11
|
+
* ranking is invented. Unlike `pickSwitchTarget` it is NOT threshold-gated: the
|
|
12
|
+
* user opened the dialog on demand, so a best-headroom target is always
|
|
13
|
+
* pre-selected as a convenience (the pick, not the suggestion, is the switch).
|
|
14
|
+
*/
|
|
15
|
+
import { maxUtilization } from "./usage.js";
|
|
16
|
+
/**
|
|
17
|
+
* Build the dialog model. `runningProfile` is the profile the live session
|
|
18
|
+
* serves; it is excluded from the suggestion (and, marked `isRunning`, from the
|
|
19
|
+
* selectable rows). Pure — takes plain data, returns plain data.
|
|
20
|
+
*/
|
|
21
|
+
export function buildRebindDialog(candidates, runningProfile) {
|
|
22
|
+
const rows = candidates.map((c) => ({
|
|
23
|
+
profile: c.name,
|
|
24
|
+
label: c.label ?? null,
|
|
25
|
+
maxUtil: c.snapshot ? maxUtilization(c.snapshot) : null,
|
|
26
|
+
isRunning: c.name === runningProfile,
|
|
27
|
+
}));
|
|
28
|
+
// Suggestion = lowest known max-utilization among the non-running rows (most
|
|
29
|
+
// headroom). Same ranking as pickSwitchTarget's `m < best.max`, reusing the
|
|
30
|
+
// maxUtilization helper — no new ranking invented. Ties keep the first row.
|
|
31
|
+
let best = null;
|
|
32
|
+
for (const r of rows) {
|
|
33
|
+
if (r.isRunning || r.maxUtil === null)
|
|
34
|
+
continue;
|
|
35
|
+
if (best === null || r.maxUtil < best.maxUtil)
|
|
36
|
+
best = r;
|
|
37
|
+
}
|
|
38
|
+
return { rows, suggestedProfile: best ? best.profile : null };
|
|
39
|
+
}
|
|
40
|
+
/** The selectable rows (non-running), in dialog order — the list the CLI
|
|
41
|
+
* numbers 1..N and that {@link resolveRebindChoice} indexes against. */
|
|
42
|
+
export function selectableRows(dialog) {
|
|
43
|
+
return dialog.rows.filter((r) => !r.isRunning);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Map a raw picker keystroke to a target profile or a control action — the pure
|
|
47
|
+
* selection→target mapping the readline loop delegates to. `q`/`c` (any case) =
|
|
48
|
+
* cancel; empty = the pre-selected suggestion (or invalid when none); a 1-based
|
|
49
|
+
* index into the selectable rows = that profile; anything else = invalid. Never
|
|
50
|
+
* switches — it only resolves intent.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveRebindChoice(input, dialog) {
|
|
53
|
+
const trimmed = input.trim().toLowerCase();
|
|
54
|
+
if (trimmed === "q" || trimmed === "c")
|
|
55
|
+
return { kind: "cancel" };
|
|
56
|
+
const selectable = selectableRows(dialog);
|
|
57
|
+
if (trimmed === "") {
|
|
58
|
+
return dialog.suggestedProfile ? { kind: "target", profile: dialog.suggestedProfile } : { kind: "invalid" };
|
|
59
|
+
}
|
|
60
|
+
const n = Number(trimmed);
|
|
61
|
+
if (Number.isInteger(n) && n >= 1 && n <= selectable.length) {
|
|
62
|
+
return { kind: "target", profile: selectable[n - 1].profile };
|
|
63
|
+
}
|
|
64
|
+
return { kind: "invalid" };
|
|
65
|
+
}
|