@itz4blitz/agentful 1.2.0 → 1.3.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 (59) hide show
  1. package/README.md +28 -1
  2. package/bin/cli.js +11 -1055
  3. package/bin/hooks/block-file-creation.js +271 -0
  4. package/bin/hooks/product-spec-watcher.js +151 -0
  5. package/lib/index.js +0 -11
  6. package/lib/init.js +2 -21
  7. package/lib/parallel-execution.js +235 -0
  8. package/lib/presets.js +26 -4
  9. package/package.json +4 -7
  10. package/template/.claude/agents/architect.md +2 -2
  11. package/template/.claude/agents/backend.md +17 -30
  12. package/template/.claude/agents/frontend.md +17 -39
  13. package/template/.claude/agents/orchestrator.md +63 -4
  14. package/template/.claude/agents/product-analyzer.md +1 -1
  15. package/template/.claude/agents/tester.md +16 -29
  16. package/template/.claude/commands/agentful-generate.md +221 -14
  17. package/template/.claude/commands/agentful-init.md +621 -0
  18. package/template/.claude/commands/agentful-product.md +1 -1
  19. package/template/.claude/commands/agentful-start.md +99 -1
  20. package/template/.claude/product/EXAMPLES.md +2 -2
  21. package/template/.claude/product/index.md +1 -1
  22. package/template/.claude/settings.json +22 -0
  23. package/template/.claude/skills/research/SKILL.md +432 -0
  24. package/template/CLAUDE.md +5 -6
  25. package/template/bin/hooks/architect-drift-detector.js +242 -0
  26. package/template/bin/hooks/product-spec-watcher.js +151 -0
  27. package/version.json +1 -1
  28. package/bin/hooks/post-agent.js +0 -101
  29. package/bin/hooks/post-feature.js +0 -227
  30. package/bin/hooks/pre-agent.js +0 -118
  31. package/bin/hooks/pre-feature.js +0 -138
  32. package/lib/VALIDATION_README.md +0 -455
  33. package/lib/ci/claude-action-integration.js +0 -641
  34. package/lib/ci/index.js +0 -10
  35. package/lib/core/analyzer.js +0 -497
  36. package/lib/core/cli.js +0 -141
  37. package/lib/core/detectors/conventions.js +0 -342
  38. package/lib/core/detectors/framework.js +0 -276
  39. package/lib/core/detectors/index.js +0 -15
  40. package/lib/core/detectors/language.js +0 -199
  41. package/lib/core/detectors/patterns.js +0 -356
  42. package/lib/core/generator.js +0 -626
  43. package/lib/core/index.js +0 -9
  44. package/lib/core/output-parser.js +0 -458
  45. package/lib/core/storage.js +0 -515
  46. package/lib/core/templates.js +0 -556
  47. package/lib/pipeline/cli.js +0 -423
  48. package/lib/pipeline/engine.js +0 -928
  49. package/lib/pipeline/executor.js +0 -440
  50. package/lib/pipeline/index.js +0 -33
  51. package/lib/pipeline/integrations.js +0 -559
  52. package/lib/pipeline/schemas.js +0 -288
  53. package/lib/remote/client.js +0 -361
  54. package/lib/server/auth.js +0 -270
  55. package/lib/server/client-example.js +0 -190
  56. package/lib/server/executor.js +0 -477
  57. package/lib/server/index.js +0 -494
  58. package/lib/update-helpers.js +0 -505
  59. package/lib/validation.js +0 -460
