@profullstack/agenticjobs 0.2.0 → 0.4.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 (80) hide show
  1. package/README.md +20 -0
  2. package/dist/cli/index.d.ts +9 -1
  3. package/dist/cli/index.js +99 -13
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/client/client.d.ts +16 -0
  6. package/dist/client/client.js +8 -0
  7. package/dist/client/client.js.map +1 -1
  8. package/dist/config.d.ts +12 -2
  9. package/dist/config.js +44 -4
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/applications.js +4 -1
  12. package/dist/core/applications.js.map +1 -1
  13. package/dist/core/auth.d.ts +2 -0
  14. package/dist/core/auth.js +2 -1
  15. package/dist/core/auth.js.map +1 -1
  16. package/dist/core/candidates.d.ts +42 -0
  17. package/dist/core/candidates.js +143 -0
  18. package/dist/core/candidates.js.map +1 -0
  19. package/dist/core/import-job.d.ts +36 -0
  20. package/dist/core/import-job.js +221 -0
  21. package/dist/core/import-job.js.map +1 -0
  22. package/dist/core/jobs.d.ts +29 -0
  23. package/dist/core/jobs.js +101 -31
  24. package/dist/core/jobs.js.map +1 -1
  25. package/dist/core/mail.d.ts +64 -0
  26. package/dist/core/mail.js +125 -0
  27. package/dist/core/mail.js.map +1 -0
  28. package/dist/core/resumes.d.ts +23 -1
  29. package/dist/core/resumes.js +86 -9
  30. package/dist/core/resumes.js.map +1 -1
  31. package/dist/directory/fetch.d.ts +11 -1
  32. package/dist/directory/fetch.js +23 -8
  33. package/dist/directory/fetch.js.map +1 -1
  34. package/dist/directory/registry.d.ts +1 -0
  35. package/dist/directory/registry.js +17 -1
  36. package/dist/directory/registry.js.map +1 -1
  37. package/dist/markup/resume.js +7 -1
  38. package/dist/markup/resume.js.map +1 -1
  39. package/dist/schema/job.d.ts +13 -10
  40. package/dist/schema/job.js.map +1 -1
  41. package/dist/schema/query.js +8 -4
  42. package/dist/schema/query.js.map +1 -1
  43. package/dist/schema/text.d.ts +12 -1
  44. package/dist/schema/text.js +14 -3
  45. package/dist/schema/text.js.map +1 -1
  46. package/dist/server/app.d.ts +6 -1
  47. package/dist/server/app.js +7 -2
  48. package/dist/server/app.js.map +1 -1
  49. package/dist/server/deps.d.ts +3 -0
  50. package/dist/server/routes/api.js +201 -24
  51. package/dist/server/routes/api.js.map +1 -1
  52. package/dist/server/routes/discovery.js +162 -1
  53. package/dist/server/routes/discovery.js.map +1 -1
  54. package/dist/server/routes/pages.js +44 -5
  55. package/dist/server/routes/pages.js.map +1 -1
  56. package/dist/server/serve.js +22 -7
  57. package/dist/server/serve.js.map +1 -1
  58. package/dist/views/auth.d.ts +1 -1
  59. package/dist/views/auth.js +3 -1
  60. package/dist/views/auth.js.map +1 -1
  61. package/dist/views/candidates.d.ts +47 -0
  62. package/dist/views/candidates.js +21 -0
  63. package/dist/views/candidates.js.map +1 -0
  64. package/dist/views/jobs.d.ts +13 -0
  65. package/dist/views/jobs.js +35 -10
  66. package/dist/views/jobs.js.map +1 -1
  67. package/dist/views/layout.js +2 -2
  68. package/dist/views/layout.js.map +1 -1
  69. package/dist/views/me.js +3 -1
  70. package/dist/views/me.js.map +1 -1
  71. package/dist/views/post.js +1 -1
  72. package/dist/views/post.js.map +1 -1
  73. package/docs/openjob.md +16 -9
  74. package/migrations/0006_board_only_applications.sql +34 -0
  75. package/migrations/0007_public_resumes.sql +24 -0
  76. package/migrations/0008_backfill_public_slugs.sql +57 -0
  77. package/migrations/0009_shorten_runaway_slugs.sql +21 -0
  78. package/package.json +16 -17
  79. package/web/public/app.css +45 -2
  80. package/web/public/sw.js +29 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/agenticjobs",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "An agent-friendly job board you self-host. It posts its own jobs, never scrapes anyone else's, and answers on every surface: web, API, MCP, CLI, TUI, desktop and PWA. Instances find each other through an open directory.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,7 +29,6 @@
