@ncukondo/gcal-cli 0.1.0 → 0.1.4
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 +185 -7
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -587628,6 +587628,28 @@ function formatEventListText(events) {
|
|
|
587628
587628
|
return lines.join(`
|
|
587629
587629
|
`);
|
|
587630
587630
|
}
|
|
587631
|
+
function formatSearchEventLine(event) {
|
|
587632
|
+
const date3 = getDateKey(event);
|
|
587633
|
+
const time3 = formatTimeRange(event).padEnd(11);
|
|
587634
|
+
if (event.all_day) {
|
|
587635
|
+
return `${date3} ${time3} ${event.title} (${event.calendar_name})`;
|
|
587636
|
+
}
|
|
587637
|
+
const tag = transparencyTag(event);
|
|
587638
|
+
return `${date3} ${time3} ${event.title} (${event.calendar_name}) ${tag}`;
|
|
587639
|
+
}
|
|
587640
|
+
function formatSearchResultText(query, events) {
|
|
587641
|
+
const count = events.length;
|
|
587642
|
+
const plural = count === 1 ? "event" : "events";
|
|
587643
|
+
if (count === 0)
|
|
587644
|
+
return `Found 0 events matching "${query}".`;
|
|
587645
|
+
const header = `Found ${count} ${plural} matching "${query}":`;
|
|
587646
|
+
const lines = [header, ""];
|
|
587647
|
+
for (const event of events) {
|
|
587648
|
+
lines.push(formatSearchEventLine(event));
|
|
587649
|
+
}
|
|
587650
|
+
return lines.join(`
|
|
587651
|
+
`);
|
|
587652
|
+
}
|
|
587631
587653
|
var CALENDAR_ID_MAX = 15;
|
|
587632
587654
|
var CALENDAR_ID_COL = 18;
|
|
587633
587655
|
function truncateId(id) {
|
|
@@ -587693,6 +587715,60 @@ var ERROR_CODE_EXIT_MAP = {
|
|
|
587693
587715
|
function errorCodeToExitCode(code) {
|
|
587694
587716
|
return ERROR_CODE_EXIT_MAP[code];
|
|
587695
587717
|
}
|
|
587718
|
+
// package.json
|
|
587719
|
+
var package_default = {
|
|
587720
|
+
name: "@ncukondo/gcal-cli",
|
|
587721
|
+
version: "0.1.4",
|
|
587722
|
+
type: "module",
|
|
587723
|
+
exports: {
|
|
587724
|
+
".": "./dist/index.js"
|
|
587725
|
+
},
|
|
587726
|
+
bin: {
|
|
587727
|
+
gcal: "./dist/index.js"
|
|
587728
|
+
},
|
|
587729
|
+
files: [
|
|
587730
|
+
"dist"
|
|
587731
|
+
],
|
|
587732
|
+
repository: {
|
|
587733
|
+
type: "git",
|
|
587734
|
+
url: "https://github.com/ncukondo/gcal-cli"
|
|
587735
|
+
},
|
|
587736
|
+
publishConfig: {
|
|
587737
|
+
access: "public"
|
|
587738
|
+
},
|
|
587739
|
+
scripts: {
|
|
587740
|
+
dev: "bun run src/index.ts",
|
|
587741
|
+
build: "bun build src/index.ts --outdir dist --target node",
|
|
587742
|
+
"build:bin": "bun build src/index.ts --compile --outfile gcal",
|
|
587743
|
+
test: "vitest",
|
|
587744
|
+
"test:unit": "vitest run src",
|
|
587745
|
+
"test:integration": "vitest run tests/integration",
|
|
587746
|
+
"test:e2e": "vitest run tests/e2e",
|
|
587747
|
+
"test:all": "vitest run",
|
|
587748
|
+
lint: "oxlint src tests",
|
|
587749
|
+
format: "oxfmt src tests",
|
|
587750
|
+
"format:check": "oxfmt --check src tests",
|
|
587751
|
+
typecheck: "tsc --noEmit",
|
|
587752
|
+
prepare: "husky || true",
|
|
587753
|
+
prepublishOnly: "bun run build"
|
|
587754
|
+
},
|
|
587755
|
+
dependencies: {
|
|
587756
|
+
commander: "^12.0.0",
|
|
587757
|
+
"date-fns": "^3.0.0",
|
|
587758
|
+
"date-fns-tz": "^3.0.0",
|
|
587759
|
+
googleapis: "^130.0.0",
|
|
587760
|
+
"smol-toml": "^1.0.0",
|
|
587761
|
+
zod: "^4.0.0-beta"
|
|
587762
|
+
},
|
|
587763
|
+
devDependencies: {
|
|
587764
|
+
"@types/bun": "latest",
|
|
587765
|
+
typescript: "^5.0.0",
|
|
587766
|
+
vitest: "^2.0.0",
|
|
587767
|
+
oxlint: "latest",
|
|
587768
|
+
oxfmt: "latest",
|
|
587769
|
+
husky: "^9.1.7"
|
|
587770
|
+
}
|
|
587771
|
+
};
|
|
587696
587772
|
|
|
587697
587773
|
// src/cli.ts
|
|
587698
587774
|
var FormatSchema = _enum(["text", "json"]);
|
|
@@ -587701,7 +587777,7 @@ function collect(value, previous) {
|
|
|
587701
587777
|
}
|
|
587702
587778
|
function createProgram() {
|
|
587703
587779
|
const program2 = new Command;
|
|
587704
|
-
program2.name("gcal").description("CLI tool for managing Google Calendar events").version(
|
|
587780
|
+
program2.name("gcal").description("CLI tool for managing Google Calendar events").version(package_default.version).option("-f, --format <format>", "Output format: text | json", "text").option("-c, --calendar <id>", "Target calendar ID (repeatable)", collect, []).option("-q, --quiet", "Minimal output", false).option("--tz, --timezone <zone>", "Timezone (e.g., Asia/Tokyo)");
|
|
587705
587781
|
program2.on("command:*", (operands) => {
|
|
587706
587782
|
process.stderr.write(`error: unknown command '${operands[0]}'
|
|
587707
587783
|
|
|
@@ -588140,9 +588216,16 @@ async function listEvents(api2, calendarId, calendarName, options) {
|
|
|
588140
588216
|
mapApiError(error);
|
|
588141
588217
|
}
|
|
588142
588218
|
}
|
|
588143
|
-
async function getEvent(api2, calendarId, calendarName, eventId) {
|
|
588219
|
+
async function getEvent(api2, calendarId, calendarName, eventId, timeZone) {
|
|
588144
588220
|
try {
|
|
588145
|
-
const
|
|
588221
|
+
const params = {
|
|
588222
|
+
calendarId,
|
|
588223
|
+
eventId
|
|
588224
|
+
};
|
|
588225
|
+
if (timeZone) {
|
|
588226
|
+
params.timeZone = timeZone;
|
|
588227
|
+
}
|
|
588228
|
+
const response = await api2.events.get(params);
|
|
588146
588229
|
return normalizeEvent(response.data, calendarId, calendarName);
|
|
588147
588230
|
} catch (error) {
|
|
588148
588231
|
mapApiError(error);
|
|
@@ -588216,7 +588299,7 @@ function isGoogleApiError(error) {
|
|
|
588216
588299
|
return error instanceof Error && "code" in error && typeof error.code === "number";
|
|
588217
588300
|
}
|
|
588218
588301
|
function isAuthRequiredError(error) {
|
|
588219
|
-
return error instanceof ApiError && error.code === "AUTH_REQUIRED";
|
|
588302
|
+
return (error instanceof ApiError || error instanceof AuthError) && (error.code === "AUTH_REQUIRED" || error.code === "AUTH_EXPIRED");
|
|
588220
588303
|
}
|
|
588221
588304
|
function mapApiError(error) {
|
|
588222
588305
|
if (isGoogleApiError(error)) {
|
|
@@ -590427,6 +590510,65 @@ function parseDateTimeInZone(dateStr, timezone) {
|
|
|
590427
590510
|
}
|
|
590428
590511
|
|
|
590429
590512
|
// src/commands/search.ts
|
|
590513
|
+
var DEFAULT_SEARCH_DAYS = 30;
|
|
590514
|
+
async function handleSearch(opts) {
|
|
590515
|
+
const { api: api2, query, format: format3, calendars, timezone, write } = opts;
|
|
590516
|
+
const writeErr = opts.writeErr ?? (() => {});
|
|
590517
|
+
const now = new Date;
|
|
590518
|
+
const days = opts.days ?? DEFAULT_SEARCH_DAYS;
|
|
590519
|
+
const isNegativeDays = days < 0;
|
|
590520
|
+
let timeMin;
|
|
590521
|
+
let timeMax;
|
|
590522
|
+
if (opts.from && opts.to) {
|
|
590523
|
+
timeMin = formatDateTimeInZone(parseDateTimeInZone(opts.from, timezone), timezone);
|
|
590524
|
+
timeMax = formatDateTimeInZone(parseDateTimeInZone(opts.to + "T23:59:59", timezone), timezone);
|
|
590525
|
+
} else if (opts.from) {
|
|
590526
|
+
const startDate = parseDateTimeInZone(opts.from, timezone);
|
|
590527
|
+
timeMin = formatDateTimeInZone(startDate, timezone);
|
|
590528
|
+
const endDate = new Date(startDate.getTime());
|
|
590529
|
+
endDate.setDate(endDate.getDate() + days);
|
|
590530
|
+
timeMax = formatDateTimeInZone(endDate, timezone);
|
|
590531
|
+
} else if (opts.to) {
|
|
590532
|
+
timeMax = formatDateTimeInZone(parseDateTimeInZone(opts.to + "T23:59:59", timezone), timezone);
|
|
590533
|
+
timeMin = formatDateTimeInZone(now, timezone);
|
|
590534
|
+
} else if (isNegativeDays) {
|
|
590535
|
+
const pastDate = new Date(now.getTime());
|
|
590536
|
+
pastDate.setDate(pastDate.getDate() + days);
|
|
590537
|
+
timeMin = formatDateTimeInZone(pastDate, timezone);
|
|
590538
|
+
timeMax = formatDateTimeInZone(now, timezone);
|
|
590539
|
+
} else {
|
|
590540
|
+
timeMin = formatDateTimeInZone(now, timezone);
|
|
590541
|
+
const endDate = new Date(now.getTime());
|
|
590542
|
+
endDate.setDate(endDate.getDate() + days);
|
|
590543
|
+
timeMax = formatDateTimeInZone(endDate, timezone);
|
|
590544
|
+
}
|
|
590545
|
+
const displayFrom = timeMin.slice(0, 10);
|
|
590546
|
+
const displayTo = timeMax.slice(0, 10);
|
|
590547
|
+
writeErr(`Searching: ${displayFrom} to ${displayTo}`);
|
|
590548
|
+
const hasExplicitRange = opts.days !== undefined || opts.from !== undefined || opts.to !== undefined;
|
|
590549
|
+
if (!hasExplicitRange) {
|
|
590550
|
+
writeErr("Tip: Use --days <n> or --from/--to to change the search range.");
|
|
590551
|
+
}
|
|
590552
|
+
const results = await Promise.all(calendars.map((cal) => listEvents(api2, cal.id, cal.name, { timeMin, timeMax, q: query })));
|
|
590553
|
+
const allEvents = results.flat();
|
|
590554
|
+
const transparency = opts.busy ? "busy" : opts.free ? "free" : undefined;
|
|
590555
|
+
const filterOpts = { transparency };
|
|
590556
|
+
if (opts.confirmed !== undefined)
|
|
590557
|
+
filterOpts.confirmed = opts.confirmed;
|
|
590558
|
+
if (opts.includeTentative !== undefined)
|
|
590559
|
+
filterOpts.includeTentative = opts.includeTentative;
|
|
590560
|
+
const filtered = applyFilters(allEvents, filterOpts);
|
|
590561
|
+
if (format3 === "json") {
|
|
590562
|
+
write(formatJsonSuccess({
|
|
590563
|
+
query,
|
|
590564
|
+
events: filtered,
|
|
590565
|
+
count: filtered.length
|
|
590566
|
+
}));
|
|
590567
|
+
} else {
|
|
590568
|
+
write(formatSearchResultText(query, filtered));
|
|
590569
|
+
}
|
|
590570
|
+
return { exitCode: ExitCode.SUCCESS };
|
|
590571
|
+
}
|
|
590430
590572
|
function createSearchCommand() {
|
|
590431
590573
|
const cmd = new Command("search").description("Search events by keyword").argument("<query>", "Search query string");
|
|
590432
590574
|
cmd.option("--from <date>", "Start date for search range");
|
|
@@ -590447,9 +590589,9 @@ function createSearchCommand() {
|
|
|
590447
590589
|
|
|
590448
590590
|
// src/commands/show.ts
|
|
590449
590591
|
async function handleShow(opts) {
|
|
590450
|
-
const { api: api2, eventId, calendarId, calendarName, format: format3, write } = opts;
|
|
590592
|
+
const { api: api2, eventId, calendarId, calendarName, format: format3, timezone, write } = opts;
|
|
590451
590593
|
try {
|
|
590452
|
-
const event = await getEvent(api2, calendarId, calendarName, eventId);
|
|
590594
|
+
const event = await getEvent(api2, calendarId, calendarName, eventId, timezone);
|
|
590453
590595
|
if (format3 === "json") {
|
|
590454
590596
|
write(formatJsonSuccess({ event }));
|
|
590455
590597
|
} else {
|
|
@@ -592127,7 +592269,41 @@ ${url}`);
|
|
|
592127
592269
|
}
|
|
592128
592270
|
});
|
|
592129
592271
|
program2.addCommand(listCmd);
|
|
592130
|
-
|
|
592272
|
+
const searchCmd = createSearchCommand();
|
|
592273
|
+
searchCmd.action(async (query) => {
|
|
592274
|
+
const globalOpts = resolveGlobalOptions2(program2);
|
|
592275
|
+
const searchOpts = searchCmd.opts();
|
|
592276
|
+
try {
|
|
592277
|
+
const config2 = loadConfig(fsAdapter);
|
|
592278
|
+
const auth = await getAuthenticatedClient(fsAdapter);
|
|
592279
|
+
const calendarApi = import_googleapis2.google.calendar({ version: "v3", auth });
|
|
592280
|
+
const api2 = createGoogleCalendarApi(calendarApi);
|
|
592281
|
+
const timezone = resolveTimezone(globalOpts.timezone, config2.timezone);
|
|
592282
|
+
const calendars = selectCalendars(globalOpts.calendar, config2);
|
|
592283
|
+
const result = await handleSearch({
|
|
592284
|
+
api: api2,
|
|
592285
|
+
query,
|
|
592286
|
+
format: globalOpts.format,
|
|
592287
|
+
calendars,
|
|
592288
|
+
timezone,
|
|
592289
|
+
days: searchOpts.days,
|
|
592290
|
+
from: searchOpts.from,
|
|
592291
|
+
to: searchOpts.to,
|
|
592292
|
+
busy: searchOpts.busy,
|
|
592293
|
+
free: searchOpts.free,
|
|
592294
|
+
confirmed: searchOpts.confirmed,
|
|
592295
|
+
includeTentative: searchOpts.includeTentative,
|
|
592296
|
+
write: (msg) => process.stdout.write(msg + `
|
|
592297
|
+
`),
|
|
592298
|
+
writeErr: (msg) => process.stderr.write(msg + `
|
|
592299
|
+
`)
|
|
592300
|
+
});
|
|
592301
|
+
process.exit(result.exitCode);
|
|
592302
|
+
} catch (error) {
|
|
592303
|
+
handleError2(error, globalOpts.format);
|
|
592304
|
+
}
|
|
592305
|
+
});
|
|
592306
|
+
program2.addCommand(searchCmd);
|
|
592131
592307
|
const showCmd = createShowCommand();
|
|
592132
592308
|
showCmd.action(async () => {
|
|
592133
592309
|
const globalOpts = resolveGlobalOptions2(program2);
|
|
@@ -592146,12 +592322,14 @@ ${url}`);
|
|
|
592146
592322
|
const enabled = config2.calendars.filter((c) => c.enabled);
|
|
592147
592323
|
cal = enabled[0] ?? { id: "primary", name: "Primary" };
|
|
592148
592324
|
}
|
|
592325
|
+
const timezone = resolveTimezone(globalOpts.timezone, config2.timezone);
|
|
592149
592326
|
const result = await handleShow({
|
|
592150
592327
|
api: api2,
|
|
592151
592328
|
eventId: showCmd.args[0],
|
|
592152
592329
|
calendarId: cal.id,
|
|
592153
592330
|
calendarName: cal.name,
|
|
592154
592331
|
format: globalOpts.format,
|
|
592332
|
+
timezone,
|
|
592155
592333
|
write: (msg) => process.stdout.write(msg + `
|
|
592156
592334
|
`)
|
|
592157
592335
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ncukondo/gcal-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js"
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"dist"
|
|
13
13
|
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/ncukondo/gcal-cli"
|
|
17
|
+
},
|
|
14
18
|
"publishConfig": {
|
|
15
19
|
"access": "public"
|
|
16
20
|
},
|