@codegame.dev/careerflow-mcp 1.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +398 -0
  3. package/dist/client.js +110 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/config.js +37 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/helpers.js +192 -0
  8. package/dist/helpers.js.map +1 -0
  9. package/dist/index.js +194 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/setup.js +296 -0
  12. package/dist/setup.js.map +1 -0
  13. package/dist/tools/admin.js +232 -0
  14. package/dist/tools/admin.js.map +1 -0
  15. package/dist/tools/benefit-funds.js +243 -0
  16. package/dist/tools/benefit-funds.js.map +1 -0
  17. package/dist/tools/chat-extra.js +172 -0
  18. package/dist/tools/chat-extra.js.map +1 -0
  19. package/dist/tools/chat.js +125 -0
  20. package/dist/tools/chat.js.map +1 -0
  21. package/dist/tools/cms.js +145 -0
  22. package/dist/tools/cms.js.map +1 -0
  23. package/dist/tools/crm-admin.js +269 -0
  24. package/dist/tools/crm-admin.js.map +1 -0
  25. package/dist/tools/crm.js +632 -0
  26. package/dist/tools/crm.js.map +1 -0
  27. package/dist/tools/dev-tickets.js +94 -0
  28. package/dist/tools/dev-tickets.js.map +1 -0
  29. package/dist/tools/forms.js +105 -0
  30. package/dist/tools/forms.js.map +1 -0
  31. package/dist/tools/hr.js +517 -0
  32. package/dist/tools/hr.js.map +1 -0
  33. package/dist/tools/kanban-extra.js +403 -0
  34. package/dist/tools/kanban-extra.js.map +1 -0
  35. package/dist/tools/misc.js +159 -0
  36. package/dist/tools/misc.js.map +1 -0
  37. package/dist/tools/projects.js +138 -0
  38. package/dist/tools/projects.js.map +1 -0
  39. package/dist/tools/tasks.js +342 -0
  40. package/dist/tools/tasks.js.map +1 -0
  41. package/dist/tools/time.js +129 -0
  42. package/dist/tools/time.js.map +1 -0
  43. package/dist/tools/workplace.js +110 -0
  44. package/dist/tools/workplace.js.map +1 -0
  45. package/dist/tools/workspace.js +199 -0
  46. package/dist/tools/workspace.js.map +1 -0
  47. package/package.json +61 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shahab Yousefi
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,398 @@
1
+ # CareerFlow MCP Server
2
+
3
+ Connect an AI assistant to [CareerFlow](https://careerflow.codegame.dev) so it can read and
4
+ manage your tasks, track your time, message colleagues, work CRM deals, handle
5
+ HR requests, and more — on your behalf.
6
+
7
+ Works with any MCP-capable client — Claude Code, Claude Desktop, and others.
8
+
9
+ > The server holds no privileges of its own. It calls the same REST API the web
10
+ > app uses, authenticated as you, so your existing role still decides what it can
11
+ > do — and the token's permissions narrow that further.
12
+
13
+ ---
14
+
15
+ ## Quick start
16
+
17
+ ### The one-command way
18
+
19
+ ```bash
20
+ npx @codegame.dev/careerflow-mcp init
21
+ ```
22
+
23
+ The setup wizard asks for your CareerFlow host and access token, detects which
24
+ MCP clients you have installed, and writes the configuration into Claude Code,
25
+ Claude Desktop, ZCode (Z.ai), or Cursor for you — or prints a ready-to-paste
26
+ JSON block for any other client. Token input is hidden as you type; it is never
27
+ passed on the command line.
28
+
29
+ Flags prefill the answers, so an onboarding page can print a one-liner per
30
+ company or client — only the token is still asked for (hidden):
31
+
32
+ ```bash
33
+ npx @codegame.dev/careerflow-mcp init --base-url https://acme.careerflow.example --name acme --client claude-code
34
+ npx @codegame.dev/careerflow-mcp init --base-url https://acme.careerflow.example --name acme --company-id 24 --client zcode
35
+ ```
36
+
37
+ - `--base-url` — the CareerFlow host, so it is never typed by hand
38
+ - `--name` — the server's name in the client config (default `careerflow`);
39
+ use one name per company or install, e.g. `acme`, `acme-staging`
40
+ - `--company-id` — optional default company
41
+ - `--client` — `claude-code`, `claude-desktop`, `zcode`, `cursor`, or `other`
42
+
43
+ None of these flags is secret, so the one-liner is safe to publish. The access
44
+ token is never accepted as a flag — argv is readable by other processes on the
45
+ machine — so the wizard always prompts for it with hidden input.
46
+
47
+ ### The manual way
48
+
49
+ 1. Create an access token.
50
+
51
+ In CareerFlow: **Settings → Access Tokens → Create access token**.
52
+
53
+ Give it a name, an expiry, and only the permissions the assistant needs.
54
+
55
+ > **The token is shown once.** It is stored hashed and cannot be recovered
56
+ > afterwards. Copy it before closing the dialog.
57
+
58
+ 2. Add it to your client.
59
+
60
+ **Claude Code**
61
+
62
+ ```bash
63
+ claude mcp add careerflow \
64
+ --env CAREERFLOW_BASE_URL=https://your-careerflow-host \
65
+ --env CAREERFLOW_TOKEN=cf_pat_xxxxxxxx \
66
+ --env CAREERFLOW_COMPANY_ID=24 \
67
+ -- npx -y @codegame.dev/careerflow-mcp
68
+ ```
69
+
70
+ **Claude Desktop** — add to `claude_desktop_config.json`:
71
+
72
+ ```json
73
+ {
74
+ "mcpServers": {
75
+ "careerflow": {
76
+ "command": "npx",
77
+ "args": ["-y", "@codegame.dev/careerflow-mcp"],
78
+ "env": {
79
+ "CAREERFLOW_BASE_URL": "https://your-careerflow-host",
80
+ "CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
81
+ "CAREERFLOW_COMPANY_ID": "24"
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ Config file locations:
89
+ - macOS — `~/Library/Application Support/Claude/claude_desktop_config.json`
90
+ - Windows — `%APPDATA%\Claude\claude_desktop_config.json`
91
+ - Linux — `~/.config/Claude/claude_desktop_config.json`
92
+
93
+ **ZCode (Z.ai)** — add to `~/.zcode/cli/config.json` (note the nested
94
+ `mcp.servers`, different from most clients' `mcpServers`):
95
+
96
+ ```json
97
+ {
98
+ "mcp": {
99
+ "servers": {
100
+ "careerflow": {
101
+ "type": "stdio",
102
+ "command": "npx",
103
+ "args": ["-y", "@codegame.dev/careerflow-mcp"],
104
+ "env": {
105
+ "CAREERFLOW_BASE_URL": "https://your-careerflow-host",
106
+ "CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
107
+ "CAREERFLOW_COMPANY_ID": "24"
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ For a project-shared server use `<repo>/.zcode/config.json` instead — but the
116
+ token would then sit in a versioned file; prefer user scope.
117
+
118
+ Restart the client. Ask it *"what are my tasks?"* to confirm the connection.
119
+
120
+ ### Configuration
121
+
122
+ | Variable | Required | Description |
123
+ | --- | --- | --- |
124
+ | `CAREERFLOW_BASE_URL` | yes | API root, e.g. `https://api.example.com` |
125
+ | `CAREERFLOW_TOKEN` | yes | Your personal access token (`cf_pat_…`) |
126
+ | `CAREERFLOW_COMPANY_ID` | no | Default company, so tools need no `company_id` per call |
127
+ | `CAREERFLOW_TIMEOUT_MS` | no | Request timeout, default `30000` |
128
+ | `CAREERFLOW_GROUPS` | no | Register only these comma-separated tool groups |
129
+ | `CAREERFLOW_EXCLUDE_GROUPS` | no | Register everything except these comma-separated groups |
130
+
131
+ The token is read only from the environment, never from command-line arguments —
132
+ argv is readable by other processes on the same machine.
133
+
134
+ ---
135
+
136
+ ## What you can ask for
137
+
138
+ **Daily work**
139
+ - "What am I working on today?" · "Anything overdue?"
140
+ - "Mark the login-bug task done."
141
+ - "Start a timer on task 412." · "Log 45 minutes on the report task."
142
+ - "Add a comment on task 88 saying the API is now deployed."
143
+ - "Add a checklist to task 12 with three items." · "Attach this link to task 40."
144
+
145
+ **Supervising a team**
146
+ - "What is Sara working on?" · "Show everything pending review."
147
+ - "Create a task 'Prepare Q3 report' in Sprint 12, assign it to Ali, due Friday, high priority."
148
+ - "Move task 91 to Done." · "Accept task 77 and score it 4.5."
149
+ - "Create a project called 'Website Redesign' and add Sara as manager."
150
+
151
+ **Sales & CRM**
152
+ - "Show my open deals." · "Create a lead for Acme Corp from the website form."
153
+ - "Move the Acme deal to Negotiation and set the amount to $12,000."
154
+ - "Log a call with the Beta Inc contact." · "What's in my pipeline this month?"
155
+
156
+ **HR & people ops**
157
+ - "Show my latest payroll slip." · "Accept my performance review."
158
+ - "List open job applications for the Backend Engineer posting."
159
+ - "Am I registered for the current loan lottery?" · "Check in my mood: 4, feeling good today."
160
+ - "Reserve lunch for tomorrow."
161
+
162
+ **Around the work**
163
+ - "Any new announcements?" · "Message Reza that the deploy finished."
164
+ - "Create a chat group for the launch team with Sara, Ali, and Reza."
165
+ - "Open a support ticket about the slow dashboard load."
166
+
167
+ ---
168
+
169
+ ## Tools
170
+
171
+ 164 tools. The everyday surface
172
+ ([only part of it can be registered](#registering-only-the-tool-groups-you-need)):
173
+
174
+ | Group | Tools |
175
+ | --- | --- |
176
+ | **Identity & structure** | `whoami`, `list_companies`, `list_projects`, `list_boards`, `list_project_members`, `find_user` |
177
+ | **Tasks** | `list_my_tasks`, `search_tasks`, `get_task`, `create_task`, `update_task`, `complete_task`, `assign_task`, `move_task`, `review_task`, `list_task_comments`, `add_task_comment` |
178
+ | **Kanban (boards, labels, checklists, attachments)** | `create_board`, `edit_board`, `archive_board`, `list_labels`, `create_label`, `delete_label`, `add_task_checklist`, `add_checklist_item`, `complete_checklist_item`, `delete_checklist`, `add_task_link_attachment`, `delete_task_attachment`, `archive_task`, `duplicate_task`, `list_recurring_tasks`, `create_recurring_task`, `delete_recurring_task`, `list_kanban_activity` |
179
+ | **Projects** | `create_project`, `update_project`, `archive_project`, `project_stats`, `invite_project_member`, `authorize_project_member` |
180
+ | **Time** | `start_timer`, `stop_timer`, `log_time`, `list_task_time` |
181
+ | **Chat** | `list_chats`, `read_messages`, `send_message`, `create_chat_group`, `edit_chat_group`, `manage_chat_group_members`, `list_chat_group_members`, `leave_chat_group`, `mute_chat`, `edit_chat_message`, `delete_chat_message`, `mark_message_seen` |
182
+ | **Workplace** | `list_announcements`, `list_surveys`, `list_my_attendance` |
183
+ | **Company & user admin** | `create_user`, `update_user`, `set_user_status`, `set_user_role`, `get_user`, `list_roles`, `get_organization_chart`, `get_company`, `list_organization_units` |
184
+ | **CRM — contacts, deals, activities** | `crm_search_contacts`, `crm_get_contact`, `crm_create_contact`, `crm_update_contact`, `crm_delete_contact`, `crm_contact_timeline`, `crm_search_deals`, `crm_get_deal`, `crm_create_deal`, `crm_update_deal`, `crm_delete_deal`, `crm_deal_stage_summary`, `crm_add_deal_next_step`, `crm_list_deal_line_items`, `crm_add_deal_line_item`, `crm_delete_deal_line_item`, `crm_search_activities`, `crm_create_activity`, `crm_complete_activity`, `crm_delete_activity`, `crm_dashboard_summary`, `crm_dashboard_pipeline` |
185
+ | **CRM — pipelines & config** | `crm_list_pipelines`, `crm_create_pipeline`, `crm_delete_pipeline`, `crm_list_stages`, `crm_create_stage`, `crm_delete_stage`, `crm_list_lead_sources`, `crm_create_lead_source`, `crm_list_tags`, `crm_create_tag`, `crm_assign_tag`, `crm_list_custom_fields`, `crm_create_custom_field`, `crm_settings` |
186
+ | **HR — payroll, discipline, careers, onboarding, cafeteria** | `list_my_payrolls`, `respond_to_payroll`, `list_my_performance_reviews`, `respond_to_performance_review`, `list_disciplinary_notes`, `estimate_disciplinary_penalty`, `create_disciplinary_note`, `list_administrative_notes`, `get_administrative_note`, `create_administrative_note`, `list_careers`, `list_career_requests`, `update_career_request_status`, `get_onboarding_checklist`, `set_onboarding_stage`, `list_food_schedules`, `reserve_food`, `list_my_food_reservations`, `mood_checkin` |
187
+ | **Benefit fund (loans & lotteries)** | `benefit_fund_my_dashboard`, `benefit_fund_hr_dashboard`, `list_benefit_fund_loan_types`, `list_benefit_fund_loans`, `request_benefit_fund_loan`, `update_benefit_fund_loan_status`, `pay_benefit_fund_loan_installment`, `list_benefit_fund_lotteries`, `register_for_lottery`, `respond_to_lottery_win`, `benefit_fund_settings`, `list_benefit_fund_membership_requests`, `request_benefit_fund_membership`, `review_benefit_fund_membership_request` |
188
+ | **Forms** | `list_forms`, `get_form`, `list_form_submissions`, `update_form_submission_status`, `message_form_submission` |
189
+ | **CMS** | `list_posts`, `get_post`, `create_post`, `update_post`, `delete_post`, `list_custom_pages`, `get_custom_page` |
190
+ | **Dev tickets (support)** | `list_dev_tickets`, `get_dev_ticket`, `create_dev_ticket`, `reply_to_dev_ticket` |
191
+ | **Misc (addresses, surveys, announcements, billing)** | `list_my_addresses`, `add_address`, `create_survey`, `delete_survey`, `list_survey_answers`, `answer_survey`, `create_announcement`, `delete_announcement`, `list_transactions`, `list_payment_orders` |
192
+
193
+ ---
194
+
195
+ ## Registering only the tool groups you need
196
+
197
+ Every registered tool's schema lands in the model's context, so a client that
198
+ loads all 164 tools spends real tokens on descriptions before you ask anything.
199
+ If the assistant only works one domain — a sales assistant, a task bot —
200
+ register just that slice:
201
+
202
+ ```json
203
+ {
204
+ "mcpServers": {
205
+ "careerflow": {
206
+ "command": "npx",
207
+ "args": ["-y", "@codegame.dev/careerflow-mcp"],
208
+ "env": {
209
+ "CAREERFLOW_BASE_URL": "https://your-careerflow-host",
210
+ "CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
211
+ "CAREERFLOW_GROUPS": "workspace,tasks,time"
212
+ }
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ Group names (matching rows in the table above, `crm` covering both CRM rows,
219
+ `chat` both chat modules):
220
+
221
+ `workspace`, `tasks`, `kanban`, `projects`, `time`, `chat`, `workplace`,
222
+ `admin`, `crm`, `hr`, `benefit-funds`, `forms`, `cms`, `dev-tickets`, `misc`
223
+
224
+ `CAREERFLOW_GROUPS` is an allowlist; `CAREERFLOW_EXCLUDE_GROUPS` removes the
225
+ named groups from the full set. An unknown name fails at startup with the valid
226
+ list. Group filtering is a context-size control only — the token's scopes still
227
+ decide what actually works.
228
+
229
+ ---
230
+
231
+ ## Permissions
232
+
233
+ Each token carries an explicit permission list. A tool call that needs a
234
+ permission the token lacks fails with a message naming the missing one — create a
235
+ new token with it added. The full list is shown on the token creation screen in
236
+ **Settings → Access Tokens → Create access token**; it's summarized here too.
237
+
238
+ Every scope comes in a `:read`/`:write` pair; some also accept a `:own` suffix
239
+ (e.g. `tasks:write:own`) that narrows the write to records the token's owner
240
+ created, owns, or is assigned to — ask CareerFlow support or check the token
241
+ creation screen if you want that narrower grant.
242
+
243
+ | Group | Scopes | Covers |
244
+ | --- | --- | --- |
245
+ | Personal | `profile`, `files` | your own profile, sessions, addresses, onboarding; file/image/video uploads |
246
+ | Work | `tasks`, `projects`, `time`, `chat` | kanban boards/tasks/checklists/labels, projects & membership, time tracking, chat & groups |
247
+ | CRM | `crm_contacts`, `crm_deals`, `crm_activities`, `crm_settings` | contacts/leads, deals, activities, and CRM configuration (pipelines, stages, tags, custom fields) |
248
+ | Company | `company`, `users`, `roles`, `attendance` | company profile & org chart, user admin, roles, attendance |
249
+ | HR | `surveys`, `announcements`, `forms`, `payroll`, `performance`, `benefit_fund`, `notes`, `careers` | surveys, announcements, form builder, payroll/performance slips, benefit fund loans & lotteries, disciplinary/administrative notes, recruiting |
250
+ | Content | `cms` | posts, custom pages, categories |
251
+ | Billing | `billing`, `dev_tickets` | payment/product orders, transactions, support tickets |
252
+ | Platform (staff-only) | `admin` | charts, logs, and other platform-admin surfaces — only issuable to CareerFlow staff accounts |
253
+
254
+ Grant the narrowest set that does the job. A read-only assistant needs only
255
+ `profile:read`, `tasks:read`, and `projects:read`.
256
+
257
+ ### Security notes
258
+
259
+ - A token acts as you, limited to its permissions. Treat it like a password.
260
+ - It cannot exceed your own role — the API enforces the same checks as the web app.
261
+ - A token cannot create, edit, or manage another token — token management is a
262
+ session-only action in the web app, so a leaked token cannot widen itself.
263
+ - Revoke any token you no longer use: **Settings → Access Tokens → Revoke**.
264
+ Revocation takes effect immediately.
265
+ - Several tools deliver something real and cannot be undone from inside the
266
+ tool call — `send_message`, `create_administrative_note`,
267
+ `message_form_submission` send to real people; `create_user`,
268
+ `set_user_status`, `set_user_role` change real accounts;
269
+ `request_benefit_fund_loan`, `update_benefit_fund_loan_status`,
270
+ `pay_benefit_fund_loan_installment`, and `respond_to_lottery_win` touch real
271
+ money. Their descriptions ask the assistant to confirm details with you
272
+ first — grant the underlying `:write` scope only if you want that
273
+ capability at all.
274
+
275
+ ---
276
+
277
+ ## Running from source
278
+
279
+ ```bash
280
+ git clone https://gitlab.com/dev.codegames/careerflow-mcp
281
+ cd careerflow-mcp
282
+ npm install
283
+ npm run build
284
+ ```
285
+
286
+ Point your client at the built entry point:
287
+
288
+ ```json
289
+ {
290
+ "mcpServers": {
291
+ "careerflow": {
292
+ "command": "node",
293
+ "args": ["/absolute/path/to/careerflow-mcp/dist/index.js"],
294
+ "env": { "CAREERFLOW_BASE_URL": "…", "CAREERFLOW_TOKEN": "…" }
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
300
+ ### Docker
301
+
302
+ ```bash
303
+ docker build -t careerflow-mcp .
304
+ ```
305
+
306
+ ```json
307
+ {
308
+ "mcpServers": {
309
+ "careerflow": {
310
+ "command": "docker",
311
+ "args": [
312
+ "run", "-i", "--rm",
313
+ "-e", "CAREERFLOW_BASE_URL",
314
+ "-e", "CAREERFLOW_TOKEN",
315
+ "-e", "CAREERFLOW_COMPANY_ID",
316
+ "careerflow-mcp"
317
+ ],
318
+ "env": {
319
+ "CAREERFLOW_BASE_URL": "https://your-careerflow-host",
320
+ "CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
321
+ "CAREERFLOW_COMPANY_ID": "24"
322
+ }
323
+ }
324
+ }
325
+ }
326
+ ```
327
+
328
+ `-i` is required: the server speaks MCP over stdin/stdout.
329
+
330
+ ---
331
+
332
+ ## Troubleshooting
333
+
334
+ **"CareerFlow rejected the access token"** — the token is revoked, expired, or
335
+ mistyped. Check it in Settings → Access Tokens, and create a new one if needed.
336
+
337
+ **"The access token does not carry the scope this action needs"** — the token is
338
+ missing a permission. Create a new one with that permission added.
339
+
340
+ **"Company ID is not valid"** — your role does not permit that read, or
341
+ `CAREERFLOW_COMPANY_ID` points at a company you do not belong to. Run `whoami` to
342
+ see which company you are in.
343
+
344
+ **Tools do not appear** — restart the client after editing its config. Verify the
345
+ server starts on its own:
346
+
347
+ ```bash
348
+ CAREERFLOW_TOKEN=cf_pat_xxx CAREERFLOW_BASE_URL=https://host npx @codegame.dev/careerflow-mcp
349
+ ```
350
+
351
+ It should print `careerflow-mcp connected (api: …)` to stderr and wait. Ctrl-C to exit.
352
+
353
+ ---
354
+
355
+ ## Requirements
356
+
357
+ - Node.js 18 or newer
358
+ - A CareerFlow instance with personal access tokens enabled (migration 99 applied)
359
+
360
+ ## Development
361
+
362
+ ```bash
363
+ npm run dev # tsc --watch
364
+ npm run typecheck # types only
365
+ npm run format # prettier
366
+ npm run smoke # boot the built server and verify tool registration
367
+ ```
368
+
369
+ ### Publishing
370
+
371
+ `npm run prepublishOnly` (typecheck, build, smoke) runs automatically on
372
+ `npm publish`. On GitLab, the `publish` job in `.gitlab-ci.yml` publishes when
373
+ a `v*` tag is pushed and the job is confirmed manually; it needs `NPM_TOKEN`
374
+ (set it as a masked CI/CD variable under Settings > CI/CD > Variables):
375
+
376
+ ```bash
377
+ git tag v1.1.0 && git push origin v1.1.0
378
+ # then confirm the `publish` job in CI/CD > Pipelines
379
+ ```
380
+
381
+ Or publish from a machine that is already verified with npm: `npm publish`.
382
+
383
+ ### Compatibility
384
+
385
+ This client tracks the CareerFlow REST API. Behaviours it deliberately works
386
+ around, worth knowing before changing a tool:
387
+
388
+ - `kanbans/tasks/edit`, `crm/deals/edit`, and `users/edit` all replace the
389
+ whole row — a field left out falls back to a default (or null) rather than
390
+ keeping its current value. `update_task`, `crm_update_deal`, and
391
+ `update_user` each read the record first and merge onto it, so a partial
392
+ change cannot silently clear untouched fields. Apply the same read-then-merge
393
+ pattern to any other endpoint discovered to have this behavior.
394
+ - Several endpoints reject an unscoped company-wide read unless the caller holds
395
+ an admin role, so tools pass the caller's own user id to mean "mine".
396
+ - A few `search` endpoints require a filter the API otherwise has no "all"
397
+ value for — e.g. `careers/requests/search` requires `status`. `list_career_requests`
398
+ reflects that by making the field required rather than optional.
package/dist/client.js ADDED
@@ -0,0 +1,110 @@
1
+ export class ApiError extends Error {
2
+ status;
3
+ errors;
4
+ constructor(message, status, errors) {
5
+ super(message);
6
+ this.status = status;
7
+ this.errors = errors;
8
+ this.name = "ApiError";
9
+ }
10
+ }
11
+ /**
12
+ * Flattens the API's nested error map into one readable line, so a tool result
13
+ * tells the caller which field was rejected and why instead of "request failed".
14
+ */
15
+ function describeErrors(body) {
16
+ if (body.error?.message) {
17
+ return body.error.message;
18
+ }
19
+ const errors = body.errors;
20
+ if (!errors || Array.isArray(errors)) {
21
+ return null;
22
+ }
23
+ const parts = [];
24
+ for (const [field, reasons] of Object.entries(errors)) {
25
+ if (reasons && typeof reasons === "object") {
26
+ for (const reason of Object.values(reasons)) {
27
+ parts.push(`${field}: ${reason}`);
28
+ }
29
+ }
30
+ }
31
+ return parts.length ? parts.join("; ") : null;
32
+ }
33
+ export class CareerFlowClient {
34
+ config;
35
+ constructor(config) {
36
+ this.config = config;
37
+ }
38
+ get defaultCompanyId() {
39
+ return this.config.companyId;
40
+ }
41
+ async get(path, query = {}) {
42
+ const url = new URL(`${this.config.baseUrl}/${path}`);
43
+ for (const [key, value] of Object.entries(query)) {
44
+ if (value !== undefined && value !== null && value !== "") {
45
+ url.searchParams.set(key, String(value));
46
+ }
47
+ }
48
+ return this.request(url.toString(), { method: "GET" });
49
+ }
50
+ async post(path, body = {}) {
51
+ const url = `${this.config.baseUrl}/${path}`;
52
+ const payload = {};
53
+ for (const [key, value] of Object.entries(body)) {
54
+ if (value !== undefined && value !== null) {
55
+ payload[key] = value;
56
+ }
57
+ }
58
+ return this.request(url, {
59
+ method: "POST",
60
+ headers: { "Content-Type": "application/json" },
61
+ body: JSON.stringify(payload)
62
+ });
63
+ }
64
+ async request(url, init) {
65
+ const controller = new AbortController();
66
+ const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);
67
+ let response;
68
+ try {
69
+ response = await fetch(url, {
70
+ ...init,
71
+ signal: controller.signal,
72
+ headers: {
73
+ ...(init.headers ?? {}),
74
+ token: this.config.token,
75
+ Accept: "application/json"
76
+ }
77
+ });
78
+ }
79
+ catch (error) {
80
+ if (error instanceof Error && error.name === "AbortError") {
81
+ throw new ApiError(`Request timed out after ${this.config.timeoutMs}ms`, 408);
82
+ }
83
+ throw new ApiError(`Could not reach CareerFlow at ${this.config.baseUrl}: ${error.message}`, 0);
84
+ }
85
+ finally {
86
+ clearTimeout(timer);
87
+ }
88
+ const text = await response.text();
89
+ let body;
90
+ try {
91
+ body = text ? JSON.parse(text) : { successful: false };
92
+ }
93
+ catch {
94
+ throw new ApiError(`CareerFlow returned a non-JSON response (HTTP ${response.status})`, response.status);
95
+ }
96
+ if (response.status === 401) {
97
+ throw new ApiError("CareerFlow rejected the access token. It may be revoked, expired, or mistyped.", 401, body.errors);
98
+ }
99
+ if (response.status === 403) {
100
+ throw new ApiError(describeErrors(body) ??
101
+ "The access token does not carry the scope this action needs. " +
102
+ "Re-create it in Settings > Access Tokens with the required scope.", 403, body.errors);
103
+ }
104
+ if (!response.ok || !body.successful) {
105
+ throw new ApiError(describeErrors(body) ?? `CareerFlow request failed (HTTP ${response.status})`, response.status, body.errors);
106
+ }
107
+ return body;
108
+ }
109
+ }
110
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/config.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Runtime configuration, read once at startup from the environment.
3
+ *
4
+ * The token is never accepted as a CLI argument: argv is visible to every other
5
+ * process on the machine, while the environment of a process is not.
6
+ */
7
+ function normaliseBaseUrl(raw) {
8
+ return raw.replace(/\/+$/, "");
9
+ }
10
+ export function loadConfig() {
11
+ const token = process.env.CAREERFLOW_TOKEN?.trim();
12
+ const baseUrl = process.env.CAREERFLOW_BASE_URL?.trim();
13
+ if (!token) {
14
+ throw new Error("CAREERFLOW_TOKEN is not set. Create a personal access token in CareerFlow " +
15
+ "under Settings > Access Tokens, then expose it to this server as CAREERFLOW_TOKEN.");
16
+ }
17
+ if (!baseUrl) {
18
+ throw new Error("CAREERFLOW_BASE_URL is not set. Point it at your CareerFlow API root, e.g. https://api.example.com");
19
+ }
20
+ const companyIdRaw = process.env.CAREERFLOW_COMPANY_ID?.trim();
21
+ const companyId = companyIdRaw ? Number(companyIdRaw) : undefined;
22
+ if (companyIdRaw && !Number.isFinite(companyId)) {
23
+ throw new Error(`CAREERFLOW_COMPANY_ID must be a number, got "${companyIdRaw}"`);
24
+ }
25
+ const timeoutRaw = process.env.CAREERFLOW_TIMEOUT_MS?.trim();
26
+ const timeoutMs = timeoutRaw ? Number(timeoutRaw) : 30_000;
27
+ if (timeoutRaw && !Number.isFinite(timeoutMs)) {
28
+ throw new Error(`CAREERFLOW_TIMEOUT_MS must be a number, got "${timeoutRaw}"`);
29
+ }
30
+ return {
31
+ baseUrl: normaliseBaseUrl(baseUrl),
32
+ token,
33
+ companyId,
34
+ timeoutMs
35
+ };
36
+ }
37
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,SAAS,gBAAgB,CAAC,GAAW;IACpC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAA;IAClD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE,CAAA;IAEvD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACd,4EAA4E;YAC3E,oFAAoF,CACrF,CAAA;IACF,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACd,oGAAoG,CACpG,CAAA;IACF,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,IAAI,EAAE,CAAA;IAC9D,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAEjE,IAAI,YAAY,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,gDAAgD,YAAY,GAAG,CAAC,CAAA;IACjF,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,IAAI,EAAE,CAAA;IAC5D,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAE1D,IAAI,UAAU,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,gDAAgD,UAAU,GAAG,CAAC,CAAA;IAC/E,CAAC;IAED,OAAO;QACN,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC;QAClC,KAAK;QACL,SAAS;QACT,SAAS;KACT,CAAA;AACF,CAAC"}