@plosson/agentio 0.3.0 → 0.3.2
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/package.json +1 -1
- package/src/auth/github-oauth.ts +41 -118
- package/src/auth/jira-oauth.ts +42 -104
- package/src/auth/oauth-server.ts +149 -0
- package/src/auth/oauth.ts +25 -98
- package/src/commands/config.ts +29 -0
- package/src/commands/discourse.ts +7 -30
- package/src/commands/gchat.ts +7 -28
- package/src/commands/github.ts +7 -28
- package/src/commands/slack.ts +7 -28
- package/src/commands/sql.ts +7 -28
- package/src/commands/status.ts +1 -1
- package/src/commands/telegram.ts +12 -33
- package/src/commands/update.ts +1 -15
- package/src/config/config-manager.ts +1 -1
- package/src/services/discourse/client.ts +2 -10
- package/src/services/gchat/client.ts +4 -6
- package/src/services/gmail/client.ts +35 -20
- package/src/services/jira/client.ts +2 -10
- package/src/services/slack/client.ts +2 -9
- package/src/types/telegram.ts +4 -4
- package/src/utils/client-factory.ts +53 -0
- package/src/utils/errors.ts +12 -0
- package/src/utils/obscure.ts +13 -0
package/src/commands/config.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { join } from 'path';
|
|
|
6
6
|
import { loadConfig, saveConfig, setEnv, unsetEnv, listEnv } from '../config/config-manager';
|
|
7
7
|
import { getAllCredentials, setAllCredentials } from '../auth/token-store';
|
|
8
8
|
import { CliError, handleError } from '../utils/errors';
|
|
9
|
+
import { confirm } from '../utils/stdin';
|
|
9
10
|
import type { Config } from '../types/config';
|
|
10
11
|
import type { StoredCredentials } from '../types/tokens';
|
|
11
12
|
|
|
@@ -308,4 +309,32 @@ export function registerConfigCommands(program: Command): void {
|
|
|
308
309
|
handleError(error);
|
|
309
310
|
}
|
|
310
311
|
});
|
|
312
|
+
|
|
313
|
+
config
|
|
314
|
+
.command('clear')
|
|
315
|
+
.description('Clear all configuration and credentials')
|
|
316
|
+
.option('--force', 'Skip confirmation prompt')
|
|
317
|
+
.action(async (options) => {
|
|
318
|
+
try {
|
|
319
|
+
if (!options.force) {
|
|
320
|
+
const confirmed = await confirm(
|
|
321
|
+
'This will delete all profiles and credentials. Are you sure?'
|
|
322
|
+
);
|
|
323
|
+
if (!confirmed) {
|
|
324
|
+
console.error('Aborted');
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Reset config to default (empty profiles)
|
|
330
|
+
await saveConfig({ profiles: {} });
|
|
331
|
+
|
|
332
|
+
// Clear all credentials
|
|
333
|
+
await setAllCredentials({});
|
|
334
|
+
|
|
335
|
+
console.log('Configuration cleared');
|
|
336
|
+
} catch (error) {
|
|
337
|
+
handleError(error);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
311
340
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { setCredentials
|
|
3
|
-
import { setProfile
|
|
2
|
+
import { setCredentials } from '../auth/token-store';
|
|
3
|
+
import { setProfile } from '../config/config-manager';
|
|
4
4
|
import { createProfileCommands } from '../utils/profile-commands';
|
|
5
|
+
import { createClientGetter } from '../utils/client-factory';
|
|
5
6
|
import { DiscourseClient } from '../services/discourse/client';
|
|
6
7
|
import { CliError, handleError } from '../utils/errors';
|
|
7
8
|
import { prompt } from '../utils/stdin';
|
|
@@ -12,34 +13,10 @@ import {
|
|
|
12
13
|
printDiscourseCategoryList,
|
|
13
14
|
} from '../utils/output';
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (!profile) {
|
|
21
|
-
throw new CliError(
|
|
22
|
-
'PROFILE_NOT_FOUND',
|
|
23
|
-
`Profile "${profileName}" not found for discourse`,
|
|
24
|
-
'Run: agentio discourse profile add'
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const credentials = await getCredentials<DiscourseCredentials>('discourse', profile);
|
|
29
|
-
|
|
30
|
-
if (!credentials) {
|
|
31
|
-
throw new CliError(
|
|
32
|
-
'AUTH_FAILED',
|
|
33
|
-
`No credentials found for discourse profile "${profile}"`,
|
|
34
|
-
`Run: agentio discourse profile add --profile ${profile}`
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
client: new DiscourseClient(credentials),
|
|
40
|
-
profile,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
16
|
+
const getDiscourseClient = createClientGetter<DiscourseCredentials, DiscourseClient>({
|
|
17
|
+
service: 'discourse',
|
|
18
|
+
createClient: (credentials) => new DiscourseClient(credentials),
|
|
19
|
+
});
|
|
43
20
|
|
|
44
21
|
export function registerDiscourseCommands(program: Command): void {
|
|
45
22
|
const discourse = program.command('discourse').description('Discourse forum operations');
|
package/src/commands/gchat.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { google } from 'googleapis';
|
|
3
3
|
import { readFile } from 'fs/promises';
|
|
4
|
-
import { setCredentials
|
|
5
|
-
import { setProfile
|
|
4
|
+
import { setCredentials } from '../auth/token-store';
|
|
5
|
+
import { setProfile } from '../config/config-manager';
|
|
6
6
|
import { createProfileCommands } from '../utils/profile-commands';
|
|
7
|
+
import { createClientGetter } from '../utils/client-factory';
|
|
7
8
|
import { performOAuthFlow } from '../auth/oauth';
|
|
8
9
|
import { createGoogleAuth } from '../auth/token-manager';
|
|
9
10
|
import { GChatClient } from '../services/gchat/client';
|
|
@@ -12,32 +13,10 @@ import { readStdin, prompt } from '../utils/stdin';
|
|
|
12
13
|
import { printGChatSendResult, printGChatMessageList, printGChatMessage, printGChatSpaceList } from '../utils/output';
|
|
13
14
|
import type { GChatCredentials, GChatWebhookCredentials, GChatOAuthCredentials } from '../types/gchat';
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
throw new CliError(
|
|
20
|
-
'PROFILE_NOT_FOUND',
|
|
21
|
-
`Profile "${profileName}" not found for gchat`,
|
|
22
|
-
'Run: agentio gchat profile add'
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const credentials = await getCredentials<GChatCredentials>('gchat', profile);
|
|
27
|
-
|
|
28
|
-
if (!credentials) {
|
|
29
|
-
throw new CliError(
|
|
30
|
-
'AUTH_FAILED',
|
|
31
|
-
`No credentials found for gchat profile "${profile}"`,
|
|
32
|
-
`Run: agentio gchat profile add --profile ${profile}`
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
client: new GChatClient(credentials),
|
|
38
|
-
profile,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
16
|
+
const getGChatClient = createClientGetter<GChatCredentials, GChatClient>({
|
|
17
|
+
service: 'gchat',
|
|
18
|
+
createClient: (credentials) => new GChatClient(credentials),
|
|
19
|
+
});
|
|
41
20
|
|
|
42
21
|
export function registerGChatCommands(program: Command): void {
|
|
43
22
|
const gchat = program
|
package/src/commands/github.ts
CHANGED
|
@@ -1,39 +1,18 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { setCredentials
|
|
3
|
-
import { setProfile
|
|
2
|
+
import { setCredentials } from '../auth/token-store';
|
|
3
|
+
import { setProfile } from '../config/config-manager';
|
|
4
4
|
import { createProfileCommands } from '../utils/profile-commands';
|
|
5
|
+
import { createClientGetter } from '../utils/client-factory';
|
|
5
6
|
import { GitHubClient } from '../services/github/client';
|
|
6
7
|
import { performGitHubOAuthFlow } from '../auth/github-oauth';
|
|
7
8
|
import { generateExportData } from './config';
|
|
8
9
|
import { CliError, handleError } from '../utils/errors';
|
|
9
10
|
import type { GitHubCredentials } from '../types/github';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
throw new CliError(
|
|
16
|
-
'PROFILE_NOT_FOUND',
|
|
17
|
-
`Profile "${profileName}" not found for github`,
|
|
18
|
-
'Run: agentio github profile add'
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const credentials = await getCredentials<GitHubCredentials>('github', profile);
|
|
23
|
-
|
|
24
|
-
if (!credentials) {
|
|
25
|
-
throw new CliError(
|
|
26
|
-
'AUTH_FAILED',
|
|
27
|
-
`No credentials found for github profile "${profile}"`,
|
|
28
|
-
`Run: agentio github profile add --profile ${profile}`
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
client: new GitHubClient(credentials),
|
|
34
|
-
profile,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
12
|
+
const getGitHubClient = createClientGetter<GitHubCredentials, GitHubClient>({
|
|
13
|
+
service: 'github',
|
|
14
|
+
createClient: (credentials) => new GitHubClient(credentials),
|
|
15
|
+
});
|
|
37
16
|
|
|
38
17
|
function parseRepo(repo: string): { owner: string; name: string } {
|
|
39
18
|
const parts = repo.split('/');
|
package/src/commands/slack.ts
CHANGED
|
@@ -1,40 +1,19 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { readFile } from 'fs/promises';
|
|
3
|
-
import { setCredentials
|
|
4
|
-
import { setProfile
|
|
3
|
+
import { setCredentials } from '../auth/token-store';
|
|
4
|
+
import { setProfile } from '../config/config-manager';
|
|
5
5
|
import { createProfileCommands } from '../utils/profile-commands';
|
|
6
|
+
import { createClientGetter } from '../utils/client-factory';
|
|
6
7
|
import { SlackClient } from '../services/slack/client';
|
|
7
8
|
import { CliError, handleError } from '../utils/errors';
|
|
8
9
|
import { readStdin, prompt } from '../utils/stdin';
|
|
9
10
|
import { printSlackSendResult } from '../utils/output';
|
|
10
11
|
import type { SlackCredentials, SlackWebhookCredentials } from '../types/slack';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new CliError(
|
|
17
|
-
'PROFILE_NOT_FOUND',
|
|
18
|
-
`Profile "${profileName}" not found for slack`,
|
|
19
|
-
'Run: agentio slack profile add'
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const credentials = await getCredentials<SlackCredentials>('slack', profile);
|
|
24
|
-
|
|
25
|
-
if (!credentials) {
|
|
26
|
-
throw new CliError(
|
|
27
|
-
'AUTH_FAILED',
|
|
28
|
-
`No credentials found for slack profile "${profile}"`,
|
|
29
|
-
`Run: agentio slack profile add --profile ${profile}`
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
client: new SlackClient(credentials),
|
|
35
|
-
profile,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
13
|
+
const getSlackClient = createClientGetter<SlackCredentials, SlackClient>({
|
|
14
|
+
service: 'slack',
|
|
15
|
+
createClient: (credentials) => new SlackClient(credentials),
|
|
16
|
+
});
|
|
38
17
|
|
|
39
18
|
export function registerSlackCommands(program: Command): void {
|
|
40
19
|
const slack = program
|
package/src/commands/sql.ts
CHANGED
|
@@ -1,38 +1,17 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { setCredentials
|
|
3
|
-
import { setProfile
|
|
2
|
+
import { setCredentials } from '../auth/token-store';
|
|
3
|
+
import { setProfile } from '../config/config-manager';
|
|
4
4
|
import { createProfileCommands } from '../utils/profile-commands';
|
|
5
|
+
import { createClientGetter } from '../utils/client-factory';
|
|
5
6
|
import { SqlClient } from '../services/sql/client';
|
|
6
7
|
import { CliError, handleError } from '../utils/errors';
|
|
7
8
|
import { readStdin, prompt } from '../utils/stdin';
|
|
8
9
|
import type { SqlCredentials } from '../types/sql';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
throw new CliError(
|
|
15
|
-
'PROFILE_NOT_FOUND',
|
|
16
|
-
`Profile "${profileName}" not found for sql`,
|
|
17
|
-
'Run: agentio sql profile add'
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const credentials = await getCredentials<SqlCredentials>('sql', profile);
|
|
22
|
-
|
|
23
|
-
if (!credentials) {
|
|
24
|
-
throw new CliError(
|
|
25
|
-
'AUTH_FAILED',
|
|
26
|
-
`No credentials found for sql profile "${profile}"`,
|
|
27
|
-
`Run: agentio sql profile add --profile ${profile}`
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
client: new SqlClient(credentials),
|
|
33
|
-
profile,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
11
|
+
const getSqlClient = createClientGetter<SqlCredentials, SqlClient>({
|
|
12
|
+
service: 'sql',
|
|
13
|
+
createClient: (credentials) => new SqlClient(credentials),
|
|
14
|
+
});
|
|
36
15
|
|
|
37
16
|
function extractDisplayName(url: string): string {
|
|
38
17
|
try {
|
package/src/commands/status.ts
CHANGED
|
@@ -48,7 +48,7 @@ async function createServiceClient(
|
|
|
48
48
|
|
|
49
49
|
case 'telegram': {
|
|
50
50
|
const creds = credentials as TelegramCredentials;
|
|
51
|
-
return new TelegramClient(creds.
|
|
51
|
+
return new TelegramClient(creds.botToken, creds.channelId);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
case 'github': {
|
package/src/commands/telegram.ts
CHANGED
|
@@ -1,38 +1,17 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { setCredentials
|
|
3
|
-
import { setProfile
|
|
2
|
+
import { setCredentials } from '../auth/token-store';
|
|
3
|
+
import { setProfile } from '../config/config-manager';
|
|
4
4
|
import { createProfileCommands } from '../utils/profile-commands';
|
|
5
|
+
import { createClientGetter } from '../utils/client-factory';
|
|
5
6
|
import { TelegramClient } from '../services/telegram/client';
|
|
6
7
|
import { CliError, handleError } from '../utils/errors';
|
|
7
8
|
import { readStdin, prompt } from '../utils/stdin';
|
|
8
9
|
import type { TelegramCredentials, TelegramSendOptions } from '../types/telegram';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
throw new CliError(
|
|
15
|
-
'PROFILE_NOT_FOUND',
|
|
16
|
-
`Profile "${profileName}" not found for telegram`,
|
|
17
|
-
'Run: agentio telegram profile add'
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const credentials = await getCredentials<TelegramCredentials>('telegram', profile);
|
|
22
|
-
|
|
23
|
-
if (!credentials) {
|
|
24
|
-
throw new CliError(
|
|
25
|
-
'AUTH_FAILED',
|
|
26
|
-
`No credentials found for telegram profile "${profile}"`,
|
|
27
|
-
`Run: agentio telegram profile add --profile ${profile}`
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
client: new TelegramClient(credentials.bot_token, credentials.channel_id),
|
|
33
|
-
profile,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
11
|
+
const getTelegramClient = createClientGetter<TelegramCredentials, TelegramClient>({
|
|
12
|
+
service: 'telegram',
|
|
13
|
+
createClient: (credentials) => new TelegramClient(credentials.botToken, credentials.channelId),
|
|
14
|
+
});
|
|
36
15
|
|
|
37
16
|
export function registerTelegramCommands(program: Command): void {
|
|
38
17
|
const telegram = program
|
|
@@ -84,7 +63,7 @@ export function registerTelegramCommands(program: Command): void {
|
|
|
84
63
|
const profile = createProfileCommands<TelegramCredentials>(telegram, {
|
|
85
64
|
service: 'telegram',
|
|
86
65
|
displayName: 'Telegram',
|
|
87
|
-
getExtraInfo: (credentials) => credentials?.
|
|
66
|
+
getExtraInfo: (credentials) => credentials?.channelName ? ` - ${credentials.channelName}` : '',
|
|
88
67
|
});
|
|
89
68
|
|
|
90
69
|
profile
|
|
@@ -175,10 +154,10 @@ export function registerTelegramCommands(program: Command): void {
|
|
|
175
154
|
|
|
176
155
|
// Save credentials
|
|
177
156
|
const credentials: TelegramCredentials = {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
157
|
+
botToken: botToken,
|
|
158
|
+
channelId: channelId,
|
|
159
|
+
botUsername: botInfo.username,
|
|
160
|
+
channelName: channelName,
|
|
182
161
|
};
|
|
183
162
|
|
|
184
163
|
await setProfile('telegram', profileName);
|
package/src/commands/update.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { createInterface } from 'readline';
|
|
3
2
|
import { CliError, handleError } from '../utils/errors';
|
|
3
|
+
import { prompt } from '../utils/stdin';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import * as os from 'os';
|
|
@@ -17,20 +17,6 @@ interface GitHubRelease {
|
|
|
17
17
|
}>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function prompt(question: string): Promise<string> {
|
|
21
|
-
const rl = createInterface({
|
|
22
|
-
input: process.stdin,
|
|
23
|
-
output: process.stderr,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
return new Promise((resolve) => {
|
|
27
|
-
rl.question(question, (answer) => {
|
|
28
|
-
rl.close();
|
|
29
|
-
resolve(answer.trim());
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
20
|
function getCurrentVersion(): string {
|
|
35
21
|
return pkg.version;
|
|
36
22
|
}
|
|
@@ -7,7 +7,7 @@ import type { Config, ServiceName } from '../types/config';
|
|
|
7
7
|
const CONFIG_DIR = join(homedir(), '.config', 'agentio');
|
|
8
8
|
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
9
9
|
|
|
10
|
-
const ALL_SERVICES: ServiceName[] = ['gmail', 'gchat', 'jira', 'slack', 'telegram', 'discourse', 'sql'];
|
|
10
|
+
const ALL_SERVICES: ServiceName[] = ['gmail', 'gchat', 'github', 'jira', 'slack', 'telegram', 'discourse', 'sql'];
|
|
11
11
|
|
|
12
12
|
const DEFAULT_CONFIG: Config = {
|
|
13
13
|
profiles: {},
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
DiscourseListOptions,
|
|
8
8
|
} from '../../types/discourse';
|
|
9
9
|
import type { ServiceClient, ValidationResult } from '../../types/service';
|
|
10
|
-
import { CliError,
|
|
10
|
+
import { CliError, httpStatusToErrorCode } from '../../utils/errors';
|
|
11
11
|
|
|
12
12
|
interface DiscourseApiResponse {
|
|
13
13
|
errors?: string[];
|
|
@@ -129,7 +129,7 @@ export class DiscourseClient implements ServiceClient {
|
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
if (!response.ok) {
|
|
132
|
-
const errorCode =
|
|
132
|
+
const errorCode = httpStatusToErrorCode(response.status);
|
|
133
133
|
const text = await response.text();
|
|
134
134
|
let message = `Discourse API error: ${response.status}`;
|
|
135
135
|
try {
|
|
@@ -152,14 +152,6 @@ export class DiscourseClient implements ServiceClient {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
private getErrorCode(status: number): ErrorCode {
|
|
156
|
-
if (status === 401) return 'AUTH_FAILED';
|
|
157
|
-
if (status === 403) return 'PERMISSION_DENIED';
|
|
158
|
-
if (status === 404) return 'NOT_FOUND';
|
|
159
|
-
if (status === 429) return 'RATE_LIMITED';
|
|
160
|
-
return 'API_ERROR';
|
|
161
|
-
}
|
|
162
|
-
|
|
163
155
|
async getCategories(): Promise<DiscourseCategory[]> {
|
|
164
156
|
const data = await this.request<CategoryListResponse>('GET', '/categories.json');
|
|
165
157
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { google } from 'googleapis';
|
|
2
2
|
import type { chat_v1 } from 'googleapis';
|
|
3
|
-
import { CliError } from '../../utils/errors';
|
|
4
|
-
import type { ErrorCode } from '../../utils/errors';
|
|
3
|
+
import { CliError, httpStatusToErrorCode, type ErrorCode } from '../../utils/errors';
|
|
5
4
|
import type { ServiceClient, ValidationResult } from '../../types/service';
|
|
6
5
|
import { GOOGLE_OAUTH_CONFIG } from '../../config/credentials';
|
|
7
6
|
import type {
|
|
@@ -331,10 +330,9 @@ export class GChatClient implements ServiceClient {
|
|
|
331
330
|
if (err && typeof err === 'object') {
|
|
332
331
|
const error = err as Record<string, unknown>;
|
|
333
332
|
const code = error.code || error.status;
|
|
334
|
-
if (code ===
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (code === 429) return 'RATE_LIMITED';
|
|
333
|
+
if (typeof code === 'number') {
|
|
334
|
+
return httpStatusToErrorCode(code);
|
|
335
|
+
}
|
|
338
336
|
}
|
|
339
337
|
return 'API_ERROR';
|
|
340
338
|
}
|
|
@@ -186,8 +186,9 @@ export class GmailClient implements ServiceClient {
|
|
|
186
186
|
messages,
|
|
187
187
|
total: response.data.resultSizeEstimate || messages.length,
|
|
188
188
|
};
|
|
189
|
-
} catch (error
|
|
190
|
-
|
|
189
|
+
} catch (error) {
|
|
190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
191
|
+
throw new CliError('API_ERROR', `Gmail API error: ${message}`);
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
194
|
|
|
@@ -209,11 +210,12 @@ export class GmailClient implements ServiceClient {
|
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
return { ...message, body };
|
|
212
|
-
} catch (error
|
|
213
|
-
if (error
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (this.isNotFoundError(error)) {
|
|
214
215
|
throw new CliError('NOT_FOUND', `Message not found: ${messageId}`);
|
|
215
216
|
}
|
|
216
|
-
|
|
217
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
218
|
+
throw new CliError('API_ERROR', `Gmail API error: ${message}`);
|
|
217
219
|
}
|
|
218
220
|
}
|
|
219
221
|
|
|
@@ -249,12 +251,13 @@ export class GmailClient implements ServiceClient {
|
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
return results;
|
|
252
|
-
} catch (error
|
|
254
|
+
} catch (error) {
|
|
253
255
|
if (error instanceof CliError) throw error;
|
|
254
|
-
if (error
|
|
256
|
+
if (this.isNotFoundError(error)) {
|
|
255
257
|
throw new CliError('NOT_FOUND', `Message not found: ${messageId}`);
|
|
256
258
|
}
|
|
257
|
-
|
|
259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
260
|
+
throw new CliError('API_ERROR', `Gmail API error: ${message}`);
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
|
|
@@ -307,8 +310,9 @@ export class GmailClient implements ServiceClient {
|
|
|
307
310
|
threadId: response.data.threadId!,
|
|
308
311
|
labelIds: response.data.labelIds || ['SENT'],
|
|
309
312
|
};
|
|
310
|
-
} catch (error
|
|
311
|
-
|
|
313
|
+
} catch (error) {
|
|
314
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
315
|
+
throw new CliError('API_ERROR', `Failed to send email: ${message}`);
|
|
312
316
|
}
|
|
313
317
|
}
|
|
314
318
|
|
|
@@ -453,9 +457,10 @@ export class GmailClient implements ServiceClient {
|
|
|
453
457
|
mimeType,
|
|
454
458
|
base64: Buffer.from(content).toString('base64'),
|
|
455
459
|
};
|
|
456
|
-
} catch (error
|
|
460
|
+
} catch (error) {
|
|
457
461
|
if (error instanceof CliError) throw error;
|
|
458
|
-
|
|
462
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
463
|
+
throw new CliError('API_ERROR', `Failed to read attachment ${attachment.path}: ${message}`);
|
|
459
464
|
}
|
|
460
465
|
}
|
|
461
466
|
|
|
@@ -510,9 +515,10 @@ export class GmailClient implements ServiceClient {
|
|
|
510
515
|
threadId: response.data.threadId!,
|
|
511
516
|
labelIds: response.data.labelIds || ['SENT'],
|
|
512
517
|
};
|
|
513
|
-
} catch (error
|
|
518
|
+
} catch (error) {
|
|
514
519
|
if (error instanceof CliError) throw error;
|
|
515
|
-
|
|
520
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
521
|
+
throw new CliError('API_ERROR', `Failed to send reply: ${message}`);
|
|
516
522
|
}
|
|
517
523
|
}
|
|
518
524
|
|
|
@@ -525,11 +531,12 @@ export class GmailClient implements ServiceClient {
|
|
|
525
531
|
removeLabelIds: ['INBOX'],
|
|
526
532
|
},
|
|
527
533
|
});
|
|
528
|
-
} catch (error
|
|
529
|
-
if (error
|
|
534
|
+
} catch (error) {
|
|
535
|
+
if (this.isNotFoundError(error)) {
|
|
530
536
|
throw new CliError('NOT_FOUND', `Message not found: ${messageId}`);
|
|
531
537
|
}
|
|
532
|
-
|
|
538
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
539
|
+
throw new CliError('API_ERROR', `Failed to archive: ${message}`);
|
|
533
540
|
}
|
|
534
541
|
}
|
|
535
542
|
|
|
@@ -542,11 +549,19 @@ export class GmailClient implements ServiceClient {
|
|
|
542
549
|
? { removeLabelIds: ['UNREAD'] }
|
|
543
550
|
: { addLabelIds: ['UNREAD'] },
|
|
544
551
|
});
|
|
545
|
-
} catch (error
|
|
546
|
-
if (error
|
|
552
|
+
} catch (error) {
|
|
553
|
+
if (this.isNotFoundError(error)) {
|
|
547
554
|
throw new CliError('NOT_FOUND', `Message not found: ${messageId}`);
|
|
548
555
|
}
|
|
549
|
-
|
|
556
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
557
|
+
throw new CliError('API_ERROR', `Failed to update message: ${message}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private isNotFoundError(error: unknown): boolean {
|
|
562
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
563
|
+
return (error as { code: unknown }).code === 404;
|
|
550
564
|
}
|
|
565
|
+
return false;
|
|
551
566
|
}
|
|
552
567
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError,
|
|
1
|
+
import { CliError, httpStatusToErrorCode } from '../../utils/errors';
|
|
2
2
|
import type { ServiceClient, ValidationResult } from '../../types/service';
|
|
3
3
|
import type {
|
|
4
4
|
JiraCredentials,
|
|
@@ -86,7 +86,7 @@ export class JiraClient implements ServiceClient {
|
|
|
86
86
|
|
|
87
87
|
if (!response.ok) {
|
|
88
88
|
const errorText = await response.text();
|
|
89
|
-
const code =
|
|
89
|
+
const code = httpStatusToErrorCode(response.status);
|
|
90
90
|
throw new CliError(code, `JIRA API error: ${errorText}`);
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -98,14 +98,6 @@ export class JiraClient implements ServiceClient {
|
|
|
98
98
|
return response.json();
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
private getErrorCode(status: number): ErrorCode {
|
|
102
|
-
if (status === 401) return 'AUTH_FAILED';
|
|
103
|
-
if (status === 403) return 'PERMISSION_DENIED';
|
|
104
|
-
if (status === 404) return 'NOT_FOUND';
|
|
105
|
-
if (status === 429) return 'RATE_LIMITED';
|
|
106
|
-
return 'API_ERROR';
|
|
107
|
-
}
|
|
108
|
-
|
|
109
101
|
async listProjects(options: JiraProjectListOptions = {}): Promise<JiraProject[]> {
|
|
110
102
|
const params = new URLSearchParams();
|
|
111
103
|
if (options.maxResults) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError } from '../../utils/errors';
|
|
1
|
+
import { CliError, httpStatusToErrorCode } from '../../utils/errors';
|
|
2
2
|
import type { ServiceClient, ValidationResult } from '../../types/service';
|
|
3
3
|
import type {
|
|
4
4
|
SlackCredentials,
|
|
@@ -54,7 +54,7 @@ export class SlackClient implements ServiceClient {
|
|
|
54
54
|
if (!response.ok) {
|
|
55
55
|
const error = await response.text();
|
|
56
56
|
throw new CliError(
|
|
57
|
-
|
|
57
|
+
httpStatusToErrorCode(response.status),
|
|
58
58
|
`Failed to send message via webhook: ${response.status} ${error}`,
|
|
59
59
|
'Check that the webhook URL is valid and the app has permission to post'
|
|
60
60
|
);
|
|
@@ -74,11 +74,4 @@ export class SlackClient implements ServiceClient {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
private getErrorCode(status: number): 'AUTH_FAILED' | 'PERMISSION_DENIED' | 'NOT_FOUND' | 'RATE_LIMITED' | 'API_ERROR' {
|
|
78
|
-
if (status === 401) return 'AUTH_FAILED';
|
|
79
|
-
if (status === 403) return 'PERMISSION_DENIED';
|
|
80
|
-
if (status === 404) return 'NOT_FOUND';
|
|
81
|
-
if (status === 429) return 'RATE_LIMITED';
|
|
82
|
-
return 'API_ERROR';
|
|
83
|
-
}
|
|
84
77
|
}
|
package/src/types/telegram.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export interface TelegramCredentials {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
botToken: string;
|
|
3
|
+
channelId: string;
|
|
4
|
+
botUsername?: string;
|
|
5
|
+
channelName?: string;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export interface TelegramBotInfo {
|