mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-25 04:33:03 +00:00
Discovery panel: show a human-runnable CLI command, not Assistant plumbing
The per-guest Discovery panel dropped the raw cli_access string into a
copyable code row — but cli_access is guidance written for the Assistant
('Use pulse_control with target_host ...'), which is meaningless to a
person and reads as a fake command.
Derive a concrete, human-runnable command from the resource coordinates
(deriveCliCommand): 'pct exec 101 -- bash' for an LXC, 'docker exec <name>
bash' for Docker, and the layered 'pct exec 101 -- docker exec
homeassistant bash' for a service in a nested container. Show that
(copyable) with a 'run from the Proxmox/Docker host' hint. For types with
no clean human command (VM, k8s, agent) fall back to the cli_access text,
now clearly labelled as how the Assistant reaches it. The cli_access field
itself (Assistant context) is unchanged.
Unit-tested (deriveCliCommand); type-check + eslint clean.
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
} from '@/utils/resourceAnalysisPresentation';
|
||||
import { useDiscoveryTabState } from './useDiscoveryTabState';
|
||||
import { orderFactsByActionability } from './factOrdering';
|
||||
import { deriveCliCommand } from './cliCommand';
|
||||
|
||||
interface DiscoveryTabProps {
|
||||
resourceType: ResourceType;
|
||||
@@ -151,6 +152,14 @@ export const DiscoveryTab: Component<DiscoveryTabProps> = (props) => {
|
||||
}
|
||||
return getConfidenceLevel(current.confidence);
|
||||
});
|
||||
// A concrete, human-runnable command for this workload (pct exec / docker
|
||||
// exec, including the nested-container layer). null when there is no clean
|
||||
// human command for the type, in which case we show the Assistant guidance.
|
||||
const cliCommand = createMemo(() => {
|
||||
const current = discovery();
|
||||
if (!current) return null;
|
||||
return deriveCliCommand(props.resourceType, current.resource_id, current.cli_access);
|
||||
});
|
||||
const commandSettingsTarget = getDiscoveryCommandSettingsTarget();
|
||||
const apiAccessSettingsTarget = getDiscoveryApiAccessSettingsTarget();
|
||||
const showManualRunAction = () => props.showManualRunAction === true;
|
||||
@@ -824,12 +833,26 @@ export const DiscoveryTab: Component<DiscoveryTabProps> = (props) => {
|
||||
<span>CLI Access</span>
|
||||
<DiscoveryProvenanceMarker showLabel={false} />
|
||||
</div>
|
||||
<CopyableCodeRow
|
||||
value={d().cli_access}
|
||||
copiedValue={copiedDiscoveryValue}
|
||||
onCopy={handleCopyDiscoveryValue}
|
||||
label="Copy CLI access"
|
||||
/>
|
||||
<Show
|
||||
when={cliCommand()}
|
||||
fallback={
|
||||
<p class="text-xs text-muted">
|
||||
<span class="text-muted">How the Pulse Assistant runs commands here: </span>
|
||||
{d().cli_access}
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<CopyableCodeRow
|
||||
value={cliCommand()!}
|
||||
copiedValue={copiedDiscoveryValue}
|
||||
onCopy={handleCopyDiscoveryValue}
|
||||
label="Copy CLI command"
|
||||
/>
|
||||
<p class="mt-1.5 text-[11px] text-muted">
|
||||
Run from the{' '}
|
||||
{props.resourceType === 'app-container' ? 'Docker host' : 'Proxmox host'}.
|
||||
</p>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { deriveCliCommand } from './cliCommand';
|
||||
|
||||
describe('deriveCliCommand', () => {
|
||||
it('derives pct exec for a native LXC', () => {
|
||||
expect(
|
||||
deriveCliCommand('system-container', '101', 'Use pulse_control ... directly inside the container.'),
|
||||
).toBe('pct exec 101 -- bash');
|
||||
});
|
||||
|
||||
it('layers docker exec for a service in a nested container (HA-in-LXC)', () => {
|
||||
const cliAccess =
|
||||
'Use pulse_control ... The service runs inside a Docker container named "homeassistant" ' +
|
||||
'— prefix commands with: docker exec homeassistant <your-command>.';
|
||||
expect(deriveCliCommand('system-container', '101', cliAccess)).toBe(
|
||||
'pct exec 101 -- docker exec homeassistant bash',
|
||||
);
|
||||
});
|
||||
|
||||
it('uses docker exec for a Docker container', () => {
|
||||
expect(deriveCliCommand('app-container', 'redis', undefined)).toBe('docker exec redis bash');
|
||||
});
|
||||
|
||||
it('returns null for types without a clean human command (VM, k8s, agent)', () => {
|
||||
expect(deriveCliCommand('vm', '200', 'Use pulse_control ... inside the VM.')).toBeNull();
|
||||
expect(deriveCliCommand('pod', 'web-0', 'Use kubectl exec -n default web-0 -- <cmd>')).toBeNull();
|
||||
expect(deriveCliCommand('agent', 'node1', 'Use pulse_control ...')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when the resource id is missing', () => {
|
||||
expect(deriveCliCommand('system-container', '', 'x')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { ResourceType } from '../../types/discovery';
|
||||
|
||||
/**
|
||||
* Derives a concrete, human-runnable command for reaching a workload from its
|
||||
* resource coordinates.
|
||||
*
|
||||
* The discovery `cli_access` field is guidance written for the Pulse Assistant
|
||||
* ("Use pulse_control with target_host …") — it is not something a person types.
|
||||
* This returns what a human would actually run (e.g. `pct exec 101 -- bash`),
|
||||
* including the nested-container layer when the service runs in Docker inside an
|
||||
* LXC/VM. Returns `null` when there is no clean human command for the type (the
|
||||
* caller then falls back to showing the guidance text, e.g. k8s kubectl which is
|
||||
* already human-readable, or VMs where SSH/credentials are the real path).
|
||||
*/
|
||||
export function deriveCliCommand(
|
||||
resourceType: ResourceType,
|
||||
resourceId: string,
|
||||
cliAccess: string | undefined,
|
||||
): string | null {
|
||||
const id = (resourceId || '').trim();
|
||||
if (!id) return null;
|
||||
|
||||
// A service running in a nested Docker container changes the access path; the
|
||||
// backend records the container in cli_access as "docker exec <name> …".
|
||||
const nested = /docker exec (\S+)/.exec(cliAccess || '')?.[1];
|
||||
|
||||
switch (resourceType) {
|
||||
case 'system-container':
|
||||
return nested ? `pct exec ${id} -- docker exec ${nested} bash` : `pct exec ${id} -- bash`;
|
||||
case 'app-container':
|
||||
return `docker exec ${id} bash`;
|
||||
default:
|
||||
// vm (qm guest exec is non-interactive; SSH is the real path), pod (the
|
||||
// guidance already shows a runnable kubectl exec), agent, etc.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user