Compare commits

..

No commits in common. "main" and "1.0.1" have entirely different histories.
main ... 1.0.1

12 changed files with 48 additions and 2480 deletions

View file

@ -32,4 +32,4 @@ jobs:
gh release create "$tag" \
--title="$tag" \
--draft \
main.js manifest.json
main.js manifest.json styles.css

View file

@ -1,3 +1,5 @@
Copyright (C) 2020-2025 by Dynalist Inc.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -78,16 +78,10 @@ If you encounter any issues:
## Star History
<a href="https://www.star-history.com/?repos=Maclean-D%2Fobsidian-limitless-lifelogs&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=Maclean-D/obsidian-limitless-lifelogs&type=date&theme=dark&legend=top-left&sealed_token=n0qnDUpZGvp6y_j0NpoDcTvohZJ0tocsgfpskhjAPGqYY0mSleeunkuQ8dm8HcZi5aCR2D_OHiD8q1Bz4pnUHiXo48HcWKIHPHFUgls95HtHhPnjnuwr5lsOOBjrenFeB0jwJjXP3aNhafiFlspD5WjXA1VVf1XqIO6Yktr2CBHPrSLgqq0YJZqhctEe" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=Maclean-D/obsidian-limitless-lifelogs&type=date&legend=top-left&sealed_token=n0qnDUpZGvp6y_j0NpoDcTvohZJ0tocsgfpskhjAPGqYY0mSleeunkuQ8dm8HcZi5aCR2D_OHiD8q1Bz4pnUHiXo48HcWKIHPHFUgls95HtHhPnjnuwr5lsOOBjrenFeB0jwJjXP3aNhafiFlspD5WjXA1VVf1XqIO6Yktr2CBHPrSLgqq0YJZqhctEe" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=Maclean-D/obsidian-limitless-lifelogs&type=date&legend=top-left&sealed_token=n0qnDUpZGvp6y_j0NpoDcTvohZJ0tocsgfpskhjAPGqYY0mSleeunkuQ8dm8HcZi5aCR2D_OHiD8q1Bz4pnUHiXo48HcWKIHPHFUgls95HtHhPnjnuwr5lsOOBjrenFeB0jwJjXP3aNhafiFlspD5WjXA1VVf1XqIO6Yktr2CBHPrSLgqq0YJZqhctEe" />
</picture>
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=Maclean-D/obsidian-limitless-lifelogs&type=Date)](https://star-history.com/#Maclean-D/obsidian-limitless-lifelogs&Date)
## Contributors
<a href="https://github.com/Maclean-D/obsidian-limitless-lifelogs/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Maclean-D/obsidian-limitless-lifelogs" />
</a>
</a>

View file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 52 KiB

91
main.ts
View file

@ -33,7 +33,7 @@ export default class LimitlessLifelogsPlugin extends Plugin {
// Add command for syncing
this.addCommand({
id: 'sync-limitless-lifelogs',
name: 'Sync',
name: 'Sync Limitless Lifelogs',
callback: async () => {
await this.syncLifelogs();
}
@ -96,8 +96,8 @@ export default class LimitlessLifelogsPlugin extends Plugin {
}
private async ensureFolderExists(path: string) {
const folder = this.app.vault.getFolderByPath(path);
if (!folder) {
const folderExists = await this.app.vault.adapter.exists(path);
if (!folderExists) {
await this.app.vault.createFolder(path);
}
}
@ -105,15 +105,14 @@ export default class LimitlessLifelogsPlugin extends Plugin {
private async getLastSyncedDate(): Promise<Date | null> {
const folderPath = normalizePath(this.settings.folderPath);
try {
const files = this.app.vault.getFiles()
.filter(file => file.path.startsWith(folderPath + '/'))
.filter(file => file.path.endsWith('.md'))
.map(file => file.basename)
.filter(basename => /^\d{4}-\d{2}-\d{2}$/.test(basename))
.map(basename => new Date(basename))
const files = await this.app.vault.adapter.list(folderPath);
const dates = files.files
.map(file => file.split('/').pop()?.replace('.md', ''))
.filter(date => date && /^\d{4}-\d{2}-\d{2}$/.test(date))
.map(date => new Date(date as string))
.sort((a, b) => b.getTime() - a.getTime());
return files.length > 0 ? files[0] : null;
return dates.length > 0 ? dates[0] : null;
} catch {
return null;
}
@ -121,9 +120,7 @@ export default class LimitlessLifelogsPlugin extends Plugin {
private formatLifelogMarkdown(lifelog: any): string {
if (lifelog.markdown) {
// Reformat Markdown
const reformattedMarkdown = lifelog.markdown.replaceAll('\n\n', '\n');
return reformattedMarkdown;
return lifelog.markdown;
}
const content: string[] = [];
@ -195,7 +192,7 @@ class LimitlessLifelogsSettingTab extends PluginSettingTab {
containerEl.empty();
new Setting(containerEl)
.setName('API key')
.setName('API Key')
.setDesc('Your Limitless AI API key')
.addText(text => text
.setPlaceholder('Enter your API key')
@ -206,7 +203,7 @@ class LimitlessLifelogsSettingTab extends PluginSettingTab {
}));
new Setting(containerEl)
.setName('Folder path')
.setName('Folder Path')
.setDesc('Where to store the lifelog entries')
.addText(text => text
.setPlaceholder('Folder path')
@ -217,7 +214,7 @@ class LimitlessLifelogsSettingTab extends PluginSettingTab {
}));
new Setting(containerEl)
.setName('Start date')
.setName('Start Date')
.setDesc('Default start date for initial sync (YYYY-MM-DD)')
.addText(text => text
.setPlaceholder('YYYY-MM-DD')
@ -233,8 +230,6 @@ class LimitlessAPI {
private apiKey: string;
private baseUrl = 'https://api.limitless.ai';
private batchSize = 10;
private maxRetries = 5;
private retryDelay = 1000; // 1 second
constructor(apiKey: string) {
this.apiKey = apiKey;
@ -263,7 +258,20 @@ class LimitlessAPI {
}
try {
const data = await this.makeRequest(`${this.baseUrl}/v1/lifelogs`, params);
const response = await requestUrl({
url: `${this.baseUrl}/v1/lifelogs?${params.toString()}`,
method: 'GET',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
}
});
if (!response.json) {
throw new Error('Invalid response format');
}
const data = response.json;
const lifelogs = data.data?.lifelogs || [];
allLifelogs.push(...lifelogs);
@ -276,49 +284,4 @@ class LimitlessAPI {
return allLifelogs;
}
private async makeRequest(url: string, params: URLSearchParams): Promise<any> {
let retries = 0;
while (true) {
try {
const response = await requestUrl({
url: `${url}?${params.toString()}`,
method: 'GET',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
}
});
if (!response.json) {
throw new Error('Invalid response format');
}
return response.json;
} catch (error) {
if (error.status === 429 && retries < this.maxRetries) {
let delay = this.retryDelay * Math.pow(2, retries);
const retryAfter = error.headers?.['retry-after'];
if (retryAfter) {
const retryAfterSeconds = parseInt(retryAfter, 10);
if (!isNaN(retryAfterSeconds)) {
delay = retryAfterSeconds * 1000;
} else {
const retryAfterDate = new Date(retryAfter);
const now = new Date();
delay = retryAfterDate.getTime() - now.getTime();
}
}
new Notice(`Rate limit exceeded. Retrying in ${Math.round(delay / 1000)} seconds...`);
await new Promise(resolve => setTimeout(resolve, delay));
retries++;
} else {
console.error('Error making request:', error);
throw error;
}
}
}
}
}

View file

@ -1,10 +1,10 @@
{
"id": "limitless-lifelogs",
"name": "Limitless Lifelogs",
"version": "1.0.6",
"version": "1.0.1",
"minAppVersion": "0.15.0",
"description": "Sync your Limitless AI lifelog entries",
"author": "Maclean Dunkin",
"description": "Download and sync your Limitless AI lifelog entries into Obsidian markdown files.",
"author": "Maclean Dunstan",
"authorUrl": "https://github.com/Maclean-D",
"isDesktopOnly": false
}
}

2397
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-limitless-lifelogs",
"version": "1.0.6",
"version": "1.0.0",
"description": "Download and sync your Limitless AI lifelog entries into Obsidian markdown files.",
"main": "main.js",
"scripts": {
@ -9,7 +9,7 @@
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": ["obsidian", "plugin", "limitless", "lifelogs"],
"author": "Maclean Dunkin",
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",

View file

8
styles.css Normal file
View file

@ -0,0 +1,8 @@
/*
This CSS file will be included with your plugin, and
available in the app when your plugin is enabled.
If your plugin does not need CSS, delete this file.
*/

View file

@ -1,5 +1,3 @@
{
"1.0.0": "0.15.0",
"1.0.5": "0.15.0",
"1.0.6": "0.15.0"
}
"1.0.0": "0.15.0"
}