@hasna/skills 0.1.32 → 0.1.33

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.
Files changed (80) hide show
  1. package/README.md +1 -16
  2. package/bin/index.js +57 -14
  3. package/bin/mcp.js +57 -13
  4. package/dist/cli/commands/runtime.d.ts +1 -1
  5. package/dist/index.js +51 -5
  6. package/dist/lib/skill-validation.d.ts +2 -0
  7. package/package.json +1 -1
  8. package/skills/ad-creative-pack/package.json +4 -7
  9. package/skills/api-docs-portal/package.json +5 -3
  10. package/skills/audio/package.json +4 -22
  11. package/skills/audio-transcript-pack/package.json +3 -2
  12. package/skills/blog-article/package.json +5 -21
  13. package/skills/brand-kit/package.json +4 -7
  14. package/skills/brand-photo-shoot/package.json +3 -2
  15. package/skills/browse/package.json +4 -33
  16. package/skills/contract-review-report/package.json +3 -2
  17. package/skills/customer-feedback-report/package.json +3 -2
  18. package/skills/deepresearch/package.json +4 -37
  19. package/skills/domainpurchase/SKILL.md +14 -31
  20. package/skills/domainpurchase/package.json +3 -8
  21. package/skills/email-sequence/package.json +4 -7
  22. package/skills/generate-book-cover/package.json +5 -23
  23. package/skills/icon-pack/package.json +4 -22
  24. package/skills/image/package.json +4 -22
  25. package/skills/invoice-reconciliation/package.json +3 -2
  26. package/skills/landing-page-pack/package.json +4 -7
  27. package/skills/logo-design/package.json +4 -2
  28. package/skills/managemcp/CLAUDE.md +9 -43
  29. package/skills/managemcp/README.md +12 -198
  30. package/skills/managemcp/package.json +2 -10
  31. package/skills/managemcp/tsconfig.json +1 -3
  32. package/skills/market-research-report/package.json +5 -4
  33. package/skills/meeting-pack/package.json +3 -2
  34. package/skills/migration-plan-pack/package.json +3 -2
  35. package/skills/music/package.json +4 -20
  36. package/skills/music-album/package.json +3 -2
  37. package/skills/one-page-website/package.json +3 -2
  38. package/skills/pdf-read/package.json +5 -23
  39. package/skills/pdf-to-dataset/package.json +5 -4
  40. package/skills/pdf-to-markdown/package.json +5 -4
  41. package/skills/performance-audit-report/package.json +3 -2
  42. package/skills/photo-album/package.json +4 -2
  43. package/skills/pitch-deck/package.json +5 -3
  44. package/skills/playlist-maker/package.json +4 -2
  45. package/skills/product-mockup/package.json +3 -2
  46. package/skills/proposal-pack/package.json +5 -4
  47. package/skills/read-pdf/package.json +4 -29
  48. package/skills/remove-background/package.json +5 -21
  49. package/skills/repo-onboarding-report/package.json +3 -2
  50. package/skills/sdk-generator/package.json +3 -2
  51. package/skills/security-audit-report/package.json +5 -4
  52. package/skills/seo-content-pack/package.json +5 -3
  53. package/skills/short-video-pack/package.json +3 -2
  54. package/skills/slide-deck-generator/package.json +3 -2
  55. package/skills/sms/README.md +18 -56
  56. package/skills/sms/package.json +1 -3
  57. package/skills/sms/tsconfig.json +1 -2
  58. package/skills/social-content-calendar/package.json +4 -7
  59. package/skills/test-suite-generator/package.json +5 -4
  60. package/skills/transcript/package.json +4 -23
  61. package/skills/video/package.json +4 -24
  62. package/skills/video-highlight-pack/package.json +3 -2
  63. package/skills/voiceover-jingle-pack/package.json +3 -2
  64. package/skills/webcrawling/package.json +4 -32
  65. package/skills/domainpurchase/.env.example +0 -7
  66. package/skills/domainpurchase/src/lib/api-client.ts +0 -571
  67. package/skills/domainpurchase/src/lib/config.ts +0 -209
  68. package/skills/domainpurchase/src/lib/installer.ts +0 -165
  69. package/skills/managemcp/.env.example +0 -16
  70. package/skills/managemcp/scripts/migrate.ts +0 -57
  71. package/skills/managemcp/scripts/seed.ts +0 -58
  72. package/skills/managemcp/src/db/index.ts +0 -37
  73. package/skills/managemcp/src/lib/api-client.ts +0 -108
  74. package/skills/managemcp/src/lib/config.ts +0 -34
  75. package/skills/managemcp/src/lib/installer.ts +0 -140
  76. package/skills/managemcp/src/lib/service-dir.ts +0 -119
  77. package/skills/sms/scripts/buy-number.ts +0 -63
  78. package/skills/sms/scripts/test-send.ts +0 -67
  79. package/skills/sms/src/server.ts +0 -351
  80. package/skills/sms/src/sse-server.ts +0 -279
