@plosson/agentio 0.3.1 → 0.4.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.
@@ -0,0 +1,81 @@
1
+ export type GDriveAccessLevel = 'readonly' | 'full';
2
+
3
+ export interface GDriveCredentials {
4
+ accessToken: string;
5
+ refreshToken?: string;
6
+ expiryDate?: number;
7
+ tokenType: string;
8
+ scope?: string;
9
+ email: string;
10
+ accessLevel: GDriveAccessLevel;
11
+ }
12
+
13
+ export interface GDriveFile {
14
+ id: string;
15
+ name: string;
16
+ mimeType: string;
17
+ size?: number;
18
+ createdTime?: string;
19
+ modifiedTime?: string;
20
+ owners?: string[];
21
+ parents?: string[];
22
+ webViewLink?: string;
23
+ webContentLink?: string;
24
+ starred: boolean;
25
+ trashed: boolean;
26
+ shared: boolean;
27
+ description?: string;
28
+ }
29
+
30
+ export interface GDriveListOptions {
31
+ limit?: number;
32
+ folderId?: string;
33
+ query?: string;
34
+ orderBy?: string;
35
+ includeTrash?: boolean;
36
+ }
37
+
38
+ export interface GDriveSearchOptions {
39
+ query: string;
40
+ mimeType?: string;
41
+ limit?: number;
42
+ folderId?: string;
43
+ }
44
+
45
+ export interface GDriveFolderListOptions {
46
+ limit?: number;
47
+ parentId?: string;
48
+ query?: string;
49
+ }
50
+
51
+ export interface GDriveDownloadOptions {
52
+ fileIdOrUrl: string;
53
+ outputPath: string;
54
+ exportFormat?: GDriveExportFormat;
55
+ }
56
+
57
+ export interface GDriveDownloadResult {
58
+ filename: string;
59
+ path: string;
60
+ size: number;
61
+ mimeType: string;
62
+ }
63
+
64
+ // Export formats for Google Workspace files
65
+ export type GDriveExportFormat = 'pdf' | 'docx' | 'xlsx' | 'csv' | 'pptx' | 'txt' | 'html' | 'odt' | 'ods' | 'odp' | 'rtf' | 'tsv' | 'png' | 'jpeg' | 'svg';
66
+
67
+ export interface GDriveUploadOptions {
68
+ filePath: string;
69
+ name?: string;
70
+ folderId?: string;
71
+ mimeType?: string;
72
+ convert?: boolean;
73
+ }
74
+
75
+ export interface GDriveUploadResult {
76
+ id: string;
77
+ name: string;
78
+ mimeType: string;
79
+ size: number;
80
+ webViewLink?: string;
81
+ }
@@ -1,8 +1,8 @@
1
1
  export interface TelegramCredentials {
2
- bot_token: string;
3
- channel_id: string;
4
- bot_username?: string;
5
- channel_name?: string;
2
+ botToken: string;
3
+ channelId: string;
4
+ botUsername?: string;
5
+ channelName?: string;
6
6
  }
7
7
 
