bamboohr-cli 1.0.8 → 1.0.10

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 (109) hide show
  1. package/dist/index.js +619 -31
  2. package/package.json +5 -4
  3. package/dist/commands/employee/directory.d.ts +0 -3
  4. package/dist/commands/employee/directory.d.ts.map +0 -1
  5. package/dist/commands/employee/directory.js +0 -15
  6. package/dist/commands/employee/directory.js.map +0 -1
  7. package/dist/commands/employee/get.d.ts +0 -3
  8. package/dist/commands/employee/get.d.ts.map +0 -1
  9. package/dist/commands/employee/get.js +0 -25
  10. package/dist/commands/employee/get.js.map +0 -1
  11. package/dist/commands/employee/goals.d.ts +0 -3
  12. package/dist/commands/employee/goals.d.ts.map +0 -1
  13. package/dist/commands/employee/goals.js +0 -22
  14. package/dist/commands/employee/goals.js.map +0 -1
  15. package/dist/commands/employee/index.d.ts +0 -3
  16. package/dist/commands/employee/index.d.ts.map +0 -1
  17. package/dist/commands/employee/index.js +0 -24
  18. package/dist/commands/employee/index.js.map +0 -1
  19. package/dist/commands/employee/photo.d.ts +0 -3
  20. package/dist/commands/employee/photo.d.ts.map +0 -1
  21. package/dist/commands/employee/photo.js +0 -27
  22. package/dist/commands/employee/photo.js.map +0 -1
  23. package/dist/commands/employee/search.d.ts +0 -3
  24. package/dist/commands/employee/search.d.ts.map +0 -1
  25. package/dist/commands/employee/search.js +0 -59
  26. package/dist/commands/employee/search.js.map +0 -1
  27. package/dist/commands/file/get.d.ts +0 -3
  28. package/dist/commands/file/get.d.ts.map +0 -1
  29. package/dist/commands/file/get.js +0 -19
  30. package/dist/commands/file/get.js.map +0 -1
  31. package/dist/commands/file/index.d.ts +0 -3
  32. package/dist/commands/file/index.d.ts.map +0 -1
  33. package/dist/commands/file/index.js +0 -15
  34. package/dist/commands/file/index.js.map +0 -1
  35. package/dist/commands/file/list.d.ts +0 -3
  36. package/dist/commands/file/list.d.ts.map +0 -1
  37. package/dist/commands/file/list.js +0 -14
  38. package/dist/commands/file/list.js.map +0 -1
  39. package/dist/commands/meta/departments.d.ts +0 -3
  40. package/dist/commands/meta/departments.d.ts.map +0 -1
  41. package/dist/commands/meta/departments.js +0 -16
  42. package/dist/commands/meta/departments.js.map +0 -1
  43. package/dist/commands/meta/divisions.d.ts +0 -3
  44. package/dist/commands/meta/divisions.d.ts.map +0 -1
  45. package/dist/commands/meta/divisions.js +0 -16
  46. package/dist/commands/meta/divisions.js.map +0 -1
  47. package/dist/commands/meta/fields.d.ts +0 -3
  48. package/dist/commands/meta/fields.d.ts.map +0 -1
  49. package/dist/commands/meta/fields.js +0 -15
  50. package/dist/commands/meta/fields.js.map +0 -1
  51. package/dist/commands/meta/index.d.ts +0 -3
  52. package/dist/commands/meta/index.d.ts.map +0 -1
  53. package/dist/commands/meta/index.js +0 -21
  54. package/dist/commands/meta/index.js.map +0 -1
  55. package/dist/commands/meta/locations.d.ts +0 -3
  56. package/dist/commands/meta/locations.d.ts.map +0 -1
  57. package/dist/commands/meta/locations.js +0 -16
  58. package/dist/commands/meta/locations.js.map +0 -1
  59. package/dist/commands/timeoff/balance.d.ts +0 -3
  60. package/dist/commands/timeoff/balance.d.ts.map +0 -1
  61. package/dist/commands/timeoff/balance.js +0 -21
  62. package/dist/commands/timeoff/balance.js.map +0 -1
  63. package/dist/commands/timeoff/create.d.ts +0 -3
  64. package/dist/commands/timeoff/create.d.ts.map +0 -1
  65. package/dist/commands/timeoff/create.js +0 -36
  66. package/dist/commands/timeoff/create.js.map +0 -1
  67. package/dist/commands/timeoff/index.d.ts +0 -3
  68. package/dist/commands/timeoff/index.d.ts.map +0 -1
  69. package/dist/commands/timeoff/index.js +0 -27
  70. package/dist/commands/timeoff/index.js.map +0 -1
  71. package/dist/commands/timeoff/requests.d.ts +0 -3
  72. package/dist/commands/timeoff/requests.d.ts.map +0 -1
  73. package/dist/commands/timeoff/requests.js +0 -51
  74. package/dist/commands/timeoff/requests.js.map +0 -1
  75. package/dist/commands/timeoff/types.d.ts +0 -3
  76. package/dist/commands/timeoff/types.d.ts.map +0 -1
  77. package/dist/commands/timeoff/types.js +0 -21
  78. package/dist/commands/timeoff/types.js.map +0 -1
  79. package/dist/commands/timeoff/update-status.d.ts +0 -3
  80. package/dist/commands/timeoff/update-status.d.ts.map +0 -1
  81. package/dist/commands/timeoff/update-status.js +0 -25
  82. package/dist/commands/timeoff/update-status.js.map +0 -1
  83. package/dist/commands/timeoff/whos-out.d.ts +0 -3
  84. package/dist/commands/timeoff/whos-out.d.ts.map +0 -1
  85. package/dist/commands/timeoff/whos-out.js +0 -22
  86. package/dist/commands/timeoff/whos-out.js.map +0 -1
  87. package/dist/index.d.ts +0 -3
  88. package/dist/index.d.ts.map +0 -1
  89. package/dist/index.js.map +0 -1
  90. package/dist/utils/cached.d.ts +0 -8
  91. package/dist/utils/cached.d.ts.map +0 -1
  92. package/dist/utils/cached.js +0 -17
  93. package/dist/utils/cached.js.map +0 -1
  94. package/dist/utils/client.d.ts +0 -3
  95. package/dist/utils/client.d.ts.map +0 -1
  96. package/dist/utils/client.js +0 -19
  97. package/dist/utils/client.js.map +0 -1
  98. package/dist/utils/output.d.ts +0 -3
  99. package/dist/utils/output.d.ts.map +0 -1
  100. package/dist/utils/output.js +0 -49
  101. package/dist/utils/output.js.map +0 -1
  102. package/dist/utils/resolve-employee.d.ts +0 -6
  103. package/dist/utils/resolve-employee.d.ts.map +0 -1
  104. package/dist/utils/resolve-employee.js +0 -10
  105. package/dist/utils/resolve-employee.js.map +0 -1
  106. package/dist/utils/strip-response.d.ts +0 -6
  107. package/dist/utils/strip-response.d.ts.map +0 -1
  108. package/dist/utils/strip-response.js +0 -29
  109. package/dist/utils/strip-response.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,32 +1,621 @@
