linkedin-import.mjs
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env node
import { mkdirSync, readFileSync } from 'fs';
import { dirname, join } from 'path';
import { flagValue, hasFlag, validateFlags } from './lib/cli-flags.mjs';
import { isMainModule } from './lib/is-main-module.mjs';
import { linkedInOffers, planLinkedInImport } from './lib/linkedin-import.mjs';
import { getCareerOpsRoot, resolveTrackerPath } from './path-resolver.mjs';
const USAGE = `Paste a LinkedIn job alert into your pipeline. No Google account.
node linkedin-import.mjs Read the alert from stdin
node linkedin-import.mjs --file alert.eml
node linkedin-import.mjs --dry-run Print the count and write nothing
node linkedin-import.mjs --json Print one JSON object
On the dashboard, paste into LinkedIn job alerts.
`;
function readIf(path) {
try {
return readFileSync(path, 'utf8');
} catch (err) {
if (err?.code === 'ENOENT') return '';
throw new Error(`I can't read ${path} (${err.message}). Nothing was saved.`);
}
}
async function readStdin() {
if (process.stdin.isTTY) return null;
const chunks = [];
for await (const chunk of process.stdin) chunks.push(chunk);
return Buffer.concat(chunks).toString('utf8');
}
async function main() {
const args = process.argv.slice(2);
validateFlags(args, ['--file', '--dry-run', '--json', '--help', '-h'], USAGE, {
valueFlags: ['--file'],
requireOperand: true,
});
const file = flagValue(args, '--file');
const dryRun = hasFlag(args, '--dry-run');
const json = hasFlag(args, '--json');
let text;
if (file !== undefined) {
try {
text = readFileSync(file, 'utf8');
} catch (err) {
console.error(`I can't read that file (${err.message}).`);
process.exit(1);
}
} else {
text = await readStdin();
if (text === null) {
console.log(USAGE);
process.exit(0);
}
}
const root = getCareerOpsRoot();
const pipelinePath = process.env.CAREER_OPS_PIPELINE?.trim() || join(root, 'data', 'pipeline.md');
const applicationsPath = resolveTrackerPath(root);
const plan = planLinkedInImport(text, `${readIf(pipelinePath)}\n${readIf(applicationsPath)}`, { dryRun });
let historyError = null;
if (!dryRun && !plan.error && plan.added.length > 0) {
const { appendToPipeline, appendToScanHistory } = await import('./scan.mjs');
const { localToday } = await import('./lib/local-today.mjs');
const offers = linkedInOffers(plan.added, text);
mkdirSync(dirname(pipelinePath), { recursive: true });
await appendToPipeline(offers, { pipelinePath });
try {
await appendToScanHistory(offers, localToday(), 'added');
} catch (err) {
historyError = err instanceof Error ? err.message : String(err);
}
}
const payload = {
added: plan.added.length,
already: plan.already.length,
found: plan.found,
message: historyError ? `${plan.message} The scan history could not be updated.` : plan.message,
...(plan.error ? { error: plan.error } : {}),
...(historyError ? { historyError } : {}),
};
if (json) console.log(JSON.stringify(payload));
else console.log(payload.message);
if (plan.error) process.exit(1);
}
if (isMainModule(import.meta.url)) {
main().catch((err) => {
console.error(err instanceof Error ? err.message : String(err));
process.exit(1);
});
}