@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.
- package/dist/index.js +130 -7
- 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.
|
|
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
|
-
|
|
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
|
|
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:
|