Skill: adamkristopher/endpoints (version 1.0.0)

--- name: endpoints description: "Endpoints document management API toolkit. Scan documents with AI extraction and organize structured data into categorized endpoints. Use when the user asks to: scan a document, upload a file, list endpoints, inspect endpoint data, check usage stats, create or delete endpoints, get file URLs, or manage document metadata. Requires ENDPOINTS_API_KEY from endpoints.work dashboard." --- # Endpoints API Toolkit ## Setup Install dependencies: ```bash cd scripts && npm install ``` Configure credentials by creating a `.env` file in the project root: ``` ENDPOINTS_API_URL=https://endpoints.work ENDPOINTS_API_KEY=ep_your_api_key_here ``` **Prerequisites**: An Endpoints account with an API key. Generate your API key from the [API Keys page](https://endpoints.work/api-keys). ## Quick Start | User says | Function to call | |-----------|-----------------| | "List my endpoints" | `listEndpoints()` | | "Show endpoint details for /job-tracker/january" | `getEndpoint('/job-tracker/january')` | | "Scan this document" | `scanFile('/path/to/file.pdf', 'job tracker')` | | "Scan this text" | `scanText('Meeting notes...', 'meeting tracker')` | | "Create an endpoint for receipts" | `createEndpoint('/receipts/2026')` | | "Delete the old endpoint" | `deleteEndpoint('/category/slug')` | | "Remove that item" | `deleteItem('abc12345')` | | "Get the file URL" | `getFileUrl('userid/path/file.pdf')` | | "Check my usage" | `getStats()` | Execute functions by importing from `scripts/src/index.ts`: ```typescript import { listEndpoints, scanText, getStats } from './scripts/src/index.js'; const categories = await listEndpoints(); const result = await scanText('Meeting with John about Q1 goals', 'meeting tracker'); const stats = await getStats(); ``` Or run directly with tsx: ```bash npx tsx scripts/src/index.ts ``` ## Workflow Pattern Every analysis follows three phases: ### 1. Analyze Run API functions. Each call hits the Endpoints API and returns structured data. ### 2. Auto-Save All results automatically save as JSON files to `results/{category}/`. File naming patterns: - Named results: `{sanitized_name}.json` - Auto-generated: `YYYYMMDD_HHMMSS__{operation}.json` ### 3. Summarize After analysis, read the saved JSON files and create a markdown summary in `results/summaries/` with data tables, insights, and extracted entities. ## High-Level Functions | Function | Purpose | What it returns | |----------|---------|----------------| | `listEndpoints()` | Get all endpoints by category | Tree structure with categories and endpoints | | `getEndpoint(path)` | Get endpoint details | Full metadata (old + new items) | | `scanText(text, prompt)` | Scan text with AI | Extracted entities and endpoint path | | `scanFile(filePath, prompt)` | Scan file with AI | Extracted entities and endpoint path | | `getStats()` | Get usage statistics | Parses used, limits, storage | ## Individual API Functions For granular control, import specific functions. See [references/api-reference.md](references/api-reference.md) for the complete list with parameters, types, and examples. ### Endpoint Functions | Function | Purpose | |----------|---------| | `listEndpoints()` | List all endpoints organized by category | | `getEndpoint(path)` | Get full endpoint details with metadata | | `createEndpoint(path)` | Create a new empty endpoint | | `deleteEndpoint(path)` | Delete endpoint and all associated files | ### Scanning Functions | Function | Purpose | |----------|---------| | `scanText(text, prompt)` | Scan text content with AI extraction | | `scanFile(filePath, prompt)` | Scan file (PDF, images, docs) with AI | ### Item Functions | Function | Purpose | |----------|---------| | `deleteItem(itemId)` | Delete a single item by its 8-char ID | ### File Functions | Function | Purpose | |----------|---------| | `getFileUrl(key)` | Get presigned S3 URL for a file | ### Billing Functions | Function | Purpose | |----------|---------| | `getStats()` | Get usage stats (parses, storage, tier) | ## Data Structures ### Living JSON Pattern Endpoints use the Living JSON pattern for document history: ```typescript { endpoint: { path, category, slug }, metadata: { oldMetadata: { ... }, // Historical items newMetadata: { ... } // Recent items } } ``` ### Metadata Item Each item has: - **8-character ID** - Unique identifier (e.g., `abc12345`) - **summary** - AI-generated description - **entities** - Extracted entities (people, companies, dates) - **filePath** - S3 URL if file was uploaded - **fileType** - MIME type - **originalText** - Source text ## Error Handling | Status | Meaning | |--------|---------| | 401 | Invalid or missing API key | | 404 | Endpoint or item not found | | 409 | Endpoint already exists | | 429 | Usage limit exceeded | ## Examples ### List and Inspect ```typescript // Get all endpoints const { categories } = await listEndpoints(); console.log(`Found ${categories.length} categories`); // Inspect specific endpoint const details = await getEndpoint('/job-tracker/january'); console.log(`Total items: ${details.totalItems}`); ``` ### Scan Documents ```typescript // Scan text content const result = await scanText( 'Email from John Smith at Acme Corp about the Q1 contract renewal', 'business contacts' ); console.log(`Created endpoint: ${result.endpoint.path}`); // Scan a PDF file const fileResult = await scanFile('./invoice.pdf', 'invoice tracker'); console.log(`Extracted ${fileResult.entriesAdded} items`); ``` ### Check Usage ```typescript const stats = await getStats(); console.log(`Parses: ${stats.parsesUsed}/${stats.parsesLimit}`); console.log(`Storage: ${stats.storageUsed} bytes`); ```

