Skip to content

Commit 39f0039

Browse files
committed
Gate WhatsApp responsiveness lint exec probes
1 parent be4147f commit 39f0039

3 files changed

Lines changed: 59 additions & 15 deletions

File tree

src/commands/doctor-whatsapp-responsiveness.test.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,16 @@ describe("doctor WhatsApp responsiveness", () => {
4343
].join("\n"),
4444
});
4545

46-
expect(listLocalTuiProcesses()).toEqual([
47-
{ pid: 101, command: "openclaw-tui" },
48-
{ pid: 104, command: "openclaw tui --local" },
49-
{ pid: 105, command: "/usr/bin/openclaw chat" },
50-
]);
46+
if (process.platform === "win32") {
47+
expect(listLocalTuiProcesses()).toEqual([]);
48+
expect(spawnSyncMock).not.toHaveBeenCalled();
49+
} else {
50+
expect(listLocalTuiProcesses()).toEqual([
51+
{ pid: 101, command: "openclaw-tui" },
52+
{ pid: 104, command: "openclaw tui --local" },
53+
{ pid: 105, command: "/usr/bin/openclaw chat" },
54+
]);
55+
}
5156
});
5257

5358
it("terminates stale local TUI processes with a kill fallback", async () => {

src/flows/doctor-health-contributions.test.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ const mocks = vi.hoisted(() => ({
101101
collectDiskSpaceHealthFindings: vi.fn((): readonly HealthFinding[] => []),
102102
collectHeartbeatTemplateHealthFindings: vi.fn(async () => [] as unknown[]),
103103
maybeRepairHeartbeatTemplate: vi.fn().mockResolvedValue(undefined),
104-
collectWhatsappResponsivenessHealthFindings: vi.fn(() => []),
104+
collectWhatsappResponsivenessHealthFindings: vi.fn((): readonly HealthFinding[] => []),
105105
noteWhatsappResponsivenessHealth: vi.fn().mockResolvedValue(undefined),
106106
collectDevicePairingHealthFindings: vi.fn(async () => []),
107107
scanConfiguredChannelPluginBlockers: vi.fn(
@@ -1755,7 +1755,6 @@ describe("doctor health contributions", () => {
17551755
requirement: "local-tui-event-loop-pressure",
17561756
},
17571757
]);
1758-
mocks.callGateway.mockResolvedValueOnce(status);
17591758

17601759
await expect(
17611760
runDoctorLintChecks(ctx, { checks, onlyIds: ["core/doctor/whatsapp-responsiveness"] }),
@@ -1770,6 +1769,7 @@ describe("doctor health contributions", () => {
17701769
params: { includeChannelSummary: false },
17711770
timeoutMs: 3000,
17721771
config: ctx.cfg,
1772+
deviceIdentity: null,
17731773
});
17741774
expect(mocks.collectWhatsappResponsivenessHealthFindings).toHaveBeenCalledWith({
17751775
cfg: ctx.cfg,
@@ -1799,6 +1799,36 @@ describe("doctor health contributions", () => {
17991799
});
18001800
});
18011801

1802+
it("skips WhatsApp responsiveness Gateway status probes for exec SecretRefs without allow-exec", async () => {
1803+
const contributionChecks = await resolveDoctorContributionHealthChecks();
1804+
const whatsappCheck = contributionChecks.find(
1805+
(check) => check.id === "core/doctor/whatsapp-responsiveness",
1806+
);
1807+
expect(whatsappCheck).toBeDefined();
1808+
mocks.gatewaySecretInputPathCanWin.mockReturnValue(true);
1809+
mocks.readGatewaySecretInputValue.mockReturnValue("exec-token");
1810+
1811+
const ctx = {
1812+
cfg: { channels: { whatsapp: { enabled: true } } },
1813+
mode: "lint",
1814+
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
1815+
} as const;
1816+
const checks = [whatsappCheck!];
1817+
1818+
await expect(
1819+
runDoctorLintChecks(ctx, { checks, onlyIds: ["core/doctor/whatsapp-responsiveness"] }),
1820+
).resolves.toMatchObject({
1821+
checksRun: 1,
1822+
checksSkipped: 0,
1823+
findings: [],
1824+
});
1825+
expect(mocks.callGateway).not.toHaveBeenCalled();
1826+
expect(mocks.collectWhatsappResponsivenessHealthFindings).toHaveBeenCalledWith({
1827+
cfg: ctx.cfg,
1828+
status: undefined,
1829+
});
1830+
});
1831+
18021832
it("keeps device pairing opt-in for default lint selection", async () => {
18031833
const contributionChecks = await resolveDoctorContributionHealthChecks();
18041834
const devicePairingCheck = contributionChecks.find(

src/flows/doctor-health-contributions.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ async function runSystemdLingerHealth(ctx: DoctorHealthFlowContext): Promise<voi
980980
}
981981

982982
async function hasActiveGatewayExecCredential(
983-
ctx: DoctorHealthFlowContext,
983+
ctx: Pick<DoctorHealthFlowContext, "cfg">,
984984
mode: DoctorFlowMode = resolveDoctorMode(ctx.cfg),
985985
): Promise<boolean> {
986986
const { resolveSecretInputRef } = await loadSecretTypesModule();
@@ -1921,15 +1921,24 @@ export function resolveDoctorHealthContributions(): DoctorHealthContribution[] {
19211921
"WhatsApp responsiveness pressure from degraded Gateway and local TUI clients.",
19221922
defaultEnabled: false,
19231923
async detect(ctx) {
1924-
const { callGateway } = await import("../gateway/call.js");
19251924
const { collectWhatsappResponsivenessHealthFindings } =
19261925
await import("../commands/doctor-whatsapp-responsiveness.js");
1927-
const status = await callGateway<import("../commands/status.types.js").StatusSummary>({
1928-
method: "status",
1929-
params: { includeChannelSummary: false },
1930-
timeoutMs: 3000,
1931-
config: ctx.cfg,
1932-
}).catch(() => undefined);
1926+
let status: import("../commands/status.types.js").StatusSummary | undefined;
1927+
if (
1928+
!(
1929+
(await hasActiveGatewayExecCredential({ cfg: ctx.cfg })) &&
1930+
ctx.allowExecSecretRefs !== true
1931+
)
1932+
) {
1933+
const { callGateway } = await import("../gateway/call.js");
1934+
status = await callGateway<import("../commands/status.types.js").StatusSummary>({
1935+
method: "status",
1936+
params: { includeChannelSummary: false },
1937+
timeoutMs: 3000,
1938+
config: ctx.cfg,
1939+
deviceIdentity: null,
1940+
}).catch(() => undefined);
1941+
}
19331942
return collectWhatsappResponsivenessHealthFindings({ cfg: ctx.cfg, status });
19341943
},
19351944
},

0 commit comments

Comments
 (0)