@timurtekb/tekjobs 0.35.1 → 0.35.2
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/app/dist/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="icon" href="/brand/tekjobs-avatar-192.png" type="image/png" sizes="192x192" />
|
|
9
9
|
<link rel="apple-touch-icon" href="/brand/tekjobs-avatar-192.png" />
|
|
10
10
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@500;600&family=Hanken+Grotesk:wght@400;500;600&family=Azeret+Mono:wght@400;500&display=swap" data-zengin="fonts" />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-BTDWWylf.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-CR3ppGJT.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
@@ -235,7 +235,9 @@ export function classifyRun({ code, out = '', err = '', command = 'claude' }) {
|
|
|
235
235
|
}
|
|
236
236
|
if (code === 0 && out.trim()) return null;
|
|
237
237
|
const signedOut = /not logged in|please run \/login|run `?\/login|failed to authenticate|authentication[_ ]error|invalid[_ ]api[_ ]key|token (has )?expired|(^|\W)unauthorized(\W|$)|\b(http|status|error)\s*:?\s*401\b/i;
|
|
238
|
-
|
|
238
|
+
// Claude Code prints "Not logged in · Please run /login" on stdout with a non-zero exit, so a failed run's
|
|
239
|
+
// stdout counts too; a successful run's stdout never does, whatever words it holds.
|
|
240
|
+
if (signedOut.test(err) || (code !== 0 && signedOut.test(out))) {
|
|
239
241
|
return Object.assign(new Error(`${command} is signed out. Open a terminal, run "${command}", sign in, then try again. (The desktop app's login is separate from the terminal's.) It said: ${tail || 'nothing'}`), { kind: 'auth' });
|
|
240
242
|
}
|
|
241
243
|
if (/requires a newer version|please upgrade/i.test(`${out}\n${err}`)) {
|
|
@@ -255,7 +257,7 @@ export function start(id, opts = {}) {
|
|
|
255
257
|
if (cur?.running) return state(id);
|
|
256
258
|
store.getJob(id); // throws early, and synchronously, if there is no such job
|
|
257
259
|
drafts.set(id, { running: true, startedAt: new Date().toISOString(), finishedAt: null, error: '', errorKind: '' });
|
|
258
|
-
materials(id, opts).then((m) => runLLM(m.prompt))
|
|
260
|
+
materials(id, opts).then((m) => runLLM(m.prompt)).then((text) => { if (String(text).trim().length < 80) throw Object.assign(new Error(`The writing CLI answered with "${String(text).trim().slice(0, 140)}" instead of a letter. If that is a sign-in or permission message, fix it in a terminal and try again.`), { kind: 'failed' }); return text; })
|
|
259
261
|
.then((text) => { save(id, text, `app · ${runnerConfig().command}`); drafts.set(id, { ...drafts.get(id), running: false, finishedAt: new Date().toISOString() }); })
|
|
260
262
|
.catch((e) => drafts.set(id, { ...drafts.get(id), running: false, finishedAt: new Date().toISOString(), error: e.message, errorKind: e.kind || 'failed' }));
|
|
261
263
|
return state(id);
|
package/app/server/index.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import * as tailored from './tailored-resume.mjs';
|
|
|
10
10
|
import * as mail from './mail-check.mjs';
|
|
11
11
|
import * as people from './people.mjs';
|
|
12
12
|
import * as linkedin from './linkedin-import.mjs';
|
|
13
|
+
import { TOOLS } from './mcp.mjs';
|
|
13
14
|
|
|
14
15
|
const PORT = Number(process.env.PORT || 8787);
|
|
15
16
|
const DIST = fileURLToPath(new URL('../dist', import.meta.url));
|
|
@@ -20,6 +21,8 @@ const readBody = (req) => new Promise((resolve, reject) => { let s = ''; req.on(
|
|
|
20
21
|
|
|
21
22
|
const routes = [
|
|
22
23
|
['GET', /^\/api\/summary$/, () => store.summary()],
|
|
24
|
+
// The MCP tools the server offers, for the Agent access page; importing mcp.mjs does not start its stdio loop.
|
|
25
|
+
['GET', /^\/api\/tools$/, () => TOOLS.map((t) => ({ name: t.name, description: t.description }))],
|
|
23
26
|
['GET', /^\/api\/today$/, (_, q) => store.today({ cap: Number(q.get('cap') || 7), waitingDays: Number(q.get('waitingDays') || 14) })],
|
|
24
27
|
['GET', /^\/api\/jobs$/, (_, q) => store.searchJobs({ ...store.filtersFromParams(q), sort: q.get('sort') || 'score', dir: q.get('dir') || 'desc', limit: Number(q.get('limit') || 500), offset: Number(q.get('offset') || 0) })],
|
|
25
28
|
['GET', /^\/api\/jobs\/facets$/, (_, q) => store.jobFacets(store.filtersFromParams(q))],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timurtekb/tekjobs",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.2",
|
|
4
4
|
"description": "A local-first job-search machine: watches hundreds of company job boards, scores every posting against your profile, and files matches into a folder of markdown notes you own. Bring your own LLM through MCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/scraper/profile.mjs
CHANGED
|
@@ -54,7 +54,7 @@ export function onboardingStatus(dir = VAULT) {
|
|
|
54
54
|
const steps = [
|
|
55
55
|
{ id: 'folder', label: 'Profile folder exists', done: exists('Targets/Search Criteria.md') && exists('Targets/Companies.md'), how: 'tekjobs init [dir]' },
|
|
56
56
|
{ id: 'resume', label: 'Resume imported', done: exists('Profile/Resume - Source.md') || (exists('Profile') && fs.readdirSync(path.join(dir, 'Profile')).some((f) => /^Resume.*\.md$/i.test(f))), how: 'tekjobs resume <file.pdf|docx|md>' },
|
|
57
|
-
{ id: 'profile', label: 'Profile written by the interview', done: profileFilled, how: 'Run the interview
|
|
57
|
+
{ id: 'profile', label: 'Profile written by the interview', done: profileFilled, how: 'Run the interview. Connect the server once (Claude Code: claude mcp add tekjobs -- tekjobs mcp; other clients: the command tekjobs mcp), then say: Use the tekjobs MCP server, call onboarding_status, then onboarding_materials, and follow its script.' },
|
|
58
58
|
{ id: 'criteria', label: 'Search criteria filled (title terms set)', done: criteriaFilled, how: 'The interview writes them; or edit Targets/Search Criteria.md.' },
|
|
59
59
|
{ id: 'scan', label: 'First scan has run', done: jobs > 0, how: 'tekjobs scan, or the Runs screen.' },
|
|
60
60
|
];
|