@hasna/connectors 0.5.0 → 0.5.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/bin/index.js +147 -6
- package/bin/mcp.js +92 -1
- package/bin/serve.js +91 -0
- package/connectors/connect-ably/.env.example +11 -0
- package/connectors/connect-ably/CLAUDE.md +111 -0
- package/connectors/connect-ably/README.md +193 -0
- package/connectors/connect-ably/package.json +54 -0
- package/connectors/connect-ably/scripts/release.ts +179 -0
- package/connectors/connect-ably/src/api/channels.ts +33 -0
- package/connectors/connect-ably/src/api/client.ts +203 -0
- package/connectors/connect-ably/src/api/index.ts +59 -0
- package/connectors/connect-ably/src/api/messages.ts +48 -0
- package/connectors/connect-ably/src/api/presence.ts +39 -0
- package/connectors/connect-ably/src/api/stats.ts +29 -0
- package/connectors/connect-ably/src/cli/index.ts +397 -0
- package/connectors/connect-ably/src/index.ts +102 -0
- package/connectors/connect-ably/src/types/index.ts +294 -0
- package/connectors/connect-ably/src/utils/auth.ts +274 -0
- package/connectors/connect-ably/src/utils/bulk.ts +212 -0
- package/connectors/connect-ably/src/utils/config.ts +323 -0
- package/connectors/connect-ably/src/utils/output.ts +175 -0
- package/connectors/connect-ably/src/utils/settings.ts +114 -0
- package/connectors/connect-ably/src/utils/storage.ts +198 -0
- package/connectors/connect-ably/tsconfig.json +16 -0
- package/connectors/connect-box/.env.example +11 -0
- package/connectors/connect-box/CLAUDE.md +272 -0
- package/connectors/connect-box/README.md +193 -0
- package/connectors/connect-box/package.json +51 -0
- package/connectors/connect-box/scripts/release.ts +179 -0
- package/connectors/connect-box/src/api/client.ts +213 -0
- package/connectors/connect-box/src/api/example.ts +48 -0
- package/connectors/connect-box/src/api/index.ts +51 -0
- package/connectors/connect-box/src/cli/index.ts +254 -0
- package/connectors/connect-box/src/index.ts +103 -0
- package/connectors/connect-box/src/types/index.ts +237 -0
- package/connectors/connect-box/src/utils/auth.ts +274 -0
- package/connectors/connect-box/src/utils/bulk.ts +212 -0
- package/connectors/connect-box/src/utils/config.ts +326 -0
- package/connectors/connect-box/src/utils/output.ts +175 -0
- package/connectors/connect-box/src/utils/settings.ts +114 -0
- package/connectors/connect-box/src/utils/storage.ts +198 -0
- package/connectors/connect-box/tsconfig.json +16 -0
- package/connectors/connect-clearbit/.env.example +11 -0
- package/connectors/connect-clearbit/CLAUDE.md +272 -0
- package/connectors/connect-clearbit/README.md +193 -0
- package/connectors/connect-clearbit/package.json +51 -0
- package/connectors/connect-clearbit/scripts/release.ts +179 -0
- package/connectors/connect-clearbit/src/api/client.ts +213 -0
- package/connectors/connect-clearbit/src/api/example.ts +48 -0
- package/connectors/connect-clearbit/src/api/index.ts +51 -0
- package/connectors/connect-clearbit/src/cli/index.ts +254 -0
- package/connectors/connect-clearbit/src/index.ts +103 -0
- package/connectors/connect-clearbit/src/types/index.ts +237 -0
- package/connectors/connect-clearbit/src/utils/auth.ts +274 -0
- package/connectors/connect-clearbit/src/utils/bulk.ts +212 -0
- package/connectors/connect-clearbit/src/utils/config.ts +326 -0
- package/connectors/connect-clearbit/src/utils/output.ts +175 -0
- package/connectors/connect-clearbit/src/utils/settings.ts +114 -0
- package/connectors/connect-clearbit/src/utils/storage.ts +198 -0
- package/connectors/connect-clearbit/tsconfig.json +16 -0
- package/connectors/connect-coda/.env.example +11 -0
- package/connectors/connect-coda/CLAUDE.md +272 -0
- package/connectors/connect-coda/README.md +193 -0
- package/connectors/connect-coda/package.json +51 -0
- package/connectors/connect-coda/scripts/release.ts +179 -0
- package/connectors/connect-coda/src/api/client.ts +213 -0
- package/connectors/connect-coda/src/api/example.ts +48 -0
- package/connectors/connect-coda/src/api/index.ts +51 -0
- package/connectors/connect-coda/src/cli/index.ts +254 -0
- package/connectors/connect-coda/src/index.ts +103 -0
- package/connectors/connect-coda/src/types/index.ts +237 -0
- package/connectors/connect-coda/src/utils/auth.ts +274 -0
- package/connectors/connect-coda/src/utils/bulk.ts +212 -0
- package/connectors/connect-coda/src/utils/config.ts +326 -0
- package/connectors/connect-coda/src/utils/output.ts +175 -0
- package/connectors/connect-coda/src/utils/settings.ts +114 -0
- package/connectors/connect-coda/src/utils/storage.ts +198 -0
- package/connectors/connect-coda/tsconfig.json +16 -0
- package/connectors/connect-dropbox/.env.example +11 -0
- package/connectors/connect-dropbox/CLAUDE.md +119 -0
- package/connectors/connect-dropbox/README.md +193 -0
- package/connectors/connect-dropbox/package.json +51 -0
- package/connectors/connect-dropbox/src/api/client.ts +222 -0
- package/connectors/connect-dropbox/src/api/index.ts +395 -0
- package/connectors/connect-dropbox/src/cli/index.ts +627 -0
- package/connectors/connect-dropbox/src/index.ts +20 -0
- package/connectors/connect-dropbox/src/types/index.ts +516 -0
- package/connectors/connect-dropbox/src/utils/config.ts +197 -0
- package/connectors/connect-dropbox/tsconfig.json +16 -0
- package/connectors/connect-linode/.env.example +11 -0
- package/connectors/connect-linode/CLAUDE.md +272 -0
- package/connectors/connect-linode/README.md +193 -0
- package/connectors/connect-linode/package.json +51 -0
- package/connectors/connect-linode/scripts/release.ts +179 -0
- package/connectors/connect-linode/src/api/client.ts +213 -0
- package/connectors/connect-linode/src/api/example.ts +48 -0
- package/connectors/connect-linode/src/api/index.ts +51 -0
- package/connectors/connect-linode/src/cli/index.ts +254 -0
- package/connectors/connect-linode/src/index.ts +103 -0
- package/connectors/connect-linode/src/types/index.ts +237 -0
- package/connectors/connect-linode/src/utils/auth.ts +274 -0
- package/connectors/connect-linode/src/utils/bulk.ts +212 -0
- package/connectors/connect-linode/src/utils/config.ts +326 -0
- package/connectors/connect-linode/src/utils/output.ts +175 -0
- package/connectors/connect-linode/src/utils/settings.ts +114 -0
- package/connectors/connect-linode/src/utils/storage.ts +198 -0
- package/connectors/connect-linode/tsconfig.json +16 -0
- package/connectors/connect-mailgun/.env.example +11 -0
- package/connectors/connect-mailgun/CLAUDE.md +272 -0
- package/connectors/connect-mailgun/README.md +193 -0
- package/connectors/connect-mailgun/package.json +51 -0
- package/connectors/connect-mailgun/scripts/release.ts +179 -0
- package/connectors/connect-mailgun/src/api/client.ts +213 -0
- package/connectors/connect-mailgun/src/api/example.ts +48 -0
- package/connectors/connect-mailgun/src/api/index.ts +51 -0
- package/connectors/connect-mailgun/src/cli/index.ts +254 -0
- package/connectors/connect-mailgun/src/index.ts +103 -0
- package/connectors/connect-mailgun/src/types/index.ts +237 -0
- package/connectors/connect-mailgun/src/utils/auth.ts +274 -0
- package/connectors/connect-mailgun/src/utils/bulk.ts +212 -0
- package/connectors/connect-mailgun/src/utils/config.ts +326 -0
- package/connectors/connect-mailgun/src/utils/output.ts +175 -0
- package/connectors/connect-mailgun/src/utils/settings.ts +114 -0
- package/connectors/connect-mailgun/src/utils/storage.ts +198 -0
- package/connectors/connect-mailgun/tsconfig.json +16 -0
- package/connectors/connect-messagebird/.env.example +11 -0
- package/connectors/connect-messagebird/CLAUDE.md +272 -0
- package/connectors/connect-messagebird/README.md +193 -0
- package/connectors/connect-messagebird/package.json +51 -0
- package/connectors/connect-messagebird/scripts/release.ts +179 -0
- package/connectors/connect-messagebird/src/api/client.ts +213 -0
- package/connectors/connect-messagebird/src/api/example.ts +48 -0
- package/connectors/connect-messagebird/src/api/index.ts +51 -0
- package/connectors/connect-messagebird/src/cli/index.ts +254 -0
- package/connectors/connect-messagebird/src/index.ts +103 -0
- package/connectors/connect-messagebird/src/types/index.ts +237 -0
- package/connectors/connect-messagebird/src/utils/auth.ts +274 -0
- package/connectors/connect-messagebird/src/utils/bulk.ts +212 -0
- package/connectors/connect-messagebird/src/utils/config.ts +326 -0
- package/connectors/connect-messagebird/src/utils/output.ts +175 -0
- package/connectors/connect-messagebird/src/utils/settings.ts +114 -0
- package/connectors/connect-messagebird/src/utils/storage.ts +198 -0
- package/connectors/connect-messagebird/tsconfig.json +16 -0
- package/connectors/connect-miro/.env.example +11 -0
- package/connectors/connect-miro/CLAUDE.md +272 -0
- package/connectors/connect-miro/README.md +193 -0
- package/connectors/connect-miro/package.json +51 -0
- package/connectors/connect-miro/scripts/release.ts +179 -0
- package/connectors/connect-miro/src/api/client.ts +213 -0
- package/connectors/connect-miro/src/api/example.ts +48 -0
- package/connectors/connect-miro/src/api/index.ts +51 -0
- package/connectors/connect-miro/src/cli/index.ts +254 -0
- package/connectors/connect-miro/src/index.ts +103 -0
- package/connectors/connect-miro/src/types/index.ts +237 -0
- package/connectors/connect-miro/src/utils/auth.ts +274 -0
- package/connectors/connect-miro/src/utils/bulk.ts +212 -0
- package/connectors/connect-miro/src/utils/config.ts +326 -0
- package/connectors/connect-miro/src/utils/output.ts +175 -0
- package/connectors/connect-miro/src/utils/settings.ts +114 -0
- package/connectors/connect-miro/src/utils/storage.ts +198 -0
- package/connectors/connect-miro/tsconfig.json +16 -0
- package/connectors/connect-monday/.env.example +11 -0
- package/connectors/connect-monday/CLAUDE.md +128 -0
- package/connectors/connect-monday/README.md +193 -0
- package/connectors/connect-monday/package.json +52 -0
- package/connectors/connect-monday/src/api/client.ts +59 -0
- package/connectors/connect-monday/src/api/index.ts +539 -0
- package/connectors/connect-monday/src/cli/index.ts +479 -0
- package/connectors/connect-monday/src/index.ts +19 -0
- package/connectors/connect-monday/src/types/index.ts +274 -0
- package/connectors/connect-monday/src/utils/config.ts +197 -0
- package/connectors/connect-monday/src/utils/output.ts +119 -0
- package/connectors/connect-monday/tsconfig.json +16 -0
- package/connectors/connect-pipedrive/.env.example +11 -0
- package/connectors/connect-pipedrive/CLAUDE.md +128 -0
- package/connectors/connect-pipedrive/README.md +193 -0
- package/connectors/connect-pipedrive/package.json +52 -0
- package/connectors/connect-pipedrive/src/api/client.ts +121 -0
- package/connectors/connect-pipedrive/src/api/index.ts +306 -0
- package/connectors/connect-pipedrive/src/cli/index.ts +824 -0
- package/connectors/connect-pipedrive/src/index.ts +19 -0
- package/connectors/connect-pipedrive/src/types/index.ts +335 -0
- package/connectors/connect-pipedrive/src/utils/config.ts +171 -0
- package/connectors/connect-pipedrive/src/utils/output.ts +119 -0
- package/connectors/connect-pipedrive/tsconfig.json +16 -0
- package/connectors/connect-pusher/.env.example +11 -0
- package/connectors/connect-pusher/CLAUDE.md +272 -0
- package/connectors/connect-pusher/README.md +193 -0
- package/connectors/connect-pusher/package.json +51 -0
- package/connectors/connect-pusher/scripts/release.ts +179 -0
- package/connectors/connect-pusher/src/api/client.ts +213 -0
- package/connectors/connect-pusher/src/api/example.ts +48 -0
- package/connectors/connect-pusher/src/api/index.ts +51 -0
- package/connectors/connect-pusher/src/cli/index.ts +254 -0
- package/connectors/connect-pusher/src/index.ts +103 -0
- package/connectors/connect-pusher/src/types/index.ts +237 -0
- package/connectors/connect-pusher/src/utils/auth.ts +274 -0
- package/connectors/connect-pusher/src/utils/bulk.ts +212 -0
- package/connectors/connect-pusher/src/utils/config.ts +326 -0
- package/connectors/connect-pusher/src/utils/output.ts +175 -0
- package/connectors/connect-pusher/src/utils/settings.ts +114 -0
- package/connectors/connect-pusher/src/utils/storage.ts +198 -0
- package/connectors/connect-pusher/tsconfig.json +16 -0
- package/connectors/connect-vonage/.env.example +11 -0
- package/connectors/connect-vonage/CLAUDE.md +272 -0
- package/connectors/connect-vonage/README.md +193 -0
- package/connectors/connect-vonage/package.json +51 -0
- package/connectors/connect-vonage/scripts/release.ts +179 -0
- package/connectors/connect-vonage/src/api/client.ts +213 -0
- package/connectors/connect-vonage/src/api/example.ts +48 -0
- package/connectors/connect-vonage/src/api/index.ts +51 -0
- package/connectors/connect-vonage/src/cli/index.ts +254 -0
- package/connectors/connect-vonage/src/index.ts +103 -0
- package/connectors/connect-vonage/src/types/index.ts +237 -0
- package/connectors/connect-vonage/src/utils/auth.ts +274 -0
- package/connectors/connect-vonage/src/utils/bulk.ts +212 -0
- package/connectors/connect-vonage/src/utils/config.ts +326 -0
- package/connectors/connect-vonage/src/utils/output.ts +175 -0
- package/connectors/connect-vonage/src/utils/settings.ts +114 -0
- package/connectors/connect-vonage/src/utils/storage.ts +198 -0
- package/connectors/connect-vonage/tsconfig.json +16 -0
- package/dist/index.js +91 -0
- package/package.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { getConfigDir, ensureConfigDir } from './config';
|
|
4
|
+
|
|
5
|
+
// ============================================
|
|
6
|
+
// Settings Storage Utility
|
|
7
|
+
// ============================================
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* TODO: Define your connector-specific settings
|
|
11
|
+
* Add fields for user preferences, defaults, etc.
|
|
12
|
+
*/
|
|
13
|
+
export interface Settings {
|
|
14
|
+
// Display settings
|
|
15
|
+
defaultFormat: 'json' | 'table' | 'pretty';
|
|
16
|
+
|
|
17
|
+
// Behavior settings
|
|
18
|
+
confirmDestructive: boolean; // Require confirmation for destructive operations
|
|
19
|
+
verboseOutput: boolean; // Enable verbose output by default
|
|
20
|
+
|
|
21
|
+
// API settings
|
|
22
|
+
defaultMaxResults: number; // Default page size for list operations
|
|
23
|
+
requestTimeout: number; // Request timeout in ms
|
|
24
|
+
|
|
25
|
+
// Add more settings as needed for your connector
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DEFAULT_SETTINGS: Settings = {
|
|
29
|
+
defaultFormat: 'pretty',
|
|
30
|
+
confirmDestructive: true,
|
|
31
|
+
verboseOutput: false,
|
|
32
|
+
defaultMaxResults: 20,
|
|
33
|
+
requestTimeout: 30000,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function getSettingsPath(): string {
|
|
37
|
+
return join(getConfigDir(), 'settings.json');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load settings from disk, merging with defaults
|
|
42
|
+
*/
|
|
43
|
+
export function loadSettings(): Settings {
|
|
44
|
+
ensureConfigDir();
|
|
45
|
+
const filepath = getSettingsPath();
|
|
46
|
+
|
|
47
|
+
if (!existsSync(filepath)) {
|
|
48
|
+
// Create default settings file
|
|
49
|
+
saveSettings(DEFAULT_SETTINGS);
|
|
50
|
+
return DEFAULT_SETTINGS;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const content = readFileSync(filepath, 'utf-8');
|
|
55
|
+
const loaded = JSON.parse(content);
|
|
56
|
+
// Merge with defaults to ensure all fields exist
|
|
57
|
+
return { ...DEFAULT_SETTINGS, ...loaded };
|
|
58
|
+
} catch {
|
|
59
|
+
return DEFAULT_SETTINGS;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Save settings to disk
|
|
65
|
+
*/
|
|
66
|
+
export function saveSettings(settings: Settings): void {
|
|
67
|
+
ensureConfigDir();
|
|
68
|
+
const filepath = getSettingsPath();
|
|
69
|
+
writeFileSync(filepath, JSON.stringify(settings, null, 2));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get a specific setting value
|
|
74
|
+
*/
|
|
75
|
+
export function getSetting<K extends keyof Settings>(key: K): Settings[K] {
|
|
76
|
+
return loadSettings()[key];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set a specific setting value
|
|
81
|
+
*/
|
|
82
|
+
export function setSetting<K extends keyof Settings>(key: K, value: Settings[K]): void {
|
|
83
|
+
const settings = loadSettings();
|
|
84
|
+
settings[key] = value;
|
|
85
|
+
saveSettings(settings);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Reset settings to defaults
|
|
90
|
+
*/
|
|
91
|
+
export function resetSettings(): void {
|
|
92
|
+
saveSettings(DEFAULT_SETTINGS);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get default settings (useful for CLI help text)
|
|
97
|
+
*/
|
|
98
|
+
export function getDefaultSettings(): Settings {
|
|
99
|
+
return { ...DEFAULT_SETTINGS };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if verbose output is enabled
|
|
104
|
+
*/
|
|
105
|
+
export function isVerbose(): boolean {
|
|
106
|
+
return loadSettings().verboseOutput;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if destructive operations need confirmation
|
|
111
|
+
*/
|
|
112
|
+
export function needsConfirmation(): boolean {
|
|
113
|
+
return loadSettings().confirmDestructive;
|
|
114
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { getConfigDir } from './config';
|
|
4
|
+
|
|
5
|
+
// ============================================
|
|
6
|
+
// Generic Data Storage Utility
|
|
7
|
+
// ============================================
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base interface for storable entities
|
|
11
|
+
* All stored entities should have an id field
|
|
12
|
+
*/
|
|
13
|
+
export interface Storable {
|
|
14
|
+
id: string;
|
|
15
|
+
createdAt?: string;
|
|
16
|
+
updatedAt?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the storage directory for an entity type
|
|
21
|
+
*/
|
|
22
|
+
function getStorageDir(entityType: string): string {
|
|
23
|
+
const dir = join(getConfigDir(), 'data', entityType);
|
|
24
|
+
if (!existsSync(dir)) {
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
return dir;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Convert an ID to a safe filename
|
|
32
|
+
*/
|
|
33
|
+
function idToFilename(id: string): string {
|
|
34
|
+
return id.toLowerCase().replace(/[^a-z0-9]/g, '_') + '.json';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the file path for an entity
|
|
39
|
+
*/
|
|
40
|
+
function getEntityPath(entityType: string, id: string): string {
|
|
41
|
+
return join(getStorageDir(entityType), idToFilename(id));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Save an entity to storage
|
|
46
|
+
*/
|
|
47
|
+
export function saveEntity<T extends Storable>(entityType: string, entity: T): void {
|
|
48
|
+
const filepath = getEntityPath(entityType, entity.id);
|
|
49
|
+
const now = new Date().toISOString();
|
|
50
|
+
|
|
51
|
+
const existing = getEntity<T>(entityType, entity.id);
|
|
52
|
+
|
|
53
|
+
const data: T = {
|
|
54
|
+
...entity,
|
|
55
|
+
createdAt: existing?.createdAt || now,
|
|
56
|
+
updatedAt: now,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
writeFileSync(filepath, JSON.stringify(data, null, 2));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get an entity by ID
|
|
64
|
+
*/
|
|
65
|
+
export function getEntity<T extends Storable>(entityType: string, id: string): T | null {
|
|
66
|
+
const filepath = getEntityPath(entityType, id);
|
|
67
|
+
|
|
68
|
+
if (!existsSync(filepath)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const content = readFileSync(filepath, 'utf-8');
|
|
74
|
+
return JSON.parse(content) as T;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if an entity exists
|
|
82
|
+
*/
|
|
83
|
+
export function entityExists(entityType: string, id: string): boolean {
|
|
84
|
+
return existsSync(getEntityPath(entityType, id));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get all entities of a type
|
|
89
|
+
*/
|
|
90
|
+
export function getAllEntities<T extends Storable>(entityType: string): T[] {
|
|
91
|
+
const storageDir = getStorageDir(entityType);
|
|
92
|
+
const files = readdirSync(storageDir).filter(f => f.endsWith('.json'));
|
|
93
|
+
|
|
94
|
+
const entities: T[] = [];
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
try {
|
|
97
|
+
const content = readFileSync(join(storageDir, file), 'utf-8');
|
|
98
|
+
entities.push(JSON.parse(content) as T);
|
|
99
|
+
} catch {
|
|
100
|
+
// Skip invalid files
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return entities.sort((a, b) => a.id.localeCompare(b.id));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Delete an entity
|
|
109
|
+
*/
|
|
110
|
+
export function deleteEntity(entityType: string, id: string): boolean {
|
|
111
|
+
const filepath = getEntityPath(entityType, id);
|
|
112
|
+
|
|
113
|
+
if (existsSync(filepath)) {
|
|
114
|
+
unlinkSync(filepath);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Search entities by a predicate function
|
|
122
|
+
*/
|
|
123
|
+
export function searchEntities<T extends Storable>(
|
|
124
|
+
entityType: string,
|
|
125
|
+
predicate: (entity: T) => boolean
|
|
126
|
+
): T[] {
|
|
127
|
+
const all = getAllEntities<T>(entityType);
|
|
128
|
+
return all.filter(predicate);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Search entities by text match across all string fields
|
|
133
|
+
*/
|
|
134
|
+
export function searchEntitiesByText<T extends Storable>(
|
|
135
|
+
entityType: string,
|
|
136
|
+
query: string,
|
|
137
|
+
fields?: (keyof T)[]
|
|
138
|
+
): T[] {
|
|
139
|
+
const all = getAllEntities<T>(entityType);
|
|
140
|
+
const q = query.toLowerCase();
|
|
141
|
+
|
|
142
|
+
return all.filter(entity => {
|
|
143
|
+
const searchFields = fields || (Object.keys(entity) as (keyof T)[]);
|
|
144
|
+
return searchFields.some(field => {
|
|
145
|
+
const value = entity[field];
|
|
146
|
+
if (typeof value === 'string') {
|
|
147
|
+
return value.toLowerCase().includes(q);
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Count entities of a type
|
|
156
|
+
*/
|
|
157
|
+
export function countEntities(entityType: string): number {
|
|
158
|
+
const storageDir = getStorageDir(entityType);
|
|
159
|
+
if (!existsSync(storageDir)) {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
return readdirSync(storageDir).filter(f => f.endsWith('.json')).length;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Clear all entities of a type
|
|
167
|
+
*/
|
|
168
|
+
export function clearEntities(entityType: string): number {
|
|
169
|
+
const storageDir = getStorageDir(entityType);
|
|
170
|
+
if (!existsSync(storageDir)) {
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const files = readdirSync(storageDir).filter(f => f.endsWith('.json'));
|
|
175
|
+
for (const file of files) {
|
|
176
|
+
unlinkSync(join(storageDir, file));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return files.length;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Create a typed storage helper for a specific entity type
|
|
184
|
+
* This provides a cleaner API for working with a single entity type
|
|
185
|
+
*/
|
|
186
|
+
export function createStorage<T extends Storable>(entityType: string) {
|
|
187
|
+
return {
|
|
188
|
+
save: (entity: T) => saveEntity<T>(entityType, entity),
|
|
189
|
+
get: (id: string) => getEntity<T>(entityType, id),
|
|
190
|
+
exists: (id: string) => entityExists(entityType, id),
|
|
191
|
+
getAll: () => getAllEntities<T>(entityType),
|
|
192
|
+
delete: (id: string) => deleteEntity(entityType, id),
|
|
193
|
+
search: (predicate: (entity: T) => boolean) => searchEntities<T>(entityType, predicate),
|
|
194
|
+
searchByText: (query: string, fields?: (keyof T)[]) => searchEntitiesByText<T>(entityType, query, fields),
|
|
195
|
+
count: () => countEntities(entityType),
|
|
196
|
+
clear: () => clearEntities(entityType),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"rootDir": "./src",
|
|
12
|
+
"types": ["bun-types"]
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist", "bin"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# API Credentials
|
|
2
|
+
# TODO: Update variable names and instructions for your API
|
|
3
|
+
# Example: PERPLEXITY_API_KEY, OPENAI_API_KEY, etc.
|
|
4
|
+
|
|
5
|
+
CONNECTOR_API_KEY=your-api-key-here
|
|
6
|
+
|
|
7
|
+
# Optional: API secret (if your API requires it)
|
|
8
|
+
# CONNECTOR_API_SECRET=your-api-secret-here
|
|
9
|
+
|
|
10
|
+
# Optional: Custom base URL (if needed)
|
|
11
|
+
# CONNECTOR_BASE_URL=https://api.example.com
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
scaffold-connector is a TypeScript template for building API connector CLIs. It provides multi-profile configuration, Bearer token authentication (customizable), OAuth2 support, and a clean CLI structure using Commander.js.
|
|
8
|
+
|
|
9
|
+
**This is a SCAFFOLD** - meant to be cloned and customized for specific APIs.
|
|
10
|
+
|
|
11
|
+
## Build & Run Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Install dependencies
|
|
15
|
+
bun install
|
|
16
|
+
|
|
17
|
+
# Run CLI in development
|
|
18
|
+
bun run dev
|
|
19
|
+
|
|
20
|
+
# Build for distribution
|
|
21
|
+
bun run build
|
|
22
|
+
|
|
23
|
+
# Type check
|
|
24
|
+
bun run typecheck
|
|
25
|
+
|
|
26
|
+
# Run specific commands
|
|
27
|
+
bun run dev profile list
|
|
28
|
+
bun run dev config show
|
|
29
|
+
bun run dev example list
|
|
30
|
+
|
|
31
|
+
# Release (auto-bump patch version and publish)
|
|
32
|
+
bun run release
|
|
33
|
+
bun run release:dry # Preview only
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Code Style
|
|
37
|
+
|
|
38
|
+
- TypeScript with strict mode
|
|
39
|
+
- ESM modules (type: module)
|
|
40
|
+
- Use async/await for all async operations
|
|
41
|
+
- Minimal dependencies: commander, chalk only
|
|
42
|
+
- Type annotations required everywhere
|
|
43
|
+
- Use interfaces for all API types
|
|
44
|
+
|
|
45
|
+
## Project Structure
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
src/
|
|
49
|
+
├── api/
|
|
50
|
+
│ ├── client.ts # HTTP client with auth, retry, timeout
|
|
51
|
+
│ ├── example.ts # Example API module (template)
|
|
52
|
+
│ └── index.ts # Main connector class
|
|
53
|
+
├── cli/
|
|
54
|
+
│ └── index.ts # CLI commands
|
|
55
|
+
├── types/
|
|
56
|
+
│ └── index.ts # Type definitions
|
|
57
|
+
├── utils/
|
|
58
|
+
│ ├── auth.ts # OAuth2 authentication
|
|
59
|
+
│ ├── bulk.ts # Bulk operation utilities
|
|
60
|
+
│ ├── config.ts # Multi-profile configuration
|
|
61
|
+
│ ├── output.ts # CLI output formatting
|
|
62
|
+
│ ├── settings.ts # User preferences storage
|
|
63
|
+
│ └── storage.ts # Local data storage
|
|
64
|
+
├── index.ts # Library exports
|
|
65
|
+
scripts/
|
|
66
|
+
└── release.ts # Release automation
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Key Patterns
|
|
70
|
+
|
|
71
|
+
### Multi-Profile Configuration
|
|
72
|
+
|
|
73
|
+
Profiles stored in `~/.connect/{connector-name}/profiles/`:
|
|
74
|
+
- Each profile is a separate JSON file
|
|
75
|
+
- `current_profile` file tracks active profile
|
|
76
|
+
- `--profile` flag overrides for single command
|
|
77
|
+
- Environment variables override profile config
|
|
78
|
+
|
|
79
|
+
### Authentication
|
|
80
|
+
|
|
81
|
+
**Bearer Token (Default)** in `src/api/client.ts`:
|
|
82
|
+
```typescript
|
|
83
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Supported Auth Methods:**
|
|
87
|
+
- Bearer token: `'Authorization': 'Bearer ${token}'`
|
|
88
|
+
- API Key header: `'X-API-Key': ${apiKey}`
|
|
89
|
+
- Basic auth: `'Authorization': 'Basic ' + base64(key:secret)`
|
|
90
|
+
- OAuth2: Use the auth utilities (see below)
|
|
91
|
+
|
|
92
|
+
### OAuth2 Authentication
|
|
93
|
+
|
|
94
|
+
For APIs that require OAuth2, use the auth utilities:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { getAuthUrl, startCallbackServer, getValidAccessToken } from './utils/auth';
|
|
98
|
+
|
|
99
|
+
// Start OAuth flow
|
|
100
|
+
const authUrl = getAuthUrl({ scopes: 'read write' });
|
|
101
|
+
// Open authUrl in browser
|
|
102
|
+
const result = await startCallbackServer();
|
|
103
|
+
if (result.success) {
|
|
104
|
+
saveOAuthTokens(result.tokens);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Get valid access token (auto-refreshes if needed)
|
|
108
|
+
const token = await getValidAccessToken();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Settings Storage
|
|
112
|
+
|
|
113
|
+
Store user preferences with the settings utility:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { getSetting, setSetting, loadSettings } from './utils/settings';
|
|
117
|
+
|
|
118
|
+
// Get a setting
|
|
119
|
+
const format = getSetting('defaultFormat');
|
|
120
|
+
|
|
121
|
+
// Set a setting
|
|
122
|
+
setSetting('verboseOutput', true);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Local Data Storage
|
|
126
|
+
|
|
127
|
+
Store local data (like contacts, cache) with the storage utility:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { createStorage, type Storable } from './utils/storage';
|
|
131
|
+
|
|
132
|
+
interface Contact extends Storable {
|
|
133
|
+
id: string;
|
|
134
|
+
email: string;
|
|
135
|
+
name?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const contacts = createStorage<Contact>('contacts');
|
|
139
|
+
|
|
140
|
+
// Save
|
|
141
|
+
contacts.save({ id: 'user@example.com', email: 'user@example.com', name: 'User' });
|
|
142
|
+
|
|
143
|
+
// Get
|
|
144
|
+
const contact = contacts.get('user@example.com');
|
|
145
|
+
|
|
146
|
+
// Search
|
|
147
|
+
const results = contacts.searchByText('example');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Bulk Operations
|
|
151
|
+
|
|
152
|
+
Process multiple items with concurrency control:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { executeBulk, createProgressReporter } from './utils/bulk';
|
|
156
|
+
|
|
157
|
+
const result = await executeBulk(
|
|
158
|
+
{
|
|
159
|
+
items: users,
|
|
160
|
+
concurrency: 5,
|
|
161
|
+
dryRun: false,
|
|
162
|
+
onProgress: createProgressReporter('Updating users'),
|
|
163
|
+
},
|
|
164
|
+
async (user) => {
|
|
165
|
+
await api.updateUser(user.id, { status: 'active' });
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
console.log(`Success: ${result.success}, Failed: ${result.failed}`);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Retry and Rate Limiting
|
|
173
|
+
|
|
174
|
+
The HTTP client includes built-in retry logic:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Retries are automatic for 429 (rate limit) and 5xx errors
|
|
178
|
+
const data = await client.get('/endpoint', { retries: 3, timeout: 30000 });
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Adding New API Modules
|
|
182
|
+
|
|
183
|
+
1. Create file in `src/api/` following `example.ts` pattern
|
|
184
|
+
2. Add to exports in `src/api/index.ts`
|
|
185
|
+
3. Add types in `src/types/index.ts`
|
|
186
|
+
4. Add CLI commands in `src/cli/index.ts`
|
|
187
|
+
|
|
188
|
+
## TODO Markers
|
|
189
|
+
|
|
190
|
+
When customizing this scaffold, search for `TODO` comments:
|
|
191
|
+
|
|
192
|
+
- `src/cli/index.ts:22-24` - CONNECTOR_NAME, VERSION, description
|
|
193
|
+
- `src/utils/config.ts:5-6` - CONNECTOR_NAME, env var prefix
|
|
194
|
+
- `src/utils/auth.ts:10-15` - OAuth URLs and scopes
|
|
195
|
+
- `src/api/client.ts:5` - DEFAULT_BASE_URL
|
|
196
|
+
- `src/api/client.ts:55-60` - Authentication method
|
|
197
|
+
- `src/api/index.ts:7` - Rename Connector class
|
|
198
|
+
- `src/types/index.ts` - Replace example types
|
|
199
|
+
|
|
200
|
+
## Environment Variables
|
|
201
|
+
|
|
202
|
+
| Variable | Description |
|
|
203
|
+
|----------|-------------|
|
|
204
|
+
| `CONNECTOR_API_KEY` | API key (overrides profile) |
|
|
205
|
+
| `CONNECTOR_TOKEN` | Token (alias for API key) |
|
|
206
|
+
| `CONNECTOR_API_SECRET` | API secret (optional) |
|
|
207
|
+
| `CONNECTOR_BASE_URL` | Override base URL |
|
|
208
|
+
|
|
209
|
+
## CLI Global Flags
|
|
210
|
+
|
|
211
|
+
| Flag | Description |
|
|
212
|
+
|------|-------------|
|
|
213
|
+
| `-k, --api-key <key>` | Override API key for this command |
|
|
214
|
+
| `-p, --profile <name>` | Use specific profile |
|
|
215
|
+
| `-f, --format <format>` | Output format (json, pretty, table) |
|
|
216
|
+
| `-v, --verbose` | Enable debug output |
|
|
217
|
+
|
|
218
|
+
## Data Storage
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
~/.connect/{connector-name}/
|
|
222
|
+
├── current_profile # Active profile name
|
|
223
|
+
├── settings.json # User preferences
|
|
224
|
+
├── data/ # Local data storage
|
|
225
|
+
│ └── {entity}/ # Entity-specific storage
|
|
226
|
+
│ └── *.json
|
|
227
|
+
└── profiles/
|
|
228
|
+
├── default.json # Default profile
|
|
229
|
+
└── {name}.json # Named profiles
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Profile JSON structure:
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"apiKey": "sk-xxx",
|
|
236
|
+
"token": "sk-xxx",
|
|
237
|
+
"apiSecret": "optional",
|
|
238
|
+
"accessToken": "oauth-access-token",
|
|
239
|
+
"refreshToken": "oauth-refresh-token",
|
|
240
|
+
"expiresAt": 1234567890,
|
|
241
|
+
"clientId": "oauth-client-id",
|
|
242
|
+
"clientSecret": "oauth-client-secret"
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Dependencies
|
|
247
|
+
|
|
248
|
+
- commander: CLI framework
|
|
249
|
+
- chalk: Terminal styling
|
|
250
|
+
|
|
251
|
+
## Error Handling
|
|
252
|
+
|
|
253
|
+
The scaffold includes enhanced error types:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { ConnectorApiError, parseApiError } from './types';
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
await api.get('/endpoint');
|
|
260
|
+
} catch (err) {
|
|
261
|
+
if (err instanceof ConnectorApiError) {
|
|
262
|
+
if (err.isRateLimited()) {
|
|
263
|
+
// Handle rate limiting
|
|
264
|
+
}
|
|
265
|
+
if (err.isAuthError()) {
|
|
266
|
+
// Handle auth errors
|
|
267
|
+
}
|
|
268
|
+
console.log(err.getUserMessage());
|
|
269
|
+
console.log(err.documentationUrl);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|