@hasna/connectors 1.3.22 → 1.3.23
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/bin/index.js +109 -59
- package/bin/mcp.js +236 -182
- package/bin/serve.js +77 -33
- package/connectors/connect-dropbox/src/api/bulk.ts +285 -0
- package/connectors/connect-dropbox/src/api/index.ts +4 -0
- package/connectors/connect-dropbox/src/cli/index.ts +151 -0
- package/connectors/connect-github/src/api/bulk.ts +328 -0
- package/connectors/connect-github/src/api/index.ts +4 -0
- package/connectors/connect-github/src/cli/index.ts +145 -0
- package/connectors/connect-gmail/src/api/history.ts +82 -0
- package/connectors/connect-gmail/src/api/index.ts +12 -0
- package/connectors/connect-gmail/src/api/settings.ts +280 -0
- package/connectors/connect-gmail/src/api/watch.ts +40 -0
- package/connectors/connect-gmail/src/cli/index.ts +237 -0
- package/connectors/connect-googleads/src/api/bulk.ts +354 -0
- package/connectors/connect-googleads/src/api/index.ts +4 -0
- package/connectors/connect-googleads/src/cli/index.ts +192 -0
- package/connectors/connect-googlecalendar/src/api/acl.ts +66 -0
- package/connectors/connect-googlecalendar/src/api/bulk.ts +452 -0
- package/connectors/connect-googlecalendar/src/api/freebusy.ts +35 -0
- package/connectors/connect-googlecalendar/src/api/index.ts +9 -0
- package/connectors/connect-googlecalendar/src/cli/index.ts +522 -0
- package/connectors/connect-googlecalendar/src/types/index.ts +90 -0
- package/connectors/connect-googlecontacts/src/api/bulk.ts +365 -0
- package/connectors/connect-googlecontacts/src/api/groups.ts +164 -0
- package/connectors/connect-googlecontacts/src/api/index.ts +8 -0
- package/connectors/connect-googlecontacts/src/cli/index.ts +340 -0
- package/connectors/connect-googledocs/src/api/bulk.ts +301 -0
- package/connectors/connect-googledocs/src/api/index.ts +4 -0
- package/connectors/connect-googledocs/src/cli/index.ts +187 -0
- package/connectors/connect-googledrive/src/api/bulk.ts +505 -0
- package/connectors/connect-googledrive/src/api/changes.ts +139 -0
- package/connectors/connect-googledrive/src/api/client.ts +34 -0
- package/connectors/connect-googledrive/src/api/index.ts +12 -0
- package/connectors/connect-googledrive/src/api/revisions.ts +132 -0
- package/connectors/connect-googledrive/src/cli/index.ts +624 -0
- package/connectors/connect-googledrive/src/index.ts +2 -0
- package/connectors/connect-googlephotos/src/api/bulk.ts +455 -0
- package/connectors/connect-googlephotos/src/api/index.ts +8 -0
- package/connectors/connect-googlephotos/src/cli/index.ts +185 -0
- package/connectors/connect-googletasks/src/api/bulk.ts +359 -0
- package/connectors/connect-googletasks/src/api/index.ts +1 -0
- package/connectors/connect-googletasks/src/cli/index.ts +178 -0
- package/connectors/connect-linear/src/api/bulk.ts +219 -0
- package/connectors/connect-linear/src/api/index.ts +4 -0
- package/connectors/connect-linear/src/cli/index.ts +116 -0
- package/connectors/connect-shopify/src/api/bulk.ts +288 -0
- package/connectors/connect-shopify/src/api/index.ts +4 -0
- package/connectors/connect-shopify/src/cli/index.ts +108 -0
- package/connectors/connect-slack/src/api/bulk.ts +237 -0
- package/connectors/connect-slack/src/api/index.ts +4 -0
- package/connectors/connect-slack/src/cli/index.ts +157 -0
- package/connectors/connect-stripe/src/api/bulk.ts +488 -0
- package/connectors/connect-stripe/src/api/index.ts +4 -0
- package/connectors/connect-stripe/src/cli/index.ts +206 -0
- package/dist/index.js +75 -33
- package/dist/lib/runner.d.ts +15 -5
- package/package.json +2 -2
- package/connectors/connect-cloudflare/bun.lock +0 -32
- package/connectors/connect-gmail/bin/index.js +0 -7027
- package/connectors/connect-gmail/dist/cli/index.js +0 -7035
- package/connectors/connect-gmail/dist/index.js +0 -2174
- package/dist/server/server.test.d.ts +0 -1
|
@@ -1303,5 +1303,629 @@ drivesCmd
|
|
|
1303
1303
|
}
|
|
1304
1304
|
});
|
|
1305
1305
|
|
|
1306
|
+
// ============================================
|
|
1307
|
+
// Bulk Commands
|
|
1308
|
+
// ============================================
|
|
1309
|
+
const bulkCmd = program
|
|
1310
|
+
.command('bulk')
|
|
1311
|
+
.description('Bulk operations on files (using Drive search queries)');
|
|
1312
|
+
|
|
1313
|
+
bulkCmd
|
|
1314
|
+
.command('preview <query>')
|
|
1315
|
+
.description('Preview files matching a Drive search query')
|
|
1316
|
+
.option('-n, --max <number>', 'Maximum files to preview', '20')
|
|
1317
|
+
.action(async (query: string, opts) => {
|
|
1318
|
+
try {
|
|
1319
|
+
const drive = requireAuth();
|
|
1320
|
+
info(`Searching for files matching: ${query}`);
|
|
1321
|
+
|
|
1322
|
+
const result = await drive.bulk.preview(query, parseInt(opts.max));
|
|
1323
|
+
|
|
1324
|
+
if (result.files.length === 0) {
|
|
1325
|
+
info('No files found matching the query');
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
success(`Found ${result.total} file(s):`);
|
|
1330
|
+
const output = result.files.map(f => ({
|
|
1331
|
+
id: f.id,
|
|
1332
|
+
name: f.name,
|
|
1333
|
+
mimeType: f.mimeType,
|
|
1334
|
+
size: f.size ? formatBytes(parseInt(f.size)) : '-',
|
|
1335
|
+
}));
|
|
1336
|
+
print(output, getFormat(bulkCmd));
|
|
1337
|
+
} catch (err) {
|
|
1338
|
+
error(String(err));
|
|
1339
|
+
process.exit(1);
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
bulkCmd
|
|
1344
|
+
.command('trash <query>')
|
|
1345
|
+
.description('Move files matching a query to trash')
|
|
1346
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1347
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1348
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1349
|
+
.action(async (query: string, opts) => {
|
|
1350
|
+
try {
|
|
1351
|
+
const drive = requireAuth();
|
|
1352
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Moving to trash files matching: ${query}`);
|
|
1353
|
+
|
|
1354
|
+
const result = await drive.bulk.trash({
|
|
1355
|
+
query,
|
|
1356
|
+
maxResults: parseInt(opts.max),
|
|
1357
|
+
concurrency: parseInt(opts.concurrency),
|
|
1358
|
+
dryRun: opts.dryRun,
|
|
1359
|
+
onProgress: (current, total) => {
|
|
1360
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1361
|
+
},
|
|
1362
|
+
});
|
|
1363
|
+
console.log();
|
|
1364
|
+
|
|
1365
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk trash complete:`);
|
|
1366
|
+
info(` Total: ${result.total}`);
|
|
1367
|
+
info(` Success: ${result.success}`);
|
|
1368
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1369
|
+
} catch (err) {
|
|
1370
|
+
error(String(err));
|
|
1371
|
+
process.exit(1);
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
bulkCmd
|
|
1376
|
+
.command('delete <query>')
|
|
1377
|
+
.description('Permanently delete files matching a query (DANGER!)')
|
|
1378
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1379
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1380
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1381
|
+
.option('--trash', 'Move to trash instead of permanent delete')
|
|
1382
|
+
.option('--confirm', 'Confirm permanent deletion')
|
|
1383
|
+
.action(async (query: string, opts) => {
|
|
1384
|
+
try {
|
|
1385
|
+
if (!opts.dryRun && !opts.confirm && !opts.trash) {
|
|
1386
|
+
error('Permanent deletion requires --confirm flag');
|
|
1387
|
+
info('Use --dry-run to preview or --trash to move to trash instead');
|
|
1388
|
+
process.exit(1);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
const drive = requireAuth();
|
|
1392
|
+
if (!opts.trash) {
|
|
1393
|
+
warn(`${opts.dryRun ? '[DRY RUN] ' : ''}PERMANENTLY DELETING files matching: ${query}`);
|
|
1394
|
+
} else {
|
|
1395
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Moving to trash files matching: ${query}`);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
const result = await drive.bulk.delete({
|
|
1399
|
+
query,
|
|
1400
|
+
maxResults: parseInt(opts.max),
|
|
1401
|
+
concurrency: parseInt(opts.concurrency),
|
|
1402
|
+
trash: opts.trash,
|
|
1403
|
+
dryRun: opts.dryRun,
|
|
1404
|
+
onProgress: (current, total) => {
|
|
1405
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1406
|
+
},
|
|
1407
|
+
});
|
|
1408
|
+
console.log();
|
|
1409
|
+
|
|
1410
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk delete complete:`);
|
|
1411
|
+
info(` Total: ${result.total}`);
|
|
1412
|
+
info(` Success: ${result.success}`);
|
|
1413
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1414
|
+
} catch (err) {
|
|
1415
|
+
error(String(err));
|
|
1416
|
+
process.exit(1);
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
bulkCmd
|
|
1421
|
+
.command('untrash <query>')
|
|
1422
|
+
.description('Restore files matching a query from trash')
|
|
1423
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1424
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1425
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1426
|
+
.action(async (query: string, opts) => {
|
|
1427
|
+
try {
|
|
1428
|
+
const drive = requireAuth();
|
|
1429
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Restoring from trash files matching: ${query}`);
|
|
1430
|
+
|
|
1431
|
+
const result = await drive.bulk.untrash({
|
|
1432
|
+
query,
|
|
1433
|
+
maxResults: parseInt(opts.max),
|
|
1434
|
+
concurrency: parseInt(opts.concurrency),
|
|
1435
|
+
dryRun: opts.dryRun,
|
|
1436
|
+
onProgress: (current, total) => {
|
|
1437
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1438
|
+
},
|
|
1439
|
+
});
|
|
1440
|
+
console.log();
|
|
1441
|
+
|
|
1442
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk untrash complete:`);
|
|
1443
|
+
info(` Total: ${result.total}`);
|
|
1444
|
+
info(` Success: ${result.success}`);
|
|
1445
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1446
|
+
} catch (err) {
|
|
1447
|
+
error(String(err));
|
|
1448
|
+
process.exit(1);
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
bulkCmd
|
|
1453
|
+
.command('move <query> <destinationFolderId>')
|
|
1454
|
+
.description('Move files matching a query to a different folder')
|
|
1455
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1456
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1457
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1458
|
+
.action(async (query: string, destinationFolderId: string, opts) => {
|
|
1459
|
+
try {
|
|
1460
|
+
const drive = requireAuth();
|
|
1461
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Moving files matching: ${query}`);
|
|
1462
|
+
info(` Destination folder: ${destinationFolderId}`);
|
|
1463
|
+
|
|
1464
|
+
const result = await drive.bulk.move({
|
|
1465
|
+
query,
|
|
1466
|
+
destinationFolderId,
|
|
1467
|
+
maxResults: parseInt(opts.max),
|
|
1468
|
+
concurrency: parseInt(opts.concurrency),
|
|
1469
|
+
dryRun: opts.dryRun,
|
|
1470
|
+
onProgress: (current, total) => {
|
|
1471
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1472
|
+
},
|
|
1473
|
+
});
|
|
1474
|
+
console.log();
|
|
1475
|
+
|
|
1476
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk move complete:`);
|
|
1477
|
+
info(` Total: ${result.total}`);
|
|
1478
|
+
info(` Success: ${result.success}`);
|
|
1479
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
error(String(err));
|
|
1482
|
+
process.exit(1);
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
bulkCmd
|
|
1487
|
+
.command('rename <query>')
|
|
1488
|
+
.description('Rename files matching a query')
|
|
1489
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1490
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1491
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1492
|
+
.option('-m, --mode <mode>', 'Rename mode: prefix, suffix, replace, lowercase, uppercase', 'prefix')
|
|
1493
|
+
.option('--prefix <text>', 'Prefix to add (for mode=prefix)')
|
|
1494
|
+
.option('--suffix <text>', 'Suffix to add before extension (for mode=suffix)')
|
|
1495
|
+
.option('--find <text>', 'Text to find (for mode=replace)')
|
|
1496
|
+
.option('--replace <text>', 'Replacement text (for mode=replace)')
|
|
1497
|
+
.action(async (query: string, opts) => {
|
|
1498
|
+
try {
|
|
1499
|
+
const drive = requireAuth();
|
|
1500
|
+
const mode = opts.mode as 'prefix' | 'suffix' | 'replace' | 'lowercase' | 'uppercase';
|
|
1501
|
+
|
|
1502
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Renaming files matching: ${query}`);
|
|
1503
|
+
info(` Mode: ${mode}`);
|
|
1504
|
+
if (opts.prefix) info(` Prefix: ${opts.prefix}`);
|
|
1505
|
+
if (opts.suffix) info(` Suffix: ${opts.suffix}`);
|
|
1506
|
+
if (opts.find) info(` Find: ${opts.find}`);
|
|
1507
|
+
if (opts.replace) info(` Replace: ${opts.replace}`);
|
|
1508
|
+
|
|
1509
|
+
const result = await drive.bulk.rename({
|
|
1510
|
+
query,
|
|
1511
|
+
mode,
|
|
1512
|
+
prefix: opts.prefix,
|
|
1513
|
+
suffix: opts.suffix,
|
|
1514
|
+
find: opts.find,
|
|
1515
|
+
replace: opts.replace,
|
|
1516
|
+
maxResults: parseInt(opts.max),
|
|
1517
|
+
concurrency: parseInt(opts.concurrency),
|
|
1518
|
+
dryRun: opts.dryRun,
|
|
1519
|
+
onProgress: (current, total) => {
|
|
1520
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1521
|
+
},
|
|
1522
|
+
});
|
|
1523
|
+
console.log();
|
|
1524
|
+
|
|
1525
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk rename complete:`);
|
|
1526
|
+
info(` Total: ${result.total}`);
|
|
1527
|
+
info(` Success: ${result.success}`);
|
|
1528
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1529
|
+
} catch (err) {
|
|
1530
|
+
error(String(err));
|
|
1531
|
+
process.exit(1);
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
bulkCmd
|
|
1536
|
+
.command('share <query>')
|
|
1537
|
+
.description('Share files matching a query with a user')
|
|
1538
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1539
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1540
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1541
|
+
.option('-e, --email <email>', 'Email address to share with (required)', '')
|
|
1542
|
+
.option('-r, --role <role>', 'Permission role: reader, writer, commenter', 'reader')
|
|
1543
|
+
.option('--no-notification', 'Do not send notification email')
|
|
1544
|
+
.action(async (query: string, opts) => {
|
|
1545
|
+
try {
|
|
1546
|
+
if (!opts.email) {
|
|
1547
|
+
error('--email is required');
|
|
1548
|
+
process.exit(1);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
const drive = requireAuth();
|
|
1552
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Sharing files matching: ${query}`);
|
|
1553
|
+
info(` With: ${opts.email} (${opts.role})`);
|
|
1554
|
+
|
|
1555
|
+
const result = await drive.bulk.share({
|
|
1556
|
+
query,
|
|
1557
|
+
email: opts.email,
|
|
1558
|
+
role: opts.role as 'reader' | 'writer' | 'commenter',
|
|
1559
|
+
sendNotification: opts.notification !== false,
|
|
1560
|
+
maxResults: parseInt(opts.max),
|
|
1561
|
+
concurrency: parseInt(opts.concurrency),
|
|
1562
|
+
dryRun: opts.dryRun,
|
|
1563
|
+
onProgress: (current, total) => {
|
|
1564
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1565
|
+
},
|
|
1566
|
+
});
|
|
1567
|
+
console.log();
|
|
1568
|
+
|
|
1569
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk share complete:`);
|
|
1570
|
+
info(` Total: ${result.total}`);
|
|
1571
|
+
info(` Success: ${result.success}`);
|
|
1572
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
error(String(err));
|
|
1575
|
+
process.exit(1);
|
|
1576
|
+
}
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
bulkCmd
|
|
1580
|
+
.command('make-public <query>')
|
|
1581
|
+
.description('Make files matching a query publicly accessible')
|
|
1582
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1583
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1584
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1585
|
+
.action(async (query: string, opts) => {
|
|
1586
|
+
try {
|
|
1587
|
+
const drive = requireAuth();
|
|
1588
|
+
warn(`${opts.dryRun ? '[DRY RUN] ' : ''}MAKING PUBLICLY ACCESSIBLE files matching: ${query}`);
|
|
1589
|
+
|
|
1590
|
+
const result = await drive.bulk.makePublic({
|
|
1591
|
+
query,
|
|
1592
|
+
maxResults: parseInt(opts.max),
|
|
1593
|
+
concurrency: parseInt(opts.concurrency),
|
|
1594
|
+
dryRun: opts.dryRun,
|
|
1595
|
+
onProgress: (current, total) => {
|
|
1596
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1597
|
+
},
|
|
1598
|
+
});
|
|
1599
|
+
console.log();
|
|
1600
|
+
|
|
1601
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk make-public complete:`);
|
|
1602
|
+
info(` Total: ${result.total}`);
|
|
1603
|
+
info(` Success: ${result.success}`);
|
|
1604
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1605
|
+
} catch (err) {
|
|
1606
|
+
error(String(err));
|
|
1607
|
+
process.exit(1);
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
bulkCmd
|
|
1612
|
+
.command('remove-public <query>')
|
|
1613
|
+
.description('Remove public access from files matching a query')
|
|
1614
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1615
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1616
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1617
|
+
.action(async (query: string, opts) => {
|
|
1618
|
+
try {
|
|
1619
|
+
const drive = requireAuth();
|
|
1620
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Removing public access from files matching: ${query}`);
|
|
1621
|
+
|
|
1622
|
+
const result = await drive.bulk.removePublicAccess({
|
|
1623
|
+
query,
|
|
1624
|
+
maxResults: parseInt(opts.max),
|
|
1625
|
+
concurrency: parseInt(opts.concurrency),
|
|
1626
|
+
dryRun: opts.dryRun,
|
|
1627
|
+
onProgress: (current, total) => {
|
|
1628
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1629
|
+
},
|
|
1630
|
+
});
|
|
1631
|
+
console.log();
|
|
1632
|
+
|
|
1633
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk remove-public complete:`);
|
|
1634
|
+
info(` Total: ${result.total}`);
|
|
1635
|
+
info(` Success: ${result.success}`);
|
|
1636
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1637
|
+
} catch (err) {
|
|
1638
|
+
error(String(err));
|
|
1639
|
+
process.exit(1);
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
bulkCmd
|
|
1644
|
+
.command('star <query>')
|
|
1645
|
+
.description('Star files matching a query')
|
|
1646
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1647
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1648
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1649
|
+
.action(async (query: string, opts) => {
|
|
1650
|
+
try {
|
|
1651
|
+
const drive = requireAuth();
|
|
1652
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Starring files matching: ${query}`);
|
|
1653
|
+
|
|
1654
|
+
const result = await drive.bulk.star({
|
|
1655
|
+
query,
|
|
1656
|
+
maxResults: parseInt(opts.max),
|
|
1657
|
+
concurrency: parseInt(opts.concurrency),
|
|
1658
|
+
dryRun: opts.dryRun,
|
|
1659
|
+
onProgress: (current, total) => {
|
|
1660
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1661
|
+
},
|
|
1662
|
+
});
|
|
1663
|
+
console.log();
|
|
1664
|
+
|
|
1665
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk star complete:`);
|
|
1666
|
+
info(` Total: ${result.total}`);
|
|
1667
|
+
info(` Success: ${result.success}`);
|
|
1668
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1669
|
+
} catch (err) {
|
|
1670
|
+
error(String(err));
|
|
1671
|
+
process.exit(1);
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
bulkCmd
|
|
1676
|
+
.command('unstar <query>')
|
|
1677
|
+
.description('Remove stars from files matching a query')
|
|
1678
|
+
.option('-n, --max <number>', 'Maximum files to process', '100')
|
|
1679
|
+
.option('-c, --concurrency <number>', 'Maximum concurrent API calls', '10')
|
|
1680
|
+
.option('--dry-run', 'Preview changes without applying them')
|
|
1681
|
+
.action(async (query: string, opts) => {
|
|
1682
|
+
try {
|
|
1683
|
+
const drive = requireAuth();
|
|
1684
|
+
info(`${opts.dryRun ? '[DRY RUN] ' : ''}Removing stars from files matching: ${query}`);
|
|
1685
|
+
|
|
1686
|
+
const result = await drive.bulk.unstar({
|
|
1687
|
+
query,
|
|
1688
|
+
maxResults: parseInt(opts.max),
|
|
1689
|
+
concurrency: parseInt(opts.concurrency),
|
|
1690
|
+
dryRun: opts.dryRun,
|
|
1691
|
+
onProgress: (current, total) => {
|
|
1692
|
+
process.stdout.write(`\r Progress: ${current}/${total}`);
|
|
1693
|
+
},
|
|
1694
|
+
});
|
|
1695
|
+
console.log();
|
|
1696
|
+
|
|
1697
|
+
success(`${opts.dryRun ? '[DRY RUN] ' : ''}Bulk unstar complete:`);
|
|
1698
|
+
info(` Total: ${result.total}`);
|
|
1699
|
+
info(` Success: ${result.success}`);
|
|
1700
|
+
if (result.failed > 0) warn(` Failed: ${result.failed}`);
|
|
1701
|
+
} catch (err) {
|
|
1702
|
+
error(String(err));
|
|
1703
|
+
process.exit(1);
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
|
|
1707
|
+
bulkCmd
|
|
1708
|
+
.command('help-query')
|
|
1709
|
+
.description('Show Drive search query syntax examples')
|
|
1710
|
+
.action(() => {
|
|
1711
|
+
info(chalk.bold('\nDrive Search Query Syntax:\n'));
|
|
1712
|
+
|
|
1713
|
+
info(chalk.cyan('Basic filters:'));
|
|
1714
|
+
info(' name = "report" - Files named "report"');
|
|
1715
|
+
info(' name contains "report" - Files with "report" in the name');
|
|
1716
|
+
info(' fullName contains "/Docs/" - Files in a path containing "Docs"\n');
|
|
1717
|
+
|
|
1718
|
+
info(chalk.cyan('Type filters:'));
|
|
1719
|
+
info(' mimeType = "application/pdf" - PDF files only');
|
|
1720
|
+
info(' mimeType = "application/vnd.google-apps.document" - Google Docs');
|
|
1721
|
+
info(' mimeType = "application/vnd.google-apps.folder" - Folders');
|
|
1722
|
+
info(' mimeType contains "image/" - Any image file\n');
|
|
1723
|
+
|
|
1724
|
+
info(chalk.cyan('Status filters:'));
|
|
1725
|
+
info(' trashed = false - Not in trash');
|
|
1726
|
+
info(' trashed = true - In trash');
|
|
1727
|
+
info(' starred = true - Starred files');
|
|
1728
|
+
info(' viewedByMe = true - Files you have viewed\n');
|
|
1729
|
+
|
|
1730
|
+
info(chalk.cyan('Ownership filters:'));
|
|
1731
|
+
info(' "me" in owners - Files you own');
|
|
1732
|
+
info(' sharedWithMe - Files shared with you');
|
|
1733
|
+
info(' "user@example.com" in owners - Files owned by specific user\n');
|
|
1734
|
+
|
|
1735
|
+
info(chalk.cyan('Folder filters:'));
|
|
1736
|
+
info(' "<folderId>" in parents - Direct children of a folder\n');
|
|
1737
|
+
|
|
1738
|
+
info(chalk.cyan('Date filters:'));
|
|
1739
|
+
info(' modifiedTime > "2024-01-01T00:00:00" - Modified after date');
|
|
1740
|
+
info(' modifiedTime < "2024-12-31T23:59:59" - Modified before date');
|
|
1741
|
+
info(' createdTime > "2024-06-01T00:00:00" - Created after date\n');
|
|
1742
|
+
|
|
1743
|
+
info(chalk.cyan('Combining filters:'));
|
|
1744
|
+
info(' mimeType = "application/pdf" and trashed = false');
|
|
1745
|
+
info(' name contains "report" and "me" in owners');
|
|
1746
|
+
info(' mimeType contains "image/" and modifiedTime > "2024-01-01T00:00:00"\n');
|
|
1747
|
+
|
|
1748
|
+
info(chalk.cyan('Examples:'));
|
|
1749
|
+
info(' bulk preview "mimeType=\'application/pdf\'"');
|
|
1750
|
+
info(' bulk trash "name contains \'temp\' and trashed = false" --dry-run');
|
|
1751
|
+
info(' bulk rename "mimeType contains \'image\'" --mode suffix --suffix _v2');
|
|
1752
|
+
info(' bulk star "sharedWithMe" -n 50');
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1755
|
+
// ============================================
|
|
1756
|
+
// Changes Commands
|
|
1757
|
+
// ============================================
|
|
1758
|
+
const changesCmd = program
|
|
1759
|
+
.command('changes')
|
|
1760
|
+
.description('Track changes to Drive files');
|
|
1761
|
+
|
|
1762
|
+
changesCmd
|
|
1763
|
+
.command('start-page-token')
|
|
1764
|
+
.description('Get the current starting page token for change tracking')
|
|
1765
|
+
.option('--drive <driveId>', 'Get token for a specific shared drive')
|
|
1766
|
+
.action(async (opts) => {
|
|
1767
|
+
try {
|
|
1768
|
+
const drive = requireAuth();
|
|
1769
|
+
const result = await drive.changes.getStartPageToken({
|
|
1770
|
+
driveId: opts.drive,
|
|
1771
|
+
supportsAllDrives: !!opts.drive,
|
|
1772
|
+
});
|
|
1773
|
+
success('Current page token: ' + result.nextPageToken);
|
|
1774
|
+
info('Use this token with "changes list" to begin tracking');
|
|
1775
|
+
} catch (err) {
|
|
1776
|
+
error(String(err));
|
|
1777
|
+
process.exit(1);
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1781
|
+
changesCmd
|
|
1782
|
+
.command('list <pageToken>')
|
|
1783
|
+
.description('List changes since the given page token')
|
|
1784
|
+
.option('-n, --max <number>', 'Maximum changes to return', '50')
|
|
1785
|
+
.option('--include-removed', 'Include removed items')
|
|
1786
|
+
.option('--my-drive-only', 'Only track My Drive changes (exclude shared drives)')
|
|
1787
|
+
.action(async (pageToken: string, opts) => {
|
|
1788
|
+
try {
|
|
1789
|
+
const drive = requireAuth();
|
|
1790
|
+
const result = await drive.changes.list({
|
|
1791
|
+
pageToken,
|
|
1792
|
+
pageSize: parseInt(opts.max),
|
|
1793
|
+
includeRemoved: opts.includeRemoved,
|
|
1794
|
+
restrictToMyDrive: opts.myDriveOnly,
|
|
1795
|
+
includeItemsFromAllDrives: !opts.myDriveOnly,
|
|
1796
|
+
supportsAllDrives: true,
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
if (!result.changes || result.changes.length === 0) {
|
|
1800
|
+
info('No changes found');
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
success('Found ' + result.changes.length + ' change(s):');
|
|
1805
|
+
info('New start page token: ' + result.newStartPageToken);
|
|
1806
|
+
|
|
1807
|
+
const changes = result.changes.map((c: any) => ({
|
|
1808
|
+
fileId: c.fileId,
|
|
1809
|
+
type: c.type,
|
|
1810
|
+
removed: c.removed || false,
|
|
1811
|
+
fileName: c.file?.name || '-',
|
|
1812
|
+
mimeType: c.file?.mimeType || '-',
|
|
1813
|
+
}));
|
|
1814
|
+
|
|
1815
|
+
print(changes, getFormat(changesCmd));
|
|
1816
|
+
|
|
1817
|
+
if (result.nextPageToken) {
|
|
1818
|
+
info('Next page token: ' + result.nextPageToken);
|
|
1819
|
+
}
|
|
1820
|
+
} catch (err) {
|
|
1821
|
+
error(String(err));
|
|
1822
|
+
process.exit(1);
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
// ============================================
|
|
1827
|
+
// Revisions Commands
|
|
1828
|
+
// ============================================
|
|
1829
|
+
const revisionsCmd = program
|
|
1830
|
+
.command('revisions')
|
|
1831
|
+
.description('File revision history commands');
|
|
1832
|
+
|
|
1833
|
+
revisionsCmd
|
|
1834
|
+
.command('list <fileId>')
|
|
1835
|
+
.description('List revisions of a file')
|
|
1836
|
+
.action(async (fileId: string) => {
|
|
1837
|
+
try {
|
|
1838
|
+
const drive = requireAuth();
|
|
1839
|
+
const result = await drive.revisions.list(fileId);
|
|
1840
|
+
|
|
1841
|
+
if (!result.revisions || result.revisions.length === 0) {
|
|
1842
|
+
info('No revisions found');
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
success('Found ' + result.revisions.length + ' revision(s):');
|
|
1847
|
+
|
|
1848
|
+
const revisions = result.revisions.map((r: any) => ({
|
|
1849
|
+
id: r.id,
|
|
1850
|
+
mimeType: r.mimeType,
|
|
1851
|
+
size: r.size ? formatBytes(r.size) : '-',
|
|
1852
|
+
modified: r.modifiedTime ? new Date(r.modifiedTime).toLocaleString() : '-',
|
|
1853
|
+
keepForever: r.keepForever || false,
|
|
1854
|
+
}));
|
|
1855
|
+
|
|
1856
|
+
print(revisions, getFormat(revisionsCmd));
|
|
1857
|
+
} catch (err) {
|
|
1858
|
+
error(String(err));
|
|
1859
|
+
process.exit(1);
|
|
1860
|
+
}
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
revisionsCmd
|
|
1864
|
+
.command('get <fileId> <revisionId>')
|
|
1865
|
+
.description('Get details of a specific revision')
|
|
1866
|
+
.action(async (fileId: string, revisionId: string) => {
|
|
1867
|
+
try {
|
|
1868
|
+
const drive = requireAuth();
|
|
1869
|
+
const revision = await drive.revisions.get(fileId, revisionId);
|
|
1870
|
+
print(revision, getFormat(revisionsCmd));
|
|
1871
|
+
} catch (err) {
|
|
1872
|
+
error(String(err));
|
|
1873
|
+
process.exit(1);
|
|
1874
|
+
}
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1877
|
+
revisionsCmd
|
|
1878
|
+
.command('download <fileId> <revisionId> [destination]')
|
|
1879
|
+
.description('Download a specific revision')
|
|
1880
|
+
.action(async (fileId: string, revisionId: string, destination?: string) => {
|
|
1881
|
+
try {
|
|
1882
|
+
const drive = requireAuth();
|
|
1883
|
+
info('Downloading revision...');
|
|
1884
|
+
|
|
1885
|
+
const data = await drive.revisions.download(fileId, revisionId);
|
|
1886
|
+
|
|
1887
|
+
if (destination) {
|
|
1888
|
+
writeFileSync(destination, Buffer.from(data));
|
|
1889
|
+
success('Downloaded revision to: ' + destination);
|
|
1890
|
+
} else {
|
|
1891
|
+
const destPath = join(process.cwd(), 'revision-' + revisionId);
|
|
1892
|
+
writeFileSync(destPath, Buffer.from(data));
|
|
1893
|
+
success('Downloaded revision');
|
|
1894
|
+
info('Saved to: ' + destPath);
|
|
1895
|
+
}
|
|
1896
|
+
} catch (err) {
|
|
1897
|
+
error(String(err));
|
|
1898
|
+
process.exit(1);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
revisionsCmd
|
|
1903
|
+
.command('keep-forever <fileId> <revisionId>')
|
|
1904
|
+
.description('Mark a revision to be kept forever (not auto-deleted)')
|
|
1905
|
+
.action(async (fileId: string, revisionId: string) => {
|
|
1906
|
+
try {
|
|
1907
|
+
const drive = requireAuth();
|
|
1908
|
+
await drive.revisions.update(fileId, revisionId, { keepForever: true });
|
|
1909
|
+
success('Revision marked to keep forever');
|
|
1910
|
+
} catch (err) {
|
|
1911
|
+
error(String(err));
|
|
1912
|
+
process.exit(1);
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1916
|
+
revisionsCmd
|
|
1917
|
+
.command('delete <fileId> <revisionId>')
|
|
1918
|
+
.description('Delete a specific revision')
|
|
1919
|
+
.action(async (fileId: string, revisionId: string) => {
|
|
1920
|
+
try {
|
|
1921
|
+
const drive = requireAuth();
|
|
1922
|
+
await drive.revisions.delete(fileId, revisionId);
|
|
1923
|
+
success('Revision deleted');
|
|
1924
|
+
} catch (err) {
|
|
1925
|
+
error(String(err));
|
|
1926
|
+
process.exit(1);
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
|
|
1306
1930
|
// Parse and execute
|
|
1307
1931
|
program.parse();
|