jobseek-cli 1.0.0

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.
Files changed (49) hide show
  1. package/dist/commands/applications.d.ts +2 -0
  2. package/dist/commands/applications.js +198 -0
  3. package/dist/commands/applications.js.map +1 -0
  4. package/dist/commands/auth.d.ts +2 -0
  5. package/dist/commands/auth.js +315 -0
  6. package/dist/commands/auth.js.map +1 -0
  7. package/dist/commands/claims.d.ts +2 -0
  8. package/dist/commands/claims.js +355 -0
  9. package/dist/commands/claims.js.map +1 -0
  10. package/dist/commands/jobs.d.ts +2 -0
  11. package/dist/commands/jobs.js +240 -0
  12. package/dist/commands/jobs.js.map +1 -0
  13. package/dist/commands/profile.d.ts +2 -0
  14. package/dist/commands/profile.js +116 -0
  15. package/dist/commands/profile.js.map +1 -0
  16. package/dist/commands/question.d.ts +2 -0
  17. package/dist/commands/question.js +54 -0
  18. package/dist/commands/question.js.map +1 -0
  19. package/dist/commands/resume.d.ts +2 -0
  20. package/dist/commands/resume.js +209 -0
  21. package/dist/commands/resume.js.map +1 -0
  22. package/dist/commands/search.d.ts +2 -0
  23. package/dist/commands/search.js +192 -0
  24. package/dist/commands/search.js.map +1 -0
  25. package/dist/commands/ssh-key.d.ts +2 -0
  26. package/dist/commands/ssh-key.js +151 -0
  27. package/dist/commands/ssh-key.js.map +1 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +72 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/lib/api-client.d.ts +16 -0
  32. package/dist/lib/api-client.js +59 -0
  33. package/dist/lib/api-client.js.map +1 -0
  34. package/dist/lib/config.d.ts +19 -0
  35. package/dist/lib/config.js +56 -0
  36. package/dist/lib/config.js.map +1 -0
  37. package/dist/lib/errors.d.ts +11 -0
  38. package/dist/lib/errors.js +23 -0
  39. package/dist/lib/errors.js.map +1 -0
  40. package/dist/lib/oauth.d.ts +41 -0
  41. package/dist/lib/oauth.js +210 -0
  42. package/dist/lib/oauth.js.map +1 -0
  43. package/dist/lib/output.d.ts +20 -0
  44. package/dist/lib/output.js +109 -0
  45. package/dist/lib/output.js.map +1 -0
  46. package/dist/lib/ssh.d.ts +25 -0
  47. package/dist/lib/ssh.js +103 -0
  48. package/dist/lib/ssh.js.map +1 -0
  49. package/package.json +52 -0
