├── .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 |
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 | 
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 |
--------------------------------------------------------------------------------