@plosson/agentio 0.1.5 → 0.1.8

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,76 @@
1
+ import { CliError } from '../../utils/errors';
2
+ import type {
3
+ SlackCredentials,
4
+ SlackSendOptions,
5
+ SlackSendResult,
6
+ SlackWebhookCredentials,
7
+ } from '../../types/slack';
8
+
9
+ export class SlackClient {
10
+ private credentials: SlackCredentials;
11
+
12
+ constructor(credentials: SlackCredentials) {
13
+ this.credentials = credentials;
14
+ }
15
+
16
+ async send(options: SlackSendOptions): Promise<SlackSendResult> {
17
+ if (this.credentials.type === 'webhook') {
18
+ return this.sendViaWebhook(options);
19
+ }
20
+ throw new CliError('INVALID_PARAMS', 'Unknown credentials type');
21
+ }
22
+
23
+ private async sendViaWebhook(options: SlackSendOptions): Promise<SlackSendResult> {
24
+ const webhookUrl = (this.credentials as SlackWebhookCredentials).webhookUrl;
25
+
26
+ if (!webhookUrl?.trim() || !webhookUrl.startsWith('https://')) {
27
+ throw new CliError(
28
+ 'INVALID_PARAMS',
29
+ 'Invalid webhook URL - must be HTTPS',
30
+ 'Check the webhook URL configuration'
31
+ );
32
+ }
33
+
34
+ // Use raw payload if provided, otherwise construct simple text message
35
+ const payload = options.payload ?? { text: options.text };
36
+
37
+ try {
38
+ const response = await fetch(webhookUrl, {
39
+ method: 'POST',
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ },
43
+ body: JSON.stringify(payload),
44
+ });
45
+
46
+ if (!response.ok) {
47
+ const error = await response.text();
48
+ throw new CliError(
49
+ this.getErrorCode(response.status),
50
+ `Failed to send message via webhook: ${response.status} ${error}`,
51
+ 'Check that the webhook URL is valid and the app has permission to post'
52
+ );
53
+ }
54
+
55
+ return {
56
+ success: true,
57
+ isJsonPayload: !!options.payload,
58
+ };
59
+ } catch (err) {
60
+ if (err instanceof CliError) throw err;
61
+ throw new CliError(
62
+ 'NETWORK_ERROR',
63
+ `Webhook request failed: ${err instanceof Error ? err.message : String(err)}`,
64
+ 'Verify the webhook URL is correct and accessible'
65
+ );
66
+ }
67
+ }
68
+
69
+ private getErrorCode(status: number): 'AUTH_FAILED' | 'PERMISSION_DENIED' | 'NOT_FOUND' | 'RATE_LIMITED' | 'API_ERROR' {
70
+ if (status === 401) return 'AUTH_FAILED';
71
+ if (status === 403) return 'PERMISSION_DENIED';
72
+ if (status === 404) return 'NOT_FOUND';
73
+ if (status === 429) return 'RATE_LIMITED';
74
+ return 'API_ERROR';
75
+ }
76
+ }
@@ -3,14 +3,16 @@ export interface Config {
3
3
  gmail?: string[];
4
4
  gchat?: string[];
5
5
  jira?: string[];
6
+ slack?: string[];
6
7
  telegram?: string[];
7
8
  };
8
9
  defaults: {
9
10
  gmail?: string;
10
11
  gchat?: string;
11
12
  jira?: string;
13
+ slack?: string;
12
14
  telegram?: string;
13
15
  };
14
16
  }
15
17
 
16
- export type ServiceName = 'gmail' | 'gchat' | 'jira' | 'telegram';
18
+ export type ServiceName = 'gmail' | 'gchat' | 'jira' | 'slack' | 'telegram';
@@ -0,0 +1,87 @@
1
+ // JIRA OAuth credentials stored per profile
2
+ // Note: clientId and clientSecret are embedded in the app (see config/credentials.ts)
3
+ export interface JiraCredentials {
4
+ accessToken: string;
5
+ refreshToken: string;
6
+ expiryDate: number;
7
+ cloudId: string;
8
+ siteUrl: string;
9
+ }
10
+
11
+ // JIRA Project
12
+ export interface JiraProject {
13
+ id: string;
14
+ key: string;
15
+ name: string;
16
+ projectTypeKey: string;
17
+ simplified: boolean;
18
+ style: string;
19
+ isPrivate: boolean;
20
+ avatarUrls?: Record<string, string>;
21
+ }
22
+
23
+ // JIRA Issue
24
+ export interface JiraIssue {
25
+ id: string;
26
+ key: string;
27
+ summary: string;
28
+ status: string;
29
+ statusCategoryKey: string;
30
+ priority?: string;
31
+ assignee?: string;
32
+ reporter?: string;
33
+ created: string;
34
+ updated: string;
35
+ projectKey: string;
36
+ issueType: string;
37
+ description?: string;
38
+ }
39
+
40
+ // JIRA Issue Transition
41
+ export interface JiraTransition {
42
+ id: string;
43
+ name: string;
44
+ to: {
45
+ id: string;
46
+ name: string;
47
+ statusCategory: {
48
+ key: string;
49
+ name: string;
50
+ };
51
+ };
52
+ }
53
+
54
+ // JIRA Comment
55
+ export interface JiraComment {
56
+ id: string;
57
+ author: string;
58
+ body: string;
59
+ created: string;
60
+ updated: string;
61
+ }
62
+
63
+ // Options for search
64
+ export interface JiraSearchOptions {
65
+ jql?: string;
66
+ project?: string;
67
+ status?: string;
68
+ assignee?: string;
69
+ maxResults?: number;
70
+ }
71
+
72
+ // Options for listing projects
73
+ export interface JiraProjectListOptions {
74
+ maxResults?: number;
75
+ }
76
+
77
+ // Result types
78
+ export interface JiraCommentResult {
79
+ id: string;
80
+ issueKey: string;
81
+ }
82
+
83
+ export interface JiraTransitionResult {
84
+ issueKey: string;
85
+ transitionName: string;
86
+ newStatus: string;
87
+ }
@@ -0,0 +1,17 @@
1
+ export interface SlackWebhookCredentials {
2
+ type: 'webhook';
3
+ webhookUrl: string;
4
+ channelName?: string; // Optional metadata for display
5
+ }
6
+
7
+ export type SlackCredentials = SlackWebhookCredentials;
8
+
9
+ export interface SlackSendOptions {
10
+ text?: string;
11
+ payload?: Record<string, unknown>; // Raw JSON payload for Block Kit messages
12
+ }
13
+
14
+ export interface SlackSendResult {
15
+ success: boolean;
16
+ isJsonPayload?: boolean;
17
+ }
@@ -1,5 +1,7 @@
1
1
  import type { GmailMessage } from '../types/gmail';
