├── .gitignore ├── README.md ├── assets ├── chart.png └── limitless-logo.svg ├── notebooks └── chart_usage.ipynb ├── openapi.yml ├── python ├── .gitignore ├── _client.py ├── export_markdown.py ├── requirements.txt └── summarize_day.py └── typescript ├── .gitignore ├── README.md ├── _client.ts ├── export_markdown.ts ├── package-lock.json ├── package.json ├── summarize_day.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | .env 3 | .env.local 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | API Icon 3 | Limitless Developer API: Code Examples 4 |

5 | 6 | This repository contains examples demonstrating how to use our API endpoints, featuring realistic, LLM-powered use cases. 7 | 8 | ## 😍 Contributing 9 | 10 | Share your examples with the community! Pull requests are welcome. We'll review PRs and potentially merge the best use cases (with credit, of course). 11 | 12 | ## 📚 Table of Contents 13 | 14 | - [Getting Started](#getting-started) 15 | - [Authentication](#authentication) 16 | - [Examples](#examples) 17 | - [Documentation](#documentation) 18 | - [Support](#support) 19 | 20 | ## 🚀 Getting Started 21 | 22 | To use these examples, you'll need: 23 | 24 | - [An API key](https://limitless.ai/developers) from Limitless 25 | - [Python 3](https://realpython.com/installing-python/) and virtualenv (you can install virtualenv with `pipx install virtualenv`) 26 | - Basic understanding of REST APIs 27 | 28 | You can also use our [OpenAPI spec](openapi.yml) to generate client libraries in your language of choice. 29 | 30 | ## 🔐 Authentication 31 | 32 | All API requests require authentication using an API key. Include it in the header of your requests: 33 | 34 | ### Installation 35 | 36 | ```bash 37 | cd examples 38 | python3 -m venv venv 39 | source venv/bin/activate 40 | pip install -r requirements.txt 41 | ``` 42 | 43 | ## 🛳️ Examples 44 | 45 | ### Example cURL 46 | 47 | ```bash 48 | curl -X GET "https://api.limitless.ai/v1/" \ 49 | -H "X-API-KEY: YOUR_API_KEY" 50 | ``` 51 | 52 | ### Chart Usage 53 | 54 | See the `notebooks/` folder for examples. 55 | 56 | ![Chart Example](./assets/chart.png) 57 | 58 | ### Export Markdown 59 | 60 | This example simply prints the Markdown content from each the most recent record. 61 | 62 | ```bash 63 | LIMITLESS_API_KEY="your_api_key" python export_markdown.py 64 | ``` 65 | 66 | ##### Output: 67 | 68 | ```markdown 69 | A request to play a song, followed by a discussion about the lyrics 70 | 71 | Requesting Siri to play "Shiny" from Moana. 72 | 73 | Speaker 1: Okay. 74 | 75 | Speaker 2: serious. 76 | 77 | Speaker 2: Siri, play Shiny from Moana in the front room. 78 | 79 | Speaker 2: Shiny from Moana soundtrack by Jemaine Clement now playing on the front room. 80 | 81 | Discussing the desired part of the song. 82 | 83 | Speaker 3: It's from the part where you're singing. we want the shiny. 84 | 85 | Speaker 3: Right, Mom. 86 | 87 | Confusion about the lyrics and a reference to Grandma's advice. 88 | 89 | ... etc ... 90 | ``` 91 | 92 | ### Generate a daily summary from your transcripts 93 | 94 | ```bash 95 | LIMITLESS_API_KEY="your_api_key" OPENAI_API_KEY="sk-...." python summarize_day.py 96 | ``` 97 | 98 | ##### Output (will stream to the console): 99 | 100 | ```markdown 101 | Here's a summary of the provided transcripts: 102 | 103 | 1. **Song Request and Lyrics Discussion**: A family member requests Siri to play "Shiny" from Moana. They discuss their favorite parts of the song, express confusion over the lyrics, and reflect on Grandma's advice to "listen to your heart." 104 | 105 | 2. **Swimming Conversation**: You and another speaker recall whose child was first in the water, discuss a successful swim attempt, and consider whether to do another lap. 106 | 107 | ... etc ... 108 | ``` 109 | 110 | ## ℹ️ More information 111 | 112 | For more information on the API, see the [documentation](https://limitless.ai/developers/docs/api). 113 | 114 | ## 🛟 Support 115 | 116 | If you need help, join our [Slack community](https://www.limitless.ai/community), follow us on [X/Twitter](https://twitter.com/limitlessai), or [email us](mailto:support@limitless.ai). 117 | -------------------------------------------------------------------------------- /assets/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/limitless-ai-inc/limitless-api-examples/d4e3aa23747d52b2843de41c761b13f9730c2f99/assets/chart.png -------------------------------------------------------------------------------- /assets/limitless-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /notebooks/chart_usage.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## View your lifelog data usage trends!" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 6, 13 | "metadata": { 14 | "vscode": { 15 | "languageId": "shellscript" 16 | } 17 | }, 18 | "outputs": [ 19 | { 20 | "name": "stdout", 21 | "output_type": "stream", 22 | "text": [ 23 | "Requirement already satisfied: pandas in /opt/homebrew/lib/python3.11/site-packages (2.2.3)\n", 24 | "Requirement already satisfied: matplotlib in /opt/homebrew/lib/python3.11/site-packages (3.10.1)\n", 25 | "Requirement already satisfied: requests in /opt/homebrew/lib/python3.11/site-packages (2.32.3)\n", 26 | "Requirement already satisfied: numpy>=1.23.2 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2.2.3)\n", 27 | "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/colinyoung/Library/Python/3.11/lib/python/site-packages (from pandas) (2.9.0.post0)\n", 28 | "Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2025.1)\n", 29 | "Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/lib/python3.11/site-packages (from pandas) (2025.1)\n", 30 | "Requirement already satisfied: contourpy>=1.0.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.3.1)\n", 31 | "Requirement already satisfied: cycler>=0.10 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (0.12.1)\n", 32 | "Requirement already satisfied: fonttools>=4.22.0 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (4.56.0)\n", 33 | "Requirement already satisfied: kiwisolver>=1.3.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (1.4.8)\n", 34 | "Requirement already satisfied: packaging>=20.0 in /Users/colinyoung/Library/Python/3.11/lib/python/site-packages (from matplotlib) (24.2)\n", 35 | "Requirement already satisfied: pillow>=8 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (11.1.0)\n", 36 | "Requirement already satisfied: pyparsing>=2.3.1 in /opt/homebrew/lib/python3.11/site-packages (from matplotlib) (3.2.1)\n", 37 | "Requirement already satisfied: charset-normalizer<4,>=2 in /opt/homebrew/lib/python3.11/site-packages (from requests) (3.4.1)\n", 38 | "Requirement already satisfied: idna<4,>=2.5 in /opt/homebrew/lib/python3.11/site-packages (from requests) (3.10)\n", 39 | "Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/lib/python3.11/site-packages (from requests) (2.3.0)\n", 40 | "Requirement already satisfied: certifi>=2017.4.17 in /opt/homebrew/lib/python3.11/site-packages (from requests) (2024.8.30)\n", 41 | "Requirement already satisfied: six>=1.5 in /Users/colinyoung/Library/Python/3.11/lib/python/site-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)\n", 42 | "\n", 43 | "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.0.1\u001b[0m\n", 44 | "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpython3.11 -m pip install --upgrade pip\u001b[0m\n", 45 | "Note: you may need to restart the kernel to use updated packages.\n" 46 | ] 47 | } 48 | ], 49 | "source": [ 50 | "%pip install pandas matplotlib requests\n", 51 | "# Set your API key\n", 52 | "API_KEY = \"< your api key >\"" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 8, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "# imports and definitions\n", 62 | "\n", 63 | "import pandas as pd\n", 64 | "import matplotlib.pyplot as plt\n", 65 | "import requests\n", 66 | "from datetime import date\n", 67 | "\n", 68 | "def download_lifelogs(date_str=None, page_size=10):\n", 69 | " \"\"\"\n", 70 | " Download lifelogs from the Limitless API with pagination.\n", 71 | " \n", 72 | " Args:\n", 73 | " date_str: Date in YYYY-MM-DD format (default: today)\n", 74 | " \n", 75 | " Returns:\n", 76 | " DataFrame with lifelog data including timestamps\n", 77 | " \"\"\"\n", 78 | " # Use today's date if none provided\n", 79 | " if date_str is None:\n", 80 | " date_str = date.today().isoformat()\n", 81 | " \n", 82 | " # API endpoint from the schema\n", 83 | " url = \"https://api.limitless.ai/v1/lifelogs\"\n", 84 | " \n", 85 | " # Parameters based on the API schema\n", 86 | " params = {\n", 87 | " \"date\": date_str,\n", 88 | " \"timezone\": \"America/Chicago\",\n", 89 | " \"includeMarkdown\": \"false\", # We only need timestamps, not content\n", 90 | " \"direction\": \"asc\",\n", 91 | " \"limit\": page_size,\n", 92 | " \"includeHeadings\": \"false\" # We only need transcript lines, not headings\n", 93 | " }\n", 94 | " \n", 95 | " # Replace with your actual auth method\n", 96 | " headers = {\n", 97 | " \"X-API-Key\": API_KEY\n", 98 | " }\n", 99 | " \n", 100 | " all_lifelogs = []\n", 101 | " \n", 102 | " while True:\n", 103 | " # Make the API request\n", 104 | " response = requests.get(url, params=params, headers=headers)\n", 105 | " \n", 106 | " # Check if request was successful\n", 107 | " if response.status_code != 200:\n", 108 | " raise Exception(f\"API request failed with status code {response.status_code}: {response.text}\")\n", 109 | " \n", 110 | " # Parse the response\n", 111 | " data = response.json()\n", 112 | " \n", 113 | " # Extract lifelogs from the response structure\n", 114 | " lifelogs = data.get('data', {}).get('lifelogs', [])\n", 115 | " \n", 116 | " # If no more lifelogs, break the loop\n", 117 | " if not lifelogs:\n", 118 | " break\n", 119 | " \n", 120 | " all_lifelogs.extend(lifelogs)\n", 121 | " \n", 122 | " # Update offset for the next page\n", 123 | " next_cursor = data.get('meta', {}).get('lifelogs', {}).get('nextCursor', None)\n", 124 | " if next_cursor:\n", 125 | " params[\"cursor\"] = next_cursor\n", 126 | " else:\n", 127 | " break\n", 128 | " \n", 129 | " # Create a list to store the data\n", 130 | " lifelog_data = []\n", 131 | " \n", 132 | " # Extract first and last start times from each lifelog\n", 133 | " for log in all_lifelogs:\n", 134 | " if 'contents' in log and log['contents']:\n", 135 | " # Only process if there are contents\n", 136 | " first_time = None\n", 137 | " last_time = None\n", 138 | " \n", 139 | " first_time = log['contents'][0]['startTime']\n", 140 | " last_time = log['contents'][-1]['startTime']\n", 141 | " lifelog_data.append({\n", 142 | " 'first_timestamp': first_time,\n", 143 | " 'last_timestamp': last_time\n", 144 | " })\n", 145 | " \n", 146 | " # Create DataFrame from the collected data\n", 147 | " df = pd.DataFrame(lifelog_data)\n", 148 | " \n", 149 | " # Convert timestamps to datetime objects\n", 150 | " if not df.empty:\n", 151 | " df['first_timestamp'] = pd.to_datetime(df['first_timestamp'])\n", 152 | " df['last_timestamp'] = pd.to_datetime(df['last_timestamp'])\n", 153 | " \n", 154 | " return df\n" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 10, 160 | "metadata": {}, 161 | "outputs": [ 162 | { 163 | "data": { 164 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAJbCAYAAAA19ScYAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAgt1JREFUeJzt3Qd4VFXi//8TqgELxQQXURQLLPa6ir0X1rpiV+xrF9auX3XXuhbEumvHtXdddYu9V1AEXcECWFDZxEjEEsWQ+T+fs7/JfxKSkCGT+0ly36/niZDJZObOe+4kzuHcc4symUwmAAAAAAAAAAnqlOSdAQAAAAAAAMKgFAAAAAAAABLHoBQAAAAAAAASx6AUAAAAAAAAEsegFAAAAAAAABLHoBQAAAAAAAASx6AUAAAAAAAAEsegFAAAAAAAABLHoBQAAAAAAAASx6AUAADtyB//+MdQVFRU57LlllsuHHTQQaEju/XWW+Pj/uSTTwp2m5tvvnn8SJIew7HHHhvaK+1rv/3tb0NHeA19/fXX7k0BACD1GJQCAHT4gYzsR5cuXcLSSy8dB3C++OIL9+Z1CLl9O3XqFPr37x+23Xbb8Pzzz4c00KBWboPGPjQQgpa56667whVXXNHs61944YXhkUceCW3l588iiywSXx/bbbdduOqqq8J3331n2zYAANqKLu4NAACgtZ177rlh+eWXDz/99FN4/fXX45vFl19+Obz33nvxjWJ798EHH8QBIZdtttkmHHjggSGTyYQZM2aEv/zlL2HLLbcM//jHP8IOO+xQkPs44IADwt577x26d+8e2pIzzzwzHHbYYbWfjx8/Pg44nHHGGeHXv/517eWrr766aQs71qCUXrOjRo1q9qDUHnvsEXbdddfQFn7+/PLLL2HWrFlxwFaP4fLLLw+PPvoo+wYAINUYlAIAdHgaGFl33XXj3zWAsOSSS4aLL744viHcc889Q1tSXV0dampqQrdu3Zr9Pe6BmpVXXjnsv//+tZ/vtttu8Y22ZrW0dFDqhx9+CD179gydO3eOH22NBuRyaZBTg1K6vNCHBmZboP3+/JHTTz89PPvss/EwyJ133jlMmTIlFBcXW7cRAAAXDt8DAKTOJptsEv+cNm1ancunTp0aZ1b06dMnDi7ojaQGruqrrKwMo0ePjuvraEBowIABcaZQ7ho1ZWVl4dBDDw39+vWLt7XGGmuEv/3tb3VuR+sj6bCeyy67LA7grLDCCvH23n///fh1zeZab7314vfra9dff32Dj6f+mlLZw4ZeeeWV8Ic//CGUlJTEwQwNFpWXl9f5Xg2A6dAyHVbUo0ePsMUWW8T7b8k6Vauttloc+NOsqXzaZrf7hRdeCEcffXQoLS2NbZtaU0qzslZZZZXYTY/hmGOOic9PfTfccENsqDf/66+/fnjppZca3Parr7463p5a9O7dO26nZugUmg4rW3XVVeN26/7+/e9/N7jukZ6LfffdN27LxhtvXDtwed5559XuL3quNDPr559/rnMbjR022NBzO3ny5LDZZpvFPmp+/vnnh3HjxjW6jpf2TXXUczlo0KBw2223zXed6dOnhxEjRsTnXD032GCDOHsuV2PPq2YT6fLsYaAa4NP3fvrpp7WHw+lxNEZf1yCeXnPZ69d/zNpPdFmvXr3CEkssEQ4++ODw448/zndbd9xxR1hnnXViGz0Wzdj7/PPPQ0toJuFZZ50VH49uP/d50DapqdoutdRS4ZBDDgkVFRW113nuuefi43n44Yfnu13tq/raa6+91qLtAwAgKcyUAgCkTvYNsN7oZ/3nP/8JG220UVxz6rTTTouDOPfdd1889OfBBx+MAzry/fffx0EtzW7Qm8W11147DkZpgGXmzJlxMKaqqiq+if7444/jotY6dOf++++Pbzb1RviEE06osz16869DC4844og4yKA3vu+++25cm0kDShpY0EDEOeecEwe5muu4446Lj1Hfp8esgS9tz7333ltn1sYll1wSdtppp7jWzaRJk+Kf2p6FNXv27Pix4oor5tU2SwNSetxnn312HFhojLr86U9/CltvvXU46qij4mGMf/3rX+MhdBqQ69q1a7zezTffHH7/+9+HYcOGxcOmNFiiGSrqvMwyy9Te3o033hiOP/74OHim50gNNEjwxhtvxIGhQtGAzkMPPRQf52KLLRZnVv3ud78Ln332Wejbt2+d62pQZ6WVVoqHounwyOxsPw22aDtPPPHEuH0XXXRR3CcbGqhYEK2vpsFIDWZof9Dzc9NNNzU6A0/7te5bg64jR44Mt9xyS9y3NXCjATb573//G3trkEdN9bi0zer+wAMPzPecN+cwyW+//Ta+xsaOHRsvW3TRRRu9/u233x47aeBMryvRIF4uzZLUa1Pt3n777fiYNRCqWZRZF1xwQRw80nV1exrU1cDlpptuGiZOnBgHtFpySKoGE5988slw+OGHx8ueeuqpuH9qgEwDUnrtaEBVf+rQYz1H+tmi/fbOO++cr6Mu0+PccMMNF3q7AABIVAYAgA5q3LhxehefefrppzPl5eWZzz//PPPAAw9kSkpKMt27d4+fZ2211VaZ1VZbLfPTTz/VXlZTU5MZNmxYZqWVVqq97Oyzz463+dBDD813f7q+XHHFFfE6d9xxR+3X5s6dm9lwww0ziy66aGbOnDnxshkzZsTrLb744pmysrI6t7XrrrtmFllkkcynn35ae9n777+f6dy5c/yeXAMHDsyMHDlyvse99dZb126TjB49On5/ZWVl/HzWrFmZLl26xPvK9cc//jF+f+5tNkbXO/TQQ2NfPYY33ngjttTlY8aMyattdrs33njjTHV1dZ37yX5NzUT31a1bt8y2226bmTdvXu31rrnmmni9W265pbZ7aWlpZs0118z8/PPPtde74YYb4vU222yz2st22WWXzCqrrJJpifvvvz/e7nPPPddoL233xx9/XHvZpEmT4uVXX3117WXnnHNOvGyfffap8/3vvPNOvPywww6rc/lJJ50UL3/22Wfr3Jdup776+8txxx2XKSoqykycOLH2soqKikyfPn3qNM9+ry578cUXay/Tc6HX04knnlh72ahRo+L1XnrppdrLvvvuu8zyyy+fWW655Wqfs/rPa5b61e84fPjweP/N1bNnzwb34WzbQw45pM7lu+22W6Zv3761n3/yySfx9XLBBRfUud67774bXzf1L68v+9jGjx/f6HWWWGKJzFprrVX7+Y8//jjfde6+++75mp9++umxefa1nH0etF0NPecAALRVHL4HAOjwNJNGM280u0AzPDQTRDObsoeGffPNN3GNF82G0BmxNPNJHzpkRrOGPvroo9qz9Wlmjw7Fa2imh2YxyD//+c84y2Gfffap/Zpm7WjGiGZa6fC0XJolo+3LmjdvXnjiiSfiTKJll1229nItnK3taS7NEMluk2iGl25bhwzJM888E2dgacZO/RlW+dBMJG2/Zpn85je/qT1sULOS8mmbpVkjC1o/6umnnw5z586N95G7yLu+d/HFF689TGzChAnxUMojjzyyzjpdmtmjQ7ZyadaLZuJoplVr74+5s3a0/pa2WTNk6tN259K+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+47RvihTiHP4AQAAAAAAAHng8D0AAAAAAAAkjkEpAAAAAAAAJI5BKQAAAAAAAHT8Qalrr702LLfccmGRRRYJv/nNb8Kbb76Z9CakzkUXXRTWW2+9sNhii4XS0tKw6667hg8++MC9Wan05z//ORQVFYVRo0a5NyU1vvjii7D//vuHvn37huLi4rDaaquFCRMmuDerw5s3b14466yzwvLLLx+7r7DCCuG8884LLGNYeC+++GLYaaedQv/+/ePPl0ceeaTO19X87LPPDr/61a/ic7H11luHjz76yLa9aWn/yy+/hFNPPTX+zOnZs2e8zoEHHhi+/PJL6zanZb/PdeSRR8brXHHFFYluY5rbT5kyJey8885hiSWWiPu//j/0s88+s2xv2vp///334dhjjw0DBgyIP/OHDh0arrvuOtv2pun91E8//RSOOeaY+P+ciy66aPjd734X/vvf/9q2OS3tv/nmm3DccceFwYMHx31+2WWXDccff3z49ttvrdudxrGETCYTdthhhwX+XrYOSt17773hD3/4Q1yl/e233w5rrLFG2G677UJZWVmSm5E6L7zwQvwB+frrr4ennnoq/o/ytttuG3744Qf3pqXK+PHjw/XXXx9WX31196akxuzZs8NGG20UunbtGv71r3+F999/P4wZMyb07t3bvWkd3sUXXxz++te/hmuuuSa+OdHnl1xySbj66qvdm9bh6Ge5fp/qH30aou5XXXVVfFPyxhtvxDeI+t2r/3lG67X/8ccf4//raHBWfz700EPxf+L0Rh2tv99nPfzww/H/f/QGHsm0nzZtWth4443DkCFDwvPPPx8mT54cXwf6B2m0fn+91/r3v/8d7rjjjvj7V/8QqkGqRx99NPFtTdv7qdGjR4fHHnss3H///fH6+keI3Xff3brdaWivzvq47LLLwnvvvRduvfXW+Bo49NBD3ZueurGEK664Ig5I5S2ToPXXXz9zzDHH1H4+b968TP/+/TMXXXRRkpuRemVlZZqqkHnhhRfcm5Ia3333XWallVbKPPXUU5nNNtssc8IJJ7g3KRVOPfXUzMYbb+zejFQaPnx45pBDDqlz2e67757Zb7/9bNuUBvrZ/vDDD9d+XlNTk1lqqaUyl156ae1llZWVme7du2fuvvtu01amo31D3nzzzXi9Tz/9NLHtSnP7mTNnZpZeeunMe++9lxk4cGBm7Nixlu1LW/u99tors//++9u2Ke39V1lllcy5555b57K11147c+aZZya8del6P6XfrV27ds3cf//9tdeZMmVKvM5rr71m3NJ0vpe97777Mt26dcv88ssviW5bmvtPnDgx/s796quvmvX/RLkSmyk1d+7c8NZbb8XDBrI6deoUP3/ttdeS2gyEUDuVsU+fPu5NSQ2NLg8fPrzO/o/Wp38VXHfddcOIESPidNO11lor3Hjjje7NSoVhw4aFZ555Jnz44Yfx80mTJoWXX345TulFcmbMmBFmzZpV52ePDqfR4fP87vX8/tW/IPbq1cu9KR1eTU1NOOCAA8LJJ58cVlllFffmpKr7P/7xj7DyyivHGZn63aufN/kcxoGW//7V//9o+QKNWz333HPxd7FmNqD13k/pfa5mkOT+vtVsQR1Kxu/b5N/L6jqLL7546NKlS4Jblt7+P/74Y9h3333jDM6llloq79tMbFDq66+/jmuM9OvXr87l+lz/w4zk/mdB03h1SNOqq67q3pxUuOeee+KhGzoeF8maPn16PIRspZVWCk888UQ46qij4jHmf/vb39yb1uGddtppYe+9947/Q6bDJzUgqJ89++23n3vTUiX7+5XfvX46XFJrTO2zzz7xf5TRunTIsN6M6Gc+kqMlObSmkdbQ3H777cOTTz4Zdtttt3gIkw4BQevTYfJaR0prSnXr1i0+D3qjuOmmm7o3rUO/n9LvVPWu/48O/L5N/r2sxh20jukRRxyR+Paltf/o0aPjgPguu+yyULfL0GEKZ+zoWFvNWEDr+/zzz8MJJ5wQj79lLQXPD07NlLrwwgvj5xoY0f6vtXVGjhzp3rwO7b777gt33nlnuOuuu+IshXfeeSf+EtO6LrRH2uhfz/fcc884a0ED5WhdmrFw5ZVXxn8QWqi1LdCi37uiNyZ6kyJrrrlmePXVV+Pv3s0228y8hekYlNLaL5otNXDgwLgwuv7/X79/mbFfGLyfarvt58yZE49O0cDsH//4x8S3L439H3300fDss8+GiRMnLvTtJjZTaskllwydO3ee7wwE+nxhpnghf1rk8PHHH4/TePWvJ0jmf4z1r4Zrr712/BdbfehfCrXosP6u2YNoPTrbmH4p5fr1r3/NGYASoENmsrOldPYxHUajNyjMGExW9vcrv3v9A1Kffvpp/AcKZkm1vpdeein+7tVhM9nfvep/4oknxjNAo3X/f1+9+d3rUVVVFc4444xw+eWXxzP06eQ6+v//vfbaKy4CjdZ7P6XfqVquprKyss71+X2b3HvZ7777Ls4M1FnidJILzdRH6/fXgJROcKFZgtnfuaKzT26++eZta1BK0xnXWWeduMZI7r+m6PMNN9wwqc1IJf3LrHYivTi10+gU7UjGVlttFd599904SyT7oZk7OoRJf9dALVqPppbWP2Wp1lXQvxyidenYcq0bmEv7e/Zf0ZEM/bzX/wzn/u7VvyLqLHz87k1uQOqjjz4KTz/9dDxNOFqfBsF1xrfc372aJaLBch3Kjdb9/32dOpzfvb6fOfrg92/y76f0PleDILm/b/U60GAsv29b/72s/t9G66bpZ5Bm7nCESnL99Y/Q9X/nytixY8O4cePa3uF7OkWpDtvQm/L1118/njJQpxI8+OCDk9yMVE6z0yE0f//73+PIcfa4Zi12W1xc7N68Dk296x/vrNOx640Ja3q1vuzxzTp8T28M33zzzXDDDTfED7Qu/QvtBRdcEGcq6PA9TenVv9wecsgh7k3rcLR+y8cff1xncXP9D4EWoFR/HTZ5/vnnx7XV9D8SOjW73qDvuuuu1u3u6O01U3OPPfaIh5DpXxY1Mzb7+1df1/84o/X2+/oDgHqzqAHawYMHG7Y2Xe01+KeZOVrDaIsttoinZn/sscfC888/b93utPTXIZJ6DvT/+BoI1Az92267Lf4ORuu9n9Kfhx56aHy/q+dCs2KPO+64OCC1wQYbuDe/Q7fPDkjpH0TvuOOO+Lk+pKSkhEkArdxfv1sbmg2on0fNngyTSdjVV1+dWXbZZeMpGtdff/3M66+/nvQmpI6e5oY+xo0b5960VNpss80yJ5xwgnszUuOxxx7LrLrqqpnu3btnhgwZkrnhhhvcm5QKc+bMifu5ft4vssgimUGDBsXTUf/888/uTetwnnvuuQZ/xo8cOTJ+vaamJnPWWWdl+vXrF18HW221VeaDDz5wb3aHbz9jxoxGf//q+9C6+319AwcOzIwdOzbx7Uxr+5tvvjmz4oorxp//a6yxRuaRRx6xbnOa+ut07AcddFCmf//+sf/gwYMzY8aMib8L0Lrvp6qqqjJHH310pnfv3pkePXpkdtttt/h8oHXbN/aa0Id+FyP5sQR9/eGHH272fRT9v28CAAAAAAAAEpPYmlIAAAAAAABAFoNSAAAAAAAASByDUgAAAAAAAEgcg1IAAAAAAABIHINSAAAAAAAASByDUgAAAAAAAEgcg1IAAAAAAABIHINSAAAAAAAASByDUgAAwOqGG24IyyyzTOjUqVO44oorQlt18803h2233bbV7+f5558PRUVFobKyMrQVU6dODRtssEFYZJFFwpprrhnai+uuuy7stNNO7s0AAACNYFAKAIAO4KCDDgq77rpruxjgyDVnzpxw7LHHhlNPPTV88cUX4YgjjmjwenoM2Y+ePXuGlVZaKT7mt956K5Ht/Omnn8JZZ50VzjnnnNrLbrzxxrDJJpuE3r17x4+tt946vPnmm43expFHHhm3vy0PvDVGj1vdP/jgg/DMM8+EtkhtH3nkkTqXHXLIIeHtt98OL730km27AABA4xiUAgAALfbLL78s1Pd99tln8XuHDx8efvWrX4UePXo0et1x48aFr776KvznP/8J1157bfj+++/Db37zm3DbbbeF1vbAAw+ExRdfPGy00UZ1Bvz22Wef8Nxzz4XXXnstzvbSTCoNrtX38MMPh9dffz30798/tFWZTCZUV1c3+LVp06aFjTfeOAwcODD07dt3oW5/7ty5IWndunUL++67b7jqqqsSv28AALBgDEoBAJAyDz74YFhllVVC9+7dw3LLLRfGjBmzwBknvXr1Crfeemv8+yeffBKvc++994bNNtssHtJ15513NjrotMsuu4RFF100Dursueee4b///W/8mm5vtdVWi38fNGhQvE3ddmO0DUsttVTcZg3+aKBov/32izOtZs+eHa9TUVERB4qWXnrpOMCl27/77rtrb0MDWBpU+fnnn+vctmaZHXDAAY3e9z333DPfYWB6zEcffXQ8nG3IkCHhpptuCjU1NfPNJNIg1XHHHRev37Vr19BcmgW27rrrxscxbNiwOEsp11//+tewwgorxIGXwYMHh9tvv732a9nn6J133qm9TLPldJkG03Jn0f3rX/8K66yzTtwfXn755fm2Q9fRtpx77rnx73/84x/j5e+++27YcsstQ3FxcWyqWW4aKKw/e++CCy6Ig3Haxux23XfffXGWmb53vfXWCx9++GEYP358fLzaV3bYYYdQXl5ee1v62jbbbBOWXHLJsMQSS8T9TjOgsrRPyG677RZvP/u56Hl79NFHQ1VVVbPbAwCAZDAoBQBAimhwQQNDe++9dxxU0ACDDkvLDjjl47TTTgsnnHBCmDJlSthuu+3m+7oGaDQg9c0334QXXnghPPXUU2H69Olhr732il/Xn08//XT8uw570ywozTbKx+jRo8N3330Xbzt7mJ0GWP7xj3+E9957Lw6UaLApe1jdiBEjwrx58+IgRVZZWVm8vg71aowGazRg0pQff/wxzvrq06dPnQa6/5NPPjkOBObjzDPPjAOGEyZMCF26dKmzfZp5pfYnnnhifJy///3vw8EHHxxnbS3M8/jnP/85Po+rr776fF/X86Jt133p7yeddFL44Ycf4nOuwxY1YHT//ffH51IDhLk0QKfBND0/jz/+eJ3DAf/v//4vDizpsWk20ymnnBKuvPLKeKjdxx9/HM4+++za6+s5HjlyZHweNONMh2/uuOOO8XLRNuTOpst+LnreNAPsjTfeyLsNAABoZRkAANDujRw5MtO5c+dMz54963wsssgiGf26nz17drzevvvum9lmm23qfO/JJ5+cGTp0aO3nuv7DDz9c5zpLLLFEZty4cfHvM2bMiNe54oormtymJ598Mm7TZ599VnvZf/7zn/i9b775Zvx84sSJ8XPdZlMa2iapqqqKX7v44osb/d7hw4dnTjzxxNrPjzrqqMwOO+xQ+/mYMWMygwYNytTU1DT4/Wqn+3jxxReb3Ebdrm5H25R14YUXxt7Z2x44cGBm7NixTd7Oc889F+/v6aefrr3sH//4R7wse9vDhg3LHH744XW+b8SIEZkdd9yxznOkvvUfh24/934eeeSRzIKsscYamXPOOaf28xtuuCHTu3fvzPfff19nGzt16pSZNWtW7T7Zr1+/zM8//1x7nex23XTTTbWX3X333fGyZ555pvayiy66KDN48OBGt2fevHmZxRZbLPPYY48tcB8Rbeutt966wMcJAACSxUwpAAA6iC222CIerpX7oUPKcmk2TO66SKLPP/rooziDKB8Lmjmk+9LMp9zZT0OHDo2H4elrhfC/sYj/HWImegznnXdePGxPM5Z0KNgTTzwRDyPMOvzww8OTTz5Zu/aTZonpULPsbdSXPexLhyk2RjONdIifZjBlr6dZaZr5o9tv7LZ1mJq2UR/1Z1LlzlrSelvZWV1NPY8L03VBz2NDdD9rrLFGXPw89/41Myz3MEM9Dzq8sL7cx9avX7/a6+Ze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E8Of//znOID04osvhk6dmv734euvvz4ceeSR4Xe/+118bb700kvh+OOPjwPJp556au31Hn300fjzesMNN6wdOL7vvvvCgQceGL7++us4OAYAQFvEoBQAdBD7779/nc81mKBBqfqXt3U//PBD6NmzZ2iv26M3mXrz2Z4stdRS4d133w2rrLJK7WW///3vwyGHHBLGjRsXzjrrrNqBqNVXXz1+5Pr888/DzJkzw2GHHVaQwbh+/frNt9/qjbxmgLS3/VmDB5oh0163p0uXLvGjLdGAS3t5jb388svzXbbCCiuEk046Kbz55pthgw02aPR7NfCmQdjhw4eHBx54IF52+OGHh5qamnDeeeeFI444IvTu3Ttefs0118RZjs8++2zo3r177Wt4yJAh8TDH9jQolclkwk8//RSKi4vdmwIASACH7wFAimiA5cQTTwzLLLNMfOMyePDgcNlll8U3Ablv+HQ9zY7JHiKVXTPp008/jbNn9H16w9C3b98wYsSIuMbLwsj+i/77778f9t133/gGa+ONN679+h133BHWWWedeF86/GXvvfeOAyD1vfHGG2HHHXeM368BJA2aXHnllXWuozdrmrGgr+vQll122SVMmTKl2dujRueff34YMGBAfFOvw2T+85//zLctDa0ptfnmm4dVV1013q6+T9+/9NJLh0suuWS+71fjnXfeOW5naWlpfDOpWUv1b/Ojjz6Ksyc0oKQ36Nou9fn2229rr6MZElOnTo0DEU3RYE/ugFTWbrvtFv+s36m+u+++O/bZb7/9QtIzvLT/ZQ+N0hv8f/zjH7VfV6/szC3NVMnuz9m1iDTrRN+/7LLLxteDXhfqrcGAhZF9nt966604Y07bpBlk8vPPP4dzzjknDu5l7+uUU06Jl9en/V6HXun7tQ/qtp588sk61/nLX/4SnzPdVv/+/cMxxxwTKisrm709uq5e10sssUR8PYwcOXK+729sTans+kM6xFe3r23Qtvz73/+e7/v1HKy77rpxH9VgjGb+NGedKm27nku9HrLPm2a7NbamlB6LZtJ99tln4be//W38u15jOixVNOi65ZZbxteVZlppFlZ9evyalZX9+ajn6uKLL46DQLm++uqr+Lr65ZdfwsLIPo6Geud67rnnQkVFRfyZm0vPtX5G5+7rc+bMiftKdkBKNJio13Y+gzvNeU4122uHHXaI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rxdtgxrq9aXXsk4uUOgBIL229DNG96VF1bOD4lrrSycfWBD9HNLhWlo/TY9Ns7q07a3h5JNPjoPBen3r8E3dtwZRNKNOM0TVKftaV//svtbUYufPP/983Df1mtJ6dBoweumll2Lr7CLkWRpg1c/nc845p/ZnkgaStC8cc8wx8bWuQWt9vxYxv+CCC2pP3KDXvfZjzZjcYIMN4sw/tdZA5cyZM+P1C0U/lzWbUANQRx99dFww//rrr4+DRJdccknt9TR4pcFq/bxVW23jLbfcEvdfreXWHBqc1aCWBt80u0+HMWo/1+HJuSc4AAC0c+7T/wEAWodOJ17/x/x3332XGT16dKZ///6Zrl27ZlZaaaXMpZdeWnu6+dxTpuv03jqdum5Dp0DPnmL94IMPziy55JKZRRddNLPddtvF6+qU3tnriE53ru9b0GnPs6cNb+yU3w8++GBm4403jqdq18eQIUPi4/rggw/qXO/ll1/ObLPNNvG05Lre6quvXuc05vL0009nNtpoo/iYFl988Xi6+vfff7/Z2zNv3rzMn/70p8yvfvWreBubb7555r333mvWY99ss80yq6yyyny3qe+rf8r76dOnZ4YPHx7vQ6d9P/HEE2MH3ebrr79ee51DDjkks8IKK2QWWWSRTJ8+fTJbbLFFfIwNPZ4FPQ/ZbW7sQ7dTn05Hr32ooqIiky/dpk4Nnw/1U8dcOi39HnvskenVq1fssP7662cef/zx+b7373//e2bo0KGZLl261LlvPf9bb7113Je1Tx9++OGZSZMmzbd9jZ3evr7GnmeZO3du5uKLL45f7969e6Z3796ZddZZJ+5T3377bZ3r3nLLLZm11lqr9nq63aeeeqrOda655pr4etBz0K9fv8xRRx0VX5/N3R49bwcccEB8LSyxxBLx7xMnTmzWY9fneh3WV/+1IM8880x8LN26dYv760033RT3aT1fC/L9999n9t133/j86j6zr5UZM2bMt526X73262usgW5Lr7P6Px9PP/30zIorrhi3V/vEsGHDMpdddll8/nLvS/ev7WjKxx9/nDnwwAMzgwYNiq9nPWZti5rqsTX0GmzotXbDDTdkBg8eXNtw7Nix8/3MljvvvDO+BtRL9/eb3/wm88ADD2SaI5/n9O23344/+/W66dGjR/zZ8+qrr873vW+99VbcBm33sssum7n88svjc1a/XUPPRXbf2WWXXeLvK92G/txnn30yH374YbMeEwCgfSjSf9wDYwAAoHE6THD06NFx1oPOptbeaUaWFszXjBSkj2Y+aiaj1mgDAADpxppSAAC0ITq0MJfWlNLhMTr8pyMMSCHd+7MGov75z382eTgpAABID9aUAgCgDdE6QlqLRWtkaY0frQejtYe0thTQ3ugMgZoRpz+1dpnWgNNC7aeccop70wAAQBvAoBQAAG2IFjPW2bk0CKXFirUI9T333BP22msv96YBedNi7FqQftasWXHRdp1s4MILL4wz/wAAAFhTCgAAAAAAAIljTSkAAAAAAAAkjkEpAAAAAAAAJI5BKQAAAAAAACSOQSkAAAAAAAAkjkEpAAAAAAAAJI5BKQAAAAAAACSOQSkAAAAAAAAkjkEpAAAAAAAAJI5BKQAAAAAAAISk/X8H50KvTF0zBQAAAABJRU5ErkJggg==", 165 | "text/plain": [ 166 | "
" 167 | ] 168 | }, 169 | "metadata": {}, 170 | "output_type": "display_data" 171 | }, 172 | { 173 | "name": "stdout", 174 | "output_type": "stream", 175 | "text": [ 176 | "Total number of recording sessions: 27\n", 177 | "Earliest recording started at: 14:05\n", 178 | "Latest recording ended at: 21:38\n", 179 | "Total recording time: 3.08 hours\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "# Download the lifelogs\n", 185 | "df = download_lifelogs()\n", 186 | "\n", 187 | "# Check if we have data\n", 188 | "if df.empty:\n", 189 | " print(\"No lifelogs found for the specified date.\")\n", 190 | "else:\n", 191 | " # Create a timeline visualization showing recording periods\n", 192 | " plt.figure(figsize=(12, 6))\n", 193 | " \n", 194 | " # For each lifelog, draw a horizontal line from first to last timestamp\n", 195 | " for i, row in df.iterrows():\n", 196 | " start_hour = row['first_timestamp'].hour + row['first_timestamp'].minute/60\n", 197 | " end_hour = row['last_timestamp'].hour + row['last_timestamp'].minute/60\n", 198 | " \n", 199 | " # Draw a horizontal line for this recording period\n", 200 | " plt.plot([start_hour, end_hour], [i, i], linewidth=3, color='blue')\n", 201 | " \n", 202 | " # Add labels and title\n", 203 | " plt.title('Recording Periods Throughout the Day')\n", 204 | " plt.xlabel('Hour of Day (24-hour format)')\n", 205 | " plt.ylabel('Recording Session')\n", 206 | " \n", 207 | " # Format x-axis to show hours\n", 208 | " plt.xticks(range(0, 25, 2))\n", 209 | " plt.xlim(0, 24)\n", 210 | " plt.grid(True, linestyle='--', alpha=0.7)\n", 211 | " \n", 212 | " # Remove y-axis ticks since they don't represent anything specific\n", 213 | " plt.yticks([])\n", 214 | " \n", 215 | " # Add a summary of total recording time\n", 216 | " total_recording_time = sum((row['last_timestamp'] - row['first_timestamp']).total_seconds()/3600 \n", 217 | " for _, row in df.iterrows())\n", 218 | " \n", 219 | " # Adjust the layout to make room for the text at the bottom\n", 220 | " plt.tight_layout(rect=[0, 0.05, 1, 1])\n", 221 | " \n", 222 | " plt.figtext(0.5, 0.01, \n", 223 | " f\"Total recordings: {len(df)} | Total recording time: {total_recording_time:.2f} hours\", \n", 224 | " ha=\"center\", fontsize=12)\n", 225 | " \n", 226 | " # Show the plot\n", 227 | " plt.show()\n", 228 | " \n", 229 | " # Print some basic statistics\n", 230 | " earliest_recording = df['first_timestamp'].min().strftime('%H:%M')\n", 231 | " latest_recording = df['last_timestamp'].max().strftime('%H:%M')\n", 232 | " \n", 233 | " print(f\"Total number of recording sessions: {len(df)}\")\n", 234 | " print(f\"Earliest recording started at: {earliest_recording}\")\n", 235 | " print(f\"Latest recording ended at: {latest_recording}\")\n", 236 | " print(f\"Total recording time: {total_recording_time:.2f} hours\")" 237 | ] 238 | } 239 | ], 240 | "metadata": { 241 | "kernelspec": { 242 | "display_name": "Python 3", 243 | "language": "python", 244 | "name": "python3" 245 | }, 246 | "language_info": { 247 | "codemirror_mode": { 248 | "name": "ipython", 249 | "version": 3 250 | }, 251 | "file_extension": ".py", 252 | "mimetype": "text/x-python", 253 | "name": "python", 254 | "nbconvert_exporter": "python", 255 | "pygments_lexer": "ipython3", 256 | "version": "3.11.11" 257 | } 258 | }, 259 | "nbformat": 4, 260 | "nbformat_minor": 2 261 | } 262 | -------------------------------------------------------------------------------- /openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Limitless Developer API 4 | description: API for accessing lifelogs, providing transparency and portability to user data. 5 | version: 1.0.0 6 | servers: 7 | - url: https://api.limitless.ai/ 8 | description: Production server 9 | 10 | tags: 11 | - name: Lifelogs 12 | description: Operations related to lifelogs data 13 | 14 | components: 15 | schemas: 16 | ContentNode: 17 | type: object 18 | properties: 19 | type: 20 | type: string 21 | description: Type of content node (e.g., heading1, heading2, heading3, blockquote, paragraph). More types might be added. 22 | content: 23 | type: string 24 | description: Content of the node. 25 | startTime: 26 | type: string 27 | format: date-time 28 | description: ISO format in given timezone. 29 | endTime: 30 | type: string 31 | format: date-time 32 | description: ISO format in given timezone. 33 | startOffsetMs: 34 | type: integer 35 | description: Milliseconds after start of this entry. 36 | endOffsetMs: 37 | type: integer 38 | description: Milliseconds after start of this entry. 39 | children: 40 | type: array 41 | items: 42 | $ref: "#/components/schemas/ContentNode" 43 | description: Child content nodes. 44 | speakerName: 45 | type: string 46 | description: Speaker identifier, present for certain node types (e.g., blockquote). 47 | nullable: true 48 | speakerIdentifier: 49 | type: string 50 | description: Speaker identifier, when applicable. Set to "user" when the speaker has been identified as the user. 51 | enum: ["user"] 52 | nullable: true 53 | 54 | Lifelog: 55 | type: object 56 | properties: 57 | id: 58 | type: string 59 | description: Unique identifier for the entry. 60 | title: 61 | type: string 62 | description: Title of the entry. Equal to the first heading1 node. 63 | markdown: 64 | type: string 65 | description: Raw markdown content of the entry. 66 | nullable: true 67 | contents: 68 | type: array 69 | items: 70 | $ref: "#/components/schemas/ContentNode" 71 | description: List of ContentNodes. 72 | startTime: 73 | type: string 74 | format: date-time 75 | description: ISO format in given timezone. 76 | endTime: 77 | type: string 78 | format: date-time 79 | description: ISO format in given timezone. 80 | isStarred: 81 | type: boolean 82 | description: Whether the lifelog has been starred by the user. 83 | updatedAt: 84 | type: string 85 | format: date-time 86 | description: The timestamp when the lifelog was last updated in ISO 8601 format. 87 | 88 | MetaLifelogs: 89 | type: object 90 | properties: 91 | nextCursor: 92 | type: string 93 | description: Cursor for pagination to retrieve the next set of lifelogs. 94 | nullable: true 95 | count: 96 | type: integer 97 | description: Number of lifelogs in the current response. 98 | 99 | Meta: 100 | type: object 101 | properties: 102 | lifelogs: 103 | $ref: "#/components/schemas/MetaLifelogs" 104 | 105 | LifelogsResponseData: 106 | type: object 107 | properties: 108 | lifelogs: 109 | type: array 110 | items: 111 | $ref: "#/components/schemas/Lifelog" 112 | 113 | LifelogsResponse: 114 | type: object 115 | properties: 116 | data: 117 | $ref: "#/components/schemas/LifelogsResponseData" 118 | meta: 119 | $ref: "#/components/schemas/Meta" 120 | 121 | paths: 122 | /v1/lifelogs: 123 | get: 124 | operationId: getLifelogs 125 | summary: Returns a list of lifelogs. 126 | description: Returns a list of lifelogs based on specified time range or date. 127 | tags: 128 | - Lifelogs 129 | parameters: 130 | - in: query 131 | name: timezone 132 | schema: 133 | type: string 134 | description: IANA timezone specifier. If missing, UTC is used. 135 | - in: query 136 | name: date 137 | schema: 138 | type: string 139 | format: date 140 | description: Will return all entries beginning on a date in the given timezone (YYYY-MM-DD). 141 | - in: query 142 | name: start 143 | schema: 144 | type: string 145 | format: date-time 146 | description: Start datetime in modified ISO-8601 format (YYYY-MM-DD or YYYY-MM-DD HH:mm:SS). Timezones/offsets will be ignored. 147 | - in: query 148 | name: end 149 | schema: 150 | type: string 151 | format: date-time 152 | description: End datetime in modified ISO-8601 format (YYYY-MM-DD or YYYY-MM-DD HH:mm:SS). Timezones/offsets will be ignored. 153 | - in: query 154 | name: cursor 155 | schema: 156 | type: string 157 | description: Cursor for pagination to retrieve the next set of entries. 158 | - in: query 159 | name: direction 160 | schema: 161 | type: string 162 | enum: ["asc", "desc"] 163 | default: "desc" 164 | description: Sort direction for entries. 165 | - in: query 166 | name: includeMarkdown 167 | schema: 168 | type: boolean 169 | default: true 170 | description: Whether to include markdown content in the response. 171 | - in: query 172 | name: includeHeadings 173 | schema: 174 | type: boolean 175 | default: true 176 | description: Whether to include headings in the response. 177 | - in: query 178 | name: limit 179 | schema: 180 | type: integer 181 | description: Maximum number of entries to return. 182 | - in: query 183 | name: isStarred 184 | schema: 185 | type: boolean 186 | default: false 187 | description: When true, only starred lifelogs will be returned. 188 | 189 | responses: 190 | "200": 191 | description: Successful response with entries. 192 | content: 193 | application/json: 194 | schema: 195 | $ref: "#/components/schemas/LifelogsResponse" 196 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .venv 3 | venv/ -------------------------------------------------------------------------------- /python/_client.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | import tzlocal 4 | 5 | def get_lifelogs(api_key, api_url=os.getenv("LIMITLESS_API_URL") or "https://api.limitless.ai", endpoint="v1/lifelogs", limit=50, batch_size=10, includeMarkdown=True, includeHeadings=False, date=None, timezone=None, direction="asc"): 6 | all_lifelogs = [] 7 | cursor = None 8 | 9 | # If limit is None, fetch all available lifelogs 10 | # Otherwise, set a batch size (e.g., 10) and fetch until we reach the limit 11 | if limit is not None: 12 | batch_size = min(batch_size, limit) 13 | 14 | while True: 15 | params = { 16 | "limit": batch_size, 17 | "includeMarkdown": "true" if includeMarkdown else "false", 18 | "includeHeadings": "true" if includeHeadings else "false", 19 | "date": date, 20 | "direction": direction, 21 | "timezone": timezone if timezone else str(tzlocal.get_localzone()) 22 | } 23 | 24 | # Add cursor for pagination if we have one 25 | if cursor: 26 | params["cursor"] = cursor 27 | 28 | response = requests.get( 29 | f"{api_url}/{endpoint}", 30 | headers={"X-API-Key": api_key}, 31 | params=params, 32 | ) 33 | 34 | if not response.ok: 35 | raise Exception(f"HTTP error! Status: {response.status_code}") 36 | 37 | data = response.json() 38 | lifelogs = data.get("data", {}).get("lifelogs", []) 39 | 40 | # Add transcripts from this batch 41 | for lifelog in lifelogs: 42 | all_lifelogs.append(lifelog) 43 | 44 | # Check if we've reached the requested limit 45 | if limit is not None and len(all_lifelogs) >= limit: 46 | return all_lifelogs[:limit] 47 | 48 | # Get the next cursor from the response 49 | next_cursor = data.get("meta", {}).get("lifelogs", {}).get("nextCursor") 50 | 51 | # If there's no next cursor or we got fewer results than requested, we're done 52 | if not next_cursor or len(lifelogs) < batch_size: 53 | break 54 | 55 | print(f"Fetched {len(lifelogs)} lifelogs, next cursor: {next_cursor}") 56 | cursor = next_cursor 57 | 58 | return all_lifelogs 59 | -------------------------------------------------------------------------------- /python/export_markdown.py: -------------------------------------------------------------------------------- 1 | import os 2 | from _client import get_lifelogs 3 | 4 | # Define a function to export the most recent lifelog 5 | # Customize the export function to your needs! 6 | def export_data(lifelogs): 7 | for lifelog in lifelogs: 8 | print(lifelog.get("markdown"), end="\n\n") 9 | 10 | # Run the script 11 | def main(): 12 | # NOTE: Increase limit to get more lifelogs 13 | lifelogs = get_lifelogs( 14 | api_key=os.getenv("LIMITLESS_API_KEY"), 15 | limit=1, 16 | direction="desc", 17 | ) 18 | 19 | # Export data 20 | export_data(lifelogs) 21 | 22 | if __name__ == "__main__": 23 | main() 24 | -------------------------------------------------------------------------------- /python/requirements.txt: -------------------------------------------------------------------------------- 1 | openai==1.65.5 2 | python-dotenv==1.0.1 3 | pytz==2025.1 4 | requests==2.32.3 5 | tzlocal==5.0.1 6 | -------------------------------------------------------------------------------- /python/summarize_day.py: -------------------------------------------------------------------------------- 1 | import os 2 | from openai import OpenAI 3 | from _client import get_lifelogs 4 | 5 | def summarize_lifelogs(lifelogs, should_stream=True): 6 | client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) 7 | 8 | response = client.chat.completions.create( 9 | model="gpt-4o-mini", 10 | messages=[ 11 | {"role": "system", "content": "You are a helpful assistant that summarizes transcripts."}, 12 | {"role": "user", "content": f"Summarize the following transcripts: {lifelogs}"} 13 | ], 14 | stream=should_stream 15 | ) 16 | if should_stream: 17 | for chunk in response: 18 | if chunk.choices[0].finish_reason is None: 19 | print(chunk.choices[0].delta.content, end='') 20 | else: 21 | return response.choices[0].message.content 22 | 23 | def main(): 24 | # Get transcripts, limiting size because OpenAI has a 128k context window 25 | lifelogs = get_lifelogs( 26 | api_key=os.getenv("LIMITLESS_API_KEY"), 27 | limit=10 28 | ) 29 | 30 | # Summarize transcripts 31 | summarize_lifelogs(lifelogs) 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /typescript/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .env 4 | *.log -------------------------------------------------------------------------------- /typescript/README.md: -------------------------------------------------------------------------------- 1 | # Limitless API TypeScript Examples 2 | 3 | This directory contains TypeScript examples for using the Limitless API. These examples demonstrate how to: 4 | 5 | - Fetch lifelogs from the API 6 | - Export lifelogs as markdown 7 | - Summarize lifelogs using OpenAI 8 | 9 | ## Setup 10 | 11 | 1. Install dependencies: 12 | 13 | ```bash 14 | npm install 15 | ``` 16 | 17 | 2. Create a `.env` file in the root directory with your API keys: 18 | 19 | ``` 20 | LIMITLESS_API_KEY=your_limitless_api_key 21 | OPENAI_API_KEY=your_openai_api_key 22 | LIMITLESS_API_URL=https://api.limitless.ai # Optional, defaults to this value 23 | ``` 24 | 25 | ## Available Scripts 26 | 27 | - `npm run build` - Compile TypeScript to JavaScript 28 | - `npm run export-markdown` - Run the markdown export example 29 | - `npm run summarize-day` - Run the lifelog summarization example 30 | 31 | ## Examples 32 | 33 | ### Export Markdown 34 | 35 | The `export_markdown.ts` script demonstrates how to fetch and export lifelogs as markdown. By default, it fetches the most recent lifelog. 36 | 37 | ### Summarize Day 38 | 39 | The `summarize_day.ts` script shows how to fetch lifelogs and use OpenAI to generate a summary. It fetches the 10 most recent lifelogs by default. 40 | 41 | ## API Client 42 | 43 | The `_client.ts` file contains the core API client functionality. It provides a `getLifelogs` function that handles pagination and supports various options for fetching lifelogs. 44 | -------------------------------------------------------------------------------- /typescript/_client.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import moment from "moment-timezone"; 3 | 4 | interface Lifelog { 5 | markdown?: string; 6 | [key: string]: any; 7 | } 8 | 9 | interface Meta { 10 | lifelogs: { 11 | nextCursor?: string; 12 | }; 13 | } 14 | 15 | interface Response { 16 | data: { 17 | lifelogs: Lifelog[]; 18 | }; 19 | meta: Meta; 20 | } 21 | 22 | export async function getLifelogs({ 23 | apiKey, 24 | apiUrl = process.env.LIMITLESS_API_URL || "https://api.limitless.ai", 25 | endpoint = "v1/lifelogs", 26 | limit = 50, 27 | batchSize = 10, 28 | includeMarkdown = true, 29 | includeHeadings = false, 30 | date, 31 | timezone, 32 | direction = "asc", 33 | }: { 34 | apiKey: string; 35 | apiUrl?: string; 36 | endpoint?: string; 37 | limit?: number | null; 38 | batchSize?: number; 39 | includeMarkdown?: boolean; 40 | includeHeadings?: boolean; 41 | date?: string; 42 | timezone?: string; 43 | direction?: "asc" | "desc"; 44 | }): Promise { 45 | const allLifelogs: Lifelog[] = []; 46 | let cursor: string | undefined; 47 | 48 | // If limit is null, fetch all available lifelogs 49 | // Otherwise, set a batch size and fetch until we reach the limit 50 | if (limit !== null) { 51 | batchSize = Math.min(batchSize, limit); 52 | } 53 | 54 | while (true) { 55 | const params: Record = { 56 | limit: batchSize.toString(), 57 | includeMarkdown: includeMarkdown.toString(), 58 | includeHeadings: includeHeadings.toString(), 59 | direction, 60 | timezone: timezone || moment.tz.guess(), 61 | }; 62 | 63 | if (date) { 64 | params.date = date; 65 | } 66 | 67 | if (cursor) { 68 | params.cursor = cursor; 69 | } 70 | 71 | try { 72 | const response = await axios.get(`${apiUrl}/${endpoint}`, { 73 | headers: { "X-API-Key": apiKey }, 74 | params, 75 | }); 76 | 77 | const lifelogs = response.data.data.lifelogs; 78 | 79 | // Add transcripts from this batch 80 | allLifelogs.push(...lifelogs); 81 | 82 | // Check if we've reached the requested limit 83 | if (limit !== null && allLifelogs.length >= limit) { 84 | return allLifelogs.slice(0, limit); 85 | } 86 | 87 | // Get the next cursor from the response 88 | const nextCursor = response.data.meta.lifelogs.nextCursor; 89 | 90 | // If there's no next cursor or we got fewer results than requested, we're done 91 | if (!nextCursor || lifelogs.length < batchSize) { 92 | break; 93 | } 94 | 95 | console.log( 96 | `Fetched ${lifelogs.length} lifelogs, next cursor: ${nextCursor}` 97 | ); 98 | cursor = nextCursor; 99 | } catch (error) { 100 | if (axios.isAxiosError(error)) { 101 | throw new Error(`HTTP error! Status: ${error.response?.status}`); 102 | } 103 | throw error; 104 | } 105 | } 106 | 107 | return allLifelogs; 108 | } 109 | -------------------------------------------------------------------------------- /typescript/export_markdown.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | import { getLifelogs } from "./_client"; 3 | 4 | // Load environment variables 5 | dotenv.config(); 6 | 7 | // Define a function to export the most recent lifelog 8 | // Customize the export function to your needs! 9 | function exportData(lifelogs: any[]): void { 10 | for (const lifelog of lifelogs) { 11 | console.log(lifelog.markdown || "", "\n"); 12 | } 13 | } 14 | 15 | // Run the script 16 | async function main(): Promise { 17 | try { 18 | // NOTE: Increase limit to get more lifelogs 19 | const lifelogs = await getLifelogs({ 20 | apiKey: process.env.LIMITLESS_API_KEY || "", 21 | limit: 1, 22 | direction: "desc", 23 | }); 24 | 25 | // Export data 26 | exportData(lifelogs); 27 | } catch (error) { 28 | console.error("Error:", error); 29 | process.exit(1); 30 | } 31 | } 32 | 33 | // Run the main function 34 | main(); 35 | -------------------------------------------------------------------------------- /typescript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "limitless-api-examples-typescript", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "limitless-api-examples-typescript", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "axios": "^1.6.7", 12 | "dotenv": "^16.4.5", 13 | "moment-timezone": "^0.5.45", 14 | "openai": "^4.28.0" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "^20.11.24", 18 | "ts-node": "^10.9.2", 19 | "typescript": "^5.3.3" 20 | } 21 | }, 22 | "node_modules/@cspotcode/source-map-support": { 23 | "version": "0.8.1", 24 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 25 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 26 | "dev": true, 27 | "license": "MIT", 28 | "dependencies": { 29 | "@jridgewell/trace-mapping": "0.3.9" 30 | }, 31 | "engines": { 32 | "node": ">=12" 33 | } 34 | }, 35 | "node_modules/@jridgewell/resolve-uri": { 36 | "version": "3.1.2", 37 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 38 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 39 | "dev": true, 40 | "license": "MIT", 41 | "engines": { 42 | "node": ">=6.0.0" 43 | } 44 | }, 45 | "node_modules/@jridgewell/sourcemap-codec": { 46 | "version": "1.5.0", 47 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 48 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 49 | "dev": true, 50 | "license": "MIT" 51 | }, 52 | "node_modules/@jridgewell/trace-mapping": { 53 | "version": "0.3.9", 54 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 55 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 56 | "dev": true, 57 | "license": "MIT", 58 | "dependencies": { 59 | "@jridgewell/resolve-uri": "^3.0.3", 60 | "@jridgewell/sourcemap-codec": "^1.4.10" 61 | } 62 | }, 63 | "node_modules/@tsconfig/node10": { 64 | "version": "1.0.11", 65 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", 66 | "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", 67 | "dev": true, 68 | "license": "MIT" 69 | }, 70 | "node_modules/@tsconfig/node12": { 71 | "version": "1.0.11", 72 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 73 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 74 | "dev": true, 75 | "license": "MIT" 76 | }, 77 | "node_modules/@tsconfig/node14": { 78 | "version": "1.0.3", 79 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 80 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 81 | "dev": true, 82 | "license": "MIT" 83 | }, 84 | "node_modules/@tsconfig/node16": { 85 | "version": "1.0.4", 86 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 87 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 88 | "dev": true, 89 | "license": "MIT" 90 | }, 91 | "node_modules/@types/node": { 92 | "version": "20.17.32", 93 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.32.tgz", 94 | "integrity": "sha512-zeMXFn8zQ+UkjK4ws0RiOC9EWByyW1CcVmLe+2rQocXRsGEDxUCwPEIVgpsGcLHS/P8JkT0oa3839BRABS0oPw==", 95 | "license": "MIT", 96 | "dependencies": { 97 | "undici-types": "~6.19.2" 98 | } 99 | }, 100 | "node_modules/@types/node-fetch": { 101 | "version": "2.6.12", 102 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", 103 | "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", 104 | "license": "MIT", 105 | "dependencies": { 106 | "@types/node": "*", 107 | "form-data": "^4.0.0" 108 | } 109 | }, 110 | "node_modules/abort-controller": { 111 | "version": "3.0.0", 112 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 113 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 114 | "license": "MIT", 115 | "dependencies": { 116 | "event-target-shim": "^5.0.0" 117 | }, 118 | "engines": { 119 | "node": ">=6.5" 120 | } 121 | }, 122 | "node_modules/acorn": { 123 | "version": "8.14.1", 124 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", 125 | "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", 126 | "dev": true, 127 | "license": "MIT", 128 | "bin": { 129 | "acorn": "bin/acorn" 130 | }, 131 | "engines": { 132 | "node": ">=0.4.0" 133 | } 134 | }, 135 | "node_modules/acorn-walk": { 136 | "version": "8.3.4", 137 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", 138 | "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", 139 | "dev": true, 140 | "license": "MIT", 141 | "dependencies": { 142 | "acorn": "^8.11.0" 143 | }, 144 | "engines": { 145 | "node": ">=0.4.0" 146 | } 147 | }, 148 | "node_modules/agentkeepalive": { 149 | "version": "4.6.0", 150 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", 151 | "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", 152 | "license": "MIT", 153 | "dependencies": { 154 | "humanize-ms": "^1.2.1" 155 | }, 156 | "engines": { 157 | "node": ">= 8.0.0" 158 | } 159 | }, 160 | "node_modules/arg": { 161 | "version": "4.1.3", 162 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 163 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 164 | "dev": true, 165 | "license": "MIT" 166 | }, 167 | "node_modules/asynckit": { 168 | "version": "0.4.0", 169 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 170 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 171 | "license": "MIT" 172 | }, 173 | "node_modules/axios": { 174 | "version": "1.9.0", 175 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", 176 | "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", 177 | "license": "MIT", 178 | "dependencies": { 179 | "follow-redirects": "^1.15.6", 180 | "form-data": "^4.0.0", 181 | "proxy-from-env": "^1.1.0" 182 | } 183 | }, 184 | "node_modules/call-bind-apply-helpers": { 185 | "version": "1.0.2", 186 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 187 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 188 | "license": "MIT", 189 | "dependencies": { 190 | "es-errors": "^1.3.0", 191 | "function-bind": "^1.1.2" 192 | }, 193 | "engines": { 194 | "node": ">= 0.4" 195 | } 196 | }, 197 | "node_modules/combined-stream": { 198 | "version": "1.0.8", 199 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 200 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 201 | "license": "MIT", 202 | "dependencies": { 203 | "delayed-stream": "~1.0.0" 204 | }, 205 | "engines": { 206 | "node": ">= 0.8" 207 | } 208 | }, 209 | "node_modules/create-require": { 210 | "version": "1.1.1", 211 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 212 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 213 | "dev": true, 214 | "license": "MIT" 215 | }, 216 | "node_modules/delayed-stream": { 217 | "version": "1.0.0", 218 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 219 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 220 | "license": "MIT", 221 | "engines": { 222 | "node": ">=0.4.0" 223 | } 224 | }, 225 | "node_modules/diff": { 226 | "version": "4.0.2", 227 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 228 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 229 | "dev": true, 230 | "license": "BSD-3-Clause", 231 | "engines": { 232 | "node": ">=0.3.1" 233 | } 234 | }, 235 | "node_modules/dotenv": { 236 | "version": "16.5.0", 237 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", 238 | "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", 239 | "license": "BSD-2-Clause", 240 | "engines": { 241 | "node": ">=12" 242 | }, 243 | "funding": { 244 | "url": "https://dotenvx.com" 245 | } 246 | }, 247 | "node_modules/dunder-proto": { 248 | "version": "1.0.1", 249 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 250 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 251 | "license": "MIT", 252 | "dependencies": { 253 | "call-bind-apply-helpers": "^1.0.1", 254 | "es-errors": "^1.3.0", 255 | "gopd": "^1.2.0" 256 | }, 257 | "engines": { 258 | "node": ">= 0.4" 259 | } 260 | }, 261 | "node_modules/es-define-property": { 262 | "version": "1.0.1", 263 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 264 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 265 | "license": "MIT", 266 | "engines": { 267 | "node": ">= 0.4" 268 | } 269 | }, 270 | "node_modules/es-errors": { 271 | "version": "1.3.0", 272 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 273 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 274 | "license": "MIT", 275 | "engines": { 276 | "node": ">= 0.4" 277 | } 278 | }, 279 | "node_modules/es-object-atoms": { 280 | "version": "1.1.1", 281 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 282 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 283 | "license": "MIT", 284 | "dependencies": { 285 | "es-errors": "^1.3.0" 286 | }, 287 | "engines": { 288 | "node": ">= 0.4" 289 | } 290 | }, 291 | "node_modules/es-set-tostringtag": { 292 | "version": "2.1.0", 293 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 294 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 295 | "license": "MIT", 296 | "dependencies": { 297 | "es-errors": "^1.3.0", 298 | "get-intrinsic": "^1.2.6", 299 | "has-tostringtag": "^1.0.2", 300 | "hasown": "^2.0.2" 301 | }, 302 | "engines": { 303 | "node": ">= 0.4" 304 | } 305 | }, 306 | "node_modules/event-target-shim": { 307 | "version": "5.0.1", 308 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 309 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 310 | "license": "MIT", 311 | "engines": { 312 | "node": ">=6" 313 | } 314 | }, 315 | "node_modules/follow-redirects": { 316 | "version": "1.15.9", 317 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 318 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 319 | "funding": [ 320 | { 321 | "type": "individual", 322 | "url": "https://github.com/sponsors/RubenVerborgh" 323 | } 324 | ], 325 | "license": "MIT", 326 | "engines": { 327 | "node": ">=4.0" 328 | }, 329 | "peerDependenciesMeta": { 330 | "debug": { 331 | "optional": true 332 | } 333 | } 334 | }, 335 | "node_modules/form-data": { 336 | "version": "4.0.2", 337 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", 338 | "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", 339 | "license": "MIT", 340 | "dependencies": { 341 | "asynckit": "^0.4.0", 342 | "combined-stream": "^1.0.8", 343 | "es-set-tostringtag": "^2.1.0", 344 | "mime-types": "^2.1.12" 345 | }, 346 | "engines": { 347 | "node": ">= 6" 348 | } 349 | }, 350 | "node_modules/form-data-encoder": { 351 | "version": "1.7.2", 352 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 353 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", 354 | "license": "MIT" 355 | }, 356 | "node_modules/formdata-node": { 357 | "version": "4.4.1", 358 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 359 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 360 | "license": "MIT", 361 | "dependencies": { 362 | "node-domexception": "1.0.0", 363 | "web-streams-polyfill": "4.0.0-beta.3" 364 | }, 365 | "engines": { 366 | "node": ">= 12.20" 367 | } 368 | }, 369 | "node_modules/function-bind": { 370 | "version": "1.1.2", 371 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 372 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 373 | "license": "MIT", 374 | "funding": { 375 | "url": "https://github.com/sponsors/ljharb" 376 | } 377 | }, 378 | "node_modules/get-intrinsic": { 379 | "version": "1.3.0", 380 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 381 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 382 | "license": "MIT", 383 | "dependencies": { 384 | "call-bind-apply-helpers": "^1.0.2", 385 | "es-define-property": "^1.0.1", 386 | "es-errors": "^1.3.0", 387 | "es-object-atoms": "^1.1.1", 388 | "function-bind": "^1.1.2", 389 | "get-proto": "^1.0.1", 390 | "gopd": "^1.2.0", 391 | "has-symbols": "^1.1.0", 392 | "hasown": "^2.0.2", 393 | "math-intrinsics": "^1.1.0" 394 | }, 395 | "engines": { 396 | "node": ">= 0.4" 397 | }, 398 | "funding": { 399 | "url": "https://github.com/sponsors/ljharb" 400 | } 401 | }, 402 | "node_modules/get-proto": { 403 | "version": "1.0.1", 404 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 405 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 406 | "license": "MIT", 407 | "dependencies": { 408 | "dunder-proto": "^1.0.1", 409 | "es-object-atoms": "^1.0.0" 410 | }, 411 | "engines": { 412 | "node": ">= 0.4" 413 | } 414 | }, 415 | "node_modules/gopd": { 416 | "version": "1.2.0", 417 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 418 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 419 | "license": "MIT", 420 | "engines": { 421 | "node": ">= 0.4" 422 | }, 423 | "funding": { 424 | "url": "https://github.com/sponsors/ljharb" 425 | } 426 | }, 427 | "node_modules/has-symbols": { 428 | "version": "1.1.0", 429 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 430 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 431 | "license": "MIT", 432 | "engines": { 433 | "node": ">= 0.4" 434 | }, 435 | "funding": { 436 | "url": "https://github.com/sponsors/ljharb" 437 | } 438 | }, 439 | "node_modules/has-tostringtag": { 440 | "version": "1.0.2", 441 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 442 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 443 | "license": "MIT", 444 | "dependencies": { 445 | "has-symbols": "^1.0.3" 446 | }, 447 | "engines": { 448 | "node": ">= 0.4" 449 | }, 450 | "funding": { 451 | "url": "https://github.com/sponsors/ljharb" 452 | } 453 | }, 454 | "node_modules/hasown": { 455 | "version": "2.0.2", 456 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 457 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 458 | "license": "MIT", 459 | "dependencies": { 460 | "function-bind": "^1.1.2" 461 | }, 462 | "engines": { 463 | "node": ">= 0.4" 464 | } 465 | }, 466 | "node_modules/humanize-ms": { 467 | "version": "1.2.1", 468 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 469 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 470 | "license": "MIT", 471 | "dependencies": { 472 | "ms": "^2.0.0" 473 | } 474 | }, 475 | "node_modules/make-error": { 476 | "version": "1.3.6", 477 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 478 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 479 | "dev": true, 480 | "license": "ISC" 481 | }, 482 | "node_modules/math-intrinsics": { 483 | "version": "1.1.0", 484 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 485 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 486 | "license": "MIT", 487 | "engines": { 488 | "node": ">= 0.4" 489 | } 490 | }, 491 | "node_modules/mime-db": { 492 | "version": "1.52.0", 493 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 494 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 495 | "license": "MIT", 496 | "engines": { 497 | "node": ">= 0.6" 498 | } 499 | }, 500 | "node_modules/mime-types": { 501 | "version": "2.1.35", 502 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 503 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 504 | "license": "MIT", 505 | "dependencies": { 506 | "mime-db": "1.52.0" 507 | }, 508 | "engines": { 509 | "node": ">= 0.6" 510 | } 511 | }, 512 | "node_modules/moment": { 513 | "version": "2.30.1", 514 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", 515 | "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", 516 | "license": "MIT", 517 | "engines": { 518 | "node": "*" 519 | } 520 | }, 521 | "node_modules/moment-timezone": { 522 | "version": "0.5.48", 523 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", 524 | "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", 525 | "license": "MIT", 526 | "dependencies": { 527 | "moment": "^2.29.4" 528 | }, 529 | "engines": { 530 | "node": "*" 531 | } 532 | }, 533 | "node_modules/ms": { 534 | "version": "2.1.3", 535 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 536 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 537 | "license": "MIT" 538 | }, 539 | "node_modules/node-domexception": { 540 | "version": "1.0.0", 541 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 542 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 543 | "deprecated": "Use your platform's native DOMException instead", 544 | "funding": [ 545 | { 546 | "type": "github", 547 | "url": "https://github.com/sponsors/jimmywarting" 548 | }, 549 | { 550 | "type": "github", 551 | "url": "https://paypal.me/jimmywarting" 552 | } 553 | ], 554 | "license": "MIT", 555 | "engines": { 556 | "node": ">=10.5.0" 557 | } 558 | }, 559 | "node_modules/node-fetch": { 560 | "version": "2.7.0", 561 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 562 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 563 | "license": "MIT", 564 | "dependencies": { 565 | "whatwg-url": "^5.0.0" 566 | }, 567 | "engines": { 568 | "node": "4.x || >=6.0.0" 569 | }, 570 | "peerDependencies": { 571 | "encoding": "^0.1.0" 572 | }, 573 | "peerDependenciesMeta": { 574 | "encoding": { 575 | "optional": true 576 | } 577 | } 578 | }, 579 | "node_modules/openai": { 580 | "version": "4.96.2", 581 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.96.2.tgz", 582 | "integrity": "sha512-R2XnxvMsizkROr7BV3uNp1q/3skwPZ7fmPjO1bXLnfB4Tu5xKxrT1EVwzjhxn0MZKBKAvOaGWS63jTMN6KrIXA==", 583 | "license": "Apache-2.0", 584 | "dependencies": { 585 | "@types/node": "^18.11.18", 586 | "@types/node-fetch": "^2.6.4", 587 | "abort-controller": "^3.0.0", 588 | "agentkeepalive": "^4.2.1", 589 | "form-data-encoder": "1.7.2", 590 | "formdata-node": "^4.3.2", 591 | "node-fetch": "^2.6.7" 592 | }, 593 | "bin": { 594 | "openai": "bin/cli" 595 | }, 596 | "peerDependencies": { 597 | "ws": "^8.18.0", 598 | "zod": "^3.23.8" 599 | }, 600 | "peerDependenciesMeta": { 601 | "ws": { 602 | "optional": true 603 | }, 604 | "zod": { 605 | "optional": true 606 | } 607 | } 608 | }, 609 | "node_modules/openai/node_modules/@types/node": { 610 | "version": "18.19.87", 611 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.87.tgz", 612 | "integrity": "sha512-OIAAu6ypnVZHmsHCeJ+7CCSub38QNBS9uceMQeg7K5Ur0Jr+wG9wEOEvvMbhp09pxD5czIUy/jND7s7Tb6Nw7A==", 613 | "license": "MIT", 614 | "dependencies": { 615 | "undici-types": "~5.26.4" 616 | } 617 | }, 618 | "node_modules/openai/node_modules/undici-types": { 619 | "version": "5.26.5", 620 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 621 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 622 | "license": "MIT" 623 | }, 624 | "node_modules/proxy-from-env": { 625 | "version": "1.1.0", 626 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 627 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 628 | "license": "MIT" 629 | }, 630 | "node_modules/tr46": { 631 | "version": "0.0.3", 632 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 633 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", 634 | "license": "MIT" 635 | }, 636 | "node_modules/ts-node": { 637 | "version": "10.9.2", 638 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 639 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 640 | "dev": true, 641 | "license": "MIT", 642 | "dependencies": { 643 | "@cspotcode/source-map-support": "^0.8.0", 644 | "@tsconfig/node10": "^1.0.7", 645 | "@tsconfig/node12": "^1.0.7", 646 | "@tsconfig/node14": "^1.0.0", 647 | "@tsconfig/node16": "^1.0.2", 648 | "acorn": "^8.4.1", 649 | "acorn-walk": "^8.1.1", 650 | "arg": "^4.1.0", 651 | "create-require": "^1.1.0", 652 | "diff": "^4.0.1", 653 | "make-error": "^1.1.1", 654 | "v8-compile-cache-lib": "^3.0.1", 655 | "yn": "3.1.1" 656 | }, 657 | "bin": { 658 | "ts-node": "dist/bin.js", 659 | "ts-node-cwd": "dist/bin-cwd.js", 660 | "ts-node-esm": "dist/bin-esm.js", 661 | "ts-node-script": "dist/bin-script.js", 662 | "ts-node-transpile-only": "dist/bin-transpile.js", 663 | "ts-script": "dist/bin-script-deprecated.js" 664 | }, 665 | "peerDependencies": { 666 | "@swc/core": ">=1.2.50", 667 | "@swc/wasm": ">=1.2.50", 668 | "@types/node": "*", 669 | "typescript": ">=2.7" 670 | }, 671 | "peerDependenciesMeta": { 672 | "@swc/core": { 673 | "optional": true 674 | }, 675 | "@swc/wasm": { 676 | "optional": true 677 | } 678 | } 679 | }, 680 | "node_modules/typescript": { 681 | "version": "5.8.3", 682 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 683 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 684 | "dev": true, 685 | "license": "Apache-2.0", 686 | "bin": { 687 | "tsc": "bin/tsc", 688 | "tsserver": "bin/tsserver" 689 | }, 690 | "engines": { 691 | "node": ">=14.17" 692 | } 693 | }, 694 | "node_modules/undici-types": { 695 | "version": "6.19.8", 696 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 697 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 698 | "license": "MIT" 699 | }, 700 | "node_modules/v8-compile-cache-lib": { 701 | "version": "3.0.1", 702 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 703 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 704 | "dev": true, 705 | "license": "MIT" 706 | }, 707 | "node_modules/web-streams-polyfill": { 708 | "version": "4.0.0-beta.3", 709 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 710 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 711 | "license": "MIT", 712 | "engines": { 713 | "node": ">= 14" 714 | } 715 | }, 716 | "node_modules/webidl-conversions": { 717 | "version": "3.0.1", 718 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 719 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", 720 | "license": "BSD-2-Clause" 721 | }, 722 | "node_modules/whatwg-url": { 723 | "version": "5.0.0", 724 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 725 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 726 | "license": "MIT", 727 | "dependencies": { 728 | "tr46": "~0.0.3", 729 | "webidl-conversions": "^3.0.0" 730 | } 731 | }, 732 | "node_modules/yn": { 733 | "version": "3.1.1", 734 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 735 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 736 | "dev": true, 737 | "license": "MIT", 738 | "engines": { 739 | "node": ">=6" 740 | } 741 | } 742 | } 743 | } 744 | -------------------------------------------------------------------------------- /typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "limitless-api-examples-typescript", 3 | "version": "1.0.0", 4 | "description": "TypeScript examples for the Limitless API", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "start": "ts-node", 9 | "export-markdown": "ts-node export_markdown.ts", 10 | "summarize-day": "ts-node summarize_day.ts" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.6.7", 14 | "dotenv": "^16.4.5", 15 | "moment-timezone": "^0.5.45", 16 | "openai": "^4.28.0" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^20.11.24", 20 | "ts-node": "^10.9.2", 21 | "typescript": "^5.3.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /typescript/summarize_day.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | import OpenAI from "openai"; 3 | import { getLifelogs } from "./_client"; 4 | 5 | // Load environment variables 6 | dotenv.config(); 7 | 8 | async function summarizeLifelogs( 9 | lifelogs: any[], 10 | shouldStream = true 11 | ): Promise { 12 | const client = new OpenAI({ 13 | apiKey: process.env.OPENAI_API_KEY, 14 | }); 15 | 16 | if (shouldStream) { 17 | const stream = await client.chat.completions.create({ 18 | model: "gpt-4.1", 19 | messages: [ 20 | { 21 | role: "system", 22 | content: "You are a helpful assistant that summarizes transcripts.", 23 | }, 24 | { 25 | role: "user", 26 | content: `Summarize the following transcripts: ${JSON.stringify( 27 | lifelogs 28 | )}`, 29 | }, 30 | ], 31 | stream: true, 32 | }); 33 | 34 | for await (const chunk of stream) { 35 | if (chunk.choices[0]?.finish_reason === null) { 36 | process.stdout.write(chunk.choices[0]?.delta?.content || ""); 37 | } 38 | } 39 | } else { 40 | const response = await client.chat.completions.create({ 41 | model: "gpt-4.1", 42 | messages: [ 43 | { 44 | role: "system", 45 | content: "You are a helpful assistant that summarizes transcripts.", 46 | }, 47 | { 48 | role: "user", 49 | content: `Summarize the following transcripts: ${JSON.stringify( 50 | lifelogs 51 | )}`, 52 | }, 53 | ], 54 | stream: false, 55 | }); 56 | 57 | const content = response.choices[0]?.message?.content; 58 | return content || ""; 59 | } 60 | } 61 | 62 | async function main(): Promise { 63 | try { 64 | // Get transcripts, limiting size because OpenAI has a 128k context window 65 | const lifelogs = await getLifelogs({ 66 | apiKey: process.env.LIMITLESS_API_KEY || "", 67 | limit: 10, 68 | }); 69 | 70 | // Summarize transcripts 71 | await summarizeLifelogs(lifelogs); 72 | } catch (error) { 73 | console.error("Error:", error); 74 | process.exit(1); 75 | } 76 | } 77 | 78 | // Run the main function 79 | main(); 80 | -------------------------------------------------------------------------------- /typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "lib": ["ES2020", "DOM"], 6 | "outDir": "./dist", 7 | "rootDir": "./", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "types": ["node"] 13 | }, 14 | "include": ["./**/*"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | --------------------------------------------------------------------------------