@walker-di/fortalece-ai-sdk 0.2.0 → 0.3.1
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/dist/ci/__tests__/hmac.test.d.ts +2 -0
- package/dist/ci/__tests__/hmac.test.d.ts.map +1 -0
- package/dist/ci/__tests__/hmac.test.js +81 -0
- package/dist/ci/cli.d.ts +12 -0
- package/dist/ci/cli.d.ts.map +1 -0
- package/dist/ci/cli.js +157 -0
- package/dist/ci/hmac.d.ts +23 -0
- package/dist/ci/hmac.d.ts.map +1 -0
- package/dist/ci/hmac.js +40 -0
- package/dist/ci/index.d.ts +28 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +28 -0
- package/dist/ci/notify.d.ts +42 -0
- package/dist/ci/notify.d.ts.map +1 -0
- package/dist/ci/notify.js +141 -0
- package/dist/ci/types.d.ts +75 -0
- package/dist/ci/types.d.ts.map +1 -0
- package/dist/ci/types.js +5 -0
- package/dist/fortalece-ai-sdk.iife.js +9 -6
- package/dist/fortalece-ai-sdk.js +1505 -1497
- package/dist/fortalece-ai-sdk.umd.cjs +9 -6
- package/package.json +17 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmac.test.d.ts","sourceRoot":"","sources":["../../../src/ci/__tests__/hmac.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createHmac } from 'crypto';
|
|
3
|
+
import { signWebhookBody, verifySignature } from '../hmac.js';
|
|
4
|
+
/**
|
|
5
|
+
* 📌 HMAC Signing Tests
|
|
6
|
+
* Tests for GitHub-compatible webhook signature generation and verification
|
|
7
|
+
*/
|
|
8
|
+
describe('signWebhookBody', () => {
|
|
9
|
+
const secret = 'test-webhook-secret';
|
|
10
|
+
it('should generate sha256 prefixed signature for string body', () => {
|
|
11
|
+
const body = '{"event":"test"}';
|
|
12
|
+
const signature = signWebhookBody(secret, body);
|
|
13
|
+
expect(signature).toMatch(/^sha256=[a-f0-9]{64}$/);
|
|
14
|
+
});
|
|
15
|
+
it('should generate sha256 prefixed signature for object body', () => {
|
|
16
|
+
const body = { event: 'test', status: 'success' };
|
|
17
|
+
const signature = signWebhookBody(secret, body);
|
|
18
|
+
expect(signature).toMatch(/^sha256=[a-f0-9]{64}$/);
|
|
19
|
+
});
|
|
20
|
+
it('should generate same signature for string and equivalent JSON.stringify', () => {
|
|
21
|
+
const obj = { event: 'test' };
|
|
22
|
+
const str = JSON.stringify(obj);
|
|
23
|
+
const sigFromObj = signWebhookBody(secret, obj);
|
|
24
|
+
const sigFromStr = signWebhookBody(secret, str);
|
|
25
|
+
expect(sigFromObj).toBe(sigFromStr);
|
|
26
|
+
});
|
|
27
|
+
it('should generate correct GitHub-compatible signature', () => {
|
|
28
|
+
const body = '{"test":"data"}';
|
|
29
|
+
// Manually compute expected signature
|
|
30
|
+
const expected = 'sha256=' + createHmac('sha256', secret)
|
|
31
|
+
.update(body, 'utf8')
|
|
32
|
+
.digest('hex');
|
|
33
|
+
const actual = signWebhookBody(secret, body);
|
|
34
|
+
expect(actual).toBe(expected);
|
|
35
|
+
});
|
|
36
|
+
it('should generate different signatures for different secrets', () => {
|
|
37
|
+
const body = '{"test":"data"}';
|
|
38
|
+
const sig1 = signWebhookBody('secret1', body);
|
|
39
|
+
const sig2 = signWebhookBody('secret2', body);
|
|
40
|
+
expect(sig1).not.toBe(sig2);
|
|
41
|
+
});
|
|
42
|
+
it('should generate different signatures for different bodies', () => {
|
|
43
|
+
const sig1 = signWebhookBody(secret, '{"a":1}');
|
|
44
|
+
const sig2 = signWebhookBody(secret, '{"a":2}');
|
|
45
|
+
expect(sig1).not.toBe(sig2);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('verifySignature', () => {
|
|
49
|
+
const secret = 'verification-test-secret';
|
|
50
|
+
const body = '{"event":"build_success","status":"success"}';
|
|
51
|
+
it('should verify valid signature', () => {
|
|
52
|
+
const signature = signWebhookBody(secret, body);
|
|
53
|
+
const isValid = verifySignature(secret, body, signature);
|
|
54
|
+
expect(isValid).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
it('should reject invalid signature', () => {
|
|
57
|
+
const isValid = verifySignature(secret, body, 'sha256=invalid');
|
|
58
|
+
expect(isValid).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
it('should reject signature with wrong secret', () => {
|
|
61
|
+
const signatureWithWrongSecret = signWebhookBody('wrong-secret', body);
|
|
62
|
+
const isValid = verifySignature(secret, body, signatureWithWrongSecret);
|
|
63
|
+
expect(isValid).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
it('should reject signature for modified body', () => {
|
|
66
|
+
const signature = signWebhookBody(secret, body);
|
|
67
|
+
const modifiedBody = '{"event":"build_success","status":"failed"}';
|
|
68
|
+
const isValid = verifySignature(secret, modifiedBody, signature);
|
|
69
|
+
expect(isValid).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
it('should work with object bodies', () => {
|
|
72
|
+
const objBody = { event: 'test', data: { nested: true } };
|
|
73
|
+
const signature = signWebhookBody(secret, objBody);
|
|
74
|
+
const isValid = verifySignature(secret, objBody, signature);
|
|
75
|
+
expect(isValid).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
it('should reject signature with different length', () => {
|
|
78
|
+
const isValid = verifySignature(secret, body, 'sha256=abc');
|
|
79
|
+
expect(isValid).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
});
|
package/dist/ci/cli.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fortalece AI CI CLI
|
|
4
|
+
* Command-line interface for sending CI notifications from GitHub Actions
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx @walker-di/fortalece-ai-sdk notify-ci --status building
|
|
8
|
+
* npx @walker-di/fortalece-ai-sdk notify-ci --status success --preview-url https://preview.example.com
|
|
9
|
+
* npx @walker-di/fortalece-ai-sdk notify-ci --status failed --error-message "Build failed"
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/ci/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
|
package/dist/ci/cli.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fortalece AI CI CLI
|
|
4
|
+
* Command-line interface for sending CI notifications from GitHub Actions
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx @walker-di/fortalece-ai-sdk notify-ci --status building
|
|
8
|
+
* npx @walker-di/fortalece-ai-sdk notify-ci --status success --preview-url https://preview.example.com
|
|
9
|
+
* npx @walker-di/fortalece-ai-sdk notify-ci --status failed --error-message "Build failed"
|
|
10
|
+
*/
|
|
11
|
+
import { notifyCi } from './notify.js';
|
|
12
|
+
function parseArgs(args) {
|
|
13
|
+
const result = {};
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
const arg = args[i];
|
|
16
|
+
const nextArg = args[i + 1];
|
|
17
|
+
switch (arg) {
|
|
18
|
+
case '--status':
|
|
19
|
+
case '-s':
|
|
20
|
+
result.status = nextArg;
|
|
21
|
+
i++;
|
|
22
|
+
break;
|
|
23
|
+
case '--webhook-url':
|
|
24
|
+
case '-u':
|
|
25
|
+
result.webhookUrl = nextArg;
|
|
26
|
+
i++;
|
|
27
|
+
break;
|
|
28
|
+
case '--webhook-secret':
|
|
29
|
+
case '-k':
|
|
30
|
+
result.webhookSecret = nextArg;
|
|
31
|
+
i++;
|
|
32
|
+
break;
|
|
33
|
+
case '--preview-url':
|
|
34
|
+
case '-p':
|
|
35
|
+
result.previewUrl = nextArg;
|
|
36
|
+
i++;
|
|
37
|
+
break;
|
|
38
|
+
case '--logs-url':
|
|
39
|
+
case '-l':
|
|
40
|
+
result.logsUrl = nextArg;
|
|
41
|
+
i++;
|
|
42
|
+
break;
|
|
43
|
+
case '--error-message':
|
|
44
|
+
case '-e':
|
|
45
|
+
result.errorMessage = nextArg;
|
|
46
|
+
i++;
|
|
47
|
+
break;
|
|
48
|
+
case '--work-item-id':
|
|
49
|
+
case '-w':
|
|
50
|
+
result.workItemId = nextArg;
|
|
51
|
+
i++;
|
|
52
|
+
break;
|
|
53
|
+
case '--service-name':
|
|
54
|
+
result.serviceName = nextArg;
|
|
55
|
+
i++;
|
|
56
|
+
break;
|
|
57
|
+
case '--help':
|
|
58
|
+
case '-h':
|
|
59
|
+
result.help = true;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
function printHelp() {
|
|
66
|
+
console.log(`
|
|
67
|
+
Fortalece AI CI Notification CLI
|
|
68
|
+
|
|
69
|
+
Usage:
|
|
70
|
+
npx @walker-di/fortalece-ai-sdk notify-ci [options]
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
-s, --status <status> Build status: building, success, or failed (required)
|
|
74
|
+
-u, --webhook-url <url> Webhook URL (or set FORTALECE_WEBHOOK_URL env var)
|
|
75
|
+
-k, --webhook-secret <key> Webhook secret (or set FORTALECE_WEBHOOK_SECRET env var)
|
|
76
|
+
-p, --preview-url <url> Preview/deployment URL (optional)
|
|
77
|
+
-l, --logs-url <url> Logs URL (optional)
|
|
78
|
+
-e, --error-message <msg> Error message for failures (optional)
|
|
79
|
+
-w, --work-item-id <id> Link to a specific work item (optional)
|
|
80
|
+
--service-name <name> Service name (optional)
|
|
81
|
+
-h, --help Show this help message
|
|
82
|
+
|
|
83
|
+
Environment Variables:
|
|
84
|
+
FORTALECE_WEBHOOK_URL Webhook URL (alternative to --webhook-url)
|
|
85
|
+
FORTALECE_WEBHOOK_SECRET Webhook secret (alternative to --webhook-secret)
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
# Notify build started
|
|
89
|
+
npx @walker-di/fortalece-ai-sdk notify-ci --status building
|
|
90
|
+
|
|
91
|
+
# Notify build success with preview URL
|
|
92
|
+
npx @walker-di/fortalece-ai-sdk notify-ci --status success --preview-url https://preview.example.com
|
|
93
|
+
|
|
94
|
+
# Notify build failure with error message
|
|
95
|
+
npx @walker-di/fortalece-ai-sdk notify-ci --status failed --error-message "Tests failed"
|
|
96
|
+
|
|
97
|
+
# Using environment variables (recommended for secrets)
|
|
98
|
+
FORTALECE_WEBHOOK_URL="\${{ secrets.FORTALECE_WEBHOOK_URL }}" \\
|
|
99
|
+
FORTALECE_WEBHOOK_SECRET="\${{ secrets.FORTALECE_WEBHOOK_SECRET }}" \\
|
|
100
|
+
npx @walker-di/fortalece-ai-sdk notify-ci --status success
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
async function main() {
|
|
104
|
+
const args = process.argv.slice(2);
|
|
105
|
+
// Check if first argument is 'notify-ci' (when called via npx)
|
|
106
|
+
if (args[0] === 'notify-ci') {
|
|
107
|
+
args.shift();
|
|
108
|
+
}
|
|
109
|
+
const parsed = parseArgs(args);
|
|
110
|
+
if (parsed.help) {
|
|
111
|
+
printHelp();
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
// Get webhook URL and secret from args or environment
|
|
115
|
+
const webhookUrl = parsed.webhookUrl || process.env.FORTALECE_WEBHOOK_URL;
|
|
116
|
+
const webhookSecret = parsed.webhookSecret || process.env.FORTALECE_WEBHOOK_SECRET;
|
|
117
|
+
if (!webhookUrl) {
|
|
118
|
+
console.error('Error: Missing webhook URL. Use --webhook-url or set FORTALECE_WEBHOOK_URL');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
if (!webhookSecret) {
|
|
122
|
+
console.error('Error: Missing webhook secret. Use --webhook-secret or set FORTALECE_WEBHOOK_SECRET');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
if (!parsed.status) {
|
|
126
|
+
console.error('Error: Missing --status. Must be: building, success, or failed');
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
if (!['building', 'success', 'failed'].includes(parsed.status)) {
|
|
130
|
+
console.error('Error: Invalid status. Must be: building, success, or failed');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
console.log(`📤 Sending CI notification: ${parsed.status}...`);
|
|
134
|
+
const result = await notifyCi({
|
|
135
|
+
webhookUrl,
|
|
136
|
+
webhookSecret,
|
|
137
|
+
status: parsed.status,
|
|
138
|
+
previewUrl: parsed.previewUrl,
|
|
139
|
+
logsUrl: parsed.logsUrl,
|
|
140
|
+
errorMessage: parsed.errorMessage,
|
|
141
|
+
workItemId: parsed.workItemId,
|
|
142
|
+
serviceName: parsed.serviceName,
|
|
143
|
+
});
|
|
144
|
+
if (result.success) {
|
|
145
|
+
console.log('✅ CI notification sent successfully');
|
|
146
|
+
process.exit(0);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.error(`❌ Failed to send CI notification: ${result.error}`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Run CLI
|
|
154
|
+
main().catch((error) => {
|
|
155
|
+
console.error('Fatal error:', error);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortalece AI CI - HMAC Signing
|
|
3
|
+
* GitHub-compatible HMAC-SHA256 signature generation
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Sign a webhook body using HMAC-SHA256
|
|
7
|
+
* Compatible with GitHub's x-hub-signature-256 format
|
|
8
|
+
*
|
|
9
|
+
* @param secret - The webhook secret
|
|
10
|
+
* @param body - The request body (string or object)
|
|
11
|
+
* @returns The signature in format "sha256=<hex>"
|
|
12
|
+
*/
|
|
13
|
+
export declare function signWebhookBody(secret: string, body: string | object): string;
|
|
14
|
+
/**
|
|
15
|
+
* Verify a webhook signature
|
|
16
|
+
*
|
|
17
|
+
* @param secret - The webhook secret
|
|
18
|
+
* @param body - The request body (string or object)
|
|
19
|
+
* @param signature - The signature to verify (format: "sha256=<hex>")
|
|
20
|
+
* @returns True if the signature is valid
|
|
21
|
+
*/
|
|
22
|
+
export declare function verifySignature(secret: string, body: string | object, signature: string): boolean;
|
|
23
|
+
//# sourceMappingURL=hmac.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmac.d.ts","sourceRoot":"","sources":["../../src/ci/hmac.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAQ7E;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAcjG"}
|
package/dist/ci/hmac.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortalece AI CI - HMAC Signing
|
|
3
|
+
* GitHub-compatible HMAC-SHA256 signature generation
|
|
4
|
+
*/
|
|
5
|
+
import { createHmac } from 'crypto';
|
|
6
|
+
/**
|
|
7
|
+
* Sign a webhook body using HMAC-SHA256
|
|
8
|
+
* Compatible with GitHub's x-hub-signature-256 format
|
|
9
|
+
*
|
|
10
|
+
* @param secret - The webhook secret
|
|
11
|
+
* @param body - The request body (string or object)
|
|
12
|
+
* @returns The signature in format "sha256=<hex>"
|
|
13
|
+
*/
|
|
14
|
+
export function signWebhookBody(secret, body) {
|
|
15
|
+
const payload = typeof body === 'string' ? body : JSON.stringify(body);
|
|
16
|
+
const signature = createHmac('sha256', secret)
|
|
17
|
+
.update(payload, 'utf8')
|
|
18
|
+
.digest('hex');
|
|
19
|
+
return `sha256=${signature}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Verify a webhook signature
|
|
23
|
+
*
|
|
24
|
+
* @param secret - The webhook secret
|
|
25
|
+
* @param body - The request body (string or object)
|
|
26
|
+
* @param signature - The signature to verify (format: "sha256=<hex>")
|
|
27
|
+
* @returns True if the signature is valid
|
|
28
|
+
*/
|
|
29
|
+
export function verifySignature(secret, body, signature) {
|
|
30
|
+
const expectedSignature = signWebhookBody(secret, body);
|
|
31
|
+
if (signature.length !== expectedSignature.length) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
// Timing-safe comparison
|
|
35
|
+
let result = 0;
|
|
36
|
+
for (let i = 0; i < signature.length; i++) {
|
|
37
|
+
result |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);
|
|
38
|
+
}
|
|
39
|
+
return result === 0;
|
|
40
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortalece AI SDK - CI Module
|
|
3
|
+
*
|
|
4
|
+
* Node-safe entrypoint for GitHub Actions CI integration.
|
|
5
|
+
* No browser/Svelte dependencies.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { notifyCi, notifyBuildSuccess } from '@walker-di/fortalece-ai-sdk/ci';
|
|
10
|
+
*
|
|
11
|
+
* // Notify build start
|
|
12
|
+
* await notifyCi({
|
|
13
|
+
* webhookUrl: process.env.FORTALECE_WEBHOOK_URL,
|
|
14
|
+
* webhookSecret: process.env.FORTALECE_WEBHOOK_SECRET,
|
|
15
|
+
* status: 'building',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Notify build success with preview URL
|
|
19
|
+
* await notifyBuildSuccess(
|
|
20
|
+
* { webhookUrl, webhookSecret },
|
|
21
|
+
* { previewUrl: 'https://preview.example.com' }
|
|
22
|
+
* );
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export { signWebhookBody, verifySignature } from './hmac.js';
|
|
26
|
+
export { notifyCiEvent, notifyCi, notifyBuildStart, notifyBuildSuccess, notifyBuildFailure, } from './notify.js';
|
|
27
|
+
export type { CiEventType, CiStatus, CiEventPayload, CiNotifyConfig, CiNotifyResponse, NotifyOptions, } from './types.js';
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ci/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG7D,OAAO,EACL,aAAa,EACb,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/ci/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortalece AI SDK - CI Module
|
|
3
|
+
*
|
|
4
|
+
* Node-safe entrypoint for GitHub Actions CI integration.
|
|
5
|
+
* No browser/Svelte dependencies.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { notifyCi, notifyBuildSuccess } from '@walker-di/fortalece-ai-sdk/ci';
|
|
10
|
+
*
|
|
11
|
+
* // Notify build start
|
|
12
|
+
* await notifyCi({
|
|
13
|
+
* webhookUrl: process.env.FORTALECE_WEBHOOK_URL,
|
|
14
|
+
* webhookSecret: process.env.FORTALECE_WEBHOOK_SECRET,
|
|
15
|
+
* status: 'building',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Notify build success with preview URL
|
|
19
|
+
* await notifyBuildSuccess(
|
|
20
|
+
* { webhookUrl, webhookSecret },
|
|
21
|
+
* { previewUrl: 'https://preview.example.com' }
|
|
22
|
+
* );
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
// Export HMAC utilities
|
|
26
|
+
export { signWebhookBody, verifySignature } from './hmac.js';
|
|
27
|
+
// Export notification functions
|
|
28
|
+
export { notifyCiEvent, notifyCi, notifyBuildStart, notifyBuildSuccess, notifyBuildFailure, } from './notify.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortalece AI CI - Notify Functions
|
|
3
|
+
* Send CI events to Fortalece webhooks from GitHub Actions
|
|
4
|
+
*/
|
|
5
|
+
import type { CiEventPayload, CiNotifyConfig, CiNotifyResponse, NotifyOptions } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Send a CI event to Fortalece webhook
|
|
8
|
+
*
|
|
9
|
+
* @param config - Webhook configuration
|
|
10
|
+
* @param payload - Event payload
|
|
11
|
+
* @returns Response from the webhook
|
|
12
|
+
*/
|
|
13
|
+
export declare function notifyCiEvent(config: CiNotifyConfig, payload: CiEventPayload): Promise<CiNotifyResponse>;
|
|
14
|
+
/**
|
|
15
|
+
* Notify Fortalece about a CI event using GitHub Actions environment
|
|
16
|
+
* Automatically reads environment variables for context
|
|
17
|
+
*
|
|
18
|
+
* @param options - Notification options
|
|
19
|
+
* @returns Response from the webhook
|
|
20
|
+
*/
|
|
21
|
+
export declare function notifyCi(options: NotifyOptions): Promise<CiNotifyResponse>;
|
|
22
|
+
/**
|
|
23
|
+
* Convenience function: Notify build started
|
|
24
|
+
*/
|
|
25
|
+
export declare function notifyBuildStart(config: CiNotifyConfig): Promise<CiNotifyResponse>;
|
|
26
|
+
/**
|
|
27
|
+
* Convenience function: Notify build success
|
|
28
|
+
*/
|
|
29
|
+
export declare function notifyBuildSuccess(config: CiNotifyConfig, options?: {
|
|
30
|
+
previewUrl?: string;
|
|
31
|
+
logsUrl?: string;
|
|
32
|
+
workItemId?: string;
|
|
33
|
+
}): Promise<CiNotifyResponse>;
|
|
34
|
+
/**
|
|
35
|
+
* Convenience function: Notify build failure
|
|
36
|
+
*/
|
|
37
|
+
export declare function notifyBuildFailure(config: CiNotifyConfig, options?: {
|
|
38
|
+
errorMessage?: string;
|
|
39
|
+
logsUrl?: string;
|
|
40
|
+
workItemId?: string;
|
|
41
|
+
}): Promise<CiNotifyResponse>;
|
|
42
|
+
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/ci/notify.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EACd,gBAAgB,EAEhB,aAAa,EACd,MAAM,YAAY,CAAC;AAqCpB;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAwC3B;AAED;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkChF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAExF;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACvE,OAAO,CAAC,gBAAgB,CAAC,CAQ3B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,OAAO,CAAC,gBAAgB,CAAC,CAQ3B"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortalece AI CI - Notify Functions
|
|
3
|
+
* Send CI events to Fortalece webhooks from GitHub Actions
|
|
4
|
+
*/
|
|
5
|
+
import { signWebhookBody } from './hmac.js';
|
|
6
|
+
/**
|
|
7
|
+
* Get GitHub Actions environment variables
|
|
8
|
+
*/
|
|
9
|
+
function getGitHubEnv() {
|
|
10
|
+
return {
|
|
11
|
+
repository: process.env.GITHUB_REPOSITORY || '',
|
|
12
|
+
branch: process.env.GITHUB_REF_NAME || process.env.GITHUB_HEAD_REF || '',
|
|
13
|
+
commit_sha: process.env.GITHUB_SHA || '',
|
|
14
|
+
github_run_id: process.env.GITHUB_RUN_ID,
|
|
15
|
+
github_run_number: process.env.GITHUB_RUN_NUMBER,
|
|
16
|
+
github_workflow: process.env.GITHUB_WORKFLOW,
|
|
17
|
+
github_actor: process.env.GITHUB_ACTOR,
|
|
18
|
+
build_url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID
|
|
19
|
+
? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
|
|
20
|
+
: undefined,
|
|
21
|
+
build_id: process.env.GITHUB_RUN_ID,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Map status to event type
|
|
26
|
+
*/
|
|
27
|
+
function statusToEventType(status) {
|
|
28
|
+
switch (status) {
|
|
29
|
+
case 'building':
|
|
30
|
+
return 'build_start';
|
|
31
|
+
case 'success':
|
|
32
|
+
return 'build_success';
|
|
33
|
+
case 'failed':
|
|
34
|
+
return 'build_failure';
|
|
35
|
+
default:
|
|
36
|
+
return 'build_start';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Send a CI event to Fortalece webhook
|
|
41
|
+
*
|
|
42
|
+
* @param config - Webhook configuration
|
|
43
|
+
* @param payload - Event payload
|
|
44
|
+
* @returns Response from the webhook
|
|
45
|
+
*/
|
|
46
|
+
export async function notifyCiEvent(config, payload) {
|
|
47
|
+
const { webhookUrl, webhookSecret } = config;
|
|
48
|
+
if (!webhookUrl) {
|
|
49
|
+
return { success: false, error: 'Missing webhook URL' };
|
|
50
|
+
}
|
|
51
|
+
if (!webhookSecret) {
|
|
52
|
+
return { success: false, error: 'Missing webhook secret' };
|
|
53
|
+
}
|
|
54
|
+
const body = JSON.stringify(payload);
|
|
55
|
+
const signature = signWebhookBody(webhookSecret, body);
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(webhookUrl, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
'X-Hub-Signature-256': signature,
|
|
62
|
+
'X-Fortalece-Event': `ci:${payload.event}`,
|
|
63
|
+
},
|
|
64
|
+
body,
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const errorData = await response.json().catch(() => ({}));
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: errorData.error || `HTTP ${response.status}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return { success: true };
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Notify Fortalece about a CI event using GitHub Actions environment
|
|
84
|
+
* Automatically reads environment variables for context
|
|
85
|
+
*
|
|
86
|
+
* @param options - Notification options
|
|
87
|
+
* @returns Response from the webhook
|
|
88
|
+
*/
|
|
89
|
+
export async function notifyCi(options) {
|
|
90
|
+
const { webhookUrl, webhookSecret, status, previewUrl, logsUrl, errorMessage, workItemId, serviceName, } = options;
|
|
91
|
+
const githubEnv = getGitHubEnv();
|
|
92
|
+
const payload = {
|
|
93
|
+
event: statusToEventType(status),
|
|
94
|
+
status,
|
|
95
|
+
repository: githubEnv.repository || 'unknown/unknown',
|
|
96
|
+
branch: githubEnv.branch || 'unknown',
|
|
97
|
+
commit_sha: githubEnv.commit_sha || 'unknown',
|
|
98
|
+
work_item_id: workItemId,
|
|
99
|
+
build_id: githubEnv.build_id,
|
|
100
|
+
build_url: githubEnv.build_url,
|
|
101
|
+
logs_url: logsUrl,
|
|
102
|
+
preview_url: previewUrl,
|
|
103
|
+
service_name: serviceName,
|
|
104
|
+
error_message: errorMessage,
|
|
105
|
+
github_run_id: githubEnv.github_run_id,
|
|
106
|
+
github_run_number: githubEnv.github_run_number,
|
|
107
|
+
github_workflow: githubEnv.github_workflow,
|
|
108
|
+
github_actor: githubEnv.github_actor,
|
|
109
|
+
};
|
|
110
|
+
return notifyCiEvent({ webhookUrl, webhookSecret }, payload);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Convenience function: Notify build started
|
|
114
|
+
*/
|
|
115
|
+
export async function notifyBuildStart(config) {
|
|
116
|
+
return notifyCi({ ...config, status: 'building' });
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Convenience function: Notify build success
|
|
120
|
+
*/
|
|
121
|
+
export async function notifyBuildSuccess(config, options) {
|
|
122
|
+
return notifyCi({
|
|
123
|
+
...config,
|
|
124
|
+
status: 'success',
|
|
125
|
+
previewUrl: options?.previewUrl,
|
|
126
|
+
logsUrl: options?.logsUrl,
|
|
127
|
+
workItemId: options?.workItemId,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Convenience function: Notify build failure
|
|
132
|
+
*/
|
|
133
|
+
export async function notifyBuildFailure(config, options) {
|
|
134
|
+
return notifyCi({
|
|
135
|
+
...config,
|
|
136
|
+
status: 'failed',
|
|
137
|
+
errorMessage: options?.errorMessage,
|
|
138
|
+
logsUrl: options?.logsUrl,
|
|
139
|
+
workItemId: options?.workItemId,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fortalece AI CI Types
|
|
3
|
+
* Types for GitHub Actions integration
|
|
4
|
+
*/
|
|
5
|
+
export type CiEventType = 'build_start' | 'build_success' | 'build_failure' | 'rebuild';
|
|
6
|
+
export type CiStatus = 'building' | 'success' | 'failed';
|
|
7
|
+
/**
|
|
8
|
+
* CI Event Payload
|
|
9
|
+
* Sent from GitHub Actions to Fortalece webhook
|
|
10
|
+
*/
|
|
11
|
+
export interface CiEventPayload {
|
|
12
|
+
/** Event type */
|
|
13
|
+
event: CiEventType;
|
|
14
|
+
/** Build status */
|
|
15
|
+
status: CiStatus;
|
|
16
|
+
/** Repository in owner/repo format */
|
|
17
|
+
repository: string;
|
|
18
|
+
/** Branch name */
|
|
19
|
+
branch: string;
|
|
20
|
+
/** Commit SHA */
|
|
21
|
+
commit_sha: string;
|
|
22
|
+
/** Optional: Link to a specific work item */
|
|
23
|
+
work_item_id?: string;
|
|
24
|
+
/** Build ID */
|
|
25
|
+
build_id?: string;
|
|
26
|
+
/** Build URL (GitHub Actions run) */
|
|
27
|
+
build_url?: string;
|
|
28
|
+
/** Logs URL */
|
|
29
|
+
logs_url?: string;
|
|
30
|
+
/** Preview URL (for deployments) */
|
|
31
|
+
preview_url?: string;
|
|
32
|
+
/** Service name */
|
|
33
|
+
service_name?: string;
|
|
34
|
+
/** Error message (for failures) */
|
|
35
|
+
error_message?: string;
|
|
36
|
+
/** GitHub Actions environment metadata */
|
|
37
|
+
github_run_id?: string;
|
|
38
|
+
github_run_number?: string;
|
|
39
|
+
github_workflow?: string;
|
|
40
|
+
github_actor?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Configuration for CI notifications
|
|
44
|
+
*/
|
|
45
|
+
export interface CiNotifyConfig {
|
|
46
|
+
/** Webhook URL (includes hookId) */
|
|
47
|
+
webhookUrl: string;
|
|
48
|
+
/** Webhook secret for HMAC signing */
|
|
49
|
+
webhookSecret: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Options for notify functions
|
|
53
|
+
*/
|
|
54
|
+
export interface NotifyOptions extends CiNotifyConfig {
|
|
55
|
+
/** Build status */
|
|
56
|
+
status: CiStatus;
|
|
57
|
+
/** Optional preview URL */
|
|
58
|
+
previewUrl?: string;
|
|
59
|
+
/** Optional logs URL */
|
|
60
|
+
logsUrl?: string;
|
|
61
|
+
/** Optional error message */
|
|
62
|
+
errorMessage?: string;
|
|
63
|
+
/** Optional work item ID to link the deployment */
|
|
64
|
+
workItemId?: string;
|
|
65
|
+
/** Optional service name */
|
|
66
|
+
serviceName?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Response from Fortalece CI webhook
|
|
70
|
+
*/
|
|
71
|
+
export interface CiNotifyResponse {
|
|
72
|
+
success: boolean;
|
|
73
|
+
error?: string;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ci/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,eAAe,GAAG,eAAe,GAAG,SAAS,CAAC;AACxF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,iBAAiB;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,mBAAmB;IACnB,MAAM,EAAE,QAAQ,CAAC;IAEjB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,UAAU,EAAE,MAAM,CAAC;IAEnB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,mBAAmB;IACnB,MAAM,EAAE,QAAQ,CAAC;IACjB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|