path: references/api-reference.md
content: # Endpoints API Reference

Complete API documentation for the Endpoints document management platform.

## Base URL

```
Production: https://endpoints.work/api
```

## Authentication

All endpoints require Bearer token authentication:

```
[REDACTED_SECRET]
```

Generate API keys from the dashboard at [endpoints.work/api-keys](https://endpoints.work/api-keys).

## Core Endpoints

### GET /api/endpoints/tree

List all endpoints organized by category.

**Response:**
```json
{
  "categories": [
    {
      "name": "job-tracker",
      "endpoints": [
        { "id": 1, "path": "/job-tracker/january", "slug": "january" },
        { "id": 2, "path": "/job-tracker/february", "slug": "february" }
      ]
    }
  ]
}
```

**TypeScript:**
```typescript
const { categories } = await listEndpoints();
```

### GET /api/endpoints/{category}/{slug}

Get endpoint details with all metadata items.

**Response:**
```json
{
  "endpoint": {
    "id": 1,
    "path": "/job-tracker/january",
    "category": "job-tracker",
    "slug": "january"
  },
  "metadata": {
    "oldMetadata": {
      "abc12345": { "summary": "...", "entities": [...] }
    },
    "newMetadata": {
      "def67890": { "summary": "...", "entities": [...] }
    }
  },
  "totalItems": 2
}
```

**TypeScript:**
```typescript
const details = await getEndpoint('/job-tracker/january');
```

### POST /api/endpoints

Create a new endpoint.

**Request:**
```json
{
  "path": "/category/slug",
  "items": []
}
```

**Response:**
```json
{
  "success": true,
  "endpoint": { "id": 1, "path": "/category/slug", ... }
}
```

**TypeScript:**
```typescript
const result = await createEndpoint('/receipts/2026');
```

### DELETE /api/endpoints/{category}/{slug}

Delete an endpoint and all associated files.

**Response:**
```json
{
  "success": true,
  "message": "Endpoint deleted"
}
```

**TypeScript:**
```typescript
const result = await deleteEndpoint('/old-category/old-slug');
```

## Scanning

### POST /api/scan

Scan files or text with AI extraction.

**Request:** `multipart/form-data`

| Field | Type | Description |
|-------|------|-------------|
| `files` | File[] | Files to scan (PDF, images, docs) |
| `texts` | String[] | Text content to scan |
| `prompt` | String | Category hint (e.g., "job tracker") |

**Response:**
```json
{
  "endpoint": {
    "path": "/job-tracker/january-2026",
    "category": "job-tracker",
    "slug": "january-2026"
  },
  "entriesAdded": 3,
  "metadata": {
    "newMetadata": {
      "abc12345": {
        "summary": "Software Engineer position at Acme Corp",
        "entities": [
          { "name": "Acme Corp", "type": "company" },
          { "name": "Software Engineer", "type": "role" }
        ],
        "filePath": "https://...",
        "fileType": "application/pdf"
      }
    }
  }
}
```

**TypeScript:**
```typescript
// Scan text
const result = await scanText('Meeting notes here', 'meeting tracker');

// Scan file
const result = await scanFile('./document.pdf', 'invoice tracker');
```

## Items

### DELETE /api/items/{itemId}

Delete a single item from an endpoint by its 8-character ID.

**Response:**
```json
{
  "success": true,
  "message": "Item deleted"
}
```

**TypeScript:**
```typescript
const result = await deleteItem('abc12345');
```

## Files

### GET /api/files/{key}

Get a presigned URL for a file.

**Query Parameters:**
- `format=json` - Return JSON with URL (default returns redirect)
- `expiresIn=3600` - URL expiration in seconds (default: 3600)

**Response:**
```json
{
  "url": "https://s3.amazonaws.com/...",
  "expiresIn": 3600
}
```

**TypeScript:**
```typescript
const { url } = await getFileUrl('userid/path/file.pdf');
const { url } = await getFileUrl('userid/path/file.pdf', 7200); // 2 hours
```

## Billing

### GET /api/billing/stats

Get usage statistics for current billing period.

**Response:**
```json
{
  "tier": "pro_managed",
  "parsesUsed": 45,
  "parsesLimit": 300,
  "storageUsed": 1073741824,
  "storageLimit": 107374182400,
  "billingPeriodStart": "2026-01-01T00:00:00Z",
  "billingPeriodEnd": "2026-02-01T00:00:00Z"
}
```

**TypeScript:**
```typescript
const stats = await getStats();
console.log(`${stats.parsesUsed}/${stats.parsesLimit} parses used`);
```

## Error Responses

| Status | Response |
|--------|----------|
| 401 | `{ "error": "Unauthorized" }` |
| 404 | `{ "error": "Endpoint not found" }` |
| 409 | `{ "error": "Endpoint already exists" }` |
| 429 | `{ "error": "Monthly parse limit exceeded" }` |
| 500 | `{ "error": "Internal server error" }` |

## Living JSON Structure

Endpoints use the Living JSON pattern for tracking document history:

```
metadata: {
  oldMetadata: { ... }  // Historical items (rotated from previous scans)
  newMetadata: { ... }  // Recent items (from latest scan)
}
```

Each item has:
- **8-character ID** - Unique identifier (e.g., `abc12345`)
- **summary** - AI-generated description
- **entities** - Extracted entities (people, companies, dates, etc.)
- **filePath** - S3 URL if file was uploaded
- **fileType** - MIME type
- **originalText** - Source text (if text scan)

## Subscription Tiers

### BYOK Tiers (Bring Your Own Key)
| Tier | Parses | Storage | Price |
|------|--------|---------|-------|
| Starter BYOK | Unlimited | 25GB | $5/mo |
| Pro BYOK | Unlimited | 100GB | $9/mo |
| Business BYOK | Unlimited | 500GB | $35/mo |

### Managed Tiers
| Tier | Parses | Storage | Price |
|------|--------|---------|-------|
| Free | 25/mo | 10GB | $0 |
| Starter | 100/mo | 25GB | $19/mo |
| Pro | 300/mo | 100GB | $69/mo |
| Business | 1000/mo | 500GB | $249/mo |

## Function Reference

| Function | Parameters | Returns |
|----------|------------|---------|
| `listEndpoints()` | none | `TreeResponse` |
| `getEndpoint(path)` | `path: string` | `EndpointDetails` |
| `createEndpoint(path)` | `path: string` | `{ success, endpoint }` |
| `deleteEndpoint(path)` | `path: string` | `{ success, message }` |
| `scanText(text, prompt)` | `text: string, prompt: string` | `ScanResult` |
| `scanFile(filePath, prompt)` | `filePath: string, prompt: string` | `ScanResult` |
| `deleteItem(itemId)` | `itemId: string` | `{ success, message }` |
| `getFileUrl(key, expiresIn?)` | `key: string, expiresIn?: number` | `FileUrlResponse` |
| `getStats()` | none | `StatsResponse` |


path: scripts/package-lock.json
content: {
  "name": "endpoints-skill",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "endpoints-skill",
      "version": "1.0.0",
      "license": "MIT",
      "dependencies": {
        "dotenv": "^16.3.1"
      },
      "devDependencies": {
        "@types/node": "^20.10.0",
        "tsx": "^4.7.0",
        "typescript": "^5.3.2"
      }
    },
    "node_modules/@esbuild/aix-ppc64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "aix"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/android-arm": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/android-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/android-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/darwin-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/darwin-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/freebsd-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "freebsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/freebsd-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "freebsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-arm": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-ia32": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-loong64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "loong64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-mips64el": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "mips64el"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-ppc64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-riscv64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "riscv64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-s390x": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "s390x"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/linux-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/netbsd-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "netbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/netbsd-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "netbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/openbsd-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "openbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/openbsd-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "openbsd"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/openharmony-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "openharmony"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/sunos-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "sunos"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/win32-arm64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/win32-ia32": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/win32-x64": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@types/node": {
      "version": "20.19.30",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "undici-types": "~6.21.0"
      }
    },
    "node_modules/dotenv": {
      "version": "16.6.1",
      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "BSD-2-Clause",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://dotenvx.com"
      }
    },
    "node_modules/esbuild": {
      "version": "0.27.2",
      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "hasInstallScript": true,
      "license": "MIT",
      "bin": {
        "esbuild": "bin/esbuild"
      },
      "engines": {
        "node": ">=18"
      },
      "optionalDependencies": {
        "@esbuild/aix-ppc64": "0.27.2",
        "@esbuild/android-arm": "0.27.2",
        "@esbuild/android-arm64": "0.27.2",
        "@esbuild/android-x64": "0.27.2",
        "@esbuild/darwin-arm64": "0.27.2",
        "@esbuild/darwin-x64": "0.27.2",
        "@esbuild/freebsd-arm64": "0.27.2",
        "@esbuild/freebsd-x64": "0.27.2",
        "@esbuild/linux-arm": "0.27.2",
        "@esbuild/linux-arm64": "0.27.2",
        "@esbuild/linux-ia32": "0.27.2",
        "@esbuild/linux-loong64": "0.27.2",
        "@esbuild/linux-mips64el": "0.27.2",
        "@esbuild/linux-ppc64": "0.27.2",
        "@esbuild/linux-riscv64": "0.27.2",
        "@esbuild/linux-s390x": "0.27.2",
        "@esbuild/linux-x64": "0.27.2",
        "@esbuild/netbsd-arm64": "0.27.2",
        "@esbuild/netbsd-x64": "0.27.2",
        "@esbuild/openbsd-arm64": "0.27.2",
        "@esbuild/openbsd-x64": "0.27.2",
        "@esbuild/openharmony-arm64": "0.27.2",
        "@esbuild/sunos-x64": "0.27.2",
        "@esbuild/win32-arm64": "0.27.2",
        "@esbuild/win32-ia32": "0.27.2",
        "@esbuild/win32-x64": "0.27.2"
      }
    },
    "node_modules/fsevents": {
      "version": "2.3.3",
      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "hasInstallScript": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
      }
    },
    "node_modules/get-tsconfig": {
      "version": "4.13.0",
      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "resolve-pkg-maps": "^1.0.0"
      },
      "funding": {
        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
      }
    },
    "node_modules/resolve-pkg-maps": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "funding": {
        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
      }
    },
    "node_modules/tsx": {
      "version": "4.21.0",
      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "esbuild": "~0.27.0",
        "get-tsconfig": "^4.7.5"
      },
      "bin": {
        "tsx": "dist/cli.mjs"
      },
      "engines": {
        "node": ">=18.0.0"
      },
      "optionalDependencies": {
        "fsevents": "~2.3.3"
      }
    },
    "node_modules/typescript": {
      "version": "5.9.3",
      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "Apache-2.0",
      "bin": {
        "tsc": "bin/tsc",
        "tsserver": "bin/tsserver"
      },
      "engines": {
        "node": ">=14.17"
      }
    },
    "node_modules/undici-types": {
      "version": "6.21.0",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    }
  }
}


