chore: remove PM2 — system cron + lockfile is sufficient, no daemon needed

This commit is contained in:
2026-03-06 02:57:49 +00:00
parent 739bb1c99c
commit 7de05fdce4
2 changed files with 44 additions and 31 deletions

View File

@@ -1,31 +0,0 @@
/**
* PM2 ecosystem config for claw-apply
* Start: pm2 start ecosystem.config.cjs
* Save: pm2 save
* Boot: pm2 startup (follow printed command)
*/
module.exports = {
apps: [
{
// Searcher is triggered by system cron, not PM2 cron_restart.
// PM2 just manages the process — system cron runs: node job_searcher.mjs
// Lockfile prevents parallel runs: if already running, new invocation exits immediately.
name: 'claw-searcher',
script: 'job_searcher.mjs',
autorestart: false, // one-shot — do not restart on exit
watch: false,
interpreter: 'node',
log_file: '/tmp/claw-searcher.log',
time: true,
},
{
name: 'claw-applier',
script: 'job_applier.mjs',
autorestart: false,
watch: false,
interpreter: 'node',
log_file: '/tmp/claw-applier.log',
time: true,
},
],
};

44
test_location_scrape.mjs Normal file
View File

@@ -0,0 +1,44 @@
import { loadEnv } from './lib/env.mjs';
loadEnv();
import { createBrowser } from './lib/browser.mjs';
import { loadConfig } from './lib/queue.mjs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dir = dirname(fileURLToPath(import.meta.url));
const settings = loadConfig(resolve(__dir, 'config/settings.json'));
const b = await createBrowser(settings, 'linkedin');
await b.page.goto('https://www.linkedin.com/jobs/search/?keywords=founding+account+executive&f_WT=2&sortBy=DD', { waitUntil: 'domcontentloaded' });
await b.page.waitForTimeout(3000);
// Click first job card
const card = await b.page.$('li.jobs-search-results__list-item');
if (card) await card.click();
await b.page.waitForTimeout(2000);
const result = await b.page.evaluate(() => {
// Try various location selectors
const selectors = [
'.job-details-jobs-unified-top-card__bullet',
'.jobs-unified-top-card__bullet',
'.jobs-unified-top-card__workplace-type',
'[class*="workplace-type"]',
'[class*="location"]',
'.tvm__text',
'.job-details-jobs-unified-top-card__job-insight span',
'span[class*="topcard__flavor"]',
];
const results = {};
for (const sel of selectors) {
const els = Array.from(document.querySelectorAll(sel));
if (els.length) results[sel] = els.map(e => e.textContent.trim()).filter(Boolean);
}
// Also dump all text in the top card area
const topCard = document.querySelector('.jobs-details__main-content, .scaffold-layout__detail');
results['_topCardText'] = topCard?.textContent?.trim().slice(0, 500) || 'not found';
return results;
});
console.log(JSON.stringify(result, null, 2));
await b.browser.close();