@aaronsb/google-workspace-mcp 2.0.0-alpha.4
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/LICENSE +21 -0
- package/README.md +193 -0
- package/build/__helpers__/testSetup.d.ts +1 -0
- package/build/__helpers__/testSetup.js +6 -0
- package/build/__helpers__/testSetup.js.map +1 -0
- package/build/__tests__/accounts/credentials.test.d.ts +1 -0
- package/build/__tests__/accounts/credentials.test.js +129 -0
- package/build/__tests__/accounts/credentials.test.js.map +1 -0
- package/build/__tests__/accounts/registry.test.d.ts +1 -0
- package/build/__tests__/accounts/registry.test.js +74 -0
- package/build/__tests__/accounts/registry.test.js.map +1 -0
- package/build/__tests__/executor/errors.test.d.ts +1 -0
- package/build/__tests__/executor/errors.test.js +42 -0
- package/build/__tests__/executor/errors.test.js.map +1 -0
- package/build/__tests__/executor/gws.test.d.ts +1 -0
- package/build/__tests__/executor/gws.test.js +178 -0
- package/build/__tests__/executor/gws.test.js.map +1 -0
- package/build/__tests__/executor/paths.test.d.ts +1 -0
- package/build/__tests__/executor/paths.test.js +60 -0
- package/build/__tests__/executor/paths.test.js.map +1 -0
- package/build/__tests__/executor/workspace.test.d.ts +1 -0
- package/build/__tests__/executor/workspace.test.js +117 -0
- package/build/__tests__/executor/workspace.test.js.map +1 -0
- package/build/__tests__/factory/generator.test.d.ts +1 -0
- package/build/__tests__/factory/generator.test.js +178 -0
- package/build/__tests__/factory/generator.test.js.map +1 -0
- package/build/__tests__/factory/patch-coverage.test.d.ts +10 -0
- package/build/__tests__/factory/patch-coverage.test.js +148 -0
- package/build/__tests__/factory/patch-coverage.test.js.map +1 -0
- package/build/__tests__/factory/safety.test.d.ts +1 -0
- package/build/__tests__/factory/safety.test.js +107 -0
- package/build/__tests__/factory/safety.test.js.map +1 -0
- package/build/__tests__/integration/executor.test.d.ts +5 -0
- package/build/__tests__/integration/executor.test.js +46 -0
- package/build/__tests__/integration/executor.test.js.map +1 -0
- package/build/__tests__/integration/handlers.test.d.ts +6 -0
- package/build/__tests__/integration/handlers.test.js +95 -0
- package/build/__tests__/integration/handlers.test.js.map +1 -0
- package/build/__tests__/integration/setup.d.ts +19 -0
- package/build/__tests__/integration/setup.js +61 -0
- package/build/__tests__/integration/setup.js.map +1 -0
- package/build/__tests__/server/formatting/markdown.test.d.ts +1 -0
- package/build/__tests__/server/formatting/markdown.test.js +149 -0
- package/build/__tests__/server/formatting/markdown.test.js.map +1 -0
- package/build/__tests__/server/formatting/next-steps.test.d.ts +1 -0
- package/build/__tests__/server/formatting/next-steps.test.js +42 -0
- package/build/__tests__/server/formatting/next-steps.test.js.map +1 -0
- package/build/__tests__/server/handler.test.d.ts +1 -0
- package/build/__tests__/server/handler.test.js +97 -0
- package/build/__tests__/server/handler.test.js.map +1 -0
- package/build/__tests__/server/handlers/__mocks__/executor.d.ts +147 -0
- package/build/__tests__/server/handlers/__mocks__/executor.js +114 -0
- package/build/__tests__/server/handlers/__mocks__/executor.js.map +1 -0
- package/build/__tests__/server/handlers/accounts.test.d.ts +1 -0
- package/build/__tests__/server/handlers/accounts.test.js +127 -0
- package/build/__tests__/server/handlers/accounts.test.js.map +1 -0
- package/build/__tests__/server/handlers/calendar.test.d.ts +1 -0
- package/build/__tests__/server/handlers/calendar.test.js +95 -0
- package/build/__tests__/server/handlers/calendar.test.js.map +1 -0
- package/build/__tests__/server/handlers/drive.test.d.ts +1 -0
- package/build/__tests__/server/handlers/drive.test.js +81 -0
- package/build/__tests__/server/handlers/drive.test.js.map +1 -0
- package/build/__tests__/server/handlers/email.test.d.ts +1 -0
- package/build/__tests__/server/handlers/email.test.js +99 -0
- package/build/__tests__/server/handlers/email.test.js.map +1 -0
- package/build/__tests__/server/handlers/validate.test.d.ts +1 -0
- package/build/__tests__/server/handlers/validate.test.js +88 -0
- package/build/__tests__/server/handlers/validate.test.js.map +1 -0
- package/build/__tests__/server/queue.test.d.ts +1 -0
- package/build/__tests__/server/queue.test.js +194 -0
- package/build/__tests__/server/queue.test.js.map +1 -0
- package/build/__tests__/server/server.test.d.ts +7 -0
- package/build/__tests__/server/server.test.js +135 -0
- package/build/__tests__/server/server.test.js.map +1 -0
- package/build/__tests__/server/tools.test.d.ts +1 -0
- package/build/__tests__/server/tools.test.js +91 -0
- package/build/__tests__/server/tools.test.js.map +1 -0
- package/build/accounts/auth.d.ts +24 -0
- package/build/accounts/auth.js +118 -0
- package/build/accounts/auth.js.map +1 -0
- package/build/accounts/credentials.d.ts +11 -0
- package/build/accounts/credentials.js +52 -0
- package/build/accounts/credentials.js.map +1 -0
- package/build/accounts/index.d.ts +6 -0
- package/build/accounts/index.js +4 -0
- package/build/accounts/index.js.map +1 -0
- package/build/accounts/registry.d.ts +11 -0
- package/build/accounts/registry.js +62 -0
- package/build/accounts/registry.js.map +1 -0
- package/build/executor/errors.d.ts +15 -0
- package/build/executor/errors.js +37 -0
- package/build/executor/errors.js.map +1 -0
- package/build/executor/file-output.d.ts +23 -0
- package/build/executor/file-output.js +67 -0
- package/build/executor/file-output.js.map +1 -0
- package/build/executor/gws.d.ts +23 -0
- package/build/executor/gws.js +144 -0
- package/build/executor/gws.js.map +1 -0
- package/build/executor/index.d.ts +4 -0
- package/build/executor/index.js +4 -0
- package/build/executor/index.js.map +1 -0
- package/build/executor/paths.d.ts +6 -0
- package/build/executor/paths.js +25 -0
- package/build/executor/paths.js.map +1 -0
- package/build/executor/workspace.d.ts +38 -0
- package/build/executor/workspace.js +146 -0
- package/build/executor/workspace.js.map +1 -0
- package/build/factory/defaults.d.ts +8 -0
- package/build/factory/defaults.js +101 -0
- package/build/factory/defaults.js.map +1 -0
- package/build/factory/generator.d.ts +23 -0
- package/build/factory/generator.js +253 -0
- package/build/factory/generator.js.map +1 -0
- package/build/factory/manifest.yaml +852 -0
- package/build/factory/patches.d.ts +6 -0
- package/build/factory/patches.js +13 -0
- package/build/factory/patches.js.map +1 -0
- package/build/factory/registry.d.ts +12 -0
- package/build/factory/registry.js +18 -0
- package/build/factory/registry.js.map +1 -0
- package/build/factory/safety.d.ts +68 -0
- package/build/factory/safety.js +157 -0
- package/build/factory/safety.js.map +1 -0
- package/build/factory/types.d.ts +102 -0
- package/build/factory/types.js +6 -0
- package/build/factory/types.js.map +1 -0
- package/build/factory/yaml.d.ts +2 -0
- package/build/factory/yaml.js +3 -0
- package/build/factory/yaml.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +14 -0
- package/build/index.js.map +1 -0
- package/build/server/formatting/markdown.d.ts +21 -0
- package/build/server/formatting/markdown.js +324 -0
- package/build/server/formatting/markdown.js.map +1 -0
- package/build/server/formatting/next-steps.d.ts +9 -0
- package/build/server/formatting/next-steps.js +123 -0
- package/build/server/formatting/next-steps.js.map +1 -0
- package/build/server/handler.d.ts +3 -0
- package/build/server/handler.js +24 -0
- package/build/server/handler.js.map +1 -0
- package/build/server/handlers/accounts.d.ts +2 -0
- package/build/server/handlers/accounts.js +181 -0
- package/build/server/handlers/accounts.js.map +1 -0
- package/build/server/handlers/calendar.d.ts +2 -0
- package/build/server/handlers/calendar.js +93 -0
- package/build/server/handlers/calendar.js.map +1 -0
- package/build/server/handlers/drive.d.ts +2 -0
- package/build/server/handlers/drive.js +74 -0
- package/build/server/handlers/drive.js.map +1 -0
- package/build/server/handlers/email.d.ts +2 -0
- package/build/server/handlers/email.js +115 -0
- package/build/server/handlers/email.js.map +1 -0
- package/build/server/handlers/validate.d.ts +3 -0
- package/build/server/handlers/validate.js +22 -0
- package/build/server/handlers/validate.js.map +1 -0
- package/build/server/handlers/workspace.d.ts +9 -0
- package/build/server/handlers/workspace.js +110 -0
- package/build/server/handlers/workspace.js.map +1 -0
- package/build/server/index.d.ts +4 -0
- package/build/server/index.js +4 -0
- package/build/server/index.js.map +1 -0
- package/build/server/queue.d.ts +11 -0
- package/build/server/queue.js +141 -0
- package/build/server/queue.js.map +1 -0
- package/build/server/server.d.ts +3 -0
- package/build/server/server.js +214 -0
- package/build/server/server.js.map +1 -0
- package/build/server/tools.d.ts +13 -0
- package/build/server/tools.js +99 -0
- package/build/server/tools.js.map +1 -0
- package/build/services/calendar/patch.d.ts +11 -0
- package/build/services/calendar/patch.js +116 -0
- package/build/services/calendar/patch.js.map +1 -0
- package/build/services/drive/patch.d.ts +10 -0
- package/build/services/drive/patch.js +131 -0
- package/build/services/drive/patch.js.map +1 -0
- package/build/services/gmail/attachments.d.ts +19 -0
- package/build/services/gmail/attachments.js +90 -0
- package/build/services/gmail/attachments.js.map +1 -0
- package/build/services/gmail/patch.d.ts +10 -0
- package/build/services/gmail/patch.js +226 -0
- package/build/services/gmail/patch.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { credentialPath, credentialsDir } from '../executor/paths.js';
|
|
4
|
+
import { execute } from '../executor/gws.js';
|
|
5
|
+
export async function hasCredential(email) {
|
|
6
|
+
try {
|
|
7
|
+
await fs.access(credentialPath(email));
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function exportAndSaveCredential(email) {
|
|
15
|
+
// --unmasked is required; without it gws masks the client_secret
|
|
16
|
+
const result = await execute(['auth', 'export', '--unmasked']);
|
|
17
|
+
const credential = result.data;
|
|
18
|
+
if (credential?.type !== 'authorized_user') {
|
|
19
|
+
throw new Error('gws auth export did not return an authorized_user credential');
|
|
20
|
+
}
|
|
21
|
+
const filePath = credentialPath(email);
|
|
22
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
23
|
+
await fs.writeFile(filePath, JSON.stringify(credential, null, 2), { mode: 0o600 });
|
|
24
|
+
return filePath;
|
|
25
|
+
}
|
|
26
|
+
export async function readCredential(email) {
|
|
27
|
+
const filePath = credentialPath(email);
|
|
28
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
29
|
+
return JSON.parse(content);
|
|
30
|
+
}
|
|
31
|
+
export async function removeCredential(email) {
|
|
32
|
+
try {
|
|
33
|
+
await fs.unlink(credentialPath(email));
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (err.code !== 'ENOENT')
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function listCredentials() {
|
|
41
|
+
try {
|
|
42
|
+
const dir = credentialsDir();
|
|
43
|
+
const files = await fs.readdir(dir);
|
|
44
|
+
return files
|
|
45
|
+
.filter(f => f.endsWith('.json'))
|
|
46
|
+
.map(f => f.replace(/\.json$/, ''));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/accounts/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAS7C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAAa;IACzD,iEAAiE;IACjE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAgC,CAAC;IAE3D,IAAI,UAAU,EAAE,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEnF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAa;IAChD,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAA6B,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAa;IAClD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { authenticateAccount } from './auth.js';
|
|
2
|
+
export type { AuthResult } from './auth.js';
|
|
3
|
+
export { hasCredential, readCredential, removeCredential, listCredentials, exportAndSaveCredential } from './credentials.js';
|
|
4
|
+
export type { AuthorizedUserCredential } from './credentials.js';
|
|
5
|
+
export { listAccounts, getAccount, addAccount, removeAccount, authenticateAndAddAccount } from './registry.js';
|
|
6
|
+
export type { Account } from './registry.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { authenticateAccount } from './auth.js';
|
|
2
|
+
export { hasCredential, readCredential, removeCredential, listCredentials, exportAndSaveCredential } from './credentials.js';
|
|
3
|
+
export { listAccounts, getAccount, addAccount, removeAccount, authenticateAndAddAccount } from './registry.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/accounts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE7H,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type AuthResult } from './auth.js';
|
|
2
|
+
export interface Account {
|
|
3
|
+
email: string;
|
|
4
|
+
category: 'personal' | 'work' | 'other';
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function listAccounts(): Promise<Account[]>;
|
|
8
|
+
export declare function getAccount(email: string): Promise<Account | undefined>;
|
|
9
|
+
export declare function addAccount(email: string, category?: Account['category'], description?: string): Promise<Account>;
|
|
10
|
+
export declare function removeAccount(email: string): Promise<void>;
|
|
11
|
+
export declare function authenticateAndAddAccount(clientId: string, clientSecret: string, category?: Account['category'], description?: string): Promise<AuthResult>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { accountsFilePath, configDir } from '../executor/paths.js';
|
|
3
|
+
import { hasCredential, removeCredential } from './credentials.js';
|
|
4
|
+
import { authenticateAccount } from './auth.js';
|
|
5
|
+
async function readAccounts() {
|
|
6
|
+
try {
|
|
7
|
+
const content = await fs.readFile(accountsFilePath(), 'utf-8');
|
|
8
|
+
return JSON.parse(content);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { accounts: [] };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function writeAccounts(data) {
|
|
15
|
+
const dir = configDir();
|
|
16
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
17
|
+
await fs.writeFile(accountsFilePath(), JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
18
|
+
}
|
|
19
|
+
export async function listAccounts() {
|
|
20
|
+
const { accounts } = await readAccounts();
|
|
21
|
+
// Enrich with credential status
|
|
22
|
+
const enriched = await Promise.all(accounts.map(async (account) => ({
|
|
23
|
+
...account,
|
|
24
|
+
hasCredential: await hasCredential(account.email),
|
|
25
|
+
})));
|
|
26
|
+
return enriched;
|
|
27
|
+
}
|
|
28
|
+
export async function getAccount(email) {
|
|
29
|
+
const { accounts } = await readAccounts();
|
|
30
|
+
return accounts.find(a => a.email === email);
|
|
31
|
+
}
|
|
32
|
+
export async function addAccount(email, category = 'personal', description) {
|
|
33
|
+
const data = await readAccounts();
|
|
34
|
+
if (data.accounts.some(a => a.email === email)) {
|
|
35
|
+
throw new Error(`Account ${email} already exists`);
|
|
36
|
+
}
|
|
37
|
+
const account = { email, category, description };
|
|
38
|
+
data.accounts.push(account);
|
|
39
|
+
await writeAccounts(data);
|
|
40
|
+
return account;
|
|
41
|
+
}
|
|
42
|
+
export async function removeAccount(email) {
|
|
43
|
+
const data = await readAccounts();
|
|
44
|
+
const index = data.accounts.findIndex(a => a.email === email);
|
|
45
|
+
if (index === -1) {
|
|
46
|
+
throw new Error(`Account ${email} not found`);
|
|
47
|
+
}
|
|
48
|
+
data.accounts.splice(index, 1);
|
|
49
|
+
await writeAccounts(data);
|
|
50
|
+
await removeCredential(email);
|
|
51
|
+
}
|
|
52
|
+
export async function authenticateAndAddAccount(clientId, clientSecret, category = 'personal', description) {
|
|
53
|
+
const result = await authenticateAccount(clientId, clientSecret);
|
|
54
|
+
if (result.status === 'success' && result.account) {
|
|
55
|
+
const existing = await getAccount(result.account);
|
|
56
|
+
if (!existing) {
|
|
57
|
+
await addAccount(result.account, category, description);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/accounts/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare enum GwsExitCode {
|
|
2
|
+
Success = 0,
|
|
3
|
+
ApiError = 1,
|
|
4
|
+
AuthError = 2,
|
|
5
|
+
ValidationError = 3,
|
|
6
|
+
DiscoveryError = 4,
|
|
7
|
+
InternalError = 5
|
|
8
|
+
}
|
|
9
|
+
export declare class GwsError extends Error {
|
|
10
|
+
readonly exitCode: GwsExitCode;
|
|
11
|
+
readonly reason?: string | undefined;
|
|
12
|
+
readonly stderr?: string | undefined;
|
|
13
|
+
constructor(message: string, exitCode: GwsExitCode, reason?: string | undefined, stderr?: string | undefined);
|
|
14
|
+
}
|
|
15
|
+
export declare function parseGwsError(exitCode: number, stdout: string, stderr: string): GwsError;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export var GwsExitCode;
|
|
2
|
+
(function (GwsExitCode) {
|
|
3
|
+
GwsExitCode[GwsExitCode["Success"] = 0] = "Success";
|
|
4
|
+
GwsExitCode[GwsExitCode["ApiError"] = 1] = "ApiError";
|
|
5
|
+
GwsExitCode[GwsExitCode["AuthError"] = 2] = "AuthError";
|
|
6
|
+
GwsExitCode[GwsExitCode["ValidationError"] = 3] = "ValidationError";
|
|
7
|
+
GwsExitCode[GwsExitCode["DiscoveryError"] = 4] = "DiscoveryError";
|
|
8
|
+
GwsExitCode[GwsExitCode["InternalError"] = 5] = "InternalError";
|
|
9
|
+
})(GwsExitCode || (GwsExitCode = {}));
|
|
10
|
+
export class GwsError extends Error {
|
|
11
|
+
exitCode;
|
|
12
|
+
reason;
|
|
13
|
+
stderr;
|
|
14
|
+
constructor(message, exitCode, reason, stderr) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.exitCode = exitCode;
|
|
17
|
+
this.reason = reason;
|
|
18
|
+
this.stderr = stderr;
|
|
19
|
+
this.name = 'GwsError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function parseGwsError(exitCode, stdout, stderr) {
|
|
23
|
+
// Try to extract structured error from stdout
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(stdout);
|
|
26
|
+
if (parsed?.error) {
|
|
27
|
+
return new GwsError(parsed.error.message || 'Unknown gws error', exitCode, parsed.error.reason, stderr);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// stdout wasn't JSON — fall through
|
|
32
|
+
}
|
|
33
|
+
const label = GwsExitCode[exitCode] ?? 'Unknown';
|
|
34
|
+
const message = stderr.trim() || `gws exited with code ${exitCode} (${label})`;
|
|
35
|
+
return new GwsError(message, exitCode, undefined, stderr);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/executor/errors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,WAOX;AAPD,WAAY,WAAW;IACrB,mDAAW,CAAA;IACX,qDAAY,CAAA;IACZ,uDAAa,CAAA;IACb,mEAAmB,CAAA;IACnB,iEAAkB,CAAA;IAClB,+DAAiB,CAAA;AACnB,CAAC,EAPW,WAAW,KAAX,WAAW,QAOtB;AAED,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGf;IACA;IACA;IAJlB,YACE,OAAe,EACC,QAAqB,EACrB,MAAe,EACf,MAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,aAAQ,GAAR,QAAQ,CAAa;QACrB,WAAM,GAAN,MAAM,CAAS;QACf,WAAM,GAAN,MAAM,CAAS;QAG/B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAc,EAAE,MAAc;IAC5E,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,MAAM,EAAE,KAAK,EAAE,CAAC;YAClB,OAAO,IAAI,QAAQ,CACjB,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,mBAAmB,EAC3C,QAAuB,EACvB,MAAM,CAAC,KAAK,CAAC,MAAM,EACnB,MAAM,CACP,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oCAAoC;IACtC,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,QAAuB,CAAC,IAAI,SAAS,CAAC;IAChE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,wBAAwB,QAAQ,KAAK,KAAK,GAAG,CAAC;IAC/E,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,QAAuB,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File output utility — saves files to workspace and returns content inline
|
|
3
|
+
* when possible. Solves the containerization problem: agents running in
|
|
4
|
+
* sandboxed environments (Claude Desktop) can't read the MCP server's
|
|
5
|
+
* local filesystem, so text content must be included in the response.
|
|
6
|
+
*
|
|
7
|
+
* Used by: getAttachment (gmail), download (drive), export (drive)
|
|
8
|
+
*/
|
|
9
|
+
/** Check if a file should have its content returned inline. */
|
|
10
|
+
export declare function isTextFile(filename: string, mimeType?: string): boolean;
|
|
11
|
+
export interface FileOutputResult {
|
|
12
|
+
filename: string;
|
|
13
|
+
path: string;
|
|
14
|
+
size: number;
|
|
15
|
+
/** Text content included inline for containerized agents. Undefined for binary files. */
|
|
16
|
+
content?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Save a buffer to the workspace directory and optionally return text content inline.
|
|
20
|
+
*/
|
|
21
|
+
export declare function saveToWorkspace(filename: string, buffer: Buffer, mimeType?: string): Promise<FileOutputResult>;
|
|
22
|
+
/** Format a file output result as markdown for the MCP response. */
|
|
23
|
+
export declare function formatFileOutput(result: FileOutputResult): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File output utility — saves files to workspace and returns content inline
|
|
3
|
+
* when possible. Solves the containerization problem: agents running in
|
|
4
|
+
* sandboxed environments (Claude Desktop) can't read the MCP server's
|
|
5
|
+
* local filesystem, so text content must be included in the response.
|
|
6
|
+
*
|
|
7
|
+
* Used by: getAttachment (gmail), download (drive), export (drive)
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'node:fs/promises';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import { ensureWorkspaceDir, resolveWorkspacePath, verifyPathSafety } from './workspace.js';
|
|
12
|
+
/** MIME types and extensions considered text-safe for inline return. */
|
|
13
|
+
const TEXT_MIME_PREFIXES = [
|
|
14
|
+
'text/', 'application/json', 'application/xml', 'application/javascript',
|
|
15
|
+
'application/x-yaml', 'application/toml', 'application/csv',
|
|
16
|
+
];
|
|
17
|
+
const TEXT_EXTENSIONS = [
|
|
18
|
+
'.md', '.txt', '.csv', '.json', '.yaml', '.yml', '.xml', '.html',
|
|
19
|
+
'.htm', '.eml', '.log', '.ini', '.toml', '.js', '.ts', '.py',
|
|
20
|
+
'.sh', '.bash', '.zsh', '.css', '.svg',
|
|
21
|
+
];
|
|
22
|
+
const MAX_INLINE_SIZE = 100_000; // 100KB — larger text files are saved only
|
|
23
|
+
/** Check if a file should have its content returned inline. */
|
|
24
|
+
export function isTextFile(filename, mimeType) {
|
|
25
|
+
if (mimeType && TEXT_MIME_PREFIXES.some(p => mimeType.startsWith(p)))
|
|
26
|
+
return true;
|
|
27
|
+
const ext = path.extname(filename).toLowerCase();
|
|
28
|
+
return TEXT_EXTENSIONS.includes(ext);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Save a buffer to the workspace directory and optionally return text content inline.
|
|
32
|
+
*/
|
|
33
|
+
export async function saveToWorkspace(filename, buffer, mimeType) {
|
|
34
|
+
const wsStatus = await ensureWorkspaceDir();
|
|
35
|
+
if (!wsStatus.valid) {
|
|
36
|
+
throw new Error(`Workspace directory invalid: ${wsStatus.warning}`);
|
|
37
|
+
}
|
|
38
|
+
const outputPath = resolveWorkspacePath(filename);
|
|
39
|
+
await verifyPathSafety(outputPath);
|
|
40
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
41
|
+
await fs.writeFile(outputPath, buffer);
|
|
42
|
+
const result = {
|
|
43
|
+
filename,
|
|
44
|
+
path: outputPath,
|
|
45
|
+
size: buffer.length,
|
|
46
|
+
};
|
|
47
|
+
if (isTextFile(filename, mimeType) && buffer.length < MAX_INLINE_SIZE) {
|
|
48
|
+
result.content = buffer.toString('utf-8');
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/** Format a file output result as markdown for the MCP response. */
|
|
53
|
+
export function formatFileOutput(result) {
|
|
54
|
+
const parts = [
|
|
55
|
+
`**${result.filename}** saved to workspace`,
|
|
56
|
+
'',
|
|
57
|
+
`**Path:** ${result.path}`,
|
|
58
|
+
`**Size:** ${result.size} bytes`,
|
|
59
|
+
];
|
|
60
|
+
if (result.content) {
|
|
61
|
+
// Escape any triple backticks in the content to prevent markdown fence injection
|
|
62
|
+
const safeContent = result.content.replace(/```/g, '` ` `');
|
|
63
|
+
parts.push('', '---', '', '```', safeContent, '```');
|
|
64
|
+
}
|
|
65
|
+
return parts.join('\n');
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=file-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-output.js","sourceRoot":"","sources":["../../src/executor/file-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface GwsResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
data: unknown;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}
|
|
6
|
+
export interface GwsOptions {
|
|
7
|
+
account?: string;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
format?: 'json' | 'table' | 'yaml' | 'csv';
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve gws binary location.
|
|
13
|
+
*
|
|
14
|
+
* Priority:
|
|
15
|
+
* 1. GWS_BINARY_PATH env var (set by mcpb manifest for bundled binary)
|
|
16
|
+
* - If path is a directory, appends platform-appropriate binary name
|
|
17
|
+
* - If path is a file, uses it directly
|
|
18
|
+
* 2. node_modules/.bin/gws (npm dependency)
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveGwsBinary(): string;
|
|
21
|
+
export declare function resolvePackageBinDir(): string;
|
|
22
|
+
export declare function execute(args: string[], options?: GwsOptions): Promise<GwsResult>;
|
|
23
|
+
export declare function gwsVersion(): Promise<string>;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { credentialPath } from './paths.js';
|
|
4
|
+
import { GwsExitCode, GwsError, parseGwsError } from './errors.js';
|
|
5
|
+
const DEFAULT_TIMEOUT = 120_000; // Hard ceiling (2min) — process dies after this regardless
|
|
6
|
+
const STALL_TIMEOUT = 30_000; // Kill if no stdout/stderr activity for 30s
|
|
7
|
+
// (generous: gws may do OAuth refresh, API pagination, cold start)
|
|
8
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
9
|
+
const GWS_BINARY_NAME = IS_WINDOWS ? 'gws.exe' : 'gws';
|
|
10
|
+
/**
|
|
11
|
+
* Resolve gws binary location.
|
|
12
|
+
*
|
|
13
|
+
* Priority:
|
|
14
|
+
* 1. GWS_BINARY_PATH env var (set by mcpb manifest for bundled binary)
|
|
15
|
+
* - If path is a directory, appends platform-appropriate binary name
|
|
16
|
+
* - If path is a file, uses it directly
|
|
17
|
+
* 2. node_modules/.bin/gws (npm dependency)
|
|
18
|
+
*/
|
|
19
|
+
export function resolveGwsBinary() {
|
|
20
|
+
const envPath = process.env.GWS_BINARY_PATH;
|
|
21
|
+
if (envPath) {
|
|
22
|
+
// Support both direct binary path and directory path
|
|
23
|
+
if (envPath.endsWith('.exe') || envPath.endsWith('/gws') || envPath.endsWith('\\gws')) {
|
|
24
|
+
return envPath;
|
|
25
|
+
}
|
|
26
|
+
return path.join(envPath, GWS_BINARY_NAME);
|
|
27
|
+
}
|
|
28
|
+
return path.join(process.cwd(), 'node_modules', '.bin', GWS_BINARY_NAME);
|
|
29
|
+
}
|
|
30
|
+
// Kept for backward compatibility with tests
|
|
31
|
+
export function resolvePackageBinDir() {
|
|
32
|
+
return path.join(process.cwd(), 'node_modules', '.bin');
|
|
33
|
+
}
|
|
34
|
+
// Stderr lines that are diagnostic noise, not errors
|
|
35
|
+
const STDERR_NOISE = [
|
|
36
|
+
/^Using keyring backend:/,
|
|
37
|
+
];
|
|
38
|
+
function filterStderr(stderr) {
|
|
39
|
+
return stderr
|
|
40
|
+
.split('\n')
|
|
41
|
+
.filter(line => !STDERR_NOISE.some(pattern => pattern.test(line)))
|
|
42
|
+
.join('\n')
|
|
43
|
+
.trim();
|
|
44
|
+
}
|
|
45
|
+
export async function execute(args, options = {}) {
|
|
46
|
+
const { account, timeout = DEFAULT_TIMEOUT, format = 'json' } = options;
|
|
47
|
+
const env = { ...process.env };
|
|
48
|
+
if (account) {
|
|
49
|
+
env.GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE = credentialPath(account);
|
|
50
|
+
}
|
|
51
|
+
const fullArgs = [...args, '--format', format];
|
|
52
|
+
// Resolve gws binary — bundled (mcpb) or npm dependency
|
|
53
|
+
const gwsBinary = resolveGwsBinary();
|
|
54
|
+
process.stderr.write(`[gws-mcp] exec: ${gwsBinary} ${fullArgs.join(' ')}\n`);
|
|
55
|
+
if (account) {
|
|
56
|
+
process.stderr.write(`[gws-mcp] cred: ${env.GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE}\n`);
|
|
57
|
+
}
|
|
58
|
+
// Still prepend bin dir to PATH for non-bundled case
|
|
59
|
+
env.PATH = `${resolvePackageBinDir()}:${env.PATH || ''}`;
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
let settled = false;
|
|
62
|
+
const settle = (fn) => { if (!settled) {
|
|
63
|
+
settled = true;
|
|
64
|
+
fn();
|
|
65
|
+
} };
|
|
66
|
+
const proc = spawn(gwsBinary, fullArgs, { env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
67
|
+
let stdout = '';
|
|
68
|
+
let stderr = '';
|
|
69
|
+
// Activity-based stall detection: kill if process goes silent
|
|
70
|
+
let lastActivity = Date.now();
|
|
71
|
+
const resetStall = () => { lastActivity = Date.now(); };
|
|
72
|
+
proc.stdout.on('data', (chunk) => { stdout += chunk.toString(); resetStall(); });
|
|
73
|
+
proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); resetStall(); });
|
|
74
|
+
const killProc = (reason) => {
|
|
75
|
+
process.stderr.write(`[gws-mcp] ${reason}\n`);
|
|
76
|
+
proc.kill('SIGTERM');
|
|
77
|
+
const killTimer = setTimeout(() => {
|
|
78
|
+
try {
|
|
79
|
+
proc.kill('SIGKILL');
|
|
80
|
+
}
|
|
81
|
+
catch { /* already dead */ }
|
|
82
|
+
}, 3000);
|
|
83
|
+
killTimer.unref();
|
|
84
|
+
};
|
|
85
|
+
// Stall detector — checks periodically if the process has gone silent
|
|
86
|
+
const stallCheck = setInterval(() => {
|
|
87
|
+
if (Date.now() - lastActivity > STALL_TIMEOUT) {
|
|
88
|
+
clearInterval(stallCheck);
|
|
89
|
+
killProc(`stall: no output for ${STALL_TIMEOUT / 1000}s, killing gws`);
|
|
90
|
+
settle(() => reject(new GwsError(`gws stalled (no output for ${STALL_TIMEOUT / 1000}s)`, GwsExitCode.InternalError, 'stall', stderr)));
|
|
91
|
+
}
|
|
92
|
+
}, 5000);
|
|
93
|
+
stallCheck.unref();
|
|
94
|
+
// Hard timeout — absolute ceiling regardless of activity
|
|
95
|
+
const timer = setTimeout(() => {
|
|
96
|
+
clearInterval(stallCheck);
|
|
97
|
+
killProc(`hard timeout: killing gws after ${timeout / 1000}s`);
|
|
98
|
+
settle(() => reject(new GwsError('gws command timed out', GwsExitCode.InternalError, 'timeout', stderr)));
|
|
99
|
+
}, timeout);
|
|
100
|
+
proc.on('error', (err) => {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
process.stderr.write(`[gws-mcp] spawn error: ${err.message}\n`);
|
|
103
|
+
settle(() => reject(new GwsError(`Failed to spawn gws: ${err.message}`, GwsExitCode.InternalError, 'spawn_error', stderr)));
|
|
104
|
+
});
|
|
105
|
+
// Ensure child process is cleaned up if the parent exits
|
|
106
|
+
const cleanup = () => { try {
|
|
107
|
+
proc.kill('SIGTERM');
|
|
108
|
+
}
|
|
109
|
+
catch { /* already dead */ } };
|
|
110
|
+
process.once('exit', cleanup);
|
|
111
|
+
proc.on('close', (code) => {
|
|
112
|
+
clearInterval(stallCheck);
|
|
113
|
+
process.removeListener('exit', cleanup);
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
const exitCode = code ?? 1;
|
|
116
|
+
const filteredStderr = filterStderr(stderr);
|
|
117
|
+
if (exitCode !== GwsExitCode.Success) {
|
|
118
|
+
settle(() => reject(parseGwsError(exitCode, stdout, filteredStderr)));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Parse JSON output
|
|
122
|
+
let data;
|
|
123
|
+
if (format === 'json' && stdout.trim()) {
|
|
124
|
+
try {
|
|
125
|
+
data = JSON.parse(stdout);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
settle(() => reject(new GwsError('Failed to parse gws JSON output', GwsExitCode.InternalError, 'parse_error', stdout)));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
data = stdout;
|
|
134
|
+
}
|
|
135
|
+
settle(() => resolve({ success: true, data, stderr: filteredStderr }));
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export async function gwsVersion() {
|
|
140
|
+
// --version outputs plain text, not JSON
|
|
141
|
+
const result = await execute(['--version'], { format: 'table' });
|
|
142
|
+
return String(result.data).trim();
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=gws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gws.js","sourceRoot":"","sources":["../../src/executor/gws.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { execute, gwsVersion } from './gws.js';
|
|
2
|
+
export type { GwsResult, GwsOptions } from './gws.js';
|
|
3
|
+
export { GwsError, GwsExitCode, parseGwsError } from './errors.js';
|
|
4
|
+
export { configDir, dataDir, credentialsDir, credentialPath, accountsFilePath, emailToSlug } from './paths.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/executor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function configDir(): string;
|
|
2
|
+
export declare function dataDir(): string;
|
|
3
|
+
export declare function credentialsDir(): string;
|
|
4
|
+
export declare function emailToSlug(email: string): string;
|
|
5
|
+
export declare function credentialPath(email: string): string;
|
|
6
|
+
export declare function accountsFilePath(): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
const APP_NAME = 'google-workspace-mcp';
|
|
4
|
+
export function configDir() {
|
|
5
|
+
const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
|
|
6
|
+
return path.join(base, APP_NAME);
|
|
7
|
+
}
|
|
8
|
+
export function dataDir() {
|
|
9
|
+
const base = process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');
|
|
10
|
+
return path.join(base, APP_NAME);
|
|
11
|
+
}
|
|
12
|
+
export function credentialsDir() {
|
|
13
|
+
return path.join(dataDir(), 'credentials');
|
|
14
|
+
}
|
|
15
|
+
export function emailToSlug(email) {
|
|
16
|
+
// Preserve uniqueness: @ → _at_, dots → _dot_, hyphens stay as-is
|
|
17
|
+
return email.replace(/@/g, '_at_').replace(/\./g, '_dot_');
|
|
18
|
+
}
|
|
19
|
+
export function credentialPath(email) {
|
|
20
|
+
return path.join(credentialsDir(), `${emailToSlug(email)}.json`);
|
|
21
|
+
}
|
|
22
|
+
export function accountsFilePath() {
|
|
23
|
+
return path.join(configDir(), 'accounts.json');
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/executor/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,MAAM,QAAQ,GAAG,sBAAsB,CAAC;AAExC,MAAM,UAAU,SAAS;IACvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,kEAAkE;IAClE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,eAAe,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace directory — safe sandbox for file I/O operations.
|
|
3
|
+
*
|
|
4
|
+
* All file operations (Drive upload/download, Docs export, Sheets CSV export)
|
|
5
|
+
* are jailed to this directory. Prevents agents from accidentally operating on
|
|
6
|
+
* home directories, document folders, or Google Drive mount points.
|
|
7
|
+
*/
|
|
8
|
+
/** Validate and return the workspace directory path. */
|
|
9
|
+
export declare function getWorkspaceDir(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Validate workspace dir is safe. Throws if it IS a protected directory.
|
|
12
|
+
* Being a subdirectory OF a protected directory is fine (e.g. ~/Documents/mcp-workspace/).
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateWorkspaceDir(dir: string): void;
|
|
15
|
+
export interface WorkspaceStatus {
|
|
16
|
+
path: string;
|
|
17
|
+
valid: boolean;
|
|
18
|
+
warning?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Check workspace directory status without crashing. */
|
|
21
|
+
export declare function checkWorkspaceStatus(): WorkspaceStatus;
|
|
22
|
+
/** Ensure the workspace directory exists and is validated. Returns status instead of throwing. */
|
|
23
|
+
export declare function ensureWorkspaceDir(): Promise<WorkspaceStatus>;
|
|
24
|
+
/**
|
|
25
|
+
* Sanitize a filename from external sources (email attachments, Drive metadata).
|
|
26
|
+
* Strips null bytes, control characters, path separators, and other dangerous chars.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitizeFilename(filename: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a file path within the workspace directory.
|
|
31
|
+
* Prevents path traversal (e.g. ../../etc/passwd) and sanitizes the filename.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveWorkspacePath(filename: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Verify a file path is safe to read/write after symlink resolution.
|
|
36
|
+
* Must be called before any fs operation on a workspace path.
|
|
37
|
+
*/
|
|
38
|
+
export declare function verifyPathSafety(filePath: string): Promise<void>;
|