Keep private-file and uninstall guidance clear through copy lint

Split two existing instructions into plain sentences without changing private-token handling, host-local removal or file ownership requirements. Verify the actual installer and removed-agent guidance and clipboard outputs in desktop Chromium and phone WebKit.

Contract-Neutral: Copy-only sentence changes preserve credential transport, installation and removal contracts.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot] 2026-10-01 19:25:52 +01:00
parent 53e69c06fe
commit 1a0cd7c9d5
5 changed files with 225 additions and 33 deletions

View file

@ -0,0 +1,193 @@
// Scoped copy proof: actual installer/Doctor/styles, synthetic token API only.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { chromium, webkit } = require('playwright');
(async () => {
const root = '/workspace/frontend-modern';
process.chdir(root);
const artifacts = path.join(root, 'node_modules', 'unix-guidance-browser');
fs.mkdirSync(artifacts, { recursive: true });
const { createServer } = await import(path.join(root, 'node_modules/vite/dist/node/index.js'));
const server = await createServer({
root,
configFile: path.join(root, 'vite.config.ts'),
cacheDir: path.join(root, 'node_modules', '.vite-unix-guidance-copy'),
optimizeDeps: {
noDiscovery: true,
include: ['solid-js', 'solid-js/web', '@solidjs/router'],
},
server: { host: '127.0.0.1', port: 5244, strictPort: true },
});
const secret = 'browser-synthetic-install-token';
const results = [];
let browser;
try {
await server.listen();
for (const [engine, width, tone] of [
['chromium', 1280, 'light'],
['webkit', 390, 'dark'],
]) {
browser = await { chromium, webkit }[engine].launch(
engine === 'chromium'
? { headless: true, channel: 'chromium', args: ['--no-sandbox'] }
: { headless: true },
);
const page = await browser.newPage({
viewport: { width, height: 900 },
...(engine === 'webkit' ? { isMobile: true, hasTouch: true } : {}),
});
const errors = [];
page.on('pageerror', (error) => {
errors.push(error.message);
console.log('PAGE_ERROR', error.message);
});
page.on('console', (message) => {
if (message.type() === 'error') {
errors.push(message.text());
console.log('CONSOLE_ERROR', message.text());
}
});
await page.addInitScript(() => {
window.__copies = [];
Object.defineProperty(navigator, 'clipboard', {
value: {
writeText: async (text) => window.__copies.push(text),
},
});
});
await page.route('**/api/**', async (route) => {
const pathname = new URL(route.request().url()).pathname;
// Do not intercept the production /src/api/*.ts module requests.
if (!pathname.startsWith('/api/')) return route.continue();
if (pathname === '/api/security/status')
return route.fulfill({
json: {
requiresAuth: true,
hasAuthentication: true,
apiTokenConfigured: true,
},
});
if (pathname === '/api/agent-install-command')
return route.fulfill({
json: {
token: secret,
record: {
id: 'synthetic-record',
name: 'Synthetic installer',
prefix: 'browser',
suffix: 'token',
createdAt: new Date().toISOString(),
},
},
});
if (pathname === '/api/state')
return route.fulfill({ json: { connectedInfrastructure: [] } });
return route.fulfill({ json: { data: [] } });
});
await page.goto('http://127.0.0.1:5244/browser-tests/unix-private-install.html', {
waitUntil: 'domcontentloaded',
timeout: 60_000,
});
await page
.getByRole('heading', { name: 'Private Unix agent installation', exact: true })
.waitFor({ timeout: 60_000 });
if (tone === 'dark')
await page.evaluate(() => document.documentElement.classList.add('dark'));
await page.getByRole('button', { name: 'Generate token', exact: true }).first().click();
const dialog = page.getByRole('dialog', { name: 'API token ready' });
await dialog.waitFor();
await dialog.getByRole('button', { name: 'Dismiss', exact: true }).click();
await dialog.waitFor({ state: 'hidden' });
const privateNote = page
.locator('span')
.filter({
hasText: /^Before running, save the separately revealed token/,
})
.first();
const fileText = await privateNote.innerText();
assert.ok(
fileText.includes(
'This command does not prompt or delete your file. Remove it through the same trusted file path afterwards.',
),
);
for (const warning of [
'0600',
'0700',
'Never put the token in a GUI command field',
'console or SSH instead',
])
assert.ok(fileText.includes(warning));
await page
.getByRole('button', {
name: 'Copy Install from a private token file (no terminal) command',
exact: true,
})
.click();
const fileCommand = await page.evaluate(() => window.__copies.at(-1));
assert.ok(fileCommand.includes('/root/.config/pulse-agent/bootstrap-token'));
assert.ok(fileCommand.includes('--token-file'));
assert.ok(!fileCommand.includes('read -r'));
assert.ok(!fileCommand.includes(secret));
await privateNote.screenshot({ path: path.join(artifacts, `${engine}-private-file.png`) });
await page
.getByRole('button', { name: 'Show details for removed-fixture', exact: true })
.click();
const removalNote = page.locator('p').filter({
hasText:
'This agent was removed from Pulse, but the agent software may still be installed on its host.',
});
const removalText = await removalNote.innerText();
assert.ok(
removalText.includes('token at its silent prompt. Never insert it into the command.'),
);
assert.ok(removalText.includes('Pulse does not run commands remotely'));
await page
.getByRole('button', {
name: 'Copy Linux / macOS / FreeBSD uninstall command for removed-fixture',
exact: true,
})
.click();
const removalCommand = await page.evaluate(() => window.__copies.at(-1));
assert.ok(removalCommand.includes('removed-agent-42'));
assert.ok(removalCommand.includes('--uninstall'));
assert.ok(!removalCommand.includes('--preflight-only'));
assert.ok(!removalCommand.includes(secret));
await removalNote.screenshot({ path: path.join(artifacts, `${engine}-removed-note.png`) });
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1));
await page.screenshot({ path: path.join(artifacts, `${engine}-viewport.png`) });
assert.deepEqual(errors, []);
results.push({
engine,
version: browser.version(),
width,
tone,
fileText,
removalText,
privateFileCopied: true,
scopedRemovalCopied: true,
errors,
});
await browser.close();
browser = undefined;
}
fs.writeFileSync(
path.join(artifacts, 'result.json'),
JSON.stringify(
{
playwright: require('playwright/package.json').version,
results,
},
null,
2,
),
);
console.log(JSON.stringify({ result: 'passed', artifacts, results }));
} finally {
if (browser) await browser.close();
await server.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});

