agentspd 1.0.0
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/LICENSE +21 -0
- package/README.md +333 -0
- package/dist/api.d.ts +198 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +171 -0
- package/dist/commands/agents.d.ts +3 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +277 -0
- package/dist/commands/audit.d.ts +3 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +181 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +226 -0
- package/dist/commands/config-cmd.d.ts +3 -0
- package/dist/commands/config-cmd.d.ts.map +1 -0
- package/dist/commands/config-cmd.js +111 -0
- package/dist/commands/index.d.ts +9 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +275 -0
- package/dist/commands/policies.d.ts +3 -0
- package/dist/commands/policies.d.ts.map +1 -0
- package/dist/commands/policies.js +362 -0
- package/dist/commands/threats.d.ts +3 -0
- package/dist/commands/threats.d.ts.map +1 -0
- package/dist/commands/threats.js +161 -0
- package/dist/commands/webhooks.d.ts +3 -0
- package/dist/commands/webhooks.d.ts.map +1 -0
- package/dist/commands/webhooks.js +222 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +328 -0
- package/dist/output.d.ts +60 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +212 -0
- package/package.json +58 -0
package/dist/api.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { getApiUrl, getAuthHeader, isAuthenticated } from './config.js';
|
|
2
|
+
class EmotosApiClient {
|
|
3
|
+
async request(method, path, body, requireAuth = true) {
|
|
4
|
+
if (requireAuth && !isAuthenticated()) {
|
|
5
|
+
return {
|
|
6
|
+
error: { message: 'Not authenticated. Run `emotos auth login` first.', code: 'NOT_AUTHENTICATED' },
|
|
7
|
+
status: 401,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const url = `${getApiUrl()}/v1${path}`;
|
|
11
|
+
const headers = {
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
...getAuthHeader(),
|
|
14
|
+
};
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
method,
|
|
18
|
+
headers,
|
|
19
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
20
|
+
});
|
|
21
|
+
const data = await response.json().catch(() => ({}));
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
return {
|
|
24
|
+
error: {
|
|
25
|
+
message: data.error || data.message || `Request failed with status ${response.status}`,
|
|
26
|
+
code: data.code,
|
|
27
|
+
},
|
|
28
|
+
status: response.status,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return { data, status: response.status };
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return {
|
|
35
|
+
error: {
|
|
36
|
+
message: error instanceof Error ? error.message : 'Network error',
|
|
37
|
+
code: 'NETWORK_ERROR',
|
|
38
|
+
},
|
|
39
|
+
status: 0,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Auth endpoints
|
|
44
|
+
async login(email, password) {
|
|
45
|
+
return this.request('POST', '/auth/login', { email, password }, false);
|
|
46
|
+
}
|
|
47
|
+
async signup(data) {
|
|
48
|
+
return this.request('POST', '/auth/signup', data, false);
|
|
49
|
+
}
|
|
50
|
+
async logout() {
|
|
51
|
+
return this.request('POST', '/auth/logout');
|
|
52
|
+
}
|
|
53
|
+
async me() {
|
|
54
|
+
return this.request('GET', '/auth/me');
|
|
55
|
+
}
|
|
56
|
+
// Agent endpoints
|
|
57
|
+
async createAgent(data) {
|
|
58
|
+
return this.request('POST', '/agents', data);
|
|
59
|
+
}
|
|
60
|
+
async listAgents(params) {
|
|
61
|
+
const query = new URLSearchParams();
|
|
62
|
+
if (params?.environment)
|
|
63
|
+
query.set('environment', params.environment);
|
|
64
|
+
if (params?.status)
|
|
65
|
+
query.set('status', params.status);
|
|
66
|
+
if (params?.limit)
|
|
67
|
+
query.set('limit', String(params.limit));
|
|
68
|
+
if (params?.offset)
|
|
69
|
+
query.set('offset', String(params.offset));
|
|
70
|
+
const queryStr = query.toString();
|
|
71
|
+
return this.request('GET', `/agents${queryStr ? `?${queryStr}` : ''}`);
|
|
72
|
+
}
|
|
73
|
+
async getAgent(agentId) {
|
|
74
|
+
return this.request('GET', `/agents/${agentId}`);
|
|
75
|
+
}
|
|
76
|
+
async issueToken(agentId, ttlSeconds) {
|
|
77
|
+
return this.request('POST', `/agents/${agentId}/token`, { ttlSeconds });
|
|
78
|
+
}
|
|
79
|
+
async revokeAgent(agentId) {
|
|
80
|
+
return this.request('POST', `/agents/${agentId}/revoke`);
|
|
81
|
+
}
|
|
82
|
+
async rotateCredentials(agentId) {
|
|
83
|
+
return this.request('POST', `/agents/${agentId}/rotate`);
|
|
84
|
+
}
|
|
85
|
+
// Policy endpoints
|
|
86
|
+
async createPolicy(data) {
|
|
87
|
+
return this.request('POST', '/policies', data);
|
|
88
|
+
}
|
|
89
|
+
async listPolicies() {
|
|
90
|
+
return this.request('GET', '/policies');
|
|
91
|
+
}
|
|
92
|
+
async getPolicy(policyId) {
|
|
93
|
+
return this.request('GET', `/policies/${policyId}`);
|
|
94
|
+
}
|
|
95
|
+
async updatePolicy(policyId, content) {
|
|
96
|
+
return this.request('PUT', `/policies/${policyId}`, { content });
|
|
97
|
+
}
|
|
98
|
+
async validatePolicy(content) {
|
|
99
|
+
return this.request('POST', '/policies/validate', { content });
|
|
100
|
+
}
|
|
101
|
+
async activatePolicy(policyId) {
|
|
102
|
+
return this.request('POST', `/policies/${policyId}/activate`);
|
|
103
|
+
}
|
|
104
|
+
async deactivatePolicy(policyId) {
|
|
105
|
+
return this.request('POST', `/policies/${policyId}/deactivate`);
|
|
106
|
+
}
|
|
107
|
+
// Audit endpoints
|
|
108
|
+
async queryAuditEvents(params) {
|
|
109
|
+
const query = new URLSearchParams();
|
|
110
|
+
if (params?.agentId)
|
|
111
|
+
query.set('agentId', params.agentId);
|
|
112
|
+
if (params?.eventType)
|
|
113
|
+
query.set('eventType', params.eventType);
|
|
114
|
+
if (params?.startTime)
|
|
115
|
+
query.set('startTime', params.startTime);
|
|
116
|
+
if (params?.endTime)
|
|
117
|
+
query.set('endTime', params.endTime);
|
|
118
|
+
if (params?.limit)
|
|
119
|
+
query.set('limit', String(params.limit));
|
|
120
|
+
const queryStr = query.toString();
|
|
121
|
+
return this.request('GET', `/audit/events${queryStr ? `?${queryStr}` : ''}`);
|
|
122
|
+
}
|
|
123
|
+
async getAuditEvent(eventId) {
|
|
124
|
+
return this.request('GET', `/audit/events/${eventId}`);
|
|
125
|
+
}
|
|
126
|
+
// Threat endpoints
|
|
127
|
+
async listThreats(params) {
|
|
128
|
+
const query = new URLSearchParams();
|
|
129
|
+
if (params?.agentId)
|
|
130
|
+
query.set('agentId', params.agentId);
|
|
131
|
+
if (params?.severity)
|
|
132
|
+
query.set('severity', params.severity);
|
|
133
|
+
if (params?.status)
|
|
134
|
+
query.set('status', params.status);
|
|
135
|
+
if (params?.limit)
|
|
136
|
+
query.set('limit', String(params.limit));
|
|
137
|
+
const queryStr = query.toString();
|
|
138
|
+
return this.request('GET', `/threats${queryStr ? `?${queryStr}` : ''}`);
|
|
139
|
+
}
|
|
140
|
+
async resolveThreat(threatId) {
|
|
141
|
+
return this.request('POST', `/threats/${threatId}/resolve`);
|
|
142
|
+
}
|
|
143
|
+
// Webhook endpoints
|
|
144
|
+
async createWebhook(data) {
|
|
145
|
+
return this.request('POST', '/webhooks', data);
|
|
146
|
+
}
|
|
147
|
+
async listWebhooks() {
|
|
148
|
+
return this.request('GET', '/webhooks');
|
|
149
|
+
}
|
|
150
|
+
async deleteWebhook(webhookId) {
|
|
151
|
+
return this.request('DELETE', `/webhooks/${webhookId}`);
|
|
152
|
+
}
|
|
153
|
+
async testWebhook(webhookId) {
|
|
154
|
+
return this.request('POST', `/webhooks/${webhookId}/test`);
|
|
155
|
+
}
|
|
156
|
+
// Organization endpoints
|
|
157
|
+
async getOrganization() {
|
|
158
|
+
return this.request('GET', '/org');
|
|
159
|
+
}
|
|
160
|
+
async updateOrganization(data) {
|
|
161
|
+
return this.request('PUT', '/org', data);
|
|
162
|
+
}
|
|
163
|
+
async rotateOrgApiKey() {
|
|
164
|
+
return this.request('POST', '/org/rotate-key');
|
|
165
|
+
}
|
|
166
|
+
// Metrics endpoints
|
|
167
|
+
async getMetrics() {
|
|
168
|
+
return this.request('GET', '/metrics');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export const api = new EmotosApiClient();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,mBAAmB,IAAI,OAAO,CA8S7C"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { api } from '../api.js';
|
|
5
|
+
import * as output from '../output.js';
|
|
6
|
+
import { getConfig } from '../config.js';
|
|
7
|
+
export function createAgentsCommand() {
|
|
8
|
+
const agents = new Command('agents')
|
|
9
|
+
.alias('agent')
|
|
10
|
+
.description('Manage AI agents');
|
|
11
|
+
agents
|
|
12
|
+
.command('create')
|
|
13
|
+
.alias('register')
|
|
14
|
+
.description('Register a new AI agent')
|
|
15
|
+
.option('-n, --name <name>', 'Agent name')
|
|
16
|
+
.option('-d, --description <desc>', 'Agent description')
|
|
17
|
+
.option('-e, --environment <env>', 'Environment (development, staging, production)')
|
|
18
|
+
.option('-p, --policy <policyId>', 'Policy ID to assign')
|
|
19
|
+
.option('--json', 'Output as JSON')
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
let name = options.name;
|
|
22
|
+
let description = options.description;
|
|
23
|
+
let environment = options.environment;
|
|
24
|
+
let policyId = options.policy;
|
|
25
|
+
// Interactive prompts
|
|
26
|
+
if (!name) {
|
|
27
|
+
const answers = await inquirer.prompt([
|
|
28
|
+
{
|
|
29
|
+
type: 'input',
|
|
30
|
+
name: 'name',
|
|
31
|
+
message: 'Agent name:',
|
|
32
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
name = answers.name;
|
|
36
|
+
}
|
|
37
|
+
if (!description) {
|
|
38
|
+
const answers = await inquirer.prompt([
|
|
39
|
+
{
|
|
40
|
+
type: 'input',
|
|
41
|
+
name: 'description',
|
|
42
|
+
message: 'Description (optional):',
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
description = answers.description || undefined;
|
|
46
|
+
}
|
|
47
|
+
if (!environment) {
|
|
48
|
+
const answers = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: 'list',
|
|
51
|
+
name: 'environment',
|
|
52
|
+
message: 'Environment:',
|
|
53
|
+
choices: ['development', 'staging', 'production'],
|
|
54
|
+
default: getConfig().defaultEnvironment,
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
environment = answers.environment;
|
|
58
|
+
}
|
|
59
|
+
const spinner = ora('Registering agent...').start();
|
|
60
|
+
const result = await api.createAgent({
|
|
61
|
+
name,
|
|
62
|
+
description,
|
|
63
|
+
environment,
|
|
64
|
+
policyId,
|
|
65
|
+
});
|
|
66
|
+
if (result.error) {
|
|
67
|
+
spinner.fail('Failed to register agent');
|
|
68
|
+
output.error(result.error.message);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
spinner.succeed('Agent registered successfully!');
|
|
72
|
+
if (options.json) {
|
|
73
|
+
output.printJson(result.data);
|
|
74
|
+
}
|
|
75
|
+
else if (result.data) {
|
|
76
|
+
console.log();
|
|
77
|
+
output.printKeyValue([
|
|
78
|
+
['Agent ID', result.data.id],
|
|
79
|
+
['Name', result.data.name],
|
|
80
|
+
['Status', result.data.status],
|
|
81
|
+
['Environment', result.data.environment],
|
|
82
|
+
['Reputation', `${result.data.reputationScore}/100`],
|
|
83
|
+
]);
|
|
84
|
+
// Show the API key if returned
|
|
85
|
+
const apiKey = result.data.metadata?.apiKey;
|
|
86
|
+
if (apiKey && typeof apiKey === 'string') {
|
|
87
|
+
output.printSecret('API Key', apiKey);
|
|
88
|
+
}
|
|
89
|
+
console.log();
|
|
90
|
+
output.info('Next steps:');
|
|
91
|
+
console.log(` 1. Issue a token: ${output.highlight(`emotos agents token ${result.data.id}`)}`);
|
|
92
|
+
console.log(` 2. Connect to proxy: ${output.highlight('wss://proxy.emotos.ai/v1/mcp')}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
agents
|
|
96
|
+
.command('list')
|
|
97
|
+
.alias('ls')
|
|
98
|
+
.description('List all agents')
|
|
99
|
+
.option('-e, --environment <env>', 'Filter by environment')
|
|
100
|
+
.option('-s, --status <status>', 'Filter by status (active, suspended, revoked)')
|
|
101
|
+
.option('-l, --limit <n>', 'Limit results', '20')
|
|
102
|
+
.option('--json', 'Output as JSON')
|
|
103
|
+
.action(async (options) => {
|
|
104
|
+
const spinner = ora('Fetching agents...').start();
|
|
105
|
+
const result = await api.listAgents({
|
|
106
|
+
environment: options.environment,
|
|
107
|
+
status: options.status,
|
|
108
|
+
limit: Number.parseInt(options.limit, 10),
|
|
109
|
+
});
|
|
110
|
+
if (result.error) {
|
|
111
|
+
spinner.fail('Failed to fetch agents');
|
|
112
|
+
output.error(result.error.message);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
spinner.stop();
|
|
116
|
+
if (options.json) {
|
|
117
|
+
output.printJson(result.data);
|
|
118
|
+
}
|
|
119
|
+
else if (result.data) {
|
|
120
|
+
if (result.data.items.length === 0) {
|
|
121
|
+
output.info('No agents found');
|
|
122
|
+
output.info('Create one with: emotos agents create');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
output.heading(`Agents (${result.data.total} total)`);
|
|
126
|
+
output.printAgentTable(result.data.items);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
agents
|
|
130
|
+
.command('get <agentId>')
|
|
131
|
+
.alias('show')
|
|
132
|
+
.description('Get agent details')
|
|
133
|
+
.option('--json', 'Output as JSON')
|
|
134
|
+
.action(async (agentId, options) => {
|
|
135
|
+
const spinner = ora('Fetching agent...').start();
|
|
136
|
+
const result = await api.getAgent(agentId);
|
|
137
|
+
if (result.error) {
|
|
138
|
+
spinner.fail('Failed to fetch agent');
|
|
139
|
+
output.error(result.error.message);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
spinner.stop();
|
|
143
|
+
if (options.json) {
|
|
144
|
+
output.printJson(result.data);
|
|
145
|
+
}
|
|
146
|
+
else if (result.data) {
|
|
147
|
+
output.heading(`Agent: ${result.data.name}`);
|
|
148
|
+
output.printKeyValue([
|
|
149
|
+
['ID', result.data.id],
|
|
150
|
+
['Name', result.data.name],
|
|
151
|
+
['Description', result.data.description],
|
|
152
|
+
['Status', output.formatStatus(result.data.status)],
|
|
153
|
+
['Environment', output.formatEnvironment(result.data.environment)],
|
|
154
|
+
['Reputation', output.formatReputation(result.data.reputationScore)],
|
|
155
|
+
['Policy ID', result.data.policyId],
|
|
156
|
+
['Created', output.formatDate(result.data.createdAt)],
|
|
157
|
+
['Updated', result.data.updatedAt ? output.formatDate(result.data.updatedAt) : undefined],
|
|
158
|
+
]);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
agents
|
|
162
|
+
.command('token <agentId>')
|
|
163
|
+
.alias('issue-token')
|
|
164
|
+
.description('Issue a JWT token for an agent')
|
|
165
|
+
.option('-t, --ttl <seconds>', 'Token TTL in seconds', '3600')
|
|
166
|
+
.option('--json', 'Output as JSON')
|
|
167
|
+
.action(async (agentId, options) => {
|
|
168
|
+
const spinner = ora('Issuing token...').start();
|
|
169
|
+
const result = await api.issueToken(agentId, Number.parseInt(options.ttl, 10));
|
|
170
|
+
if (result.error) {
|
|
171
|
+
spinner.fail('Failed to issue token');
|
|
172
|
+
output.error(result.error.message);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
spinner.succeed('Token issued successfully');
|
|
176
|
+
if (options.json) {
|
|
177
|
+
output.printJson(result.data);
|
|
178
|
+
}
|
|
179
|
+
else if (result.data) {
|
|
180
|
+
output.printSecret('JWT Token', result.data.token);
|
|
181
|
+
output.info(`Expires: ${output.formatDate(result.data.expiresAt)}`);
|
|
182
|
+
console.log();
|
|
183
|
+
output.info('Usage:');
|
|
184
|
+
console.log(' Connect to MCP proxy with this token:');
|
|
185
|
+
console.log();
|
|
186
|
+
console.log(output.highlight(' const ws = new WebSocket("wss://proxy.emotos.ai/v1/mcp", {'));
|
|
187
|
+
console.log(output.highlight(' headers: { "Authorization": "Bearer <token>" }'));
|
|
188
|
+
console.log(output.highlight(' });'));
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
agents
|
|
192
|
+
.command('revoke <agentId>')
|
|
193
|
+
.description('Revoke an agent and all its tokens')
|
|
194
|
+
.option('-f, --force', 'Skip confirmation')
|
|
195
|
+
.action(async (agentId, options) => {
|
|
196
|
+
if (!options.force) {
|
|
197
|
+
const answers = await inquirer.prompt([
|
|
198
|
+
{
|
|
199
|
+
type: 'confirm',
|
|
200
|
+
name: 'confirm',
|
|
201
|
+
message: `Are you sure you want to revoke agent ${agentId}? This cannot be undone.`,
|
|
202
|
+
default: false,
|
|
203
|
+
},
|
|
204
|
+
]);
|
|
205
|
+
if (!answers.confirm) {
|
|
206
|
+
output.info('Operation cancelled');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const spinner = ora('Revoking agent...').start();
|
|
211
|
+
const result = await api.revokeAgent(agentId);
|
|
212
|
+
if (result.error) {
|
|
213
|
+
spinner.fail('Failed to revoke agent');
|
|
214
|
+
output.error(result.error.message);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
spinner.succeed('Agent revoked successfully');
|
|
218
|
+
if (result.data) {
|
|
219
|
+
output.info(`Revoked at: ${output.formatDate(result.data.revokedAt)}`);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
agents
|
|
223
|
+
.command('rotate <agentId>')
|
|
224
|
+
.description('Rotate agent credentials')
|
|
225
|
+
.option('--json', 'Output as JSON')
|
|
226
|
+
.action(async (agentId, options) => {
|
|
227
|
+
const spinner = ora('Rotating credentials...').start();
|
|
228
|
+
const result = await api.rotateCredentials(agentId);
|
|
229
|
+
if (result.error) {
|
|
230
|
+
spinner.fail('Failed to rotate credentials');
|
|
231
|
+
output.error(result.error.message);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
spinner.succeed('Credentials rotated successfully');
|
|
235
|
+
if (options.json) {
|
|
236
|
+
output.printJson(result.data);
|
|
237
|
+
}
|
|
238
|
+
else if (result.data) {
|
|
239
|
+
output.printSecret('New API Key', result.data.apiKey);
|
|
240
|
+
output.printSecret('New JWT Token', result.data.token);
|
|
241
|
+
output.info(`Token expires: ${output.formatDate(result.data.expiresAt)}`);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
agents
|
|
245
|
+
.command('monitor <agentId>')
|
|
246
|
+
.description('Monitor agent activity in real-time')
|
|
247
|
+
.option('-l, --limit <n>', 'Number of events to show', '10')
|
|
248
|
+
.action(async (agentId, options) => {
|
|
249
|
+
output.heading(`Monitoring agent: ${agentId}`);
|
|
250
|
+
output.info('Press Ctrl+C to stop');
|
|
251
|
+
console.log();
|
|
252
|
+
const fetchEvents = async () => {
|
|
253
|
+
const result = await api.queryAuditEvents({
|
|
254
|
+
agentId,
|
|
255
|
+
limit: Number.parseInt(options.limit, 10),
|
|
256
|
+
});
|
|
257
|
+
if (result.data && result.data.items.length > 0) {
|
|
258
|
+
output.printAuditTable(result.data.items);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
output.dim('No recent events');
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
// Initial fetch
|
|
265
|
+
await fetchEvents();
|
|
266
|
+
// Poll every 5 seconds
|
|
267
|
+
const interval = setInterval(fetchEvents, 5000);
|
|
268
|
+
// Handle Ctrl+C
|
|
269
|
+
process.on('SIGINT', () => {
|
|
270
|
+
clearInterval(interval);
|
|
271
|
+
console.log();
|
|
272
|
+
output.info('Monitoring stopped');
|
|
273
|
+
process.exit(0);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
return agents;
|
|
277
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,kBAAkB,IAAI,OAAO,CAwM5C"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { api } from '../api.js';
|
|
4
|
+
import * as output from '../output.js';
|
|
5
|
+
export function createAuditCommand() {
|
|
6
|
+
const audit = new Command('audit')
|
|
7
|
+
.description('Query audit logs');
|
|
8
|
+
audit
|
|
9
|
+
.command('events')
|
|
10
|
+
.alias('list')
|
|
11
|
+
.description('Query audit events')
|
|
12
|
+
.option('-a, --agent <agentId>', 'Filter by agent ID')
|
|
13
|
+
.option('-t, --type <eventType>', 'Filter by event type')
|
|
14
|
+
.option('--start <date>', 'Start time (ISO 8601)')
|
|
15
|
+
.option('--end <date>', 'End time (ISO 8601)')
|
|
16
|
+
.option('-l, --limit <n>', 'Limit results', '50')
|
|
17
|
+
.option('--json', 'Output as JSON')
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
const spinner = ora('Fetching audit events...').start();
|
|
20
|
+
const result = await api.queryAuditEvents({
|
|
21
|
+
agentId: options.agent,
|
|
22
|
+
eventType: options.type,
|
|
23
|
+
startTime: options.start,
|
|
24
|
+
endTime: options.end,
|
|
25
|
+
limit: Number.parseInt(options.limit, 10),
|
|
26
|
+
});
|
|
27
|
+
if (result.error) {
|
|
28
|
+
spinner.fail('Failed to fetch audit events');
|
|
29
|
+
output.error(result.error.message);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
spinner.stop();
|
|
33
|
+
if (options.json) {
|
|
34
|
+
output.printJson(result.data);
|
|
35
|
+
}
|
|
36
|
+
else if (result.data) {
|
|
37
|
+
if (result.data.items.length === 0) {
|
|
38
|
+
output.info('No audit events found');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
output.heading(`Audit Events (${result.data.total} total)`);
|
|
42
|
+
output.printAuditTable(result.data.items);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
audit
|
|
46
|
+
.command('get <eventId>')
|
|
47
|
+
.alias('show')
|
|
48
|
+
.description('Get audit event details')
|
|
49
|
+
.option('--json', 'Output as JSON')
|
|
50
|
+
.action(async (eventId, options) => {
|
|
51
|
+
const spinner = ora('Fetching audit event...').start();
|
|
52
|
+
const result = await api.getAuditEvent(eventId);
|
|
53
|
+
if (result.error) {
|
|
54
|
+
spinner.fail('Failed to fetch audit event');
|
|
55
|
+
output.error(result.error.message);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
spinner.stop();
|
|
59
|
+
if (options.json) {
|
|
60
|
+
output.printJson(result.data);
|
|
61
|
+
}
|
|
62
|
+
else if (result.data) {
|
|
63
|
+
output.heading('Audit Event');
|
|
64
|
+
output.printKeyValue([
|
|
65
|
+
['ID', result.data.id],
|
|
66
|
+
['Agent ID', result.data.agentId],
|
|
67
|
+
['Event Type', result.data.eventType],
|
|
68
|
+
['Timestamp', output.formatDate(result.data.timestamp)],
|
|
69
|
+
['Signature', result.data.signature ? 'Verified' : 'Not signed'],
|
|
70
|
+
]);
|
|
71
|
+
if (result.data.requestData) {
|
|
72
|
+
console.log();
|
|
73
|
+
output.heading('Request Data');
|
|
74
|
+
output.printJson(result.data.requestData);
|
|
75
|
+
}
|
|
76
|
+
if (result.data.responseData) {
|
|
77
|
+
console.log();
|
|
78
|
+
output.heading('Response Data');
|
|
79
|
+
output.printJson(result.data.responseData);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
audit
|
|
84
|
+
.command('export')
|
|
85
|
+
.description('Export audit events to file')
|
|
86
|
+
.option('-a, --agent <agentId>', 'Filter by agent ID')
|
|
87
|
+
.option('--start <date>', 'Start time (ISO 8601)')
|
|
88
|
+
.option('--end <date>', 'End time (ISO 8601)')
|
|
89
|
+
.option('-o, --output <path>', 'Output file path', 'audit-export.json')
|
|
90
|
+
.option('-f, --format <format>', 'Output format (json, csv)', 'json')
|
|
91
|
+
.action(async (options) => {
|
|
92
|
+
const spinner = ora('Exporting audit events...').start();
|
|
93
|
+
let allEvents = [];
|
|
94
|
+
let cursor;
|
|
95
|
+
let hasMore = true;
|
|
96
|
+
while (hasMore) {
|
|
97
|
+
const result = await api.queryAuditEvents({
|
|
98
|
+
agentId: options.agent,
|
|
99
|
+
startTime: options.start,
|
|
100
|
+
endTime: options.end,
|
|
101
|
+
limit: 100,
|
|
102
|
+
});
|
|
103
|
+
if (result.error) {
|
|
104
|
+
spinner.fail('Failed to export audit events');
|
|
105
|
+
output.error(result.error.message);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (result.data) {
|
|
109
|
+
allEvents = [...allEvents, ...result.data.items];
|
|
110
|
+
hasMore = result.data.hasMore;
|
|
111
|
+
cursor = result.data.cursor;
|
|
112
|
+
spinner.text = `Exporting audit events... (${allEvents.length} events)`;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
hasMore = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Write to file
|
|
119
|
+
const fs = await import('node:fs');
|
|
120
|
+
if (options.format === 'csv') {
|
|
121
|
+
const events = allEvents;
|
|
122
|
+
const csv = [
|
|
123
|
+
'id,agentId,eventType,timestamp',
|
|
124
|
+
...events.map(e => `${e.id},${e.agentId},${e.eventType},${e.timestamp}`),
|
|
125
|
+
].join('\n');
|
|
126
|
+
fs.writeFileSync(options.output, csv);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
fs.writeFileSync(options.output, JSON.stringify(allEvents, null, 2));
|
|
130
|
+
}
|
|
131
|
+
spinner.succeed(`Exported ${allEvents.length} events to ${options.output}`);
|
|
132
|
+
});
|
|
133
|
+
audit
|
|
134
|
+
.command('stats')
|
|
135
|
+
.description('Show audit statistics')
|
|
136
|
+
.option('--start <date>', 'Start time (ISO 8601)')
|
|
137
|
+
.option('--end <date>', 'End time (ISO 8601)')
|
|
138
|
+
.action(async (options) => {
|
|
139
|
+
const spinner = ora('Calculating statistics...').start();
|
|
140
|
+
const result = await api.queryAuditEvents({
|
|
141
|
+
startTime: options.start,
|
|
142
|
+
endTime: options.end,
|
|
143
|
+
limit: 1000,
|
|
144
|
+
});
|
|
145
|
+
if (result.error) {
|
|
146
|
+
spinner.fail('Failed to fetch audit events');
|
|
147
|
+
output.error(result.error.message);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
spinner.stop();
|
|
151
|
+
if (result.data) {
|
|
152
|
+
const events = result.data.items;
|
|
153
|
+
const byType = {};
|
|
154
|
+
const byAgent = {};
|
|
155
|
+
for (const event of events) {
|
|
156
|
+
byType[event.eventType] = (byType[event.eventType] || 0) + 1;
|
|
157
|
+
byAgent[event.agentId] = (byAgent[event.agentId] || 0) + 1;
|
|
158
|
+
}
|
|
159
|
+
output.heading('Audit Statistics');
|
|
160
|
+
output.printKeyValue([
|
|
161
|
+
['Total Events', result.data.total],
|
|
162
|
+
['Unique Event Types', Object.keys(byType).length],
|
|
163
|
+
['Unique Agents', Object.keys(byAgent).length],
|
|
164
|
+
]);
|
|
165
|
+
console.log();
|
|
166
|
+
output.heading('Events by Type');
|
|
167
|
+
const typeRows = Object.entries(byType)
|
|
168
|
+
.sort((a, b) => b[1] - a[1])
|
|
169
|
+
.map(([type, count]) => [type, String(count)]);
|
|
170
|
+
output.printTable(['Event Type', 'Count'], typeRows);
|
|
171
|
+
console.log();
|
|
172
|
+
output.heading('Events by Agent');
|
|
173
|
+
const agentRows = Object.entries(byAgent)
|
|
174
|
+
.sort((a, b) => b[1] - a[1])
|
|
175
|
+
.slice(0, 10)
|
|
176
|
+
.map(([agent, count]) => [agent, String(count)]);
|
|
177
|
+
output.printTable(['Agent ID', 'Count'], agentRows);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return audit;
|
|
181
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,iBAAiB,IAAI,OAAO,CA6P3C"}
|