@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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hmac.test.d.ts.map
@@ -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
+ });
@@ -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"}
@@ -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"}
@@ -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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Fortalece AI CI Types
3
+ * Types for GitHub Actions integration
4
+ */
5
+ export {};