@reticlehq/server 3.8.1 → 3.9.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/LICENSE +201 -104
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/bin/launch.js +13 -0
- package/bin/package.json +3 -0
- package/bin/reticle.js +66 -8
- package/dist/chromium-launch-options.d.ts +25 -0
- package/dist/chromium-launch-options.js +21 -1
- package/dist/command/cli/adhoc-verdict.js +34 -7
- package/dist/command/cli/answers/gate-exit.d.ts +6 -2
- package/dist/command/cli/answers/gate-exit.js +12 -2
- package/dist/command/cli/ci-hint.d.ts +44 -0
- package/dist/command/cli/ci-hint.js +72 -0
- package/dist/command/cli/cli-doctor.d.ts +6 -1
- package/dist/command/cli/cli-doctor.js +45 -4
- package/dist/command/cli/cli-flow-commands.d.ts +7 -1
- package/dist/command/cli/cli-flow-commands.js +39 -17
- package/dist/command/cli/cli-kill.d.ts +21 -0
- package/dist/command/cli/cli-kill.js +46 -3
- package/dist/command/cli/cli-parse.d.ts +5 -2
- package/dist/command/cli/cli-parse.js +10 -5
- package/dist/command/cli/cli-usage.js +3 -2
- package/dist/command/cli/cli-verify.d.ts +6 -1
- package/dist/command/cli/cli-verify.js +32 -13
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +6 -1
- package/dist/command/cli/doctor/browser/chromium-hint.js +11 -5
- package/dist/command/cli/doctor/doctor-fix.d.ts +44 -0
- package/dist/command/cli/doctor/doctor-fix.js +89 -0
- package/dist/command/cli/doctor/doctor-launcher-line.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-launcher-line.js +60 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/doctor-repairs.d.ts +64 -0
- package/dist/command/cli/doctor-repairs.js +214 -0
- package/dist/command/cli/drive/drive-command.js +3 -7
- package/dist/command/cli/first-use-checks.d.ts +41 -0
- package/dist/command/cli/first-use-checks.js +127 -0
- package/dist/command/cli/first-verdict-node.d.ts +3 -0
- package/dist/command/cli/first-verdict-node.js +189 -0
- package/dist/command/cli/first-verdict.d.ts +126 -0
- package/dist/command/cli/first-verdict.js +267 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +3 -2
- package/dist/command/cli/launch/sdk-version-match.js +3 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +6 -5
- package/dist/command/cli/mcp-command.js +13 -4
- package/dist/command/cli/ports/port-holder.d.ts +2 -0
- package/dist/command/cli/ports/port-holder.js +11 -3
- package/dist/command/cli/setup-mcp-cli.d.ts +27 -1
- package/dist/command/cli/setup-mcp-cli.js +98 -23
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +50 -17
- package/dist/command/cli/status-fields.js +17 -3
- package/dist/command/cli/try-command.d.ts +7 -0
- package/dist/command/cli/try-command.js +11 -3
- package/dist/command/cli.js +70 -33
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +3 -1
- package/dist/command/daemon/binding/daemon-status-probe.js +4 -2
- package/dist/command/daemon/daemon-argv.d.ts +9 -0
- package/dist/command/daemon/daemon-argv.js +11 -0
- package/dist/command/daemon/daemon.d.ts +10 -3
- package/dist/command/daemon/daemon.js +17 -6
- package/dist/command/daemon/lifetime/daemon-exit.d.ts +24 -0
- package/dist/command/daemon/lifetime/daemon-exit.js +39 -0
- package/dist/command/daemon/lifetime/death-cause.d.ts +15 -0
- package/dist/command/daemon/lifetime/death-cause.js +130 -0
- package/dist/command/daemon/lifetime/heartbeat.d.ts +7 -15
- package/dist/command/daemon/lifetime/heartbeat.js +4 -13
- package/dist/command/daemon/port-state.d.ts +14 -0
- package/dist/command/daemon/port-state.js +31 -0
- package/dist/command/setup/agent-configs.d.ts +2 -0
- package/dist/command/setup/agent-configs.js +42 -15
- package/dist/command/setup/agent-next-steps.d.ts +77 -0
- package/dist/command/setup/agent-next-steps.js +284 -0
- package/dist/command/setup/agent-writer.js +11 -6
- package/dist/command/setup/init/init-host.js +2 -0
- package/dist/command/setup/init/init-runtime.d.ts +6 -1
- package/dist/command/setup/init/init-runtime.js +57 -21
- package/dist/command/setup/mcp-launcher.d.ts +3 -0
- package/dist/command/setup/mcp-launcher.js +11 -0
- package/dist/command/setup/setup-command.js +30 -12
- package/dist/command/setup/setup-mcp.d.ts +8 -0
- package/dist/command/setup/setup-mcp.js +47 -20
- package/dist/command/setup/start-dev-server.d.ts +8 -0
- package/dist/command/setup/start-dev-server.js +43 -0
- package/dist/command/version/identity/server-version.d.ts +2 -0
- package/dist/command/version/identity/server-version.js +2 -0
- package/dist/command/version/version-skew.d.ts +7 -0
- package/dist/command/version/version-skew.js +13 -2
- package/dist/features/license/license.d.ts +11 -71
- package/dist/features/license/license.js +43 -136
- package/dist/features/visual/desktop-capture.d.ts +33 -0
- package/dist/features/visual/desktop-capture.js +94 -0
- package/dist/features/visual/step-shots.js +139 -35
- package/dist/features/visual/visual-tools.d.ts +0 -18
- package/dist/features/visual/visual-tools.js +2 -81
- package/dist/import-graph.js +5 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.js +14 -5
- package/dist/judgement/runs/run-ledger.d.ts +7 -5
- package/dist/judgement/runs/run-ledger.js +98 -69
- package/dist/judgement/runs/verification-sync.d.ts +6 -3
- package/dist/judgement/runs/verification-sync.js +21 -10
- package/dist/language/flows/flow-store-for-session.js +7 -5
- package/dist/language/flows/recording/tape/recordings.d.ts +2 -13
- package/dist/language/flows/recording/tape/recordings.js +41 -44
- package/dist/language/flows/replay.d.ts +17 -7
- package/dist/language/flows/replay.js +95 -44
- package/dist/launch-chromium.d.ts +1 -0
- package/dist/launch-chromium.js +49 -12
- package/dist/memory/cloud/sync-screenshots.js +6 -1
- package/dist/portal/bridge/bridge.d.ts +13 -4
- package/dist/portal/bridge/bridge.js +10 -9
- package/dist/portal/input/drive-error.d.ts +6 -0
- package/dist/portal/input/drive-error.js +9 -0
- package/dist/portal/input/network-mock.js +3 -2
- package/dist/portal/input/real-input-press.d.ts +34 -0
- package/dist/portal/input/real-input-press.js +150 -0
- package/dist/portal/input/real-input.d.ts +21 -23
- package/dist/portal/input/real-input.js +153 -194
- package/dist/portal/input/viewport-tools.js +6 -3
- package/dist/portal/pool/browser-pool.d.ts +5 -1
- package/dist/portal/pool/browser-pool.js +32 -38
- package/dist/portal/pool/context-permissions.d.ts +20 -17
- package/dist/portal/pool/context-permissions.js +63 -1
- package/dist/portal/pool/csp-widen.d.ts +62 -0
- package/dist/portal/pool/csp-widen.js +132 -0
- package/dist/portal/pool/lease-page-error.d.ts +29 -0
- package/dist/portal/pool/lease-page-error.js +18 -0
- package/dist/portal/pool/page-error-journal.d.ts +57 -0
- package/dist/portal/pool/page-error-journal.js +181 -0
- package/dist/portal/pool/playwright-launcher.js +21 -8
- package/dist/portal/pool/pool-contract.d.ts +22 -0
- package/dist/portal/pool/pool-lease.d.ts +35 -2
- package/dist/portal/pool/pool-lease.js +23 -0
- package/dist/portal/pool/zero-install.js +69 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +8 -0
- package/dist/portal/session/dev-server/dev-command.js +56 -8
- package/dist/portal/session/no-session-diagnosis.js +10 -3
- package/dist/portal/session/no-session-next-action.d.ts +12 -0
- package/dist/portal/session/no-session-next-action.js +44 -4
- package/dist/portal/session/no-session-watch.js +3 -1
- package/dist/portal/session/page-commands/undeclared-command.d.ts +9 -3
- package/dist/portal/session/page-commands/undeclared-command.js +16 -7
- package/dist/portal/session/recorded-gaps.d.ts +1 -1
- package/dist/portal/session/recorded-gaps.js +5 -2
- package/dist/portal/session/session-manager.js +40 -21
- package/dist/portal/session/session.js +4 -3
- package/dist/portal/session/zero-install-first-call.d.ts +20 -1
- package/dist/portal/session/zero-install-first-call.js +34 -2
- package/dist/start-wiring.d.ts +29 -2
- package/dist/start-wiring.js +73 -9
- package/dist/surface/mcp/mcp-proxy.js +10 -2
- package/dist/surface/mcp/mcp.js +5 -3
- package/dist/surface/mcp/peer/agents-connected.d.ts +6 -0
- package/dist/surface/mcp/peer/agents-connected.js +58 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +17 -5
- package/dist/surface/tools/act/act-preflight.js +37 -17
- package/dist/surface/tools/act/act-target.js +3 -1
- package/dist/surface/tools/act/after-match.d.ts +7 -0
- package/dist/surface/tools/act/after-match.js +33 -0
- package/dist/surface/tools/act/resolve-target.d.ts +2 -0
- package/dist/surface/tools/act/resolve-target.js +35 -8
- package/dist/surface/tools/act/sequence-grade.d.ts +3 -1
- package/dist/surface/tools/act/sequence-grade.js +8 -1
- package/dist/surface/tools/act-sequence-tool.js +3 -0
- package/dist/surface/tools/act-tools.js +12 -12
- package/dist/surface/tools/assert/assert-verdict.js +28 -1
- package/dist/surface/tools/browser-tools.js +75 -7
- package/dist/surface/tools/dynamic-tools.js +2 -1
- package/dist/surface/tools/empty-session-list.d.ts +2 -0
- package/dist/surface/tools/empty-session-list.js +4 -2
- package/dist/surface/tools/error-recovery.js +43 -22
- package/dist/surface/tools/invoke-tool.js +27 -20
- package/dist/surface/tools/lease-hint.d.ts +7 -0
- package/dist/surface/tools/lease-hint.js +10 -3
- package/dist/surface/tools/lease-readiness.d.ts +21 -0
- package/dist/surface/tools/lease-readiness.js +47 -0
- package/dist/surface/tools/lease-seed.d.ts +2 -0
- package/dist/surface/tools/lease-seed.js +2 -1
- package/dist/surface/tools/lease-tools.js +43 -15
- package/dist/surface/tools/real-input-attempt.js +70 -31
- package/dist/surface/tools/repeat-hint.d.ts +40 -0
- package/dist/surface/tools/repeat-hint.js +171 -0
- package/dist/surface/tools/run-report.d.ts +2 -2
- package/dist/surface/tools/run-report.js +27 -15
- package/dist/surface/tools/tool-kit.d.ts +12 -0
- package/dist/surface/tools/tool-kit.js +7 -0
- package/dist/surface/tools/tools.js +26 -13
- package/dist/telemetry/argument-shape.js +16 -7
- package/dist/telemetry/cli-telemetry.d.ts +10 -1
- package/dist/telemetry/cli-telemetry.js +15 -0
- package/dist/telemetry/init-telemetry.d.ts +2 -1
- package/dist/telemetry/init-telemetry.js +47 -2
- package/dist/telemetry/install-trace.js +23 -4
- package/dist/telemetry/onboarding-funnel.d.ts +19 -2
- package/dist/telemetry/onboarding-funnel.js +25 -3
- package/dist/telemetry/proxy-telemetry.d.ts +2 -1
- package/dist/telemetry/proxy-telemetry.js +2 -2
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +67 -27
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +60 -28
- package/dist/telemetry/tool-refused.d.ts +2 -2
- package/dist/telemetry/tool-refused.js +9 -2
- package/docs/architecture.md +3 -6
- package/docs/cli/doctor.mdx +41 -2
- package/docs/cli/help.mdx +4 -2
- package/docs/cli/license.mdx +4 -5
- package/docs/cli/setup.mdx +13 -2
- package/docs/cli/status.mdx +4 -0
- package/docs/cli.mdx +1 -1
- package/docs/deploy-checks.md +25 -0
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +15 -20
- package/docs/environment.mdx +1 -0
- package/docs/first-drive.md +1 -1
- package/docs/install-manual.mdx +16 -1
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/react.mdx +14 -2
- package/docs/packages/server.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/test.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +1 -0
- package/docs/packages.mdx +1 -1
- package/docs/platform-integration.md +1 -1
- package/docs/skill.md +1 -1
- package/docs/telemetry.md +4 -2
- package/package.json +14 -10
- package/LICENSE-ENTERPRISE +0 -37
- package/dist/features/ee/audit-log.d.ts +0 -14
- package/dist/features/ee/audit-log.js +0 -11
|
@@ -86,8 +86,9 @@ async function openConnection(ports) {
|
|
|
86
86
|
/** Orchestration: boot → wait for a session → replay flows → verdict → exit code. Browser-free. */
|
|
87
87
|
export async function runVerify(args, outer) {
|
|
88
88
|
const conn = await openConnection(outer);
|
|
89
|
-
if (conn === undefined)
|
|
89
|
+
if (conn === undefined) {
|
|
90
90
|
return;
|
|
91
|
+
}
|
|
91
92
|
/*
|
|
92
93
|
* The exit is held until the connection has closed. The live exit is process.exit, and close()
|
|
93
94
|
* runs the last cloud sync cycle that sends this run's flows and screenshots: exiting first skipped
|
|
@@ -132,8 +133,9 @@ export async function runVerify(args, outer) {
|
|
|
132
133
|
*/
|
|
133
134
|
if (0 === names.length && (true === args.explore || args.persona !== undefined)) {
|
|
134
135
|
const explored = await explore(conn, args, ports);
|
|
135
|
-
if (!explored)
|
|
136
|
+
if (!explored) {
|
|
136
137
|
return;
|
|
138
|
+
}
|
|
137
139
|
names = await conn.listFlows();
|
|
138
140
|
}
|
|
139
141
|
if (0 === names.length) {
|
|
@@ -192,8 +194,9 @@ export async function runVerify(args, outer) {
|
|
|
192
194
|
finally {
|
|
193
195
|
await conn.close().catch(() => undefined);
|
|
194
196
|
// In the finally, so the early refusals that `return` from the try exit too.
|
|
195
|
-
if (exitCode !== undefined)
|
|
197
|
+
if (exitCode !== undefined) {
|
|
196
198
|
outer.exit(exitCode);
|
|
199
|
+
}
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
202
|
/**
|
|
@@ -269,8 +272,9 @@ function delay(ms) {
|
|
|
269
272
|
async function waitForSession(sessions, timeoutMs, now) {
|
|
270
273
|
const deadline = now() + timeoutMs;
|
|
271
274
|
while (now() < deadline) {
|
|
272
|
-
if (sessions.count() > 0)
|
|
275
|
+
if (sessions.count() > 0) {
|
|
273
276
|
return true;
|
|
277
|
+
}
|
|
274
278
|
await delay(SESSION_POLL_MS);
|
|
275
279
|
}
|
|
276
280
|
return sessions.count() > 0;
|
|
@@ -298,8 +302,9 @@ export function buildVerifyDeps(running, reticleRoot, now) {
|
|
|
298
302
|
// RETICLE_API_KEY was told to run `reticle connect` when asking the Harness to drive.
|
|
299
303
|
linkedCloud: linkedCloudPort(fs, reticleRoot, homedir(), process.env),
|
|
300
304
|
};
|
|
301
|
-
if (running.realInput !== undefined)
|
|
305
|
+
if (running.realInput !== undefined) {
|
|
302
306
|
deps.realInput = running.realInput;
|
|
307
|
+
}
|
|
303
308
|
return deps;
|
|
304
309
|
}
|
|
305
310
|
/** Split a drive URL into its origin + whether it's loopback — decides token/injection pairing. */
|
|
@@ -501,8 +506,9 @@ export function routeVerify(args) {
|
|
|
501
506
|
? { route: VerifyRoute.ADHOC }
|
|
502
507
|
: { route: VerifyRoute.REFUSE, message: expectNeedsDaemonMessage(args.port, args.presence) };
|
|
503
508
|
}
|
|
504
|
-
if (args.presence !== PortPresence.DAEMON)
|
|
509
|
+
if (args.presence !== PortPresence.DAEMON) {
|
|
505
510
|
return { route: VerifyRoute.FLOWS };
|
|
511
|
+
}
|
|
506
512
|
if (true === args.explore && true !== args.wantsOwnBrowser && true !== args.hasSavedFlows) {
|
|
507
513
|
return { route: VerifyRoute.ADHOC_EXPLORE };
|
|
508
514
|
}
|
|
@@ -512,7 +518,12 @@ export function routeVerify(args) {
|
|
|
512
518
|
? { route: VerifyRoute.REFUSE, message: portBusyMessage(args.port) }
|
|
513
519
|
: { route: VerifyRoute.ADHOC_SUITE };
|
|
514
520
|
}
|
|
515
|
-
export function handleVerify(parsed
|
|
521
|
+
export function handleVerify(parsed,
|
|
522
|
+
/**
|
|
523
|
+
* Called once, after the last verdict line and before the exit. `cli.ts` passes the CI line:
|
|
524
|
+
* `telemetry` already reaches this directory, so this directory does not reach back.
|
|
525
|
+
*/
|
|
526
|
+
beforeExit = () => undefined) {
|
|
516
527
|
const now = () => Date.now();
|
|
517
528
|
/*
|
|
518
529
|
* `--expect-file` is `--expect` with the shell taken out of the loop.
|
|
@@ -559,7 +570,10 @@ export function handleVerify(parsed) {
|
|
|
559
570
|
}),
|
|
560
571
|
out: (line) => process.stdout.write(`${line}\n`),
|
|
561
572
|
fail: (line) => process.stderr.write(`${line}\n`),
|
|
562
|
-
exit: (code) =>
|
|
573
|
+
exit: (code) => {
|
|
574
|
+
beforeExit();
|
|
575
|
+
process.exit(code);
|
|
576
|
+
},
|
|
563
577
|
cloud: linkedRunsCloudPort(createNodeFileSystem(), reticleRoot, homedir(), process.env),
|
|
564
578
|
dashboardUrl: () => readDashboardUrl(reticleRoot),
|
|
565
579
|
writeResults: (path, text) => writeFile(path, text, 'utf8'),
|
|
@@ -569,8 +583,9 @@ export function handleVerify(parsed) {
|
|
|
569
583
|
// this used to reach the user as a raw node stack rather than as an answer.
|
|
570
584
|
void (async () => {
|
|
571
585
|
const keyNote = (await resolveProjectCloud(createNodeFileSystem(), reticleRoot, homedir(), process.env)).note;
|
|
572
|
-
if (keyNote !== undefined)
|
|
586
|
+
if (keyNote !== undefined) {
|
|
573
587
|
process.stderr.write(`${keyNote}\n`);
|
|
588
|
+
}
|
|
574
589
|
const port = parsed.port ?? RETICLE_DEFAULT_PORT;
|
|
575
590
|
// A lease opened for this verdict writes its runs into this folder's project, not `unmatched/`.
|
|
576
591
|
const root = callerArtifactRoot(process.cwd());
|
|
@@ -613,8 +628,9 @@ export function handleVerify(parsed) {
|
|
|
613
628
|
...(parsed.sessionId === undefined ? {} : { sessionId: parsed.sessionId }),
|
|
614
629
|
...((t) => (t === undefined || 0 === t.length ? {} : { token: t }))(readOrCreatePairingTokenSync(defaultPairingTokenDir())),
|
|
615
630
|
});
|
|
616
|
-
for (const line of verdict.lines)
|
|
631
|
+
for (const line of verdict.lines) {
|
|
617
632
|
ports.out(line);
|
|
633
|
+
}
|
|
618
634
|
ports.exit(verdict.code);
|
|
619
635
|
return;
|
|
620
636
|
}
|
|
@@ -631,8 +647,9 @@ export function handleVerify(parsed) {
|
|
|
631
647
|
...(parsed.sessionId === undefined ? {} : { sessionId: parsed.sessionId }),
|
|
632
648
|
...((t) => (t === undefined || 0 === t.length ? {} : { token: t }))(readOrCreatePairingTokenSync(defaultPairingTokenDir())),
|
|
633
649
|
});
|
|
634
|
-
for (const line of suite.lines)
|
|
650
|
+
for (const line of suite.lines) {
|
|
635
651
|
ports.out(line);
|
|
652
|
+
}
|
|
636
653
|
ports.exit(suite.code);
|
|
637
654
|
return;
|
|
638
655
|
}
|
|
@@ -647,15 +664,17 @@ export function handleVerify(parsed) {
|
|
|
647
664
|
...callerRoot,
|
|
648
665
|
...((t) => (t === undefined || 0 === t.length ? {} : { token: t }))(readOrCreatePairingTokenSync(defaultPairingTokenDir())),
|
|
649
666
|
});
|
|
650
|
-
for (const line of explored.lines)
|
|
667
|
+
for (const line of explored.lines) {
|
|
651
668
|
ports.out(line);
|
|
669
|
+
}
|
|
652
670
|
// The drive runs INSIDE the daemon, so the key has to be in ITS environment. A reader who
|
|
653
671
|
// exported it in this shell and was told to link has been told to do the thing they just
|
|
654
672
|
// did; say where the key is actually missing.
|
|
655
673
|
if (0 !== explored.code && harnessAvailable(process.env)) {
|
|
656
674
|
const refusedForKey = explored.lines.some((line) => line.includes(MSG_NO_HARNESS_KEY));
|
|
657
|
-
if (refusedForKey)
|
|
675
|
+
if (refusedForKey) {
|
|
658
676
|
ports.fail(daemonLacksKeyMessage(port));
|
|
677
|
+
}
|
|
659
678
|
}
|
|
660
679
|
ports.exit(explored.code);
|
|
661
680
|
return;
|
|
@@ -48,6 +48,11 @@ export interface ChromiumProbe {
|
|
|
48
48
|
* `Microsoft Edge`) — see launch-chromium. Present only when `exists` is false and one was found.
|
|
49
49
|
*/
|
|
50
50
|
fallback?: string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Where Reticle looked for an installed Chrome or Edge when neither Playwright's build nor a
|
|
53
|
+
* fallback browser was found.
|
|
54
|
+
*/
|
|
55
|
+
fallbackPaths?: readonly string[] | undefined;
|
|
51
56
|
/** `executablePath` came from RETICLE_CHROMIUM_PATH, so the bundled build is not in play at all. */
|
|
52
57
|
configured?: boolean | undefined;
|
|
53
58
|
/** Why that configured executable cannot be launched. Present only when `exists` is false. */
|
|
@@ -118,7 +123,7 @@ export declare function parseChromiumRevision(executablePath: string): {
|
|
|
118
123
|
* `executablePath()` resolves the revision the daemon will actually launch AND the root it will look
|
|
119
124
|
* in, which is why the probe never reads `PLAYWRIGHT_BROWSERS_PATH` itself.
|
|
120
125
|
*/
|
|
121
|
-
export declare function probeChromium(): Promise<ChromiumProbe>;
|
|
126
|
+
export declare function probeChromium(exists?: (path: string) => boolean): Promise<ChromiumProbe>;
|
|
122
127
|
/** The bundled playwright's version, or undefined if it cannot be read (the hint then goes unpinned). */
|
|
123
128
|
export declare function bundledPlaywrightVersion(): string | undefined;
|
|
124
129
|
/**
|
|
@@ -173,7 +173,7 @@ function installedRevisions(root) {
|
|
|
173
173
|
* `executablePath()` resolves the revision the daemon will actually launch AND the root it will look
|
|
174
174
|
* in, which is why the probe never reads `PLAYWRIGHT_BROWSERS_PATH` itself.
|
|
175
175
|
*/
|
|
176
|
-
export async function probeChromium() {
|
|
176
|
+
export async function probeChromium(exists = existsSync) {
|
|
177
177
|
const searched = playwrightResolutionRoots();
|
|
178
178
|
try {
|
|
179
179
|
const { chromium } = await import('playwright');
|
|
@@ -181,14 +181,14 @@ export async function probeChromium() {
|
|
|
181
181
|
const parsed = parseChromiumRevision(executablePath);
|
|
182
182
|
return {
|
|
183
183
|
executablePath,
|
|
184
|
-
exists:
|
|
184
|
+
exists: exists(executablePath),
|
|
185
185
|
playwrightVersion: bundledPlaywrightVersion(),
|
|
186
186
|
...(null === parsed
|
|
187
187
|
? {}
|
|
188
188
|
: {
|
|
189
189
|
wantedRevision: parsed.revision,
|
|
190
190
|
browsersRoot: parsed.root,
|
|
191
|
-
installedRevisions: installedRevisions(parsed.root),
|
|
191
|
+
installedRevisions: exists === existsSync ? installedRevisions(parsed.root) : [],
|
|
192
192
|
}),
|
|
193
193
|
};
|
|
194
194
|
}
|
|
@@ -248,6 +248,9 @@ export function bundledPlaywrightVersion() {
|
|
|
248
248
|
export function chromiumPreflightRefusal(probe) {
|
|
249
249
|
if (probe.exists)
|
|
250
250
|
return undefined;
|
|
251
|
+
if (undefined !== probe.fallback) {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
251
254
|
if (true === probe.configured && probe.executablePath !== undefined) {
|
|
252
255
|
return configuredChromiumRefusal(probe.executablePath, probe.problem ?? ChromiumPathProblem.MISSING);
|
|
253
256
|
}
|
|
@@ -281,16 +284,19 @@ export function chromiumHint(probe) {
|
|
|
281
284
|
const where = 0 === searched.length ? '' : ` — looked in ${searched.join(', ')}`;
|
|
282
285
|
return `✗ the playwright package is not installed${where}; run: ${command}`;
|
|
283
286
|
}
|
|
287
|
+
const fallbackPaths = probe.fallbackPaths ?? [];
|
|
284
288
|
const present = probe.installedRevisions ?? [];
|
|
285
289
|
if (present.length > 0 && probe.wantedRevision !== undefined) {
|
|
286
290
|
// The reported machine. Every fact here is one the reader could not otherwise get: what is
|
|
287
291
|
// wanted, what is there, and where "there" is. `npx playwright install chromium` unpinned adds
|
|
288
292
|
// a build to this list and changes nothing, which is what happened.
|
|
293
|
+
const fallbackWhere = 0 === fallbackPaths.length ? '' : `; also looked for ${fallbackPaths.join(', ')}`;
|
|
289
294
|
return (`✗ wrong revision — the bundled playwright wants ${probe.wantedRevision}; ` +
|
|
290
|
-
`${probe.browsersRoot ?? ''} holds ${present.join(', ')}. run: ${command}`);
|
|
295
|
+
`${probe.browsersRoot ?? ''} holds ${present.join(', ')}${fallbackWhere}. run: ${command}`);
|
|
291
296
|
}
|
|
292
297
|
// Naming the path is what turns "missing" from a verdict into evidence: a reader with browsers on
|
|
293
298
|
// disk can see immediately whether this is a missing install or a lookup pointed somewhere else
|
|
294
299
|
// (PLAYWRIGHT_BROWSERS_PATH, a different browsers root, a revision bump).
|
|
295
|
-
|
|
300
|
+
const lookedFor = [probe.executablePath, ...fallbackPaths].join(', ');
|
|
301
|
+
return `✗ missing — looked for ${lookedFor}; run: ${command}`;
|
|
296
302
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `reticle doctor --fix`: repair a half-done install, then say what still needs a person.
|
|
3
|
+
*
|
|
4
|
+
* A repair changes only Reticle's own state: its pid file, its own stuck daemon, its own Chromium,
|
|
5
|
+
* and its entry in an agent's MCP config. A row that needs a change to the user's app, a process
|
|
6
|
+
* that Reticle cannot prove is its daemon, or an action only the user can do, is never changed
|
|
7
|
+
* here. Its row already names the step, and this prints that step.
|
|
8
|
+
*
|
|
9
|
+
* Each repair checks again before it acts, so a second run finds nothing to do and changes nothing.
|
|
10
|
+
*/
|
|
11
|
+
import { DoctorRow } from './doctor-rows.js';
|
|
12
|
+
type DoctorRowLabel = (typeof DoctorRow)[keyof typeof DoctorRow];
|
|
13
|
+
/** One safe repair. `needed` reads the machine; `apply` changes only Reticle's own state. */
|
|
14
|
+
export interface DoctorRepair {
|
|
15
|
+
/** The doctor row this repair makes green. */
|
|
16
|
+
readonly row: DoctorRowLabel;
|
|
17
|
+
/** What the repair is about, in a few words, so two repairs on one row read apart. */
|
|
18
|
+
readonly what: string;
|
|
19
|
+
/** The manual step, printed when the repair is not possible or does not take. */
|
|
20
|
+
readonly step: string;
|
|
21
|
+
needed(): Promise<boolean>;
|
|
22
|
+
apply(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/** What `--fix` says about each repair. */
|
|
25
|
+
export declare const FixOutcome: {
|
|
26
|
+
readonly FIXED: "fixed";
|
|
27
|
+
readonly NOTHING: "nothing to do";
|
|
28
|
+
readonly STILL_NEEDS: "still needs";
|
|
29
|
+
};
|
|
30
|
+
/** The last line of a plain `doctor` run, when `--fix` can repair `count` rows. */
|
|
31
|
+
export declare function fixFooter(count: number): string | undefined;
|
|
32
|
+
/** How many repairs have work to do now. */
|
|
33
|
+
export declare function countNeeded(repairs: readonly DoctorRepair[]): Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* Run doctor, apply the needed repairs in the given order, run doctor again, and print one line for
|
|
36
|
+
* each repair and one for each row that still fails. True when no row fails after the repairs.
|
|
37
|
+
*/
|
|
38
|
+
export declare function runDoctorFix(deps: {
|
|
39
|
+
/** Runs doctor and returns its rows. Prints them only when `show` is true. */
|
|
40
|
+
doctor: (show: boolean) => Promise<string[]>;
|
|
41
|
+
repairs: readonly DoctorRepair[];
|
|
42
|
+
print: (line: string) => void;
|
|
43
|
+
}): Promise<boolean>;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `reticle doctor --fix`: repair a half-done install, then say what still needs a person.
|
|
3
|
+
*
|
|
4
|
+
* A repair changes only Reticle's own state: its pid file, its own stuck daemon, its own Chromium,
|
|
5
|
+
* and its entry in an agent's MCP config. A row that needs a change to the user's app, a process
|
|
6
|
+
* that Reticle cannot prove is its daemon, or an action only the user can do, is never changed
|
|
7
|
+
* here. Its row already names the step, and this prints that step.
|
|
8
|
+
*
|
|
9
|
+
* Each repair checks again before it acts, so a second run finds nothing to do and changes nothing.
|
|
10
|
+
*/
|
|
11
|
+
import { DoctorRow, doctorRow, LABEL_COLUMN } from './doctor-rows.js';
|
|
12
|
+
/** What `--fix` says about each repair. */
|
|
13
|
+
export const FixOutcome = {
|
|
14
|
+
FIXED: 'fixed',
|
|
15
|
+
NOTHING: 'nothing to do',
|
|
16
|
+
STILL_NEEDS: 'still needs',
|
|
17
|
+
};
|
|
18
|
+
/** The mark doctor puts on a row that fails. */
|
|
19
|
+
const FAIL_MARK = '✗';
|
|
20
|
+
const FIX_HEADING = 'reticle doctor --fix';
|
|
21
|
+
/** The last line of a plain `doctor` run, when `--fix` can repair `count` rows. */
|
|
22
|
+
export function fixFooter(count) {
|
|
23
|
+
if (0 === count) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return `Run \`reticle doctor --fix\` to repair ${String(count)} of these.`;
|
|
27
|
+
}
|
|
28
|
+
/** How many repairs have work to do now. */
|
|
29
|
+
export async function countNeeded(repairs) {
|
|
30
|
+
let count = 0;
|
|
31
|
+
for (const repair of repairs) {
|
|
32
|
+
if (await repair.needed()) {
|
|
33
|
+
count++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return count;
|
|
37
|
+
}
|
|
38
|
+
/** The row label at the head of a doctor line, or undefined for a line that is not a row. */
|
|
39
|
+
function rowLabelOf(line) {
|
|
40
|
+
const label = line.slice(0, LABEL_COLUMN + 2).trim();
|
|
41
|
+
return 0 === label.length ? undefined : label;
|
|
42
|
+
}
|
|
43
|
+
async function applyOne(repair) {
|
|
44
|
+
if (!(await repair.needed())) {
|
|
45
|
+
return FixOutcome.NOTHING;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
await repair.apply();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// The check below decides. A repair that threw and a repair that did nothing say the same thing.
|
|
52
|
+
}
|
|
53
|
+
if (await repair.needed()) {
|
|
54
|
+
return `${FixOutcome.STILL_NEEDS}: ${repair.step}`;
|
|
55
|
+
}
|
|
56
|
+
return FixOutcome.FIXED;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run doctor, apply the needed repairs in the given order, run doctor again, and print one line for
|
|
60
|
+
* each repair and one for each row that still fails. True when no row fails after the repairs.
|
|
61
|
+
*/
|
|
62
|
+
export async function runDoctorFix(deps) {
|
|
63
|
+
await deps.doctor(true);
|
|
64
|
+
const results = [];
|
|
65
|
+
const stillFailing = new Set();
|
|
66
|
+
for (const repair of deps.repairs) {
|
|
67
|
+
const outcome = await applyOne(repair);
|
|
68
|
+
if (outcome.startsWith(FixOutcome.STILL_NEEDS)) {
|
|
69
|
+
stillFailing.add(repair.row);
|
|
70
|
+
}
|
|
71
|
+
results.push(doctorRow(repair.row, `${repair.what}: ${outcome}`));
|
|
72
|
+
}
|
|
73
|
+
const after = await deps.doctor(false);
|
|
74
|
+
const failures = after.filter((line) => line.includes(FAIL_MARK));
|
|
75
|
+
deps.print('');
|
|
76
|
+
deps.print(FIX_HEADING);
|
|
77
|
+
for (const line of results) {
|
|
78
|
+
deps.print(line);
|
|
79
|
+
}
|
|
80
|
+
for (const line of failures) {
|
|
81
|
+
// A repair that did not take already printed its step for this row.
|
|
82
|
+
const label = rowLabelOf(line);
|
|
83
|
+
if (label !== undefined && stillFailing.has(label)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
deps.print(` ${FixOutcome.STILL_NEEDS}: ${line.trim()}`);
|
|
87
|
+
}
|
|
88
|
+
return 0 === failures.length;
|
|
89
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** One agent's `reticle` entry, as its config holds it. */
|
|
2
|
+
export interface RegisteredEntry {
|
|
3
|
+
readonly client: string;
|
|
4
|
+
readonly entry: unknown;
|
|
5
|
+
}
|
|
6
|
+
interface ConfigIo {
|
|
7
|
+
readFile(path: string): string | null;
|
|
8
|
+
exists(path: string): boolean;
|
|
9
|
+
homeDir(): string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The `reticle` entry of each agent config here: Claude Code's user, local and project scopes, and
|
|
13
|
+
* every file-backed client that `init` registers with.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registeredEntries(io: ConfigIo, cwd: string): RegisteredEntry[];
|
|
16
|
+
/**
|
|
17
|
+
* The row text, or undefined when every registered launcher exists and no entry is stale.
|
|
18
|
+
*
|
|
19
|
+
* @param floatingNote `claudeStaleEntryNote`, when Claude Code's user-scope entry is the floating
|
|
20
|
+
* npx form. `setup mcp` keeps that entry, so the row names the remove-then-add step.
|
|
21
|
+
*/
|
|
22
|
+
export declare function launcherLine(entries: readonly RegisteredEntry[], exists: (path: string) => boolean, floatingNote?: string): string | undefined;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `launcher` row of `reticle doctor`: an agent config starts Reticle from an installed copy
|
|
3
|
+
* (`node <path> mcp`), and that file is gone.
|
|
4
|
+
*
|
|
5
|
+
* `init` registers the installed bin when there is one, so a sandbox boot does not download the
|
|
6
|
+
* package again. When the package is removed later, the agent shows the server as failed and says
|
|
7
|
+
* no more. This row names the path and the command that writes a working entry. It prints only
|
|
8
|
+
* when a launcher is missing, like `version`.
|
|
9
|
+
*/
|
|
10
|
+
import { McpClient, RETICLE_NPM_PACKAGE, claudeReticleEntries, clientSpec, detectMcpClients, isReticleLauncher, } from '@reticlehq/init';
|
|
11
|
+
import { MCP_SERVER_NAME } from '@reticlehq/core';
|
|
12
|
+
const SETUP_MCP = `npx ${RETICLE_NPM_PACKAGE} setup mcp`;
|
|
13
|
+
/** `setup mcp` keeps an existing Claude Code entry, so that entry goes first. */
|
|
14
|
+
const CLAUDE_REMOVE = `claude mcp remove ${MCP_SERVER_NAME} -s user`;
|
|
15
|
+
const record = (value) => 'object' === typeof value && null !== value ? value : {};
|
|
16
|
+
/** Every string in `command` and `args`, in both the `{ command, args }` and the array shape. */
|
|
17
|
+
function strings(entry) {
|
|
18
|
+
const { command, args } = record(entry);
|
|
19
|
+
return [command, args].flat().filter((v) => 'string' === typeof v);
|
|
20
|
+
}
|
|
21
|
+
function parsed(text) {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(text ?? '');
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The `reticle` entry of each agent config here: Claude Code's user, local and project scopes, and
|
|
31
|
+
* every file-backed client that `init` registers with.
|
|
32
|
+
*/
|
|
33
|
+
export function registeredEntries(io, cwd) {
|
|
34
|
+
const claude = claudeReticleEntries(io, cwd).map((entry) => ({
|
|
35
|
+
client: McpClient.CLAUDE_CODE,
|
|
36
|
+
entry,
|
|
37
|
+
}));
|
|
38
|
+
const others = detectMcpClients(io).map((c) => ({
|
|
39
|
+
client: c.id,
|
|
40
|
+
entry: record(record(parsed(c.existing))[clientSpec(c.id).serversKey])[MCP_SERVER_NAME],
|
|
41
|
+
}));
|
|
42
|
+
return [...claude, ...others].filter((e) => e.entry !== undefined);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The row text, or undefined when every registered launcher exists and no entry is stale.
|
|
46
|
+
*
|
|
47
|
+
* @param floatingNote `claudeStaleEntryNote`, when Claude Code's user-scope entry is the floating
|
|
48
|
+
* npx form. `setup mcp` keeps that entry, so the row names the remove-then-add step.
|
|
49
|
+
*/
|
|
50
|
+
export function launcherLine(entries, exists, floatingNote) {
|
|
51
|
+
for (const { client, entry } of entries) {
|
|
52
|
+
const missing = strings(entry).find((s) => isReticleLauncher(s) && !exists(s));
|
|
53
|
+
if (missing === undefined) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const fix = McpClient.CLAUDE_CODE === client ? `${CLAUDE_REMOVE}, then ${SETUP_MCP}` : SETUP_MCP;
|
|
57
|
+
return `✗ ${client} starts Reticle from ${missing}, and that file does not exist. Run: ${fix}`;
|
|
58
|
+
}
|
|
59
|
+
return floatingNote === undefined ? undefined : `✗ ${McpClient.CLAUDE_CODE}: ${floatingNote}`;
|
|
60
|
+
}
|
|
@@ -40,6 +40,8 @@ export declare const DoctorRow: {
|
|
|
40
40
|
* verification." Every row in this list checks a COMPONENT; none checked a LINK.
|
|
41
41
|
*/
|
|
42
42
|
readonly AGENT_LINK: "agent link";
|
|
43
|
+
/** An agent config starts Reticle from an installed copy that is gone. Only then. */
|
|
44
|
+
readonly LAUNCHER: "launcher";
|
|
43
45
|
/** Is the APP in this folder wired, not just the tools. Printed by `doctor-project-line.ts`. */
|
|
44
46
|
readonly PROJECT: "project";
|
|
45
47
|
/**
|
|
@@ -68,6 +70,8 @@ export declare const DoctorRow: {
|
|
|
68
70
|
readonly PROMPTS: "prompts";
|
|
69
71
|
/** The daemon's background sync for this project: on or off, why, and the last push. */
|
|
70
72
|
readonly SYNC: "sync";
|
|
73
|
+
/** Which coding agents found on this machine have Reticle's MCP server registered. */
|
|
74
|
+
readonly MCP: "mcp";
|
|
71
75
|
};
|
|
72
76
|
type DoctorRowLabel = (typeof DoctorRow)[keyof typeof DoctorRow];
|
|
73
77
|
export declare const DOCTOR_ROW_LABELS: readonly DoctorRowLabel[];
|
|
@@ -40,6 +40,8 @@ export const DoctorRow = {
|
|
|
40
40
|
* verification." Every row in this list checks a COMPONENT; none checked a LINK.
|
|
41
41
|
*/
|
|
42
42
|
AGENT_LINK: 'agent link',
|
|
43
|
+
/** An agent config starts Reticle from an installed copy that is gone. Only then. */
|
|
44
|
+
LAUNCHER: 'launcher',
|
|
43
45
|
/** Is the APP in this folder wired, not just the tools. Printed by `doctor-project-line.ts`. */
|
|
44
46
|
PROJECT: 'project',
|
|
45
47
|
/**
|
|
@@ -68,6 +70,8 @@ export const DoctorRow = {
|
|
|
68
70
|
PROMPTS: 'prompts',
|
|
69
71
|
/** The daemon's background sync for this project: on or off, why, and the last push. */
|
|
70
72
|
SYNC: 'sync',
|
|
73
|
+
/** Which coding agents found on this machine have Reticle's MCP server registered. */
|
|
74
|
+
MCP: 'mcp',
|
|
71
75
|
};
|
|
72
76
|
export const DOCTOR_ROW_LABELS = Object.values(DoctorRow);
|
|
73
77
|
/** Indent every row carries, before the label. */
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { PortPresence } from '../daemon/binding/port-presence.js';
|
|
2
|
+
import { type StepReporter } from './setup-mcp-cli.js';
|
|
3
|
+
import { type PortHolder } from './ports/port-holder.js';
|
|
4
|
+
import { type ChromiumProbe } from './doctor/browser/chromium-hint.js';
|
|
5
|
+
import type { DoctorRepair } from './doctor/doctor-fix.js';
|
|
6
|
+
/** A pid file whose process is gone. `removePid` itself never removes the file of a live process. */
|
|
7
|
+
export declare function stalePidRepair(deps: {
|
|
8
|
+
readPid: () => number | null;
|
|
9
|
+
isAlive: (pid: number) => boolean;
|
|
10
|
+
removePid: () => void;
|
|
11
|
+
}): DoctorRepair;
|
|
12
|
+
/**
|
|
13
|
+
* A Reticle daemon that holds the port and does not answer. Only a holder whose command line is a
|
|
14
|
+
* Reticle daemon's qualifies; `stopWedgedDaemon` checks that again, after a grace period, before it
|
|
15
|
+
* sends a signal. A stranger and the agent's own `reticle mcp` proxy are never stopped.
|
|
16
|
+
*/
|
|
17
|
+
export declare function wedgedDaemonRepair(port: number, deps: {
|
|
18
|
+
presence: () => Promise<PortPresence>;
|
|
19
|
+
holder: () => PortHolder | null;
|
|
20
|
+
stop: () => Promise<boolean>;
|
|
21
|
+
}): DoctorRepair;
|
|
22
|
+
/**
|
|
23
|
+
* No browser that a launch can use. Only when Playwright's own build is missing or at the wrong
|
|
24
|
+
* revision and no installed Chrome or Edge stands in. A RETICLE_CHROMIUM_PATH the user set is never
|
|
25
|
+
* replaced, and an absent playwright package is not a download this can fix.
|
|
26
|
+
*/
|
|
27
|
+
export declare function chromiumRepair(deps: {
|
|
28
|
+
probe: () => Promise<ChromiumProbe>;
|
|
29
|
+
install: () => Promise<unknown>;
|
|
30
|
+
/** The bundled playwright's version, which pins the manual install command. */
|
|
31
|
+
playwrightVersion: string | undefined;
|
|
32
|
+
}): DoctorRepair;
|
|
33
|
+
/** Agents found on this machine that do not have Reticle's MCP server registered. */
|
|
34
|
+
export declare function mcpRepair(deps: {
|
|
35
|
+
gaps: () => readonly string[];
|
|
36
|
+
register: () => void;
|
|
37
|
+
}): DoctorRepair;
|
|
38
|
+
/**
|
|
39
|
+
* An agent config starts Reticle from an installed copy that no longer exists. `setup mcp`
|
|
40
|
+
* replaces Reticle's own entry in the JSON clients. It keeps an existing Claude Code entry, so for
|
|
41
|
+
* Claude Code the row still fails after the repair, and the step is the row's remove-then-add.
|
|
42
|
+
*/
|
|
43
|
+
export declare function launcherRepair(deps: {
|
|
44
|
+
/** The launcher row, or undefined when every registered launcher exists. */
|
|
45
|
+
line: () => string | undefined;
|
|
46
|
+
register: () => void;
|
|
47
|
+
}): DoctorRepair;
|
|
48
|
+
/** The launcher row for the agent configs of this machine and folder. */
|
|
49
|
+
export declare function readLauncherLine(cwd: string): string | undefined;
|
|
50
|
+
/** Which agents found here have Reticle, and which do not. */
|
|
51
|
+
export interface McpRegistration {
|
|
52
|
+
readonly missing: readonly string[];
|
|
53
|
+
readonly present: readonly string[];
|
|
54
|
+
}
|
|
55
|
+
/** The `mcp` doctor row. */
|
|
56
|
+
export declare function mcpRegistrationRow(registration: McpRegistration): string;
|
|
57
|
+
/**
|
|
58
|
+
* Ask `setupMcp` what it would do, and do none of it. Config writes and registration commands are
|
|
59
|
+
* recorded, not run; only the two `--version` probes run, because they say whether an agent is here.
|
|
60
|
+
* No telemetry: this is a check, not a registration.
|
|
61
|
+
*/
|
|
62
|
+
export declare function readMcpRegistration(): McpRegistration;
|
|
63
|
+
/** The repairs for this machine and port, in the order `--fix` applies them. */
|
|
64
|
+
export declare function machineRepairs(port: number, reportStep: StepReporter): DoctorRepair[];
|