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 +2 -1
- package/dist/cli.js +150 -53
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +82 -13
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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/
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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: [
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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: ${
|
|
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, `_${
|
|
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,
|
|
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, `${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ${
|
|
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(`${
|
|
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 ${
|
|
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 ${
|
|
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(`${
|
|
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} ${
|
|
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(
|
|
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: ${
|
|
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
|
|
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
|
}
|