webpeel 0.8.1 → 0.9.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 (99) hide show
  1. package/README.md +39 -5
  2. package/dist/cli.js +1299 -85
  3. package/dist/cli.js.map +1 -1
  4. package/dist/core/application-tracker.d.ts +85 -0
  5. package/dist/core/application-tracker.d.ts.map +1 -0
  6. package/dist/core/application-tracker.js +184 -0
  7. package/dist/core/application-tracker.js.map +1 -0
  8. package/dist/core/apply.d.ts +163 -0
  9. package/dist/core/apply.d.ts.map +1 -0
  10. package/dist/core/apply.js +817 -0
  11. package/dist/core/apply.js.map +1 -0
  12. package/dist/core/branding.d.ts +1 -1
  13. package/dist/core/branding.d.ts.map +1 -1
  14. package/dist/core/budget.d.ts +43 -0
  15. package/dist/core/budget.d.ts.map +1 -0
  16. package/dist/core/budget.js +325 -0
  17. package/dist/core/budget.js.map +1 -0
  18. package/dist/core/challenge-detection.d.ts +27 -0
  19. package/dist/core/challenge-detection.d.ts.map +1 -0
  20. package/dist/core/challenge-detection.js +436 -0
  21. package/dist/core/challenge-detection.js.map +1 -0
  22. package/dist/core/change-tracking.d.ts.map +1 -1
  23. package/dist/core/change-tracking.js +10 -1
  24. package/dist/core/change-tracking.js.map +1 -1
  25. package/dist/core/crawler.d.ts.map +1 -1
  26. package/dist/core/crawler.js +17 -4
  27. package/dist/core/crawler.js.map +1 -1
  28. package/dist/core/diff.d.ts +62 -0
  29. package/dist/core/diff.d.ts.map +1 -0
  30. package/dist/core/diff.js +289 -0
  31. package/dist/core/diff.js.map +1 -0
  32. package/dist/core/extract-listings.d.ts +39 -0
  33. package/dist/core/extract-listings.d.ts.map +1 -0
  34. package/dist/core/extract-listings.js +331 -0
  35. package/dist/core/extract-listings.js.map +1 -0
  36. package/dist/core/extract.d.ts.map +1 -1
  37. package/dist/core/extract.js +15 -2
  38. package/dist/core/extract.js.map +1 -1
  39. package/dist/core/fetcher.d.ts +29 -3
  40. package/dist/core/fetcher.d.ts.map +1 -1
  41. package/dist/core/fetcher.js +158 -20
  42. package/dist/core/fetcher.js.map +1 -1
  43. package/dist/core/human.d.ts +176 -0
  44. package/dist/core/human.d.ts.map +1 -0
  45. package/dist/core/human.js +681 -0
  46. package/dist/core/human.js.map +1 -0
  47. package/dist/core/jobs.d.ts +12 -2
  48. package/dist/core/jobs.d.ts.map +1 -1
  49. package/dist/core/jobs.js +124 -2
  50. package/dist/core/jobs.js.map +1 -1
  51. package/dist/core/map.d.ts.map +1 -1
  52. package/dist/core/map.js +14 -2
  53. package/dist/core/map.js.map +1 -1
  54. package/dist/core/paginate.d.ts +32 -0
  55. package/dist/core/paginate.d.ts.map +1 -0
  56. package/dist/core/paginate.js +107 -0
  57. package/dist/core/paginate.js.map +1 -0
  58. package/dist/core/rate-governor.d.ts +81 -0
  59. package/dist/core/rate-governor.d.ts.map +1 -0
  60. package/dist/core/rate-governor.js +238 -0
  61. package/dist/core/rate-governor.js.map +1 -0
  62. package/dist/core/search-provider.d.ts +5 -0
  63. package/dist/core/search-provider.d.ts.map +1 -1
  64. package/dist/core/search-provider.js +81 -2
  65. package/dist/core/search-provider.js.map +1 -1
  66. package/dist/core/site-search.d.ts +45 -0
  67. package/dist/core/site-search.d.ts.map +1 -0
  68. package/dist/core/site-search.js +253 -0
  69. package/dist/core/site-search.js.map +1 -0
  70. package/dist/core/strategies.d.ts +8 -0
  71. package/dist/core/strategies.d.ts.map +1 -1
  72. package/dist/core/strategies.js +185 -45
  73. package/dist/core/strategies.js.map +1 -1
  74. package/dist/core/strategy-hooks.d.ts +6 -0
  75. package/dist/core/strategy-hooks.d.ts.map +1 -1
  76. package/dist/core/strategy-hooks.js.map +1 -1
  77. package/dist/core/table-format.d.ts +31 -0
  78. package/dist/core/table-format.d.ts.map +1 -0
  79. package/dist/core/table-format.js +147 -0
  80. package/dist/core/table-format.js.map +1 -0
  81. package/dist/core/user-agents.d.ts +58 -0
  82. package/dist/core/user-agents.d.ts.map +1 -0
  83. package/dist/core/user-agents.js +159 -0
  84. package/dist/core/user-agents.js.map +1 -0
  85. package/dist/core/watch.d.ts +100 -0
  86. package/dist/core/watch.d.ts.map +1 -0
  87. package/dist/core/watch.js +368 -0
  88. package/dist/core/watch.js.map +1 -0
  89. package/dist/index.d.ts +13 -2
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +41 -4
  92. package/dist/index.js.map +1 -1
  93. package/dist/mcp/server.js +3 -0
  94. package/dist/mcp/server.js.map +1 -1
  95. package/dist/types.d.ts +73 -0
  96. package/dist/types.d.ts.map +1 -1
  97. package/dist/types.js.map +1 -1
  98. package/llms.txt +1 -1
  99. package/package.json +3 -3
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Application Tracker — persists job application records to disk.
3
+ *
4
+ * Stored at: ~/.webpeel/applications.json
5
+ *
6
+ * Features:
7
+ * - Add / update application records
8
+ * - Duplicate-URL detection
9
+ * - Filter by platform, status, or since-date
10
+ * - Quick stats summary (total, by-platform, by-status, this-week, today)
11
+ */
12
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
13
+ import { homedir } from 'os';
14
+ import { join } from 'path';
15
+ import { randomUUID } from 'crypto';
16
+ // ── Storage ────────────────────────────────────────────────────────────
17
+ const WEBPEEL_DIR = join(homedir(), '.webpeel');
18
+ const APPLICATIONS_PATH = join(WEBPEEL_DIR, 'applications.json');
19
+ // ── ApplicationTracker class ───────────────────────────────────────────
20
+ export class ApplicationTracker {
21
+ records;
22
+ constructor() {
23
+ this.records = this.loadRecords();
24
+ }
25
+ // ── Public API ───────────────────────────────────────────────────────
26
+ /**
27
+ * Add a new application record.
28
+ * Assigns a random UUID and returns the completed record.
29
+ */
30
+ add(record) {
31
+ const full = {
32
+ id: randomUUID(),
33
+ ...record,
34
+ };
35
+ this.records.push(full);
36
+ this.save();
37
+ return full;
38
+ }
39
+ /**
40
+ * Update the status (and optionally notes) of an existing record.
41
+ * Throws if the ID is not found.
42
+ */
43
+ updateStatus(id, status, notes) {
44
+ const record = this.records.find((r) => r.id === id);
45
+ if (!record) {
46
+ throw new Error(`Application record not found: ${id}`);
47
+ }
48
+ record.status = status;
49
+ if (notes !== undefined) {
50
+ record.notes = notes;
51
+ }
52
+ this.save();
53
+ }
54
+ /**
55
+ * Return true if there is already an 'applied' record for this URL.
56
+ * Normalises the URL by stripping trailing slashes and query strings that
57
+ * are tracking parameters (utm_*, ref, etc.) before comparison.
58
+ */
59
+ hasApplied(url) {
60
+ const norm = normaliseUrl(url);
61
+ return this.records.some((r) => normaliseUrl(r.url) === norm && r.status === 'applied');
62
+ }
63
+ /**
64
+ * Return records, optionally filtered by platform, status, and/or since-date.
65
+ * Results are sorted newest-first.
66
+ */
67
+ list(filter) {
68
+ let results = [...this.records];
69
+ if (filter?.platform) {
70
+ const p = filter.platform.toLowerCase();
71
+ results = results.filter((r) => r.platform.toLowerCase() === p);
72
+ }
73
+ if (filter?.status) {
74
+ const s = filter.status.toLowerCase();
75
+ results = results.filter((r) => r.status.toLowerCase() === s);
76
+ }
77
+ if (filter?.since) {
78
+ const sinceTs = Date.parse(filter.since);
79
+ if (!isNaN(sinceTs)) {
80
+ results = results.filter((r) => Date.parse(r.appliedAt) >= sinceTs);
81
+ }
82
+ }
83
+ // Newest first
84
+ results.sort((a, b) => Date.parse(b.appliedAt) - Date.parse(a.appliedAt));
85
+ return results;
86
+ }
87
+ /**
88
+ * Return an aggregate stats summary.
89
+ */
90
+ stats() {
91
+ const now = new Date();
92
+ const todayStr = toDateString(now);
93
+ const weekAgo = new Date(now);
94
+ weekAgo.setDate(weekAgo.getDate() - 7);
95
+ const weekAgoTs = weekAgo.getTime();
96
+ const byPlatform = {};
97
+ const byStatus = {};
98
+ let thisWeek = 0;
99
+ let today = 0;
100
+ for (const r of this.records) {
101
+ // Platform counts
102
+ byPlatform[r.platform] = (byPlatform[r.platform] ?? 0) + 1;
103
+ // Status counts
104
+ byStatus[r.status] = (byStatus[r.status] ?? 0) + 1;
105
+ // Time-based counts
106
+ const appliedTs = Date.parse(r.appliedAt);
107
+ if (!isNaN(appliedTs)) {
108
+ if (appliedTs >= weekAgoTs)
109
+ thisWeek++;
110
+ if (toDateString(new Date(appliedTs)) === todayStr)
111
+ today++;
112
+ }
113
+ }
114
+ return {
115
+ total: this.records.length,
116
+ byPlatform,
117
+ byStatus,
118
+ thisWeek,
119
+ today,
120
+ };
121
+ }
122
+ // ── Private helpers ──────────────────────────────────────────────────
123
+ loadRecords() {
124
+ try {
125
+ if (existsSync(APPLICATIONS_PATH)) {
126
+ const raw = readFileSync(APPLICATIONS_PATH, 'utf-8');
127
+ const parsed = JSON.parse(raw);
128
+ if (Array.isArray(parsed))
129
+ return parsed;
130
+ }
131
+ }
132
+ catch {
133
+ // Corrupt / missing data — start fresh
134
+ }
135
+ return [];
136
+ }
137
+ save() {
138
+ try {
139
+ mkdirSync(WEBPEEL_DIR, { recursive: true });
140
+ writeFileSync(APPLICATIONS_PATH, JSON.stringify(this.records, null, 2), 'utf-8');
141
+ }
142
+ catch (err) {
143
+ console.error('[application-tracker] Failed to save records:', err);
144
+ }
145
+ }
146
+ }
147
+ // ── Utility ────────────────────────────────────────────────────────────
148
+ /** Format a Date as YYYY-MM-DD in local time. */
149
+ function toDateString(d) {
150
+ const y = d.getFullYear();
151
+ const m = String(d.getMonth() + 1).padStart(2, '0');
152
+ const day = String(d.getDate()).padStart(2, '0');
153
+ return `${y}-${m}-${day}`;
154
+ }
155
+ /** Tracking parameters to strip before URL comparison. */
156
+ const TRACKING_PARAMS = new Set([
157
+ 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',
158
+ 'ref', 'referer', 'referrer', 'source', 'trk', 'trackingId',
159
+ ]);
160
+ /**
161
+ * Strip trailing slashes, fragments, and common tracking query parameters
162
+ * from a URL for stable duplicate detection.
163
+ */
164
+ function normaliseUrl(rawUrl) {
165
+ try {
166
+ const u = new URL(rawUrl);
167
+ u.hash = '';
168
+ for (const param of TRACKING_PARAMS) {
169
+ u.searchParams.delete(param);
170
+ }
171
+ // Sort remaining params for canonical ordering
172
+ u.searchParams.sort();
173
+ let result = u.toString();
174
+ // Strip trailing slash
175
+ if (result.endsWith('/'))
176
+ result = result.slice(0, -1);
177
+ return result.toLowerCase();
178
+ }
179
+ catch {
180
+ // Not a valid URL — return as-is lowercased
181
+ return rawUrl.toLowerCase().replace(/\/$/, '');
182
+ }
183
+ }
184
+ //# sourceMappingURL=application-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-tracker.js","sourceRoot":"","sources":["../../src/core/application-tracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Job application pipeline — stealth automated job applications.
3
+ * Uses the human behavior engine for natural interaction.
4
+ *
5
+ * Flow:
6
+ * 1. Rate-limit check (daily limit)
7
+ * 2. Launch persistent browser (preserves login cookies)
8
+ * 3. Warmup browse (optional, looks human)
9
+ * 4. Navigate to job posting naturally
10
+ * 5. Detect & click Apply button
11
+ * 6. Scan form fields & categorize
12
+ * 7. Fill fields with human behavior
13
+ * 8. Handle multi-step forms
14
+ * 9. Review / dry-run / auto submit
15
+ * 10. Log to ~/.webpeel/applications.json
16
+ */
17
+ export interface ApplyProfile {
18
+ /** Full name */
19
+ name: string;
20
+ /** Email */
21
+ email: string;
22
+ /** Phone number */
23
+ phone: string;
24
+ /** LinkedIn profile URL */
25
+ linkedin?: string;
26
+ /** Portfolio/website URL */
27
+ website?: string;
28
+ /** City, State */
29
+ location: string;
30
+ /** Work authorization status (e.g. "US Citizen", "Permanent Resident", "H-1B", "Need Sponsorship") */
31
+ workAuthorization: string;
32
+ /** Years of experience */
33
+ yearsExperience: number;
34
+ /** Current/most recent title */
35
+ currentTitle: string;
36
+ /** Skills list */
37
+ skills: string[];
38
+ /** Education: degree, school (e.g. "B.S. Computer Science, MIT") */
39
+ education: string;
40
+ /** Path to resume file (PDF) */
41
+ resumePath: string;
42
+ /** Brief professional summary (for cover letter / LLM question generation) */
43
+ summary: string;
44
+ /** Desired salary range */
45
+ salaryRange?: {
46
+ min: number;
47
+ max: number;
48
+ };
49
+ /** Willing to relocate? */
50
+ willingToRelocate?: boolean;
51
+ /** Sponsorship needed? */
52
+ needsSponsorship?: boolean;
53
+ }
54
+ export interface ApplyOptions {
55
+ /** Job URL to apply to */
56
+ url: string;
57
+ /** Profile data */
58
+ profile: ApplyProfile;
59
+ /** Path to persistent browser session directory (cookies, localStorage) */
60
+ sessionDir?: string;
61
+ /** Mode: 'auto' (fully automated) | 'review' (pause before submit) | 'dry-run' (fill but don't submit) */
62
+ mode?: 'auto' | 'review' | 'dry-run';
63
+ /** LLM API key for generating tailored answers to custom questions */
64
+ llmKey?: string;
65
+ /** LLM provider: 'openai' | 'anthropic' (default: 'openai') */
66
+ llmProvider?: string;
67
+ /** Daily application limit (default: 8) */
68
+ dailyLimit?: number;
69
+ /** Timeout per application in ms (default: 120000) */
70
+ timeout?: number;
71
+ /** Browse the site naturally before applying (default: true) */
72
+ warmup?: boolean;
73
+ /** Warmup duration in ms (default: 15000-30000 random) */
74
+ warmupDuration?: number;
75
+ /** Callback for progress updates */
76
+ onProgress?: (event: ApplyProgressEvent) => void;
77
+ }
78
+ export interface ApplyProgressEvent {
79
+ stage: 'warmup' | 'navigating' | 'reading' | 'filling' | 'reviewing' | 'submitting' | 'done' | 'error';
80
+ message: string;
81
+ /** Form fields detected (during 'filling' stage) */
82
+ fields?: DetectedField[];
83
+ /** Answers generated for fields */
84
+ answers?: Record<string, string>;
85
+ }
86
+ export interface DetectedField {
87
+ /** Field type: text, email, tel, textarea, select, radio, checkbox, file */
88
+ type: string;
89
+ /** Label or placeholder text */
90
+ label: string;
91
+ /** CSS selector */
92
+ selector: string;
93
+ /** Select/radio options if applicable */
94
+ options?: string[];
95
+ /** Whether field is required */
96
+ required: boolean;
97
+ /** What we think this field is asking for */
98
+ category: 'name' | 'email' | 'phone' | 'linkedin' | 'website' | 'location' | 'work-auth' | 'experience' | 'salary' | 'education' | 'resume' | 'cover-letter' | 'skills' | 'custom-question' | 'unknown';
99
+ }
100
+ export interface ApplyResult {
101
+ /** Whether the application was submitted */
102
+ submitted: boolean;
103
+ /** Job details extracted during the process */
104
+ job: {
105
+ title: string;
106
+ company: string;
107
+ location?: string;
108
+ salary?: string;
109
+ };
110
+ /** Fields that were filled */
111
+ fieldsFilled: number;
112
+ /** Fields that needed LLM-generated answers */
113
+ llmAnswers: number;
114
+ /** Fields that couldn't be filled (unknown/ambiguous) */
115
+ fieldsSkipped: string[];
116
+ /** Any warnings */
117
+ warnings: string[];
118
+ /** Total time taken in ms */
119
+ elapsed: number;
120
+ /** Error if failed */
121
+ error?: string;
122
+ }
123
+ export interface ApplicationRecord {
124
+ id: string;
125
+ url: string;
126
+ company: string;
127
+ title: string;
128
+ location?: string;
129
+ salary?: string;
130
+ appliedAt: string;
131
+ mode: 'auto' | 'review' | 'dry-run';
132
+ status: 'applied' | 'interview' | 'rejected' | 'offer' | 'withdrawn';
133
+ fieldsFilled: number;
134
+ fieldsSkipped: string[];
135
+ warnings: string[];
136
+ notes?: string;
137
+ }
138
+ export declare function loadApplications(): ApplicationRecord[];
139
+ export declare function saveApplication(record: ApplicationRecord): void;
140
+ export declare function getApplicationsToday(): number;
141
+ export declare function updateApplicationStatus(id: string, status: ApplicationRecord['status']): void;
142
+ /**
143
+ * Apply to a job with stealth human-like behavior.
144
+ *
145
+ * Default mode is 'review' — it fills the form and waits for your approval
146
+ * before submitting. Use 'auto' for fully automated, 'dry-run' to see what
147
+ * would be filled without actually clicking submit.
148
+ *
149
+ * Requires a persistent browser session for login state preservation.
150
+ * On first run, the browser will open — log into LinkedIn, then the session
151
+ * is saved to `~/.webpeel/sessions/linkedin/` for future runs.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const result = await applyToJob({
156
+ * url: 'https://linkedin.com/jobs/view/...',
157
+ * profile: myProfile,
158
+ * mode: 'review',
159
+ * });
160
+ * ```
161
+ */
162
+ export declare function applyToJob(options: ApplyOptions): Promise<ApplyResult>;
163
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/core/apply.ts"],"names":[],"mappings":"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"}