8
8
  export interface TelegramBotInfo {
@@ -0,0 +1,56 @@
1
+ import { getCredentials } from '../auth/token-store';
2
+ import { resolveProfile } from '../config/config-manager';
3
+ import { CliError } from './errors';
4
+ import type { ServiceName } from '../types/config';
5
+
6
+ export interface ClientFactoryConfig<TCredentials, TClient> {
7
+ service: ServiceName;
8
+ createClient: (credentials: TCredentials) => TClient;
9
+ }
10
+
11
+ /**
12
+ * Creates a type-safe client getter function for a service.
13
+ * Profile is optional - if only one profile exists, it will be used automatically.
14
+ *
15
+ * Usage:
16
+ * ```typescript
17
+ * const getSlackClient = createClientGetter<SlackCredentials, SlackClient>({
18
+ * service: 'slack',
19
+ * createClient: (credentials) => new SlackClient(credentials),
20
+ * });
21
+ * ```
22
+ */
23
+ export function createClientGetter<TCredentials, TClient>(
24
+ config: ClientFactoryConfig<TCredentials, TClient>
25
+ ): (profileName?: string) => Promise<{ client: TClient; profile: string }> {
26
+ const { service, createClient } = config;
27
+
28
+ return async (profileName?: string): Promise<{ client: TClient; profile: string }> => {
29
+ const { profile, error } = await resolveProfile(service, profileName);
30
+
31
+ if (!profile) {
32
+ if (error === 'none') {
33
+ throw new CliError('PROFILE_NOT_FOUND', `No ${service} profile configured`, `Run: agentio ${service} profile add`);
34
+ }
35
+ if (error === 'multiple') {
36
+ throw new CliError('PROFILE_NOT_FOUND', `Multiple ${service} profiles exist`, 'Specify --profile <name>');
37
+ }
38
+ throw new CliError('PROFILE_NOT_FOUND', `Profile "${profileName}" not found for ${service}`, `Run: agentio ${service} profile add`);
39
+ }
40
+
41
+ const credentials = await getCredentials<TCredentials>(service, profile);
42
+
43
+ if (!credentials) {
44
+ throw new CliError(
45
+ 'AUTH_FAILED',
46
+ `No credentials found for ${service} profile "${profile}"`,
47
+ `Run: agentio ${service} profile add --profile ${profile}`
48
+ );
49
+ }
50
+
51
+ return {
52
+ client: createClient(credentials),
53
+ profile,
54
+ };
55
+ };
56
+ }
@@ -10,6 +10,18 @@ export type ErrorCode =
10
10
  | 'NOT_FOUND'
11
11
  | 'CONFIG_ERROR';
12
12
 
13
+ /**
14
+ * Map HTTP status codes to standard error codes.
15
+ * Use this in API clients to standardize error handling.
16
+ */
17
+ export function httpStatusToErrorCode(status: number): ErrorCode {
18
+ if (status === 401) return 'AUTH_FAILED';
19
+ if (status === 403) return 'PERMISSION_DENIED';
20
+ if (status === 404) return 'NOT_FOUND';
21
+ if (status === 429) return 'RATE_LIMITED';
22
+ return 'API_ERROR';
23
+ }
24
+
13
25
  export class CliError extends Error {
14
26
  constructor(
15
27
  public code: ErrorCode,
@@ -1,5 +1,18 @@
1
1
  import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
2
2
 
3
+ /**
4
+ * Simple obfuscation utilities for embedding secrets in source code.
5
+ *
6
+ * NOT for real security - just prevents secret scanners from flagging embedded
7
+ * OAuth client secrets. Uses the same pattern as rclone.
8
+ *
9
+ * obscure() is exported for generating new obscured values (run in a scratch file):
10
+ * import { obscure } from './src/utils/obscure';
11
+ * console.log(obscure('your-secret-here'));
12
+ *
13
+ * reveal() is used at runtime to decode the obscured values.
14
+ */
15
+
3
16
  // Hardcoded key for obfuscation (not real security, just to avoid secret scanners)
4
17
  // This is the same pattern rclone uses
5
18
  const OBSCURE_KEY = Buffer.from('9c935b2aa628f0e9d48d5f3e8a4b7c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b', 'hex');
@@ -1,5 +1,7 @@
1
1
  import type { GmailMessage, GmailAttachmentInfo } from '../types/gmail';
2
2
  import type { GChatMessage, GChatSpace } from '../types/gchat';
3
+ import type { GDocsDocument, GDocsCreateResult } from '../types/gdocs';
4
+ import type { GDriveFile, GDriveDownloadResult, GDriveUploadResult } from '../types/gdrive';
3
5
  import type { JiraProject, JiraIssue, JiraTransition, JiraCommentResult, JiraTransitionResult } from '../types/jira';
4
6
  import type { SlackSendResult } from '../types/slack';
5
7
  import type { RssFeed, RssArticle } from '../types/rss';
@@ -373,3 +375,110 @@ export function printDiscourseTopic(topic: DiscourseTopicDetail): void {
373
375
  console.log(content);
374
376
  }
375
377
  }
378
+
379
+ // Google Docs specific formatters
380
+ export function printGDocsList(docs: GDocsDocument[]): void {
381
+ if (docs.length === 0) {
382
+ console.log('No documents found');
383
+ return;
384
+ }
385
+
386
+ console.log(`Documents (${docs.length})\n`);
387
+
388
+ for (let i = 0; i < docs.length; i++) {
389
+ const doc = docs[i];
390
+ console.log(`[${i + 1}] ${doc.title}`);
391
+ console.log(` ID: ${doc.id}`);
392
+ if (doc.owner) console.log(` Owner: ${doc.owner}`);
393
+ if (doc.modifiedTime) console.log(` Modified: ${doc.modifiedTime}`);
394
+ console.log(` Link: ${doc.webViewLink}`);
395
+ console.log('');
396
+ }
397
+ }
398
+
399
+ export function printGDocCreated(result: GDocsCreateResult): void {
400
+ console.log('Document created');
401
+ console.log(`ID: ${result.id}`);
402
+ console.log(`Title: ${result.title}`);
403
+ console.log(`Link: ${result.webViewLink}`);
404
+ }
405
+
406
+ // Google Drive specific formatters
407
+ function getShortMimeType(mimeType: string): string {
408
+ const shortTypes: Record<string, string> = {
409
+ 'application/vnd.google-apps.folder': 'folder',
410
+ 'application/vnd.google-apps.document': 'gdoc',
411
+ 'application/vnd.google-apps.spreadsheet': 'gsheet',
412
+ 'application/vnd.google-apps.presentation': 'gslide',
413
+ 'application/pdf': 'pdf',
414
+ 'image/png': 'png',
415
+ 'image/jpeg': 'jpg',
416
+ 'text/plain': 'txt',
417
+ 'application/zip': 'zip',
418
+ };
419
+ if (shortTypes[mimeType]) return shortTypes[mimeType];
420
+ if (mimeType.startsWith('image/')) return 'image';
421
+ if (mimeType.startsWith('video/')) return 'video';
422
+ if (mimeType.startsWith('audio/')) return 'audio';
423
+ if (mimeType.startsWith('text/')) return 'text';
424
+ return 'file';
425
+ }
426
+
427
+ export function printGDriveFileList(files: GDriveFile[], title: string = 'Files'): void {
428
+ if (files.length === 0) {
429
+ console.log('No files found');
430
+ return;
431
+ }
432
+
433
+ console.log(`${title} (${files.length})\n`);
434
+
435
+ for (let i = 0; i < files.length; i++) {
436
+ const file = files[i];
437
+ const isFolder = file.mimeType === 'application/vnd.google-apps.folder';
438
+ const typeIndicator = isFolder ? '[folder]' : `[${getShortMimeType(file.mimeType)}]`;
439
+ const flags: string[] = [];
440
+ if (file.starred) flags.push('*');
441
+ if (file.shared) flags.push('shared');
442
+ const flagStr = flags.length > 0 ? ` (${flags.join(', ')})` : '';
443
+
444
+ console.log(`[${i + 1}] ${file.name} ${typeIndicator}${flagStr}`);
445
+ console.log(` ID: ${file.id}`);
446
+ if (file.size) console.log(` Size: ${formatBytes(file.size)}`);
447
+ if (file.owners?.length) console.log(` Owner: ${file.owners[0]}`);
448
+ if (file.modifiedTime) console.log(` Modified: ${file.modifiedTime}`);
449
+ if (file.webViewLink) console.log(` Link: ${file.webViewLink}`);
450
+ console.log('');
451
+ }
452
+ }
453
+
454
+ export function printGDriveFile(file: GDriveFile): void {
455
+ console.log(`ID: ${file.id}`);
456
+ console.log(`Name: ${file.name}`);
457
+ console.log(`Type: ${file.mimeType}`);
458
+ if (file.size) console.log(`Size: ${formatBytes(file.size)}`);
459
+ if (file.description) console.log(`Description: ${file.description}`);
460
+ if (file.owners?.length) console.log(`Owners: ${file.owners.join(', ')}`);
461
+ if (file.parents?.length) console.log(`Parents: ${file.parents.join(', ')}`);
462
+ console.log(`Starred: ${file.starred ? 'yes' : 'no'}`);
463
+ console.log(`Shared: ${file.shared ? 'yes' : 'no'}`);
464
+ console.log(`Trashed: ${file.trashed ? 'yes' : 'no'}`);
465
+ if (file.createdTime) console.log(`Created: ${file.createdTime}`);
466
+ if (file.modifiedTime) console.log(`Modified: ${file.modifiedTime}`);
467
+ if (file.webViewLink) console.log(`View: ${file.webViewLink}`);
468
+ if (file.webContentLink) console.log(`Download: ${file.webContentLink}`);
469
+ }
470
+
471
+ export function printGDriveDownloaded(result: GDriveDownloadResult): void {
472
+ console.log(`Downloaded: ${result.filename}`);
473
+ console.log(` Path: ${result.path}`);
474
+ console.log(` Size: ${formatBytes(result.size)}`);
475
+ console.log(` Type: ${result.mimeType}`);
476
+ }
477
+
478
+ export function printGDriveUploaded(result: GDriveUploadResult): void {
479
+ console.log(`Uploaded: ${result.name}`);
480
+ console.log(` ID: ${result.id}`);
481
+ console.log(` Size: ${formatBytes(result.size)}`);
482
+ console.log(` Type: ${result.mimeType}`);
483
+ if (result.webViewLink) console.log(` Link: ${result.webViewLink}`);
484
+ }