@profullstack/agenticjobs 0.4.2 → 0.6.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 (51) hide show
  1. package/README.md +42 -4
  2. package/dist/cli/index.js +70 -1
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/client/client.d.ts +36 -0
  5. package/dist/client/client.js +22 -0
  6. package/dist/client/client.js.map +1 -1
  7. package/dist/config.d.ts +1 -1
  8. package/dist/config.js +1 -1
  9. package/dist/core/auth.js +4 -1
  10. package/dist/core/auth.js.map +1 -1
  11. package/dist/core/markdown-media.d.ts +51 -0
  12. package/dist/core/markdown-media.js +224 -0
  13. package/dist/core/markdown-media.js.map +1 -0
  14. package/dist/core/resumes.d.ts +12 -0
  15. package/dist/core/resumes.js +19 -0
  16. package/dist/core/resumes.js.map +1 -1
  17. package/dist/core/updates.d.ts +149 -0
  18. package/dist/core/updates.js +332 -0
  19. package/dist/core/updates.js.map +1 -0
  20. package/dist/directory/fetch.d.ts +1 -1
  21. package/dist/mcp/tools.js +79 -0
  22. package/dist/mcp/tools.js.map +1 -1
  23. package/dist/server/middleware.d.ts +11 -6
  24. package/dist/server/middleware.js +12 -7
  25. package/dist/server/middleware.js.map +1 -1
  26. package/dist/server/routes/api.js +167 -0
  27. package/dist/server/routes/api.js.map +1 -1
  28. package/dist/server/routes/discovery.js +261 -26
  29. package/dist/server/routes/discovery.js.map +1 -1
  30. package/dist/server/routes/openapi.js +105 -0
  31. package/dist/server/routes/openapi.js.map +1 -1
  32. package/dist/server/routes/pages.js +208 -9
  33. package/dist/server/routes/pages.js.map +1 -1
  34. package/dist/views/candidates.d.ts +3 -0
  35. package/dist/views/candidates.js +3 -2
  36. package/dist/views/candidates.js.map +1 -1
  37. package/dist/views/jobs.d.ts +40 -1
  38. package/dist/views/jobs.js +69 -4
  39. package/dist/views/jobs.js.map +1 -1
  40. package/dist/views/layout.js +2 -2
  41. package/dist/views/layout.js.map +1 -1
  42. package/dist/views/me.d.ts +11 -0
  43. package/dist/views/me.js +5 -2
  44. package/dist/views/me.js.map +1 -1
  45. package/dist/views/updates.d.ts +80 -0
  46. package/dist/views/updates.js +49 -0
  47. package/dist/views/updates.js.map +1 -0
  48. package/migrations/0010_updates_and_follows.sql +70 -0
  49. package/package.json +17 -15
  50. package/web/public/app.css +44 -0
  51. package/web/public/sw.js +1 -1
