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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sheetlink",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "CLI for SheetLink — sync your bank transactions to any destination",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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.error(`Wrote ${transactions.length} transactions to ${filePath}`);
44
+ console.log(`Wrote ${transactions.length} transactions to ${filePath}`);
45
45
  }
@@ -91,7 +91,7 @@ export async function writePostgres(transactions, accounts, connectionString) {
91
91
  ]);
92
92
  }
93
93
 
94
- console.error(`Synced ${transactions.length} transactions and ${accounts.length} accounts to Postgres`);
94
+ console.log(`Synced ${transactions.length} transactions and ${accounts.length} accounts to Postgres`);
95
95
  } finally {
96
96
  await client.end();
97
97
  }
@@ -91,5 +91,5 @@ export async function writeSQLite(transactions, accounts, dbPath) {
91
91
  accBatch(accounts);
92
92
  db.close();
93
93
 
94
- console.error(`Synced ${transactions.length} transactions and ${accounts.length} accounts to ${dbPath}`);
94
+ console.log(`Synced ${transactions.length} transactions and ${accounts.length} accounts to ${dbPath}`);
95
95
  }
@@ -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
- writeConfig({ api_key: options.apiKey, jwt: null });
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', reject);
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
  });
@@ -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
- process.stderr.write(`Syncing ${id}...`);
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
- process.stderr.write(` ${result.transactions?.length ?? 0} transactions\n`);
54
+ clearInterval(spinner);
55
+ process.stderr.write(`\r✓ Synced ${id} — ${result.transactions?.length ?? 0} transactions\n`);
50
56
  } catch (e) {
51
- process.stderr.write(` error: ${e.message}\n`);
57
+ clearInterval(spinner);
58
+ process.stderr.write(`\r✗ ${id} — ${e.message}\n`);
52
59
  }
53
60
  }
54
61