Make loadConfig async and route through storage layer (S3 or disk)
- loadConfig now uses loadJSON when storage is initialized - Fix getS3Key to handle config/ and data/ paths (not just data/) - All loadConfig calls updated to await - settings.json still bootstraps from disk (needed to know storage type) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,7 +45,7 @@ async function main() {
|
||||
|
||||
const settings = loadConfig(resolve(__dir, 'config/settings.json'));
|
||||
await initQueue(settings);
|
||||
const profile = loadConfig(resolve(__dir, 'config/profile.json'));
|
||||
const profile = await loadConfig(resolve(__dir, 'config/profile.json'));
|
||||
|
||||
// Ensure resume is available locally (downloads from S3 if needed)
|
||||
if (profile.resume_path) {
|
||||
@@ -53,7 +53,7 @@ async function main() {
|
||||
}
|
||||
|
||||
const answersPath = resolve(__dir, 'config/answers.json');
|
||||
const answers = existsSync(answersPath) ? loadConfig(answersPath) : [];
|
||||
const answers = await loadConfig(answersPath).catch(() => []);
|
||||
const maxApps = settings.max_applications_per_run || Infinity;
|
||||
const maxRetries = settings.max_retries ?? DEFAULT_MAX_RETRIES;
|
||||
const enabledTypes = settings.enabled_apply_types || DEFAULT_ENABLED_APPLY_TYPES;
|
||||
@@ -218,7 +218,7 @@ async function main() {
|
||||
|
||||
// Reload answers.json before each job — picks up Telegram replies between jobs
|
||||
try {
|
||||
const freshAnswers = existsSync(answersPath) ? loadConfig(answersPath) : [];
|
||||
const freshAnswers = await loadConfig(answersPath).catch(() => []);
|
||||
formFiller.answers = freshAnswers;
|
||||
} catch { /* keep existing answers on read error */ }
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ async function collect(state, settings) {
|
||||
for (const [k, v] of Object.entries(usage)) totalUsage[k] = (totalUsage[k] || 0) + v;
|
||||
}
|
||||
|
||||
const searchConfig = loadConfig(resolve(__dir, 'config/search_config.json'));
|
||||
const searchConfig = await loadConfig(resolve(__dir, 'config/search_config.json'));
|
||||
const globalMin = searchConfig.filter_min_score ?? DEFAULT_FILTER_MIN_SCORE;
|
||||
|
||||
let passed = 0, filtered = 0, errors = 0;
|
||||
@@ -306,8 +306,8 @@ async function main() {
|
||||
|
||||
const settings = loadConfig(resolve(__dir, 'config/settings.json'));
|
||||
await initQueue(settings);
|
||||
const searchConfig = loadConfig(resolve(__dir, 'config/search_config.json'));
|
||||
const candidateProfile = loadConfig(resolve(__dir, 'config/profile.json'));
|
||||
const searchConfig = await loadConfig(resolve(__dir, 'config/search_config.json'));
|
||||
const candidateProfile = await loadConfig(resolve(__dir, 'config/profile.json'));
|
||||
|
||||
console.log('🔍 claw-apply: AI Job Filter\n');
|
||||
|
||||
|
||||
@@ -96,10 +96,10 @@ async function main() {
|
||||
});
|
||||
|
||||
// Load config
|
||||
const searchConfig = loadConfig(resolve(__dir, 'config/search_config.json'));
|
||||
const searchConfig = await loadConfig(resolve(__dir, 'config/search_config.json'));
|
||||
|
||||
// First run detection: if queue is empty, use first_run_days lookback
|
||||
const profile = loadConfig(resolve(__dir, 'config/profile.json'));
|
||||
const profile = await loadConfig(resolve(__dir, 'config/profile.json'));
|
||||
const anthropicKey = process.env.ANTHROPIC_API_KEY || settings.anthropic_api_key;
|
||||
|
||||
// Determine lookback: check for an in-progress run first, then fall back to first-run/normal logic
|
||||
|
||||
@@ -20,10 +20,23 @@ const LOG_PATH = `${__dir}/../data/applications_log.json`;
|
||||
const UPDATES_PATH = `${__dir}/../data/queue_updates.jsonl`;
|
||||
|
||||
/**
|
||||
* Load and validate a JSON config file. Throws with a clear message on failure.
|
||||
* Load and validate a JSON config file.
|
||||
* Uses the storage layer (S3 or disk) when initialized.
|
||||
* Falls back to direct disk read for bootstrap (settings.json loaded before initQueue).
|
||||
*/
|
||||
export function loadConfig(filePath) {
|
||||
export async function loadConfig(filePath) {
|
||||
const resolved = resolve(filePath);
|
||||
|
||||
// If storage is initialized, use the storage layer
|
||||
if (_initialized) {
|
||||
const data = await loadJSON(resolved, null);
|
||||
if (data === null) {
|
||||
throw new Error(`Config file not found: ${resolved}\nCopy the matching .example.json and fill in your values.`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// Bootstrap fallback (settings.json loaded before initQueue)
|
||||
if (!existsSync(resolved)) {
|
||||
throw new Error(`Config file not found: ${resolved}\nCopy the matching .example.json and fill in your values.`);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,16 @@ export function storageType() {
|
||||
}
|
||||
|
||||
function getS3Key(filePath) {
|
||||
return `data/${basename(filePath)}`;
|
||||
// Extract relative path from project root (e.g. config/foo.json or data/bar.json)
|
||||
const projectRoot = dirname(dirname(import.meta.url.replace('file://', '')));
|
||||
const abs = filePath.startsWith('/') ? filePath : join(projectRoot, filePath);
|
||||
if (abs.startsWith(projectRoot)) {
|
||||
const rel = abs.slice(projectRoot.length + 1);
|
||||
return rel;
|
||||
}
|
||||
// Fallback: use last two path segments (e.g. data/jobs_queue.json)
|
||||
const parts = filePath.split('/');
|
||||
return parts.slice(-2).join('/');
|
||||
}
|
||||
|
||||
async function getS3Client() {
|
||||
@@ -60,6 +69,7 @@ export async function loadJSON(filePath, defaultValue = []) {
|
||||
return parsed;
|
||||
} catch (err) {
|
||||
if (err.name === 'NoSuchKey') return defaultValue;
|
||||
if (err.$metadata?.httpStatusCode === 404) return defaultValue;
|
||||
console.warn(`⚠️ S3 load failed for ${basename(filePath)}: ${err.message}`);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export async function processTelegramReplies(settings, answersPath) {
|
||||
const updates = await getTelegramUpdates(botToken, offset, 1);
|
||||
if (updates.length === 0) return 0;
|
||||
|
||||
// Build lookup: telegram_message_id → job
|
||||
// Build lookup: telegram_message_id → job (loadQueue returns cached in-memory queue)
|
||||
const queue = loadQueue();
|
||||
const jobsByMsgId = new Map();
|
||||
for (const job of queue) {
|
||||
|
||||
Reference in New Issue
Block a user