2
2
  import type { GChatMessage } from '../types/gchat';
3
+ import type { JiraProject, JiraIssue, JiraTransition, JiraCommentResult, JiraTransitionResult } from '../types/jira';
4
+ import type { SlackSendResult } from '../types/slack';
3
5
 
4
6
  // Format a list of Gmail messages
5
7
  export function printMessageList(messages: GmailMessage[], total: number): void {
@@ -103,3 +105,89 @@ export function printGChatMessage(msg: GChatMessage): void {
103
105
  console.log(msg.text);
104
106
  }
105
107
  }
108
+
109
+ // JIRA specific formatters
110
+ export function printJiraProjectList(projects: JiraProject[]): void {
111
+ if (projects.length === 0) {
112
+ console.log('No projects found');
113
+ return;
114
+ }
115
+
116
+ console.log(`Projects (${projects.length})\n`);
117
+
118
+ for (const project of projects) {
119
+ const privateMarker = project.isPrivate ? ' [private]' : '';
120
+ console.log(`${project.key} - ${project.name}${privateMarker}`);
121
+ console.log(` Type: ${project.projectTypeKey}`);
122
+ console.log('');
123
+ }
124
+ }
125
+
126
+ export function printJiraIssueList(issues: JiraIssue[]): void {
127
+ if (issues.length === 0) {
128
+ console.log('No issues found');
129
+ return;
130
+ }
131
+
132
+ console.log(`Issues (${issues.length})\n`);
133
+
134
+ for (const issue of issues) {
135
+ console.log(`${issue.key} [${issue.status}] ${issue.summary}`);
136
+ console.log(` Type: ${issue.issueType} | Project: ${issue.projectKey}`);
137
+ if (issue.assignee) console.log(` Assignee: ${issue.assignee}`);
138
+ if (issue.priority) console.log(` Priority: ${issue.priority}`);
139
+ console.log(` Updated: ${issue.updated}`);
140
+ console.log('');
141
+ }
142
+ }
143
+
144
+ export function printJiraIssue(issue: JiraIssue): void {
145
+ console.log(`Key: ${issue.key}`);
146
+ console.log(`Summary: ${issue.summary}`);
147
+ console.log(`Status: ${issue.status}`);
148
+ console.log(`Type: ${issue.issueType}`);
149
+ console.log(`Project: ${issue.projectKey}`);
150
+ if (issue.priority) console.log(`Priority: ${issue.priority}`);
151
+ if (issue.assignee) console.log(`Assignee: ${issue.assignee}`);
152
+ if (issue.reporter) console.log(`Reporter: ${issue.reporter}`);
153
+ console.log(`Created: ${issue.created}`);
154
+ console.log(`Updated: ${issue.updated}`);
155
+ if (issue.description) {
156
+ console.log('---');
157
+ console.log(issue.description);
158
+ }
159
+ }
160
+
161
+ export function printJiraTransitions(issueKey: string, transitions: JiraTransition[]): void {
162
+ if (transitions.length === 0) {
163
+ console.log(`No transitions available for ${issueKey}`);
164
+ return;
165
+ }
166
+
167
+ console.log(`Available transitions for ${issueKey}:\n`);
168
+
169
+ for (const transition of transitions) {
170
+ console.log(`[${transition.id}] ${transition.name} → ${transition.to.name}`);
171
+ }
172
+ }
173
+
174
+ export function printJiraCommentResult(result: JiraCommentResult): void {
175
+ console.log('Comment added');
176
+ console.log(`Issue: ${result.issueKey}`);
177
+ console.log(`Comment ID: ${result.id}`);
178
+ }
179
+
180
+ export function printJiraTransitionResult(result: JiraTransitionResult): void {
181
+ console.log('Issue transitioned');
182
+ console.log(`Issue: ${result.issueKey}`);
183
+ console.log(`Transition: ${result.transitionName}`);
184
+ console.log(`New Status: ${result.newStatus}`);
185
+ }
186
+
187
+ // Slack specific formatters
188
+ export function printSlackSendResult(result: SlackSendResult): void {
189
+ console.log('Message sent');
190
+ if (result.isJsonPayload) {
191
+ console.log('Type: Block Kit payload');
192
+ }
193
+ }