@sonde/agent 0.2.6 → 0.2.8
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/.turbo/turbo-build.log +6 -4
- package/.turbo/turbo-test.log +65 -23
- package/CHANGELOG.md +20 -0
- package/dist/cli/service.d.ts +12 -0
- package/dist/cli/service.d.ts.map +1 -0
- package/dist/cli/service.js +164 -0
- package/dist/cli/service.js.map +1 -0
- package/dist/cli/service.test.d.ts +2 -0
- package/dist/cli/service.test.d.ts.map +1 -0
- package/dist/cli/service.test.js +99 -0
- package/dist/cli/service.test.js.map +1 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +11 -8
- package/dist/cli/update.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +60 -12
- package/dist/index.js.map +1 -1
- package/dist/tui/installer/InstallerApp.d.ts.map +1 -1
- package/dist/tui/installer/InstallerApp.js +3 -1
- package/dist/tui/installer/InstallerApp.js.map +1 -1
- package/dist/tui/installer/StepService.d.ts +6 -0
- package/dist/tui/installer/StepService.d.ts.map +1 -0
- package/dist/tui/installer/StepService.js +49 -0
- package/dist/tui/installer/StepService.js.map +1 -0
- package/dist/tui/status/PackToggleView.d.ts +15 -0
- package/dist/tui/status/PackToggleView.d.ts.map +1 -0
- package/dist/tui/status/PackToggleView.js +30 -0
- package/dist/tui/status/PackToggleView.js.map +1 -0
- package/dist/tui/status/StatusApp.d.ts +6 -0
- package/dist/tui/status/StatusApp.d.ts.map +1 -0
- package/dist/tui/status/StatusApp.js +98 -0
- package/dist/tui/status/StatusApp.js.map +1 -0
- package/dist/tui/status/StatusInfoView.d.ts +13 -0
- package/dist/tui/status/StatusInfoView.d.ts.map +1 -0
- package/dist/tui/status/StatusInfoView.js +14 -0
- package/dist/tui/status/StatusInfoView.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/service.test.ts +124 -0
- package/src/cli/service.ts +178 -0
- package/src/cli/update.ts +11 -8
- package/src/index.ts +71 -13
- package/src/tui/installer/InstallerApp.tsx +6 -2
- package/src/tui/installer/StepService.tsx +112 -0
- package/src/tui/status/PackToggleView.tsx +69 -0
- package/src/tui/status/StatusApp.tsx +167 -0
- package/src/tui/status/StatusInfoView.tsx +88 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { packRegistry } from '@sonde/packs';
|
|
2
|
+
import { Box, Text, useApp, useInput } from 'ink';
|
|
3
|
+
import { useMemo, useState } from 'react';
|
|
4
|
+
import { getServiceStatus, isServiceInstalled, restartService } from '../../cli/service.js';
|
|
5
|
+
import {
|
|
6
|
+
getConfigPath,
|
|
7
|
+
loadConfig,
|
|
8
|
+
readPidFile,
|
|
9
|
+
saveConfig,
|
|
10
|
+
stopRunningAgent,
|
|
11
|
+
} from '../../config.js';
|
|
12
|
+
import { createSystemChecker, scanForSoftware } from '../../system/scanner.js';
|
|
13
|
+
import { VERSION } from '../../version.js';
|
|
14
|
+
import type { PackRow } from './PackToggleView.js';
|
|
15
|
+
import { PackToggleView } from './PackToggleView.js';
|
|
16
|
+
import { StatusInfoView } from './StatusInfoView.js';
|
|
17
|
+
|
|
18
|
+
type View = 'status' | 'packs';
|
|
19
|
+
|
|
20
|
+
interface StatusAppProps {
|
|
21
|
+
respawnAgent: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function StatusApp({ respawnAgent }: StatusAppProps): JSX.Element {
|
|
25
|
+
const { exit } = useApp();
|
|
26
|
+
const [view, setView] = useState<View>('status');
|
|
27
|
+
const [message, setMessage] = useState<string | undefined>();
|
|
28
|
+
|
|
29
|
+
const initial = useMemo(() => {
|
|
30
|
+
const config = loadConfig();
|
|
31
|
+
const configPath = getConfigPath();
|
|
32
|
+
const serviceInstalled = isServiceInstalled();
|
|
33
|
+
const serviceStatus = serviceInstalled ? getServiceStatus() : 'not-installed';
|
|
34
|
+
const daemonPid = readPidFile();
|
|
35
|
+
|
|
36
|
+
const manifests = [...packRegistry.values()].map((p) => p.manifest);
|
|
37
|
+
const disabledSet = new Set(config?.disabledPacks ?? []);
|
|
38
|
+
const checker = createSystemChecker();
|
|
39
|
+
const scanResults = scanForSoftware(manifests, checker);
|
|
40
|
+
const scanMap = new Map(scanResults.map((r) => [r.packName, r.detected]));
|
|
41
|
+
|
|
42
|
+
const packRows: PackRow[] = manifests.map((m) => ({
|
|
43
|
+
name: m.name,
|
|
44
|
+
description: m.description,
|
|
45
|
+
probeCount: m.probes.length,
|
|
46
|
+
detected: scanMap.get(m.name) ?? false,
|
|
47
|
+
enabled: !disabledSet.has(m.name),
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
const enabledPackNames = packRows.filter((r) => r.enabled).map((r) => r.name);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
config,
|
|
54
|
+
configPath,
|
|
55
|
+
serviceInstalled,
|
|
56
|
+
serviceStatus,
|
|
57
|
+
daemonPid,
|
|
58
|
+
packRows,
|
|
59
|
+
enabledPackNames,
|
|
60
|
+
};
|
|
61
|
+
}, []);
|
|
62
|
+
|
|
63
|
+
const [packRows, setPackRows] = useState(initial.packRows);
|
|
64
|
+
const [enabledPackNames, setEnabledPackNames] = useState(initial.enabledPackNames);
|
|
65
|
+
|
|
66
|
+
useInput((input) => {
|
|
67
|
+
if (view !== 'status') return;
|
|
68
|
+
if (input === 'p' && initial.config) {
|
|
69
|
+
setMessage(undefined);
|
|
70
|
+
setView('packs');
|
|
71
|
+
} else if (input === 'q') {
|
|
72
|
+
exit();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
function handlePackConfirm(enabledNames: string[]): void {
|
|
77
|
+
const config = loadConfig();
|
|
78
|
+
if (!config) return;
|
|
79
|
+
|
|
80
|
+
const allNames = packRows.map((r) => r.name);
|
|
81
|
+
const enabledSet = new Set(enabledNames);
|
|
82
|
+
const disabledPacks = allNames.filter((n) => !enabledSet.has(n));
|
|
83
|
+
|
|
84
|
+
config.disabledPacks = disabledPacks;
|
|
85
|
+
saveConfig(config);
|
|
86
|
+
|
|
87
|
+
let resultMessage: string;
|
|
88
|
+
const svcInstalled = isServiceInstalled();
|
|
89
|
+
const svcStatus = svcInstalled ? getServiceStatus() : '';
|
|
90
|
+
|
|
91
|
+
if (svcInstalled && svcStatus === 'active') {
|
|
92
|
+
const result = restartService();
|
|
93
|
+
resultMessage = result.success
|
|
94
|
+
? 'Packs saved. Service restarted.'
|
|
95
|
+
: `Packs saved. ${result.message}`;
|
|
96
|
+
} else if (readPidFile() !== undefined) {
|
|
97
|
+
stopRunningAgent();
|
|
98
|
+
respawnAgent();
|
|
99
|
+
resultMessage = 'Packs saved. Agent restarted.';
|
|
100
|
+
} else {
|
|
101
|
+
resultMessage = 'Packs saved. Changes take effect on next start.';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const updatedRows = packRows.map((r) => ({
|
|
105
|
+
...r,
|
|
106
|
+
enabled: enabledSet.has(r.name),
|
|
107
|
+
}));
|
|
108
|
+
setPackRows(updatedRows);
|
|
109
|
+
setEnabledPackNames(enabledNames);
|
|
110
|
+
setMessage(resultMessage);
|
|
111
|
+
setView('status');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function handlePackBack(): void {
|
|
115
|
+
setView('status');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const enrolled = initial.config !== undefined;
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Box flexDirection="column" borderStyle="round" borderColor="cyan" paddingX={1}>
|
|
122
|
+
<Box marginBottom={1}>
|
|
123
|
+
<Text bold color="cyan">
|
|
124
|
+
Sonde Agent Status
|
|
125
|
+
</Text>
|
|
126
|
+
</Box>
|
|
127
|
+
|
|
128
|
+
{message && (
|
|
129
|
+
<Box marginBottom={1}>
|
|
130
|
+
<Text color="green">{message}</Text>
|
|
131
|
+
</Box>
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
{view === 'status' && (
|
|
135
|
+
<StatusInfoView
|
|
136
|
+
config={initial.config}
|
|
137
|
+
version={VERSION}
|
|
138
|
+
configPath={initial.configPath}
|
|
139
|
+
serviceInstalled={initial.serviceInstalled}
|
|
140
|
+
serviceStatus={initial.serviceStatus}
|
|
141
|
+
daemonPid={initial.daemonPid}
|
|
142
|
+
enabledPackNames={enabledPackNames}
|
|
143
|
+
/>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
{view === 'packs' && (
|
|
147
|
+
<PackToggleView
|
|
148
|
+
initialRows={packRows}
|
|
149
|
+
onConfirm={handlePackConfirm}
|
|
150
|
+
onBack={handlePackBack}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
<Box marginTop={1}>
|
|
155
|
+
{enrolled && (
|
|
156
|
+
<>
|
|
157
|
+
<Text color={view === 'packs' ? 'cyan' : 'gray'} bold={view === 'packs'}>
|
|
158
|
+
p:packs
|
|
159
|
+
</Text>
|
|
160
|
+
<Text> </Text>
|
|
161
|
+
</>
|
|
162
|
+
)}
|
|
163
|
+
<Text color="gray">q:quit</Text>
|
|
164
|
+
</Box>
|
|
165
|
+
</Box>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Box, Text } from 'ink';
|
|
2
|
+
import type { AgentConfig } from '../../config.js';
|
|
3
|
+
|
|
4
|
+
interface StatusInfoViewProps {
|
|
5
|
+
config: AgentConfig | undefined;
|
|
6
|
+
version: string;
|
|
7
|
+
configPath: string;
|
|
8
|
+
serviceInstalled: boolean;
|
|
9
|
+
serviceStatus: string;
|
|
10
|
+
daemonPid: number | undefined;
|
|
11
|
+
enabledPackNames: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function StatusInfoView({
|
|
15
|
+
config,
|
|
16
|
+
version,
|
|
17
|
+
configPath,
|
|
18
|
+
serviceInstalled,
|
|
19
|
+
serviceStatus,
|
|
20
|
+
daemonPid,
|
|
21
|
+
enabledPackNames,
|
|
22
|
+
}: StatusInfoViewProps): JSX.Element {
|
|
23
|
+
if (!config) {
|
|
24
|
+
return (
|
|
25
|
+
<Box flexDirection="column">
|
|
26
|
+
<Text>
|
|
27
|
+
Not enrolled. Run <Text color="cyan">sonde enroll</Text> or{' '}
|
|
28
|
+
<Text color="cyan">sonde install</Text> to get started.
|
|
29
|
+
</Text>
|
|
30
|
+
</Box>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const processStatus = serviceInstalled
|
|
35
|
+
? `service ${serviceStatus}`
|
|
36
|
+
: daemonPid
|
|
37
|
+
? `running (PID ${daemonPid})`
|
|
38
|
+
: 'stopped';
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Box flexDirection="column">
|
|
42
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
43
|
+
<Text bold color="white">
|
|
44
|
+
Agent
|
|
45
|
+
</Text>
|
|
46
|
+
<Text>
|
|
47
|
+
{' '}Name: <Text color="cyan">{config.agentName}</Text>
|
|
48
|
+
</Text>
|
|
49
|
+
<Text>
|
|
50
|
+
{' '}Hub: <Text color="cyan">{config.hubUrl}</Text>
|
|
51
|
+
</Text>
|
|
52
|
+
<Text>
|
|
53
|
+
{' '}ID: <Text color="cyan">{config.agentId ?? '(not assigned)'}</Text>
|
|
54
|
+
</Text>
|
|
55
|
+
<Text>
|
|
56
|
+
{' '}Config: <Text color="cyan">{configPath}</Text>
|
|
57
|
+
</Text>
|
|
58
|
+
<Text>
|
|
59
|
+
{' '}Version: <Text color="cyan">v{version}</Text>
|
|
60
|
+
</Text>
|
|
61
|
+
</Box>
|
|
62
|
+
|
|
63
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
64
|
+
<Text bold color="white">
|
|
65
|
+
Process
|
|
66
|
+
</Text>
|
|
67
|
+
<Text>
|
|
68
|
+
{' '}Status:{' '}
|
|
69
|
+
<Text color={daemonPid || serviceStatus === 'active' ? 'green' : 'yellow'}>
|
|
70
|
+
{processStatus}
|
|
71
|
+
</Text>
|
|
72
|
+
</Text>
|
|
73
|
+
</Box>
|
|
74
|
+
|
|
75
|
+
<Box flexDirection="column">
|
|
76
|
+
<Text bold color="white">
|
|
77
|
+
Packs ({enabledPackNames.length})
|
|
78
|
+
</Text>
|
|
79
|
+
{enabledPackNames.map((name) => (
|
|
80
|
+
<Text key={name}>
|
|
81
|
+
{' '}
|
|
82
|
+
<Text color="cyan">{name}</Text>
|
|
83
|
+
</Text>
|
|
84
|
+
))}
|
|
85
|
+
</Box>
|
|
86
|
+
</Box>
|
|
87
|
+
);
|
|
88
|
+
}
|