@ncukondo/gcal-cli 0.1.5 → 0.2.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 (2) hide show
  1. package/dist/index.js +130 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -587718,7 +587718,7 @@ function errorCodeToExitCode(code) {
587718
587718
  // package.json
587719
587719
  var package_default = {
587720
587720
  name: "@ncukondo/gcal-cli",
587721
- version: "0.1.5",
587721
+ version: "0.2.0",
587722
587722
  type: "module",
587723
587723
  exports: {
587724
587724
  ".": "./dist/index.js"
@@ -587872,9 +587872,58 @@ function getClientCredentials(fs) {
587872
587872
  }
587873
587873
  throw new AuthError("AUTH_REQUIRED", "No client credentials found. Place client_secret.json in ~/.config/gcal-cli/ or set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables.");
587874
587874
  }
587875
+ async function getClientCredentialsOrPrompt(fs, write, promptFn) {
587876
+ try {
587877
+ return getClientCredentials(fs);
587878
+ } catch (err) {
587879
+ if (!(err instanceof AuthError)) {
587880
+ throw err;
587881
+ }
587882
+ const { clientId, clientSecret } = await promptForClientCredentials(write, promptFn);
587883
+ saveClientCredentials(fs, clientId, clientSecret);
587884
+ return {
587885
+ clientId,
587886
+ clientSecret,
587887
+ redirectUri: DEFAULT_REDIRECT_URI
587888
+ };
587889
+ }
587890
+ }
587875
587891
  function getCredentialsPath() {
587876
587892
  return `${getCredentialsDir()}/credentials.json`;
587877
587893
  }
587894
+ async function promptForClientCredentials(write, promptFn) {
587895
+ write("No client credentials found.");
587896
+ write("");
587897
+ write("To set up Google Calendar API access:");
587898
+ write(" 1. Go to https://console.cloud.google.com");
587899
+ write(" 2. Create a project and enable the Google Calendar API");
587900
+ write(" 3. Create OAuth 2.0 credentials (Desktop app)");
587901
+ write(" 4. Copy the Client ID and Client Secret below");
587902
+ write("");
587903
+ const clientId = (await promptFn("Client ID: ")).trim();
587904
+ if (!clientId) {
587905
+ throw new AuthError("AUTH_REQUIRED", "Client ID is required.");
587906
+ }
587907
+ const clientSecret = (await promptFn("Client Secret: ")).trim();
587908
+ if (!clientSecret) {
587909
+ throw new AuthError("AUTH_REQUIRED", "Client Secret is required.");
587910
+ }
587911
+ return { clientId, clientSecret };
587912
+ }
587913
+ function saveClientCredentials(fs, clientId, clientSecret) {
587914
+ const dir = getCredentialsDir();
587915
+ fs.mkdirSync(dir, { recursive: true });
587916
+ const clientSecretPath = `${dir}/client_secret.json`;
587917
+ const data = {
587918
+ installed: {
587919
+ client_id: clientId,
587920
+ client_secret: clientSecret,
587921
+ redirect_uris: [DEFAULT_REDIRECT_URI]
587922
+ }
587923
+ };
587924
+ fs.writeFileSync(clientSecretPath, JSON.stringify(data, null, 2));
587925
+ fs.chmodSync(clientSecretPath, 384);
587926
+ }
587878
587927
  function loadTokens(fs) {
587879
587928
  const credPath = getCredentialsPath();
587880
587929
  if (!fs.existsSync(credPath)) {
@@ -588036,14 +588085,18 @@ async function fetchUserEmail(accessToken, fetchFn) {
588036
588085
  }
588037
588086
  }
588038
588087
  async function handleAuth(opts) {
588039
- const { fs, format, write, openUrl, fetchFn, startOAuthFlowFn = startOAuthFlow } = opts;
588088
+ const { fs, format, write, openUrl, fetchFn, startOAuthFlowFn = startOAuthFlow, promptFn } = opts;
588040
588089
  const tokens = loadTokens(fs);
588041
588090
  if (tokens) {
588042
588091
  return handleAuthStatus({ fs, format, write, fetchFn, cachedTokens: tokens });
588043
588092
  }
588044
588093
  let credentials;
588045
588094
  try {
588046
- credentials = getClientCredentials(fs);
588095
+ if (promptFn && format === "text") {
588096
+ credentials = await getClientCredentialsOrPrompt(fs, write, promptFn);
588097
+ } else {
588098
+ credentials = getClientCredentials(fs);
588099
+ }
588047
588100
  } catch (err) {
588048
588101
  if (err instanceof AuthError) {
588049
588102
  if (format === "json") {
@@ -591822,6 +591875,43 @@ async function handleUpdate(opts) {
591822
591875
  withTime.allDay = false;
591823
591876
  input.timeZone = timezone;
591824
591877
  }
591878
+ if (opts.dryRun) {
591879
+ const changes = {};
591880
+ if (input.title !== undefined)
591881
+ changes.title = input.title;
591882
+ if (input.description !== undefined)
591883
+ changes.description = input.description;
591884
+ if (input.transparency !== undefined)
591885
+ changes.transparency = input.transparency;
591886
+ const withTime = input;
591887
+ if (withTime.start !== undefined)
591888
+ changes.start = withTime.start;
591889
+ if (withTime.end !== undefined)
591890
+ changes.end = withTime.end;
591891
+ if (format3 === "json") {
591892
+ write(formatJsonSuccess({
591893
+ dry_run: true,
591894
+ action: "update",
591895
+ event_id: eventId,
591896
+ changes
591897
+ }));
591898
+ } else {
591899
+ const lines = [`DRY RUN: Would update event "${eventId}":`];
591900
+ if (changes.title !== undefined)
591901
+ lines.push(` title: "${changes.title}"`);
591902
+ if (changes.start !== undefined)
591903
+ lines.push(` start: "${changes.start}"`);
591904
+ if (changes.end !== undefined)
591905
+ lines.push(` end: "${changes.end}"`);
591906
+ if (changes.description !== undefined)
591907
+ lines.push(` description: "${changes.description}"`);
591908
+ if (changes.transparency !== undefined)
591909
+ lines.push(` transparency: ${changes.transparency}`);
591910
+ write(lines.join(`
591911
+ `));
591912
+ }
591913
+ return { exitCode: ExitCode.SUCCESS };
591914
+ }
591825
591915
  const updated = await updateEvent(api2, calendarId, calendarName, eventId, input);
591826
591916
  if (format3 === "json") {
591827
591917
  write(formatJsonSuccess({ event: updated }));
@@ -591839,6 +591929,7 @@ function createUpdateCommand() {
591839
591929
  cmd.option("-d, --description <text>", "New description");
591840
591930
  cmd.option("--busy", "Mark as busy");
591841
591931
  cmd.option("--free", "Mark as free");
591932
+ cmd.option("--dry-run", "Preview without executing");
591842
591933
  const busyOpt = cmd.options.find((o) => o.long === "--busy");
591843
591934
  const freeOpt = cmd.options.find((o) => o.long === "--free");
591844
591935
  busyOpt.conflicts(["free"]);
@@ -591920,11 +592011,24 @@ function createAddCommand() {
591920
592011
 
591921
592012
  // src/commands/delete.ts
591922
592013
  async function handleDelete(opts) {
591923
- const { api: api2, eventId, calendarId, format: format3, quiet, write } = opts;
592014
+ const { api: api2, eventId, calendarId, format: format3, quiet, dryRun = false, write } = opts;
591924
592015
  if (!eventId) {
591925
592016
  write(formatJsonError("INVALID_ARGS", "event-id is required"));
591926
592017
  return { exitCode: ExitCode.ARGUMENT };
591927
592018
  }
592019
+ if (dryRun) {
592020
+ if (format3 === "json") {
592021
+ write(formatJsonSuccess({
592022
+ dry_run: true,
592023
+ action: "delete",
592024
+ event_id: eventId,
592025
+ calendar_id: calendarId
592026
+ }));
592027
+ } else {
592028
+ write(`DRY RUN: Would delete event "${eventId}" from calendar "${calendarId}"`);
592029
+ }
592030
+ return { exitCode: ExitCode.SUCCESS };
592031
+ }
591928
592032
  try {
591929
592033
  await deleteEvent(api2, calendarId, eventId);
591930
592034
  if (!quiet) {
@@ -591948,7 +592052,7 @@ async function handleDelete(opts) {
591948
592052
  }
591949
592053
  }
591950
592054
  function createDeleteCommand() {
591951
- return new Command("delete").description("Delete a calendar event").argument("<event-id>", "Event ID").option("-c, --calendar <id>", "Calendar ID to query");
592055
+ return new Command("delete").description("Delete a calendar event").argument("<event-id>", "Event ID").option("-c, --calendar <id>", "Calendar ID to query").option("--dry-run", "Preview without executing");
591952
592056
  }
591953
592057
 
591954
592058
  // src/commands/calendars.ts
@@ -592187,6 +592291,22 @@ function handleError2(error, format3) {
592187
592291
  process.exit(errorCodeToExitCode(errorCode));
592188
592292
  }
592189
592293
 
592294
+ // src/lib/prompt.ts
592295
+ import { createInterface } from "node:readline/promises";
592296
+ function createReadlinePrompt() {
592297
+ return async (message2) => {
592298
+ const rl = createInterface({
592299
+ input: process.stdin,
592300
+ output: process.stderr
592301
+ });
592302
+ try {
592303
+ return await rl.question(message2);
592304
+ } finally {
592305
+ rl.close();
592306
+ }
592307
+ };
592308
+ }
592309
+
592190
592310
  // src/lib/resolve-calendar.ts
592191
592311
  async function resolveEventCalendar(api2, eventId, calendars) {
592192
592312
  const results = await Promise.all(calendars.map(async (cal) => {
@@ -592237,7 +592357,8 @@ function registerCommands(program2) {
592237
592357
  openUrl: (url) => {
592238
592358
  write(`Open this URL in your browser:
592239
592359
  ${url}`);
592240
- }
592360
+ },
592361
+ promptFn: createReadlinePrompt()
592241
592362
  });
592242
592363
  }
592243
592364
  process.exit(result.exitCode);
@@ -592389,6 +592510,7 @@ ${url}`);
592389
592510
  calendarId: resolvedCalendarId,
592390
592511
  format: globalOpts.format,
592391
592512
  quiet: globalOpts.quiet,
592513
+ dryRun: deleteOpts.dryRun ?? false,
592392
592514
  write: (msg) => process.stdout.write(msg + `
592393
592515
  `)
592394
592516
  });
@@ -592455,7 +592577,8 @@ ${url}`);
592455
592577
  },
592456
592578
  requestAuth: async () => {
592457
592579
  apiRef = null;
592458
- const credentials = getClientCredentials(fsAdapter);
592580
+ const promptFn = createReadlinePrompt();
592581
+ const credentials = globalOpts.format === "text" ? await getClientCredentialsOrPrompt(fsAdapter, write, promptFn) : getClientCredentials(fsAdapter);
592459
592582
  const { authUrl, waitForCode, server } = await startOAuthFlow(credentials, fsAdapter, globalThis.fetch);
592460
592583
  write(`Not authenticated. Starting OAuth flow...`);
592461
592584
  write(`Open this URL in your browser:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ncukondo/gcal-cli",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dist/index.js"