path: scripts/package.json
content: {
  "name": "endpoints-skill",
  "version": "1.0.0",
  "type": "module",
  "description": "Endpoints API toolkit for MoltBot",
  "main": "src/index.js",
  "scripts": {
    "build": "tsc",
    "start": "tsx src/index.ts"
  },
  "dependencies": {
    "dotenv": "^16.3.1"
  },
  "devDependencies": {
    "@types/node": "^20.10.0",
    "tsx": "^4.7.0",
    "typescript": "^5.3.2"
  },
  "keywords": [
    "endpoints",
    "moltbot",
    "clawdbot",
    "ai",
    "document-management"
  ],
  "license": "MIT"
}


path: scripts/src/index.ts
content: import { config } from "dotenv";
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
import { join, dirname, basename } from "path";
import { fileURLToPath } from "url";

// Load environment variables
const __dirname = dirname(fileURLToPath(import.meta.url));
config({ path: join(__dirname, "../../.env") });

// Configuration
const API_URL = process.env.ENDPOINTS_API_URL || "https://endpoints.work";
const [REDACTED_SECRET];

if (!API_KEY) {
  console.error("Error: ENDPOINTS_API_KEY not set");
  console.error("Set it in .env file: ENDPOINTS_API_KEY=ep_your_key_here");
  process.exit(1);
}

