gsheet-lvt 0.1.0 → 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/README.md CHANGED
@@ -78,6 +78,7 @@ gs account reauth
78
78
 
79
79
  # spreadsheet commands
80
80
  gs spreadsheet add [--id <value>] [--name <value>]
81
+ gs spreadsheet create --name <value> [--local-name <value>] [--no-select]
81
82
  gs spreadsheet list [--output <value>]
82
83
  gs spreadsheet remove [--id <value>]
83
84
  gs spreadsheet select [--id <value>] [--add] [--name <value>]
@@ -143,7 +144,7 @@ OAuth tokens are stored locally and refreshed automatically before expiry.
143
144
 
144
145
  ## 📜 License
145
146
 
146
- [MIT](https://github.com/lucasvtiradentes/sheet-cmd/blob/main/LICENSE)
147
+ [MIT](https://github.com/lucasvtiradentes/gsheet/blob/main/LICENSE)
147
148
 
148
149
  <!-- <DYNFIELD:FOOTER> -->
149
150
  <div width="100%" align="center">
package/dist/cli.js CHANGED
@@ -28,6 +28,9 @@ var APP_INFO = {
28
28
  display_name: "Google Sheets CLI",
29
29
  version: packageJson.version
30
30
  };
31
+ function getProgramName() {
32
+ return process.env.SHEET_CMD_PROG_NAME || APP_INFO.name;
33
+ }
31
34
  var configDirectoryByOS = {
32
35
  ["linux" /* Linux */]: (homeDir) => path.join(homeDir, ".config", APP_INFO.name),
33
36
  ["wsl" /* Wsl */]: (homeDir) => path.join(homeDir, ".config", APP_INFO.name),
@@ -62,6 +65,7 @@ var CONFIG_PATHS = {
62
65
  };
63
66
  var OAUTH_SCOPES = {
64
67
  SPREADSHEETS: "https://www.googleapis.com/auth/spreadsheets",
68
+ DRIVE_FILE: "https://www.googleapis.com/auth/drive.file",
65
69
  DRIVE_READONLY: "https://www.googleapis.com/auth/drive.readonly",
66
70
  USERINFO_EMAIL: "https://www.googleapis.com/auth/userinfo.email"
67
71
  };
@@ -141,12 +145,20 @@ var Logger = class {
141
145
  };
142
146
 
143
147
  // src/auth/oauth-scopes.ts
144
- var DRIVE_SCOPES = /* @__PURE__ */ new Set([OAUTH_SCOPES.DRIVE_READONLY, "https://www.googleapis.com/auth/drive"]);
148
+ var DRIVE_READ_SCOPES = /* @__PURE__ */ new Set([
149
+ OAUTH_SCOPES.DRIVE_READONLY,
150
+ OAUTH_SCOPES.DRIVE_FILE,
151
+ "https://www.googleapis.com/auth/drive"
152
+ ]);
153
+ var DRIVE_FILE_SCOPES = /* @__PURE__ */ new Set([OAUTH_SCOPES.DRIVE_FILE, "https://www.googleapis.com/auth/drive"]);
145
154
  var REQUIRED_OAUTH_SCOPES = [OAUTH_SCOPES.SPREADSHEETS, OAUTH_SCOPES.DRIVE_READONLY];
146
155
  function getMissingOAuthScopes(grantedScopes) {
147
156
  return REQUIRED_OAUTH_SCOPES.filter((scope) => {
148
157
  if (scope === OAUTH_SCOPES.DRIVE_READONLY) {
149
- return !grantedScopes.some((grantedScope) => DRIVE_SCOPES.has(grantedScope));
158
+ return !grantedScopes.some((grantedScope) => DRIVE_READ_SCOPES.has(grantedScope));
159
+ }
160
+ if (scope === OAUTH_SCOPES.DRIVE_FILE) {
161
+ return !grantedScopes.some((grantedScope) => DRIVE_FILE_SCOPES.has(grantedScope));
150
162
  }
151
163
  return !grantedScopes.includes(scope);
152
164
  });
@@ -161,7 +173,20 @@ function assertRequiredOAuthScopes(grantedScopes) {
161
173
  "Google returned an access token without required scopes.",
162
174
  `Missing scopes: ${missingScopes.join(", ")}`,
163
175
  `Granted scopes: ${grantedScopes.length > 0 ? grantedScopes.join(", ") : "none"}`,
164
- "Fix: in Google Cloud Console, add the missing scopes to the OAuth consent screen, publish/save the consent screen, then run `gsheet account reauth` again."
176
+ `Fix: in Google Cloud Console, add the missing scopes to the OAuth consent screen, publish/save the consent screen, then run \`${getProgramName()} account reauth\` again.`
177
+ ].join("\n")
178
+ );
179
+ }
180
+ function assertDriveFileOAuthScope(grantedScopes) {
181
+ if (grantedScopes.some((grantedScope) => DRIVE_FILE_SCOPES.has(grantedScope))) {
182
+ return;
183
+ }
184
+ throw new Error(
185
+ [
186
+ "Google returned an access token without permission to create Drive files.",
187
+ `Missing scopes: ${OAUTH_SCOPES.DRIVE_FILE}`,
188
+ `Granted scopes: ${grantedScopes.length > 0 ? grantedScopes.join(", ") : "none"}`,
189
+ `Fix: in Google Cloud Console, add the missing scope to the OAuth consent screen, publish/save the consent screen, then run \`${getProgramName()} account reauth\` again.`
165
190
  ].join("\n")
166
191
  );
167
192
  }
@@ -173,7 +198,12 @@ async function performOAuthFlow(clientId, clientSecret, options = {}) {
173
198
  const oauth2Client = new OAuth2Client(clientId, clientSecret, redirectUri);
174
199
  const authUrl = oauth2Client.generateAuthUrl({
175
200
  access_type: OAUTH_CONFIG.ACCESS_TYPE,
176
- scope: [OAUTH_SCOPES.SPREADSHEETS, OAUTH_SCOPES.DRIVE_READONLY, OAUTH_SCOPES.USERINFO_EMAIL],
201
+ scope: [
202
+ OAUTH_SCOPES.SPREADSHEETS,
203
+ OAUTH_SCOPES.DRIVE_READONLY,
204
+ OAUTH_SCOPES.DRIVE_FILE,
205
+ OAUTH_SCOPES.USERINFO_EMAIL
206
+ ],
177
207
  prompt: OAUTH_CONFIG.PROMPT,
178
208
  include_granted_scopes: true,
179
209
  login_hint: options.loginHint
@@ -304,7 +334,7 @@ async function refreshToken(credentials) {
304
334
  const { credentials: newTokens } = await oauth2Client.refreshAccessToken();
305
335
  const accessToken = newTokens.access_token || credentials.access_token;
306
336
  if (!accessToken) {
307
- throw new Error("No access token available after refresh. Run `gsheet account reauth`.");
337
+ throw new Error(`No access token available after refresh. Run \`${getProgramName()} account reauth\`.`);
308
338
  }
309
339
  const tokenInfo = await oauth2Client.getTokenInfo(accessToken);
310
340
  assertRequiredOAuthScopes(tokenInfo.scopes);
@@ -686,7 +716,7 @@ var ConfigManager = class {
686
716
  };
687
717
  async function assertCredentialsHaveRequiredScopes(credentials) {
688
718
  if (!credentials.access_token) {
689
- throw new Error("No access token available. Run `gsheet account reauth`.");
719
+ throw new Error(`No access token available. Run \`${getProgramName()} account reauth\`.`);
690
720
  }
691
721
  const oauth2Client = new OAuth2Client3(credentials.client_id, credentials.client_secret);
692
722
  const tokenInfo = await oauth2Client.getTokenInfo(credentials.access_token);
@@ -731,7 +761,7 @@ var addAccountCommand = defineSubCommand({
731
761
  Logger.bold("STEP 3: Configure OAuth Consent Screen");
732
762
  Logger.link(` ${GOOGLE_CLOUD_CONSOLE_URLS.CONSENT_SCREEN}`);
733
763
  Logger.info(" - User Type: External");
734
- Logger.info(" - App name: gsheet (or any name)");
764
+ Logger.info(` - App name: ${getProgramName()} (or any name)`);
735
765
  Logger.info(" - User support email: your email");
736
766
  Logger.info(" - Developer contact: your email");
737
767
  Logger.info(' - Click "SAVE AND CONTINUE"');
@@ -742,6 +772,7 @@ var addAccountCommand = defineSubCommand({
742
772
  Logger.info(" - Search and add:");
743
773
  Logger.info(" \u2192 .../auth/spreadsheets");
744
774
  Logger.info(" \u2192 .../auth/drive.readonly");
775
+ Logger.info(" \u2192 .../auth/drive.file");
745
776
  Logger.info(" \u2192 .../auth/userinfo.email");
746
777
  Logger.info(' - Click "UPDATE" then "SAVE AND CONTINUE"');
747
778
  Logger.plain("");
@@ -755,7 +786,7 @@ var addAccountCommand = defineSubCommand({
755
786
  Logger.link(` ${GOOGLE_CLOUD_CONSOLE_URLS.CREDENTIALS}`);
756
787
  Logger.info(' - Click "CREATE CREDENTIALS" \u2192 "OAuth client ID"');
757
788
  Logger.info(" - Application type: Desktop app");
758
- Logger.info(" - Name: gsheet");
789
+ Logger.info(` - Name: ${getProgramName()}`);
759
790
  Logger.info(' - Click "CREATE"');
760
791
  Logger.info(" - Copy the Client ID and Client Secret");
761
792
  Logger.plain("");
@@ -781,7 +812,7 @@ var addAccountCommand = defineSubCommand({
781
812
  Logger.success(`Account '${result.email}' added and set as active!`);
782
813
  } else {
783
814
  Logger.success(`Account '${result.email}' added successfully!`);
784
- Logger.info("Switch to this account: gsheet account select");
815
+ Logger.info(`Switch to this account: ${getProgramName()} account select`);
785
816
  }
786
817
  process.exit(0);
787
818
  }
@@ -798,7 +829,7 @@ var listAccountsCommand = defineSubCommand({
798
829
  const activeAccountEmail = configManager.getActiveAccountEmail();
799
830
  if (accounts.length === 0) {
800
831
  Logger.info("No accounts configured.");
801
- Logger.info("Add one with: gsheet account add");
832
+ Logger.info(`Add one with: ${getProgramName()} account add`);
802
833
  return;
803
834
  }
804
835
  Logger.info("Configured accounts:");
@@ -821,7 +852,7 @@ var reauthAccountCommand = defineSubCommand({
821
852
  const activeAccount = configManager.getActiveAccount();
822
853
  if (!activeAccount) {
823
854
  Logger.error("No active account set.");
824
- Logger.info("Use: gsheet account switch <email>");
855
+ Logger.info(`Use: ${getProgramName()} account select`);
825
856
  process.exit(1);
826
857
  }
827
858
  Logger.info(`Re-authenticating account: ${activeAccount.email}`);
@@ -851,7 +882,7 @@ var removeAccountCommand = defineSubCommand({
851
882
  const accounts = configManager.getAllAccounts();
852
883
  if (accounts.length === 0) {
853
884
  Logger.warning("No accounts configured.");
854
- Logger.info("Use: gsheet account add");
885
+ Logger.info(`Use: ${getProgramName()} account add`);
855
886
  process.exit(0);
856
887
  }
857
888
  let selectedEmail = args.email;
@@ -914,7 +945,7 @@ var selectAccountCommand = defineSubCommand({
914
945
  const accounts = configManager.getAllAccounts();
915
946
  if (accounts.length === 0) {
916
947
  Logger.warning("No accounts configured.");
917
- Logger.info("Use: gsheet account add");
948
+ Logger.info(`Use: ${getProgramName()} account add`);
918
949
  process.exit(0);
919
950
  }
920
951
  let selectedEmail = args.email;
@@ -1253,7 +1284,7 @@ function createCompletionCommand(program2) {
1253
1284
  completionProgram = program2;
1254
1285
  program2.command(completionCommand.name, completionCommand.description).action(async () => {
1255
1286
  Logger.info(`Available shells: ${completionShells.join(", ")}`);
1256
- Logger.info(`Usage: ${APP_INFO.name} completion <shell>`);
1287
+ Logger.info(`Usage: ${getProgramName()} completion <shell>`);
1257
1288
  });
1258
1289
  for (const shellCommand of completionCommand.subcommands) {
1259
1290
  program2.command(`${completionCommand.name} ${shellCommand.name}`, shellCommand.description).action(async ({ program: program3 }) => {
@@ -1306,7 +1337,7 @@ async function installZshCompletionSilent() {
1306
1337
  targetDir = join2(homeDir, ".zsh", "completions");
1307
1338
  mkdirSync2(targetDir, { recursive: true });
1308
1339
  }
1309
- const completionFile = join2(targetDir, `_${APP_INFO.name}`);
1340
+ const completionFile = join2(targetDir, `_${getProgramName()}`);
1310
1341
  writeFileSync2(completionFile, await getCurrentCompletionScript("zsh" /* Zsh */));
1311
1342
  }
1312
1343
  async function installBashCompletionSilent() {
@@ -1332,14 +1363,14 @@ async function installBashCompletionSilent() {
1332
1363
  targetDir = join2(homeDir, ".bash_completion.d");
1333
1364
  mkdirSync2(targetDir, { recursive: true });
1334
1365
  }
1335
- const completionFile = join2(targetDir, APP_INFO.name);
1366
+ const completionFile = join2(targetDir, getProgramName());
1336
1367
  writeFileSync2(completionFile, await getCurrentCompletionScript("bash" /* Bash */));
1337
1368
  }
1338
1369
  async function installFishCompletionSilent() {
1339
1370
  const homeDir = homedir2();
1340
1371
  const targetDir = join2(homeDir, ".config", "fish", "completions");
1341
1372
  mkdirSync2(targetDir, { recursive: true });
1342
- const completionFile = join2(targetDir, `${APP_INFO.name}.fish`);
1373
+ const completionFile = join2(targetDir, `${getProgramName()}.fish`);
1343
1374
  writeFileSync2(completionFile, await getCurrentCompletionScript("fish" /* Fish */));
1344
1375
  }
1345
1376
  async function clearZshCompletionCache() {
@@ -1690,18 +1721,20 @@ async function getGoogleSheetsService() {
1690
1721
  const activeAccount = configManager.getActiveAccount();
1691
1722
  if (!activeAccount) {
1692
1723
  Logger.error("No active account set.");
1693
- Logger.info("Use: gsheet account add");
1724
+ Logger.info(`Use: ${getProgramName()} account add`);
1694
1725
  process.exit(1);
1695
1726
  }
1696
1727
  const activeSpreadsheetName = configManager.getActiveSpreadsheetName(activeAccount.email);
1697
1728
  if (!activeSpreadsheetName) {
1698
1729
  Logger.error("No active spreadsheet set.");
1699
- Logger.info("Use: gsheet spreadsheet select");
1730
+ Logger.info(`Use: ${getProgramName()} spreadsheet select`);
1700
1731
  process.exit(1);
1701
1732
  }
1702
1733
  const spreadsheet = configManager.getSpreadsheet(activeAccount.email, activeSpreadsheetName);
1703
1734
  if (!spreadsheet) {
1704
- Logger.error(`Spreadsheet '${activeSpreadsheetName}' not found. Use "gsheet spreadsheet add" to add one.`);
1735
+ Logger.error(
1736
+ `Spreadsheet '${activeSpreadsheetName}' not found. Use "${getProgramName()} spreadsheet add" to add one.`
1737
+ );
1705
1738
  process.exit(1);
1706
1739
  }
1707
1740
  const refreshedCredentials = await configManager.getRefreshedCredentials(activeAccount.email);
@@ -1718,19 +1751,19 @@ function getActiveSheetName(sheetName) {
1718
1751
  const activeAccount = configManager.getActiveAccount();
1719
1752
  if (!activeAccount) {
1720
1753
  Logger.error("No active account set.");
1721
- Logger.info("Use: gsheet account add");
1754
+ Logger.info(`Use: ${getProgramName()} account add`);
1722
1755
  process.exit(1);
1723
1756
  }
1724
1757
  const activeSpreadsheetName = configManager.getActiveSpreadsheetName(activeAccount.email);
1725
1758
  if (!activeSpreadsheetName) {
1726
1759
  Logger.error("No active spreadsheet set.");
1727
- Logger.info("Use: gsheet spreadsheet select");
1760
+ Logger.info(`Use: ${getProgramName()} spreadsheet select`);
1728
1761
  process.exit(1);
1729
1762
  }
1730
1763
  const activeSheetName = configManager.getActiveSheetName(activeAccount.email, activeSpreadsheetName);
1731
1764
  if (!activeSheetName) {
1732
1765
  Logger.error("No active sheet set.");
1733
- Logger.info("Use: gsheet sheet select");
1766
+ Logger.info(`Use: ${getProgramName()} sheet select`);
1734
1767
  process.exit(1);
1735
1768
  }
1736
1769
  return activeSheetName;
@@ -2240,19 +2273,19 @@ var activeCommand = defineSubCommand({
2240
2273
  const activeAccount = configManager.getActiveAccount();
2241
2274
  if (!activeAccount) {
2242
2275
  Logger.error("No active account set.");
2243
- Logger.info("Use: gsheet account add");
2276
+ Logger.info(`Use: ${getProgramName()} account add`);
2244
2277
  return;
2245
2278
  }
2246
2279
  const activeSpreadsheetName = configManager.getActiveSpreadsheetName(activeAccount.email);
2247
2280
  if (!activeSpreadsheetName) {
2248
2281
  Logger.warning("No active spreadsheet set.");
2249
- Logger.info("Use: gsheet spreadsheet select");
2282
+ Logger.info(`Use: ${getProgramName()} spreadsheet select`);
2250
2283
  return;
2251
2284
  }
2252
2285
  const activeSheetName = configManager.getActiveSheetName(activeAccount.email, activeSpreadsheetName);
2253
2286
  if (!activeSheetName) {
2254
2287
  Logger.warning("No active sheet set.");
2255
- Logger.info("Use: gsheet sheet select");
2288
+ Logger.info(`Use: ${getProgramName()} sheet select`);
2256
2289
  return;
2257
2290
  }
2258
2291
  const sheetsService = await getGoogleSheetsService();
@@ -2360,13 +2393,13 @@ var selectCommand = defineSubCommand({
2360
2393
  const activeAccount = configManager.getActiveAccount();
2361
2394
  if (!activeAccount) {
2362
2395
  Logger.error("No active account set.");
2363
- Logger.info("Use: gsheet account add");
2396
+ Logger.info(`Use: ${getProgramName()} account add`);
2364
2397
  process.exit(1);
2365
2398
  }
2366
2399
  const activeSpreadsheetName = configManager.getActiveSpreadsheetName(activeAccount.email);
2367
2400
  if (!activeSpreadsheetName) {
2368
2401
  Logger.error("No active spreadsheet set.");
2369
- Logger.info("Use: gsheet spreadsheet select");
2402
+ Logger.info(`Use: ${getProgramName()} spreadsheet select`);
2370
2403
  process.exit(1);
2371
2404
  }
2372
2405
  let sheetName = options.name;
@@ -2432,13 +2465,13 @@ var activeSpreadsheetCommand = defineSubCommand({
2432
2465
  const activeAccount = configManager.getActiveAccount();
2433
2466
  if (!activeAccount) {
2434
2467
  Logger.error("No active account set.");
2435
- Logger.info("Use: gsheet account add");
2468
+ Logger.info(`Use: ${getProgramName()} account add`);
2436
2469
  return;
2437
2470
  }
2438
2471
  const activeSpreadsheetName = configManager.getActiveSpreadsheetName(activeAccount.email);
2439
2472
  if (!activeSpreadsheetName) {
2440
2473
  Logger.warning("No active spreadsheet set.");
2441
- Logger.info('Use "gsheet spreadsheet select" to set one.');
2474
+ Logger.info(`Use "${getProgramName()} spreadsheet select" to set one.`);
2442
2475
  return;
2443
2476
  }
2444
2477
  const activeSpreadsheet = configManager.getSpreadsheet(activeAccount.email, activeSpreadsheetName);
@@ -2476,12 +2509,7 @@ var GoogleDriveService = class {
2476
2509
  this.credentials = oauthCredentials;
2477
2510
  }
2478
2511
  async listSpreadsheets() {
2479
- const oauth2Client = new google.auth.OAuth2(this.credentials.client_id, this.credentials.client_secret);
2480
- oauth2Client.setCredentials({
2481
- access_token: this.credentials.access_token,
2482
- refresh_token: this.credentials.refresh_token,
2483
- expiry_date: this.credentials.expiry_date
2484
- });
2512
+ const oauth2Client = this.createOAuthClient();
2485
2513
  Logger.info("Checking access token...");
2486
2514
  const tokenInfo = await oauth2Client.getTokenInfo(this.credentials.access_token || "");
2487
2515
  Logger.info(`Token scopes: ${tokenInfo.scopes?.join(", ") || "none"}`);
@@ -2512,10 +2540,42 @@ var GoogleDriveService = class {
2512
2540
  Logger.info(` - ${OAUTH_SCOPES.DRIVE_READONLY}`);
2513
2541
  Logger.info("\nTo fix this:");
2514
2542
  Logger.info(" 1. Add Drive API scope in OAuth Consent Screen");
2515
- Logger.info(" 2. Run: gsheet account reauth");
2543
+ Logger.info(` 2. Run: ${getProgramName()} account reauth`);
2516
2544
  throw error;
2517
2545
  }
2518
2546
  }
2547
+ async createSpreadsheet(name) {
2548
+ const oauth2Client = this.createOAuthClient();
2549
+ const tokenInfo = await oauth2Client.getTokenInfo(this.credentials.access_token || "");
2550
+ assertDriveFileOAuthScope(tokenInfo.scopes);
2551
+ const drive = google.drive({ version: "v3", auth: oauth2Client });
2552
+ Logger.info("Creating spreadsheet in Google Drive...");
2553
+ const response = await drive.files.create({
2554
+ requestBody: {
2555
+ name,
2556
+ mimeType: "application/vnd.google-apps.spreadsheet"
2557
+ },
2558
+ fields: "id, name, webViewLink"
2559
+ });
2560
+ const file = response.data;
2561
+ if (!file.id) {
2562
+ throw new Error("Google Drive did not return a spreadsheet ID");
2563
+ }
2564
+ return {
2565
+ id: file.id,
2566
+ name: file.name || name,
2567
+ webViewLink: file.webViewLink || `https://docs.google.com/spreadsheets/d/${file.id}`
2568
+ };
2569
+ }
2570
+ createOAuthClient() {
2571
+ const oauth2Client = new google.auth.OAuth2(this.credentials.client_id, this.credentials.client_secret);
2572
+ oauth2Client.setCredentials({
2573
+ access_token: this.credentials.access_token,
2574
+ refresh_token: this.credentials.refresh_token,
2575
+ expiry_date: this.credentials.expiry_date
2576
+ });
2577
+ return oauth2Client;
2578
+ }
2519
2579
  };
2520
2580
 
2521
2581
  // src/core/spreadsheet-title.ts
@@ -2543,7 +2603,7 @@ var addSpreadsheetCommand = defineSubCommand({
2543
2603
  const activeAccount = configManager.getActiveAccount();
2544
2604
  if (!activeAccount) {
2545
2605
  Logger.error("No active account set.");
2546
- Logger.info("Use: gsheet account add");
2606
+ Logger.info(`Use: ${getProgramName()} account add`);
2547
2607
  process.exit(1);
2548
2608
  }
2549
2609
  let spreadsheetId;
@@ -2615,7 +2675,43 @@ var addSpreadsheetCommand = defineSubCommand({
2615
2675
  Logger.success(`Spreadsheet '${name}' added and set as active!`);
2616
2676
  } else {
2617
2677
  Logger.success(`Spreadsheet '${name}' added successfully!`);
2618
- Logger.info(`Switch to this spreadsheet: gsheet spreadsheet select ${name}`);
2678
+ Logger.info(`Switch to this spreadsheet: ${getProgramName()} spreadsheet select -i ${spreadsheetId}`);
2679
+ }
2680
+ }
2681
+ });
2682
+
2683
+ // src/commands/spreadsheet/create.ts
2684
+ var createSpreadsheetCommand = defineSubCommand({
2685
+ name: "create",
2686
+ description: "Create a new Google spreadsheet and add it to local config",
2687
+ flags: [
2688
+ flag.string("--name", "Spreadsheet name", { alias: "-n", required: true }),
2689
+ flag.string("--local-name", "Local config name for the spreadsheet"),
2690
+ flag.boolean("--no-select", "Do not set the created spreadsheet as active")
2691
+ ],
2692
+ errorMessage: "Failed to create spreadsheet",
2693
+ action: async ({ options }) => {
2694
+ const configManager = new ConfigManager();
2695
+ const activeAccount = configManager.getActiveAccount();
2696
+ if (!activeAccount) {
2697
+ Logger.error("No active account set.");
2698
+ Logger.info(`Use: ${getProgramName()} account add`);
2699
+ process.exit(1);
2700
+ }
2701
+ const credentials = await configManager.getRefreshedCredentials(activeAccount.email);
2702
+ const driveService = new GoogleDriveService(credentials);
2703
+ const spreadsheet = await driveService.createSpreadsheet(options.name);
2704
+ const localName = options.localName?.trim() || spreadsheet.name;
2705
+ await configManager.addSpreadsheet(activeAccount.email, localName, spreadsheet.id);
2706
+ if (options.select !== false) {
2707
+ configManager.setActiveSpreadsheet(activeAccount.email, localName);
2708
+ }
2709
+ Logger.success(`Created spreadsheet: ${spreadsheet.name}`);
2710
+ Logger.dim(` Local name: ${localName}`);
2711
+ Logger.dim(` ID: ${spreadsheet.id}`);
2712
+ Logger.dim(` URL: ${spreadsheet.webViewLink || getSpreadsheetUrl(spreadsheet.id)}`);
2713
+ if (options.select !== false) {
2714
+ Logger.dim(" Active: yes");
2619
2715
  }
2620
2716
  }
2621
2717
  });
@@ -2631,7 +2727,7 @@ var listSpreadsheetsCommand = defineSubCommand({
2631
2727
  const activeAccount = configManager.getActiveAccount();
2632
2728
  if (!activeAccount) {
2633
2729
  Logger.error("No active account set.");
2634
- Logger.info("Use: gsheet account add");
2730
+ Logger.info(`Use: ${getProgramName()} account add`);
2635
2731
  return;
2636
2732
  }
2637
2733
  const spreadsheets = configManager.listSpreadsheets(activeAccount.email);
@@ -2651,7 +2747,7 @@ var listSpreadsheetsCommand = defineSubCommand({
2651
2747
  return;
2652
2748
  }
2653
2749
  if (spreadsheets.length === 0) {
2654
- Logger.warning('No spreadsheets configured. Use "gsheet spreadsheet add" to add one.');
2750
+ Logger.warning(`No spreadsheets configured. Use "${getProgramName()} spreadsheet add" to add one.`);
2655
2751
  return;
2656
2752
  }
2657
2753
  Logger.bold(`
@@ -2685,7 +2781,7 @@ var removeSpreadsheetCommand = defineSubCommand({
2685
2781
  const activeAccount = configManager.getActiveAccount();
2686
2782
  if (!activeAccount) {
2687
2783
  Logger.error("No active account set.");
2688
- Logger.info("Use: gsheet account add");
2784
+ Logger.info(`Use: ${getProgramName()} account add`);
2689
2785
  process.exit(1);
2690
2786
  }
2691
2787
  const spreadsheetIdOption = options.id ?? options.i;
@@ -2758,7 +2854,7 @@ var selectSpreadsheetCommand = defineSubCommand({
2758
2854
  const activeAccount = configManager.getActiveAccount();
2759
2855
  if (!activeAccount) {
2760
2856
  Logger.error("No active account set.");
2761
- Logger.info("Use: gsheet account add");
2857
+ Logger.info(`Use: ${getProgramName()} account add`);
2762
2858
  process.exit(1);
2763
2859
  }
2764
2860
  const spreadsheetIdOption = options.id ?? options.i;
@@ -2766,7 +2862,7 @@ var selectSpreadsheetCommand = defineSubCommand({
2766
2862
  if (!spreadsheetId) {
2767
2863
  const spreadsheets = configManager.listSpreadsheets(activeAccount.email);
2768
2864
  if (spreadsheets.length === 0) {
2769
- Logger.warning('No spreadsheets configured. Use "gsheet spreadsheet add" to add one.');
2865
+ Logger.warning(`No spreadsheets configured. Use "${getProgramName()} spreadsheet add" to add one.`);
2770
2866
  return;
2771
2867
  }
2772
2868
  const activeSpreadsheet = configManager.getActiveSpreadsheetName(activeAccount.email);
@@ -2827,7 +2923,7 @@ var updateCommandsByPackageManager = {
2827
2923
  };
2828
2924
  var updateCommand = defineSubCommand({
2829
2925
  name: "update",
2830
- description: `Update ${APP_INFO.name} to latest version`,
2926
+ description: `Update ${getProgramName()} to latest version`,
2831
2927
  errorMessage: "Failed to check for updates",
2832
2928
  action: async () => {
2833
2929
  Logger.loading("Checking current version...");
@@ -2845,25 +2941,25 @@ var updateCommand = defineSubCommand({
2845
2941
  Logger.info(`\u{1F4E6} Current version: ${currentVersion}`);
2846
2942
  Logger.info(`\u{1F4E6} Latest version: ${latestVersion}`);
2847
2943
  if (currentVersion === latestVersion) {
2848
- Logger.success(`${APP_INFO.name} is already up to date!`);
2944
+ Logger.success(`${getProgramName()} is already up to date!`);
2849
2945
  return;
2850
2946
  }
2851
2947
  Logger.loading("Detecting package manager...");
2852
2948
  const packageManager = await detectPackageManager();
2853
2949
  if (!packageManager) {
2854
- Logger.error(`Could not detect how ${APP_INFO.name} was installed`);
2950
+ Logger.error(`Could not detect how ${getProgramName()} was installed`);
2855
2951
  Logger.dim("Please update manually using your package manager");
2856
2952
  return;
2857
2953
  }
2858
2954
  Logger.info(`\u{1F4E6} Detected package manager: ${packageManager}`);
2859
- Logger.loading(`Updating ${APP_INFO.name} from ${currentVersion} to ${latestVersion}...`);
2955
+ Logger.loading(`Updating ${getProgramName()} from ${currentVersion} to ${latestVersion}...`);
2860
2956
  const updateCommand2 = getUpdateCommand(packageManager);
2861
2957
  const { stdout, stderr } = await execAsync(updateCommand2);
2862
2958
  if (stderr && !stderr.includes("npm WARN")) {
2863
2959
  Logger.error(`Error updating: ${stderr}`);
2864
2960
  return;
2865
2961
  }
2866
- Logger.success(`${APP_INFO.name} updated successfully from ${currentVersion} to ${latestVersion}!`);
2962
+ Logger.success(`${getProgramName()} updated successfully from ${currentVersion} to ${latestVersion}!`);
2867
2963
  if (stdout) {
2868
2964
  Logger.dim(stdout);
2869
2965
  }
@@ -2904,7 +3000,7 @@ async function getGlobalNpmPath() {
2904
3000
  const isWindows = platform2() === "win32";
2905
3001
  try {
2906
3002
  const whereCommand = isWindows ? "where" : "which";
2907
- const { stdout } = await execAsync(`${whereCommand} ${APP_INFO.name}`);
3003
+ const { stdout } = await execAsync(`${whereCommand} ${getProgramName()}`);
2908
3004
  const execPath = stdout.trim();
2909
3005
  if (execPath) {
2910
3006
  if (!isWindows) {
@@ -2966,6 +3062,7 @@ var spreadsheetCommand = defineCommand({
2966
3062
  description: "Manage spreadsheet configurations",
2967
3063
  subcommands: [
2968
3064
  addSpreadsheetCommand,
3065
+ createSpreadsheetCommand,
2969
3066
  listSpreadsheetsCommand,
2970
3067
  removeSpreadsheetCommand,
2971
3068
  selectSpreadsheetCommand,
@@ -3011,7 +3108,7 @@ function handleCommandError(baseMessage) {
3011
3108
  if (errorDetails.includes("invalid_grant")) {
3012
3109
  Logger.error("OAuth token refresh failed: invalid_grant");
3013
3110
  Logger.info("Your refresh token is expired or invalid");
3014
- Logger.info("Fix: gsheet account reauth");
3111
+ Logger.info(`Fix: ${getProgramName()} account reauth`);
3015
3112
  process.exit(1);
3016
3113
  }
3017
3114
  const prefix = typeof baseMessage === "function" ? baseMessage(error) : baseMessage;
@@ -3039,7 +3136,7 @@ function registerParentCommand(program2, definition) {
3039
3136
  applyAliases(command, definition.aliases);
3040
3137
  command.action(async () => {
3041
3138
  const commandPromise = async () => {
3042
- console.log(`Usage: ${APP_INFO.name} ${definition.name} <command>`);
3139
+ console.log(`Usage: ${getProgramName()} ${definition.name} <command>`);
3043
3140
  console.log("");
3044
3141
  console.log(`Available commands: ${definition.subcommands.map((subcommand) => subcommand.name).join(", ")}`);
3045
3142
  };
@@ -3125,7 +3222,7 @@ function getProgramConstructor() {
3125
3222
  return Program;
3126
3223
  }
3127
3224
  function getProgramBin() {
3128
- if (process.env.SHEET_CMD_PROG_NAME) return process.env.SHEET_CMD_PROG_NAME;
3225
+ if (process.env.SHEET_CMD_PROG_NAME) return getProgramName();
3129
3226
  if (isDirectRun() && process.argv[1]) return basename(process.argv[1]);
3130
3227
  return APP_INFO.name;
3131
3228
  }