@jobshimo/browser-link 0.40.0 → 0.42.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.
Files changed (82) hide show
  1. package/README.md +27 -12
  2. package/dist/admin/actions.d.ts +57 -0
  3. package/dist/admin/actions.js +163 -0
  4. package/dist/admin/actions.js.map +1 -0
  5. package/dist/admin/descriptor.d.ts +65 -0
  6. package/dist/admin/descriptor.js +205 -0
  7. package/dist/admin/descriptor.js.map +1 -0
  8. package/dist/admin/i18n.d.ts +90 -0
  9. package/dist/admin/i18n.js +203 -0
  10. package/dist/admin/i18n.js.map +1 -0
  11. package/dist/admin/primary.d.ts +21 -0
  12. package/dist/admin/primary.js +56 -0
  13. package/dist/admin/primary.js.map +1 -0
  14. package/dist/admin/sources.d.ts +22 -0
  15. package/dist/admin/sources.js +82 -0
  16. package/dist/admin/sources.js.map +1 -0
  17. package/dist/bridge/dispatch.js +17 -7
  18. package/dist/bridge/dispatch.js.map +1 -1
  19. package/dist/bridge/ws-bridge.d.ts +65 -3
  20. package/dist/bridge/ws-bridge.js +165 -7
  21. package/dist/bridge/ws-bridge.js.map +1 -1
  22. package/dist/cli.js +6 -2
  23. package/dist/cli.js.map +1 -1
  24. package/dist/commands/doctor.d.ts +24 -0
  25. package/dist/commands/doctor.js +62 -1
  26. package/dist/commands/doctor.js.map +1 -1
  27. package/dist/commands/free-port.d.ts +40 -1
  28. package/dist/commands/free-port.js +72 -7
  29. package/dist/commands/free-port.js.map +1 -1
  30. package/dist/extension/admin-view.d.ts +95 -0
  31. package/dist/extension/admin-view.js +164 -0
  32. package/dist/extension/admin-view.js.map +1 -0
  33. package/dist/extension/background.js +176 -8
  34. package/dist/extension/background.js.map +1 -1
  35. package/dist/extension/manifest.json +1 -1
  36. package/dist/extension/popup.html +113 -0
  37. package/dist/extension/popup.js +282 -1
  38. package/dist/extension/popup.js.map +1 -1
  39. package/dist/extension/repair-policy.d.ts +60 -0
  40. package/dist/extension/repair-policy.js +79 -0
  41. package/dist/extension/repair-policy.js.map +1 -0
  42. package/dist/extension/version-status.d.ts +44 -0
  43. package/dist/extension/version-status.js +60 -0
  44. package/dist/extension/version-status.js.map +1 -0
  45. package/dist/lifecycle/election.d.ts +98 -0
  46. package/dist/lifecycle/election.js +187 -0
  47. package/dist/lifecycle/election.js.map +1 -0
  48. package/dist/lifecycle/processes.d.ts +67 -0
  49. package/dist/lifecycle/processes.js +239 -0
  50. package/dist/lifecycle/processes.js.map +1 -0
  51. package/dist/lifecycle/repair.d.ts +48 -0
  52. package/dist/lifecycle/repair.js +67 -0
  53. package/dist/lifecycle/repair.js.map +1 -0
  54. package/dist/lifecycle/version-endpoint.d.ts +16 -0
  55. package/dist/lifecycle/version-endpoint.js +50 -0
  56. package/dist/lifecycle/version-endpoint.js.map +1 -0
  57. package/dist/manual/topics.js +1 -1
  58. package/dist/manual/topics.js.map +1 -1
  59. package/dist/messages.d.ts +115 -2
  60. package/dist/server.js +104 -1
  61. package/dist/server.js.map +1 -1
  62. package/dist/tools/browser-dispatch.d.ts +8 -0
  63. package/dist/tools/browser-dispatch.js +15 -1
  64. package/dist/tools/browser-dispatch.js.map +1 -1
  65. package/dist/ui/app.d.ts +4 -1
  66. package/dist/ui/app.js +55 -42
  67. package/dist/ui/app.js.map +1 -1
  68. package/dist/ui/screens/free-port.js +4 -2
  69. package/dist/ui/screens/free-port.js.map +1 -1
  70. package/dist/ui/screens/index.d.ts +1 -0
  71. package/dist/ui/screens/index.js +1 -0
  72. package/dist/ui/screens/index.js.map +1 -1
  73. package/dist/ui/screens/menu.d.ts +11 -1
  74. package/dist/ui/screens/menu.js +12 -4
  75. package/dist/ui/screens/menu.js.map +1 -1
  76. package/dist/ui/screens/status.d.ts +19 -0
  77. package/dist/ui/screens/status.js +67 -0
  78. package/dist/ui/screens/status.js.map +1 -0
  79. package/dist/version.d.ts +8 -0
  80. package/dist/version.js +16 -0
  81. package/dist/version.js.map +1 -1
  82. package/package.json +1 -1
