sheetlink 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/package.json +1 -1
- package/src/adapters/csv.js +1 -1
- package/src/adapters/postgres.js +1 -1
- package/src/adapters/sqlite.js +1 -1
- package/src/commands/auth.js +11 -6
- package/src/commands/sync.js +10 -3
package/README.md
CHANGED
|
@@ -16,23 +16,27 @@ Requires a [SheetLink](https://sheetlink.app) account on the **PRO** or **MAX**
|
|
|
16
16
|
Authenticate with SheetLink.
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
sheetlink auth # OAuth login (PRO)
|
|
19
|
+
sheetlink auth # OAuth login (PRO) — opens browser, JWT valid for ~1 hour
|
|
20
20
|
sheetlink auth --api-key sl_... # API key (MAX — for automation)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
> **Security note:** Avoid passing `--api-key` directly in commands — it may appear in your shell history. Use the `SHEETLINK_API_KEY` environment variable instead.
|
|
24
|
+
|
|
23
25
|
### `sheetlink sync`
|
|
24
26
|
Sync transactions from all connected banks.
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
29
|
sheetlink sync # JSON to stdout (default)
|
|
28
30
|
sheetlink sync | jq '.items[].transactions | length' # Pipe to jq
|
|
29
|
-
sheetlink sync --output csv # CSV snapshot
|
|
31
|
+
sheetlink sync --output csv # CSV snapshot (overwrites each run)
|
|
30
32
|
sheetlink sync --output csv --file ~/finances.csv # CSV to custom path
|
|
31
33
|
sheetlink sync --output postgres://localhost/mydb # Upsert to Postgres (MAX)
|
|
32
34
|
sheetlink sync --output sqlite:///~/finance.db # Upsert to SQLite (MAX)
|
|
33
35
|
sheetlink sync --item <item_id> # One bank only
|
|
34
36
|
```
|
|
35
37
|
|
|
38
|
+
> **Note:** CSV output overwrites the file on every run. For an append/dedup history, use Postgres or SQLite.
|
|
39
|
+
|
|
36
40
|
### `sheetlink items`
|
|
37
41
|
List connected bank accounts.
|
|
38
42
|
|
package/package.json
CHANGED
package/src/adapters/csv.js
CHANGED
|
@@ -41,5 +41,5 @@ function formatRow(txn) {
|
|
|
41
41
|
export function writeCsv(transactions, filePath = './sheetlink-transactions.csv') {
|
|
42
42
|
const lines = [HEADERS.join(','), ...transactions.map(formatRow)];
|
|
43
43
|
fs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf8');
|
|
44
|
-
console.
|
|
44
|
+
console.log(`Wrote ${transactions.length} transactions to ${filePath}`);
|
|
45
45
|
}
|
package/src/adapters/postgres.js
CHANGED
|
@@ -91,7 +91,7 @@ export async function writePostgres(transactions, accounts, connectionString) {
|
|
|
91
91
|
]);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
console.
|
|
94
|
+
console.log(`Synced ${transactions.length} transactions and ${accounts.length} accounts to Postgres`);
|
|
95
95
|
} finally {
|
|
96
96
|
await client.end();
|
|
97
97
|
}
|
package/src/adapters/sqlite.js
CHANGED
|
@@ -91,5 +91,5 @@ export async function writeSQLite(transactions, accounts, dbPath) {
|
|
|
91
91
|
accBatch(accounts);
|
|
92
92
|
db.close();
|
|
93
93
|
|
|
94
|
-
console.
|
|
94
|
+
console.log(`Synced ${transactions.length} transactions and ${accounts.length} accounts to ${dbPath}`);
|
|
95
95
|
}
|
package/src/commands/auth.js
CHANGED
|
@@ -38,13 +38,12 @@ export async function cmdAuth(options) {
|
|
|
38
38
|
console.error(' export SHEETLINK_API_KEY=sl_...');
|
|
39
39
|
console.error('');
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
console.log('API key saved to ~/.sheetlink/config.json');
|
|
43
|
-
console.log('');
|
|
44
|
-
|
|
45
|
-
// Verify it works by hitting an API-key-aware endpoint
|
|
41
|
+
// Verify before saving
|
|
46
42
|
try {
|
|
47
43
|
const { items } = await listItems();
|
|
44
|
+
writeConfig({ api_key: options.apiKey, jwt: null });
|
|
45
|
+
console.log('API key saved to ~/.sheetlink/config.json');
|
|
46
|
+
console.log('');
|
|
48
47
|
console.log(`Authenticated. ${items.length} bank${items.length !== 1 ? 's' : ''} connected.`);
|
|
49
48
|
} catch (e) {
|
|
50
49
|
console.error(`Could not verify key: ${e.message}`);
|
|
@@ -119,7 +118,13 @@ async function googleOAuthFlow() {
|
|
|
119
118
|
}
|
|
120
119
|
});
|
|
121
120
|
|
|
122
|
-
server.on('error',
|
|
121
|
+
server.on('error', (err) => {
|
|
122
|
+
if (err.code === 'EADDRINUSE') {
|
|
123
|
+
reject(new Error(`Port ${REDIRECT_PORT} is already in use. Close any other process using it and try again.`));
|
|
124
|
+
} else {
|
|
125
|
+
reject(err);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
123
128
|
const timeout = setTimeout(() => { server.closeAllConnections?.(); server.close(); reject(new Error('OAuth timeout (2 minutes)')); }, 120_000);
|
|
124
129
|
server.on('close', () => clearTimeout(timeout));
|
|
125
130
|
});
|
package/src/commands/sync.js
CHANGED
|
@@ -39,16 +39,23 @@ export async function cmdSync(options) {
|
|
|
39
39
|
const allAccounts = [];
|
|
40
40
|
const results = [];
|
|
41
41
|
|
|
42
|
+
const spinnerFrames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
43
|
+
|
|
42
44
|
for (const id of itemIds) {
|
|
43
|
-
|
|
45
|
+
let i = 0;
|
|
46
|
+
const spinner = setInterval(() => {
|
|
47
|
+
process.stderr.write(`\r${spinnerFrames[i++ % spinnerFrames.length]} Syncing ${id}...`);
|
|
48
|
+
}, 80);
|
|
44
49
|
try {
|
|
45
50
|
const result = await syncItem(id);
|
|
46
51
|
allTransactions.push(...(result.transactions || []));
|
|
47
52
|
allAccounts.push(...(result.accounts || []));
|
|
48
53
|
results.push({ item_id: id, ...result });
|
|
49
|
-
|
|
54
|
+
clearInterval(spinner);
|
|
55
|
+
process.stderr.write(`\r✓ Synced ${id} — ${result.transactions?.length ?? 0} transactions\n`);
|
|
50
56
|
} catch (e) {
|
|
51
|
-
|
|
57
|
+
clearInterval(spinner);
|
|
58
|
+
process.stderr.write(`\r✗ ${id} — ${e.message}\n`);
|
|
52
59
|
}
|
|
53
60
|
}
|
|
54
61
|
|