@@ -1,270 +0,0 @@
1
- /**
2
- * Authentication Middleware for Agentful Server
3
- *
4
- * Supports three authentication modes:
5
- * - tailscale: Network-level security (Tailscale handles auth)
6
- * - hmac: HMAC-SHA256 signature verification with replay protection
7
- * - none: Local-only access (127.0.0.1)
8
- *
9
- * @module server/auth
10
- */
11
-
12
- import crypto from 'crypto';
13
-
14
- /**
15
- * Replay protection window (5 minutes)
16
- */
17
- const REPLAY_WINDOW_MS = 5 * 60 * 1000;
18
-
19
- /**
20
- * Maximum size for signature cache (prevents memory exhaustion)
21
- */
22
- const MAX_SIGNATURE_CACHE_SIZE = 10000;
23
-
24
- /**
25
- * In-memory store for seen request signatures (prevents replay attacks)
26
- * In production, use Redis or similar distributed cache
27
- */
28
- const seenSignatures = new Map();
29
-
30
- /**
31
- * Clean up old signatures periodically (every 10 minutes)
32
- */
33
- setInterval(() => {
34
- const cutoff = Date.now() - REPLAY_WINDOW_MS;
35
- for (const [signature, timestamp] of seenSignatures.entries()) {
36
- if (timestamp < cutoff) {
37
- seenSignatures.delete(signature);
38
- }
39
- }
40
-
41
- // If cache is still too large after cleanup, remove oldest entries (LRU)
42
- if (seenSignatures.size > MAX_SIGNATURE_CACHE_SIZE) {
43
- const sortedEntries = Array.from(seenSignatures.entries())
44
- .sort((a, b) => a[1] - b[1]);
45
- const toRemove = sortedEntries.slice(0, seenSignatures.size - MAX_SIGNATURE_CACHE_SIZE);
46
- for (const [sig] of toRemove) {
47
- seenSignatures.delete(sig);
48
- }
49
- }
50
- }, 10 * 60 * 1000);
51
-
52
- /**
53
- * Verify HMAC signature with replay protection
54
- * @param {string} timestamp - Request timestamp
55
- * @param {string} body - Request body (JSON string)
56
- * @param {string} signature - HMAC signature from header
57
- * @param {string} secret - Shared secret
58
- * @returns {Object} Verification result { valid: boolean, error?: string }
59
- */
60
- export function verifyHMACSignature(timestamp, body, signature, secret) {
61
- // Validate timestamp format
62
- const requestTime = parseInt(timestamp, 10);
63
- if (isNaN(requestTime)) {
64
- return { valid: false, error: 'Invalid timestamp format' };
65
- }
66
-
67
- // Check timestamp is within acceptable window
68
- const now = Date.now();
69
- const timeDiff = Math.abs(now - requestTime);
70
-
71
- if (timeDiff > REPLAY_WINDOW_MS) {
72
- return {
73
- valid: false,
74
- error: `Timestamp outside acceptable window (${REPLAY_WINDOW_MS / 1000}s)`,
75
- };
76
- }
77
-
78
- // Check for replay attack
79
- if (seenSignatures.has(signature)) {
80
- return { valid: false, error: 'Signature already used (replay attack)' };
81
- }
82
-
83
- // Compute expected signature
84
- const message = timestamp + body;
85
- const expectedSignature = crypto
86
- .createHmac('sha256', secret)
87
- .update(message)
88
- .digest('hex');
89
-
90
- // Compare signatures (constant-time comparison to prevent timing attacks)
91
- const signatureBuffer = Buffer.from(signature);
92
- const expectedBuffer = Buffer.from(expectedSignature);
93
-
94
- // Ensure buffers are same length before timingSafeEqual to prevent crash
95
- if (signatureBuffer.length !== expectedBuffer.length) {
96
- return { valid: false, error: 'Invalid signature' };
97
- }
98
-
99
- const isValid = crypto.timingSafeEqual(signatureBuffer, expectedBuffer);
100
-
101
- if (!isValid) {
102
- return { valid: false, error: 'Invalid signature' };
103
- }
104
-
105
- // Store signature to prevent replay (with cache size limit)
106
- if (seenSignatures.size >= MAX_SIGNATURE_CACHE_SIZE) {
107
- // Find and remove oldest entry
108
- let oldestSig = null;
109
- let oldestTime = Infinity;
110
- for (const [sig, time] of seenSignatures.entries()) {
111
- if (time < oldestTime) {
112
- oldestTime = time;
113
- oldestSig = sig;
114
- }
115
- }
116
- if (oldestSig) {
117
- seenSignatures.delete(oldestSig);
118
- }
119
- }
120
-
121
- seenSignatures.set(signature, requestTime);
122
-
123
- return { valid: true };
124
- }
125
-
126
- /**
127
- * Minimum secret length for HMAC (256 bits / 32 bytes)
128
- */
129
- const MIN_SECRET_LENGTH = 32;
130
-
131
- /**
132
- * Create authentication middleware based on mode
133
- * @param {string} mode - Authentication mode ('tailscale', 'hmac', 'none')
134
- * @param {Object} config - Configuration options
135
- * @param {string} [config.secret] - Shared secret (required for HMAC mode)
136
- * @returns {Function} Middleware function
137
- */
138
- export function createAuthMiddleware(mode, config = {}) {
139
- switch (mode) {
140
- case 'tailscale':
141
- // Tailscale mode: No authentication needed (network-level security)
142
- return (req, res, next) => {
143
- next();
144
- };
145
-
146
- case 'hmac':
147
- if (!config.secret) {
148
- throw new Error('HMAC mode requires a secret');
149
- }
150
-
151
- // Validate secret strength
152
- if (config.secret.length < MIN_SECRET_LENGTH) {
153
- throw new Error(
154
- `HMAC secret must be at least ${MIN_SECRET_LENGTH} characters. ` +
155
- `Use: openssl rand -hex 32`
156
- );
157
- }
158
-
159
- return (req, res, next) => {
160
- // Extract headers
161
- const signature = req.headers['x-agentful-signature'];
162
- const timestamp = req.headers['x-agentful-timestamp'];
163
-
164
- // Validate headers present
165
- if (!signature || !timestamp) {
166
- res.writeHead(401, { 'Content-Type': 'application/json' });
167
- return res.end(
168
- JSON.stringify({
169
- error: 'Authentication required',
170
- message: 'Missing X-Agentful-Signature or X-Agentful-Timestamp header',
171
- })
172
- );
173
- }
174
-
175
- // Verify signature
176
- const result = verifyHMACSignature(
177
- timestamp,
178
- req.rawBody || '',
179
- signature,
180
- config.secret
181
- );
182
-
183
- if (!result.valid) {
184
- res.writeHead(401, { 'Content-Type': 'application/json' });
185
- return res.end(
186
- JSON.stringify({
187
- error: 'Authentication failed',
188
- message: result.error,
189
- })
190
- );
191
- }
192
-
193
- next();
194
- };
195
-
196
- case 'none':
197
- // None mode: Allow all connections (use with SSH tunnel for security)
198
- return (req, res, next) => {
199
- next();
200
- };
201
-
202
- default:
203
- throw new Error(`Unknown authentication mode: ${mode}`);
204
- }
205
- }
206
-
207
- /**
208
- * Maximum request body size (10MB)
209
- */
210
- const MAX_BODY_SIZE = 10 * 1024 * 1024;
211
-
212
- /**
213
- * Middleware to capture raw request body (needed for HMAC verification)
214
- * Must be used before express.json() middleware
215
- */
216
- export function captureRawBody(req, res, next) {
217
- let data = '';
218
- let size = 0;
219
-
220
- req.on('data', (chunk) => {
221
- size += chunk.length;
222
-
223
- // Check if body size exceeds limit
224
- if (size > MAX_BODY_SIZE) {
225
- req.destroy();
226
- res.writeHead(413, { 'Content-Type': 'application/json' });
227
- res.end(
228
- JSON.stringify({
229
- error: 'Payload Too Large',
230
- message: `Request body exceeds maximum size of ${MAX_BODY_SIZE / 1024 / 1024}MB`,
231
- })
232
- );
233
- return;
234
- }
235
-
236
- data += chunk.toString();
237
- });
238
-
239
- req.on('end', () => {
240
- req.rawBody = data;
241
- next();
242
- });
243
- }
244
-
245
- /**
246
- * Generate HMAC signature for a request (for client use)
247
- * @param {string} body - Request body (JSON string)
248
- * @param {string} secret - Shared secret
249
- * @returns {Object} Headers to add to request { 'X-Agentful-Signature', 'X-Agentful-Timestamp' }
250
- */
251
- export function generateHMACHeaders(body, secret) {
252
- const timestamp = Date.now().toString();
253
- const message = timestamp + body;
254
- const signature = crypto
255
- .createHmac('sha256', secret)
256
- .update(message)
257
- .digest('hex');
258
-
259
- return {
260
- 'X-Agentful-Signature': signature,
261
- 'X-Agentful-Timestamp': timestamp,
262
- };
263
- }
264
-
265
- export default {
266
- createAuthMiddleware,
267
- verifyHMACSignature,
268
- captureRawBody,
269
- generateHMACHeaders,
270
- };
@@ -1,190 +0,0 @@
1
- /**
2
- * Example Client for Agentful Server
3
- *
4
- * Demonstrates how to make authenticated requests to agentful serve
5
- *
6
- * @module server/client-example
7
- */
8
-
9
- import { generateHMACHeaders } from './auth.js';
10
-
11
- /**
12
- * Example: Trigger agent execution with HMAC authentication
13
- */
14
- async function triggerAgentWithHMAC() {
15
- const serverUrl = 'https://your-server.com:3000';
16
- const secret = 'your-shared-secret';
17
-
18
- // Request body
19
- const body = JSON.stringify({
20
- agent: 'backend',
21
- task: 'Implement user authentication API',
22
- });
23
-
24
- // Generate HMAC headers
25
- const headers = generateHMACHeaders(body, secret);
26
-
27
- // Make request
28
- const response = await fetch(`${serverUrl}/trigger`, {
29
- method: 'POST',
30
- headers: {
31
- 'Content-Type': 'application/json',
32
- ...headers,
33
- },
34
- body,
35
- });
36
-
37
- if (!response.ok) {
38
- const error = await response.json();
39
- throw new Error(`Request failed: ${error.message}`);
40
- }
41
-
42
- const result = await response.json();
43
- console.log('Execution started:', result.executionId);
44
-
45
- return result.executionId;
46
- }
47
-
48
- /**
49
- * Example: Check execution status
50
- */
51
- async function checkExecutionStatus(executionId) {
52
- const serverUrl = 'https://your-server.com:3000';
53
- const secret = 'your-shared-secret';
54
-
55
- // For GET requests, body is empty
56
- const body = '';
57
- const headers = generateHMACHeaders(body, secret);
58
-
59
- const response = await fetch(`${serverUrl}/status/${executionId}`, {
60
- method: 'GET',
61
- headers,
62
- });
63
-
64
- if (!response.ok) {
65
- const error = await response.json();
66
- throw new Error(`Request failed: ${error.message}`);
67
- }
68
-
69
- const status = await response.json();
70
- console.log('Execution status:', status.state);
71
- console.log('Duration:', status.duration, 'ms');
72
-
73
- return status;
74
- }
75
-
76
- /**
77
- * Example: Trigger and poll for completion
78
- */
79
- async function triggerAndWait() {
80
- const executionId = await triggerAgentWithHMAC();
81
-
82
- console.log('Waiting for execution to complete...');
83
-
84
- // Poll every 5 seconds
85
- while (true) {
86
- await new Promise((resolve) => setTimeout(resolve, 5000));
87
-
88
- const status = await checkExecutionStatus(executionId);
89
-
90
- if (status.state === 'completed') {
91
- console.log('Execution completed successfully!');
92
- console.log('Output:', status.output);
93
- break;
94
- }
95
-
96
- if (status.state === 'failed') {
97
- console.error('Execution failed:', status.error);
98
- break;
99
- }
100
- }
101
- }
102
-
103
- /**
104
- * Example: No authentication (localhost only)
105
- */
106
- async function triggerAgentLocalhost() {
107
- const serverUrl = 'http://localhost:3000';
108
-
109
- const response = await fetch(`${serverUrl}/trigger`, {
110
- method: 'POST',
111
- headers: {
112
- 'Content-Type': 'application/json',
113
- },
114
- body: JSON.stringify({
115
- agent: 'backend',
116
- task: 'Implement user authentication API',
117
- }),
118
- });
119
-
120
- const result = await response.json();
121
- console.log('Execution started:', result.executionId);
122
-
123
- return result.executionId;
124
- }
125
-
126
- /**
127
- * Example: Tailscale mode (no auth headers needed)
128
- */
129
- async function triggerAgentTailscale() {
130
- const serverUrl = 'http://your-tailscale-ip:3000';
131
-
132
- const response = await fetch(`${serverUrl}/trigger`, {
133
- method: 'POST',
134
- headers: {
135
- 'Content-Type': 'application/json',
136
- },
137
- body: JSON.stringify({
138
- agent: 'backend',
139
- task: 'Implement user authentication API',
140
- }),
141
- });
142
-
143
- const result = await response.json();
144
- console.log('Execution started:', result.executionId);
145
-
146
- return result.executionId;
147
- }
148
-
149
- /**
150
- * Example: List available agents
151
- */
152
- async function listAgents() {
153
- const serverUrl = 'http://localhost:3000';
154
-
155
- const response = await fetch(`${serverUrl}/agents`);
156
- const result = await response.json();
157
-
158
- console.log('Available agents:', result.agents);
159
- return result.agents;
160
- }
161
-
162
- /**
163
- * Example: List recent executions
164
- */
165
- async function listExecutions(filters = {}) {
166
- const serverUrl = 'http://localhost:3000';
167
-
168
- // Build query string
169
- const params = new URLSearchParams();
170
- if (filters.agent) params.append('agent', filters.agent);
171
- if (filters.state) params.append('state', filters.state);
172
- if (filters.limit) params.append('limit', filters.limit.toString());
173
-
174
- const response = await fetch(`${serverUrl}/executions?${params}`);
175
- const result = await response.json();
176
-
177
- console.log(`Found ${result.count} executions`);
178
- return result.executions;
179
- }
180
-
181
- // Export examples
182
- export default {
183
- triggerAgentWithHMAC,
184
- checkExecutionStatus,
185
- triggerAndWait,
186
- triggerAgentLocalhost,
187
- triggerAgentTailscale,
188
- listAgents,
189
- listExecutions,
190
- };