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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +333 -0
  3. package/dist/api.d.ts +198 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/api.js +171 -0
  6. package/dist/commands/agents.d.ts +3 -0
  7. package/dist/commands/agents.d.ts.map +1 -0
  8. package/dist/commands/agents.js +277 -0
  9. package/dist/commands/audit.d.ts +3 -0
  10. package/dist/commands/audit.d.ts.map +1 -0
  11. package/dist/commands/audit.js +181 -0
  12. package/dist/commands/auth.d.ts +3 -0
  13. package/dist/commands/auth.d.ts.map +1 -0
  14. package/dist/commands/auth.js +226 -0
  15. package/dist/commands/config-cmd.d.ts +3 -0
  16. package/dist/commands/config-cmd.d.ts.map +1 -0
  17. package/dist/commands/config-cmd.js +111 -0
  18. package/dist/commands/index.d.ts +9 -0
  19. package/dist/commands/index.d.ts.map +1 -0
  20. package/dist/commands/index.js +8 -0
  21. package/dist/commands/init.d.ts +3 -0
  22. package/dist/commands/init.d.ts.map +1 -0
  23. package/dist/commands/init.js +275 -0
  24. package/dist/commands/policies.d.ts +3 -0
  25. package/dist/commands/policies.d.ts.map +1 -0
  26. package/dist/commands/policies.js +362 -0
  27. package/dist/commands/threats.d.ts +3 -0
  28. package/dist/commands/threats.d.ts.map +1 -0
  29. package/dist/commands/threats.js +161 -0
  30. package/dist/commands/webhooks.d.ts +3 -0
  31. package/dist/commands/webhooks.d.ts.map +1 -0
  32. package/dist/commands/webhooks.js +222 -0
  33. package/dist/config.d.ts +24 -0
  34. package/dist/config.d.ts.map +1 -0
  35. package/dist/config.js +58 -0
  36. package/dist/index.d.ts +3 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +328 -0
  39. package/dist/output.d.ts +60 -0
  40. package/dist/output.d.ts.map +1 -0
  41. package/dist/output.js +212 -0
  42. package/package.json +58 -0