@@ -1,209 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
2
- import { join } from "path";
3
- import { homedir } from "os";
4
-
5
- export interface Config {
6
- apiKey?: string;
7
- apiSecret?: string;
8
- customerId?: string;
9
- shopperId?: string;
10
- apiUrl: string;
11
- // Remote server configuration
12
- useRemoteServer?: boolean;
13
- remoteServerUrl: string;
14
- remoteApiKey?: string;
15
- }
16
-
17
- const CONFIG_DIR = join(homedir(), ".config", "service-domainpurchase");
18
- export const CONFIG_FILE = join(CONFIG_DIR, "config.json");
19
- const AWS_SECRET_NAME = process.env.AWS_SECRET_NAME || "your-org/your-tool/api/live";
20
-
21
- const DEFAULT_CONFIG: Config = {
22
- apiUrl: "https://api.godaddy.com",
23
- remoteServerUrl: process.env.DOMAIN_REMOTE_SERVER_URL || "https://service.example.com/domainpurchase",
24
- useRemoteServer: false,
25
- };
26
-
27
- // Cache for AWS secrets to avoid repeated API calls
28
- let awsSecretsCache: Partial<Config> | null = null;
29
-
30
- async function loadAwsSecrets(): Promise<Partial<Config>> {
31
- if (awsSecretsCache) {
32
- return awsSecretsCache;
33
- }
34
-
35
- try {
36
- const { SecretsManagerClient, GetSecretValueCommand } = await import(
37
- "@aws-sdk/client-secrets-manager"
38
- );
39
- const client = new SecretsManagerClient({ region: "us-east-1" });
40
- const response = await client.send(
41
- new GetSecretValueCommand({ SecretId: AWS_SECRET_NAME })
42
- );
43
-
44
- if (response.SecretString) {
45
- const secret = JSON.parse(response.SecretString);
46
- awsSecretsCache = {
47
- apiKey: secret.api_key,
48
- apiSecret: secret.api_secret,
49
- customerId: secret.customer_id,
50
- shopperId: secret.shopper_id,
51
- };
52
- return awsSecretsCache;
53
- }
54
- } catch {
55
- // AWS secrets not available, fall back to other sources
56
- }
57
-
58
- return {};
59
- }
60
-
61
- export function loadConfig(): Config {
62
- // First check environment variables
63
- const envConfig: Partial<Config> = {
64
- apiKey: process.env.DOMAIN_API_KEY,
65
- apiSecret: process.env.DOMAIN_API_SECRET,
66
- customerId: process.env.DOMAIN_CUSTOMER_ID,
67
- apiUrl: process.env.DOMAIN_API_URL,
68
- };
69
-
70
- // Then load from config file
71
- let fileConfig: Partial<Config> = {};
72
- if (existsSync(CONFIG_FILE)) {
73
- try {
74
- fileConfig = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
75
- } catch {
76
- // Ignore parse errors
77
- }
78
- }
79
-
80
- // Merge with defaults (env vars take priority)
81
- return {
82
- ...DEFAULT_CONFIG,
83
- ...fileConfig,
84
- ...Object.fromEntries(
85
- Object.entries(envConfig).filter(([_, v]) => v !== undefined)
86
- ),
87
- };
88
- }
89
-
90
- export async function loadConfigAsync(): Promise<Config> {
91
- // Load AWS secrets first (lowest priority)
92
- const awsConfig = await loadAwsSecrets();
93
-
94
- // Then check environment variables
95
- const envConfig: Partial<Config> = {
96
- apiKey: process.env.DOMAIN_API_KEY,
97
- apiSecret: process.env.DOMAIN_API_SECRET,
98
- customerId: process.env.DOMAIN_CUSTOMER_ID,
99
- apiUrl: process.env.DOMAIN_API_URL,
100
- useRemoteServer: process.env.DOMAIN_USE_REMOTE_SERVER === "true",
101
- remoteServerUrl: process.env.DOMAIN_REMOTE_SERVER_URL,
102
- remoteApiKey: process.env.DOMAIN_REMOTE_API_KEY,
103
- };
104
-
105
- // Then load from config file
106
- let fileConfig: Partial<Config> = {};
107
- if (existsSync(CONFIG_FILE)) {
108
- try {
109
- fileConfig = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
110
- } catch {
111
- // Ignore parse errors
112
- }
113
- }
114
-
115
- // Merge: AWS -> file -> env (env takes highest priority)
116
- return {
117
- ...DEFAULT_CONFIG,
118
- ...awsConfig,
119
- ...fileConfig,
120
- ...Object.fromEntries(
121
- Object.entries(envConfig).filter(([_, v]) => v !== undefined)
122
- ),
123
- };
124
- }
125
-
126
- export function saveConfig(config: Partial<Config>): void {
127
- if (!existsSync(CONFIG_DIR)) {
128
- mkdirSync(CONFIG_DIR, { recursive: true });
129
- }
130
-
131
- const current = loadConfig();
132
- const merged = { ...current, ...config };
133
- writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2));
134
- }
135
-
136
- export function getApiKey(): string | undefined {
137
- return loadConfig().apiKey;
138
- }
139
-
140
- export function getApiSecret(): string | undefined {
141
- return loadConfig().apiSecret;
142
- }
143
-
144
- export function getCustomerId(): string | undefined {
145
- return loadConfig().customerId;
146
- }
147
-
148
- export function getApiUrl(): string {
149
- return loadConfig().apiUrl;
150
- }
151
-
152
- // Async versions that can load from AWS Secrets Manager
153
- export async function getApiKeyAsync(): Promise<string | undefined> {
154
- return (await loadConfigAsync()).apiKey;
155
- }
156
-
157
- export async function getApiSecretAsync(): Promise<string | undefined> {
158
- return (await loadConfigAsync()).apiSecret;
159
- }
160
-
161
- export async function getCustomerIdAsync(): Promise<string | undefined> {
162
- return (await loadConfigAsync()).customerId;
163
- }
164
-
165
- export async function getShopperIdAsync(): Promise<string | undefined> {
166
- return (await loadConfigAsync()).shopperId;
167
- }
168
-
169
- export async function getApiUrlAsync(): Promise<string> {
170
- return (await loadConfigAsync()).apiUrl;
171
- }
172
-
173
- // Remote server configuration getters
174
- export function useRemoteServer(): boolean {
175
- return loadConfig().useRemoteServer || false;
176
- }
177
-
178
- export function getRemoteServerUrl(): string {
179
- return loadConfig().remoteServerUrl;
180
- }
181
-
182
- export function getRemoteApiKey(): string | undefined {
183
- return loadConfig().remoteApiKey;
184
- }
185
-
186
- export async function getRemoteApiKeyAsync(): Promise<string | undefined> {
187
- const config = await loadConfigAsync();
188
- if (config.remoteApiKey) {
189
- return config.remoteApiKey;
190
- }
191
- // Try to load from AWS Secrets Manager
192
- try {
193
- const { SecretsManagerClient, GetSecretValueCommand } = await import(
194
- "@aws-sdk/client-secrets-manager"
195
- );
196
- const client = new SecretsManagerClient({ region: "us-east-1" });
197
- const response = await client.send(
198
- new GetSecretValueCommand({ SecretId: process.env.DOMAIN_API_KEY_SECRET_ID || "your-org/prod/your-service/api-key" })
199
- );
200
- return response.SecretString;
201
- } catch {
202
- return undefined;
203
- }
204
- }
205
-
206
- // Initialize config from AWS on first run
207
- export async function initConfig(): Promise<void> {
208
- await loadConfigAsync();
209
- }
@@ -1,165 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync, appendFileSync } from "fs";
2
- import { join } from "path";
3
- import { homedir } from "os";
4
-
5
- const SERVICE_NAME = "service-domainpurchase";
6
- const SERVICE_DIR_NAME = `.${SERVICE_NAME}`;
7
-
8
- export interface InstallOptions {
9
- force?: boolean;
10
- }
11
-
12
- export function getServiceDir(): string {
13
- return join(homedir(), SERVICE_DIR_NAME);
14
- }
15
-
16
- export function getLogFilePath(): string {
17
- return join(getServiceDir(), `${SERVICE_NAME}.log`);
18
- }
19
-
20
- export function getReadmePath(): string {
21
- return join(getServiceDir(), "README.md");
22
- }
23
-
24
- export function isInstalled(): boolean {
25
- return existsSync(getServiceDir());
26
- }
27
-
28
- export function ensureInstalled(): void {
29
- if (!isInstalled()) {
30
- install();
31
- }
32
- }
33
-
34
- export function install(options: InstallOptions = {}): { success: boolean; path: string; message: string } {
35
- const serviceDir = getServiceDir();
36
-
37
- if (existsSync(serviceDir) && !options.force) {
38
- return {
39
- success: false,
40
- path: serviceDir,
41
- message: "Already installed. Use --force to reinstall.",
42
- };
43
- }
44
-
45
- mkdirSync(serviceDir, { recursive: true });
46
-
47
- const readmeContent = generateReadme();
48
- writeFileSync(getReadmePath(), readmeContent);
49
-
50
- const logHeader = `# ${SERVICE_NAME} log file
51
- # Created: ${new Date().toISOString()}
52
- # This file contains activity history and logs.
53
- # -------------------------------------------------------
54
-
55
- `;
56
- writeFileSync(getLogFilePath(), logHeader);
57
-
58
- return {
59
- success: true,
60
- path: serviceDir,
61
- message: `Installed at ${serviceDir}`,
62
- };
63
- }
64
-
65
- export function uninstall(): { success: boolean; message: string } {
66
- const serviceDir = getServiceDir();
67
-
68
- if (!existsSync(serviceDir)) {
69
- return {
70
- success: false,
71
- message: "Not installed.",
72
- };
73
- }
74
-
75
- const { rmSync } = require("fs");
76
- rmSync(serviceDir, { recursive: true, force: true });
77
-
78
- return {
79
- success: true,
80
- message: `Uninstalled from ${serviceDir}`,
81
- };
82
- }
83
-
84
- function generateReadme(): string {
85
- return `# .${SERVICE_NAME}
86
-
87
- This directory contains local configuration and logs for \`${SERVICE_NAME}\`.
88
-
89
- ## Files
90
-
91
- | Path | Description |
92
- |------|-------------|
93
- | \`${SERVICE_NAME}.log\` | Activity log |
94
- | \`README.md\` | This file |
95
-
96
- ## What is ${SERVICE_NAME}?
97
-
98
- \`${SERVICE_NAME}\` is a domain purchase service that provides:
99
-
100
- - Domain registration automation
101
- - Registrar integrations (GoDaddy, Namecheap, etc.)
102
- - DNS configuration
103
- - Domain transfer management
104
- - Renewal tracking
105
-
106
- ## Usage
107
-
108
- \`\`\`bash
109
- # Purchase a domain
110
- service-domainpurchase buy --domain "example.com"
111
-
112
- # List purchased domains
113
- service-domainpurchase list
114
-
115
- # Check purchase status
116
- service-domainpurchase status --domain "example.com"
117
-
118
- # Start the server
119
- service-domainpurchase server
120
- \`\`\`
121
-
122
- ## Log File
123
-
124
- The \`${SERVICE_NAME}.log\` file contains:
125
- - Purchase transactions
126
- - Registration status
127
- - DNS changes
128
- - API activity
129
-
130
- ## More Information
131
-
132
- For full documentation, run:
133
-
134
- \`\`\`bash
135
- service-domainpurchase --help
136
- \`\`\`
137
- `;
138
- }
139
-
140
- export type LogLevel = "debug" | "info" | "warn" | "error";
141
-
142
- export function logToFile(message: string, level: LogLevel = "info"): void {
143
- ensureInstalled();
144
-
145
- const timestamp = new Date().toISOString();
146
- const logLine = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
147
-
148
- try {
149
- appendFileSync(getLogFilePath(), logLine);
150
- } catch {
151
- // Silently fail
152
- }
153
- }
154
-
155
- export function logInfo(message: string): void {
156
- logToFile(message, "info");
157
- }
158
-
159
- export function logError(message: string): void {
160
- logToFile(message, "error");
161
- }
162
-
163
- export function logWarn(message: string): void {
164
- logToFile(message, "warn");
165
- }
@@ -1,16 +0,0 @@
1
- # Database (individual connection params)
2
- DB_HOST=localhost
3
- DB_PORT=5432
4
- DB_NAME=managemcp_db
5
- DB_USER=postgres
6
- DB_PASSWORD=your-password-here
7
-
8
- # Or use a full connection string
9
- # DATABASE_URL=postgres://user:password@host:port/dbname
10
-
11
- # Server
12
- PORT=3000
13
-
14
- # API (for CLI usage)
15
- API_URL=https://api.example.com
16
- API_KEY=your-api-key-here
@@ -1,57 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { sql, testConnection, closeConnection } from '../src/db';
4
-
5
- console.log('Running database migrations...');
6
-
7
- const connected = await testConnection();
8
- if (!connected) {
9
- console.error('Failed to connect to database');
10
- process.exit(1);
11
- }
12
-
13
- console.log('Database connected\n');
14
-
15
- try {
16
- console.log('Creating tables...');
17
- await sql`
18
- CREATE TABLE IF NOT EXISTS items (
19
- id SERIAL PRIMARY KEY,
20
- name VARCHAR(255) NOT NULL,
21
- description TEXT,
22
- metadata JSONB DEFAULT '{}',
23
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
24
- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
25
- )
26
- `;
27
-
28
- console.log('Creating indexes...');
29
- await sql`CREATE INDEX IF NOT EXISTS idx_items_name ON items(name)`;
30
- await sql`CREATE INDEX IF NOT EXISTS idx_items_created_at ON items(created_at DESC)`;
31
-
32
- console.log('Creating triggers...');
33
- await sql`
34
- CREATE OR REPLACE FUNCTION update_updated_at_column()
35
- RETURNS TRIGGER AS $$
36
- BEGIN
37
- NEW.updated_at = CURRENT_TIMESTAMP;
38
- RETURN NEW;
39
- END;
40
- $$ language 'plpgsql'
41
- `;
42
-
43
- await sql`DROP TRIGGER IF EXISTS update_items_updated_at ON items`;
44
- await sql`
45
- CREATE TRIGGER update_items_updated_at
46
- BEFORE UPDATE ON items
47
- FOR EACH ROW
48
- EXECUTE FUNCTION update_updated_at_column()
49
- `;
50
-
51
- console.log('\nMigrations completed successfully');
52
- } catch (error) {
53
- console.error('\nMigration failed:', error);
54
- process.exit(1);
55
- } finally {
56
- await closeConnection();
57
- }
@@ -1,58 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { sql, testConnection, closeConnection } from '../src/db';
4
-
5
- console.log('Seeding database...');
6
-
7
- const connected = await testConnection();
8
- if (!connected) {
9
- console.error('Failed to connect to database');
10
- process.exit(1);
11
- }
12
-
13
- console.log('Database connected\n');
14
-
15
- try {
16
- const [{ count }] = await sql`SELECT COUNT(*) as count FROM items`;
17
-
18
- if (parseInt(count) > 0) {
19
- console.log(`Database already has ${count} items - skipping seed`);
20
- console.log('To re-seed, truncate the items table first\n');
21
- process.exit(0);
22
- }
23
-
24
- console.log('Inserting seed data...');
25
-
26
- const seedItems = [
27
- {
28
- name: 'Example Item 1',
29
- description: 'This is the first example item',
30
- metadata: { category: 'example', priority: 'high' },
31
- },
32
- {
33
- name: 'Example Item 2',
34
- description: 'This is the second example item',
35
- metadata: { category: 'sample', priority: 'medium' },
36
- },
37
- {
38
- name: 'Example Item 3',
39
- description: 'This is the third example item',
40
- metadata: { category: 'demo', priority: 'low' },
41
- },
42
- ];
43
-
44
- for (const item of seedItems) {
45
- await sql`
46
- INSERT INTO items (name, description, metadata)
47
- VALUES (${item.name}, ${item.description}, ${JSON.stringify(item.metadata)})
48
- `;
49
- console.log(` Created: ${item.name}`);
50
- }
51
-
52
- console.log(`\nSeeded ${seedItems.length} items successfully`);
53
- } catch (error) {
54
- console.error('\nSeeding failed:', error);
55
- process.exit(1);
56
- } finally {
57
- await closeConnection();
58
- }
@@ -1,37 +0,0 @@
1
- import postgres from 'postgres';
2
-
3
- function getConnectionString(): string {
4
- if (process.env.DATABASE_URL) return process.env.DATABASE_URL;
5
-
6
- const {
7
- DB_USER = 'postgres',
8
- DB_PASSWORD = '',
9
- DB_HOST = 'localhost',
10
- DB_PORT = '5432',
11
- DB_NAME = 'managemcp_db',
12
- } = process.env;
13
-
14
- return `postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}`;
15
- }
16
-
17
- export const sql = postgres(getConnectionString(), {
18
- max: 10,
19
- idle_timeout: 20,
20
- connect_timeout: 10,
21
- });
22
-
23
- export async function testConnection(): Promise<boolean> {
24
- try {
25
- await sql`SELECT 1`;
26
- return true;
27
- } catch (error) {
28
- console.error('Database connection failed:', error instanceof Error ? error.message : 'Unknown error');
29
- return false;
30
- }
31
- }
32
-
33
- export async function closeConnection(): Promise<void> {
34
- await sql.end({ timeout: 5 });
35
- }
36
-
37
- export type Sql = typeof sql;
@@ -1,108 +0,0 @@
1
- import { getApiKey, getApiUrl } from './config';
2
-
3
- export interface ApiItem {
4
- id: string;
5
- name: string;
6
- description?: string;
7
- metadata?: Record<string, unknown>;
8
- createdAt?: string;
9
- updatedAt?: string;
10
- }
11
-
12
- export interface ListOptions {
13
- filter?: string;
14
- limit?: number;
15
- offset?: number;
16
- }
17
-
18
- export interface CreateItemData {
19
- name: string;
20
- description?: string;
21
- metadata?: Record<string, unknown>;
22
- }
23
-
24
- export class ApiError extends Error {
25
- constructor(
26
- message: string,
27
- public statusCode?: number,
28
- public response?: unknown
29
- ) {
30
- super(message);
31
- this.name = 'ApiError';
32
- }
33
- }
34
-
35
- async function makeRequest<T>(
36
- endpoint: string,
37
- options: RequestInit = {}
38
- ): Promise<T> {
39
- const apiUrl = await getApiUrl();
40
- const apiKey = await getApiKey();
41
-
42
- if (!apiUrl) {
43
- throw new ApiError('API URL not configured. Run: managemcp config --set-api-url <url>');
44
- }
45
-
46
- if (!apiKey) {
47
- throw new ApiError('API key not configured. Run: managemcp config --set-api-key <key>');
48
- }
49
-
50
- const url = `${apiUrl}${endpoint}`;
51
- const headers = {
52
- 'Authorization': `Bearer ${apiKey}`,
53
- 'Content-Type': 'application/json',
54
- ...options.headers,
55
- };
56
-
57
- try {
58
- const response = await fetch(url, {
59
- ...options,
60
- headers,
61
- });
62
-
63
- if (!response.ok) {
64
- const errorData = await response.json().catch(() => null);
65
- const message = errorData?.error || response.statusText;
66
- throw new ApiError(`API request failed: ${message}`, response.status, errorData);
67
- }
68
-
69
- return await response.json();
70
- } catch (error) {
71
- if (error instanceof ApiError) throw error;
72
- throw new ApiError(`Network error: ${error instanceof Error ? error.message : 'Unknown error'}`);
73
- }
74
- }
75
-
76
- export async function listItems(options: ListOptions = {}): Promise<ApiItem[]> {
77
- const params = new URLSearchParams();
78
- if (options.filter) params.set('filter', options.filter);
79
- if (options.limit) params.set('limit', options.limit.toString());
80
- if (options.offset) params.set('offset', options.offset.toString());
81
-
82
- const query = params.toString();
83
- const endpoint = `/items${query ? `?${query}` : ''}`;
84
-
85
- const response = await makeRequest<{ items: ApiItem[] }>(endpoint);
86
- return response.items;
87
- }
88
-
89
- export async function getItem(id: string): Promise<ApiItem> {
90
- return await makeRequest<ApiItem>(`/items/${id}`);
91
- }
92
-
93
- export async function searchItems(query: string, limit = 10): Promise<ApiItem[]> {
94
- const params = new URLSearchParams({
95
- q: query,
96
- limit: limit.toString(),
97
- });
98
-
99
- const response = await makeRequest<{ items: ApiItem[] }>(`/items/search?${params}`);
100
- return response.items;
101
- }
102
-
103
- export async function createItem(data: CreateItemData): Promise<ApiItem> {
104
- return await makeRequest<ApiItem>('/items', {
105
- method: 'POST',
106
- body: JSON.stringify(data),
107
- });
108
- }
@@ -1,34 +0,0 @@
1
- import { readFile, writeFile } from 'fs/promises';
2
- import { join } from 'path';
3
- import { homedir } from 'os';
4
-
5
- export interface Config {
6
- apiUrl?: string;
7
- apiKey?: string;
8
- }
9
-
10
- export const CONFIG_FILE = join(homedir(), '.managemcp.json');
11
-
12
- export async function loadConfig(): Promise<Config> {
13
- try {
14
- const data = await readFile(CONFIG_FILE, 'utf-8');
15
- return JSON.parse(data);
16
- } catch (error) {
17
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return {};
18
- throw error;
19
- }
20
- }
21
-
22
- export async function saveConfig(config: Config): Promise<void> {
23
- await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
24
- }
25
-
26
- export async function getApiKey(): Promise<string | undefined> {
27
- const config = await loadConfig();
28
- return config.apiKey || process.env.API_KEY;
29
- }
30
-
31
- export async function getApiUrl(): Promise<string | undefined> {
32
- const config = await loadConfig();
33
- return config.apiUrl || process.env.API_URL;
34
- }