@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.
- package/README.md +42 -4
- package/dist/cli/index.js +70 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/client.d.ts +36 -0
- package/dist/client/client.js +22 -0
- package/dist/client/client.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/core/auth.js +4 -1
- package/dist/core/auth.js.map +1 -1
- package/dist/core/markdown-media.d.ts +51 -0
- package/dist/core/markdown-media.js +224 -0
- package/dist/core/markdown-media.js.map +1 -0
- package/dist/core/resumes.d.ts +12 -0
- package/dist/core/resumes.js +19 -0
- package/dist/core/resumes.js.map +1 -1
- package/dist/core/updates.d.ts +149 -0
- package/dist/core/updates.js +332 -0
- package/dist/core/updates.js.map +1 -0
- package/dist/directory/fetch.d.ts +1 -1
- package/dist/mcp/tools.js +79 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/server/middleware.d.ts +11 -6
- package/dist/server/middleware.js +12 -7
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/routes/api.js +167 -0
- package/dist/server/routes/api.js.map +1 -1
- package/dist/server/routes/discovery.js +261 -26
- package/dist/server/routes/discovery.js.map +1 -1
- package/dist/server/routes/openapi.js +105 -0
- package/dist/server/routes/openapi.js.map +1 -1
- package/dist/server/routes/pages.js +208 -9
- package/dist/server/routes/pages.js.map +1 -1
- package/dist/views/candidates.d.ts +3 -0
- package/dist/views/candidates.js +3 -2
- package/dist/views/candidates.js.map +1 -1
- package/dist/views/jobs.d.ts +40 -1
- package/dist/views/jobs.js +69 -4
- package/dist/views/jobs.js.map +1 -1
- package/dist/views/layout.js +2 -2
- package/dist/views/layout.js.map +1 -1
- package/dist/views/me.d.ts +11 -0
- package/dist/views/me.js +5 -2
- package/dist/views/me.js.map +1 -1
- package/dist/views/updates.d.ts +80 -0
- package/dist/views/updates.js +49 -0
- package/dist/views/updates.js.map +1 -0
- package/migrations/0010_updates_and_follows.sql +70 -0
- package/package.json +17 -15
- package/web/public/app.css +44 -0
- 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.
|
|
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
|
+
}
|
package/web/public/app.css
CHANGED
|
@@ -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 = '
|
|
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
|
|