// Types
interface TreeEndpoint {
  id: number;
  path: string;
  slug: string;
}

interface TreeCategory {
  name: string;
  endpoints: TreeEndpoint[];
}

interface TreeResponse {
  categories: TreeCategory[];
}

interface MetadataItem {
  filePath?: string;
  fileType?: string;
  fileSize?: number;
  originalText?: string;
  summary: string;
  entities: Array<{ name: string; type: string; role?: string }>;
}

interface EndpointDetails {
  endpoint: {
    id: number;
    path: string;
    category: string;
    slug: string;
  };
  metadata: {
    oldMetadata: Record<string, MetadataItem>;
    newMetadata: Record<string, MetadataItem>;
  };
  totalItems: number;
}

interface ScanResult {
  endpoint: {
    path: string;
    category: string;
    slug: string;
  };
  entriesAdded: number;
  metadata: {
    newMetadata: Record<string, MetadataItem>;
  };
}

interface FileUrlResponse {
  url: string;
  expiresIn: number;
}

interface StatsResponse {
  tier: string;
  parsesUsed: number;
  parsesLimit: number;
  storageUsed: number;
  storageLimit: number;
  billingPeriodStart: string;
  billingPeriodEnd: string;
}

// HTTP Client
async function apiRequest<T>(
  path: string,
  options: RequestInit = {}
): Promise<T> {
  const url = `${API_URL}${path}`;
  const headers: Record<string, string> = {
    Authorization: `Bearer ${API_KEY}`,
    ...((options.headers as Record<string, string>) || {}),
  };

  // Don't set Content-Type for FormData (browser sets it with boundary)
  if (!(options.body instanceof FormData)) {
    headers["Content-Type"] = "application/json";
  }

  const response = await fetch(url, {
    ...options,
    headers,
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`HTTP ${response.status}: ${error}`);
  }

  return response.json();
}

