@sonde/agent 0.2.5 → 0.2.7
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 +96 -23
- package/CHANGELOG.md +20 -0
- package/dist/cli/packs.d.ts +6 -0
- package/dist/cli/packs.d.ts.map +1 -1
- package/dist/cli/packs.js +42 -8
- package/dist/cli/packs.js.map +1 -1
- package/dist/cli/packs.test.js +56 -1
- package/dist/cli/packs.test.js.map +1 -1
- 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 +7 -8
- package/dist/cli/update.js.map +1 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +126 -5
- 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/StepComplete.d.ts.map +1 -1
- package/dist/tui/installer/StepComplete.js +10 -1
- package/dist/tui/installer/StepComplete.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/manager/ManagerApp.d.ts +2 -1
- package/dist/tui/manager/ManagerApp.d.ts.map +1 -1
- package/dist/tui/manager/ManagerApp.js +3 -2
- package/dist/tui/manager/ManagerApp.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/packs.test.ts +65 -0
- package/src/cli/packs.ts +56 -10
- package/src/cli/service.test.ts +124 -0
- package/src/cli/service.ts +178 -0
- package/src/cli/update.ts +7 -8
- package/src/config.ts +57 -0
- package/src/index.ts +156 -5
- package/src/tui/installer/InstallerApp.tsx +6 -2
- package/src/tui/installer/StepComplete.tsx +12 -1
- package/src/tui/installer/StepService.tsx +112 -0
- package/src/tui/manager/ManagerApp.tsx +4 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { packRegistry } from '@sonde/packs';
|
|
2
3
|
import { Box, Text, useApp, useInput } from 'ink';
|
|
3
4
|
import Spinner from 'ink-spinner';
|
|
4
5
|
import { useEffect, useState } from 'react';
|
|
6
|
+
import { buildEnabledPacks } from '../../cli/packs.js';
|
|
5
7
|
import { saveConfig } from '../../config.js';
|
|
6
8
|
import { enrollWithHub } from '../../runtime/connection.js';
|
|
7
9
|
import { ProbeExecutor } from '../../runtime/executor.js';
|
|
@@ -11,13 +13,20 @@ export function StepComplete({ hubConfig, selectedPacks }) {
|
|
|
11
13
|
const [agentId, setAgentId] = useState('');
|
|
12
14
|
const [error, setError] = useState('');
|
|
13
15
|
useEffect(() => {
|
|
16
|
+
const selectedNames = new Set(selectedPacks.map((p) => p.name));
|
|
17
|
+
const disabledPacks = [...packRegistry.keys()]
|
|
18
|
+
.filter((name) => !selectedNames.has(name));
|
|
14
19
|
const config = {
|
|
15
20
|
hubUrl: hubConfig.hubUrl,
|
|
16
21
|
apiKey: hubConfig.apiKey,
|
|
17
22
|
agentName: hubConfig.agentName,
|
|
23
|
+
disabledPacks: disabledPacks.length > 0
|
|
24
|
+
? disabledPacks
|
|
25
|
+
: undefined,
|
|
18
26
|
};
|
|
19
27
|
saveConfig(config);
|
|
20
|
-
const
|
|
28
|
+
const enabledPacks = buildEnabledPacks(packRegistry, disabledPacks);
|
|
29
|
+
const executor = new ProbeExecutor(enabledPacks);
|
|
21
30
|
enrollWithHub(config, executor)
|
|
22
31
|
.then(({ agentId: id, apiKey: mintedKey }) => {
|
|
23
32
|
config.agentId = id;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StepComplete.js","sourceRoot":"","sources":["../../../src/tui/installer/StepComplete.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"StepComplete.js","sourceRoot":"","sources":["../../../src/tui/installer/StepComplete.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StepService.d.ts","sourceRoot":"","sources":["../../../src/tui/installer/StepService.tsx"],"names":[],"mappings":"AAKA,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAID,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,gBAAgB,GAAG,GAAG,CAAC,OAAO,CAoGrE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import Spinner from 'ink-spinner';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { installService } from '../../cli/service.js';
|
|
6
|
+
export function StepService({ onNext }) {
|
|
7
|
+
const [phase, setPhase] = useState('prompt');
|
|
8
|
+
const [result, setResult] = useState(null);
|
|
9
|
+
const isLinux = process.platform === 'linux';
|
|
10
|
+
useInput((input, key) => {
|
|
11
|
+
if (!isLinux) {
|
|
12
|
+
if (key.return)
|
|
13
|
+
onNext();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (phase === 'prompt') {
|
|
17
|
+
if (input === 'y' || input === 'Y' || key.return) {
|
|
18
|
+
setPhase('installing');
|
|
19
|
+
// Run async to let the spinner render
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
const res = installService();
|
|
22
|
+
setResult(res);
|
|
23
|
+
setPhase('done');
|
|
24
|
+
}, 0);
|
|
25
|
+
}
|
|
26
|
+
else if (input === 'n' || input === 'N') {
|
|
27
|
+
setResult(null);
|
|
28
|
+
setPhase('done');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (phase === 'done' && (key.return || input)) {
|
|
32
|
+
onNext();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (!isLinux) {
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Systemd Service" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Skipped \u2014 systemd services are only available on Linux." }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Press Enter to continue." }) })] }));
|
|
37
|
+
}
|
|
38
|
+
if (phase === 'installing') {
|
|
39
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { children: " Installing systemd service..." })] }));
|
|
40
|
+
}
|
|
41
|
+
if (phase === 'done') {
|
|
42
|
+
if (result === null) {
|
|
43
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Systemd Service" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["Skipped. You can set this up later with:", ' ', _jsx(Text, { color: "cyan", children: "sonde service install" })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Press any key to continue." }) })] }));
|
|
44
|
+
}
|
|
45
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Systemd Service" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: result.success ? 'green' : 'red', children: [result.success ? ' OK' : ' !!', " ", result.message] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Press any key to continue." }) })] }));
|
|
46
|
+
}
|
|
47
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Systemd Service" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Set up sonde-agent as a systemd service? (starts on boot)" }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "y: install service | n: skip" }) })] }));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=StepService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StepService.js","sourceRoot":"","sources":["../../../src/tui/installer/StepService.tsx"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,8 @@ interface Runtime {
|
|
|
14
14
|
}
|
|
15
15
|
interface ManagerAppProps {
|
|
16
16
|
createRuntime: (events: ConnectionEvents) => Runtime;
|
|
17
|
+
onDetach?: () => void;
|
|
17
18
|
}
|
|
18
|
-
export declare function ManagerApp({ createRuntime }: ManagerAppProps): JSX.Element;
|
|
19
|
+
export declare function ManagerApp({ createRuntime, onDetach }: ManagerAppProps): JSX.Element;
|
|
19
20
|
export {};
|
|
20
21
|
//# sourceMappingURL=ManagerApp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManagerApp.d.ts","sourceRoot":"","sources":["../../../src/tui/manager/ManagerApp.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAQ/D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,OAAO;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED,UAAU,eAAe;IACvB,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ManagerApp.d.ts","sourceRoot":"","sources":["../../../src/tui/manager/ManagerApp.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAQ/D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,OAAO;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED,UAAU,eAAe;IACvB,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,CAAC;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAID,wBAAgB,UAAU,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,eAAe,GAAG,GAAG,CAAC,OAAO,CA6HpF"}
|
|
@@ -7,7 +7,7 @@ import { AuditView } from './AuditView.js';
|
|
|
7
7
|
import { PackManager } from './PackManager.js';
|
|
8
8
|
import { StatusView } from './StatusView.js';
|
|
9
9
|
const MAX_ACTIVITY = 50;
|
|
10
|
-
export function ManagerApp({ createRuntime }) {
|
|
10
|
+
export function ManagerApp({ createRuntime, onDetach }) {
|
|
11
11
|
const { exit } = useApp();
|
|
12
12
|
const [view, setView] = useState('status');
|
|
13
13
|
const [connected, setConnected] = useState(false);
|
|
@@ -63,6 +63,7 @@ export function ManagerApp({ createRuntime }) {
|
|
|
63
63
|
break;
|
|
64
64
|
case 'q':
|
|
65
65
|
runtimeRef.current?.connection.stop();
|
|
66
|
+
onDetach?.();
|
|
66
67
|
exit();
|
|
67
68
|
break;
|
|
68
69
|
}
|
|
@@ -74,6 +75,6 @@ export function ManagerApp({ createRuntime }) {
|
|
|
74
75
|
const statusColor = connected ? 'green' : 'yellow';
|
|
75
76
|
const statusText = connected ? 'Connected' : 'Connecting...';
|
|
76
77
|
const displayId = agentId ? ` (${agentId})` : '';
|
|
77
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Sonde Agent" }), _jsx(Text, { children: " " }), _jsx(Text, { color: statusColor, children: statusText }), _jsx(Text, { color: "gray", children: displayId })] }), view === 'status' && config && executor && auditLog && (_jsx(StatusView, { config: config, connected: connected, agentId: agentId, executor: executor, auditLog: auditLog, activity: activity })), view === 'packs' && executor && _jsx(PackManager, { executor: executor }), view === 'activity' && _jsx(ActivityLog, { activity: activity }), view === 'audit' && auditLog && _jsx(AuditView, { auditLog: auditLog }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: view === 'status' ? 'cyan' : 'gray', bold: view === 'status', children: "s:status" }), _jsx(Text, { children: " " }), _jsx(Text, { color: view === 'packs' ? 'cyan' : 'gray', bold: view === 'packs', children: "p:packs" }), _jsx(Text, { children: " " }), _jsx(Text, { color: view === 'activity' ? 'cyan' : 'gray', bold: view === 'activity', children: "l:activity" }), _jsx(Text, { children: " " }), _jsx(Text, { color: view === 'audit' ? 'cyan' : 'gray', bold: view === 'audit', children: "a:audit" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: "q:
|
|
78
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Sonde Agent" }), _jsx(Text, { children: " " }), _jsx(Text, { color: statusColor, children: statusText }), _jsx(Text, { color: "gray", children: displayId })] }), view === 'status' && config && executor && auditLog && (_jsx(StatusView, { config: config, connected: connected, agentId: agentId, executor: executor, auditLog: auditLog, activity: activity })), view === 'packs' && executor && _jsx(PackManager, { executor: executor }), view === 'activity' && _jsx(ActivityLog, { activity: activity }), view === 'audit' && auditLog && _jsx(AuditView, { auditLog: auditLog }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: view === 'status' ? 'cyan' : 'gray', bold: view === 'status', children: "s:status" }), _jsx(Text, { children: " " }), _jsx(Text, { color: view === 'packs' ? 'cyan' : 'gray', bold: view === 'packs', children: "p:packs" }), _jsx(Text, { children: " " }), _jsx(Text, { color: view === 'activity' ? 'cyan' : 'gray', bold: view === 'activity', children: "l:activity" }), _jsx(Text, { children: " " }), _jsx(Text, { color: view === 'audit' ? 'cyan' : 'gray', bold: view === 'audit', children: "a:audit" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: "q:detach" })] })] }));
|
|
78
79
|
}
|
|
79
80
|
//# sourceMappingURL=ManagerApp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManagerApp.js","sourceRoot":"","sources":["../../../src/tui/manager/ManagerApp.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ManagerApp.js","sourceRoot":"","sources":["../../../src/tui/manager/ManagerApp.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli/packs.test.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { SystemChecker } from '../system/scanner.js';
|
|
|
4
4
|
import {
|
|
5
5
|
type PackCommandDeps,
|
|
6
6
|
type PackState,
|
|
7
|
+
buildEnabledPacks,
|
|
7
8
|
cmdPacksInstall,
|
|
8
9
|
cmdPacksList,
|
|
9
10
|
cmdPacksScan,
|
|
@@ -52,6 +53,7 @@ function createDeps(overrides: Partial<PackCommandDeps> = {}): PackCommandDeps {
|
|
|
52
53
|
checker: createMockChecker(),
|
|
53
54
|
getUserGroups: () => [],
|
|
54
55
|
log: vi.fn(),
|
|
56
|
+
persist: vi.fn(),
|
|
55
57
|
...overrides,
|
|
56
58
|
};
|
|
57
59
|
}
|
|
@@ -210,4 +212,67 @@ describe('cmdPacksUninstall', () => {
|
|
|
210
212
|
const log = deps.log as ReturnType<typeof vi.fn>;
|
|
211
213
|
expect(log).toHaveBeenCalledWith('Error: Pack "docker" is not installed.');
|
|
212
214
|
});
|
|
215
|
+
|
|
216
|
+
it('persists disabled packs on uninstall', () => {
|
|
217
|
+
const deps = createDeps();
|
|
218
|
+
cmdPacksUninstall('system', deps);
|
|
219
|
+
|
|
220
|
+
const persist = deps.persist as ReturnType<typeof vi.fn>;
|
|
221
|
+
expect(persist).toHaveBeenCalledTimes(1);
|
|
222
|
+
const firstCall = persist.mock.calls[0] as unknown[];
|
|
223
|
+
const disabled = firstCall[0] as string[];
|
|
224
|
+
expect(disabled).toContain('system');
|
|
225
|
+
expect(disabled).toContain('docker');
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('cmdPacksInstall persistence', () => {
|
|
230
|
+
it('persists disabled packs on install', () => {
|
|
231
|
+
const deps = createDeps();
|
|
232
|
+
cmdPacksInstall('docker', deps);
|
|
233
|
+
|
|
234
|
+
const persist = deps.persist as ReturnType<typeof vi.fn>;
|
|
235
|
+
expect(persist).toHaveBeenCalledTimes(1);
|
|
236
|
+
const firstCall = persist.mock.calls[0] as unknown[];
|
|
237
|
+
const disabled = firstCall[0] as string[];
|
|
238
|
+
expect(disabled).not.toContain('docker');
|
|
239
|
+
expect(disabled).not.toContain('system');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('does not persist when install fails', () => {
|
|
243
|
+
const deps = createDeps();
|
|
244
|
+
cmdPacksInstall('nonexistent', deps);
|
|
245
|
+
|
|
246
|
+
const persist = deps.persist as ReturnType<typeof vi.fn>;
|
|
247
|
+
expect(persist).not.toHaveBeenCalled();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe('buildEnabledPacks', () => {
|
|
252
|
+
it('returns all packs when none disabled', () => {
|
|
253
|
+
const registry = new Map([
|
|
254
|
+
['system', createMockPack('system')],
|
|
255
|
+
['docker', createMockPack('docker')],
|
|
256
|
+
]);
|
|
257
|
+
const result = buildEnabledPacks(registry, []);
|
|
258
|
+
expect([...result.keys()]).toEqual(['system', 'docker']);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('filters out disabled packs', () => {
|
|
262
|
+
const registry = new Map([
|
|
263
|
+
['system', createMockPack('system')],
|
|
264
|
+
['docker', createMockPack('docker')],
|
|
265
|
+
['nginx', createMockPack('nginx')],
|
|
266
|
+
]);
|
|
267
|
+
const result = buildEnabledPacks(registry, ['docker', 'nginx']);
|
|
268
|
+
expect([...result.keys()]).toEqual(['system']);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('ignores disabled names not in registry', () => {
|
|
272
|
+
const registry = new Map([
|
|
273
|
+
['system', createMockPack('system')],
|
|
274
|
+
]);
|
|
275
|
+
const result = buildEnabledPacks(registry, ['nonexistent']);
|
|
276
|
+
expect([...result.keys()]).toEqual(['system']);
|
|
277
|
+
});
|
|
213
278
|
});
|
package/src/cli/packs.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Pack } from '@sonde/packs';
|
|
2
2
|
import { packRegistry } from '@sonde/packs';
|
|
3
3
|
import type { PackManifest } from '@sonde/shared';
|
|
4
|
+
import { loadConfig, saveConfig } from '../config.js';
|
|
4
5
|
import {
|
|
5
6
|
type PermissionCheck,
|
|
6
7
|
type ScanResult,
|
|
@@ -10,6 +11,24 @@ import {
|
|
|
10
11
|
scanForSoftware,
|
|
11
12
|
} from '../system/scanner.js';
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Build a pack map filtered by a disabled list.
|
|
16
|
+
* Used by both the CLI commands and ProbeExecutor creation.
|
|
17
|
+
*/
|
|
18
|
+
export function buildEnabledPacks(
|
|
19
|
+
registry: ReadonlyMap<string, Pack>,
|
|
20
|
+
disabledPacks: string[],
|
|
21
|
+
): Map<string, Pack> {
|
|
22
|
+
const disabled = new Set(disabledPacks);
|
|
23
|
+
const result = new Map<string, Pack>();
|
|
24
|
+
for (const [name, pack] of registry) {
|
|
25
|
+
if (!disabled.has(name)) {
|
|
26
|
+
result.set(name, pack);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
13
32
|
export interface PackState {
|
|
14
33
|
/** Packs currently loaded/active on this agent */
|
|
15
34
|
installed: Map<string, Pack>;
|
|
@@ -22,17 +41,28 @@ export interface PackCommandDeps {
|
|
|
22
41
|
checker: SystemChecker;
|
|
23
42
|
getUserGroups: () => string[];
|
|
24
43
|
log: (msg: string) => void;
|
|
44
|
+
persist: (disabledPacks: string[]) => void;
|
|
25
45
|
}
|
|
26
46
|
|
|
27
47
|
function createDefaultDeps(): PackCommandDeps {
|
|
48
|
+
const config = loadConfig();
|
|
49
|
+
const disabledPacks = config?.disabledPacks ?? [];
|
|
28
50
|
return {
|
|
29
51
|
state: {
|
|
30
|
-
installed:
|
|
52
|
+
installed: buildEnabledPacks(packRegistry, disabledPacks),
|
|
31
53
|
available: packRegistry,
|
|
32
54
|
},
|
|
33
55
|
checker: createSystemChecker(),
|
|
34
56
|
getUserGroups: getProcessUserGroups,
|
|
35
57
|
log: console.log,
|
|
58
|
+
persist: (disabled) => {
|
|
59
|
+
const current = loadConfig();
|
|
60
|
+
if (current) {
|
|
61
|
+
current.disabledPacks =
|
|
62
|
+
disabled.length > 0 ? disabled : undefined;
|
|
63
|
+
saveConfig(current);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
36
66
|
};
|
|
37
67
|
}
|
|
38
68
|
|
|
@@ -58,17 +88,20 @@ export function cmdPacksList(deps?: PackCommandDeps): void {
|
|
|
58
88
|
return;
|
|
59
89
|
}
|
|
60
90
|
|
|
91
|
+
const bold = (s: string) => `\x1b[1m${s}\x1b[0m`;
|
|
92
|
+
|
|
61
93
|
log('Installed packs:');
|
|
62
94
|
log('');
|
|
63
95
|
for (const [name, pack] of state.installed) {
|
|
64
96
|
const probeCount = pack.manifest.probes.length;
|
|
65
|
-
log(` ${name} v${pack.manifest.version} (${probeCount} probes)`);
|
|
97
|
+
log(` ${bold(name)} v${pack.manifest.version} (${probeCount} probes)`);
|
|
66
98
|
log(` ${pack.manifest.description}`);
|
|
67
99
|
for (const probe of pack.manifest.probes) {
|
|
68
100
|
log(` - ${name}.${probe.name}: ${probe.description}`);
|
|
69
101
|
}
|
|
70
102
|
log('');
|
|
71
103
|
}
|
|
104
|
+
log(` Manage: sonde packs install ${bold('<name>')} | uninstall ${bold('<name>')}`);
|
|
72
105
|
}
|
|
73
106
|
|
|
74
107
|
export function cmdPacksScan(deps?: PackCommandDeps): ScanResult[] {
|
|
@@ -112,7 +145,8 @@ export function cmdPacksInstall(
|
|
|
112
145
|
name: string,
|
|
113
146
|
deps?: PackCommandDeps,
|
|
114
147
|
): { success: boolean; permissions?: PermissionCheck } {
|
|
115
|
-
const { state, checker, getUserGroups, log } =
|
|
148
|
+
const { state, checker, getUserGroups, log, persist } =
|
|
149
|
+
deps ?? createDefaultDeps();
|
|
116
150
|
|
|
117
151
|
const pack = state.available.get(name);
|
|
118
152
|
if (!pack) {
|
|
@@ -128,7 +162,9 @@ export function cmdPacksInstall(
|
|
|
128
162
|
|
|
129
163
|
// Check permissions
|
|
130
164
|
const userGroups = getUserGroups();
|
|
131
|
-
const permissions = checkPackPermissions(
|
|
165
|
+
const permissions = checkPackPermissions(
|
|
166
|
+
pack.manifest, checker, userGroups,
|
|
167
|
+
);
|
|
132
168
|
|
|
133
169
|
if (!permissions.satisfied) {
|
|
134
170
|
log(`Pack "${name}" requires additional permissions:`);
|
|
@@ -150,13 +186,19 @@ export function cmdPacksInstall(
|
|
|
150
186
|
}
|
|
151
187
|
|
|
152
188
|
state.installed.set(name, pack);
|
|
189
|
+
const disabled = [...state.available.keys()]
|
|
190
|
+
.filter((k) => !state.installed.has(k));
|
|
191
|
+
persist(disabled);
|
|
153
192
|
log(`Pack "${name}" installed successfully.`);
|
|
154
193
|
log(` ${pack.manifest.probes.length} probes now available.`);
|
|
155
194
|
return { success: true, permissions };
|
|
156
195
|
}
|
|
157
196
|
|
|
158
|
-
export function cmdPacksUninstall(
|
|
159
|
-
|
|
197
|
+
export function cmdPacksUninstall(
|
|
198
|
+
name: string,
|
|
199
|
+
deps?: PackCommandDeps,
|
|
200
|
+
): boolean {
|
|
201
|
+
const { state, log, persist } = deps ?? createDefaultDeps();
|
|
160
202
|
|
|
161
203
|
if (!state.installed.has(name)) {
|
|
162
204
|
log(`Error: Pack "${name}" is not installed.`);
|
|
@@ -164,19 +206,23 @@ export function cmdPacksUninstall(name: string, deps?: PackCommandDeps): boolean
|
|
|
164
206
|
}
|
|
165
207
|
|
|
166
208
|
state.installed.delete(name);
|
|
209
|
+
const disabled = [...state.available.keys()]
|
|
210
|
+
.filter((k) => !state.installed.has(k));
|
|
211
|
+
persist(disabled);
|
|
167
212
|
log(`Pack "${name}" uninstalled.`);
|
|
168
213
|
return true;
|
|
169
214
|
}
|
|
170
215
|
|
|
171
216
|
export function handlePacksCommand(subArgs: string[]): void {
|
|
172
217
|
const subcommand = subArgs[0];
|
|
218
|
+
const deps = createDefaultDeps();
|
|
173
219
|
|
|
174
220
|
switch (subcommand) {
|
|
175
221
|
case 'list':
|
|
176
|
-
cmdPacksList();
|
|
222
|
+
cmdPacksList(deps);
|
|
177
223
|
break;
|
|
178
224
|
case 'scan':
|
|
179
|
-
cmdPacksScan();
|
|
225
|
+
cmdPacksScan(deps);
|
|
180
226
|
break;
|
|
181
227
|
case 'install': {
|
|
182
228
|
const name = subArgs[1];
|
|
@@ -184,7 +230,7 @@ export function handlePacksCommand(subArgs: string[]): void {
|
|
|
184
230
|
console.error('Usage: sonde packs install <name>');
|
|
185
231
|
process.exit(1);
|
|
186
232
|
}
|
|
187
|
-
const result = cmdPacksInstall(name);
|
|
233
|
+
const result = cmdPacksInstall(name, deps);
|
|
188
234
|
if (!result.success) process.exit(1);
|
|
189
235
|
break;
|
|
190
236
|
}
|
|
@@ -194,7 +240,7 @@ export function handlePacksCommand(subArgs: string[]): void {
|
|
|
194
240
|
console.error('Usage: sonde packs uninstall <name>');
|
|
195
241
|
process.exit(1);
|
|
196
242
|
}
|
|
197
|
-
if (!cmdPacksUninstall(name)) process.exit(1);
|
|
243
|
+
if (!cmdPacksUninstall(name, deps)) process.exit(1);
|
|
198
244
|
break;
|
|
199
245
|
}
|
|
200
246
|
default:
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('node:child_process', () => ({
|
|
4
|
+
execFileSync: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
vi.mock('node:fs', () => ({
|
|
8
|
+
default: { existsSync: vi.fn() },
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock('node:os', () => ({
|
|
12
|
+
default: {
|
|
13
|
+
userInfo: () => ({
|
|
14
|
+
username: 'testuser',
|
|
15
|
+
homedir: '/home/testuser',
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
import { execFileSync } from 'node:child_process';
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import {
|
|
23
|
+
generateUnitFile,
|
|
24
|
+
getServiceStatus,
|
|
25
|
+
isServiceInstalled,
|
|
26
|
+
} from './service.js';
|
|
27
|
+
|
|
28
|
+
const mockExec = vi.mocked(execFileSync);
|
|
29
|
+
const mockExists = vi.mocked(fs.existsSync);
|
|
30
|
+
|
|
31
|
+
describe('generateUnitFile', () => {
|
|
32
|
+
it('includes the current user and home directory', () => {
|
|
33
|
+
mockExec.mockReturnValueOnce('/usr/local/bin/sonde\n');
|
|
34
|
+
const unit = generateUnitFile();
|
|
35
|
+
expect(unit).toContain('User=testuser');
|
|
36
|
+
expect(unit).toContain('Environment=HOME=/home/testuser');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('includes the resolved sonde binary path', () => {
|
|
40
|
+
mockExec.mockReturnValueOnce('/usr/local/bin/sonde\n');
|
|
41
|
+
const unit = generateUnitFile();
|
|
42
|
+
expect(unit).toContain(
|
|
43
|
+
'ExecStart=/usr/local/bin/sonde start --headless',
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('sets restart on failure with 5s delay', () => {
|
|
48
|
+
mockExec.mockReturnValueOnce('/usr/bin/sonde\n');
|
|
49
|
+
const unit = generateUnitFile();
|
|
50
|
+
expect(unit).toContain('Restart=on-failure');
|
|
51
|
+
expect(unit).toContain('RestartSec=5');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('targets network-online', () => {
|
|
55
|
+
mockExec.mockReturnValueOnce('/usr/bin/sonde\n');
|
|
56
|
+
const unit = generateUnitFile();
|
|
57
|
+
expect(unit).toContain('After=network-online.target');
|
|
58
|
+
expect(unit).toContain('Wants=network-online.target');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('isServiceInstalled', () => {
|
|
63
|
+
const originalPlatform = process.platform;
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
Object.defineProperty(process, 'platform', {
|
|
67
|
+
value: originalPlatform,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('returns false on non-Linux platforms', () => {
|
|
72
|
+
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
|
73
|
+
expect(isServiceInstalled()).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('returns true when unit file exists on Linux', () => {
|
|
77
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
78
|
+
mockExists.mockReturnValueOnce(true);
|
|
79
|
+
expect(isServiceInstalled()).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('returns false when unit file is missing on Linux', () => {
|
|
83
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
84
|
+
mockExists.mockReturnValueOnce(false);
|
|
85
|
+
expect(isServiceInstalled()).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('getServiceStatus', () => {
|
|
90
|
+
const originalPlatform = process.platform;
|
|
91
|
+
|
|
92
|
+
afterEach(() => {
|
|
93
|
+
Object.defineProperty(process, 'platform', {
|
|
94
|
+
value: originalPlatform,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('returns unsupported on non-Linux', () => {
|
|
99
|
+
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
|
100
|
+
expect(getServiceStatus()).toBe('unsupported');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('returns not-installed when unit file missing', () => {
|
|
104
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
105
|
+
mockExists.mockReturnValueOnce(false);
|
|
106
|
+
expect(getServiceStatus()).toBe('not-installed');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('returns active status from systemctl', () => {
|
|
110
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
111
|
+
mockExists.mockReturnValueOnce(true);
|
|
112
|
+
mockExec.mockReturnValueOnce('active\n');
|
|
113
|
+
expect(getServiceStatus()).toBe('active');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns inactive when systemctl throws', () => {
|
|
117
|
+
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
118
|
+
mockExists.mockReturnValueOnce(true);
|
|
119
|
+
mockExec.mockImplementationOnce(() => {
|
|
120
|
+
throw new Error('exit code 3');
|
|
121
|
+
});
|
|
122
|
+
expect(getServiceStatus()).toBe('inactive');
|
|
123
|
+
});
|
|
124
|
+
});
|