1
1
  #!/usr/bin/env node
2
- import { styleText } from 'node:util';
3
- import { Command } from 'commander';
4
- import { registerEmployeeCommands } from './commands/employee/index.js';
5
- import { registerFileCommands } from './commands/file/index.js';
6
- import { registerMetaCommands } from './commands/meta/index.js';
7
- import { registerTimeoffCommands } from './commands/timeoff/index.js';
8
- import { handleError } from './utils/output.js';
9
- const DIM = '\x1b[2m';
10
- const RESET = '\x1b[0m';
11
- const program = new Command();
12
- program
13
- .name('bamboohr')
14
- .description('BambooHR CLI')
15
- .version('1.0.0')
16
- .configureHelp({
17
- styleTitle: (str) => styleText('bold', str),
18
- styleUsage: (str) => styleText('dim', str),
19
- styleCommandDescription: (str) => styleText('dim', str),
20
- styleOptionDescription: (str) => styleText('dim', str),
21
- styleSubcommandDescription: (str) => styleText('dim', str),
22
- })
23
- .addHelpText('beforeAll', `\n${styleText('bold', 'bamboohr')} ${DIM}— BambooHR CLI${RESET}\n`)
24
- .addHelpText('after', `
25
- ${styleText('bold', 'Environment:')}
2
+
3
+ // src/index.ts
4
+ import { styleText } from "util";
5
+ import { Command as Command6 } from "commander";
6
+
7
+ // ../../cli-utils/dist/cache.js
8
+ import { readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
9
+ import { homedir } from "os";
10
+ import { join } from "path";
11
+ var DEFAULT_TTL = 36e5;
12
+ function getCacheDir(name) {
13
+ return join(homedir(), ".cache", name);
14
+ }
15
+ function getCachePath(name, key) {
16
+ return join(getCacheDir(name), `${key}.json`);
17
+ }
18
+ function cacheGet(options, key) {
19
+ const ttl = options.ttl ?? DEFAULT_TTL;
20
+ const path = getCachePath(options.name, key);
21
+ try {
22
+ const stat = statSync(path);
23
+ if (Date.now() - stat.mtimeMs > ttl)
24
+ return null;
25
+ const raw = readFileSync(path, "utf-8");
26
+ const entry = JSON.parse(raw);
27
+ return entry.data;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ function cacheSet(options, key, data) {
33
+ const dir = getCacheDir(options.name);
34
+ const path = getCachePath(options.name, key);
35
+ const entry = { data, timestamp: Date.now() };
36
+ try {
37
+ mkdirSync(dir, { recursive: true });
38
+ writeFileSync(path, JSON.stringify(entry));
39
+ } catch {
40
+ }
41
+ }
42
+ async function cacheGetOrFetch(options, key, fetcher) {
43
+ const cached = cacheGet(options, key);
44
+ if (cached !== null)
45
+ return cached;
46
+ const data = await fetcher();
47
+ cacheSet(options, key, data);
48
+ return data;
49
+ }
50
+
51
+ // src/utils/cached.ts
52
+ var CACHE_NAME = "bamboohr";
53
+ var TWELVE_HOURS = 432e5;
54
+ var SEVEN_DAYS = 6048e5;
55
+ function getDirectory(client) {
56
+ return cacheGetOrFetch({ name: CACHE_NAME, ttl: TWELVE_HOURS }, "directory", () => client.employees.getDirectory());
57
+ }
58
+ function getTimeOffTypes(client) {
59
+ return cacheGetOrFetch({ name: CACHE_NAME, ttl: SEVEN_DAYS }, "timeoff-types", () => client.timeOff.getTypes());
60
+ }
61
+ function getMetaFields(client) {
62
+ return cacheGetOrFetch({ name: CACHE_NAME, ttl: SEVEN_DAYS }, "meta-fields", () => client.meta.getFields());
63
+ }
64
+
65
+ // src/utils/client.ts
66
+ import { BambooHRClient } from "bamboohr-client";
67
+
68
+ // src/utils/credentials.ts
69
+ function getCredentialInfo() {
70
+ const companyDomain = process.env.BAMBOO_COMPANY_DOMAIN;
71
+ const token = process.env.BAMBOO_TOKEN;
72
+ return {
73
+ environment: {
74
+ BAMBOO_COMPANY_DOMAIN: {
75
+ value: companyDomain ?? null,
76
+ description: 'Company subdomain (e.g., "mycompany" for mycompany.bamboohr.com)'
77
+ },
78
+ BAMBOO_TOKEN: {
79
+ value: token ? "<set>" : null,
80
+ description: "BambooHR API token"
81
+ }
82
+ },
83
+ tokenUrl: `https://${companyDomain ?? "<companyDomain>"}.bamboohr.com/app/settings/permissions/api_keys`,
84
+ hint: "Export environment variables in your shell profile (e.g., ~/.zshrc)."
85
+ };
86
+ }
87
+
88
+ // src/utils/client.ts
89
+ function getClient() {
90
+ const apiToken = process.env.BAMBOO_TOKEN;
91
+ const companyDomain = process.env.BAMBOO_COMPANY_DOMAIN;
92
+ if (!apiToken || !companyDomain) {
93
+ const missing = [...!apiToken ? ["BAMBOO_TOKEN"] : [], ...!companyDomain ? ["BAMBOO_COMPANY_DOMAIN"] : []];
94
+ process.stderr.write(
95
+ `${JSON.stringify({
96
+ error: `Missing required environment variables: ${missing.join(", ")}`,
97
+ ...getCredentialInfo()
98
+ })}
99
+ `
100
+ );
101
+ process.exit(1);
102
+ }
103
+ return new BambooHRClient({ apiToken, companyDomain });
104
+ }
105
+
106
+ // src/utils/strip-response.ts
107
+ function stripResponse(obj) {
108
+ if (Array.isArray(obj)) {
109
+ return obj.map(stripResponse);
110
+ }
111
+ if (obj === null || typeof obj !== "object") {
112
+ return obj;
113
+ }
114
+ const record = obj;
115
+ const result = {};
116
+ for (const [key, value] of Object.entries(record)) {
117
+ if (key === "self" || key === "_links" || key === "_expandable" || key === "expand" || key === "avatarUrls" || key === "avatarId" || key === "iconUrl") {
118
+ continue;
119
+ }
120
+ result[key] = stripResponse(value);
121
+ }
122
+ return result;
123
+ }
124
+
125
+ // src/utils/output.ts
126
+ function output(data) {
127
+ process.stdout.write(`${JSON.stringify(stripResponse(data), null, 2)}
128
+ `);
129
+ }
130
+ function handleError(err) {
131
+ const message = err instanceof Error ? err.message : String(err);
132
+ const axiosStatus = err?.response?.status;
133
+ if (axiosStatus === 400) {
134
+ const responseData = err?.response?.data;
135
+ const apiErrors = responseData && typeof responseData === "object" ? responseData : void 0;
136
+ process.stderr.write(
137
+ `${JSON.stringify({
138
+ error: "Bad request (HTTP 400)",
139
+ detail: apiErrors ?? message,
140
+ hint: "Check that all parameters are valid (employee IDs, field names, date formats, etc.)."
141
+ })}
142
+ `
143
+ );
144
+ } else if (axiosStatus === 401) {
145
+ process.stderr.write(
146
+ `${JSON.stringify({
147
+ error: "Authentication failed (HTTP 401)",
148
+ ...getCredentialInfo()
149
+ })}
150
+ `
151
+ );
152
+ } else if (axiosStatus === 403) {
153
+ const responseData = err?.response?.data;
154
+ process.stderr.write(
155
+ `${JSON.stringify({
156
+ error: "Forbidden (HTTP 403)",
157
+ detail: responseData && typeof responseData === "object" ? responseData : message,
158
+ hint: "Your account does not have permission for this operation. Check your BambooHR access level."
159
+ })}
160
+ `
161
+ );
162
+ } else if (axiosStatus === 404) {
163
+ process.stderr.write(
164
+ `${JSON.stringify({
165
+ error: "Not found (HTTP 404)",
166
+ hint: "Resource not found. Note: BambooHR also returns 404 for invalid credentials \u2014 verify that BAMBOO_TOKEN and BAMBOO_COMPANY_DOMAIN are correct."
167
+ })}
168
+ `
169
+ );
170
+ } else if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED") || message.includes("ECONNRESET")) {
171
+ process.stderr.write(
172
+ `${JSON.stringify({
173
+ error: `Cannot connect to BambooHR API: ${message}`,
174
+ hint: "Verify that BAMBOO_COMPANY_DOMAIN is correct and you have internet connectivity."
175
+ })}
176
+ `
177
+ );
178
+ } else {
179
+ process.stderr.write(`${JSON.stringify({ error: message })}
180
+ `);
181
+ }
182
+ process.exit(1);
183
+ }
184
+
185
+ // src/commands/employee/directory.ts
186
+ function directory(parent) {
187
+ parent.command("directory").description("Get the company employee directory").addHelpText("after", "\nExamples:\n $ bamboohr employee directory").action(async () => {
188
+ const client = getClient();
189
+ const result = await getDirectory(client);
190
+ output(result);
191
+ });
192
+ }
193
+
194
+ // src/commands/employee/get.ts
195
+ function get(parent) {
196
+ parent.command("get [id]").description("Get employee data by ID (default: current user)").option("--fields <fields>", "Comma-separated field names", "firstName,lastName,email,jobTitle").option("--include-future", "Include future-dated values from history fields").addHelpText(
197
+ "after",
198
+ `
199
+ Examples:
200
+ $ bamboohr employee get
201
+ $ bamboohr employee get 123
202
+ $ bamboohr employee get 123 --fields "firstName,lastName,department,hireDate"
203
+ $ bamboohr employee get 123 --include-future`
204
+ ).action(async (id, opts) => {
205
+ const client = getClient();
206
+ const result = await client.employees.get({
207
+ id: id ?? "0",
208
+ fields: opts.fields,
209
+ onlyCurrent: !opts.includeFuture
210
+ });
211
+ output(result);
212
+ });
213
+ }
214
+
215
+ // src/commands/employee/goals.ts
216
+ import { Option } from "commander";
217
+ function goals(parent) {
218
+ parent.command("goals <id>").description("Get employee performance goals").addOption(
219
+ new Option("--filter <filter>", "Filter goals by status").choices(["open", "closed", "all"]).default("all")
220
+ ).addHelpText(
221
+ "after",
222
+ `
223
+ Examples:
224
+ $ bamboohr employee goals 123
225
+ $ bamboohr employee goals 123 --filter open`
226
+ ).action(async (id, opts) => {
227
+ const client = getClient();
228
+ const result = await client.goals.get({
229
+ employeeId: id,
230
+ filter: opts.filter === "all" ? void 0 : opts.filter
231
+ });
232
+ output(result);
233
+ });
234
+ }
235
+
236
+ // src/commands/employee/photo.ts
237
+ import { writeFileSync as writeFileSync2 } from "fs";
238
+ import { Option as Option2 } from "commander";
239
+ function photo(parent) {
240
+ parent.command("photo <id>").description("Download employee photo").requiredOption("--output <path>", "File path to save photo").addOption(
241
+ new Option2("--size <size>", "Photo size").choices(["original", "large", "medium", "small", "xs", "tiny"]).default("medium")
242
+ ).addHelpText(
243
+ "after",
244
+ `
245
+ Examples:
246
+ $ bamboohr employee photo 123 --output ./photo.jpg
247
+ $ bamboohr employee photo 123 --output ./photo.jpg --size large`
248
+ ).action(async (id, opts) => {
249
+ const client = getClient();
250
+ const buffer = await client.employees.getPhoto({
251
+ employeeId: id,
252
+ size: opts.size
253
+ });
254
+ writeFileSync2(opts.output, buffer);
255
+ output({ saved: true, path: opts.output, size: buffer.length });
256
+ });
257
+ }
258
+
259
+ // src/utils/resolve-employee.ts
260
+ async function resolveEmployeeId(client, employeeId) {
261
+ if (employeeId) return employeeId;
262
+ const me = await client.employees.get({ id: "0", fields: "id" });
263
+ return me.id;
264
+ }
265
+
266
+ // src/commands/employee/search.ts
267
+ function search(parent) {
268
+ parent.command("search [query]").description("Search and filter employees").option("--supervisor <name>", 'Filter by supervisor name (use "me" for current user)').option("--department <name>", "Filter by department").option("--location <name>", "Filter by location").option("--division <name>", "Filter by division").addHelpText(
269
+ "after",
270
+ `
271
+ Examples:
272
+ bamboohr employee search john
273
+ bamboohr employee search --supervisor me
274
+ bamboohr employee search --department "R&D" --location Sofia
275
+ bamboohr employee search --supervisor "Borislav Mihaylov"`
276
+ ).action(
277
+ async (query, opts) => {
278
+ const client = getClient();
279
+ const dir = await getDirectory(client);
280
+ let matches = dir.employees;
281
+ if (query) {
282
+ const q = query.toLowerCase();
283
+ matches = matches.filter((e) => {
284
+ const name = `${e.firstName ?? ""} ${e.lastName ?? ""} ${e.preferredName ?? ""} ${e.displayName ?? ""}`.toLowerCase();
285
+ return name.includes(q);
286
+ });
287
+ }
288
+ if (opts.supervisor) {
289
+ let supervisorName = opts.supervisor;
290
+ if (supervisorName.toLowerCase() === "me") {
291
+ const myId = await resolveEmployeeId(client);
292
+ const me = dir.employees.find((e) => e.id === myId);
293
+ supervisorName = me?.displayName ?? `${me?.firstName ?? ""} ${me?.lastName ?? ""}`;
294
+ }
295
+ const s = supervisorName.toLowerCase();
296
+ matches = matches.filter((e) => (e.supervisor ?? "").toLowerCase().includes(s));
297
+ }
298
+ if (opts.department) {
299
+ const d = opts.department.toLowerCase();
300
+ matches = matches.filter((e) => (e.department ?? "").toLowerCase().includes(d));
301
+ }
302
+ if (opts.location) {
303
+ const l = opts.location.toLowerCase();
304
+ matches = matches.filter((e) => (e.location ?? "").toLowerCase().includes(l));
305
+ }
306
+ if (opts.division) {
307
+ const dv = opts.division.toLowerCase();
308
+ matches = matches.filter((e) => (e.division ?? "").toLowerCase().includes(dv));
309
+ }
310
+ output(matches);
311
+ }
312
+ );
313
+ }
314
+
315
+ // src/commands/employee/index.ts
316
+ function registerEmployeeCommands(program2) {
317
+ const employee = program2.command("employee").description("Employee operations").addHelpText(
318
+ "after",
319
+ `
320
+ Examples:
321
+ $ bamboohr employee get
322
+ $ bamboohr employee get 123 --fields "firstName,lastName,department"
323
+ $ bamboohr employee directory
324
+ $ bamboohr employee photo 123 --output ./photo.jpg
325
+ $ bamboohr employee goals 123 --filter open
326
+ `
327
+ );
328
+ get(employee);
329
+ search(employee);
330
+ directory(employee);
331
+ photo(employee);
332
+ goals(employee);
333
+ }
334
+
335
+ // src/commands/file/get.ts
336
+ import { writeFileSync as writeFileSync3 } from "fs";
337
+ function get2(parent) {
338
+ parent.command("get <fileId>").description("Download a company file").requiredOption("--output <path>", "File path to save to").addHelpText(
339
+ "after",
340
+ `
341
+ Examples:
342
+ $ bamboohr file get 42 --output ./handbook.pdf`
343
+ ).action(async (fileId, opts) => {
344
+ const client = getClient();
345
+ const buffer = await client.files.get({ fileId });
346
+ writeFileSync3(opts.output, buffer);
347
+ output({ saved: true, path: opts.output, size: buffer.length });
348
+ });
349
+ }
350
+
351
+ // src/commands/file/list.ts
352
+ function list(parent) {
353
+ parent.command("list").description("List all company files and categories").addHelpText("after", "\nExamples:\n $ bamboohr file list").action(async () => {
354
+ const client = getClient();
355
+ const result = await client.files.list();
356
+ output(result);
357
+ });
358
+ }
359
+
360
+ // src/commands/file/index.ts
361
+ function registerFileCommands(program2) {
362
+ const file = program2.command("file").description("Company file operations").addHelpText(
363
+ "after",
364
+ `
365
+ Examples:
366
+ $ bamboohr file list
367
+ $ bamboohr file get 42 --output ./handbook.pdf
368
+ `
369
+ );
370
+ list(file);
371
+ get2(file);
372
+ }
373
+
374
+ // src/commands/meta/departments.ts
375
+ function departments(parent) {
376
+ parent.command("departments").description("List all departments").addHelpText("after", "\nExamples:\n bamboohr meta departments").action(async () => {
377
+ const client = getClient();
378
+ const dir = await getDirectory(client);
379
+ const unique = [...new Set(dir.employees.map((e) => e.department).filter(Boolean))].sort();
380
+ output(unique);
381
+ });
382
+ }
383
+
384
+ // src/commands/meta/divisions.ts
385
+ function divisions(parent) {
386
+ parent.command("divisions").description("List all divisions").addHelpText("after", "\nExamples:\n bamboohr meta divisions").action(async () => {
387
+ const client = getClient();
388
+ const dir = await getDirectory(client);
389
+ const unique = [...new Set(dir.employees.map((e) => e.division).filter(Boolean))].sort();
390
+ output(unique);
391
+ });
392
+ }
393
+
394
+ // src/commands/meta/fields.ts
395
+ function fields(parent) {
396
+ parent.command("fields").description("List all available BambooHR fields").addHelpText("after", "\nExamples:\n $ bamboohr meta fields").action(async () => {
397
+ const client = getClient();
398
+ const result = await getMetaFields(client);
399
+ output(result);
400
+ });
401
+ }
402
+
403
+ // src/commands/meta/locations.ts
404
+ function locations(parent) {
405
+ parent.command("locations").description("List all office locations").addHelpText("after", "\nExamples:\n bamboohr meta locations").action(async () => {
406
+ const client = getClient();
407
+ const dir = await getDirectory(client);
408
+ const unique = [...new Set(dir.employees.map((e) => e.location).filter(Boolean))].sort();
409
+ output(unique);
410
+ });
411
+ }
412
+
413
+ // src/commands/meta/index.ts
414
+ function registerMetaCommands(program2) {
415
+ const meta = program2.command("meta").description("Metadata operations").addHelpText(
416
+ "after",
417
+ `
418
+ Examples:
419
+ $ bamboohr meta fields
420
+ $ bamboohr meta departments
421
+ $ bamboohr meta locations
422
+ $ bamboohr meta divisions
423
+ `
424
+ );
425
+ fields(meta);
426
+ departments(meta);
427
+ locations(meta);
428
+ divisions(meta);
429
+ }
430
+
431
+ // src/commands/timeoff/balance.ts
432
+ function balance(parent) {
433
+ parent.command("balance [employeeId]").description("Estimate time off balance (defaults to current user)").option("--date <date>", "Future date to estimate balance for (YYYY-MM-DD)").addHelpText(
434
+ "after",
435
+ `
436
+ Examples:
437
+ $ bamboohr timeoff balance
438
+ $ bamboohr timeoff balance 123
439
+ $ bamboohr timeoff balance --date 2026-06-30`
440
+ ).action(async (employeeId, opts) => {
441
+ const client = getClient();
442
+ const id = await resolveEmployeeId(client, employeeId);
443
+ const result = await client.timeOff.getBalance({ employeeId: id, date: opts.date });
444
+ output(result);
445
+ });
446
+ }
447
+
448
+ // src/commands/timeoff/create.ts
449
+ import { Option as Option3 } from "commander";
450
+ function create(parent) {
451
+ parent.command("create [employeeId]").description("Create a time off request (defaults to current user)").requiredOption("--start <date>", "Start date (YYYY-MM-DD)").requiredOption("--end <date>", "End date (YYYY-MM-DD)").requiredOption("--type-id <id>", 'Time off type ID (use "timeoff types" to find IDs)', parseInt).requiredOption("--amount <amount>", "Total amount of time off (in days or hours depending on type)", parseFloat).addOption(
452
+ new Option3("--status <status>", "Request status").choices(["requested", "approved"]).default("requested")
453
+ ).option("--note <text>", "Note to include with the request").option("--dates <json>", `Per-day amounts as JSON array, e.g. '[{"ymd":"2026-03-21","amount":4}]' for half-day`).addHelpText(
454
+ "after",
455
+ `
456
+ Examples:
457
+ $ bamboohr timeoff create --start 2026-04-01 --end 2026-04-01 --type-id 83 --amount 1
458
+ $ bamboohr timeoff create 504 --start 2026-04-01 --end 2026-04-01 --type-id 83 --amount 1 --note "Doctor appointment"`
459
+ ).action(
460
+ async (employeeId, opts) => {
461
+ const client = getClient();
462
+ const id = await resolveEmployeeId(client, employeeId);
463
+ const result = await client.timeOff.createRequest({
464
+ employeeId: id,
465
+ status: opts.status,
466
+ start: opts.start,
467
+ end: opts.end,
468
+ timeOffTypeId: opts.typeId,
469
+ amount: opts.amount,
470
+ notes: opts.note ? [{ from: "employee", note: opts.note }] : void 0,
471
+ dates: opts.dates ? JSON.parse(opts.dates) : void 0
472
+ });
473
+ output(result || { created: true, employeeId: id, start: opts.start, end: opts.end });
474
+ }
475
+ );
476
+ }
477
+
478
+ // src/commands/timeoff/requests.ts
479
+ import { Option as Option4 } from "commander";
480
+ function requests(parent) {
481
+ parent.command("requests").description("Get time off requests (defaults to current user)").option("--id <id>", "Specific request ID", parseInt).addOption(new Option4("--action <action>", "Access level filter").choices(["view", "approve"])).option("--employee <id>", "Filter by employee ID").option("--all", "Show all visible requests instead of just current user").option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)").addOption(
482
+ new Option4("--status <status>", "Filter by request status").choices([
483
+ "approved",
484
+ "denied",
485
+ "superceded",
486
+ "requested",
487
+ "canceled"
488
+ ])
489
+ ).option("--type <typeId>", "Filter by time off type ID").addHelpText(
490
+ "after",
491
+ `
492
+ Examples:
493
+ $ bamboohr timeoff requests
494
+ $ bamboohr timeoff requests --status requested
495
+ $ bamboohr timeoff requests --all --action approve --status requested
496
+ $ bamboohr timeoff requests --employee 123 --start 2026-01-01 --end 2026-03-31`
497
+ ).action(
498
+ async (opts) => {
499
+ const year = (/* @__PURE__ */ new Date()).getFullYear();
500
+ const start = opts.start ?? `${year}-01-01`;
501
+ const end = opts.end ?? `${year}-12-31`;
502
+ const client = getClient();
503
+ let employeeId = opts.employee;
504
+ if (!employeeId && !opts.all) {
505
+ employeeId = await resolveEmployeeId(client);
506
+ }
507
+ const result = await client.timeOff.getRequests({
508
+ id: opts.id,
509
+ action: opts.action,
510
+ employeeId,
511
+ start,
512
+ end,
513
+ status: opts.status,
514
+ type: opts.type
515
+ });
516
+ output(result);
517
+ }
518
+ );
519
+ }
520
+
521
+ // src/commands/timeoff/types.ts
522
+ function types(parent) {
523
+ parent.command("types").description("List available time off types").option("--requestable", "Only show types the user can request").addHelpText(
524
+ "after",
525
+ `
526
+ Examples:
527
+ $ bamboohr timeoff types
528
+ $ bamboohr timeoff types --requestable`
529
+ ).action(async (opts) => {
530
+ const client = getClient();
531
+ const result = opts.requestable ? await client.timeOff.getTypes({ mode: "request" }) : await getTimeOffTypes(client);
532
+ output(result);
533
+ });
534
+ }
535
+
536
+ // src/commands/timeoff/update-status.ts
537
+ import { Option as Option5 } from "commander";
538
+ function updateStatus(parent) {
539
+ parent.command("update-status <requestId>").description("Update time off request status (approve, deny, cancel)").addOption(
540
+ new Option5("--status <status>", "New status").choices(["approved", "denied", "canceled"]).makeOptionMandatory()
541
+ ).option("--note <text>", "Note to attach to the status change").addHelpText(
542
+ "after",
543
+ `
544
+ Examples:
545
+ $ bamboohr timeoff update-status 7890 --status approved
546
+ $ bamboohr timeoff update-status 7890 --status denied --note "Team at capacity that week"
547
+ $ bamboohr timeoff update-status 7890 --status canceled`
548
+ ).action(async (requestId, opts) => {
549
+ const client = getClient();
550
+ const result = await client.timeOff.updateRequestStatus({
551
+ requestId,
552
+ status: opts.status,
553
+ note: opts.note
554
+ });
555
+ output(result || { updated: true, requestId, status: opts.status });
556
+ });
557
+ }
558
+
559
+ // src/commands/timeoff/whos-out.ts
560
+ function whosOut(parent) {
561
+ parent.command("whos-out").description("View who's out \u2014 upcoming time off and holidays").option("--start <date>", "Start date (YYYY-MM-DD, defaults to today)").option("--end <date>", "End date (YYYY-MM-DD, defaults to 14 days from start)").addHelpText(
562
+ "after",
563
+ `
564
+ Examples:
565
+ $ bamboohr timeoff whos-out
566
+ $ bamboohr timeoff whos-out --start 2026-03-20 --end 2026-04-03`
567
+ ).action(async (opts) => {
568
+ const client = getClient();
569
+ const result = await client.timeOff.getWhosOut({
570
+ start: opts.start,
571
+ end: opts.end
572
+ });
573
+ output(result);
574
+ });
575
+ }
576
+
577
+ // src/commands/timeoff/index.ts
578
+ function registerTimeoffCommands(program2) {
579
+ const timeoff = program2.command("timeoff").description("Time off operations").addHelpText(
580
+ "after",
581
+ `
582
+ Examples:
583
+ $ bamboohr timeoff types --requestable
584
+ $ bamboohr timeoff create 504 --start 2026-04-01 --end 2026-04-01 --type-id 83 --amount 1
585
+ $ bamboohr timeoff requests --status requested --start 2026-01-01 --end 2026-12-31
586
+ $ bamboohr timeoff balance 504
587
+ $ bamboohr timeoff whos-out
588
+ $ bamboohr timeoff update-status 7890 --status approved
589
+ `
590
+ );
591
+ types(timeoff);
592
+ create(timeoff);
593
+ requests(timeoff);
594
+ balance(timeoff);
595
+ whosOut(timeoff);
596
+ updateStatus(timeoff);
597
+ }
598
+
599
+ // src/index.ts
600
+ var DIM = "\x1B[2m";
601
+ var RESET = "\x1B[0m";
602
+ var program = new Command6();
603
+ program.name("bamboohr").description("BambooHR CLI").version("1.0.0").configureHelp({
604
+ styleTitle: (str) => styleText("bold", str),
605
+ styleUsage: (str) => styleText("dim", str),
606
+ styleCommandDescription: (str) => styleText("dim", str),
607
+ styleOptionDescription: (str) => styleText("dim", str),
608
+ styleSubcommandDescription: (str) => styleText("dim", str)
609
+ }).addHelpText("beforeAll", `
610
+ ${styleText("bold", "bamboohr")} ${DIM}\u2014 BambooHR CLI${RESET}
611
+ `).addHelpText(
612
+ "after",
613
+ `
614
+ ${styleText("bold", "Environment:")}
26
615
  BAMBOO_TOKEN BambooHR API token ${DIM}(generate in BambooHR > Settings > API Keys)${RESET}
27
616
  BAMBOO_COMPANY_DOMAIN Company subdomain ${DIM}(e.g., "mycompany" for mycompany.bamboohr.com)${RESET}
28
617
 
29
- ${styleText('bold', 'Examples:')}
618
+ ${styleText("bold", "Examples:")}
30
619
  ${DIM}$${RESET} bamboohr employee get
31
620
  ${DIM}$${RESET} bamboohr employee get 123 --fields "firstName,lastName,department"
32
621
  ${DIM}$${RESET} bamboohr employee directory
@@ -39,15 +628,14 @@ ${styleText('bold', 'Examples:')}
39
628
  ${DIM}$${RESET} bamboohr timeoff update-status 7890 --status approved
40
629
  ${DIM}$${RESET} bamboohr file list
41
630
  ${DIM}$${RESET} bamboohr meta fields
42
- `);
631
+ `
632
+ );
43
633
  registerEmployeeCommands(program);
44
634
  registerTimeoffCommands(program);
45
635
  registerFileCommands(program);
46
636
  registerMetaCommands(program);
47
637
  try {
48
- await program.parseAsync();
49
- }
50
- catch (err) {
51
- handleError(err);
638
+ await program.parseAsync();
639
+ } catch (err) {
640
+ handleError(err);
52
641
  }
53
- //# sourceMappingURL=index.js.map