@sonde/agent 0.2.3 → 0.2.4
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 +66 -21
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +10 -0
- package/dist/cli/mcp-bridge.d.ts +2 -0
- package/dist/cli/mcp-bridge.d.ts.map +1 -0
- package/dist/cli/mcp-bridge.js +193 -0
- package/dist/cli/mcp-bridge.js.map +1 -0
- package/dist/cli/mcp-bridge.test.d.ts +2 -0
- package/dist/cli/mcp-bridge.test.d.ts.map +1 -0
- package/dist/cli/mcp-bridge.test.js +54 -0
- package/dist/cli/mcp-bridge.test.js.map +1 -0
- package/dist/cli/update.d.ts +18 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +71 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/update.test.d.ts +2 -0
- package/dist/cli/update.test.d.ts.map +1 -0
- package/dist/cli/update.test.js +55 -0
- package/dist/cli/update.test.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -2
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -5
- package/dist/index.js.map +1 -1
- package/dist/runtime/connection.d.ts +1 -0
- package/dist/runtime/connection.d.ts.map +1 -1
- package/dist/runtime/connection.js +30 -5
- package/dist/runtime/connection.js.map +1 -1
- package/dist/runtime/executor.d.ts +6 -0
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +2 -2
- package/dist/runtime/executor.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/mcp-bridge.test.ts +58 -0
- package/src/cli/mcp-bridge.ts +217 -0
- package/src/cli/update.test.ts +69 -0
- package/src/cli/update.ts +78 -0
- package/src/config.ts +16 -2
- package/src/index.ts +66 -7
- package/src/runtime/connection.ts +39 -6
- package/src/runtime/executor.ts +14 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { checkForUpdate, semverLt } from './update.js';
|
|
3
|
+
|
|
4
|
+
describe('semverLt', () => {
|
|
5
|
+
it('returns true when a < b (major)', () => {
|
|
6
|
+
expect(semverLt('0.1.0', '1.0.0')).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns true when a < b (minor)', () => {
|
|
10
|
+
expect(semverLt('1.0.0', '1.1.0')).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns true when a < b (patch)', () => {
|
|
14
|
+
expect(semverLt('1.1.0', '1.1.1')).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('returns false when equal', () => {
|
|
18
|
+
expect(semverLt('1.2.3', '1.2.3')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns false when a > b', () => {
|
|
22
|
+
expect(semverLt('2.0.0', '1.9.9')).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('checkForUpdate', () => {
|
|
27
|
+
const originalFetch = globalThis.fetch;
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
globalThis.fetch = originalFetch;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns update info when newer version is available', async () => {
|
|
34
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
35
|
+
ok: true,
|
|
36
|
+
json: async () => ({ version: '99.0.0' }),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const result = await checkForUpdate();
|
|
40
|
+
expect(result.latestVersion).toBe('99.0.0');
|
|
41
|
+
expect(result.updateAvailable).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns no update when on latest version', async () => {
|
|
45
|
+
// Use 0.0.0 which is lower than any real version
|
|
46
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
47
|
+
ok: true,
|
|
48
|
+
json: async () => ({ version: '0.0.0' }),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const result = await checkForUpdate();
|
|
52
|
+
expect(result.updateAvailable).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('throws on network error', async () => {
|
|
56
|
+
globalThis.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
|
|
57
|
+
|
|
58
|
+
await expect(checkForUpdate()).rejects.toThrow('Network error');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('throws on non-ok response', async () => {
|
|
62
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
63
|
+
ok: false,
|
|
64
|
+
status: 500,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await expect(checkForUpdate()).rejects.toThrow('Failed to check npm registry');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { VERSION } from '../version.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lightweight semver comparison: returns true if a < b.
|
|
6
|
+
*/
|
|
7
|
+
export function semverLt(a: string, b: string): boolean {
|
|
8
|
+
const pa = a.split('.').map(Number);
|
|
9
|
+
const pb = b.split('.').map(Number);
|
|
10
|
+
for (let i = 0; i < 3; i++) {
|
|
11
|
+
const av = pa[i] ?? 0;
|
|
12
|
+
const bv = pb[i] ?? 0;
|
|
13
|
+
if (av < bv) return true;
|
|
14
|
+
if (av > bv) return false;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check for available updates by querying the npm registry.
|
|
21
|
+
*/
|
|
22
|
+
export async function checkForUpdate(): Promise<{
|
|
23
|
+
currentVersion: string;
|
|
24
|
+
latestVersion: string;
|
|
25
|
+
updateAvailable: boolean;
|
|
26
|
+
}> {
|
|
27
|
+
const res = await fetch('https://registry.npmjs.org/@sonde/agent/latest', {
|
|
28
|
+
headers: { Accept: 'application/json' },
|
|
29
|
+
signal: AbortSignal.timeout(10_000),
|
|
30
|
+
});
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
throw new Error(`Failed to check npm registry (HTTP ${res.status})`);
|
|
33
|
+
}
|
|
34
|
+
const data = (await res.json()) as { version?: string };
|
|
35
|
+
const latestVersion = data.version;
|
|
36
|
+
if (!latestVersion) {
|
|
37
|
+
throw new Error('No version found in npm registry response');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
currentVersion: VERSION,
|
|
42
|
+
latestVersion,
|
|
43
|
+
updateAvailable: semverLt(VERSION, latestVersion),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Perform the update by running npm install -g.
|
|
49
|
+
* After install, tries to restart the systemd service (best-effort).
|
|
50
|
+
*/
|
|
51
|
+
export function performUpdate(targetVersion: string): void {
|
|
52
|
+
console.log(`Installing @sonde/agent@${targetVersion}...`);
|
|
53
|
+
execFileSync('npm', ['install', '-g', `@sonde/agent@${targetVersion}`], {
|
|
54
|
+
stdio: 'inherit',
|
|
55
|
+
timeout: 120_000,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Verify the installed version
|
|
59
|
+
const output = execFileSync('sonde', ['--version'], {
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
timeout: 5_000,
|
|
62
|
+
}).trim();
|
|
63
|
+
if (output !== targetVersion) {
|
|
64
|
+
throw new Error(`Version mismatch after install: expected ${targetVersion}, got ${output}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(`Successfully updated to v${targetVersion}`);
|
|
68
|
+
|
|
69
|
+
// Best-effort systemd restart
|
|
70
|
+
try {
|
|
71
|
+
execFileSync('systemctl', ['restart', 'sonde-agent'], {
|
|
72
|
+
timeout: 10_000,
|
|
73
|
+
});
|
|
74
|
+
console.log('Restarted sonde-agent systemd service');
|
|
75
|
+
} catch {
|
|
76
|
+
// systemd not available or service not installed — that's fine
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface AgentConfig {
|
|
|
12
12
|
keyPath?: string;
|
|
13
13
|
caCertPath?: string;
|
|
14
14
|
scrubPatterns?: string[];
|
|
15
|
+
allowUnsignedPacks?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const CONFIG_DIR = path.join(os.homedir(), '.sonde');
|
|
@@ -22,11 +23,24 @@ export function getConfigPath(): string {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export function loadConfig(): AgentConfig | undefined {
|
|
26
|
+
let raw: string;
|
|
27
|
+
try {
|
|
28
|
+
raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
29
|
+
} catch (err: unknown) {
|
|
30
|
+
const code = (err as { code?: string }).code;
|
|
31
|
+
if (code === 'ENOENT') return undefined;
|
|
32
|
+
if (code === 'EACCES') {
|
|
33
|
+
console.error(`Cannot read config at ${CONFIG_FILE}. Check file permissions.`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
try {
|
|
26
|
-
const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
27
40
|
return JSON.parse(raw) as AgentConfig;
|
|
28
41
|
} catch {
|
|
29
|
-
|
|
42
|
+
console.error(`Config file corrupted at ${CONFIG_FILE}. Re-enroll with "sonde enroll".`);
|
|
43
|
+
process.exit(1);
|
|
30
44
|
}
|
|
31
45
|
}
|
|
32
46
|
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import { handlePacksCommand } from './cli/packs.js';
|
|
5
|
+
import { checkForUpdate, performUpdate } from './cli/update.js';
|
|
5
6
|
import { type AgentConfig, getConfigPath, loadConfig, saveConfig } from './config.js';
|
|
6
7
|
import { AgentConnection, type ConnectionEvents, enrollWithHub } from './runtime/connection.js';
|
|
7
8
|
import { ProbeExecutor } from './runtime/executor.js';
|
|
@@ -26,6 +27,8 @@ function printUsage(): void {
|
|
|
26
27
|
console.log(' start Start the agent (TUI by default, --headless for daemon)');
|
|
27
28
|
console.log(' status Show agent status');
|
|
28
29
|
console.log(' packs Manage packs (list, scan, install, uninstall)');
|
|
30
|
+
console.log(' update Check for and install agent updates');
|
|
31
|
+
console.log(' mcp-bridge stdio MCP bridge (for Claude Code integration)');
|
|
29
32
|
console.log('');
|
|
30
33
|
console.log('Enroll options:');
|
|
31
34
|
console.log(' --hub <url> Hub URL (e.g. http://localhost:3000)');
|
|
@@ -64,6 +67,21 @@ function createRuntime(events: ConnectionEvents): Runtime {
|
|
|
64
67
|
return { config, executor, connection };
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
async function cmdUpdate(): Promise<void> {
|
|
71
|
+
console.log(`Current version: v${VERSION}`);
|
|
72
|
+
console.log('Checking for updates...');
|
|
73
|
+
|
|
74
|
+
const { latestVersion, updateAvailable } = await checkForUpdate();
|
|
75
|
+
|
|
76
|
+
if (!updateAvailable) {
|
|
77
|
+
console.log(`Already on the latest version (v${latestVersion}).`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(`New version available: v${latestVersion}`);
|
|
82
|
+
performUpdate(latestVersion);
|
|
83
|
+
}
|
|
84
|
+
|
|
67
85
|
async function cmdEnroll(): Promise<void> {
|
|
68
86
|
const hubUrl = getArg('--hub');
|
|
69
87
|
const apiKey = getArg('--key');
|
|
@@ -129,6 +147,11 @@ function cmdStart(): void {
|
|
|
129
147
|
config.agentId = agentId;
|
|
130
148
|
saveConfig(config);
|
|
131
149
|
},
|
|
150
|
+
onUpdateAvailable: (latestVersion, currentVersion) => {
|
|
151
|
+
console.log(
|
|
152
|
+
`Update available: v${currentVersion} → v${latestVersion}. Run "sonde update" to upgrade.`,
|
|
153
|
+
);
|
|
154
|
+
},
|
|
132
155
|
});
|
|
133
156
|
|
|
134
157
|
console.log(`Sonde Agent v${VERSION}`);
|
|
@@ -137,12 +160,15 @@ function cmdStart(): void {
|
|
|
137
160
|
console.log('');
|
|
138
161
|
|
|
139
162
|
connection.start();
|
|
163
|
+
process.stdin.unref();
|
|
140
164
|
|
|
141
|
-
|
|
165
|
+
const shutdown = () => {
|
|
142
166
|
console.log('\nShutting down...');
|
|
143
167
|
connection.stop();
|
|
144
168
|
process.exit(0);
|
|
145
|
-
}
|
|
169
|
+
};
|
|
170
|
+
process.on('SIGINT', shutdown);
|
|
171
|
+
process.on('SIGTERM', shutdown);
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
async function cmdManager(): Promise<void> {
|
|
@@ -190,8 +216,17 @@ switch (command) {
|
|
|
190
216
|
});
|
|
191
217
|
break;
|
|
192
218
|
case 'enroll':
|
|
193
|
-
cmdEnroll().catch((err: Error) => {
|
|
194
|
-
|
|
219
|
+
cmdEnroll().catch((err: Error & { code?: string }) => {
|
|
220
|
+
if (err.code === 'ECONNREFUSED') {
|
|
221
|
+
const hubUrl = getArg('--hub') ?? 'the hub';
|
|
222
|
+
console.error(`Could not connect to hub at ${hubUrl}. Verify the hub is running.`);
|
|
223
|
+
} else if (err.message?.includes('401') || err.message?.includes('Unauthorized')) {
|
|
224
|
+
console.error('Authentication failed. Check your API key or enrollment token.');
|
|
225
|
+
} else if (err.message?.includes('timed out')) {
|
|
226
|
+
console.error('Enrollment timed out. The hub may be unreachable.');
|
|
227
|
+
} else {
|
|
228
|
+
console.error(`Enrollment failed: ${err.message}`);
|
|
229
|
+
}
|
|
195
230
|
process.exit(1);
|
|
196
231
|
});
|
|
197
232
|
break;
|
|
@@ -199,8 +234,14 @@ switch (command) {
|
|
|
199
234
|
if (hasFlag('--headless')) {
|
|
200
235
|
cmdStart();
|
|
201
236
|
} else {
|
|
202
|
-
cmdManager().catch((err: Error) => {
|
|
203
|
-
|
|
237
|
+
cmdManager().catch((err: Error & { code?: string }) => {
|
|
238
|
+
if (err.code === 'ECONNREFUSED') {
|
|
239
|
+
console.error(
|
|
240
|
+
'Could not connect to hub. Verify the hub is running and the URL is correct.',
|
|
241
|
+
);
|
|
242
|
+
} else {
|
|
243
|
+
console.error(err.message);
|
|
244
|
+
}
|
|
204
245
|
process.exit(1);
|
|
205
246
|
});
|
|
206
247
|
}
|
|
@@ -211,10 +252,28 @@ switch (command) {
|
|
|
211
252
|
case 'packs':
|
|
212
253
|
handlePacksCommand(args.slice(1));
|
|
213
254
|
break;
|
|
255
|
+
case 'update':
|
|
256
|
+
cmdUpdate().catch((err: Error) => {
|
|
257
|
+
console.error(`Update failed: ${err.message}`);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
});
|
|
260
|
+
break;
|
|
261
|
+
case 'mcp-bridge':
|
|
262
|
+
import('./cli/mcp-bridge.js').then(({ startMcpBridge }) =>
|
|
263
|
+
startMcpBridge().catch((err: Error) => {
|
|
264
|
+
process.stderr.write(`[sonde-bridge] Fatal: ${err.message}\n`);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}),
|
|
267
|
+
);
|
|
268
|
+
break;
|
|
214
269
|
default:
|
|
215
270
|
if (command) {
|
|
216
271
|
printUsage();
|
|
217
|
-
|
|
272
|
+
if (command.startsWith('--')) {
|
|
273
|
+
console.error(`\nUnknown flag: ${command}. Did you mean "sonde start ${command}"?`);
|
|
274
|
+
} else {
|
|
275
|
+
console.error(`\nUnknown command: ${command}`);
|
|
276
|
+
}
|
|
218
277
|
process.exit(1);
|
|
219
278
|
} else {
|
|
220
279
|
// No command: launch TUI if enrolled, otherwise show usage
|
|
@@ -22,6 +22,7 @@ export interface ConnectionEvents {
|
|
|
22
22
|
onError?: (error: Error) => void;
|
|
23
23
|
onRegistered?: (agentId: string) => void;
|
|
24
24
|
onProbeCompleted?: (probe: string, status: string, durationMs: number) => void;
|
|
25
|
+
onUpdateAvailable?: (latestVersion: string, currentVersion: string) => void;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
/** Minimum/maximum reconnect delays */
|
|
@@ -119,13 +120,27 @@ export function enrollWithHub(
|
|
|
119
120
|
}
|
|
120
121
|
});
|
|
121
122
|
|
|
122
|
-
ws.on('error', (err) => {
|
|
123
|
+
ws.on('error', (err: Error & { code?: string }) => {
|
|
123
124
|
clearTimeout(timeout);
|
|
124
|
-
reject(err);
|
|
125
|
+
reject(new Error(humanizeWsError(err, config.hubUrl)));
|
|
125
126
|
});
|
|
126
127
|
});
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
/** Map low-level WebSocket/network error codes to actionable messages. */
|
|
131
|
+
function humanizeWsError(err: Error & { code?: string }, hubUrl: string): string {
|
|
132
|
+
switch (err.code) {
|
|
133
|
+
case 'ECONNREFUSED':
|
|
134
|
+
return `Could not connect to hub at ${hubUrl}. Verify the hub is running.`;
|
|
135
|
+
case 'ENOTFOUND':
|
|
136
|
+
return `Hub hostname not found: ${hubUrl}. Check the URL.`;
|
|
137
|
+
case 'ETIMEDOUT':
|
|
138
|
+
return `Connection to hub at ${hubUrl} timed out. The hub may be unreachable.`;
|
|
139
|
+
default:
|
|
140
|
+
return err.message;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
129
144
|
/** Build WebSocket options, including TLS client cert if available. */
|
|
130
145
|
function buildWsOptions(config: AgentConfig): WebSocket.ClientOptions {
|
|
131
146
|
const options: WebSocket.ClientOptions = {
|
|
@@ -139,7 +154,7 @@ function buildWsOptions(config: AgentConfig): WebSocket.ClientOptions {
|
|
|
139
154
|
options.cert = fs.readFileSync(config.certPath, 'utf-8');
|
|
140
155
|
options.key = fs.readFileSync(config.keyPath, 'utf-8');
|
|
141
156
|
options.ca = [fs.readFileSync(config.caCertPath, 'utf-8')];
|
|
142
|
-
options.rejectUnauthorized =
|
|
157
|
+
options.rejectUnauthorized = true; // Verify hub cert against our CA
|
|
143
158
|
} catch {
|
|
144
159
|
// Cert files missing or unreadable — fall back to API key only
|
|
145
160
|
}
|
|
@@ -224,16 +239,21 @@ export class AgentConnection {
|
|
|
224
239
|
this.handleMessage(data.toString());
|
|
225
240
|
});
|
|
226
241
|
|
|
227
|
-
this.ws.on('close', () => {
|
|
242
|
+
this.ws.on('close', (code) => {
|
|
228
243
|
this.clearTimers();
|
|
244
|
+
if (code === 4001) {
|
|
245
|
+
this.events.onError?.(
|
|
246
|
+
new Error("Authentication rejected by hub. Run 'sonde enroll' to re-authenticate."),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
229
249
|
this.events.onDisconnected?.();
|
|
230
250
|
if (this.running) {
|
|
231
251
|
this.scheduleReconnect();
|
|
232
252
|
}
|
|
233
253
|
});
|
|
234
254
|
|
|
235
|
-
this.ws.on('error', (err) => {
|
|
236
|
-
this.events.onError?.(err);
|
|
255
|
+
this.ws.on('error', (err: Error & { code?: string }) => {
|
|
256
|
+
this.events.onError?.(new Error(humanizeWsError(err, this.config.hubUrl)));
|
|
237
257
|
});
|
|
238
258
|
}
|
|
239
259
|
|
|
@@ -261,6 +281,14 @@ export class AgentConnection {
|
|
|
261
281
|
case 'probe.request':
|
|
262
282
|
this.handleProbeRequest(envelope);
|
|
263
283
|
break;
|
|
284
|
+
case 'hub.update_available': {
|
|
285
|
+
const updatePayload = envelope.payload as {
|
|
286
|
+
latestVersion: string;
|
|
287
|
+
currentVersion: string;
|
|
288
|
+
};
|
|
289
|
+
this.events.onUpdateAvailable?.(updatePayload.latestVersion, updatePayload.currentVersion);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
264
292
|
default:
|
|
265
293
|
break;
|
|
266
294
|
}
|
|
@@ -288,6 +316,11 @@ export class AgentConnection {
|
|
|
288
316
|
|
|
289
317
|
const response = await this.executor.execute(request);
|
|
290
318
|
|
|
319
|
+
// Echo back requestId for concurrent probe correlation
|
|
320
|
+
if (request.requestId) {
|
|
321
|
+
response.requestId = request.requestId;
|
|
322
|
+
}
|
|
323
|
+
|
|
291
324
|
this.auditLog.log(request.probe, response.status, response.durationMs);
|
|
292
325
|
this.events.onProbeCompleted?.(request.probe, response.status, response.durationMs);
|
|
293
326
|
|
package/src/runtime/executor.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type ExecFn,
|
|
5
|
+
type Pack,
|
|
6
|
+
type PackRegistryOptions,
|
|
7
|
+
createPackRegistry,
|
|
8
|
+
packRegistry,
|
|
9
|
+
} from '@sonde/packs';
|
|
4
10
|
import type { ProbeRequest, ProbeResponse } from '@sonde/shared';
|
|
5
11
|
import { VERSION } from '../version.js';
|
|
6
12
|
import { type ScrubPattern, buildPatterns, scrubData } from './scrubber.js';
|
|
@@ -16,6 +22,13 @@ async function defaultExec(command: string, args: string[]): Promise<string> {
|
|
|
16
22
|
return stdout;
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
export interface ProbeExecutorOptions {
|
|
26
|
+
packs?: ReadonlyMap<string, Pack>;
|
|
27
|
+
exec?: ExecFn;
|
|
28
|
+
scrubPatterns?: ScrubPattern[];
|
|
29
|
+
allowUnsignedPacks?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
export class ProbeExecutor {
|
|
20
33
|
private packs: ReadonlyMap<string, Pack>;
|
|
21
34
|
private exec: ExecFn;
|