@@ -0,0 +1,70 @@
1
+ -- Updates, and following the people who post them.
2
+ --
3
+ -- A short post from an employer or a candidate: hiring news, what shipped,
4
+ -- what someone is looking for next. The thing that makes this worth having
5
+ -- rather than another timeline is that it is attached to an identity the board
6
+ -- already knows, so an update is from a real employer or a real candidate and
7
+ -- not from an account made this morning.
8
+ --
9
+ -- Two constraints are in the schema rather than in a validator, because they
10
+ -- are what stop this becoming a spam feed and a validator is easy to route
11
+ -- around:
12
+ --
13
+ -- - An update is short. 600 characters is a paragraph and a link, which is
14
+ -- the shape of the thing; it is not room for an article.
15
+ -- - An update has exactly one author, and that author is an employer this
16
+ -- person belongs to or their own candidate profile. There is no anonymous
17
+ -- posting and no posting as somebody else.
18
+
19
+ create table if not exists updates (
20
+ id uuid primary key default gen_random_uuid(),
21
+ -- Exactly one of these. A CHECK enforces it rather than a comment hoping.
22
+ org_id uuid references organisations (id) on delete cascade,
23
+ user_id uuid references users (id) on delete cascade,
24
+ -- Who actually typed it, kept even for an org post so a deleted member's
25
+ -- posts can be found, and so "who posted this" has an answer.
26
+ author_id uuid not null references users (id) on delete cascade,
27
+ body text not null check (length(body) between 1 and 600),
28
+ -- One link, optional. Kept as a column rather than fished out of the body,
29
+ -- so a reader and an agent see the same URL and nothing has to parse prose.
30
+ link text,
31
+ created_at timestamptz not null default now(),
32
+ constraint updates_one_author check (
33
+ (org_id is not null and user_id is null) or (org_id is null and user_id is not null)
34
+ )
35
+ );
36
+
37
+ -- The feed reads "newest first, by author", and the whole-board feed reads
38
+ -- "newest first" across everything.
39
+ create index if not exists updates_org_idx on updates (org_id, created_at desc)
40
+ where org_id is not null;
41
+ create index if not exists updates_user_idx on updates (user_id, created_at desc)
42
+ where user_id is not null;
43
+ create index if not exists updates_recent_idx on updates (created_at desc);
44
+
45
+ -- Following.
46
+ --
47
+ -- A row is one person following one employer or one candidate. Same one-target
48
+ -- rule as an update, for the same reason, and a unique index rather than a
49
+ -- primary key over nullable columns so following twice is a no-op instead of
50
+ -- an error the caller has to interpret.
51
+ create table if not exists follows (
52
+ id uuid primary key default gen_random_uuid(),
53
+ follower_id uuid not null references users (id) on delete cascade,
54
+ org_id uuid references organisations (id) on delete cascade,
55
+ user_id uuid references users (id) on delete cascade,
56
+ created_at timestamptz not null default now(),
57
+ constraint follows_one_target check (
58
+ (org_id is not null and user_id is null) or (org_id is null and user_id is not null)
59
+ ),
60
+ -- Nobody follows themselves; it is not an error worth a message, but it is
61
+ -- not a row worth keeping either.
62
+ constraint follows_not_self check (user_id is null or user_id <> follower_id)
63
+ );
64
+
65
+ create unique index if not exists follows_org_key
66
+ on follows (follower_id, org_id) where org_id is not null;
67
+ create unique index if not exists follows_user_key
68
+ on follows (follower_id, user_id) where user_id is not null;
69
+ create index if not exists follows_org_count_idx on follows (org_id) where org_id is not null;
70
+ create index if not exists follows_user_count_idx on follows (user_id) where user_id is not null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/agenticjobs",
3
- "version": "0.4.2",
3
+ "version": "0.6.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,6 +29,7 @@
29
29
  "engines": {
30
30
  "node": ">=24"
31
31
  },
32
+ "packageManager": "pnpm@11.18.0",
32
33
  "bin": {
33
34
  "agenticjobs": "./bin/agenticjobs.mjs",
34
35
  "agenticjobs-mcp": "./bin/agenticjobs-mcp.mjs"
@@ -42,6 +43,20 @@
42
43
  "README.md",
43
44
  "LICENSE"
44
45
  ],
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
+ },
45
60
  "dependencies": {
46
61
  "@hono/node-server": "^1.14.0",
47
62
  "@profullstack/emailer": "^1.0.3",
@@ -74,18 +89,5 @@
74
89
  "default": "./dist/mcp/server.js"
75
90
  },
76
91
  "./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\""
90
92
  }
91
- }
93
+ }
@@ -815,3 +815,47 @@ a.badge-plain:hover .badge {
815
815
  .install-strip .install-note {
816
816
  margin: 0;
817
817
  }
818
+
819
+ /* --- updates ------------------------------------------------------------
820
+ *
821
+ * A short post from an employer or a candidate. Deliberately quieter than a
822
+ * job card: this is the space between listings rather than a second kind of
823
+ * listing, and anything that makes one post look important is an argument
824
+ * for posting more of them.
825
+ */
826
+
827
+ .update-list {
828
+ list-style: none;
829
+ margin: 0;
830
+ padding: 0;
831
+ display: flex;
832
+ flex-direction: column;
833
+ gap: 0.75rem;
834
+ }
835
+
836
+ .update {
837
+ border: 1px solid var(--border);
838
+ border-radius: var(--radius);
839
+ padding: 0.9rem 1rem;
840
+ background: var(--card);
841
+ /* The feed's guids are anchors into this page, so an item opened from a
842
+ reader lands somewhere that is not behind the header. */
843
+ scroll-margin-top: 5rem;
844
+ }
845
+
846
+ .update-author {
847
+ margin: 0 0 0.35rem;
848
+ }
849
+
850
+ .update-body {
851
+ margin: 0;
852
+ /* Written in a textarea, so its line breaks are the ones the author made. */
853
+ white-space: pre-wrap;
854
+ /* A pasted URL is one unbreakable word and would otherwise push the card
855
+ wider than the viewport on a phone. */
856
+ overflow-wrap: anywhere;
857
+ }
858
+
859
+ .update-link {
860
+ overflow-wrap: anywhere;
861
+ }
package/web/public/sw.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * drifted, which is the only version of this rule that survives contact.
14
14
  */
15
15
 
16
- const VERSION = '6c4facbe';
16
+ const VERSION = 'c6b9327c';
17
17
  const SHELL = `shell-${VERSION}`;
18
18
  const ASSETS = ['/assets/app.css', '/assets/tokens.css', '/assets/icon.svg'];
19
19