@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Profullstack, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# agenticjobs
|
|
2
|
+
|
|
3
|
+
**A job board where the agent on each side has a person behind it.**
|
|
4
|
+
|
|
5
|
+
The next few years of hiring look like this: a candidate's agent finds the role and
|
|
6
|
+
drafts the application, an employer's agent writes the posting and reads what comes
|
|
7
|
+
back, and a person at each end decides what actually happens. Everything in this
|
|
8
|
+
repository is built around that sentence.
|
|
9
|
+
|
|
10
|
+
Which means two things most job boards get wrong:
|
|
11
|
+
|
|
12
|
+
**Nothing here is scraped.** Every listing was posted to this board by the employer.
|
|
13
|
+
An empty search means nobody posted that job, not that a crawler missed it. That is
|
|
14
|
+
the only way a search result is worth anything to an agent, which cannot tell a stale
|
|
15
|
+
aggregator entry from a real opening.
|
|
16
|
+
|
|
17
|
+
**Both ends have a human control point, and they are the same shape.** An employer's
|
|
18
|
+
agent writes a listing and it lands as a draft for a person to publish. A candidate's
|
|
19
|
+
agent writes an application and it lands as a draft for a person to send. Neither
|
|
20
|
+
side can be automated past its human, and neither side is asked to trust that the
|
|
21
|
+
other one was not.
|
|
22
|
+
|
|
23
|
+
Self-hosted, MIT, Postgres. One person, one account, both sides of the table.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
npx @profullstack/agenticjobs --help
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Every surface, one engine
|
|
30
|
+
|
|
31
|
+
| | |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| Web | Server-rendered, mobile first, works with JavaScript off |
|
|
34
|
+
| PWA | Installable, offline shell, no build step |
|
|
35
|
+
| REST | `/api/v1`, hand-written OpenAPI, reads need no credentials |
|
|
36
|
+
| MCP | `/api/mcp` over streamable HTTP, or `agenticjobs-mcp` over stdio |
|
|
37
|
+
| CLI | `agenticjobs search`, `apply`, `post`, `publish`, `drafts` |
|
|
38
|
+
| TUI | `agenticjobs tui`, both sides in one window |
|
|
39
|
+
| Desktop | Electron, signed in to the same boards as your terminal |
|
|
40
|
+
|
|
41
|
+
The MCP tools call the REST API rather than the database, so a tool call takes the
|
|
42
|
+
same code path and the same permission checks a browser request does. There is
|
|
43
|
+
deliberately no second read path anywhere in this codebase.
|
|
44
|
+
|
|
45
|
+
## For agents
|
|
46
|
+
|
|
47
|
+
Two requests to apply. No page to render, no form to guess.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# what this board is, and where everything lives
|
|
51
|
+
curl https://agenticjobs.work/.well-known/agenticjobs
|
|
52
|
+
|
|
53
|
+
# search it
|
|
54
|
+
curl "https://agenticjobs.work/api/v1/jobs?q=rust&workplace=remote&agentPolicy=welcome"
|
|
55
|
+
|
|
56
|
+
# the application form, as data
|
|
57
|
+
curl https://agenticjobs.work/api/v1/jobs/SLUG/apply-schema
|
|
58
|
+
|
|
59
|
+
# send it
|
|
60
|
+
curl -X POST https://agenticjobs.work/api/v1/jobs/SLUG/apply \
|
|
61
|
+
-H 'content-type: application/json' \
|
|
62
|
+
-d '{"name":"Ada","email":"ada@example.com","cover":"...",
|
|
63
|
+
"resume":"# Ada Lovelace\n\n- **Email**: ada@example.com\n",
|
|
64
|
+
"agent":{"name":"claude-opus-5","supervised":true}}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### The agent policy
|
|
68
|
+
|
|
69
|
+
Every listing declares one, and it is required:
|
|
70
|
+
|
|
71
|
+
| | |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `welcome` | Agent-written applications are fine. Nothing is asked. |
|
|
74
|
+
| `disclose` | Fine, but say so. The application carries a structured disclosure. |
|
|
75
|
+
| `human-only` | The employer is asking for something a person wrote. |
|
|
76
|
+
|
|
77
|
+
`human-only` is a request, not a control. No board can tell who wrote a cover letter,
|
|
78
|
+
and one that claims it can is selling something. Saying it plainly is worth more than
|
|
79
|
+
pretending to enforce it.
|
|
80
|
+
|
|
81
|
+
And disclosure is not evidence against a candidate. A board that collects the
|
|
82
|
+
disclosure and then quietly filters those applications out has broken the field for
|
|
83
|
+
everyone, because the next candidate learns to lie.
|
|
84
|
+
|
|
85
|
+
## Resumes are Markdown
|
|
86
|
+
|
|
87
|
+
A file a person can read, diff, and keep. A file an agent can write without being
|
|
88
|
+
taught a schema first.
|
|
89
|
+
|
|
90
|
+
```markdown
|
|
91
|
+
# Ada Lovelace
|
|
92
|
+
|
|
93
|
+
- **Email**: ada@example.com
|
|
94
|
+
- **Location**: London
|
|
95
|
+
|
|
96
|
+
## Experience
|
|
97
|
+
|
|
98
|
+
### Analytical Engine | London
|
|
99
|
+
Chief Programmer (1842 - 1843)
|
|
100
|
+
|
|
101
|
+
- Wrote the first published algorithm intended for a machine.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Upload a PDF, a Word file, plain text or Markdown and it is converted; what you keep
|
|
105
|
+
and what employers read is the Markdown, and you edit it before it goes anywhere. A
|
|
106
|
+
conversion nobody checks is a conversion nobody should trust.
|
|
107
|
+
|
|
108
|
+
The convention is [OpenResume.md](docs/openresume.md). The listing format is
|
|
109
|
+
[OpenJob](docs/openjob.md). Both are conventions anyone can implement, and neither
|
|
110
|
+
requires this software.
|
|
111
|
+
|
|
112
|
+
## Run your own
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone https://github.com/profullstack/agenticjobs
|
|
116
|
+
cd agenticjobs
|
|
117
|
+
cp .env.example .env # set SECRET and PUBLIC_URL
|
|
118
|
+
docker compose up -d
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
That is a board on http://localhost:8787 with its own Postgres. Migrations run at
|
|
122
|
+
boot behind an advisory lock, so `--scale app=3` is safe.
|
|
123
|
+
|
|
124
|
+
Without Docker:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pnpm install && pnpm build
|
|
128
|
+
pnpm migrate && pnpm seed
|
|
129
|
+
pnpm start
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Deploying to Railway: the repo has a `railway.json`, so add a Postgres and point a
|
|
133
|
+
service at this Dockerfile. `PUBLIC_URL` and `SECRET` are the two variables that
|
|
134
|
+
matter.
|
|
135
|
+
|
|
136
|
+
## Boards find each other
|
|
137
|
+
|
|
138
|
+
Instances are independent. Each one has its own database, its own domain and its own
|
|
139
|
+
rules, and any of them can list itself in a directory so people can find it:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
DIRECTORY_URL=https://agenticjobs.work
|
|
143
|
+
ANNOUNCE=true
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Your instance sends one field: its own URL. The directory then reads
|
|
147
|
+
`/.well-known/agenticjobs` from you directly, so nothing about your board is taken on
|
|
148
|
+
trust from the announcement. (A stream directory can trust an announcement that says
|
|
149
|
+
"I am playing something". A job board's announcement carries a company name and a job
|
|
150
|
+
count, and one that could claim someone else's would be a phishing tool on day one.)
|
|
151
|
+
|
|
152
|
+
A directory can then answer one question across every listed board at once. A board
|
|
153
|
+
that is slow or down is named in the results rather than silently dropped, so nobody
|
|
154
|
+
sees a shorter list with no explanation.
|
|
155
|
+
|
|
156
|
+
The same works from your terminal, for boards that are listed nowhere:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
agenticjobs login https://agenticjobs.work
|
|
160
|
+
agenticjobs login https://jobs.your-company.internal
|
|
161
|
+
agenticjobs search "staff engineer" --all # asks both, in parallel
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Both sides, from a terminal
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# looking
|
|
168
|
+
agenticjobs search "rust" --remote --agents
|
|
169
|
+
agenticjobs apply SLUG --resume ~/resume.md --draft # prepare, do not send
|
|
170
|
+
agenticjobs drafts # read what your agent wrote
|
|
171
|
+
agenticjobs submit <id> # you decide
|
|
172
|
+
|
|
173
|
+
# hiring, on the same account
|
|
174
|
+
agenticjobs post job.md --org acme # creates a draft
|
|
175
|
+
agenticjobs publish staff-engineer # you decide
|
|
176
|
+
agenticjobs applications staff-engineer
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Sign-in is the device flow: the terminal shows a short code, you approve it in a
|
|
180
|
+
browser, and no credential crosses the terminal. A terminal token can read and apply
|
|
181
|
+
and is never an administrator.
|
|
182
|
+
|
|
183
|
+
## Posting from myna
|
|
184
|
+
|
|
185
|
+
A job opening goes out with the rest of a launch:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
myna login jobs
|
|
189
|
+
myna jobs post opening.md
|
|
190
|
+
myna jobs applicants staff-engineer
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The board is an **explicit target**. It is never part of `myna post --to all`, because
|
|
194
|
+
a status update fanning out into a job opening at your company is not something you
|
|
195
|
+
can delete your way out of.
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
pnpm install
|
|
201
|
+
docker compose up -d db
|
|
202
|
+
pnpm build && pnpm migrate && pnpm seed
|
|
203
|
+
pnpm dev
|
|
204
|
+
pnpm test # 61 tests; the API suite needs the database above
|
|
205
|
+
pnpm typecheck
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The repository is one package with a build step. The build exists because Node cannot
|
|
209
|
+
strip types from `.tsx` at all, and the pages are Hono JSX. Everything else follows
|
|
210
|
+
the house pattern: pnpm, Node 24, raw SQL forward-only migrations, no ORM.
|
|
211
|
+
|
|
212
|
+
shadcn's token vocabulary and component anatomy are ported by hand into plain CSS
|
|
213
|
+
(`web/public/tokens.css`, `app.css`). There is no Tailwind and no React here, so the
|
|
214
|
+
components could not be used directly; what is ported is their structure, and a theme
|
|
215
|
+
from the shadcn theme editor pastes straight in.
|
|
216
|
+
|
|
217
|
+
## Licence
|
|
218
|
+
|
|
219
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Argument parsing, small enough to read.
|
|
3
|
+
*
|
|
4
|
+
* `--flag value`, `--flag=value`, `--bool`, `-x`, and `--` to stop parsing.
|
|
5
|
+
* Repeated flags collect into an array, because `--answer a=1 --answer b=2` is
|
|
6
|
+
* how the apply command takes fields.
|
|
7
|
+
*/
|
|
8
|
+
export interface Args {
|
|
9
|
+
command: string;
|
|
10
|
+
positional: string[];
|
|
11
|
+
flags: Record<string, string | boolean | string[]>;
|
|
12
|
+
}
|
|
13
|
+
export declare function parseArgs(argv: string[]): Args;
|
|
14
|
+
export declare function flagString(args: Args, ...names: string[]): string | undefined;
|
|
15
|
+
export declare function flagBool(args: Args, ...names: string[]): boolean;
|
|
16
|
+
export declare function flagList(args: Args, ...names: string[]): string[];
|
|
17
|
+
export declare function flagNumber(args: Args, ...names: string[]): number | undefined;
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Argument parsing, small enough to read.
|
|
3
|
+
*
|
|
4
|
+
* `--flag value`, `--flag=value`, `--bool`, `-x`, and `--` to stop parsing.
|
|
5
|
+
* Repeated flags collect into an array, because `--answer a=1 --answer b=2` is
|
|
6
|
+
* how the apply command takes fields.
|
|
7
|
+
*/
|
|
8
|
+
export function parseArgs(argv) {
|
|
9
|
+
const positional = [];
|
|
10
|
+
const flags = {};
|
|
11
|
+
let stopped = false;
|
|
12
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
13
|
+
const token = argv[index] ?? '';
|
|
14
|
+
if (stopped) {
|
|
15
|
+
positional.push(token);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (token === '--') {
|
|
19
|
+
stopped = true;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (token.startsWith('--')) {
|
|
23
|
+
const body = token.slice(2);
|
|
24
|
+
const equals = body.indexOf('=');
|
|
25
|
+
if (equals >= 0) {
|
|
26
|
+
set(flags, body.slice(0, equals), body.slice(equals + 1));
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const next = argv[index + 1];
|
|
30
|
+
if (next !== undefined && !next.startsWith('-')) {
|
|
31
|
+
set(flags, body, next);
|
|
32
|
+
index += 1;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
set(flags, body, true);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (token.startsWith('-') && token.length > 1) {
|
|
39
|
+
const body = token.slice(1);
|
|
40
|
+
const next = argv[index + 1];
|
|
41
|
+
if (next !== undefined && !next.startsWith('-')) {
|
|
42
|
+
set(flags, body, next);
|
|
43
|
+
index += 1;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
set(flags, body, true);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
positional.push(token);
|
|
50
|
+
}
|
|
51
|
+
return { command: positional.shift() ?? '', positional, flags };
|
|
52
|
+
}
|
|
53
|
+
function set(flags, key, value) {
|
|
54
|
+
const existing = flags[key];
|
|
55
|
+
if (existing === undefined) {
|
|
56
|
+
flags[key] = value;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// Repeats collect. The first one having been a bare boolean is a typo, and
|
|
60
|
+
// dropping it silently is worse than keeping it as a string.
|
|
61
|
+
const list = Array.isArray(existing) ? existing : [String(existing)];
|
|
62
|
+
list.push(String(value));
|
|
63
|
+
flags[key] = list;
|
|
64
|
+
}
|
|
65
|
+
export function flagString(args, ...names) {
|
|
66
|
+
for (const name of names) {
|
|
67
|
+
const value = args.flags[name];
|
|
68
|
+
if (typeof value === 'string')
|
|
69
|
+
return value;
|
|
70
|
+
if (Array.isArray(value))
|
|
71
|
+
return value[value.length - 1];
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
export function flagBool(args, ...names) {
|
|
76
|
+
for (const name of names) {
|
|
77
|
+
const value = args.flags[name];
|
|
78
|
+
if (value === true)
|
|
79
|
+
return true;
|
|
80
|
+
if (typeof value === 'string')
|
|
81
|
+
return !['false', '0', 'no'].includes(value.toLowerCase());
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
export function flagList(args, ...names) {
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const name of names) {
|
|
88
|
+
const value = args.flags[name];
|
|
89
|
+
if (typeof value === 'string')
|
|
90
|
+
out.push(value);
|
|
91
|
+
else if (Array.isArray(value))
|
|
92
|
+
out.push(...value);
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
export function flagNumber(args, ...names) {
|
|
97
|
+
const raw = flagString(args, ...names);
|
|
98
|
+
if (raw === undefined)
|
|
99
|
+
return undefined;
|
|
100
|
+
const parsed = Number.parseInt(raw, 10);
|
|
101
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal formatting.
|
|
3
|
+
*
|
|
4
|
+
* The escape byte is built rather than written literally, so the source stays
|
|
5
|
+
* plain ASCII and nothing here can be mangled by a tool that strips control
|
|
6
|
+
* characters on the way through.
|
|
7
|
+
*
|
|
8
|
+
* Colour is applied only when someone is looking at it: piping to a file or
|
|
9
|
+
* into jq gets clean text, which is what makes `--json` and plain output both
|
|
10
|
+
* usable in a script.
|
|
11
|
+
*/
|
|
12
|
+
export declare const dim: (value: string) => string;
|
|
13
|
+
export declare const bold: (value: string) => string;
|
|
14
|
+
export declare const green: (value: string) => string;
|
|
15
|
+
export declare const red: (value: string) => string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal formatting.
|
|
3
|
+
*
|
|
4
|
+
* The escape byte is built rather than written literally, so the source stays
|
|
5
|
+
* plain ASCII and nothing here can be mangled by a tool that strips control
|
|
6
|
+
* characters on the way through.
|
|
7
|
+
*
|
|
8
|
+
* Colour is applied only when someone is looking at it: piping to a file or
|
|
9
|
+
* into jq gets clean text, which is what makes `--json` and plain output both
|
|
10
|
+
* usable in a script.
|
|
11
|
+
*/
|
|
12
|
+
const ESC = String.fromCharCode(27);
|
|
13
|
+
function paint(code, value) {
|
|
14
|
+
if (process.stdout.isTTY !== true)
|
|
15
|
+
return value;
|
|
16
|
+
if (process.env['NO_COLOR'] !== undefined)
|
|
17
|
+
return value;
|
|
18
|
+
return `${ESC}[${code}m${value}${ESC}[0m`;
|
|
19
|
+
}
|
|
20
|
+
export const dim = (value) => paint('2', value);
|
|
21
|
+
export const bold = (value) => paint('1', value);
|
|
22
|
+
export const green = (value) => paint('32', value);
|
|
23
|
+
export const red = (value) => paint('31', value);
|
|
24
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAEpC,SAAS,KAAK,CAAC,IAAY,EAAE,KAAa;IACxC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,GAAG,GAAG,IAAI,IAAI,IAAI,KAAK,GAAG,GAAG,KAAK,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AACjE,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACnE,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The terminal.
|
|
3
|
+
*
|
|
4
|
+
* The whole board is here, both halves of it: a candidate searches, prepares
|
|
5
|
+
* and releases applications; an employer posts, publishes and reads what came
|
|
6
|
+
* in. One account does both, because the person running an agency and the
|
|
7
|
+
* person looking for work are frequently the same person on a different
|
|
8
|
+
* afternoon.
|
|
9
|
+
*
|
|
10
|
+
* Every command goes through BoardClient, so the terminal and the browser are
|
|
11
|
+
* looking at the same board through the same API.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|