View file

@ -91,7 +91,17 @@ const Doctor = () => {
agentIdentity: { hostname: 'doctor.example', commandsEnabled: false },
},
} as InfrastructureAgentDoctorTarget;
return <InfrastructureAgentDoctorPage targets={[target]} />;
const removedTarget = {
...target,
key: 'agent:removed-fixture',
connectionId: 'agent:removed-fixture',
displayName: 'removed-fixture',
status: 'removed',
needsCredentialRepair: false,
connection: undefined,
diagnostic: { agentId: 'removed-agent-42', hostname: 'removed.example' },
} as InfrastructureAgentDoctorTarget;
return <InfrastructureAgentDoctorPage targets={[target, removedTarget]} />;
};
render(
() => (

View file

@ -1,27 +1,18 @@
{
"version": 1,
"base_sha": "9189a7262e84b201a5af6649caf0f8cff7c6d144",
"verified_at": "2026-10-01T13:06:33.056850Z",
"base_sha": "53e69c06fe24e3c07deaddb5e40418d07bb0b34b",
"verified_at": "2026-10-01T18:25:48.680085Z",
"result": "passed",
"changed_paths": [
"frontend-modern/src/components/Settings/InfrastructureAgentDoctorPage.tsx",
"frontend-modern/src/components/Settings/InfrastructureInstallerSection.tsx",
"frontend-modern/src/components/Settings/infrastructureOperationsModel.tsx",
"frontend-modern/src/components/Settings/useInfrastructureInstallState.tsx",
"frontend-modern/src/components/Settings/useInfrastructureOperationsState.tsx",
"frontend-modern/src/utils/agentInstallCommand.ts"
"frontend-modern/src/components/Settings/infrastructureOperationsModel.tsx"
],
"content_sha256": {
"frontend-modern/src/components/Settings/InfrastructureAgentDoctorPage.tsx": "416c7627303c665f956168a32e5aaf6af4268abacdd7e93045ab26cd45732d14",
"frontend-modern/src/components/Settings/InfrastructureInstallerSection.tsx": "d2ff336c1e602794fd696aed3541684498adfcfeaf1f4e603e21a44865fce3bb",
"frontend-modern/src/components/Settings/infrastructureOperationsModel.tsx": "983a44871cfb908851c215a177e4a39185f7ac9017cc1b2152323b7ee396995a",
"frontend-modern/src/components/Settings/useInfrastructureInstallState.tsx": "c57bf5a99d457d6d21f0d22e0cc34af3618db214391fd09afce34743173a1c0f",
"frontend-modern/src/components/Settings/useInfrastructureOperationsState.tsx": "276c89076d36670752f5d37be1895d739ae13501ea9d87d1cc3a497c7e6f5895",
"frontend-modern/src/utils/agentInstallCommand.ts": "9bb98b1db1b49aa478f894b346479951954899d4c6049d991ec4c75730cafa30"
"frontend-modern/src/components/Settings/InfrastructureAgentDoctorPage.tsx": "f9b44049c02a5e5146d882befeefe8aa5bab221a2c4a0951e529c274a416010c",
"frontend-modern/src/components/Settings/infrastructureOperationsModel.tsx": "1f04fe06cfbb1874c657883e07b4c7ce0992399696b5bed05f3ea029b1f00c0f"
},
"routes": [
"/browser-tests/unix-private-install.html",
"/browser-tests/unix-private-install.html?optional=1"
"/browser-tests/unix-private-install.html"
],
"viewports": [
{
@ -34,29 +25,27 @@
}
],
"states": [
"Actual infrastructure installer, operations closures, Agent Doctor and token dialog with production CSS; synthetic APIs, not installed agent/appliance execution",
"Required-auth Unix command and credential remain separate; single-line command grammar, private file no-terminal variant and separate-token instructions",
"Canonical repair/removal identity and custom CA preserved; saved-state update has no replacement credential; removal has no new-binary preflight",
"Optional-auth commands require no terminal or token-file; default TLS verification and monitoring-only issuance unchanged",
"Desktop Chromium light and phone-emulated WebKit dark; four final screenshots visually inspected"
"Production installer and Agent Doctor with synthetic APIs, required authentication and separately issued token; not native installation or enrollment",
"Private-file note preserves trusted editor/upload, root 0600 file and 0700 directory, no GUI secret and console/SSH fallback; command preserves the user-owned file",
"Removed agent note preserves host-local detachment, silent prompt and no token in copied command; command retains exact removed-agent identity and no new-binary preflight",
"Desktop Chromium light and phone-emulated WebKit dark; four affected-note screenshots inspected, outer page width contained"
],
"interactions": [
"Generate scoped host token, copy token with keyboard, Escape, reopen existing reveal without issuing another token",
"Copy actual Linux installer, no-terminal private-file installer and Agent Doctor repair commands; raw credential absent from each clipboard result",
"Change endpoint and custom CA; copy actual operations-state repair/removal/saved-state update commands; identity/profile/trust checked",
"Reload with optional auth and confirm tokenless commands"
"Generate separate token and dismiss token dialog",
"Copy the private-file installation command; verify fixed private path and token secrecy",
"Expand removed agent and copy its scoped uninstall command; verify token secrecy and no preflight"
],
"command": "pulse-worker-browser frontend-modern/browser-tests/unix-private-install.cjs",
"command": "pulse-worker-browser frontend-modern/browser-tests/unix-guidance-copy.cjs",
"browser_versions": {
"playwright": "1.56.1",
"chromium": "141.0.7390.37",
"webkit": "26.0"
},
"artifacts": [
"/var/lib/pulse-maintainer/worker-outputs/web-product-wielhxw1/browser/chromium-commands.png",
"/var/lib/pulse-maintainer/worker-outputs/web-product-wielhxw1/browser/chromium-token.png",
"/var/lib/pulse-maintainer/worker-outputs/web-product-wielhxw1/browser/result.json",
"/var/lib/pulse-maintainer/worker-outputs/web-product-wielhxw1/browser/webkit-commands.png",
"/var/lib/pulse-maintainer/worker-outputs/web-product-wielhxw1/browser/webkit-token.png"
"/var/lib/pulse-maintainer/worker-outputs/web-product-yqww6hbj/browser/unix/chromium-private-file.png",
"/var/lib/pulse-maintainer/worker-outputs/web-product-yqww6hbj/browser/unix/chromium-removed-note.png",
"/var/lib/pulse-maintainer/worker-outputs/web-product-yqww6hbj/browser/unix/webkit-private-file.png",
"/var/lib/pulse-maintainer/worker-outputs/web-product-yqww6hbj/browser/unix/webkit-removed-note.png",
"/var/lib/pulse-maintainer/worker-outputs/web-product-yqww6hbj/browser/unix/result.json"
]
}

View file

@ -943,7 +943,7 @@ export const InfrastructureAgentDoctorPage: Component<InfrastructureAgentDoctorP
the uninstall command on the affected host itself. Pulse does
not run commands remotely. For Unix commands that require a
token, run the command first, then paste the separately revealed
token at its silent prompt; never insert it into the command.
token at its silent prompt. Never insert it into the command.
</p>
<For each={handoff().commands}>
{(entry) => (

View file

@ -403,7 +403,7 @@ export const buildCommandsByPlatform = (
<code>/root/.config/pulse-agent/bootstrap-token</code> using a trusted file editor
or upload path. The file must be root-owned with mode <code>0600</code> in a
root-owned <code>0700</code> directory. Never put the token in a GUI command
field. This command does not prompt or delete your file; remove it through the
field. This command does not prompt or delete your file. Remove it through the
same trusted file path afterwards. If your GUI cannot create private files, use
console or SSH instead. Bash must already be installed.
</span>