@@ -0,0 +1,210 @@
1
+ import { createServer } from 'node:http';
2
+ import { randomBytes, subtle } from 'node:crypto';
3
+ /**
4
+ * Generate a PKCE code_verifier and code_challenge (S256).
5
+ */
6
+ export async function generatePkce() {
7
+ const codeVerifier = randomBytes(32).toString('base64url');
8
+ const data = new TextEncoder().encode(codeVerifier);
9
+ const digest = await subtle.digest('SHA-256', data);
10
+ const codeChallenge = Buffer.from(digest).toString('base64url');
11
+ return { codeVerifier, codeChallenge };
12
+ }
13
+ /**
14
+ * Generate a random state parameter for CSRF protection.
15
+ */
16
+ export function generateState() {
17
+ return randomBytes(16).toString('hex');
18
+ }
19
+ /**
20
+ * Start a temporary local HTTP server to receive the OAuth callback.
21
+ * Returns a promise that resolves with the authorization code and state.
22
+ * The server shuts down automatically after receiving the callback.
23
+ */
24
+ export function startCallbackServer(port) {
25
+ return new Promise((resolve, reject) => {
26
+ let settled = false;
27
+ const timeout = setTimeout(() => {
28
+ if (!settled) {
29
+ settled = true;
30
+ destroyServer();
31
+ reject(new Error('Timed out waiting for authentication callback'));
32
+ }
33
+ }, 5 * 60 * 1000);
34
+ timeout.unref(); // Don't keep process alive just for the timeout
35
+ const sockets = new Set();
36
+ const server = createServer((req, res) => {
37
+ const url = new URL(req.url || '/', `http://localhost:${port}`);
38
+ if (url.pathname !== '/callback') {
39
+ res.writeHead(404);
40
+ res.end('Not found');
41
+ return;
42
+ }
43
+ const code = url.searchParams.get('code');
44
+ const state = url.searchParams.get('state');
45
+ const error = url.searchParams.get('error');
46
+ if (error) {
47
+ const description = url.searchParams.get('error_description') || error;
48
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
49
+ res.end(callbackHtml('Authentication Failed', `Error: ${description}`, false));
50
+ clearTimeout(timeout);
51
+ destroyServer();
52
+ if (!settled) {
53
+ settled = true;
54
+ reject(new Error(`OAuth error: ${description}`));
55
+ }
56
+ return;
57
+ }
58
+ if (!code || !state) {
59
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
60
+ res.end(callbackHtml('Authentication Failed', 'Missing authorization code or state.', false));
61
+ clearTimeout(timeout);
62
+ destroyServer();
63
+ if (!settled) {
64
+ settled = true;
65
+ reject(new Error('Missing code or state in callback'));
66
+ }
67
+ return;
68
+ }
69
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
70
+ res.end(callbackHtml('Authentication Successful', 'You can close this tab and return to the terminal.', true));
71
+ clearTimeout(timeout);
72
+ destroyServer();
73
+ if (!settled) {
74
+ settled = true;
75
+ resolve({ code, state });
76
+ }
77
+ });
78
+ server.on('connection', (socket) => {
79
+ sockets.add(socket);
80
+ socket.on('close', () => sockets.delete(socket));
81
+ });
82
+ server.on('error', (err) => {
83
+ clearTimeout(timeout);
84
+ if (!settled) {
85
+ settled = true;
86
+ reject(err);
87
+ }
88
+ });
89
+ server.listen(port, '127.0.0.1');
90
+ function destroyServer() {
91
+ server.close();
92
+ for (const socket of sockets) {
93
+ socket.destroy();
94
+ }
95
+ }
96
+ });
97
+ }
98
+ /**
99
+ * Find an available port for the callback server.
100
+ */
101
+ export async function findAvailablePort() {
102
+ return new Promise((resolve, reject) => {
103
+ const server = createServer();
104
+ server.listen(0, '127.0.0.1', () => {
105
+ const address = server.address();
106
+ if (address && typeof address === 'object') {
107
+ const port = address.port;
108
+ server.close(() => resolve(port));
109
+ }
110
+ else {
111
+ server.close(() => reject(new Error('Could not find available port')));
112
+ }
113
+ });
114
+ server.on('error', reject);
115
+ });
116
+ }
117
+ /**
118
+ * Register a dynamic OAuth client with the server.
119
+ */
120
+ export async function registerOAuthClient(apiUrl, redirectUri) {
121
+ const response = await fetch(`${apiUrl}/api/oauth/register`, {
122
+ method: 'POST',
123
+ headers: { 'Content-Type': 'application/json' },
124
+ body: JSON.stringify({
125
+ redirect_uris: [redirectUri],
126
+ client_name: 'JobSeek CLI',
127
+ grant_types: ['authorization_code'],
128
+ token_endpoint_auth_method: 'none',
129
+ }),
130
+ });
131
+ if (!response.ok) {
132
+ const body = await response.json().catch(() => ({}));
133
+ throw new Error(`Failed to register OAuth client: ${body.error || response.statusText}`);
134
+ }
135
+ const data = await response.json();
136
+ return { clientId: data.client_id };
137
+ }
138
+ /**
139
+ * Exchange an authorization code for an API key via the token endpoint.
140
+ * Decodes the access token JWT to extract the apiKey field.
141
+ */
142
+ export async function exchangeCodeForApiKey(apiUrl, clientId, code, codeVerifier, redirectUri) {
143
+ const params = new URLSearchParams({
144
+ grant_type: 'authorization_code',
145
+ code,
146
+ client_id: clientId,
147
+ code_verifier: codeVerifier,
148
+ redirect_uri: redirectUri,
149
+ });
150
+ const response = await fetch(`${apiUrl}/api/oauth/token`, {
151
+ method: 'POST',
152
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
153
+ body: params.toString(),
154
+ });
155
+ if (!response.ok) {
156
+ const body = await response.json().catch(() => ({}));
157
+ throw new Error(`Token exchange failed: ${body.error_description || response.statusText}`);
158
+ }
159
+ const data = await response.json();
160
+ const apiKey = decodeJwtApiKey(data.access_token);
161
+ if (!apiKey) {
162
+ throw new Error('Access token does not contain an API key');
163
+ }
164
+ return apiKey;
165
+ }
166
+ /**
167
+ * Decode a JWT payload (no verification — we trust the token since we just got it over HTTPS).
168
+ * Extracts the `apiKey` field from the payload.
169
+ */
170
+ function decodeJwtApiKey(jwt) {
171
+ const parts = jwt.split('.');
172
+ if (parts.length !== 3)
173
+ return null;
174
+ try {
175
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
176
+ return payload.apiKey || null;
177
+ }
178
+ catch {
179
+ return null;
180
+ }
181
+ }
182
+ /**
183
+ * Build the authorization URL for the browser.
184
+ */
185
+ export function buildAuthorizeUrl(apiUrl, clientId, redirectUri, codeChallenge, state) {
186
+ const url = new URL('/oauth/authorize', apiUrl);
187
+ url.searchParams.set('client_id', clientId);
188
+ url.searchParams.set('redirect_uri', redirectUri);
189
+ url.searchParams.set('response_type', 'code');
190
+ url.searchParams.set('code_challenge', codeChallenge);
191
+ url.searchParams.set('code_challenge_method', 'S256');
192
+ url.searchParams.set('state', state);
193
+ url.searchParams.set('scope', 'mcp:tools');
194
+ return url.toString();
195
+ }
196
+ function callbackHtml(title, message, success) {
197
+ const color = success ? '#6B8E7F' : '#c0392b';
198
+ return `<!DOCTYPE html>
199
+ <html>
200
+ <head><meta charset="utf-8"><title>${title}</title></head>
201
+ <body style="font-family: -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #F5F3F0;">
202
+ <div style="text-align: center; padding: 2rem; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); max-width: 400px;">
203
+ <div style="font-size: 48px; margin-bottom: 16px;">${success ? '✓' : '✗'}</div>
204
+ <h1 style="color: ${color}; margin: 0 0 8px;">${title}</h1>
205
+ <p style="color: #666; margin: 0;">${message}</p>
206
+ </div>
207
+ </body>
208
+ </html>`;
209
+ }
210
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import Table from 'cli-table3';
2
+ import { type Ora } from 'ora';
3
+ export declare function setJsonMode(enabled: boolean): void;
4
+ export declare function isJsonMode(): boolean;
5
+ export declare function outputJson(data: unknown): void;
6
+ export declare function scoreColor(score: number): string;
7
+ export declare function scoreIndicator(score: number): string;
8
+ export declare function statusColor(status: string): string;
9
+ export declare function createTable(head: string[], colWidths?: number[]): Table.Table;
10
+ export declare function spinner(text: string): Ora;
11
+ export declare function formatDate(iso: string): string;
12
+ export declare function formatCents(cents: number | null | undefined): string;
13
+ export declare function truncate(str: string, maxLen: number): string;
14
+ export declare function heading(text: string): void;
15
+ export declare function success(text: string): void;
16
+ export declare function warn(text: string): void;
17
+ export declare function error(text: string): void;
18
+ export declare function info(text: string): void;
19
+ export declare function keyValue(key: string, value: string): void;
20
+ export declare function bulletList(items: string[]): void;
@@ -0,0 +1,109 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ import ora from 'ora';
4
+ // Global JSON mode flag — set by Commander's --json option
5
+ let jsonMode = false;
6
+ export function setJsonMode(enabled) {
7
+ jsonMode = enabled;
8
+ }
9
+ export function isJsonMode() {
10
+ return jsonMode;
11
+ }
12
+ // -- JSON output --
13
+ export function outputJson(data) {
14
+ console.log(JSON.stringify(data, null, 2));
15
+ }
16
+ // -- Score coloring --
17
+ export function scoreColor(score) {
18
+ if (score >= 80)
19
+ return chalk.green(`${score}%`);
20
+ if (score >= 60)
21
+ return chalk.yellow(`${score}%`);
22
+ return chalk.red(`${score}%`);
23
+ }
24
+ export function scoreIndicator(score) {
25
+ if (score >= 80)
26
+ return chalk.green('●');
27
+ if (score >= 60)
28
+ return chalk.yellow('●');
29
+ return chalk.red('●');
30
+ }
31
+ // -- Status coloring --
32
+ const STATUS_COLORS = {
33
+ draft: chalk.gray,
34
+ applied: chalk.blue,
35
+ interview: chalk.cyan,
36
+ interviewing: chalk.cyan,
37
+ received: chalk.blue,
38
+ offer: chalk.green,
39
+ accepted: chalk.green,
40
+ rejected: chalk.red,
41
+ declined: chalk.red,
42
+ ACTIVE: chalk.green,
43
+ PENDING: chalk.yellow,
44
+ CLOSED: chalk.gray,
45
+ DENIED: chalk.red,
46
+ EXHAUSTED: chalk.red,
47
+ SUSPENDED: chalk.yellow,
48
+ };
49
+ export function statusColor(status) {
50
+ const colorFn = STATUS_COLORS[status] || chalk.white;
51
+ return colorFn(status);
52
+ }
53
+ // -- Tables --
54
+ export function createTable(head, colWidths) {
55
+ return new Table({
56
+ head: head.map(h => chalk.bold(h)),
57
+ ...(colWidths ? { colWidths } : {}),
58
+ style: { head: [], border: [] },
59
+ wordWrap: true,
60
+ });
61
+ }
62
+ // -- Spinners --
63
+ export function spinner(text) {
64
+ return ora({ text, spinner: 'dots' });
65
+ }
66
+ // -- Formatting helpers --
67
+ export function formatDate(iso) {
68
+ return new Date(iso).toLocaleDateString('en-US', {
69
+ month: 'short',
70
+ day: 'numeric',
71
+ year: 'numeric',
72
+ });
73
+ }
74
+ export function formatCents(cents) {
75
+ if (cents == null)
76
+ return 'N/A';
77
+ return `$${(cents / 100).toFixed(2)}`;
78
+ }
79
+ export function truncate(str, maxLen) {
80
+ if (str.length <= maxLen)
81
+ return str;
82
+ return str.slice(0, maxLen - 1) + '…';
83
+ }
84
+ export function heading(text) {
85
+ console.log();
86
+ console.log(chalk.bold.underline(text));
87
+ console.log();
88
+ }
89
+ export function success(text) {
90
+ console.log(chalk.green('✓') + ' ' + text);
91
+ }
92
+ export function warn(text) {
93
+ console.log(chalk.yellow('!') + ' ' + text);
94
+ }
95
+ export function error(text) {
96
+ console.log(chalk.red('✗') + ' ' + text);
97
+ }
98
+ export function info(text) {
99
+ console.log(chalk.blue('i') + ' ' + text);
100
+ }
101
+ export function keyValue(key, value) {
102
+ console.log(` ${chalk.dim(key + ':')} ${value}`);
103
+ }
104
+ export function bulletList(items) {
105
+ for (const item of items) {
106
+ console.log(` ${chalk.dim('•')} ${item}`);
107
+ }
108
+ }
109
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export interface SshKeyInfo {
2
+ path: string;
3
+ type: string;
4
+ fingerprint: string;
5
+ comment: string;
6
+ }
7
+ /**
8
+ * Find SSH public keys in ~/.ssh/
9
+ */
10
+ export declare function findSshKeys(): SshKeyInfo[];
11
+ /**
12
+ * Compute the fingerprint of a public key string (matching ssh-keygen -lf format).
13
+ */
14
+ export declare function computeFingerprint(publicKeyContent: string): string | null;
15
+ /**
16
+ * Read the public key content from a .pub file.
17
+ */
18
+ export declare function readPublicKey(pubPath: string): string;
19
+ /**
20
+ * Sign a challenge string using ssh-keygen -Y sign.
21
+ * This works with all SSH key types (ed25519, rsa, ecdsa) including
22
+ * OpenSSH-format private keys that Node's crypto module can't parse directly.
23
+ * Returns the full SSH signature (PEM-armored).
24
+ */
25
+ export declare function signChallenge(publicKeyPath: string, challenge: string): string;
@@ -0,0 +1,103 @@
1
+ import { readFileSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { createHash } from 'node:crypto';
5
+ import { execFileSync } from 'node:child_process';
6
+ /**
7
+ * Find SSH public keys in ~/.ssh/
8
+ */
9
+ export function findSshKeys() {
10
+ const sshDir = join(homedir(), '.ssh');
11
+ const keys = [];
12
+ let files;
13
+ try {
14
+ files = readdirSync(sshDir);
15
+ }
16
+ catch {
17
+ return [];
18
+ }
19
+ for (const file of files) {
20
+ if (!file.endsWith('.pub'))
21
+ continue;
22
+ // Skip known non-identity files
23
+ if (file === 'authorized_keys' || file === 'known_hosts')
24
+ continue;
25
+ const pubPath = join(sshDir, file);
26
+ try {
27
+ const content = readFileSync(pubPath, 'utf-8').trim();
28
+ const parsed = parseSshPublicKey(content);
29
+ if (parsed) {
30
+ keys.push({
31
+ path: pubPath,
32
+ type: parsed.type,
33
+ fingerprint: parsed.fingerprint,
34
+ comment: parsed.comment,
35
+ });
36
+ }
37
+ }
38
+ catch {
39
+ // Skip unreadable files
40
+ }
41
+ }
42
+ return keys;
43
+ }
44
+ /**
45
+ * Parse an SSH public key string and compute its fingerprint.
46
+ * Supports ssh-rsa, ssh-ed25519, ecdsa-sha2-*.
47
+ */
48
+ function parseSshPublicKey(content) {
49
+ const parts = content.split(/\s+/);
50
+ if (parts.length < 2)
51
+ return null;
52
+ const type = parts[0];
53
+ const keyData = parts[1];
54
+ const comment = parts.slice(2).join(' ') || '';
55
+ // Validate it's a known key type
56
+ if (!type.startsWith('ssh-') && !type.startsWith('ecdsa-'))
57
+ return null;
58
+ try {
59
+ const rawBytes = Buffer.from(keyData, 'base64');
60
+ const hash = createHash('sha256').update(rawBytes).digest('base64');
61
+ // Remove trailing '=' padding to match ssh-keygen output
62
+ const fingerprint = `SHA256:${hash.replace(/=+$/, '')}`;
63
+ return { type, fingerprint, comment };
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ /**
70
+ * Compute the fingerprint of a public key string (matching ssh-keygen -lf format).
71
+ */
72
+ export function computeFingerprint(publicKeyContent) {
73
+ const parsed = parseSshPublicKey(publicKeyContent.trim());
74
+ return parsed?.fingerprint ?? null;
75
+ }
76
+ /**
77
+ * Read the public key content from a .pub file.
78
+ */
79
+ export function readPublicKey(pubPath) {
80
+ return readFileSync(pubPath, 'utf-8').trim();
81
+ }
82
+ /**
83
+ * Sign a challenge string using ssh-keygen -Y sign.
84
+ * This works with all SSH key types (ed25519, rsa, ecdsa) including
85
+ * OpenSSH-format private keys that Node's crypto module can't parse directly.
86
+ * Returns the full SSH signature (PEM-armored).
87
+ */
88
+ export function signChallenge(publicKeyPath, challenge) {
89
+ const privateKeyPath = publicKeyPath.replace(/\.pub$/, '');
90
+ // ssh-keygen -Y sign reads data from stdin and signs it
91
+ const signature = execFileSync('ssh-keygen', [
92
+ '-Y', 'sign',
93
+ '-f', privateKeyPath,
94
+ '-n', 'jobseek', // namespace
95
+ '-q', // quiet
96
+ ], {
97
+ input: challenge,
98
+ encoding: 'utf-8',
99
+ timeout: 10000,
100
+ });
101
+ return Buffer.from(signature.trim()).toString('base64');
102
+ }
103
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/lib/ssh.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "jobseek-cli",
3
+ "version": "1.0.0",
4
+ "description": "JobSeek CLI - AI-powered job search from your terminal",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Shawn Mitchell",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/shawnmitchell/jobseek.git",
11
+ "directory": "jobseek-cli"
12
+ },
13
+ "homepage": "https://www.getjobseek.com",
14
+ "keywords": [
15
+ "job-search",
16
+ "cli",
17
+ "resume",
18
+ "cover-letter",
19
+ "ai",
20
+ "career",
21
+ "jobseek"
22
+ ],
23
+ "bin": {
24
+ "jobseek": "./dist/index.js",
25
+ "js": "./dist/index.js"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "prepublishOnly": "npm run build",
36
+ "dev": "tsx src/index.ts",
37
+ "start": "node dist/index.js"
38
+ },
39
+ "dependencies": {
40
+ "@inquirer/prompts": "^7.0.0",
41
+ "chalk": "^5.4.1",
42
+ "cli-table3": "^0.6.5",
43
+ "commander": "^13.1.0",
44
+ "open": "^10.1.0",
45
+ "ora": "^8.2.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.0.0",
49
+ "tsx": "^4.19.0",
50
+ "typescript": "^5.7.0"
51
+ }
52
+ }