// Result saving utilities
function getResultsDir(category: string): string {
  const dir = join(__dirname, "../../results", category);
  if (!existsSync(dir)) {
    mkdirSync(dir, { recursive: true });
  }
  return dir;
}

function saveResult(category: string, name: string, data: unknown): string {
  const dir = getResultsDir(category);
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
  const filename = name
    ? `${name.replace(/[^a-z0-9]/gi, "_")}.json`
    : `${timestamp}__result.json`;
  const filepath = join(dir, filename);
  writeFileSync(filepath, JSON.stringify(data, null, 2));
  return filepath;
}

// ============================================
// ENDPOINT FUNCTIONS
// ============================================

/**
 * List all endpoints organized by category
 */
export async function listEndpoints(): Promise<TreeResponse> {
  const result = await apiRequest<TreeResponse>("/api/endpoints/tree");
  saveResult("endpoints", "tree", result);
  return result;
}

/**
 * Get full endpoint details with metadata
 */
export async function getEndpoint(path: string): Promise<EndpointDetails> {
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
  const result = await apiRequest<EndpointDetails>(
    `/api/endpoints/${normalizedPath}`
  );
  const name = normalizedPath.replace(/\//g, "_");
  saveResult("endpoints", name, result);
  return result;
}

/**
 * Create a new empty endpoint
 */
export async function createEndpoint(
  path: string
): Promise<{ success: boolean; endpoint: EndpointDetails["endpoint"] }> {
  const result = await apiRequest<{
    success: boolean;
    endpoint: EndpointDetails["endpoint"];
  }>("/api/endpoints", {
    method: "POST",
    body: JSON.stringify({ path, items: [] }),
  });
  saveResult("endpoints", `created_${path.replace(/\//g, "_")}`, result);
  return result;
}

/**
 * Delete endpoint and all associated files
 */
export async function deleteEndpoint(
  path: string
): Promise<{ success: boolean; message: string }> {
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
  return apiRequest<{ success: boolean; message: string }>(
    `/api/endpoints/${normalizedPath}`,
    { method: "DELETE" }
  );
}

// ============================================
// SCANNING FUNCTIONS
// ============================================

/**
 * Scan text content with AI extraction
 */
export async function scanText(
  text: string,
  prompt: string
): Promise<ScanResult> {
  const formData = new FormData();
  formData.append("texts", text);
  formData.append("prompt", prompt);

  const result = await apiRequest<ScanResult>("/api/scan", {
    method: "POST",
    body: formData,
  });

  const name = `scan_${prompt.replace(/[^a-z0-9]/gi, "_")}`;
  saveResult("scans", name, result);
  return result;
}

/**
 * Scan file (PDF, images, docs) with AI extraction
 */
export async function scanFile(
  filePath: string,
  prompt: string
): Promise<ScanResult> {
  const fileContent = readFileSync(filePath);
  const fileName = basename(filePath);

  const formData = new FormData();
  formData.append("files", new Blob([fileContent]), fileName);
  formData.append("prompt", prompt);

  const result = await apiRequest<ScanResult>("/api/scan", {
    method: "POST",
    body: formData,
  });

  const name = `scan_${fileName.replace(/[^a-z0-9]/gi, "_")}`;
  saveResult("scans", name, result);
  return result;
}

// ============================================
// ITEM FUNCTIONS
// ============================================

/**
 * Delete a single item by its 8-character ID
 */
export async function deleteItem(
  itemId: string
): Promise<{ success: boolean; message: string }> {
  return apiRequest<{ success: boolean; message: string }>(
    `/api/items/${itemId}`,
    { method: "DELETE" }
  );
}

// ============================================
// FILE FUNCTIONS
// ============================================

/**
 * Get presigned S3 URL for a file
 */
export async function getFileUrl(
  key: string,
  expiresIn?: number
): Promise<FileUrlResponse> {
  const params = new URLSearchParams({ format: "json" });
  if (expiresIn) params.append("expiresIn", expiresIn.toString());

  return apiRequest<FileUrlResponse>(`/api/files/${key}?${params}`);
}

// ============================================
// BILLING FUNCTIONS
// ============================================

/**
 * Get usage stats (parses, storage, tier)
 */
export async function getStats(): Promise<StatsResponse> {
  const result = await apiRequest<StatsResponse>("/api/billing/stats");
  saveResult("billing", "stats", result);
  return result;
}

// ============================================
// CLI DEMO
// ============================================

async function main() {
  console.log("Endpoints API Toolkit\n");

  try {
    // Demo: List endpoints
    console.log("Fetching endpoints...");
    const { categories } = await listEndpoints();
    console.log(`Found ${categories.length} categories:`);
    categories.forEach((cat) => {
      console.log(`  ${cat.name}: ${cat.endpoints.length} endpoints`);
    });

    // Demo: Get stats
    console.log("\nFetching usage stats...");
    const stats = await getStats();
    console.log(`Tier: ${stats.tier}`);
    console.log(`Parses: ${stats.parsesUsed}/${stats.parsesLimit}`);
    console.log(
      `Storage: ${(stats.storageUsed / 1024 / 1024).toFixed(2)} MB / ${(stats.storageLimit / 1024 / 1024 / 1024).toFixed(2)} GB`
    );
  } catch (error) {
    console.error("Error:", error);
  }
}

// Run if executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
  main();
}


path: scripts/tsconfig.json
content: {
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)