@profullstack/agenticjobs 0.1.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/LICENSE +21 -0
- package/README.md +219 -0
- package/bin/agenticjobs-mcp.mjs +2 -0
- package/bin/agenticjobs.mjs +2 -0
- package/dist/cli/args.d.ts +17 -0
- package/dist/cli/args.js +103 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/format.d.ts +15 -0
- package/dist/cli/format.js +24 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.js +620 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/jobfile.d.ts +15 -0
- package/dist/cli/jobfile.js +94 -0
- package/dist/cli/jobfile.js.map +1 -0
- package/dist/client/client.d.ts +105 -0
- package/dist/client/client.js +182 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/config.d.ts +33 -0
- package/dist/client/config.js +92 -0
- package/dist/client/config.js.map +1 -0
- package/dist/client/fanout.d.ts +35 -0
- package/dist/client/fanout.js +77 -0
- package/dist/client/fanout.js.map +1 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +5 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/login.d.ts +23 -0
- package/dist/client/login.js +45 -0
- package/dist/client/login.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.js +64 -0
- package/dist/config.js.map +1 -0
- package/dist/core/applications.d.ts +67 -0
- package/dist/core/applications.js +187 -0
- package/dist/core/applications.js.map +1 -0
- package/dist/core/auth.d.ts +120 -0
- package/dist/core/auth.js +246 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/import.d.ts +52 -0
- package/dist/core/import.js +311 -0
- package/dist/core/import.js.map +1 -0
- package/dist/core/jobs.d.ts +91 -0
- package/dist/core/jobs.js +381 -0
- package/dist/core/jobs.js.map +1 -0
- package/dist/core/orgs.d.ts +21 -0
- package/dist/core/orgs.js +111 -0
- package/dist/core/orgs.js.map +1 -0
- package/dist/core/resumes.d.ts +51 -0
- package/dist/core/resumes.js +144 -0
- package/dist/core/resumes.js.map +1 -0
- package/dist/core/zip.d.ts +17 -0
- package/dist/core/zip.js +77 -0
- package/dist/core/zip.js.map +1 -0
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.js +3 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +15 -0
- package/dist/db/migrate.js +67 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/pool.d.ts +13 -0
- package/dist/db/pool.js +68 -0
- package/dist/db/pool.js.map +1 -0
- package/dist/db/seed.d.ts +9 -0
- package/dist/db/seed.js +120 -0
- package/dist/db/seed.js.map +1 -0
- package/dist/directory/announce.d.ts +32 -0
- package/dist/directory/announce.js +59 -0
- package/dist/directory/announce.js.map +1 -0
- package/dist/directory/federate.d.ts +59 -0
- package/dist/directory/federate.js +126 -0
- package/dist/directory/federate.js.map +1 -0
- package/dist/directory/fetch.d.ts +35 -0
- package/dist/directory/fetch.js +138 -0
- package/dist/directory/fetch.js.map +1 -0
- package/dist/directory/index.d.ts +4 -0
- package/dist/directory/index.js +5 -0
- package/dist/directory/index.js.map +1 -0
- package/dist/directory/registry.d.ts +55 -0
- package/dist/directory/registry.js +155 -0
- package/dist/directory/registry.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/markup/escape.d.ts +20 -0
- package/dist/markup/escape.js +56 -0
- package/dist/markup/escape.js.map +1 -0
- package/dist/markup/markdown.d.ts +35 -0
- package/dist/markup/markdown.js +279 -0
- package/dist/markup/markdown.js.map +1 -0
- package/dist/markup/resume.d.ts +72 -0
- package/dist/markup/resume.js +260 -0
- package/dist/markup/resume.js.map +1 -0
- package/dist/mcp/protocol.d.ts +56 -0
- package/dist/mcp/protocol.js +48 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/server.d.ts +21 -0
- package/dist/mcp/server.js +73 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +13 -0
- package/dist/mcp/stdio.js +85 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/mcp/tools.d.ts +26 -0
- package/dist/mcp/tools.js +351 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.js +6 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/instance.d.ts +85 -0
- package/dist/schema/instance.js +147 -0
- package/dist/schema/instance.js.map +1 -0
- package/dist/schema/job.d.ts +151 -0
- package/dist/schema/job.js +73 -0
- package/dist/schema/job.js.map +1 -0
- package/dist/schema/jsonld.d.ts +18 -0
- package/dist/schema/jsonld.js +120 -0
- package/dist/schema/jsonld.js.map +1 -0
- package/dist/schema/query.d.ts +47 -0
- package/dist/schema/query.js +103 -0
- package/dist/schema/query.js.map +1 -0
- package/dist/schema/text.d.ts +27 -0
- package/dist/schema/text.js +117 -0
- package/dist/schema/text.js.map +1 -0
- package/dist/server/app.d.ts +13 -0
- package/dist/server/app.js +57 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/deps.d.ts +20 -0
- package/dist/server/deps.js +2 -0
- package/dist/server/deps.js.map +1 -0
- package/dist/server/middleware.d.ts +39 -0
- package/dist/server/middleware.js +100 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/routes/api.d.ts +20 -0
- package/dist/server/routes/api.js +584 -0
- package/dist/server/routes/api.js.map +1 -0
- package/dist/server/routes/descriptor.d.ts +11 -0
- package/dist/server/routes/descriptor.js +31 -0
- package/dist/server/routes/descriptor.js.map +1 -0
- package/dist/server/routes/discovery.d.ts +12 -0
- package/dist/server/routes/discovery.js +305 -0
- package/dist/server/routes/discovery.js.map +1 -0
- package/dist/server/routes/mcp.d.ts +19 -0
- package/dist/server/routes/mcp.js +85 -0
- package/dist/server/routes/mcp.js.map +1 -0
- package/dist/server/routes/openapi.d.ts +11 -0
- package/dist/server/routes/openapi.js +297 -0
- package/dist/server/routes/openapi.js.map +1 -0
- package/dist/server/routes/pages.d.ts +11 -0
- package/dist/server/routes/pages.js +445 -0
- package/dist/server/routes/pages.js.map +1 -0
- package/dist/server/routes/passkey.d.ts +20 -0
- package/dist/server/routes/passkey.js +198 -0
- package/dist/server/routes/passkey.js.map +1 -0
- package/dist/server/routes/specs.d.ts +10 -0
- package/dist/server/routes/specs.js +42 -0
- package/dist/server/routes/specs.js.map +1 -0
- package/dist/server/serve.d.ts +11 -0
- package/dist/server/serve.js +79 -0
- package/dist/server/serve.js.map +1 -0
- package/dist/tui/index.d.ts +13 -0
- package/dist/tui/index.js +266 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/state.d.ts +63 -0
- package/dist/tui/state.js +136 -0
- package/dist/tui/state.js.map +1 -0
- package/dist/tui/types.d.ts +2 -0
- package/dist/tui/types.js +2 -0
- package/dist/tui/types.js.map +1 -0
- package/dist/tui/views.d.ts +11 -0
- package/dist/tui/views.js +197 -0
- package/dist/tui/views.js.map +1 -0
- package/dist/views/auth.d.ts +21 -0
- package/dist/views/auth.js +5 -0
- package/dist/views/auth.js.map +1 -0
- package/dist/views/docs.d.ts +17 -0
- package/dist/views/docs.js +40 -0
- package/dist/views/docs.js.map +1 -0
- package/dist/views/jobs.d.ts +49 -0
- package/dist/views/jobs.js +44 -0
- package/dist/views/jobs.js.map +1 -0
- package/dist/views/layout.d.ts +58 -0
- package/dist/views/layout.js +36 -0
- package/dist/views/layout.js.map +1 -0
- package/dist/views/me.d.ts +27 -0
- package/dist/views/me.js +6 -0
- package/dist/views/me.js.map +1 -0
- package/dist/views/network.d.ts +21 -0
- package/dist/views/network.js +17 -0
- package/dist/views/network.js.map +1 -0
- package/dist/views/post.d.ts +22 -0
- package/dist/views/post.js +20 -0
- package/dist/views/post.js.map +1 -0
- package/docs/openjob.md +200 -0
- package/docs/openresume.md +143 -0
- package/migrations/0001_init.sql +109 -0
- package/migrations/0002_auth.sql +82 -0
- package/migrations/0003_directory.sql +27 -0
- package/migrations/0004_resumes.sql +63 -0
- package/migrations/0005_application_drafts.sql +20 -0
- package/package.json +92 -0
- package/web/public/app.css +731 -0
- package/web/public/app.js +173 -0
- package/web/public/icon.svg +5 -0
- package/web/public/sw.js +64 -0
- package/web/public/tokens.css +153 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A job as a file.
|
|
3
|
+
*
|
|
4
|
+
* Front matter for the structured fields, Markdown below it for the
|
|
5
|
+
* description - the shape everybody already writes posts in, so a listing can
|
|
6
|
+
* live in a repository, go through review, and be posted by CI.
|
|
7
|
+
*
|
|
8
|
+
* The parser is deliberately not YAML. It handles `key: value`, `key: [a, b]`
|
|
9
|
+
* and block lists, which is the whole vocabulary a job posting needs, and it
|
|
10
|
+
* cannot execute anything or pull in a parser with its own CVE history.
|
|
11
|
+
*/
|
|
12
|
+
export interface JobDocument extends Record<string, unknown> {
|
|
13
|
+
description: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function parseJobDocument(source: string): JobDocument;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A job as a file.
|
|
3
|
+
*
|
|
4
|
+
* Front matter for the structured fields, Markdown below it for the
|
|
5
|
+
* description - the shape everybody already writes posts in, so a listing can
|
|
6
|
+
* live in a repository, go through review, and be posted by CI.
|
|
7
|
+
*
|
|
8
|
+
* The parser is deliberately not YAML. It handles `key: value`, `key: [a, b]`
|
|
9
|
+
* and block lists, which is the whole vocabulary a job posting needs, and it
|
|
10
|
+
* cannot execute anything or pull in a parser with its own CVE history.
|
|
11
|
+
*/
|
|
12
|
+
export function parseJobDocument(source) {
|
|
13
|
+
const text = source.replace(/\r\n?/g, '\n');
|
|
14
|
+
const match = /^---\n([\s\S]*?)\n---\n?/.exec(text);
|
|
15
|
+
if (match === null) {
|
|
16
|
+
// No front matter: the whole file is the description, and its first
|
|
17
|
+
// heading is the title, so a plain Markdown file still posts.
|
|
18
|
+
const title = /^#\s+(.+)$/m.exec(text)?.[1]?.trim();
|
|
19
|
+
return {
|
|
20
|
+
description: text.trim(),
|
|
21
|
+
...(title === undefined ? {} : { title }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const front = parseFrontMatter(match[1] ?? '');
|
|
25
|
+
const body = text.slice(match[0].length).trim();
|
|
26
|
+
const title = front['title'] ?? /^#\s+(.+)$/m.exec(body)?.[1]?.trim();
|
|
27
|
+
return {
|
|
28
|
+
...front,
|
|
29
|
+
...(title === undefined ? {} : { title }),
|
|
30
|
+
// The h1 is dropped from the body when it became the title, so the page
|
|
31
|
+
// does not show the same line twice.
|
|
32
|
+
description: title !== undefined && front['title'] === undefined
|
|
33
|
+
? body.replace(/^#\s+.+\n?/, '').trim()
|
|
34
|
+
: body,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function parseFrontMatter(block) {
|
|
38
|
+
const out = {};
|
|
39
|
+
const lines = block.split('\n');
|
|
40
|
+
let key = null;
|
|
41
|
+
let list = null;
|
|
42
|
+
const flush = () => {
|
|
43
|
+
if (key !== null && list !== null)
|
|
44
|
+
out[key] = list;
|
|
45
|
+
list = null;
|
|
46
|
+
};
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
if (line.trim() === '' || line.trimStart().startsWith('#'))
|
|
49
|
+
continue;
|
|
50
|
+
const item = /^\s*-\s+(.*)$/.exec(line);
|
|
51
|
+
if (item !== null && key !== null) {
|
|
52
|
+
list ??= [];
|
|
53
|
+
list.push(unquote(item[1] ?? ''));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const pair = /^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
57
|
+
if (pair === null)
|
|
58
|
+
continue;
|
|
59
|
+
flush();
|
|
60
|
+
key = camel(pair[1] ?? '');
|
|
61
|
+
const value = (pair[2] ?? '').trim();
|
|
62
|
+
if (value === '') {
|
|
63
|
+
// An empty value opens a block list on the following lines.
|
|
64
|
+
list = [];
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const inline = /^\[(.*)\]$/.exec(value);
|
|
68
|
+
if (inline !== null) {
|
|
69
|
+
out[key] = (inline[1] ?? '')
|
|
70
|
+
.split(',')
|
|
71
|
+
.map((entry) => unquote(entry.trim()))
|
|
72
|
+
.filter((entry) => entry !== '');
|
|
73
|
+
key = null;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
out[key] = unquote(value);
|
|
77
|
+
key = null;
|
|
78
|
+
}
|
|
79
|
+
flush();
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** `salary_min` and `salary-min` both mean `salaryMin`. */
|
|
83
|
+
function camel(key) {
|
|
84
|
+
return key.replace(/[_-]([a-z])/g, (_whole, letter) => letter.toUpperCase());
|
|
85
|
+
}
|
|
86
|
+
function unquote(value) {
|
|
87
|
+
const trimmed = value.trim();
|
|
88
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
89
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
90
|
+
return trimmed.slice(1, -1);
|
|
91
|
+
}
|
|
92
|
+
return trimmed;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=jobfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobfile.js","sourceRoot":"","sources":["../../src/cli/jobfile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One client over the REST API, shared by the CLI, the TUI, the desktop app
|
|
3
|
+
* and the stdio MCP binary.
|
|
4
|
+
*
|
|
5
|
+
* There is deliberately no second HTTP layer anywhere in this repository. A
|
|
6
|
+
* command that builds its own request is a command that gets its own bugs
|
|
7
|
+
* around auth headers, error shapes and trailing slashes.
|
|
8
|
+
*/
|
|
9
|
+
import type { Job, JobPage, JobQuery, Organisation } from '../schema/index.ts';
|
|
10
|
+
import type { InstanceDescriptor, InstanceListing } from '../schema/instance.ts';
|
|
11
|
+
export declare class ApiError extends Error {
|
|
12
|
+
readonly status: number;
|
|
13
|
+
readonly code: string;
|
|
14
|
+
readonly fields: {
|
|
15
|
+
field: string;
|
|
16
|
+
message: string;
|
|
17
|
+
}[];
|
|
18
|
+
constructor(message: string, status: number, code?: string, fields?: {
|
|
19
|
+
field: string;
|
|
20
|
+
message: string;
|
|
21
|
+
}[]);
|
|
22
|
+
}
|
|
23
|
+
export interface ClientOptions {
|
|
24
|
+
token?: string | null;
|
|
25
|
+
fetch?: typeof fetch;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
userAgent?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class BoardClient {
|
|
30
|
+
readonly server: string;
|
|
31
|
+
private token;
|
|
32
|
+
private readonly doFetch;
|
|
33
|
+
private readonly timeoutMs;
|
|
34
|
+
private readonly userAgent;
|
|
35
|
+
constructor(server: string, options?: ClientOptions);
|
|
36
|
+
setToken(token: string | null): void;
|
|
37
|
+
hasToken(): boolean;
|
|
38
|
+
request<T>(method: 'GET' | 'POST' | 'PATCH' | 'DELETE', path: string, body?: unknown): Promise<T>;
|
|
39
|
+
describe(): Promise<InstanceDescriptor>;
|
|
40
|
+
search(query: Partial<JobQuery>): Promise<JobPage<Job> & {
|
|
41
|
+
query: JobQuery;
|
|
42
|
+
}>;
|
|
43
|
+
job(slug: string): Promise<{
|
|
44
|
+
job: Job;
|
|
45
|
+
html: string;
|
|
46
|
+
jsonld: unknown;
|
|
47
|
+
}>;
|
|
48
|
+
applySchema(slug: string): Promise<Record<string, unknown>>;
|
|
49
|
+
apply(slug: string, body: Record<string, unknown>): Promise<{
|
|
50
|
+
applicationId: string;
|
|
51
|
+
}>;
|
|
52
|
+
employers(): Promise<{
|
|
53
|
+
items: Organisation[];
|
|
54
|
+
}>;
|
|
55
|
+
me(): Promise<{
|
|
56
|
+
user: {
|
|
57
|
+
id: string;
|
|
58
|
+
email: string;
|
|
59
|
+
name: string | null;
|
|
60
|
+
isAdmin: boolean;
|
|
61
|
+
};
|
|
62
|
+
orgs: Organisation[];
|
|
63
|
+
resumes: {
|
|
64
|
+
slug: string;
|
|
65
|
+
title: string;
|
|
66
|
+
visibility: string;
|
|
67
|
+
updatedAt: string;
|
|
68
|
+
}[];
|
|
69
|
+
}>;
|
|
70
|
+
resumes(): Promise<{
|
|
71
|
+
items: {
|
|
72
|
+
slug: string;
|
|
73
|
+
title: string;
|
|
74
|
+
markdown: string;
|
|
75
|
+
}[];
|
|
76
|
+
}>;
|
|
77
|
+
saveResume(markdown: string, options?: {
|
|
78
|
+
slug?: string;
|
|
79
|
+
title?: string;
|
|
80
|
+
}): Promise<unknown>;
|
|
81
|
+
postJob(input: Record<string, unknown>): Promise<{
|
|
82
|
+
job: Job;
|
|
83
|
+
}>;
|
|
84
|
+
publishJob(slug: string): Promise<{
|
|
85
|
+
job: Job;
|
|
86
|
+
}>;
|
|
87
|
+
applications(slug: string): Promise<{
|
|
88
|
+
items: unknown[];
|
|
89
|
+
}>;
|
|
90
|
+
instances(): Promise<{
|
|
91
|
+
items: InstanceListing[];
|
|
92
|
+
}>;
|
|
93
|
+
searchNetwork(query: Partial<JobQuery>): Promise<unknown>;
|
|
94
|
+
startDeviceAuth(label: string): Promise<{
|
|
95
|
+
deviceCode: string;
|
|
96
|
+
userCode: string;
|
|
97
|
+
verifyUrl: string;
|
|
98
|
+
interval: number;
|
|
99
|
+
expiresAt: number;
|
|
100
|
+
}>;
|
|
101
|
+
pollDeviceAuth(deviceCode: string): Promise<{
|
|
102
|
+
status: 'pending' | 'approved' | 'expired';
|
|
103
|
+
token?: string;
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One client over the REST API, shared by the CLI, the TUI, the desktop app
|
|
3
|
+
* and the stdio MCP binary.
|
|
4
|
+
*
|
|
5
|
+
* There is deliberately no second HTTP layer anywhere in this repository. A
|
|
6
|
+
* command that builds its own request is a command that gets its own bugs
|
|
7
|
+
* around auth headers, error shapes and trailing slashes.
|
|
8
|
+
*/
|
|
9
|
+
import { queryToParams } from "../schema/query.js";
|
|
10
|
+
import { WELL_KNOWN_PATH } from "../schema/instance.js";
|
|
11
|
+
import { normaliseServer } from "./config.js";
|
|
12
|
+
export class ApiError extends Error {
|
|
13
|
+
// Written out rather than declared as constructor parameters: parameter
|
|
14
|
+
// properties are erasable-syntax-only violations, and this repo enforces
|
|
15
|
+
// that so the .ts half of it can still be run without a build.
|
|
16
|
+
status;
|
|
17
|
+
code;
|
|
18
|
+
fields;
|
|
19
|
+
constructor(message, status, code = 'error', fields = []) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'ApiError';
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.fields = fields;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class BoardClient {
|
|
28
|
+
server;
|
|
29
|
+
token;
|
|
30
|
+
doFetch;
|
|
31
|
+
timeoutMs;
|
|
32
|
+
userAgent;
|
|
33
|
+
constructor(server, options = {}) {
|
|
34
|
+
this.server = normaliseServer(server);
|
|
35
|
+
this.token = options.token ?? null;
|
|
36
|
+
this.doFetch = options.fetch ?? fetch;
|
|
37
|
+
this.timeoutMs = options.timeoutMs ?? 20_000;
|
|
38
|
+
this.userAgent = options.userAgent ?? 'agenticjobs-client';
|
|
39
|
+
}
|
|
40
|
+
setToken(token) {
|
|
41
|
+
this.token = token;
|
|
42
|
+
}
|
|
43
|
+
hasToken() {
|
|
44
|
+
return this.token !== null && this.token !== '';
|
|
45
|
+
}
|
|
46
|
+
async request(method, path, body) {
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
49
|
+
try {
|
|
50
|
+
const response = await this.doFetch(`${this.server}${path}`, {
|
|
51
|
+
method,
|
|
52
|
+
signal: controller.signal,
|
|
53
|
+
headers: {
|
|
54
|
+
accept: 'application/json',
|
|
55
|
+
'user-agent': this.userAgent,
|
|
56
|
+
...(this.token === null ? {} : { authorization: `Bearer ${this.token}` }),
|
|
57
|
+
...(body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
58
|
+
},
|
|
59
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
60
|
+
});
|
|
61
|
+
const raw = await response.text();
|
|
62
|
+
let parsed = null;
|
|
63
|
+
try {
|
|
64
|
+
parsed = raw === '' ? null : JSON.parse(raw);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// A board that answers HTML where JSON was asked for is usually a
|
|
68
|
+
// proxy or a login wall, and saying so beats "unexpected token <".
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new ApiError(`${this.server} answered ${response.status} with something that is not JSON. Is that a board?`, response.status);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const error = parsed?.error;
|
|
75
|
+
throw new ApiError(error?.message ?? `${this.server} answered ${response.status}.`, response.status, error?.code ?? 'error', error?.fields ?? []);
|
|
76
|
+
}
|
|
77
|
+
return parsed;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error instanceof ApiError)
|
|
81
|
+
throw error;
|
|
82
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
83
|
+
throw new ApiError(`${this.server} did not answer in time.`, 0, 'timeout');
|
|
84
|
+
}
|
|
85
|
+
throw new ApiError(`Could not reach ${this.server}: ${error instanceof Error ? error.message : String(error)}`, 0, 'unreachable');
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// --- reads ------------------------------------------------------------
|
|
92
|
+
async describe() {
|
|
93
|
+
return this.request('GET', WELL_KNOWN_PATH);
|
|
94
|
+
}
|
|
95
|
+
async search(query) {
|
|
96
|
+
const params = queryToParams({
|
|
97
|
+
q: null,
|
|
98
|
+
employmentType: null,
|
|
99
|
+
workplace: null,
|
|
100
|
+
seniority: null,
|
|
101
|
+
agentPolicy: null,
|
|
102
|
+
tags: [],
|
|
103
|
+
salaryMin: null,
|
|
104
|
+
org: null,
|
|
105
|
+
sort: 'recent',
|
|
106
|
+
limit: 25,
|
|
107
|
+
offset: 0,
|
|
108
|
+
...query,
|
|
109
|
+
});
|
|
110
|
+
const search = params.toString();
|
|
111
|
+
return this.request('GET', `/api/v1/jobs${search === '' ? '' : `?${search}`}`);
|
|
112
|
+
}
|
|
113
|
+
async job(slug) {
|
|
114
|
+
return this.request('GET', `/api/v1/jobs/${encodeURIComponent(slug)}`);
|
|
115
|
+
}
|
|
116
|
+
async applySchema(slug) {
|
|
117
|
+
return this.request('GET', `/api/v1/jobs/${encodeURIComponent(slug)}/apply-schema`);
|
|
118
|
+
}
|
|
119
|
+
async apply(slug, body) {
|
|
120
|
+
return this.request('POST', `/api/v1/jobs/${encodeURIComponent(slug)}/apply`, body);
|
|
121
|
+
}
|
|
122
|
+
async employers() {
|
|
123
|
+
return this.request('GET', '/api/v1/orgs');
|
|
124
|
+
}
|
|
125
|
+
async me() {
|
|
126
|
+
return this.request('GET', '/api/v1/me');
|
|
127
|
+
}
|
|
128
|
+
async resumes() {
|
|
129
|
+
return this.request('GET', '/api/v1/resumes');
|
|
130
|
+
}
|
|
131
|
+
async saveResume(markdown, options = {}) {
|
|
132
|
+
if (options.slug !== undefined) {
|
|
133
|
+
return this.request('PATCH', `/api/v1/resumes/${encodeURIComponent(options.slug)}`, {
|
|
134
|
+
markdown,
|
|
135
|
+
...(options.title === undefined ? {} : { title: options.title }),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return this.request('POST', '/api/v1/resumes', {
|
|
139
|
+
markdown,
|
|
140
|
+
...(options.title === undefined ? {} : { title: options.title }),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async postJob(input) {
|
|
144
|
+
return this.request('POST', '/api/v1/jobs', input);
|
|
145
|
+
}
|
|
146
|
+
async publishJob(slug) {
|
|
147
|
+
return this.request('POST', `/api/v1/jobs/${encodeURIComponent(slug)}/publish`);
|
|
148
|
+
}
|
|
149
|
+
async applications(slug) {
|
|
150
|
+
return this.request('GET', `/api/v1/jobs/${encodeURIComponent(slug)}/applications`);
|
|
151
|
+
}
|
|
152
|
+
// --- federation -------------------------------------------------------
|
|
153
|
+
async instances() {
|
|
154
|
+
return this.request('GET', '/api/v1/directory/instances');
|
|
155
|
+
}
|
|
156
|
+
async searchNetwork(query) {
|
|
157
|
+
const params = queryToParams({
|
|
158
|
+
q: null,
|
|
159
|
+
employmentType: null,
|
|
160
|
+
workplace: null,
|
|
161
|
+
seniority: null,
|
|
162
|
+
agentPolicy: null,
|
|
163
|
+
tags: [],
|
|
164
|
+
salaryMin: null,
|
|
165
|
+
org: null,
|
|
166
|
+
sort: 'recent',
|
|
167
|
+
limit: 25,
|
|
168
|
+
offset: 0,
|
|
169
|
+
...query,
|
|
170
|
+
});
|
|
171
|
+
const search = params.toString();
|
|
172
|
+
return this.request('GET', `/api/v1/directory/search${search === '' ? '' : `?${search}`}`);
|
|
173
|
+
}
|
|
174
|
+
// --- sign in ----------------------------------------------------------
|
|
175
|
+
async startDeviceAuth(label) {
|
|
176
|
+
return this.request('POST', '/api/v1/auth/device', { label });
|
|
177
|
+
}
|
|
178
|
+
async pollDeviceAuth(deviceCode) {
|
|
179
|
+
return this.request('POST', '/api/v1/auth/device/poll', { deviceCode });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the terminal keeps which boards it talks to, and the token for each.
|
|
3
|
+
*
|
|
4
|
+
* Several boards at once is the normal case, not a power feature: the whole
|
|
5
|
+
* point of the network is that a candidate watches a general board, two
|
|
6
|
+
* niche ones and their employer's own, and asks all of them one question.
|
|
7
|
+
* So the config is a map keyed by server URL with one marked current, and
|
|
8
|
+
* every command takes `--server` to address a different one.
|
|
9
|
+
*
|
|
10
|
+
* Tokens are credentials, so the file is 0600 and the directory 0700.
|
|
11
|
+
*/
|
|
12
|
+
export interface BoardConfig {
|
|
13
|
+
server: string;
|
|
14
|
+
token: string | null;
|
|
15
|
+
email?: string | null;
|
|
16
|
+
/** The board's own name, cached so `boards` can list without a round trip. */
|
|
17
|
+
name?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface Config {
|
|
20
|
+
current: string | null;
|
|
21
|
+
boards: Record<string, BoardConfig>;
|
|
22
|
+
/** Directories to discover other boards through. */
|
|
23
|
+
directories: string[];
|
|
24
|
+
}
|
|
25
|
+
export declare function configPath(): string;
|
|
26
|
+
export declare function loadConfig(): Config;
|
|
27
|
+
export declare function saveConfig(config: Config): void;
|
|
28
|
+
export declare function normaliseServer(input: string): string;
|
|
29
|
+
export declare function currentBoard(config?: Config): BoardConfig | null;
|
|
30
|
+
export declare function rememberBoard(board: BoardConfig): Config;
|
|
31
|
+
export declare function forgetBoard(server: string): boolean;
|
|
32
|
+
export declare function rememberDirectory(url: string): void;
|
|
33
|
+
export declare const DEFAULT_SERVER = "https://agenticjobs.work";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the terminal keeps which boards it talks to, and the token for each.
|
|
3
|
+
*
|
|
4
|
+
* Several boards at once is the normal case, not a power feature: the whole
|
|
5
|
+
* point of the network is that a candidate watches a general board, two
|
|
6
|
+
* niche ones and their employer's own, and asks all of them one question.
|
|
7
|
+
* So the config is a map keyed by server URL with one marked current, and
|
|
8
|
+
* every command takes `--server` to address a different one.
|
|
9
|
+
*
|
|
10
|
+
* Tokens are credentials, so the file is 0600 and the directory 0700.
|
|
11
|
+
*/
|
|
12
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
const EMPTY = { current: null, boards: {}, directories: [] };
|
|
16
|
+
export function configPath() {
|
|
17
|
+
const base = process.env['AGENTICJOBS_CONFIG_DIR'] ??
|
|
18
|
+
process.env['XDG_CONFIG_HOME'] ??
|
|
19
|
+
join(homedir(), '.config');
|
|
20
|
+
return join(base, 'agenticjobs', 'config.json');
|
|
21
|
+
}
|
|
22
|
+
export function loadConfig() {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(readFileSync(configPath(), 'utf8'));
|
|
25
|
+
return {
|
|
26
|
+
current: parsed.current ?? null,
|
|
27
|
+
boards: parsed.boards ?? {},
|
|
28
|
+
directories: parsed.directories ?? [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// A missing or unreadable config is the first run, not an error.
|
|
33
|
+
return { ...EMPTY, boards: {}, directories: [] };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function saveConfig(config) {
|
|
37
|
+
const path = configPath();
|
|
38
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
39
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
40
|
+
try {
|
|
41
|
+
// writeFileSync only applies its mode when it creates the file, so a
|
|
42
|
+
// config written before this rule existed would keep its old permissions.
|
|
43
|
+
chmodSync(path, 0o600);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// A filesystem that refuses chmod is not a reason to fail.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function normaliseServer(input) {
|
|
50
|
+
const trimmed = input.trim().replace(/\/+$/, '');
|
|
51
|
+
if (/^https?:\/\//.test(trimmed))
|
|
52
|
+
return trimmed;
|
|
53
|
+
// A bare hostname is almost always meant as https; localhost almost never is.
|
|
54
|
+
const local = /^(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/.test(trimmed);
|
|
55
|
+
return `${local ? 'http' : 'https'}://${trimmed}`;
|
|
56
|
+
}
|
|
57
|
+
export function currentBoard(config = loadConfig()) {
|
|
58
|
+
if (config.current === null)
|
|
59
|
+
return null;
|
|
60
|
+
return config.boards[config.current] ?? null;
|
|
61
|
+
}
|
|
62
|
+
export function rememberBoard(board) {
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
const server = normaliseServer(board.server);
|
|
65
|
+
config.boards[server] = { ...board, server };
|
|
66
|
+
config.current = server;
|
|
67
|
+
saveConfig(config);
|
|
68
|
+
return config;
|
|
69
|
+
}
|
|
70
|
+
export function forgetBoard(server) {
|
|
71
|
+
const config = loadConfig();
|
|
72
|
+
const key = normaliseServer(server);
|
|
73
|
+
if (config.boards[key] === undefined)
|
|
74
|
+
return false;
|
|
75
|
+
delete config.boards[key];
|
|
76
|
+
if (config.current === key) {
|
|
77
|
+
// Falling back to any remaining board beats leaving `current` pointing at
|
|
78
|
+
// something that no longer exists, which every later command would trip on.
|
|
79
|
+
config.current = Object.keys(config.boards)[0] ?? null;
|
|
80
|
+
}
|
|
81
|
+
saveConfig(config);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
export function rememberDirectory(url) {
|
|
85
|
+
const config = loadConfig();
|
|
86
|
+
const key = normaliseServer(url);
|
|
87
|
+
if (!config.directories.includes(key))
|
|
88
|
+
config.directories.push(key);
|
|
89
|
+
saveConfig(config);
|
|
90
|
+
}
|
|
91
|
+
export const DEFAULT_SERVER = 'https://agenticjobs.work';
|
|
92
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/client/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAiB1C,MAAM,KAAK,GAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;AAErE,MAAM,UAAU,UAAU;IACxB,MAAM,IAAI,GACR,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC9B,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,CAAoB,CAAC;QACjF,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI;YAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;YAC3B,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;SACtC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;QACjE,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IACnD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7E,IAAI,CAAC;QACH,qEAAqE;QACrE,0EAA0E;QAC1E,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACjD,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACjD,8EAA8E;IAC9E,MAAM,KAAK,GAAG,2CAA2C,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxE,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,OAAO,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB,UAAU,EAAE;IACxD,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAkB;IAC9C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC;IAC7C,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACnD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,MAAM,CAAC,OAAO,KAAK,GAAG,EAAE,CAAC;QAC3B,0EAA0E;QAC1E,4EAA4E;QAC5E,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACzD,CAAC;IACD,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking every board you are signed in to, at once.
|
|
3
|
+
*
|
|
4
|
+
* The directory's federated search covers boards that opted into being listed.
|
|
5
|
+
* This covers the other case, which is the one a person actually has: three
|
|
6
|
+
* boards in their config, one of them private to their employer and listed
|
|
7
|
+
* nowhere. Same guarantees - parallel, per-board timeout, a board that fails
|
|
8
|
+
* is named rather than silently dropped.
|
|
9
|
+
*/
|
|
10
|
+
import type { Job, JobQuery } from '../schema/index.ts';
|
|
11
|
+
import { type BoardConfig } from './config.ts';
|
|
12
|
+
export interface FanoutHit {
|
|
13
|
+
job: Job;
|
|
14
|
+
server: string;
|
|
15
|
+
boardName: string;
|
|
16
|
+
url: string;
|
|
17
|
+
}
|
|
18
|
+
export interface FanoutSource {
|
|
19
|
+
server: string;
|
|
20
|
+
name: string;
|
|
21
|
+
ok: boolean;
|
|
22
|
+
count: number;
|
|
23
|
+
total: number;
|
|
24
|
+
ms: number;
|
|
25
|
+
error: string | null;
|
|
26
|
+
}
|
|
27
|
+
export interface FanoutResult {
|
|
28
|
+
jobs: FanoutHit[];
|
|
29
|
+
sources: FanoutSource[];
|
|
30
|
+
total: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function configuredBoards(): BoardConfig[];
|
|
33
|
+
export declare function searchEverywhere(boards: BoardConfig[], query: Partial<JobQuery>, options?: {
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
}): Promise<FanoutResult>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking every board you are signed in to, at once.
|
|
3
|
+
*
|
|
4
|
+
* The directory's federated search covers boards that opted into being listed.
|
|
5
|
+
* This covers the other case, which is the one a person actually has: three
|
|
6
|
+
* boards in their config, one of them private to their employer and listed
|
|
7
|
+
* nowhere. Same guarantees - parallel, per-board timeout, a board that fails
|
|
8
|
+
* is named rather than silently dropped.
|
|
9
|
+
*/
|
|
10
|
+
import { BoardClient } from "./client.js";
|
|
11
|
+
import { loadConfig } from "./config.js";
|
|
12
|
+
export function configuredBoards() {
|
|
13
|
+
const config = loadConfig();
|
|
14
|
+
return Object.values(config.boards);
|
|
15
|
+
}
|
|
16
|
+
export async function searchEverywhere(boards, query, options = {}) {
|
|
17
|
+
const sources = boards.map((board) => ({
|
|
18
|
+
server: board.server,
|
|
19
|
+
name: board.name ?? hostOf(board.server),
|
|
20
|
+
ok: false,
|
|
21
|
+
count: 0,
|
|
22
|
+
total: 0,
|
|
23
|
+
ms: 0,
|
|
24
|
+
error: null,
|
|
25
|
+
}));
|
|
26
|
+
const jobs = [];
|
|
27
|
+
await Promise.all(boards.map(async (board, index) => {
|
|
28
|
+
const source = sources[index];
|
|
29
|
+
if (source === undefined)
|
|
30
|
+
return;
|
|
31
|
+
const started = Date.now();
|
|
32
|
+
try {
|
|
33
|
+
const client = new BoardClient(board.server, {
|
|
34
|
+
token: board.token,
|
|
35
|
+
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
36
|
+
});
|
|
37
|
+
const page = await client.search(query);
|
|
38
|
+
for (const job of page.items) {
|
|
39
|
+
jobs.push({
|
|
40
|
+
job,
|
|
41
|
+
server: board.server,
|
|
42
|
+
boardName: source.name,
|
|
43
|
+
url: `${board.server}/jobs/${job.slug}`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
source.count = page.items.length;
|
|
47
|
+
source.total = page.total;
|
|
48
|
+
source.ok = true;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
source.error = error instanceof Error ? error.message : String(error);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
source.ms = Date.now() - started;
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
// Each board sorted its own page; "newest" across three boards is none of
|
|
58
|
+
// those orders, so it is redone here.
|
|
59
|
+
jobs.sort((a, b) => published(b.job) - published(a.job));
|
|
60
|
+
return {
|
|
61
|
+
jobs,
|
|
62
|
+
sources,
|
|
63
|
+
total: sources.reduce((sum, source) => sum + (source.ok ? source.total : 0), 0),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function published(job) {
|
|
67
|
+
return Date.parse(job.publishedAt ?? job.createdAt) || 0;
|
|
68
|
+
}
|
|
69
|
+
function hostOf(server) {
|
|
70
|
+
try {
|
|
71
|
+
return new URL(server).hostname;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return server;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=fanout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fanout.js","sourceRoot":"","sources":["../../src/client/fanout.ts"],"names":[],"mappings":"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"}
|