Initial commit
This commit is contained in:
140
skills/google-workspace/drive/create_file.js
Normal file
140
skills/google-workspace/drive/create_file.js
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Create Drive File (Google Docs, Sheets, Slides)
|
||||
*
|
||||
* Usage: node create_file.js <account> --name <name> --type <type> [options]
|
||||
*
|
||||
* Options:
|
||||
* --name File name (required)
|
||||
* --type File type: doc, sheet, slide (required)
|
||||
* --content Initial content (for docs)
|
||||
* --folder Parent folder ID
|
||||
*
|
||||
* Examples:
|
||||
* node create_file.js psd --name "Meeting Notes" --type doc
|
||||
* node create_file.js personal --name "Budget 2024" --type sheet
|
||||
* node create_file.js consulting --name "Proposal" --type slide --folder 1abc123
|
||||
*/
|
||||
|
||||
const { google } = require('googleapis');
|
||||
const path = require('path');
|
||||
const { getAuthClient } = require(path.join(__dirname, '..', 'auth', 'token_manager'));
|
||||
|
||||
const MIME_TYPES = {
|
||||
doc: 'application/vnd.google-apps.document',
|
||||
sheet: 'application/vnd.google-apps.spreadsheet',
|
||||
slide: 'application/vnd.google-apps.presentation',
|
||||
};
|
||||
|
||||
async function createFile(account, options) {
|
||||
const auth = await getAuthClient(account);
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
|
||||
const mimeType = MIME_TYPES[options.type];
|
||||
if (!mimeType) {
|
||||
throw new Error(`Invalid type: ${options.type}. Use: doc, sheet, slide`);
|
||||
}
|
||||
|
||||
const fileMetadata = {
|
||||
name: options.name,
|
||||
mimeType,
|
||||
};
|
||||
|
||||
if (options.folder) {
|
||||
fileMetadata.parents = [options.folder];
|
||||
}
|
||||
|
||||
const response = await drive.files.create({
|
||||
requestBody: fileMetadata,
|
||||
fields: 'id, name, mimeType, webViewLink',
|
||||
});
|
||||
|
||||
const file = response.data;
|
||||
|
||||
// If content provided for doc, update it
|
||||
if (options.content && options.type === 'doc') {
|
||||
const docs = google.docs({ version: 'v1', auth });
|
||||
await docs.documents.batchUpdate({
|
||||
documentId: file.id,
|
||||
requestBody: {
|
||||
requests: [{
|
||||
insertText: {
|
||||
location: { index: 1 },
|
||||
text: options.content,
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
account,
|
||||
file: {
|
||||
id: file.id,
|
||||
name: file.name,
|
||||
mimeType: file.mimeType,
|
||||
webLink: file.webViewLink,
|
||||
},
|
||||
metadata: {
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// CLI interface
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const account = args[0];
|
||||
|
||||
if (!account) {
|
||||
console.error(JSON.stringify({
|
||||
error: 'Missing account',
|
||||
usage: 'node create_file.js <account> --name <name> --type doc|sheet|slide [options]'
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Parse options
|
||||
const options = {};
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
switch (args[i]) {
|
||||
case '--name':
|
||||
options.name = args[++i];
|
||||
break;
|
||||
case '--type':
|
||||
options.type = args[++i];
|
||||
break;
|
||||
case '--content':
|
||||
options.content = args[++i];
|
||||
break;
|
||||
case '--folder':
|
||||
options.folder = args[++i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.name || !options.type) {
|
||||
console.error(JSON.stringify({
|
||||
error: 'Missing required options: --name, --type',
|
||||
usage: 'node create_file.js <account> --name <name> --type doc|sheet|slide'
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await createFile(account, options);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify({
|
||||
error: error.message,
|
||||
account,
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
module.exports = { createFile };
|
||||
92
skills/google-workspace/drive/download_file.js
Normal file
92
skills/google-workspace/drive/download_file.js
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Download Drive File
|
||||
*
|
||||
* Downloads a file from Google Drive to local filesystem.
|
||||
*
|
||||
* Usage: bun download_file.js <account> <fileId> <outputPath>
|
||||
*/
|
||||
|
||||
const { google } = require('googleapis');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { getAuthClient } = require(path.join(__dirname, '..', 'auth', 'token_manager'));
|
||||
|
||||
async function downloadFile(account, fileId, outputPath) {
|
||||
const auth = await getAuthClient(account);
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
|
||||
// Get file metadata first
|
||||
const fileInfo = await drive.files.get({
|
||||
fileId,
|
||||
fields: 'name, mimeType',
|
||||
supportsAllDrives: true
|
||||
});
|
||||
|
||||
// Download file content
|
||||
const response = await drive.files.get(
|
||||
{ fileId, alt: 'media', supportsAllDrives: true },
|
||||
{ responseType: 'stream' }
|
||||
);
|
||||
|
||||
// Ensure output directory exists
|
||||
const outputDir = path.dirname(outputPath);
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Write to file
|
||||
const dest = fs.createWriteStream(outputPath);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
response.data
|
||||
.on('end', () => {
|
||||
resolve({
|
||||
success: true,
|
||||
account,
|
||||
file: {
|
||||
id: fileId,
|
||||
name: fileInfo.data.name,
|
||||
mimeType: fileInfo.data.mimeType,
|
||||
outputPath
|
||||
}
|
||||
});
|
||||
})
|
||||
.on('error', err => {
|
||||
reject(err);
|
||||
})
|
||||
.pipe(dest);
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const account = args[0];
|
||||
const fileId = args[1];
|
||||
const outputPath = args[2];
|
||||
|
||||
if (!account || !fileId || !outputPath) {
|
||||
console.error(JSON.stringify({
|
||||
error: 'Missing arguments',
|
||||
usage: 'bun download_file.js <account> <fileId> <outputPath>'
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await downloadFile(account, fileId, outputPath);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify({
|
||||
error: error.message,
|
||||
account,
|
||||
fileId
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
module.exports = { downloadFile };
|
||||
133
skills/google-workspace/drive/list_files.js
Normal file
133
skills/google-workspace/drive/list_files.js
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* List Drive Files
|
||||
*
|
||||
* Usage: node list_files.js <account> [options]
|
||||
*
|
||||
* Options:
|
||||
* --query Search query (Drive search syntax)
|
||||
* --type File type: doc, sheet, slide, pdf, folder
|
||||
* --max Maximum files to return (default: 20)
|
||||
* --folder Folder ID to list contents of
|
||||
*
|
||||
* Examples:
|
||||
* node list_files.js psd
|
||||
* node list_files.js psd --query "budget"
|
||||
* node list_files.js personal --type sheet
|
||||
* node list_files.js consulting --type pdf --query "contract"
|
||||
*/
|
||||
|
||||
const { google } = require('googleapis');
|
||||
const path = require('path');
|
||||
const { getAuthClient } = require(path.join(__dirname, '..', 'auth', 'token_manager'));
|
||||
|
||||
const MIME_TYPES = {
|
||||
doc: 'application/vnd.google-apps.document',
|
||||
sheet: 'application/vnd.google-apps.spreadsheet',
|
||||
slide: 'application/vnd.google-apps.presentation',
|
||||
pdf: 'application/pdf',
|
||||
folder: 'application/vnd.google-apps.folder',
|
||||
};
|
||||
|
||||
async function listFiles(account, options = {}) {
|
||||
const auth = await getAuthClient(account);
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
|
||||
// Build query
|
||||
const queryParts = [];
|
||||
|
||||
if (options.query) {
|
||||
queryParts.push(`fullText contains '${options.query}'`);
|
||||
}
|
||||
|
||||
if (options.type && MIME_TYPES[options.type]) {
|
||||
queryParts.push(`mimeType = '${MIME_TYPES[options.type]}'`);
|
||||
}
|
||||
|
||||
if (options.folder) {
|
||||
queryParts.push(`'${options.folder}' in parents`);
|
||||
}
|
||||
|
||||
// Exclude trashed files
|
||||
queryParts.push('trashed = false');
|
||||
|
||||
const response = await drive.files.list({
|
||||
q: queryParts.join(' and '),
|
||||
pageSize: options.max || 20,
|
||||
fields: 'files(id, name, mimeType, size, createdTime, modifiedTime, webViewLink, parents)',
|
||||
orderBy: 'modifiedTime desc',
|
||||
supportsAllDrives: true,
|
||||
includeItemsFromAllDrives: true,
|
||||
});
|
||||
|
||||
const files = (response.data.files || []).map(file => ({
|
||||
id: file.id,
|
||||
name: file.name,
|
||||
mimeType: file.mimeType,
|
||||
size: file.size,
|
||||
created: file.createdTime,
|
||||
modified: file.modifiedTime,
|
||||
webLink: file.webViewLink,
|
||||
parents: file.parents,
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
account,
|
||||
files,
|
||||
metadata: {
|
||||
timestamp: new Date().toISOString(),
|
||||
count: files.length,
|
||||
query: options.query || '(all)',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// CLI interface
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const account = args[0];
|
||||
|
||||
if (!account) {
|
||||
console.error(JSON.stringify({
|
||||
error: 'Missing account',
|
||||
usage: 'node list_files.js <account> [--query "..."] [--type doc|sheet|slide|pdf|folder] [--max N]'
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Parse options
|
||||
const options = {};
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
switch (args[i]) {
|
||||
case '--query':
|
||||
options.query = args[++i];
|
||||
break;
|
||||
case '--type':
|
||||
options.type = args[++i];
|
||||
break;
|
||||
case '--max':
|
||||
options.max = parseInt(args[++i], 10);
|
||||
break;
|
||||
case '--folder':
|
||||
options.folder = args[++i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await listFiles(account, options);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify({
|
||||
error: error.message,
|
||||
account,
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
module.exports = { listFiles };
|
||||
130
skills/google-workspace/drive/read_file.js
Normal file
130
skills/google-workspace/drive/read_file.js
Normal file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Read Drive File Content
|
||||
*
|
||||
* Usage: node read_file.js <account> <file-id> [options]
|
||||
*
|
||||
* Options:
|
||||
* --format Export format for Google Docs (text, html, pdf)
|
||||
*
|
||||
* Examples:
|
||||
* node read_file.js psd 1abc123def456
|
||||
* node read_file.js personal 1xyz789 --format html
|
||||
*/
|
||||
|
||||
const { google } = require('googleapis');
|
||||
const path = require('path');
|
||||
const { getAuthClient } = require(path.join(__dirname, '..', 'auth', 'token_manager'));
|
||||
|
||||
const EXPORT_FORMATS = {
|
||||
'application/vnd.google-apps.document': {
|
||||
text: 'text/plain',
|
||||
html: 'text/html',
|
||||
pdf: 'application/pdf',
|
||||
},
|
||||
'application/vnd.google-apps.spreadsheet': {
|
||||
csv: 'text/csv',
|
||||
pdf: 'application/pdf',
|
||||
},
|
||||
'application/vnd.google-apps.presentation': {
|
||||
text: 'text/plain',
|
||||
pdf: 'application/pdf',
|
||||
},
|
||||
};
|
||||
|
||||
async function readFile(account, fileId, options = {}) {
|
||||
const auth = await getAuthClient(account);
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
|
||||
// Get file metadata
|
||||
const metadata = await drive.files.get({
|
||||
fileId,
|
||||
fields: 'id, name, mimeType, size, webViewLink',
|
||||
supportsAllDrives: true,
|
||||
});
|
||||
|
||||
const file = metadata.data;
|
||||
let content = '';
|
||||
|
||||
// Check if it's a Google Workspace file (needs export)
|
||||
if (file.mimeType.startsWith('application/vnd.google-apps.')) {
|
||||
const exportFormats = EXPORT_FORMATS[file.mimeType];
|
||||
if (exportFormats) {
|
||||
const format = options.format || 'text';
|
||||
const mimeType = exportFormats[format] || exportFormats.text || Object.values(exportFormats)[0];
|
||||
|
||||
const response = await drive.files.export({
|
||||
fileId,
|
||||
mimeType,
|
||||
}, { responseType: 'text' });
|
||||
|
||||
content = response.data;
|
||||
} else {
|
||||
content = '[Cannot export this file type]';
|
||||
}
|
||||
} else {
|
||||
// Regular file - download content
|
||||
const response = await drive.files.get({
|
||||
fileId,
|
||||
alt: 'media',
|
||||
supportsAllDrives: true,
|
||||
}, { responseType: 'text' });
|
||||
|
||||
content = response.data;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
account,
|
||||
file: {
|
||||
id: file.id,
|
||||
name: file.name,
|
||||
mimeType: file.mimeType,
|
||||
webLink: file.webViewLink,
|
||||
content,
|
||||
},
|
||||
metadata: {
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// CLI interface
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const account = args[0];
|
||||
const fileId = args[1];
|
||||
|
||||
if (!account || !fileId) {
|
||||
console.error(JSON.stringify({
|
||||
error: 'Missing arguments',
|
||||
usage: 'node read_file.js <account> <file-id> [--format text|html|pdf]'
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Parse options
|
||||
const options = {};
|
||||
for (let i = 2; i < args.length; i++) {
|
||||
if (args[i] === '--format') {
|
||||
options.format = args[++i];
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await readFile(account, fileId, options);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify({
|
||||
error: error.message,
|
||||
account,
|
||||
fileId,
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
module.exports = { readFile };
|
||||
Reference in New Issue
Block a user