golem-cc 1.0.2 → 2.1.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.
@@ -1,167 +0,0 @@
1
- /**
2
- * Freshservice API client
3
- *
4
- * API Docs: https://api.freshservice.com/
5
- */
6
-
7
- import type { FreshTicket, FreshTicketCreatePayload } from '../types.js';
8
-
9
- export interface FreshworksConfig {
10
- domain: string; // e.g., "yourcompany.freshservice.com"
11
- apiKey: string;
12
- }
13
-
14
- export class FreshworksClient {
15
- private baseUrl: string;
16
- private headers: Headers;
17
-
18
- constructor(config: FreshworksConfig) {
19
- this.baseUrl = `https://${config.domain}/api/v2`;
20
- // Freshservice uses Basic Auth with API key as username, 'X' as password
21
- const auth = Buffer.from(`${config.apiKey}:X`).toString('base64');
22
- this.headers = new Headers({
23
- 'Authorization': `Basic ${auth}`,
24
- 'Content-Type': 'application/json',
25
- });
26
- }
27
-
28
- private async request<T>(
29
- method: string,
30
- endpoint: string,
31
- body?: unknown
32
- ): Promise<T> {
33
- const response = await fetch(`${this.baseUrl}${endpoint}`, {
34
- method,
35
- headers: this.headers,
36
- body: body ? JSON.stringify(body) : undefined,
37
- });
38
-
39
- if (!response.ok) {
40
- const text = await response.text();
41
- throw new Error(`Freshservice API error ${response.status}: ${text}`);
42
- }
43
-
44
- return response.json();
45
- }
46
-
47
- /**
48
- * Get a ticket by ID
49
- */
50
- async getTicket(id: number): Promise<FreshTicket> {
51
- const result = await this.request<{ ticket: FreshTicket }>('GET', `/tickets/${id}`);
52
- return result.ticket;
53
- }
54
-
55
- /**
56
- * Get tickets assigned to current user
57
- */
58
- async getMyTickets(): Promise<FreshTicket[]> {
59
- // Filter: new_and_my_open gets tickets assigned to me that are open
60
- const result = await this.request<{ tickets: FreshTicket[] }>(
61
- 'GET',
62
- '/tickets?filter=new_and_my_open'
63
- );
64
- return result.tickets;
65
- }
66
-
67
- /**
68
- * Create a new ticket
69
- */
70
- async createTicket(payload: FreshTicketCreatePayload): Promise<FreshTicket> {
71
- const result = await this.request<{ ticket: FreshTicket }>(
72
- 'POST',
73
- '/tickets',
74
- { ticket: payload }
75
- );
76
- return result.ticket;
77
- }
78
-
79
- /**
80
- * Update a ticket
81
- */
82
- async updateTicket(
83
- id: number,
84
- updates: Partial<FreshTicketCreatePayload>
85
- ): Promise<FreshTicket> {
86
- const result = await this.request<{ ticket: FreshTicket }>(
87
- 'PUT',
88
- `/tickets/${id}`,
89
- { ticket: updates }
90
- );
91
- return result.ticket;
92
- }
93
-
94
- /**
95
- * Get current agent info
96
- */
97
- async getCurrentAgent(): Promise<{ id: number; email: string }> {
98
- const result = await this.request<{ agent: { id: number; email: string } }>('GET', '/agents/me');
99
- return result.agent;
100
- }
101
-
102
- /**
103
- * Add a note to a ticket
104
- */
105
- async addNote(
106
- ticketId: number,
107
- body: string,
108
- isPrivate = true
109
- ): Promise<void> {
110
- await this.request('POST', `/tickets/${ticketId}/notes`, {
111
- body,
112
- private: isPrivate,
113
- });
114
- }
115
-
116
- /**
117
- * Close/resolve a ticket
118
- * Note: Freshservice requires responder_id and resolution_notes when closing
119
- */
120
- async closeTicket(id: number, resolution?: string): Promise<FreshTicket> {
121
- // Get current agent to assign as responder (required for closing)
122
- const agent = await this.getCurrentAgent();
123
-
124
- const resolutionText = resolution || 'Completed via Golem';
125
-
126
- const updates: Partial<FreshTicketCreatePayload> & { status: number; responder_id: number } = {
127
- status: 4, // Resolved in Freshservice (5 = Closed requires additional workflow)
128
- responder_id: agent.id,
129
- resolution_notes: resolutionText,
130
- };
131
-
132
- return this.updateTicket(id, updates);
133
- }
134
-
135
- /**
136
- * Format ticket ID for display (e.g., "INC-1234")
137
- */
138
- static formatTicketId(id: number, type: 'Incident' | 'Service Request' = 'Incident'): string {
139
- const prefix = type === 'Incident' ? 'INC' : 'SR';
140
- return `${prefix}-${id}`;
141
- }
142
-
143
- /**
144
- * Parse ticket ID from string (e.g., "INC-1234" -> 1234)
145
- */
146
- static parseTicketId(idString: string): number {
147
- const match = idString.match(/(?:INC|SR)-?(\d+)/i);
148
- if (!match) {
149
- throw new Error(`Invalid ticket ID format: ${idString}`);
150
- }
151
- return parseInt(match[1], 10);
152
- }
153
- }
154
-
155
- /**
156
- * Create client from environment variables
157
- */
158
- export function createFreshworksClient(): FreshworksClient {
159
- const domain = process.env.FRESH_DOMAIN;
160
- const apiKey = process.env.FRESH_API_KEY;
161
-
162
- if (!domain || !apiKey) {
163
- throw new Error('Missing FRESH_DOMAIN or FRESH_API_KEY environment variables');
164
- }
165
-
166
- return new FreshworksClient({ domain, apiKey });
167
- }
package/src/api/gitea.ts DELETED
@@ -1,215 +0,0 @@
1
- /**
2
- * Gitea API client
3
- *
4
- * API Docs: https://docs.gitea.com/api/1.20/
5
- * Swagger: {your-gitea}/api/swagger
6
- */
7
-
8
- import type { GiteaIssue, GiteaIssueCreatePayload, GiteaPullRequest } from '../types.js';
9
-
10
- export interface GiteaConfig {
11
- baseUrl: string; // e.g., "https://dev.pearlriverresort.com"
12
- token: string;
13
- org: string; // Default org, e.g., "CRDE"
14
- }
15
-
16
- export class GiteaClient {
17
- private baseUrl: string;
18
- private headers: Headers;
19
- private defaultOrg: string;
20
-
21
- constructor(config: GiteaConfig) {
22
- this.baseUrl = `${config.baseUrl}/api/v1`;
23
- this.defaultOrg = config.org;
24
- this.headers = new Headers({
25
- 'Authorization': `token ${config.token}`,
26
- 'Content-Type': 'application/json',
27
- });
28
- }
29
-
30
- private async request<T>(
31
- method: string,
32
- endpoint: string,
33
- body?: unknown
34
- ): Promise<T> {
35
- const response = await fetch(`${this.baseUrl}${endpoint}`, {
36
- method,
37
- headers: this.headers,
38
- body: body ? JSON.stringify(body) : undefined,
39
- });
40
-
41
- if (!response.ok) {
42
- const text = await response.text();
43
- throw new Error(`Gitea API error ${response.status}: ${text}`);
44
- }
45
-
46
- // Handle empty responses (204 No Content)
47
- if (response.status === 204) {
48
- return undefined as T;
49
- }
50
-
51
- return response.json();
52
- }
53
-
54
- private repoPath(repo: string): string {
55
- // If repo doesn't include org, prepend default org
56
- if (!repo.includes('/')) {
57
- return `${this.defaultOrg}/${repo}`;
58
- }
59
- return repo;
60
- }
61
-
62
- // ============ Issues ============
63
-
64
- /**
65
- * Get an issue by number
66
- */
67
- async getIssue(repo: string, number: number): Promise<GiteaIssue> {
68
- const path = this.repoPath(repo);
69
- return this.request<GiteaIssue>('GET', `/repos/${path}/issues/${number}`);
70
- }
71
-
72
- /**
73
- * List issues for a repo
74
- */
75
- async listIssues(
76
- repo: string,
77
- options: { state?: 'open' | 'closed' | 'all'; labels?: string[] } = {}
78
- ): Promise<GiteaIssue[]> {
79
- const path = this.repoPath(repo);
80
- const params = new URLSearchParams();
81
- if (options.state) params.set('state', options.state);
82
- if (options.labels?.length) params.set('labels', options.labels.join(','));
83
-
84
- const query = params.toString();
85
- return this.request<GiteaIssue[]>(
86
- 'GET',
87
- `/repos/${path}/issues${query ? `?${query}` : ''}`
88
- );
89
- }
90
-
91
- /**
92
- * Find an issue by ticket ID prefix in the title (e.g., "[INC-123]")
93
- * Returns the first matching issue or null if not found
94
- */
95
- async findIssueByTicketId(repo: string, ticketId: string): Promise<GiteaIssue | null> {
96
- // Search all issues (open and closed) for one with the ticket ID in the title
97
- const issues = await this.listIssues(repo, { state: 'all' });
98
- const prefix = `[${ticketId}]`;
99
- const match = issues.find(issue => issue.title.startsWith(prefix));
100
- return match || null;
101
- }
102
-
103
- /**
104
- * Create a new issue
105
- */
106
- async createIssue(repo: string, payload: GiteaIssueCreatePayload): Promise<GiteaIssue> {
107
- const path = this.repoPath(repo);
108
- return this.request<GiteaIssue>('POST', `/repos/${path}/issues`, payload);
109
- }
110
-
111
- /**
112
- * Update an issue
113
- */
114
- async updateIssue(
115
- repo: string,
116
- number: number,
117
- updates: Partial<GiteaIssueCreatePayload> & { state?: 'open' | 'closed' }
118
- ): Promise<GiteaIssue> {
119
- const path = this.repoPath(repo);
120
- return this.request<GiteaIssue>('PATCH', `/repos/${path}/issues/${number}`, updates);
121
- }
122
-
123
- /**
124
- * Add a comment to an issue
125
- */
126
- async addIssueComment(repo: string, number: number, body: string): Promise<void> {
127
- const path = this.repoPath(repo);
128
- await this.request('POST', `/repos/${path}/issues/${number}/comments`, { body });
129
- }
130
-
131
- /**
132
- * Close an issue
133
- */
134
- async closeIssue(repo: string, number: number): Promise<GiteaIssue> {
135
- return this.updateIssue(repo, number, { state: 'closed' });
136
- }
137
-
138
- // ============ Pull Requests ============
139
-
140
- /**
141
- * Get a pull request by number
142
- */
143
- async getPullRequest(repo: string, number: number): Promise<GiteaPullRequest> {
144
- const path = this.repoPath(repo);
145
- return this.request<GiteaPullRequest>('GET', `/repos/${path}/pulls/${number}`);
146
- }
147
-
148
- /**
149
- * Create a pull request
150
- */
151
- async createPullRequest(
152
- repo: string,
153
- payload: {
154
- title: string;
155
- body: string;
156
- head: string; // Source branch
157
- base: string; // Target branch (usually 'main')
158
- }
159
- ): Promise<GiteaPullRequest> {
160
- const path = this.repoPath(repo);
161
- return this.request<GiteaPullRequest>('POST', `/repos/${path}/pulls`, payload);
162
- }
163
-
164
- /**
165
- * Merge a pull request
166
- */
167
- async mergePullRequest(
168
- repo: string,
169
- number: number,
170
- options: {
171
- mergeStyle?: 'merge' | 'rebase' | 'squash';
172
- title?: string;
173
- message?: string;
174
- } = {}
175
- ): Promise<void> {
176
- const path = this.repoPath(repo);
177
- await this.request('POST', `/repos/${path}/pulls/${number}/merge`, {
178
- Do: options.mergeStyle || 'squash',
179
- MergeTitleField: options.title,
180
- MergeMessageField: options.message,
181
- });
182
- }
183
-
184
- // ============ Repos ============
185
-
186
- /**
187
- * List repos in the default org
188
- */
189
- async listOrgRepos(): Promise<{ name: string; full_name: string; html_url: string }[]> {
190
- return this.request('GET', `/orgs/${this.defaultOrg}/repos`);
191
- }
192
-
193
- /**
194
- * Get repo info
195
- */
196
- async getRepo(repo: string): Promise<{ name: string; full_name: string; default_branch: string }> {
197
- const path = this.repoPath(repo);
198
- return this.request('GET', `/repos/${path}`);
199
- }
200
- }
201
-
202
- /**
203
- * Create client from environment variables
204
- */
205
- export function createGiteaClient(): GiteaClient {
206
- const baseUrl = process.env.GITEA_URL;
207
- const token = process.env.GITEA_TOKEN;
208
- const org = process.env.GITEA_ORG;
209
-
210
- if (!baseUrl || !token || !org) {
211
- throw new Error('Missing GITEA_URL, GITEA_TOKEN, or GITEA_ORG environment variables');
212
- }
213
-
214
- return new GiteaClient({ baseUrl, token, org });
215
- }