29
29
  "engines": {
30
30
  "node": ">=24"
31
31
  },
32
- "packageManager": "pnpm@11.18.0",
33
32
  "bin": {
34
33
  "agenticjobs": "./bin/agenticjobs.mjs",
35
34
  "agenticjobs-mcp": "./bin/agenticjobs-mcp.mjs"
@@ -43,22 +42,9 @@
43
42
  "README.md",
44
43
  "LICENSE"
45
44
  ],
46
- "scripts": {
47
- "build": "tsc -p tsconfig.json",
48
- "clean": "rm -rf dist",
49
- "dev": "node --env-file-if-exists=.env --watch dist/cli/index.js serve",
50
- "start": "node --env-file-if-exists=.env dist/cli/index.js serve",
51
- "migrate": "node --env-file-if-exists=.env dist/db/migrate.js",
52
- "seed": "node --env-file-if-exists=.env dist/db/seed.js",
53
- "tui": "node --env-file-if-exists=.env dist/cli/index.js tui",
54
- "test": "pnpm run build && node --test \"test/*.test.ts\"",
55
- "typecheck": "tsc -p tsconfig.json --noEmit",
56
- "format": "prettier --write .",
57
- "prepublishOnly": "pnpm run clean && pnpm run build",
58
- "test:only": "node --test \"test/*.test.ts\""
59
- },
60
45
  "dependencies": {
61
46
  "@hono/node-server": "^1.14.0",
47
+ "@profullstack/emailer": "^1.0.3",
62
48
  "@profullstack/hqtui": "^0.3.0",
63
49
  "@simplewebauthn/server": "^14.0.1",
64
50
  "hono": "^4.6.14",
@@ -88,5 +74,18 @@
88
74
  "default": "./dist/mcp/server.js"
89
75
  },
90
76
  "./package.json": "./package.json"
77
+ },
78
+ "scripts": {
79
+ "build": "tsc -p tsconfig.json",
80
+ "clean": "rm -rf dist",
81
+ "dev": "node --env-file-if-exists=.env --watch dist/cli/index.js serve",
82
+ "start": "node --env-file-if-exists=.env dist/cli/index.js serve",
83
+ "migrate": "node --env-file-if-exists=.env dist/db/migrate.js",
84
+ "seed": "node --env-file-if-exists=.env dist/db/seed.js",
85
+ "tui": "node --env-file-if-exists=.env dist/cli/index.js tui",
86
+ "test": "pnpm run build && node --test \"test/*.test.ts\"",
87
+ "typecheck": "tsc -p tsconfig.json --noEmit",
88
+ "format": "prettier --write .",
89
+ "test:only": "node --test \"test/*.test.ts\""
91
90
  }
92
- }
91
+ }
@@ -148,10 +148,23 @@ main {
148
148
  color: var(--muted-foreground);
149
149
  }
150
150
 
151
+ /*
152
+ * `1fr` means `minmax(auto, 1fr)`, and that `auto` floor is the widest child's
153
+ * min-content. One long unbreakable string in a code block is then enough to
154
+ * push the single mobile column past the viewport and scroll the whole page
155
+ * sideways. `minmax(0, …)` lets the track shrink so the code block scrolls
156
+ * inside itself instead, which is what its own `overflow-x: auto` is for.
157
+ * The two-column rule below always had this right; the mobile base did not.
158
+ */
151
159
  .grid-2 {
152
160
  display: grid;
153
161
  gap: 1.5rem;
154
- grid-template-columns: 1fr;
162
+ grid-template-columns: minmax(0, 1fr);
163
+ }
164
+
165
+ /* A grid item's default `min-width: auto` defeats the same shrinking. */
166
+ .grid-2 > * {
167
+ min-width: 0;
155
168
  }
156
169
 
