bamboohr-cli 1.0.21 → 1.0.22-g942e3c7.1
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/dist/index.js +111 -63
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -60,33 +60,77 @@ function readPackageVersion(importMetaUrl) {
|
|
|
60
60
|
|
|
61
61
|
// ../../cli-utils/dist/validators.js
|
|
62
62
|
import { InvalidArgumentError } from "commander";
|
|
63
|
+
function nonNegativeInt(raw) {
|
|
64
|
+
const n = parseInt(raw, 10);
|
|
65
|
+
if (Number.isNaN(n) || n < 0) {
|
|
66
|
+
throw new InvalidArgumentError("Must be a non-negative integer.");
|
|
67
|
+
}
|
|
68
|
+
return n;
|
|
69
|
+
}
|
|
63
70
|
function positiveInt(raw) {
|
|
64
71
|
const n = parseInt(raw, 10);
|
|
65
72
|
if (Number.isNaN(n) || n < 1) {
|
|
66
|
-
throw new InvalidArgumentError(
|
|
73
|
+
throw new InvalidArgumentError("Must be a positive integer.");
|
|
67
74
|
}
|
|
68
75
|
return n;
|
|
69
76
|
}
|
|
77
|
+
function text(raw) {
|
|
78
|
+
return raw;
|
|
79
|
+
}
|
|
80
|
+
function date(raw) {
|
|
81
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw);
|
|
82
|
+
if (!m) {
|
|
83
|
+
throw new InvalidArgumentError("Must be a date in YYYY-MM-DD format.");
|
|
84
|
+
}
|
|
85
|
+
const [, y, mo, d] = m;
|
|
86
|
+
const year = Number(y);
|
|
87
|
+
const month = Number(mo);
|
|
88
|
+
const day = Number(d);
|
|
89
|
+
const dt = new Date(Date.UTC(year, month - 1, day));
|
|
90
|
+
if (dt.getUTCFullYear() !== year || dt.getUTCMonth() !== month - 1 || dt.getUTCDate() !== day) {
|
|
91
|
+
throw new InvalidArgumentError("Not a real calendar date.");
|
|
92
|
+
}
|
|
93
|
+
return raw;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ../../cli-utils/dist/json.js
|
|
97
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
98
|
+
function jsonShape(schema) {
|
|
99
|
+
return (raw) => {
|
|
100
|
+
let parsed;
|
|
101
|
+
try {
|
|
102
|
+
parsed = JSON.parse(raw);
|
|
103
|
+
} catch {
|
|
104
|
+
throw new InvalidArgumentError2("Must be valid JSON.");
|
|
105
|
+
}
|
|
106
|
+
const result = schema.safeParse(parsed);
|
|
107
|
+
if (!result.success) {
|
|
108
|
+
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
109
|
+
throw new InvalidArgumentError2(`Invalid JSON shape \u2014 ${issues}`);
|
|
110
|
+
}
|
|
111
|
+
return result.data;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
70
114
|
|
|
71
115
|
// ../../cli-utils/dist/text-or-file.js
|
|
72
116
|
import { readFileSync as readFileSync3 } from "fs";
|
|
73
|
-
import { InvalidArgumentError as
|
|
117
|
+
import { InvalidArgumentError as InvalidArgumentError3, Option } from "commander";
|
|
74
118
|
var STDIN_REF = "-";
|
|
75
119
|
function rejectStdinSentinel(value) {
|
|
76
120
|
if (value === "@-" || value === STDIN_REF) {
|
|
77
|
-
throw new
|
|
121
|
+
throw new InvalidArgumentError3(`"${value}" looks like a stdin redirect, which is not supported here. Pass the text directly, or read it from a file/stdin with the matching --\u2026-file <path|-> option.`);
|
|
78
122
|
}
|
|
79
123
|
return value;
|
|
80
124
|
}
|
|
81
125
|
function readFileOrStdin(ref) {
|
|
82
126
|
if (ref === STDIN_REF) {
|
|
83
127
|
if (process.stdin.isTTY) {
|
|
84
|
-
throw new
|
|
128
|
+
throw new InvalidArgumentError3('"--\u2026-file -" reads stdin, but stdin is a terminal (nothing piped).');
|
|
85
129
|
}
|
|
86
130
|
try {
|
|
87
131
|
return readFileSync3(0, "utf8");
|
|
88
132
|
} catch (err) {
|
|
89
|
-
throw new
|
|
133
|
+
throw new InvalidArgumentError3(`failed to read stdin for "--\u2026-file -": ${err.message}`);
|
|
90
134
|
}
|
|
91
135
|
}
|
|
92
136
|
try {
|
|
@@ -94,28 +138,29 @@ function readFileOrStdin(ref) {
|
|
|
94
138
|
} catch (err) {
|
|
95
139
|
const e = err;
|
|
96
140
|
if (e.code === "ENOENT") {
|
|
97
|
-
throw new
|
|
141
|
+
throw new InvalidArgumentError3(`file not found: "${ref}".`);
|
|
98
142
|
}
|
|
99
|
-
throw new
|
|
143
|
+
throw new InvalidArgumentError3(`failed to read file "${ref}": ${e.message}`);
|
|
100
144
|
}
|
|
101
145
|
}
|
|
102
146
|
function textOrFileOption(cmd, name, opts = {}) {
|
|
103
147
|
const label = `${name.charAt(0).toUpperCase()}${name.slice(1)} content`;
|
|
104
148
|
cmd.option(`--${name} <text>`, opts.description ?? label, rejectStdinSentinel);
|
|
105
|
-
cmd.addOption(new Option(`--${name}-file <path>`, `Read --${name} from a file, or "-" for stdin (mutually exclusive with --${name})`).conflicts(name));
|
|
149
|
+
cmd.addOption(new Option(`--${name}-file <path>`, `Read --${name} from a file, or "-" for stdin (mutually exclusive with --${name})`).conflicts(name).argParser(text));
|
|
106
150
|
return cmd;
|
|
107
151
|
}
|
|
108
152
|
function resolveTextOrFile(opts, name, { required = true } = {}) {
|
|
109
|
-
const
|
|
110
|
-
const
|
|
153
|
+
const record = opts;
|
|
154
|
+
const literal = record[name];
|
|
155
|
+
const ref = record[`${name}File`];
|
|
111
156
|
if (literal !== void 0 && ref !== void 0) {
|
|
112
|
-
throw new
|
|
157
|
+
throw new InvalidArgumentError3(`--${name} and --${name}-file are mutually exclusive; provide only one.`);
|
|
113
158
|
}
|
|
114
159
|
if (ref !== void 0) {
|
|
115
160
|
return readFileOrStdin(ref);
|
|
116
161
|
}
|
|
117
162
|
if (literal === void 0 && required) {
|
|
118
|
-
throw new
|
|
163
|
+
throw new InvalidArgumentError3(`a ${name} is required: provide --${name} <text> or --${name}-file <path>.`);
|
|
119
164
|
}
|
|
120
165
|
return literal;
|
|
121
166
|
}
|
|
@@ -518,7 +563,7 @@ function directory(parent) {
|
|
|
518
563
|
|
|
519
564
|
// src/commands/employee/get.ts
|
|
520
565
|
function get(parent) {
|
|
521
|
-
const cmd = parent.command("get
|
|
566
|
+
const cmd = parent.command("get").description("Get employee data by ID (default: current user)").argument("[employeeId]", "Employee ID (0 or omitted = current user)", nonNegativeInt).option("--fields <fields>", "Comma-separated field names", text, "firstName,lastName,email,jobTitle").option("--include-future", "Include future-dated values from history fields").option("--with-photos", "Include the employee's signed photo URL and photoUploaded flag");
|
|
522
567
|
examples(cmd, [
|
|
523
568
|
"",
|
|
524
569
|
"123",
|
|
@@ -531,7 +576,7 @@ function get(parent) {
|
|
|
531
576
|
const client = getClient();
|
|
532
577
|
const fields2 = opts.withPhotos ? mergeFields(opts.fields, ["photoUrl", "photoUploaded"]) : opts.fields;
|
|
533
578
|
const result = await client.employees.get({
|
|
534
|
-
id: employeeId
|
|
579
|
+
id: employeeId !== void 0 ? String(employeeId) : "0",
|
|
535
580
|
fields: fields2,
|
|
536
581
|
onlyCurrent: !opts.includeFuture
|
|
537
582
|
});
|
|
@@ -551,12 +596,12 @@ function mergeFields(existing, extra) {
|
|
|
551
596
|
import { Option as Option2 } from "commander";
|
|
552
597
|
var GOAL_FILTERS = ["open", "closed", "all"];
|
|
553
598
|
function goals(parent) {
|
|
554
|
-
const cmd = parent.command("goals
|
|
599
|
+
const cmd = parent.command("goals").description("Get employee performance goals").argument("<employeeId>", "Employee ID (0 = current user)", nonNegativeInt).addOption(new Option2("--filter <filter>", "Filter goals by status").choices(GOAL_FILTERS).default("all"));
|
|
555
600
|
examples(cmd, ["123", ["123 --filter open", "only open goals"], ["123 --filter closed", "only closed goals"]]);
|
|
556
601
|
cmd.action(async (employeeId, opts) => {
|
|
557
602
|
const client = getClient();
|
|
558
603
|
const result = await client.goals.get({
|
|
559
|
-
employeeId,
|
|
604
|
+
employeeId: String(employeeId),
|
|
560
605
|
filter: opts.filter === "all" ? void 0 : opts.filter
|
|
561
606
|
});
|
|
562
607
|
output(transformGoals(result));
|
|
@@ -568,12 +613,12 @@ import { writeFileSync as writeFileSync2 } from "fs";
|
|
|
568
613
|
import { Option as Option3 } from "commander";
|
|
569
614
|
var PHOTO_SIZES = ["original", "large", "medium", "small", "xs", "tiny"];
|
|
570
615
|
function photo(parent) {
|
|
571
|
-
const cmd = parent.command("photo
|
|
616
|
+
const cmd = parent.command("photo").description("Download employee photo").argument("<employeeId>", "Employee ID (0 = current user)", nonNegativeInt).requiredOption("--output <path>", "File path to save photo", text).addOption(new Option3("--size <size>", "Photo size").choices(PHOTO_SIZES).default("medium"));
|
|
572
617
|
examples(cmd, [["123 --output ./photo.jpg", "medium size (default)"], "123 --output ./photo.jpg --size large"]);
|
|
573
618
|
cmd.action(async (employeeId, opts) => {
|
|
574
619
|
const client = getClient();
|
|
575
620
|
const buffer = await client.employees.getPhoto({
|
|
576
|
-
employeeId,
|
|
621
|
+
employeeId: String(employeeId),
|
|
577
622
|
size: opts.size
|
|
578
623
|
});
|
|
579
624
|
writeFileSync2(opts.output, buffer);
|
|
@@ -590,7 +635,7 @@ async function resolveEmployeeId(client, employeeId) {
|
|
|
590
635
|
|
|
591
636
|
// src/commands/employee/search.ts
|
|
592
637
|
function search(parent) {
|
|
593
|
-
const cmd = parent.command("search
|
|
638
|
+
const cmd = parent.command("search").description("Search and filter employees").argument("[query]", "Name fragment to search for", text).option("--supervisor <name>", 'Filter by supervisor name (use "me" for current user)', text).option("--department <name>", "Filter by department", text).option("--location <name>", "Filter by location", text).option("--division <name>", "Filter by division", text).option("--with-photos", "Include each result's signed photo URL and photoUploaded flag");
|
|
594
639
|
examples(cmd, [
|
|
595
640
|
"john",
|
|
596
641
|
"--supervisor me",
|
|
@@ -657,11 +702,11 @@ function registerEmployeeCommands(program) {
|
|
|
657
702
|
// src/commands/file/get.ts
|
|
658
703
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
659
704
|
function get2(parent) {
|
|
660
|
-
const cmd = parent.command("get
|
|
705
|
+
const cmd = parent.command("get").description("Download a company file").argument("<fileId>", "File ID", positiveInt).requiredOption("--output <path>", "File path to save to", text);
|
|
661
706
|
examples(cmd, ["42 --output ./handbook.pdf"]);
|
|
662
707
|
cmd.action(async (fileId, opts) => {
|
|
663
708
|
const client = getClient();
|
|
664
|
-
const buffer = await client.files.get({ fileId });
|
|
709
|
+
const buffer = await client.files.get({ fileId: String(fileId) });
|
|
665
710
|
writeFileSync3(opts.output, buffer);
|
|
666
711
|
output({ saved: true, path: opts.output, size: buffer.length });
|
|
667
712
|
});
|
|
@@ -745,59 +790,43 @@ function registerMetaCommands(program) {
|
|
|
745
790
|
|
|
746
791
|
// src/commands/timeoff/balance.ts
|
|
747
792
|
function balance(parent) {
|
|
748
|
-
const cmd = parent.command("balance
|
|
793
|
+
const cmd = parent.command("balance").description("Estimate time off balance (defaults to current user)").argument("[employeeId]", "Employee ID (0 or omitted = current user)", nonNegativeInt).option("--date <date>", "Future date to estimate balance for (YYYY-MM-DD)", date);
|
|
749
794
|
examples(cmd, ["", "123", ["--date 2026-06-30", "balance at a future date"]]);
|
|
750
795
|
cmd.action(async (employeeId, opts) => {
|
|
751
796
|
const client = getClient();
|
|
752
|
-
const id = await resolveEmployeeId(client, employeeId);
|
|
797
|
+
const id = await resolveEmployeeId(client, employeeId !== void 0 ? String(employeeId) : void 0);
|
|
753
798
|
const result = await client.timeOff.getBalance({ employeeId: id, date: opts.date });
|
|
754
799
|
output(transformTimeOffBalances(result));
|
|
755
800
|
});
|
|
756
801
|
}
|
|
757
802
|
|
|
758
803
|
// src/commands/timeoff/create.ts
|
|
759
|
-
import { InvalidArgumentError as
|
|
804
|
+
import { InvalidArgumentError as InvalidArgumentError5, Option as Option4 } from "commander";
|
|
805
|
+
import { z } from "zod";
|
|
760
806
|
|
|
761
807
|
// src/utils/timeoff-dates.ts
|
|
762
|
-
import { InvalidArgumentError as
|
|
808
|
+
import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
763
809
|
var WEEKDAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
764
810
|
var YMD = /^\d{4}-\d{2}-\d{2}$/;
|
|
765
|
-
function parseDatesJson(raw) {
|
|
766
|
-
let parsed;
|
|
767
|
-
try {
|
|
768
|
-
parsed = JSON.parse(raw);
|
|
769
|
-
} catch {
|
|
770
|
-
throw new InvalidArgumentError3(
|
|
771
|
-
`--dates is not valid JSON. Expected an array like '[{"ymd":"2026-03-21","amount":0.5}]'.`
|
|
772
|
-
);
|
|
773
|
-
}
|
|
774
|
-
const isEntry = (d) => typeof d === "object" && d !== null && typeof d.ymd === "string" && YMD.test(d.ymd) && typeof d.amount === "number" && Number.isFinite(d.amount) && d.amount >= 0;
|
|
775
|
-
if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isEntry)) {
|
|
776
|
-
throw new InvalidArgumentError3(
|
|
777
|
-
'--dates must be a non-empty JSON array of {"ymd":"YYYY-MM-DD","amount":<non-negative number>} objects.'
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
return parsed.map((d) => ({ ymd: d.ymd, amount: d.amount }));
|
|
781
|
-
}
|
|
782
811
|
function parseYmd(value, label) {
|
|
783
812
|
if (!YMD.test(value)) {
|
|
784
|
-
throw new
|
|
813
|
+
throw new InvalidArgumentError4(`${label} must be a YYYY-MM-DD date, got "${value}".`);
|
|
785
814
|
}
|
|
786
|
-
const
|
|
787
|
-
if (Number.isNaN(
|
|
788
|
-
throw new
|
|
815
|
+
const date2 = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
|
|
816
|
+
if (Number.isNaN(date2.getTime())) {
|
|
817
|
+
throw new InvalidArgumentError4(`${label} is not a valid date: "${value}".`);
|
|
789
818
|
}
|
|
790
|
-
return
|
|
819
|
+
return date2;
|
|
791
820
|
}
|
|
792
|
-
function toYmd(
|
|
793
|
-
return
|
|
821
|
+
function toYmd(date2) {
|
|
822
|
+
return date2.toISOString().slice(0, 10);
|
|
794
823
|
}
|
|
795
824
|
function buildTimeOffDates(params) {
|
|
796
825
|
const { start, end, units, defaultHours, holidays = [] } = params;
|
|
797
826
|
const startDate = parseYmd(start, "--start-date");
|
|
798
827
|
const endDate = parseYmd(end, "--end-date");
|
|
799
828
|
if (endDate < startDate) {
|
|
800
|
-
throw new
|
|
829
|
+
throw new InvalidArgumentError4(`--end-date ${end} is before --start-date ${start}.`);
|
|
801
830
|
}
|
|
802
831
|
const hoursByName = new Map(defaultHours.map((entry) => [entry.name, Number(entry.amount)]));
|
|
803
832
|
const fallbackHours = hoursByName.get("default") ?? 8;
|
|
@@ -813,12 +842,31 @@ function buildTimeOffDates(params) {
|
|
|
813
842
|
}
|
|
814
843
|
|
|
815
844
|
// src/commands/timeoff/create.ts
|
|
845
|
+
var datesSchema = z.array(
|
|
846
|
+
z.object({
|
|
847
|
+
// Reuse the shared `date` validator so an impossible calendar date (e.g. 2026-02-30)
|
|
848
|
+
// is rejected inside --dates JSON exactly as it is for --start-date/--end-date.
|
|
849
|
+
ymd: z.string().refine(
|
|
850
|
+
(v) => {
|
|
851
|
+
try {
|
|
852
|
+
date(v);
|
|
853
|
+
return true;
|
|
854
|
+
} catch {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
{ message: "must be a real YYYY-MM-DD date" }
|
|
859
|
+
),
|
|
860
|
+
amount: z.number().finite().nonnegative()
|
|
861
|
+
})
|
|
862
|
+
).min(1, "must be a non-empty array");
|
|
816
863
|
function create(parent) {
|
|
817
|
-
const cmd = parent.command("create
|
|
864
|
+
const cmd = parent.command("create").description("Create a time off request (defaults to current user)").argument("[employeeId]", "Employee ID (0 or omitted = current user)", nonNegativeInt).requiredOption("--start-date <date>", "Start date (YYYY-MM-DD)", date).requiredOption("--end-date <date>", "End date (YYYY-MM-DD)", date).requiredOption("--type-id <id>", 'Time off type ID (use "timeoff types" to find IDs)', positiveInt).addOption(
|
|
818
865
|
new Option4("--status <status>", "Request status").choices(["requested", "approved"]).default("requested")
|
|
819
866
|
).option(
|
|
820
867
|
"--dates <json>",
|
|
821
|
-
`Per-day amounts as JSON array, e.g. '[{"ymd":"2026-03-21","amount":0.5}]' for a half day. When omitted, generated automatically: 1 day (or the weekday's working hours) per working day, 0 on weekends and company holidays
|
|
868
|
+
`Per-day amounts as JSON array, e.g. '[{"ymd":"2026-03-21","amount":0.5}]' for a half day. When omitted, generated automatically: 1 day (or the weekday's working hours) per working day, 0 on weekends and company holidays`,
|
|
869
|
+
jsonShape(datesSchema)
|
|
822
870
|
);
|
|
823
871
|
textOrFileOption(cmd, "note", { description: "Note to include with the request" });
|
|
824
872
|
examples(cmd, [
|
|
@@ -835,11 +883,11 @@ function create(parent) {
|
|
|
835
883
|
cmd.action(
|
|
836
884
|
async (employeeId, opts) => {
|
|
837
885
|
const client = getClient();
|
|
838
|
-
const id = await resolveEmployeeId(client, employeeId);
|
|
886
|
+
const id = await resolveEmployeeId(client, employeeId !== void 0 ? String(employeeId) : void 0);
|
|
839
887
|
const note = resolveTextOrFile(opts, "note", { required: false });
|
|
840
888
|
let dates;
|
|
841
889
|
if (opts.dates) {
|
|
842
|
-
dates =
|
|
890
|
+
dates = opts.dates;
|
|
843
891
|
} else {
|
|
844
892
|
const [types2, whosOut2] = await Promise.all([
|
|
845
893
|
getTimeOffTypes(client),
|
|
@@ -847,7 +895,7 @@ function create(parent) {
|
|
|
847
895
|
]);
|
|
848
896
|
const type = types2.timeOffTypes.find((t) => Number(t.id) === opts.typeId);
|
|
849
897
|
if (!type) {
|
|
850
|
-
throw new
|
|
898
|
+
throw new InvalidArgumentError5(`Unknown time off type ID ${opts.typeId}. Run "timeoff types" to list IDs.`);
|
|
851
899
|
}
|
|
852
900
|
const holidays = whosOut2.filter((e) => e.type === "holiday").map((e) => ({ start: e.start, end: e.end }));
|
|
853
901
|
dates = buildTimeOffDates({
|
|
@@ -860,7 +908,7 @@ function create(parent) {
|
|
|
860
908
|
}
|
|
861
909
|
const total = dates.reduce((sum, d) => sum + d.amount, 0);
|
|
862
910
|
if (total <= 0) {
|
|
863
|
-
throw new
|
|
911
|
+
throw new InvalidArgumentError5(
|
|
864
912
|
opts.dates ? "The per-day amounts in --dates sum to 0; a request must claim more than 0 days/hours." : "The requested range contains no working days (weekends/company holidays only). Adjust the dates or pass --dates explicitly."
|
|
865
913
|
);
|
|
866
914
|
}
|
|
@@ -884,7 +932,7 @@ function create(parent) {
|
|
|
884
932
|
// src/commands/timeoff/requests.ts
|
|
885
933
|
import { Option as Option5 } from "commander";
|
|
886
934
|
function requests(parent) {
|
|
887
|
-
const cmd = parent.command("requests").description("Get time off requests (defaults to current user)").option("--request-id <id>", "Specific request ID", positiveInt).addOption(new Option5("--action <action>", "Access level filter").choices(["view", "approve"])).option("--employee-id <id>", "Filter by employee ID").option("--all", "Show all visible requests instead of just current user").option("--start-date <date>", "Start date (YYYY-MM-DD)").option("--end-date <date>", "End date (YYYY-MM-DD)").addOption(
|
|
935
|
+
const cmd = parent.command("requests").description("Get time off requests (defaults to current user)").option("--request-id <id>", "Specific request ID", positiveInt).addOption(new Option5("--action <action>", "Access level filter").choices(["view", "approve"])).option("--employee-id <id>", "Filter by employee ID (0 = current user)", nonNegativeInt).option("--all", "Show all visible requests instead of just current user").option("--start-date <date>", "Start date (YYYY-MM-DD)", date).option("--end-date <date>", "End date (YYYY-MM-DD)", date).addOption(
|
|
888
936
|
new Option5("--status <status>", "Filter by request status").choices([
|
|
889
937
|
"approved",
|
|
890
938
|
"denied",
|
|
@@ -892,7 +940,7 @@ function requests(parent) {
|
|
|
892
940
|
"requested",
|
|
893
941
|
"canceled"
|
|
894
942
|
])
|
|
895
|
-
).option("--type-id <typeId>", "Filter by time off type ID");
|
|
943
|
+
).option("--type-id <typeId>", "Filter by time off type ID", positiveInt);
|
|
896
944
|
examples(cmd, [
|
|
897
945
|
"",
|
|
898
946
|
"--status requested",
|
|
@@ -905,7 +953,7 @@ function requests(parent) {
|
|
|
905
953
|
const start = opts.startDate ?? `${year}-01-01`;
|
|
906
954
|
const end = opts.endDate ?? `${year}-12-31`;
|
|
907
955
|
const client = getClient();
|
|
908
|
-
let employeeId = opts.employeeId;
|
|
956
|
+
let employeeId = opts.employeeId !== void 0 ? String(opts.employeeId) : void 0;
|
|
909
957
|
if (!employeeId && !opts.all) {
|
|
910
958
|
employeeId = await resolveEmployeeId(client);
|
|
911
959
|
}
|
|
@@ -916,7 +964,7 @@ function requests(parent) {
|
|
|
916
964
|
start,
|
|
917
965
|
end,
|
|
918
966
|
status: opts.status,
|
|
919
|
-
type: opts.typeId
|
|
967
|
+
type: opts.typeId !== void 0 ? String(opts.typeId) : void 0
|
|
920
968
|
});
|
|
921
969
|
output(transformTimeOffRequests(result));
|
|
922
970
|
}
|
|
@@ -937,7 +985,7 @@ function types(parent) {
|
|
|
937
985
|
// src/commands/timeoff/update-status.ts
|
|
938
986
|
import { Option as Option6 } from "commander";
|
|
939
987
|
function updateStatus(parent) {
|
|
940
|
-
const cmd = parent.command("update-status
|
|
988
|
+
const cmd = parent.command("update-status").description("Update time off request status (approve, deny, cancel)").argument("<requestId>", "Time off request ID", positiveInt).addOption(
|
|
941
989
|
new Option6("--status <status>", "New status").choices(["approved", "denied", "canceled"]).makeOptionMandatory()
|
|
942
990
|
);
|
|
943
991
|
textOrFileOption(cmd, "note", { description: "Note to attach to the status change" });
|
|
@@ -951,7 +999,7 @@ function updateStatus(parent) {
|
|
|
951
999
|
const client = getClient();
|
|
952
1000
|
const note = resolveTextOrFile(opts, "note", { required: false });
|
|
953
1001
|
const result = await client.timeOff.updateRequestStatus({
|
|
954
|
-
requestId,
|
|
1002
|
+
requestId: String(requestId),
|
|
955
1003
|
status: opts.status,
|
|
956
1004
|
note
|
|
957
1005
|
});
|
|
@@ -962,7 +1010,7 @@ function updateStatus(parent) {
|
|
|
962
1010
|
|
|
963
1011
|
// src/commands/timeoff/whos-out.ts
|
|
964
1012
|
function whosOut(parent) {
|
|
965
|
-
const cmd = parent.command("whos-out").description("View who's out \u2014 upcoming time off and holidays").option("--start-date <date>", "Start date (YYYY-MM-DD, defaults to today)").option("--end-date <date>", "End date (YYYY-MM-DD, defaults to today)");
|
|
1013
|
+
const cmd = parent.command("whos-out").description("View who's out \u2014 upcoming time off and holidays").option("--start-date <date>", "Start date (YYYY-MM-DD, defaults to today)", date).option("--end-date <date>", "End date (YYYY-MM-DD, defaults to today)", date);
|
|
966
1014
|
examples(cmd, ["", ["--start-date 2026-03-20 --end-date 2026-04-03", "two-week window"]]);
|
|
967
1015
|
cmd.action(async (opts) => {
|
|
968
1016
|
const client = getClient();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bamboohr-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22-g942e3c7.1",
|
|
4
4
|
"publish": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"commander": "^13.1.0",
|
|
15
|
-
"
|
|
15
|
+
"zod": "^3.25.67",
|
|
16
|
+
"bamboohr-client": "1.0.24-g942e3c7.1"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"@types/node": "24.10.4",
|
|
@@ -32,8 +33,8 @@
|
|
|
32
33
|
"scripts": {
|
|
33
34
|
"build": "tsup",
|
|
34
35
|
"dev": "tsx src/index.ts",
|
|
35
|
-
"lint": "eslint src",
|
|
36
|
-
"lint:fix": "eslint src --fix",
|
|
36
|
+
"lint": "eslint src tests",
|
|
37
|
+
"lint:fix": "eslint src tests --fix",
|
|
37
38
|
"test": "vitest run",
|
|
38
39
|
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
39
40
|
}
|