gogcli-mcp-drive 2.18.2 → 2.18.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +199 -4
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
10
|
-
"version": "2.18.
|
|
10
|
+
"version": "2.18.4"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "gogcli (Drive)",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
18
|
-
"version": "2.18.
|
|
18
|
+
"version": "2.18.4",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-drive",
|
|
3
3
|
"displayName": "gogcli (Drive)",
|
|
4
|
-
"version": "2.18.
|
|
4
|
+
"version": "2.18.4",
|
|
5
5
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/dist/index.js
CHANGED
|
@@ -31285,6 +31285,198 @@ async function runBinary(args, options = {}) {
|
|
|
31285
31285
|
return spawnExecutor(fullArgs, { timeout, interactive: false, spawner, binary: true });
|
|
31286
31286
|
}
|
|
31287
31287
|
|
|
31288
|
+
// ../gogcli-mcp/src/timestamps.ts
|
|
31289
|
+
var DEFAULT_DISPLAY_TZ = "America/New_York";
|
|
31290
|
+
function isValidTimeZone(tz) {
|
|
31291
|
+
try {
|
|
31292
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
31293
|
+
return true;
|
|
31294
|
+
} catch {
|
|
31295
|
+
return false;
|
|
31296
|
+
}
|
|
31297
|
+
}
|
|
31298
|
+
function displayTimeZone() {
|
|
31299
|
+
const configured = readEnvVar("DISPLAY_TZ");
|
|
31300
|
+
if (configured && isValidTimeZone(configured)) return configured;
|
|
31301
|
+
return DEFAULT_DISPLAY_TZ;
|
|
31302
|
+
}
|
|
31303
|
+
function naiveSourceTimeZone() {
|
|
31304
|
+
const configured = readEnvVar("GOG_TIMEZONE");
|
|
31305
|
+
if (configured && isValidTimeZone(configured)) return configured;
|
|
31306
|
+
return displayTimeZone();
|
|
31307
|
+
}
|
|
31308
|
+
function offsetAt(instant, tz) {
|
|
31309
|
+
const w = wallPartsIn(instant, tz);
|
|
31310
|
+
const asUTC = Date.UTC(w.year, w.month - 1, w.day, w.hour, w.minute, w.second, instant.getUTCMilliseconds());
|
|
31311
|
+
const minutes = Math.round((asUTC - instant.getTime()) / 6e4);
|
|
31312
|
+
const sign = minutes < 0 ? "-" : "+";
|
|
31313
|
+
const abs = Math.abs(minutes);
|
|
31314
|
+
return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;
|
|
31315
|
+
}
|
|
31316
|
+
function wallPartsIn(instant, tz) {
|
|
31317
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
31318
|
+
timeZone: tz,
|
|
31319
|
+
year: "numeric",
|
|
31320
|
+
month: "2-digit",
|
|
31321
|
+
day: "2-digit",
|
|
31322
|
+
hour: "2-digit",
|
|
31323
|
+
minute: "2-digit",
|
|
31324
|
+
second: "2-digit",
|
|
31325
|
+
// h23 pins midnight to hour 00; without it some ICU builds render hour 24.
|
|
31326
|
+
hourCycle: "h23"
|
|
31327
|
+
}).formatToParts(instant);
|
|
31328
|
+
const out = {};
|
|
31329
|
+
for (const p of parts) {
|
|
31330
|
+
if (p.type !== "literal") out[p.type] = Number(p.value);
|
|
31331
|
+
}
|
|
31332
|
+
return out;
|
|
31333
|
+
}
|
|
31334
|
+
function wallTimeToInstant(y, mo, d, h, mi, s, ms, tz) {
|
|
31335
|
+
let guess = Date.UTC(y, mo - 1, d, h, mi, s, ms);
|
|
31336
|
+
for (let i = 0; i < 2; i += 1) {
|
|
31337
|
+
const seen = wallPartsIn(new Date(guess), tz);
|
|
31338
|
+
const seenUTC = Date.UTC(seen.year, seen.month - 1, seen.day, seen.hour, seen.minute, seen.second, ms);
|
|
31339
|
+
const drift = Date.UTC(y, mo - 1, d, h, mi, s, ms) - seenUTC;
|
|
31340
|
+
if (drift === 0) break;
|
|
31341
|
+
guess += drift;
|
|
31342
|
+
}
|
|
31343
|
+
return new Date(guess);
|
|
31344
|
+
}
|
|
31345
|
+
function pad(n, width = 2) {
|
|
31346
|
+
return String(n).padStart(width, "0");
|
|
31347
|
+
}
|
|
31348
|
+
function isoWithOffset(instant, tz, offset) {
|
|
31349
|
+
const w = wallPartsIn(instant, tz);
|
|
31350
|
+
const msPart = instant.getUTCMilliseconds();
|
|
31351
|
+
const frac = msPart ? `.${pad(msPart, 3)}` : "";
|
|
31352
|
+
return `${pad(w.year, 4)}-${pad(w.month)}-${pad(w.day)}T${pad(w.hour)}:${pad(w.minute)}:${pad(w.second)}${frac}${offset}`;
|
|
31353
|
+
}
|
|
31354
|
+
function formatInstant(instant, tz = displayTimeZone()) {
|
|
31355
|
+
const offset = offsetAt(instant, tz);
|
|
31356
|
+
const display = new Intl.DateTimeFormat("en-US", {
|
|
31357
|
+
timeZone: tz,
|
|
31358
|
+
weekday: "short",
|
|
31359
|
+
month: "short",
|
|
31360
|
+
day: "numeric",
|
|
31361
|
+
year: "numeric",
|
|
31362
|
+
hour: "numeric",
|
|
31363
|
+
minute: "2-digit",
|
|
31364
|
+
timeZoneName: "short"
|
|
31365
|
+
}).format(instant);
|
|
31366
|
+
return { iso: isoWithOffset(instant, tz, offset), display };
|
|
31367
|
+
}
|
|
31368
|
+
var TIMESTAMP_KEYS = /* @__PURE__ */ new Set([
|
|
31369
|
+
"date",
|
|
31370
|
+
// gog gmail message/thread listings ("2026-07-28 03:36")
|
|
31371
|
+
"dateTime",
|
|
31372
|
+
// Calendar event start/end
|
|
31373
|
+
"internalDate",
|
|
31374
|
+
// Gmail, epoch milliseconds (authoritative)
|
|
31375
|
+
"modifiedTime",
|
|
31376
|
+
// Drive
|
|
31377
|
+
"createdTime",
|
|
31378
|
+
// Drive
|
|
31379
|
+
"createTime",
|
|
31380
|
+
"updateTime",
|
|
31381
|
+
"updated",
|
|
31382
|
+
"originalStartTime",
|
|
31383
|
+
"expirationTime",
|
|
31384
|
+
"lastModified",
|
|
31385
|
+
"sentAt",
|
|
31386
|
+
"viewedAt",
|
|
31387
|
+
"modifiedAt",
|
|
31388
|
+
"fetchedBodyAt",
|
|
31389
|
+
"asOf"
|
|
31390
|
+
]);
|
|
31391
|
+
var ZONE_NAME_KEYS = /* @__PURE__ */ new Set(["timeZone", "timezone"]);
|
|
31392
|
+
var RFC3339_WITH_OFFSET = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d{1,9}))?(Z|[+-]\d{2}:?\d{2})$/;
|
|
31393
|
+
var NAIVE_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d{1,9}))?$/;
|
|
31394
|
+
var EPOCH_MILLIS = /^\d{13}$/;
|
|
31395
|
+
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
31396
|
+
function isRealCalendarDate(p) {
|
|
31397
|
+
const utc = new Date(Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second));
|
|
31398
|
+
return utc.getUTCFullYear() === p.year && utc.getUTCMonth() === p.month - 1 && utc.getUTCDate() === p.day && utc.getUTCHours() === p.hour && utc.getUTCMinutes() === p.minute && utc.getUTCSeconds() === p.second;
|
|
31399
|
+
}
|
|
31400
|
+
function parseTimestampValue(key, value, assumeNaiveIn) {
|
|
31401
|
+
if (typeof value !== "string") return null;
|
|
31402
|
+
const raw = value.trim();
|
|
31403
|
+
if (raw === "" || DATE_ONLY.test(raw)) return null;
|
|
31404
|
+
if (key === "internalDate" && EPOCH_MILLIS.test(raw)) {
|
|
31405
|
+
return new Date(Number(raw));
|
|
31406
|
+
}
|
|
31407
|
+
if (RFC3339_WITH_OFFSET.test(raw)) {
|
|
31408
|
+
const parsed = new Date(raw.replace(" ", "T"));
|
|
31409
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
31410
|
+
}
|
|
31411
|
+
const naive = NAIVE_DATE_TIME.exec(raw);
|
|
31412
|
+
if (naive) {
|
|
31413
|
+
const [, y, mo, d, h, mi, s, frac] = naive;
|
|
31414
|
+
const ms = frac ? Number(frac.padEnd(3, "0").slice(0, 3)) : 0;
|
|
31415
|
+
const parts = {
|
|
31416
|
+
year: Number(y),
|
|
31417
|
+
month: Number(mo),
|
|
31418
|
+
day: Number(d),
|
|
31419
|
+
hour: Number(h),
|
|
31420
|
+
minute: Number(mi),
|
|
31421
|
+
second: Number(s ?? "0")
|
|
31422
|
+
};
|
|
31423
|
+
if (!isRealCalendarDate(parts)) return null;
|
|
31424
|
+
return wallTimeToInstant(
|
|
31425
|
+
parts.year,
|
|
31426
|
+
parts.month,
|
|
31427
|
+
parts.day,
|
|
31428
|
+
parts.hour,
|
|
31429
|
+
parts.minute,
|
|
31430
|
+
parts.second,
|
|
31431
|
+
ms,
|
|
31432
|
+
assumeNaiveIn
|
|
31433
|
+
);
|
|
31434
|
+
}
|
|
31435
|
+
return null;
|
|
31436
|
+
}
|
|
31437
|
+
function walk(node, tz, naiveTz) {
|
|
31438
|
+
let changed = false;
|
|
31439
|
+
if (Array.isArray(node)) {
|
|
31440
|
+
for (const item of node) {
|
|
31441
|
+
if (walk(item, tz, naiveTz)) changed = true;
|
|
31442
|
+
}
|
|
31443
|
+
return changed;
|
|
31444
|
+
}
|
|
31445
|
+
if (node === null || typeof node !== "object") return false;
|
|
31446
|
+
const obj = node;
|
|
31447
|
+
for (const key of Object.keys(obj)) {
|
|
31448
|
+
const value = obj[key];
|
|
31449
|
+
if (value !== null && typeof value === "object") {
|
|
31450
|
+
if (walk(value, tz, naiveTz)) changed = true;
|
|
31451
|
+
continue;
|
|
31452
|
+
}
|
|
31453
|
+
if (ZONE_NAME_KEYS.has(key) || !TIMESTAMP_KEYS.has(key)) continue;
|
|
31454
|
+
const instant = parseTimestampValue(key, value, naiveTz);
|
|
31455
|
+
if (!instant) continue;
|
|
31456
|
+
const { iso, display } = formatInstant(instant, tz);
|
|
31457
|
+
obj[key] = iso;
|
|
31458
|
+
obj[`${key}Display`] = display;
|
|
31459
|
+
changed = true;
|
|
31460
|
+
}
|
|
31461
|
+
return changed;
|
|
31462
|
+
}
|
|
31463
|
+
function detectIndent(text) {
|
|
31464
|
+
const match = /\n(\s+)\S/.exec(text);
|
|
31465
|
+
return match ? match[1].replace(/\t/g, " ").length : 0;
|
|
31466
|
+
}
|
|
31467
|
+
function normalizeTimestamps(text, tz = displayTimeZone(), naiveTz = naiveSourceTimeZone()) {
|
|
31468
|
+
const trimmed = text.trim();
|
|
31469
|
+
if (trimmed === "" || !/^[[{]/.test(trimmed)) return text;
|
|
31470
|
+
let parsed;
|
|
31471
|
+
try {
|
|
31472
|
+
parsed = JSON.parse(trimmed);
|
|
31473
|
+
} catch {
|
|
31474
|
+
return text;
|
|
31475
|
+
}
|
|
31476
|
+
if (!walk(parsed, tz, naiveTz)) return text;
|
|
31477
|
+
return JSON.stringify(parsed, null, detectIndent(text));
|
|
31478
|
+
}
|
|
31479
|
+
|
|
31288
31480
|
// ../gogcli-mcp/src/tools/utils.ts
|
|
31289
31481
|
var accountParam = external_exports.string().optional().describe(
|
|
31290
31482
|
"Google account email to use, e.g. you@gmail.com \u2014 must be the full address, not a bare username. Overrides the GOG_ACCOUNT env var. Omit to use the single configured account."
|
|
@@ -31383,7 +31575,8 @@ ${accounts || "(none)"}${hint}`);
|
|
|
31383
31575
|
}
|
|
31384
31576
|
async function runOrDiagnose(args, options) {
|
|
31385
31577
|
try {
|
|
31386
|
-
|
|
31578
|
+
const raw = await run(args, options);
|
|
31579
|
+
return rawTextResult(options.lossless ? raw : normalizeTimestamps(raw));
|
|
31387
31580
|
} catch (err) {
|
|
31388
31581
|
return diagnose(err);
|
|
31389
31582
|
}
|
|
@@ -31745,14 +31938,16 @@ function registerDriveTools(server) {
|
|
|
31745
31938
|
const { name, mimeType } = fileMeta(await run(["drive", "get", fileId], { account }));
|
|
31746
31939
|
const params = JSON.stringify({ fileId, alt: "media" });
|
|
31747
31940
|
const blob = await runBinary(["api", "call", "drive", "v3", "files.get", `--params=${params}`], { account });
|
|
31941
|
+
const type = mimeType ?? "application/octet-stream";
|
|
31942
|
+
const unrenderableHint = type.startsWith("image/") ? "" : ` If your client does not render a ${type} resource, gog_drive_extract_text returns the same file as text (PDFs included, via OCR).`;
|
|
31748
31943
|
return {
|
|
31749
31944
|
content: [
|
|
31750
|
-
{ type: "text", text: `${name ?? fileId} (${
|
|
31945
|
+
{ type: "text", text: `${name ?? fileId} (${type}) \u2014 ${Buffer.from(blob, "base64").length} bytes, base64 resource below.${unrenderableHint}` },
|
|
31751
31946
|
{
|
|
31752
31947
|
type: "resource",
|
|
31753
31948
|
resource: {
|
|
31754
31949
|
uri: `gogdrive://${fileId}/${encodeURIComponent(name ?? "file")}`,
|
|
31755
|
-
mimeType:
|
|
31950
|
+
mimeType: type,
|
|
31756
31951
|
blob
|
|
31757
31952
|
}
|
|
31758
31953
|
}
|
|
@@ -31775,7 +31970,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
|
31775
31970
|
);
|
|
31776
31971
|
|
|
31777
31972
|
// ../gogcli-mcp/src/server.ts
|
|
31778
|
-
var VERSION = true ? "2.18.
|
|
31973
|
+
var VERSION = true ? "2.18.4" : "0.0.0";
|
|
31779
31974
|
|
|
31780
31975
|
// src/tools/drive-extra.ts
|
|
31781
31976
|
function registerExtraDriveTools(server) {
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.3",
|
|
4
4
|
"name": "gogcli-mcp-drive",
|
|
5
5
|
"display_name": "gogcli (Drive)",
|
|
6
|
-
"version": "2.18.
|
|
6
|
+
"version": "2.18.4",
|
|
7
7
|
"description": "Extended Google Drive for Claude via gogcli — auth + full Drive support (upload, download, permissions, comments, shared drives)",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-drive",
|
|
3
|
-
"version": "2.18.
|
|
3
|
+
"version": "2.18.4",
|
|
4
4
|
"mcpName": "io.github.chrischall/gogcli-mcp-drive",
|
|
5
5
|
"description": "Extended Google Drive MCP server via gogcli — auth + full Drive support (upload/download/permissions/comments/shared drives)",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/gogcli-mcp-drive"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.18.
|
|
10
|
+
"version": "2.18.4",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-drive",
|
|
15
|
-
"version": "2.18.
|
|
15
|
+
"version": "2.18.4",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|