157
170
  @media (min-width: 60rem) {
@@ -529,6 +542,34 @@ legend {
529
542
  margin: 0;
530
543
  }
531
544
 
545
+ /*
546
+ * The card used to be one big anchor, which is why its tags could not be
547
+ * links: an anchor cannot contain another. The title carries the link now, and
548
+ * this keeps the card feeling clickable while every tag on it is separately
549
+ * reachable.
550
+ */
551
+ .job-card .job-title a {
552
+ color: inherit;
553
+ text-decoration: none;
554
+ }
555
+
556
+ .job-card:hover {
557
+ border-color: color-mix(in oklab, var(--primary) 45%, var(--border));
558
+ }
559
+
560
+ .job-card .job-title a:hover {
561
+ text-decoration: underline;
562
+ }
563
+
564
+ /* A linked badge must not look like body copy that happens to be blue. */
565
+ a.badge {
566
+ text-decoration: none;
567
+ }
568
+
569
+ a.badge:hover {
570
+ border-color: color-mix(in oklab, var(--primary) 55%, var(--border));
571
+ }
572
+
532
573
  .job-card .job-org {
533
574
  color: var(--muted-foreground);
534
575
  font-size: 0.9rem;
@@ -555,7 +596,9 @@ legend {
555
596
  @media (min-width: 40rem) {
556
597
  .filters-inline {
557
598
  display: grid;
558
- grid-template-columns: 1fr auto;
599
+ /* minmax(0, …) for the same reason as .grid-2: a long query in the search
600
+ box must not be able to widen the row. */
601
+ grid-template-columns: minmax(0, 1fr) auto;
559
602
  gap: 0.5rem;
560
603
  align-items: end;
561
604
  }
package/web/public/sw.js CHANGED
@@ -6,11 +6,14 @@
6
6
  * is cached is the shell (stylesheet, icon) and an offline fallback, and every
7
7
  * document goes to the network first.
8
8
  *
9
- * Bump VERSION on any change here or to the assets listed below. Without a
10
- * bump, a returning reader keeps the old worker and never sees the change.
9
+ * VERSION is a hash of the shell assets, not a number someone remembers to
10
+ * raise. It was a number, and the first CSS fix that shipped after this file
11
+ * was written did not raise it, so every returning reader kept the broken
12
+ * stylesheet forever. `pnpm test` recomputes the hash and fails when it has
13
+ * drifted, which is the only version of this rule that survives contact.
11
14
  */
12
15
 
13
- const VERSION = 'v1';
16
+ const VERSION = '82142d5f';
14
17
  const SHELL = `shell-${VERSION}`;
15
18
  const ASSETS = ['/assets/app.css', '/assets/tokens.css', '/assets/icon.svg'];
16
19
 
@@ -18,7 +21,13 @@ self.addEventListener('install', (event) => {
18
21
  event.waitUntil(
19
22
  caches
20
23
  .open(SHELL)
21
- .then((cache) => cache.addAll(ASSETS))
24
+ // `cache: 'reload'` because addAll goes through the HTTP cache by
25
+ // default, and these assets are served with max-age=3600. Without it the
26
+ // worker faithfully caches whatever stale copy the HTTP cache is still
27
+ // holding, under a fresh cache name, and the result is a cache that
28
+ // looks correct and serves the old file for an hour. That is exactly
29
+ // what happened to the mobile fix.
30
+ .then((cache) => cache.addAll(ASSETS.map((asset) => new Request(asset, { cache: 'reload' }))))
22
31
  .then(() => self.skipWaiting()),
23
32
  );
24
33
  });
@@ -43,8 +52,23 @@ self.addEventListener('fetch', (event) => {
43
52
  if (url.pathname.startsWith('/api/') || url.pathname.startsWith('/me')) return;
44
53
 
45
54
  if (ASSETS.includes(url.pathname)) {
55
+ // Answer from the cache, then refresh it in the background. A stale asset
56
+ // therefore survives one render rather than until the next VERSION bump,
57
+ // which matters because the bump is the step that gets missed.
46
58
  event.respondWith(
47
- caches.match(request).then((hit) => hit || fetch(request)),
59
+ caches.open(SHELL).then((cache) =>
60
+ cache.match(request).then((hit) => {
61
+ // Same reason as install: revalidating through the HTTP cache can
62
+ // refresh the entry with the copy it already had.
63
+ const fresh = fetch(new Request(request.url, { cache: 'reload' }))
64
+ .then((response) => {
65
+ if (response.ok) void cache.put(request, response.clone());
66
+ return response;
67
+ })
68
+ .catch(() => hit);
69
+ return hit || fresh;
70
+ }),
71
+ ),
48
72
  );
49
73
  return;
50
74
  }