@@ -0,0 +1,362 @@
1
+ import { Command } from 'commander';
2
+ import inquirer from 'inquirer';
3
+ import ora from 'ora';
4
+ import * as fs from 'node:fs';
5
+ import { api } from '../api.js';
6
+ import * as output from '../output.js';
7
+ const DEFAULT_POLICY_TEMPLATE = `# Emotos Security Policy
8
+ # Version 1.0
9
+
10
+ version: "1.0"
11
+ name: "my-policy"
12
+ description: "My security policy"
13
+
14
+ settings:
15
+ default_action: deny
16
+ require_identity: true
17
+
18
+ # Tool permissions
19
+ tools:
20
+ # Allow read operations
21
+ - pattern: "read_*"
22
+ action: allow
23
+
24
+ # Block dangerous operations
25
+ - pattern: "exec_*"
26
+ action: deny
27
+ reason: "Shell execution not permitted"
28
+
29
+ # Allow with rate limiting
30
+ - pattern: "web_*"
31
+ action: allow
32
+ rate_limit:
33
+ requests_per_minute: 10
34
+
35
+ # Prompt injection protection
36
+ prompt_injection:
37
+ enabled: true
38
+ action: block
39
+
40
+ # Data exfiltration prevention
41
+ exfiltration:
42
+ enabled: true
43
+ block_patterns:
44
+ - name: "aws_keys"
45
+ pattern: "AKIA[0-9A-Z]{16}"
46
+ - name: "private_keys"
47
+ pattern: "-----BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY-----"
48
+ `;
49
+ export function createPoliciesCommand() {
50
+ const policies = new Command('policies')
51
+ .alias('policy')
52
+ .description('Manage security policies');
53
+ policies
54
+ .command('create')
55
+ .description('Create a new security policy')
56
+ .option('-n, --name <name>', 'Policy name')
57
+ .option('-d, --description <desc>', 'Policy description')
58
+ .option('-f, --file <path>', 'Read policy content from file')
59
+ .option('-t, --template', 'Start with a template')
60
+ .option('--json', 'Output as JSON')
61
+ .action(async (options) => {
62
+ let name = options.name;
63
+ let description = options.description;
64
+ let content;
65
+ // Get policy content
66
+ if (options.file) {
67
+ if (!fs.existsSync(options.file)) {
68
+ output.error(`File not found: ${options.file}`);
69
+ return;
70
+ }
71
+ content = fs.readFileSync(options.file, 'utf-8');
72
+ }
73
+ else if (options.template) {
74
+ content = DEFAULT_POLICY_TEMPLATE;
75
+ }
76
+ else {
77
+ output.info('Enter policy YAML (end with Ctrl+D on a new line):');
78
+ console.log();
79
+ const answers = await inquirer.prompt([
80
+ {
81
+ type: 'editor',
82
+ name: 'content',
83
+ message: 'Policy content:',
84
+ default: DEFAULT_POLICY_TEMPLATE,
85
+ },
86
+ ]);
87
+ content = answers.content;
88
+ }
89
+ // Get name and description if not provided
90
+ if (!name) {
91
+ const answers = await inquirer.prompt([
92
+ {
93
+ type: 'input',
94
+ name: 'name',
95
+ message: 'Policy name:',
96
+ validate: (input) => input.length > 0 || 'Name is required',
97
+ },
98
+ ]);
99
+ name = answers.name;
100
+ }
101
+ if (!description) {
102
+ const answers = await inquirer.prompt([
103
+ {
104
+ type: 'input',
105
+ name: 'description',
106
+ message: 'Description (optional):',
107
+ },
108
+ ]);
109
+ description = answers.description || undefined;
110
+ }
111
+ // Validate first
112
+ const validateSpinner = ora('Validating policy...').start();
113
+ const validateResult = await api.validatePolicy(content);
114
+ if (validateResult.error || (validateResult.data && !validateResult.data.valid)) {
115
+ validateSpinner.fail('Policy validation failed');
116
+ if (validateResult.data?.errors) {
117
+ for (const err of validateResult.data.errors) {
118
+ output.error(err);
119
+ }
120
+ }
121
+ else if (validateResult.error) {
122
+ output.error(validateResult.error.message);
123
+ }
124
+ return;
125
+ }
126
+ validateSpinner.succeed('Policy validated');
127
+ // Create policy
128
+ const spinner = ora('Creating policy...').start();
129
+ const result = await api.createPolicy({ name, description, content });
130
+ if (result.error) {
131
+ spinner.fail('Failed to create policy');
132
+ output.error(result.error.message);
133
+ return;
134
+ }
135
+ spinner.succeed('Policy created successfully!');
136
+ if (options.json) {
137
+ output.printJson(result.data);
138
+ }
139
+ else if (result.data) {
140
+ console.log();
141
+ output.printKeyValue([
142
+ ['Policy ID', result.data.id],
143
+ ['Name', result.data.name],
144
+ ['Version', result.data.version],
145
+ ['Active', result.data.isActive ? 'Yes' : 'No'],
146
+ ['Created', output.formatDate(result.data.createdAt)],
147
+ ]);
148
+ console.log();
149
+ output.info('Next steps:');
150
+ console.log(` 1. Activate policy: ${output.highlight(`emotos policies activate ${result.data.id}`)}`);
151
+ console.log(` 2. Assign to agent: ${output.highlight(`emotos agents create --policy ${result.data.id}`)}`);
152
+ }
153
+ });
154
+ policies
155
+ .command('list')
156
+ .alias('ls')
157
+ .description('List all policies')
158
+ .option('--json', 'Output as JSON')
159
+ .action(async (options) => {
160
+ const spinner = ora('Fetching policies...').start();
161
+ const result = await api.listPolicies();
162
+ if (result.error) {
163
+ spinner.fail('Failed to fetch policies');
164
+ output.error(result.error.message);
165
+ return;
166
+ }
167
+ spinner.stop();
168
+ if (options.json) {
169
+ output.printJson(result.data);
170
+ }
171
+ else if (result.data) {
172
+ if (result.data.policies.length === 0) {
173
+ output.info('No policies found');
174
+ output.info('Create one with: emotos policies create');
175
+ return;
176
+ }
177
+ output.heading('Security Policies');
178
+ output.printPolicyTable(result.data.policies);
179
+ }
180
+ });
181
+ policies
182
+ .command('get <policyId>')
183
+ .alias('show')
184
+ .description('Get policy details')
185
+ .option('--json', 'Output as JSON')
186
+ .option('--content', 'Show policy content only')
187
+ .action(async (policyId, options) => {
188
+ const spinner = ora('Fetching policy...').start();
189
+ const result = await api.getPolicy(policyId);
190
+ if (result.error) {
191
+ spinner.fail('Failed to fetch policy');
192
+ output.error(result.error.message);
193
+ return;
194
+ }
195
+ spinner.stop();
196
+ if (options.content && result.data) {
197
+ console.log(result.data.content);
198
+ return;
199
+ }
200
+ if (options.json) {
201
+ output.printJson(result.data);
202
+ }
203
+ else if (result.data) {
204
+ output.heading(`Policy: ${result.data.name}`);
205
+ output.printKeyValue([
206
+ ['ID', result.data.id],
207
+ ['Name', result.data.name],
208
+ ['Description', result.data.description],
209
+ ['Version', result.data.version],
210
+ ['Active', result.data.isActive ? 'Yes' : 'No'],
211
+ ['Created', output.formatDate(result.data.createdAt)],
212
+ ['Updated', result.data.updatedAt ? output.formatDate(result.data.updatedAt) : undefined],
213
+ ]);
214
+ console.log();
215
+ output.heading('Content');
216
+ console.log(result.data.content);
217
+ }
218
+ });
219
+ policies
220
+ .command('update <policyId>')
221
+ .description('Update a policy')
222
+ .option('-f, --file <path>', 'Read policy content from file')
223
+ .option('--json', 'Output as JSON')
224
+ .action(async (policyId, options) => {
225
+ let content;
226
+ if (options.file) {
227
+ if (!fs.existsSync(options.file)) {
228
+ output.error(`File not found: ${options.file}`);
229
+ return;
230
+ }
231
+ content = fs.readFileSync(options.file, 'utf-8');
232
+ }
233
+ else {
234
+ // Fetch current content
235
+ const current = await api.getPolicy(policyId);
236
+ if (current.error) {
237
+ output.error(current.error.message);
238
+ return;
239
+ }
240
+ const answers = await inquirer.prompt([
241
+ {
242
+ type: 'editor',
243
+ name: 'content',
244
+ message: 'Policy content:',
245
+ default: current.data?.content,
246
+ },
247
+ ]);
248
+ content = answers.content;
249
+ }
250
+ // Validate first
251
+ const validateSpinner = ora('Validating policy...').start();
252
+ const validateResult = await api.validatePolicy(content);
253
+ if (validateResult.error || (validateResult.data && !validateResult.data.valid)) {
254
+ validateSpinner.fail('Policy validation failed');
255
+ if (validateResult.data?.errors) {
256
+ for (const err of validateResult.data.errors) {
257
+ output.error(err);
258
+ }
259
+ }
260
+ return;
261
+ }
262
+ validateSpinner.succeed('Policy validated');
263
+ // Update policy
264
+ const spinner = ora('Updating policy...').start();
265
+ const result = await api.updatePolicy(policyId, content);
266
+ if (result.error) {
267
+ spinner.fail('Failed to update policy');
268
+ output.error(result.error.message);
269
+ return;
270
+ }
271
+ spinner.succeed('Policy updated successfully!');
272
+ if (options.json) {
273
+ output.printJson(result.data);
274
+ }
275
+ else if (result.data) {
276
+ output.printKeyValue([
277
+ ['Version', result.data.version],
278
+ ['Updated', result.data.updatedAt ? output.formatDate(result.data.updatedAt) : 'Now'],
279
+ ]);
280
+ }
281
+ });
282
+ policies
283
+ .command('validate')
284
+ .description('Validate a policy file')
285
+ .argument('<file>', 'Policy file to validate')
286
+ .action(async (file) => {
287
+ if (!fs.existsSync(file)) {
288
+ output.error(`File not found: ${file}`);
289
+ return;
290
+ }
291
+ const content = fs.readFileSync(file, 'utf-8');
292
+ const spinner = ora('Validating policy...').start();
293
+ const result = await api.validatePolicy(content);
294
+ if (result.error) {
295
+ spinner.fail('Validation failed');
296
+ output.error(result.error.message);
297
+ return;
298
+ }
299
+ if (result.data?.valid) {
300
+ spinner.succeed('Policy is valid!');
301
+ }
302
+ else {
303
+ spinner.fail('Policy is invalid');
304
+ if (result.data?.errors) {
305
+ for (const err of result.data.errors) {
306
+ output.error(err);
307
+ }
308
+ }
309
+ }
310
+ });
311
+ policies
312
+ .command('activate <policyId>')
313
+ .description('Activate a policy')
314
+ .action(async (policyId) => {
315
+ const spinner = ora('Activating policy...').start();
316
+ const result = await api.activatePolicy(policyId);
317
+ if (result.error) {
318
+ spinner.fail('Failed to activate policy');
319
+ output.error(result.error.message);
320
+ return;
321
+ }
322
+ spinner.succeed('Policy activated!');
323
+ });
324
+ policies
325
+ .command('deactivate <policyId>')
326
+ .description('Deactivate a policy')
327
+ .action(async (policyId) => {
328
+ const spinner = ora('Deactivating policy...').start();
329
+ const result = await api.deactivatePolicy(policyId);
330
+ if (result.error) {
331
+ spinner.fail('Failed to deactivate policy');
332
+ output.error(result.error.message);
333
+ return;
334
+ }
335
+ spinner.succeed('Policy deactivated');
336
+ });
337
+ policies
338
+ .command('init')
339
+ .description('Initialize a new policy file from template')
340
+ .option('-o, --output <path>', 'Output file path', 'emotos-policy.yaml')
341
+ .action(async (options) => {
342
+ if (fs.existsSync(options.output)) {
343
+ const answers = await inquirer.prompt([
344
+ {
345
+ type: 'confirm',
346
+ name: 'overwrite',
347
+ message: `File ${options.output} already exists. Overwrite?`,
348
+ default: false,
349
+ },
350
+ ]);
351
+ if (!answers.overwrite) {
352
+ output.info('Operation cancelled');
353
+ return;
354
+ }
355
+ }
356
+ fs.writeFileSync(options.output, DEFAULT_POLICY_TEMPLATE);
357
+ output.success(`Policy template created: ${options.output}`);
358
+ output.info('Edit this file to customize your security policy');
359
+ output.info(`Then upload with: emotos policies create --file ${options.output}`);
360
+ });
361
+ return policies;
362
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createThreatsCommand(): Command;
3
+ //# sourceMappingURL=threats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threats.d.ts","sourceRoot":"","sources":["../../src/commands/threats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,oBAAoB,IAAI,OAAO,CAoL9C"}
@@ -0,0 +1,161 @@
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
+ export function createThreatsCommand() {
7
+ const threats = new Command('threats')
8
+ .alias('threat')
9
+ .description('Monitor and respond to security threats');
10
+ threats
11
+ .command('list')
12
+ .alias('ls')
13
+ .description('List detected threats')
14
+ .option('-a, --agent <agentId>', 'Filter by agent ID')
15
+ .option('-s, --severity <severity>', 'Filter by severity (low, medium, high, critical)')
16
+ .option('--status <status>', 'Filter by status (detected, blocked, escalated, resolved)')
17
+ .option('-l, --limit <n>', 'Limit results', '20')
18
+ .option('--json', 'Output as JSON')
19
+ .action(async (options) => {
20
+ const spinner = ora('Fetching threats...').start();
21
+ const result = await api.listThreats({
22
+ agentId: options.agent,
23
+ severity: options.severity,
24
+ status: options.status,
25
+ limit: Number.parseInt(options.limit, 10),
26
+ });
27
+ if (result.error) {
28
+ spinner.fail('Failed to fetch threats');
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.success('No threats detected!');
39
+ return;
40
+ }
41
+ output.heading(`Security Threats (${result.data.total} total)`);
42
+ output.printThreatTable(result.data.items);
43
+ }
44
+ });
45
+ threats
46
+ .command('resolve <threatId>')
47
+ .description('Mark a threat as resolved')
48
+ .option('-f, --force', 'Skip confirmation')
49
+ .action(async (threatId, options) => {
50
+ if (!options.force) {
51
+ const answers = await inquirer.prompt([
52
+ {
53
+ type: 'confirm',
54
+ name: 'confirm',
55
+ message: `Mark threat ${threatId} as resolved?`,
56
+ default: true,
57
+ },
58
+ ]);
59
+ if (!answers.confirm) {
60
+ output.info('Operation cancelled');
61
+ return;
62
+ }
63
+ }
64
+ const spinner = ora('Resolving threat...').start();
65
+ const result = await api.resolveThreat(threatId);
66
+ if (result.error) {
67
+ spinner.fail('Failed to resolve threat');
68
+ output.error(result.error.message);
69
+ return;
70
+ }
71
+ spinner.succeed('Threat resolved');
72
+ if (result.data) {
73
+ output.info(`Resolved at: ${output.formatDate(result.data.resolvedAt || new Date().toISOString())}`);
74
+ }
75
+ });
76
+ threats
77
+ .command('watch')
78
+ .description('Watch for threats in real-time')
79
+ .option('-a, --agent <agentId>', 'Filter by agent ID')
80
+ .option('-s, --severity <severity>', 'Minimum severity to show')
81
+ .action(async (options) => {
82
+ output.heading('Threat Monitor');
83
+ output.info('Press Ctrl+C to stop');
84
+ console.log();
85
+ let lastSeen = null;
86
+ const fetchThreats = async () => {
87
+ const result = await api.listThreats({
88
+ agentId: options.agent,
89
+ severity: options.severity,
90
+ limit: 10,
91
+ });
92
+ if (result.data && result.data.items.length > 0) {
93
+ const newThreats = lastSeen
94
+ ? result.data.items.filter(t => t.id !== lastSeen && new Date(t.createdAt) > new Date(lastSeen))
95
+ : result.data.items;
96
+ if (newThreats.length > 0) {
97
+ for (const threat of newThreats) {
98
+ const severityStr = output.formatSeverity(threat.severity);
99
+ const statusStr = output.formatStatus(threat.status);
100
+ console.log(`[${output.formatDate(threat.createdAt)}] ${severityStr} ${threat.threatType} - Agent: ${threat.agentId} - Status: ${statusStr}`);
101
+ }
102
+ lastSeen = result.data.items[0].id;
103
+ }
104
+ }
105
+ };
106
+ // Initial fetch
107
+ await fetchThreats();
108
+ // Poll every 3 seconds
109
+ const interval = setInterval(fetchThreats, 3000);
110
+ // Handle Ctrl+C
111
+ process.on('SIGINT', () => {
112
+ clearInterval(interval);
113
+ console.log();
114
+ output.info('Monitoring stopped');
115
+ process.exit(0);
116
+ });
117
+ });
118
+ threats
119
+ .command('stats')
120
+ .description('Show threat statistics')
121
+ .action(async () => {
122
+ const spinner = ora('Fetching threat statistics...').start();
123
+ const result = await api.listThreats({ limit: 1000 });
124
+ if (result.error) {
125
+ spinner.fail('Failed to fetch threats');
126
+ output.error(result.error.message);
127
+ return;
128
+ }
129
+ spinner.stop();
130
+ if (result.data) {
131
+ const threats = result.data.items;
132
+ const bySeverity = {};
133
+ const byStatus = {};
134
+ const byType = {};
135
+ for (const threat of threats) {
136
+ bySeverity[threat.severity] = (bySeverity[threat.severity] || 0) + 1;
137
+ byStatus[threat.status] = (byStatus[threat.status] || 0) + 1;
138
+ byType[threat.threatType] = (byType[threat.threatType] || 0) + 1;
139
+ }
140
+ output.heading('Threat Statistics');
141
+ output.printKeyValue([
142
+ ['Total Threats', result.data.total],
143
+ ['Blocked', byStatus['blocked'] || 0],
144
+ ['Resolved', byStatus['resolved'] || 0],
145
+ ]);
146
+ console.log();
147
+ output.heading('By Severity');
148
+ const severityRows = Object.entries(bySeverity)
149
+ .map(([sev, count]) => [output.formatSeverity(sev), String(count)]);
150
+ output.printTable(['Severity', 'Count'], severityRows);
151
+ console.log();
152
+ output.heading('By Type');
153
+ const typeRows = Object.entries(byType)
154
+ .sort((a, b) => b[1] - a[1])
155
+ .slice(0, 10)
156
+ .map(([type, count]) => [type, String(count)]);
157
+ output.printTable(['Threat Type', 'Count'], typeRows);
158
+ }
159
+ });
160
+ return threats;
161
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createWebhooksCommand(): Command;
3
+ //# sourceMappingURL=webhooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/commands/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoBpC,wBAAgB,qBAAqB,IAAI,OAAO,CAkO/C"}