open source is social v-it.org
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Use env-paths config directory for credentials and session storage

Migrate vit credential/session persistence out of process.cwd() and into env-paths config storage.\n\n- add env-paths dependency and new src/lib/paths.js helper\n- read/write .env via configPath() in src/lib/env.js\n- read/write bsky_session.json via configPath() in src/lib/oauth.js\n- ensure config dir exists before session writes in set/del\n- switch pds-record to createSessionStore().get(did) instead of direct file reads\n- update oauth success output to print actual saved .env path\n- update stale --did option text to reference saved credentials

+39 -25
+5
bun.lock
··· 11 11 "@noble/hashes": "^1.7.0", 12 12 "bs58": "^6.0.0", 13 13 "commander": "^13.0.0", 14 + "env-paths": "4.0.0", 14 15 }, 15 16 }, 16 17 }, ··· 79 80 80 81 "core-js": ["core-js@3.48.0", "", {}, "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ=="], 81 82 83 + "env-paths": ["env-paths@4.0.0", "", { "dependencies": { "is-safe-filename": "^0.1.0" } }, "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw=="], 84 + 82 85 "ipaddr.js": ["ipaddr.js@2.3.0", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="], 86 + 87 + "is-safe-filename": ["is-safe-filename@0.1.1", "", {}, "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g=="], 83 88 84 89 "iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA=="], 85 90
+2 -1
package.json
··· 18 18 "@noble/curves": "^1.8.0", 19 19 "@noble/hashes": "^1.7.0", 20 20 "bs58": "^6.0.0", 21 - "commander": "^13.0.0" 21 + "commander": "^13.0.0", 22 + "env-paths": "4.0.0" 22 23 } 23 24 }
+1 -1
src/cmd/firehose.js
··· 108 108 .command('firehose') 109 109 .description('Listen to Bluesky Jetstream firehose for custom record events') 110 110 .option('-v, --verbose', 'Show full JSON for each event') 111 - .option('--did <did>', 'Filter by DID (reads BSKY_DID from .env if not provided)') 111 + .option('--did <did>', 'Filter by DID (reads saved BSKY_DID if not provided)') 112 112 .option('--collection <nsid>', 'Collection NSID to filter', DEFAULT_COLLECTION) 113 113 .action(async (opts) => { 114 114 try {
+2 -1
src/cmd/oauth.js
··· 4 4 import { writeFileSync } from 'node:fs'; 5 5 import { saveToEnv } from '../lib/env.js'; 6 6 import { createOAuthClient, createSessionStore, createStore } from '../lib/oauth.js'; 7 + import { configPath } from '../lib/paths.js'; 7 8 8 9 export default function register(program) { 9 10 program ··· 143 144 BSKY_REFRESH_TOKEN: outputData.refreshToken ?? '', 144 145 BSKY_EXPIRES_AT: outputData.expiresAt ?? '', 145 146 }); 146 - console.log('Saved credentials to .env'); 147 + console.log(`Saved credentials to ${configPath('.env')}`); 147 148 } catch (err) { 148 149 console.error(err instanceof Error ? err.message : String(err)); 149 150 process.exitCode = 1;
+8 -14
src/cmd/pds-record.js
··· 2 2 // Copyright (c) 2026 sol pbc 3 3 4 4 import { Agent } from '@atproto/api'; 5 - import { readFileSync } from 'node:fs'; 6 - import { join } from 'node:path'; 7 5 import { loadEnv } from '../lib/env.js'; 8 6 import { createOAuthClient, createSessionStore } from '../lib/oauth.js'; 7 + import { configPath } from '../lib/paths.js'; 9 8 10 9 export default function register(program) { 11 10 program 12 11 .command('pds-record') 13 12 .description('Write and read a custom org.v-it.hello record on the authenticated PDS') 14 13 .option('-v, --verbose', 'Show full API responses') 15 - .option('--did <did>', 'DID to use (overrides .env)') 14 + .option('--did <did>', 'DID to use (overrides saved credentials)') 16 15 .option('--message <msg>', 'Message to write', 'hello world') 17 16 .action(async (opts) => { 18 17 try { ··· 23 22 throw new Error('No DID found. Run `vit oauth` first or pass --did <did>.'); 24 23 } 25 24 26 - let sessionData; 27 - const sessionFile = join(process.cwd(), 'bsky_session.json'); 28 - try { 29 - sessionData = JSON.parse(readFileSync(sessionFile, 'utf-8')); 30 - } catch { 31 - throw new Error('Session file not found. Run `vit oauth` first to authenticate.'); 32 - } 33 - 34 - if (!sessionData[did]) { 35 - throw new Error(`No session found for ${did}. Run \`vit oauth\` first to authenticate.`); 25 + const sessionStore = createSessionStore(); 26 + const sessionData = await sessionStore.get(did); 27 + if (!sessionData) { 28 + throw new Error( 29 + `No session found for ${did} in ${configPath('bsky_session.json')}. Run \`vit oauth\` first to authenticate.`, 30 + ); 36 31 } 37 32 38 33 if (opts.verbose) { 39 34 console.log(`[verbose] Restoring session for ${did}`); 40 35 } 41 36 42 - const sessionStore = createSessionStore(); 43 37 const client = createOAuthClient({ 44 38 stateStore: { 45 39 set: async () => {},
+1 -1
src/cmd/plc-verify.js
··· 8 8 .command('plc-verify') 9 9 .description('Verify PLC directory entry for saved Bluesky DID') 10 10 .option('-v, --verbose', 'Show full API responses') 11 - .option('--did <did>', 'DID to check (overrides .env)') 11 + .option('--did <did>', 'DID to check (overrides saved credentials)') 12 12 .action(async (opts) => { 13 13 try { 14 14 const env = loadEnv();
+5 -4
src/lib/env.js
··· 1 1 // SPDX-License-Identifier: AGPL-3.0-only 2 2 // Copyright (c) 2026 sol pbc 3 3 4 - import { readFileSync, writeFileSync, existsSync } from 'node:fs'; 5 - import { join } from 'node:path'; 4 + import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs'; 5 + import { configDir, configPath } from './paths.js'; 6 6 7 7 export function loadEnv() { 8 - const envPath = join(process.cwd(), '.env'); 8 + const envPath = configPath('.env'); 9 9 const vars = {}; 10 10 let content; 11 11 if (!existsSync(envPath)) { ··· 24 24 } 25 25 26 26 export function saveToEnv(vars) { 27 - const envPath = join(process.cwd(), '.env'); 27 + const envPath = configPath('.env'); 28 28 let lines = []; 29 29 if (existsSync(envPath)) { 30 30 try { ··· 45 45 lines.push(`${key}=${value}`); 46 46 } 47 47 } 48 + mkdirSync(configDir, { recursive: true }); 48 49 writeFileSync(envPath, lines.join('\n') + '\n'); 49 50 }
+5 -3
src/lib/oauth.js
··· 2 2 // Copyright (c) 2026 sol pbc 3 3 4 4 import { NodeOAuthClient } from '@atproto/oauth-client-node'; 5 - import { readFileSync, writeFileSync } from 'node:fs'; 6 - import { join } from 'node:path'; 5 + import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; 6 + import { configDir, configPath } from './paths.js'; 7 7 8 8 export const requestLock = async (_name, fn) => await fn(); 9 9 ··· 22 22 } 23 23 24 24 export function createSessionStore() { 25 - const sessionFile = join(process.cwd(), 'bsky_session.json'); 25 + const sessionFile = configPath('bsky_session.json'); 26 26 let data = {}; 27 27 try { 28 28 data = JSON.parse(readFileSync(sessionFile, 'utf-8')); ··· 30 30 return { 31 31 set: async (key, value) => { 32 32 data[key] = value; 33 + mkdirSync(configDir, { recursive: true }); 33 34 writeFileSync(sessionFile, JSON.stringify(data, null, 2) + '\n'); 34 35 }, 35 36 get: async (key) => data[key], 36 37 del: async (key) => { 37 38 delete data[key]; 39 + mkdirSync(configDir, { recursive: true }); 38 40 writeFileSync(sessionFile, JSON.stringify(data, null, 2) + '\n'); 39 41 }, 40 42 };
+10
src/lib/paths.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + import envPaths from 'env-paths'; 5 + import { join } from 'node:path'; 6 + 7 + const paths = envPaths('vit'); 8 + 9 + export const configDir = paths.config; 10 + export const configPath = (filename) => join(paths.config, filename);