package/README.md CHANGED
@@ -59,10 +59,12 @@ The fastest path is the interactive UI — a full-screen [Ink](https://github.co
59
59
  browser-link
60
60
  ```
61
61
 
62
- That opens the welcome screen and then a setup menu to register with
63
- **Claude Code**, **OpenCode** or **GitHub Copilot CLI**, see the Chrome
64
- extension install steps, run a doctor diagnose, and manage per-tool
65
- permissions.
62
+ That opens the welcome screen and then a status view: the running server,
63
+ its ports, multi-agent, the instructions block per client, cdp-direct and
64
+ updates, with the setup menu's entries and hotkeys right below it (Tab opens
65
+ the full menu) to register with **Claude Code**, **OpenCode** or **GitHub
66
+ Copilot CLI**, see the Chrome extension install steps, run a doctor
67
+ diagnose, and manage per-tool permissions.
66
68
 
67
69
  Or drive it directly:
68
70
 
@@ -70,7 +72,8 @@ Or drive it directly:
70
72
  browser-link install # register in every detected client
71
73
  browser-link install --client claude # register only in Claude Code
72
74
  browser-link extension # show the Chrome extension assets path + steps
73
- browser-link doctor # diagnose current setup
75
+ browser-link doctor # diagnose current setup (running vs installed version, live processes)
76
+ browser-link stop # stop every live browser-link server, one line per process
74
77
  browser-link tools # show which of the 31 MCP tools are enabled
75
78
  browser-link tools preset readonly # all | readonly | no-eval | no-map
76
79
  browser-link map # list apps the persistent UI map knows about
@@ -84,11 +87,11 @@ tab you want the agent to reach.
84
87
 
85
88
  ## The 31 tools, at a glance
86
89
 
87
- | Family | Count | What it covers |
88
- | -------------------------- | ----: | ----------------------------------------------------------------------------------- |
89
- | Browser bridge — read | 13 | list/claim tabs, snapshot/find/state, console, network, wait_for(_tab), events |
90
- | Browser bridge — actions | 12 | navigate, click, type, press, drag, flow, evaluate, dialogs, permissions, reset |
91
- | Persistent UI map | 6 | recall, save, record_use, forget, rename_app, apps |
90
+ | Family | Count | What it covers |
91
+ | ------------------------ | ----: | ------------------------------------------------------------------------------- |
92
+ | Browser bridge — read | 13 | list/claim tabs, snapshot/find/state, console, network, wait_for(_tab), events |
93
+ | Browser bridge — actions | 12 | navigate, click, type, press, drag, flow, evaluate, dialogs, permissions, reset |
94
+ | Persistent UI map | 6 | recall, save, record_use, forget, rename_app, apps |
92
95
 
93
96
  Every tool can be disabled per machine via `browser-link tools disable
94
97
  <name>` or a preset (`all` / `readonly` / `no-eval` / `no-map`). The full
@@ -99,8 +102,8 @@ this page stays a landing page on purpose.
99
102
 
100
103
  ## `browser-link map` — inspect and manage the persistent map
101
104
 
102
- The 6 map tools above are how an *agent* reads and writes the map while it
103
- works. `browser-link map` is how a *human* looks at the same data from a
105
+ The 6 map tools above are how an _agent_ reads and writes the map while it
106
+ works. `browser-link map` is how a _human_ looks at the same data from a
104
107
  terminal, no agent required:
105
108
 
106
109
  ```bash
@@ -127,6 +130,18 @@ browser-link map import map.json # restore it (merge by default, --re
127
130
  the same bridge and map. `browser-link multi-agent` shows the current
128
131
  state; see [Multi-agent mode](https://github.com/jobshimo/browser-link#multi-agent-mode)
129
132
  for how it works.
133
+ - **Updates take effect by themselves**: a newer browser-link that starts
134
+ displaces an older primary (it reads `GET /version` on the bridge port),
135
+ `browser-link stop` stops every live server, `doctor` and `list_tabs`
136
+ show "running X / installed Y", and the extension popup has a "Repair
137
+ connection" button. See
138
+ [Updating while a server is running](https://github.com/jobshimo/browser-link#updating-while-a-server-is-running).
139
+ - **Admin panel**: the console's status view and the extension popup's
140
+ Admin section render the same server-built sections and labels (English
141
+ or Spanish). Every change that writes to disk shows a one-line preview and
142
+ waits for your confirmation; stopping or updating the server stays a
143
+ console command. See
144
+ [Admin panel](https://github.com/jobshimo/browser-link#admin-panel-console-and-popup).
130
145
  - **cdp-direct mode** (off by default): an extension-free transport gated
131
146
  behind `browser-link config set cdp-direct.enabled true` PLUS a
132
147
  time-boxed `browser-link cdp allow` grant — see
@@ -0,0 +1,57 @@
1
+ import { type BrowserLinkConfig } from '../config.js';
2
+ import { disableTools, enableTools } from '../commands/tools.js';
3
+ import { disableAutoReelect, disableMultiAgent, enableAutoReelect, enableMultiAgent } from '../commands/multi-agent.js';
4
+ import { setCdpDirectEnabled, setCdpDirectGrantTtl } from '../commands/config.js';
5
+ import type { ClientId } from '../installers/index.js';
6
+ import type { AdminActionResult } from '../messages.js';
7
+ import { type AdminInput } from './descriptor.js';
8
+ /**
9
+ * Admin actions (S2). The only way the popup changes server state, and a
10
+ * closed allowlist: an action id must name an actionable item of the state
11
+ * the server builds right now (so a tool outside the catalogue, a client
12
+ * that is not detected, or a readout can never be written), its value is
13
+ * checked against the item's kind and options, a disk write needs an
14
+ * explicit `confirm: true`, and the write itself goes through the SAME
15
+ * helper the CLI uses, never a new write path.
16
+ */
17
+ export interface AdminActionRequest {
18
+ id: string;
19
+ value?: string | number | boolean;
20
+ confirm?: boolean;
21
+ }
22
+ /** The existing write helpers, injectable so tests can spy on each one. */
23
+ export interface AdminHelpers {
24
+ saveConfig: (patch: Partial<BrowserLinkConfig>) => unknown;
25
+ enableTools: typeof enableTools;
26
+ disableTools: typeof disableTools;
27
+ enableMultiAgent: typeof enableMultiAgent;
28
+ disableMultiAgent: typeof disableMultiAgent;
29
+ enableAutoReelect: typeof enableAutoReelect;
30
+ disableAutoReelect: typeof disableAutoReelect;
31
+ setCdpDirectEnabled: typeof setCdpDirectEnabled;
32
+ setCdpDirectGrantTtl: typeof setCdpDirectGrantTtl;
33
+ installInstructionsFor: (client: ClientId) => {
34
+ ok: boolean;
35
+ message?: string;
36
+ };
37
+ uninstallInstructionsFor: (client: ClientId) => {
38
+ ok: boolean;
39
+ message?: string;
40
+ };
41
+ installFor: (client: ClientId) => {
42
+ installedClient: boolean;
43
+ message: string;
44
+ };
45
+ }
46
+ export declare const DEFAULT_ADMIN_HELPERS: AdminHelpers;
47
+ export interface AdminActionDeps {
48
+ /** Fresh facts for the state; called before and after the action. */
49
+ input: () => Promise<AdminInput>;
50
+ /** `runDoctor` + `formatDoctor`, split into lines. Slow: runs only on the button. */
51
+ doctor: () => Promise<string[]>;
52
+ /** A forced update check, formatted into lines (network). */
53
+ checkUpdates: () => Promise<string[]>;
54
+ helpers?: Partial<AdminHelpers>;
55
+ }
56
+ /** Validate, confirm, apply. Never throws: every failure is `ok: false`. */
57
+ export declare function applyAdminAction(req: AdminActionRequest, deps: AdminActionDeps): Promise<AdminActionResult>;
@@ -0,0 +1,163 @@
1
+ import { saveConfig } from '../config.js';
2
+ import { disableTools, enableTools } from '../commands/tools.js';
3
+ import { disableAutoReelect, disableMultiAgent, enableAutoReelect, enableMultiAgent, } from '../commands/multi-agent.js';
4
+ import { setCdpDirectEnabled, setCdpDirectGrantTtl } from '../commands/config.js';
5
+ import { installInstructionsFor, uninstallInstructionsFor } from '../commands/instructions.js';
6
+ import { installFor } from '../commands/install.js';
7
+ import { adminItems, adminLanguage, buildAdminState } from './descriptor.js';
8
+ import { ADMIN_I18N } from './i18n.js';
9
+ export const DEFAULT_ADMIN_HELPERS = {
10
+ saveConfig,
11
+ enableTools,
12
+ disableTools,
13
+ enableMultiAgent,
14
+ disableMultiAgent,
15
+ enableAutoReelect,
16
+ disableAutoReelect,
17
+ setCdpDirectEnabled,
18
+ setCdpDirectGrantTtl,
19
+ installInstructionsFor,
20
+ uninstallInstructionsFor,
21
+ installFor: (client) => installFor(client),
22
+ };
23
+ /** Toggles whose value is one config key written with `saveConfig`, exactly
24
+ * as the console does for the keys without a dedicated CLI helper. */
25
+ const PLAIN_TOGGLES = [
26
+ 'instructionsAutoRefresh',
27
+ 'activityEnabled',
28
+ 'activityRecordPayloads',
29
+ ];
30
+ function isPlainToggle(id) {
31
+ return PLAIN_TOGGLES.includes(id);
32
+ }
33
+ class AdminRefusal extends Error {
34
+ }
35
+ function suffix(id, prefix) {
36
+ return id.startsWith(prefix) ? id.slice(prefix.length) : null;
37
+ }
38
+ function lines(text) {
39
+ return text === undefined || text.length === 0 ? [] : text.split('\n');
40
+ }
41
+ function checkValue(item, value) {
42
+ if (item.kind === 'toggle')
43
+ return typeof value === 'boolean';
44
+ if (item.kind === 'select') {
45
+ if (typeof value !== 'string' && typeof value !== 'number')
46
+ return false;
47
+ return (item.options ?? []).some((o) => o.value === String(value));
48
+ }
49
+ return true;
50
+ }
51
+ function preview(item, value, input) {
52
+ const t = ADMIN_I18N[adminLanguage(input.config)];
53
+ const tool = suffix(item.id, 'tools.');
54
+ if (tool !== null)
55
+ return value === true ? t.previewToolOn(tool) : t.previewToolOff(tool);
56
+ if (item.kind === 'toggle')
57
+ return t.previewToggle(item.label, value === true);
58
+ if (item.kind === 'select') {
59
+ const option = item.options?.find((o) => o.value === String(value));
60
+ return t.previewSelect(item.label, option?.label ?? String(value));
61
+ }
62
+ const displayName = (id) => input.instructions.find((s) => s.id === id)?.displayName ??
63
+ input.clients.find((c) => c.id === id)?.displayName ??
64
+ String(id);
65
+ const install = suffix(item.id, 'instructions.install.');
66
+ if (install !== null)
67
+ return t.previewInstall(displayName(install));
68
+ const uninstall = suffix(item.id, 'instructions.uninstall.');
69
+ if (uninstall !== null)
70
+ return t.previewUninstall(displayName(uninstall));
71
+ return t.previewRegister(displayName(suffix(item.id, 'client.register.')));
72
+ }
73
+ /** Runs the write for one validated, confirmed item. Returns output lines;
74
+ * throws an `AdminRefusal` (or the helper's own error) when it refused. */
75
+ async function apply(item, value, input, deps, h) {
76
+ const language = adminLanguage(input.config);
77
+ const on = value === true;
78
+ const { id } = item;
79
+ if (id === 'doctor')
80
+ return deps.doctor();
81
+ if (id === 'updates.check')
82
+ return deps.checkUpdates();
83
+ if (id === 'language') {
84
+ h.saveConfig({ language: value === 'es' ? 'es' : 'en' });
85
+ return [];
86
+ }
87
+ const tool = suffix(id, 'tools.');
88
+ if (tool !== null) {
89
+ return lines(on ? h.enableTools([tool], language) : h.disableTools([tool], language));
90
+ }
91
+ if (id === 'multiAgent') {
92
+ return lines(on ? h.enableMultiAgent(language) : h.disableMultiAgent(language));
93
+ }
94
+ if (id === 'autoReelect') {
95
+ return lines(on ? h.enableAutoReelect(language) : h.disableAutoReelect(language));
96
+ }
97
+ if (isPlainToggle(id)) {
98
+ h.saveConfig({ [id]: on });
99
+ return [];
100
+ }
101
+ if (id === 'cdpDirectEnabled')
102
+ return lines(h.setCdpDirectEnabled(String(on), language));
103
+ if (id === 'cdpDirectGrantTtlMinutes') {
104
+ return lines(h.setCdpDirectGrantTtl(String(value), language));
105
+ }
106
+ const install = suffix(id, 'instructions.install.');
107
+ const uninstall = suffix(id, 'instructions.uninstall.');
108
+ if (install !== null || uninstall !== null) {
109
+ const report = install !== null
110
+ ? h.installInstructionsFor(install)
111
+ : h.uninstallInstructionsFor(uninstall);
112
+ if (!report.ok)
113
+ throw new AdminRefusal(report.message ?? id);
114
+ return lines(report.message);
115
+ }
116
+ const register = suffix(id, 'client.register.');
117
+ if (register !== null) {
118
+ const report = h.installFor(register);
119
+ if (!report.installedClient)
120
+ throw new AdminRefusal(report.message);
121
+ return lines(report.message);
122
+ }
123
+ // Reaching here means the descriptor offers an actionable item this
124
+ // module does not know: refuse rather than guess.
125
+ throw new AdminRefusal(ADMIN_I18N[language].unknownAction(id));
126
+ }
127
+ /** Validate, confirm, apply. Never throws: every failure is `ok: false`. */
128
+ export async function applyAdminAction(req, deps) {
129
+ try {
130
+ const input = await deps.input();
131
+ const t = ADMIN_I18N[adminLanguage(input.config)];
132
+ const item = adminItems(buildAdminState(input)).find((i) => i.id === req.id);
133
+ if (!item)
134
+ return { ok: false, error: t.unknownAction(req.id) };
135
+ if (item.kind === 'readout' || item.kind === 'link') {
136
+ return { ok: false, error: t.notAnAction(item.id) };
137
+ }
138
+ if (!checkValue(item, req.value))
139
+ return { ok: false, error: t.invalidValue(item.id) };
140
+ if (item.writesDisk && req.confirm !== true) {
141
+ return {
142
+ ok: true,
143
+ applied: false,
144
+ needsConfirm: true,
145
+ preview: preview(item, req.value, input),
146
+ };
147
+ }
148
+ const helpers = { ...DEFAULT_ADMIN_HELPERS, ...deps.helpers };
149
+ const output = await apply(item, req.value, input, deps, helpers);
150
+ const state = buildAdminState(await deps.input());
151
+ return {
152
+ ok: true,
153
+ applied: true,
154
+ ...(item.requiresRestart ? { requiresRestart: true } : {}),
155
+ state,
156
+ ...(output.length > 0 ? { output } : {}),
157
+ };
158
+ }
159
+ catch (error) {
160
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
161
+ }
162
+ }
163
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/admin/actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { type BrowserLinkConfig } from '../config.js';
2
+ import type { ClientInstructionsStatus } from '../manual/status.js';
3
+ import type { ClientId } from '../installers/index.js';
4
+ import type { DoctorServerStatus } from '../commands/doctor.js';
5
+ import type { Language } from '../commands/welcome.js';
6
+ import type { AdminItem, AdminState } from '../messages.js';
7
+ /**
8
+ * The admin descriptor (S1): one source of truth for what the console's
9
+ * status view and the popup's Admin section show. `buildAdminState` is pure:
10
+ * every fact arrives in `AdminInput`, gathered by `admin/sources.ts` in the
11
+ * primary (in process) or in the console (out of process, through the
12
+ * primary's `/version`). Every string is localised here.
13
+ *
14
+ * Process-ending operations (`stop`/free-port, self-update) are never
15
+ * actions: they would end the very primary that answers the popup, so they
16
+ * are readouts carrying the console command instead.
17
+ */
18
+ export declare const REPO_URL = "https://github.com/jobshimo/browser-link";
19
+ export declare const STOP_COMMAND = "browser-link stop";
20
+ export declare const SELF_UPDATE_COMMAND = "npm install -g @jobshimo/browser-link@latest";
21
+ export interface AdminClient {
22
+ id: ClientId;
23
+ displayName: string;
24
+ /** The client's config exists on this machine. */
25
+ installed: boolean;
26
+ /** browser-link is already registered in it. */
27
+ registered: boolean;
28
+ }
29
+ export interface AdminInput {
30
+ config: BrowserLinkConfig;
31
+ /** Epoch ms used for `generatedAt` and the uptime. */
32
+ now: number;
33
+ /** The version of the process building the state. */
34
+ version: string;
35
+ /** Running primary (from `/version` or in process) against the version on disk. */
36
+ server: DoctorServerStatus;
37
+ /** `null` when unknown: the console runs out of process. */
38
+ tabsConnected: number | null;
39
+ ports: {
40
+ ws: number;
41
+ ipc: number;
42
+ };
43
+ extension: {
44
+ path: string | null;
45
+ connectedVersion: string | null;
46
+ };
47
+ instructions: readonly ClientInstructionsStatus[];
48
+ clients: readonly AdminClient[];
49
+ cdpGrant: {
50
+ live: boolean;
51
+ remainingMs: number | null;
52
+ };
53
+ /** Cached update check; `null` when none ran yet. */
54
+ update: {
55
+ latest: string | null;
56
+ isNewer: boolean;
57
+ } | null;
58
+ toolNames: readonly string[];
59
+ downloadsDir: string;
60
+ }
61
+ export declare function adminLanguage(config: BrowserLinkConfig): Language;
62
+ export declare function formatUptime(ms: number): string;
63
+ export declare function buildAdminState(input: AdminInput): AdminState;
64
+ /** Every item of a state, in display order. */
65
+ export declare function adminItems(state: AdminState): AdminItem[];
@@ -0,0 +1,205 @@
1
+ import { DEFAULT_CDP_DIRECT_PORT, DEFAULT_GRANT_TTL_MINUTES, } from '../config.js';
2
+ import { ADMIN_I18N } from './i18n.js';
3
+ /**
4
+ * The admin descriptor (S1): one source of truth for what the console's
5
+ * status view and the popup's Admin section show. `buildAdminState` is pure:
6
+ * every fact arrives in `AdminInput`, gathered by `admin/sources.ts` in the
7
+ * primary (in process) or in the console (out of process, through the
8
+ * primary's `/version`). Every string is localised here.
9
+ *
10
+ * Process-ending operations (`stop`/free-port, self-update) are never
11
+ * actions: they would end the very primary that answers the popup, so they
12
+ * are readouts carrying the console command instead.
13
+ */
14
+ export const REPO_URL = 'https://github.com/jobshimo/browser-link';
15
+ export const STOP_COMMAND = 'browser-link stop';
16
+ export const SELF_UPDATE_COMMAND = 'npm install -g @jobshimo/browser-link@latest';
17
+ /** Common lifetimes for the cdp-direct grant select; `0` = never expires. */
18
+ const GRANT_TTL_CHOICES = [15, 30, 60, 240, 1440, 0];
19
+ export function adminLanguage(config) {
20
+ return config.language === 'es' ? 'es' : 'en';
21
+ }
22
+ function readout(id, label, value, extra = {}) {
23
+ return { id, kind: 'readout', label, value, writesDisk: false, ...extra };
24
+ }
25
+ function toggle(id, label, value, requiresRestart = false) {
26
+ return {
27
+ id,
28
+ kind: 'toggle',
29
+ label,
30
+ value,
31
+ writesDisk: true,
32
+ ...(requiresRestart ? { requiresRestart: true } : {}),
33
+ };
34
+ }
35
+ export function formatUptime(ms) {
36
+ const totalMinutes = Math.max(0, Math.floor(ms / 60_000));
37
+ const days = Math.floor(totalMinutes / 1440);
38
+ const hours = Math.floor((totalMinutes % 1440) / 60);
39
+ const minutes = totalMinutes % 60;
40
+ if (days > 0)
41
+ return `${days}d ${hours}h ${minutes}m`;
42
+ if (hours > 0)
43
+ return `${hours}h ${minutes}m`;
44
+ return `${minutes}m`;
45
+ }
46
+ function statusSection(input, t) {
47
+ const { server, config } = input;
48
+ const running = server.running;
49
+ const startedAt = running ? Date.parse(running.startedAt) : NaN;
50
+ const items = [
51
+ readout('status.server', t.server, running ? t.serverRunning(running.version) : t.serverNotRunning, running ? {} : { hint: t.serverNotRunningHint }),
52
+ readout('status.installed', t.installed, server.installed === null
53
+ ? t.unknown
54
+ : server.outdated
55
+ ? t.installedOutdated(server.installed)
56
+ : server.installed, server.outdated ? { hint: STOP_COMMAND } : {}),
57
+ readout('status.pid', t.pid, running ? running.pid : null),
58
+ readout('status.uptime', t.uptime, running && Number.isFinite(startedAt) ? formatUptime(input.now - startedAt) : null),
59
+ readout('status.tabs', t.tabs, input.tabsConnected, input.tabsConnected === null ? { hint: t.tabsUnknownHint } : {}),
60
+ readout('status.ports', t.ports, `WS ${input.ports.ws} · IPC ${input.ports.ipc}`),
61
+ readout('status.multiAgent', t.multiAgentStatus, config.multiAgent === false ? t.off : t.on),
62
+ readout('status.extension', t.extension,
63
+ // Out of process the console cannot see the tabs, so it cannot know.
64
+ input.tabsConnected === null
65
+ ? t.unknown
66
+ : input.extension.connectedVersion === null
67
+ ? t.extensionNotConnected
68
+ : input.extension.connectedVersion === input.version
69
+ ? t.extensionAligned(input.extension.connectedVersion)
70
+ : t.extensionMismatch(input.extension.connectedVersion, input.version)),
71
+ ...input.instructions.map((s) => readout(`status.instructions.${s.id}`, t.instructionsFor(s.displayName), t.instructionsState(s.state))),
72
+ readout('status.cdpDirect', t.cdpDirect, config.cdpDirectEnabled !== true
73
+ ? t.off
74
+ : input.cdpGrant.live
75
+ ? t.cdpDirectGranted(input.cdpGrant.remainingMs === null
76
+ ? null
77
+ : Math.ceil(input.cdpGrant.remainingMs / 60_000))
78
+ : t.cdpDirectNoGrant),
79
+ readout('status.update', t.update, input.update === null || input.update.latest === null
80
+ ? t.unknown
81
+ : input.update.isNewer
82
+ ? t.updateAvailable(input.update.latest)
83
+ : t.upToDate, input.update?.isNewer === true ? { hint: SELF_UPDATE_COMMAND } : {}),
84
+ readout('status.activity', t.activity, config.activityEnabled === false ? t.off : t.on),
85
+ ];
86
+ return { id: 'status', title: t.sections.status, items };
87
+ }
88
+ function grantTtlOptions(current, t) {
89
+ const values = [...GRANT_TTL_CHOICES];
90
+ if (!values.includes(current))
91
+ values.unshift(current);
92
+ return values.map((n) => ({ value: String(n), label: n === 0 ? t.never : t.minutes(n) }));
93
+ }
94
+ function setupSection(input, t) {
95
+ const { config } = input;
96
+ const disabled = new Set(config.disabledTools ?? []);
97
+ const grantTtl = config.cdpDirectGrantTtlMinutes ?? DEFAULT_GRANT_TTL_MINUTES;
98
+ const items = [
99
+ {
100
+ id: 'language',
101
+ kind: 'select',
102
+ label: t.language,
103
+ value: adminLanguage(config),
104
+ options: [
105
+ { value: 'en', label: 'English' },
106
+ { value: 'es', label: 'Español' },
107
+ ],
108
+ writesDisk: true,
109
+ },
110
+ ...input.toolNames.map((name) => toggle(`tools.${name}`, t.tool(name), !disabled.has(name))),
111
+ toggle('multiAgent', t.multiAgent, config.multiAgent !== false, true),
112
+ toggle('autoReelect', t.autoReelect, config.autoReelect === true, true),
113
+ toggle('instructionsAutoRefresh', t.instructionsAutoRefresh, config.instructionsAutoRefresh !== false, true),
114
+ toggle('cdpDirectEnabled', t.cdpDirectEnabled, config.cdpDirectEnabled === true),
115
+ readout('cdpDirectPort', t.cdpDirectPort, config.cdpDirectPort ?? DEFAULT_CDP_DIRECT_PORT, {
116
+ hint: 'browser-link config set cdp-direct.port <port>',
117
+ }),
118
+ {
119
+ id: 'cdpDirectGrantTtlMinutes',
120
+ kind: 'select',
121
+ label: t.cdpDirectGrantTtl,
122
+ value: String(grantTtl),
123
+ options: grantTtlOptions(grantTtl, t),
124
+ writesDisk: true,
125
+ },
126
+ // Read once when the primary wires the trail, so a change needs a restart.
127
+ toggle('activityEnabled', t.activityEnabled, config.activityEnabled !== false, true),
128
+ toggle('activityRecordPayloads', t.activityRecordPayloads, config.activityRecordPayloads !== false, true),
129
+ readout('downloadsDir', t.downloadsDir, input.downloadsDir, {
130
+ hint: 'browser-link config set downloads.dir <absolute-path|default>',
131
+ }),
132
+ // These two keep their timestamped `settings.update` path (popup and
133
+ // `config set`), so the admin panel only shows them.
134
+ readout('idleTtl', t.idleTtl, config.idleTtlMinutes === undefined
135
+ ? t.popupDefault
136
+ : config.idleTtlMinutes === 0
137
+ ? t.never
138
+ : t.minutes(config.idleTtlMinutes), { hint: t.idleTtlHint }),
139
+ readout('flowRecording', t.flowRecording, config.flowRecordingEnabled === undefined
140
+ ? t.popupDefault
141
+ : config.flowRecordingEnabled
142
+ ? t.on
143
+ : t.off, { hint: t.flowRecordingHint }),
144
+ ];
145
+ return { id: 'setup', title: t.sections.setup, items };
146
+ }
147
+ function button(id, label, writesDisk, description) {
148
+ return { id, kind: 'button', label, writesDisk, ...(description ? { description } : {}) };
149
+ }
150
+ function diagnoseSection(input, t) {
151
+ const items = [
152
+ button('doctor', t.doctor, false),
153
+ button('updates.check', t.updatesCheck, false),
154
+ ];
155
+ for (const s of input.instructions) {
156
+ // A corrupt file is the CLI's business: the installers refuse to touch it.
157
+ if (s.state.kind === 'corrupt')
158
+ continue;
159
+ items.push(button(`instructions.install.${s.id}`, t.instructionsInstall(s.displayName), true));
160
+ if (s.state.kind === 'installed' || s.state.kind === 'installed-outdated') {
161
+ items.push(button(`instructions.uninstall.${s.id}`, t.instructionsUninstall(s.displayName), true));
162
+ }
163
+ }
164
+ for (const c of input.clients) {
165
+ if (!c.installed)
166
+ continue;
167
+ items.push(button(`client.register.${c.id}`, t.register(c.displayName), true, c.registered ? t.registered : t.notRegistered));
168
+ }
169
+ items.push(readout('stop', t.stop, STOP_COMMAND, { description: t.stopDescription }));
170
+ items.push(readout('selfUpdate', t.selfUpdate, SELF_UPDATE_COMMAND, {
171
+ description: t.selfUpdateDescription,
172
+ }));
173
+ return { id: 'diagnose', title: t.sections.diagnose, items };
174
+ }
175
+ function referenceSection(input, t) {
176
+ return {
177
+ id: 'reference',
178
+ title: t.sections.reference,
179
+ items: [
180
+ readout('about', t.about, t.aboutText),
181
+ { id: 'repo', kind: 'link', label: t.repo, value: REPO_URL, writesDisk: false },
182
+ readout('extension.path', t.extensionPath, input.extension.path ?? t.extensionNotFound),
183
+ ],
184
+ };
185
+ }
186
+ export function buildAdminState(input) {
187
+ const language = adminLanguage(input.config);
188
+ const t = ADMIN_I18N[language];
189
+ return {
190
+ version: 1,
191
+ generatedAt: new Date(input.now).toISOString(),
192
+ sections: [
193
+ statusSection(input, t),
194
+ setupSection(input, t),
195
+ diagnoseSection(input, t),
196
+ referenceSection(input, t),
197
+ ],
198
+ ui: { language, ...t.ui },
199
+ };
200
+ }
201
+ /** Every item of a state, in display order. */
202
+ export function adminItems(state) {
203
+ return state.sections.flatMap((s) => s.items);
204
+ }
205
+ //# sourceMappingURL=descriptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptor.js","sourceRoot":"","sources":["../../src/admin/descriptor.ts"],"names":[],"mappings":"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"}