gogcli-mcp 2.0.6 → 2.0.8
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 +4 -4
- package/.claude-plugin/plugin.json +2 -2
- package/dist/index.js +39 -5
- package/dist/lib.js +39 -5
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/server.json +3 -3
- package/src/runner.ts +48 -5
- package/tests/runner.test.ts +121 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/lib.d.ts +0 -6
- package/dist/lib.d.ts.map +0 -1
- package/dist/runner.d.ts +0 -12
- package/dist/runner.d.ts.map +0 -1
- package/dist/server.d.ts +0 -7
- package/dist/server.d.ts.map +0 -1
- package/dist/tools/auth.d.ts +0 -3
- package/dist/tools/auth.d.ts.map +0 -1
- package/dist/tools/calendar.d.ts +0 -3
- package/dist/tools/calendar.d.ts.map +0 -1
- package/dist/tools/contacts.d.ts +0 -3
- package/dist/tools/contacts.d.ts.map +0 -1
- package/dist/tools/docs.d.ts +0 -3
- package/dist/tools/docs.d.ts.map +0 -1
- package/dist/tools/drive.d.ts +0 -3
- package/dist/tools/drive.d.ts.map +0 -1
- package/dist/tools/gmail.d.ts +0 -3
- package/dist/tools/gmail.d.ts.map +0 -1
- package/dist/tools/sheets.d.ts +0 -3
- package/dist/tools/sheets.d.ts.map +0 -1
- package/dist/tools/tasks.d.ts +0 -3
- package/dist/tools/tasks.d.ts.map +0 -1
- package/dist/tools/utils.d.ts +0 -14
- package/dist/tools/utils.d.ts.map +0 -1
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
"email": "chris.c.hall@gmail.com"
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
|
-
"description": "Google Sheets (and more) for Claude via gogcli
|
|
10
|
-
"version": "2.0.
|
|
9
|
+
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
10
|
+
"version": "2.0.8"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
14
14
|
"name": "gogcli-mcp",
|
|
15
15
|
"displayName": "gogcli",
|
|
16
16
|
"source": "./",
|
|
17
|
-
"description": "Google Sheets (and more) for Claude via gogcli
|
|
18
|
-
"version": "2.0.
|
|
17
|
+
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
18
|
+
"version": "2.0.8",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp",
|
|
3
3
|
"displayName": "gogcli",
|
|
4
|
-
"version": "2.0.
|
|
5
|
-
"description": "Google Sheets (and more) for Claude via gogcli
|
|
4
|
+
"version": "2.0.8",
|
|
5
|
+
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
|
8
8
|
"email": "chris.c.hall@gmail.com"
|
package/dist/index.js
CHANGED
|
@@ -8415,8 +8415,8 @@ function emoji() {
|
|
|
8415
8415
|
}
|
|
8416
8416
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
8417
8417
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
8418
|
-
var mac = (
|
|
8419
|
-
const escapedDelim = escapeRegex(
|
|
8418
|
+
var mac = (delimiter2) => {
|
|
8419
|
+
const escapedDelim = escapeRegex(delimiter2 ?? ":");
|
|
8420
8420
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
8421
8421
|
};
|
|
8422
8422
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -30863,7 +30863,34 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
30863
30863
|
|
|
30864
30864
|
// src/runner.ts
|
|
30865
30865
|
import { spawn } from "node:child_process";
|
|
30866
|
+
import { delimiter } from "node:path";
|
|
30866
30867
|
var TIMEOUT_MS = 3e4;
|
|
30868
|
+
function envOrUndefined(key) {
|
|
30869
|
+
const value = process.env[key];
|
|
30870
|
+
if (!value || value.startsWith("${")) return void 0;
|
|
30871
|
+
return value;
|
|
30872
|
+
}
|
|
30873
|
+
function augmentedPath() {
|
|
30874
|
+
const home = process.env.HOME;
|
|
30875
|
+
const candidates = [
|
|
30876
|
+
process.env.PATH ?? "",
|
|
30877
|
+
"/opt/homebrew/bin",
|
|
30878
|
+
"/usr/local/bin",
|
|
30879
|
+
home ? `${home}/.local/bin` : "",
|
|
30880
|
+
home ? `${home}/go/bin` : ""
|
|
30881
|
+
];
|
|
30882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30883
|
+
const parts = [];
|
|
30884
|
+
for (const c of candidates) {
|
|
30885
|
+
if (!c) continue;
|
|
30886
|
+
for (const dir of c.split(delimiter)) {
|
|
30887
|
+
if (!dir || seen.has(dir)) continue;
|
|
30888
|
+
seen.add(dir);
|
|
30889
|
+
parts.push(dir);
|
|
30890
|
+
}
|
|
30891
|
+
}
|
|
30892
|
+
return parts.join(delimiter);
|
|
30893
|
+
}
|
|
30867
30894
|
function formatTimeout(ms) {
|
|
30868
30895
|
const seconds = Math.round(ms / 1e3);
|
|
30869
30896
|
if (seconds >= 60) {
|
|
@@ -30874,7 +30901,7 @@ function formatTimeout(ms) {
|
|
|
30874
30901
|
}
|
|
30875
30902
|
async function run(args, options = {}) {
|
|
30876
30903
|
const { account, spawner = spawn, interactive = false, timeout } = options;
|
|
30877
|
-
const effectiveAccount = account ??
|
|
30904
|
+
const effectiveAccount = account ?? envOrUndefined("GOG_ACCOUNT");
|
|
30878
30905
|
const fullArgs = ["--json", "--color=never"];
|
|
30879
30906
|
if (!interactive) {
|
|
30880
30907
|
fullArgs.push("--no-input");
|
|
@@ -30886,7 +30913,8 @@ async function run(args, options = {}) {
|
|
|
30886
30913
|
const effectiveTimeout = timeout ?? TIMEOUT_MS;
|
|
30887
30914
|
return new Promise((resolve, reject) => {
|
|
30888
30915
|
const { GOG_ACCESS_TOKEN: _, ...cleanEnv } = process.env;
|
|
30889
|
-
const
|
|
30916
|
+
const childEnv = { ...cleanEnv, PATH: augmentedPath() };
|
|
30917
|
+
const child = spawner(envOrUndefined("GOG_PATH") ?? "gog", fullArgs, { env: childEnv });
|
|
30890
30918
|
const stdoutChunks = [];
|
|
30891
30919
|
const stderrChunks = [];
|
|
30892
30920
|
let settled = false;
|
|
@@ -30921,6 +30949,12 @@ async function run(args, options = {}) {
|
|
|
30921
30949
|
clearTimeout(timer);
|
|
30922
30950
|
if (settled) return;
|
|
30923
30951
|
settled = true;
|
|
30952
|
+
if (err.code === "ENOENT") {
|
|
30953
|
+
reject(new Error(
|
|
30954
|
+
"gog executable not found. Install gogcli (https://github.com/steipete/gogcli) or set GOG_PATH in your MCP client config to the absolute binary path (run `which gog` in a terminal to find it)."
|
|
30955
|
+
));
|
|
30956
|
+
return;
|
|
30957
|
+
}
|
|
30924
30958
|
reject(err);
|
|
30925
30959
|
});
|
|
30926
30960
|
});
|
|
@@ -32118,7 +32152,7 @@ function registerTasksTools(server2) {
|
|
|
32118
32152
|
}
|
|
32119
32153
|
|
|
32120
32154
|
// src/server.ts
|
|
32121
|
-
var VERSION = true ? "2.0.
|
|
32155
|
+
var VERSION = true ? "2.0.8" : "0.0.0";
|
|
32122
32156
|
function createServer(options) {
|
|
32123
32157
|
return new McpServer({
|
|
32124
32158
|
name: options?.name ?? "gogcli",
|
package/dist/lib.js
CHANGED
|
@@ -12044,8 +12044,8 @@ function emoji() {
|
|
|
12044
12044
|
}
|
|
12045
12045
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
12046
12046
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
12047
|
-
var mac = (
|
|
12048
|
-
const escapedDelim = escapeRegex(
|
|
12047
|
+
var mac = (delimiter2) => {
|
|
12048
|
+
const escapedDelim = escapeRegex(delimiter2 ?? ":");
|
|
12049
12049
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
12050
12050
|
};
|
|
12051
12051
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -30770,7 +30770,34 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
30770
30770
|
|
|
30771
30771
|
// src/runner.ts
|
|
30772
30772
|
import { spawn } from "node:child_process";
|
|
30773
|
+
import { delimiter } from "node:path";
|
|
30773
30774
|
var TIMEOUT_MS = 3e4;
|
|
30775
|
+
function envOrUndefined(key) {
|
|
30776
|
+
const value = process.env[key];
|
|
30777
|
+
if (!value || value.startsWith("${")) return void 0;
|
|
30778
|
+
return value;
|
|
30779
|
+
}
|
|
30780
|
+
function augmentedPath() {
|
|
30781
|
+
const home = process.env.HOME;
|
|
30782
|
+
const candidates = [
|
|
30783
|
+
process.env.PATH ?? "",
|
|
30784
|
+
"/opt/homebrew/bin",
|
|
30785
|
+
"/usr/local/bin",
|
|
30786
|
+
home ? `${home}/.local/bin` : "",
|
|
30787
|
+
home ? `${home}/go/bin` : ""
|
|
30788
|
+
];
|
|
30789
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30790
|
+
const parts = [];
|
|
30791
|
+
for (const c of candidates) {
|
|
30792
|
+
if (!c) continue;
|
|
30793
|
+
for (const dir of c.split(delimiter)) {
|
|
30794
|
+
if (!dir || seen.has(dir)) continue;
|
|
30795
|
+
seen.add(dir);
|
|
30796
|
+
parts.push(dir);
|
|
30797
|
+
}
|
|
30798
|
+
}
|
|
30799
|
+
return parts.join(delimiter);
|
|
30800
|
+
}
|
|
30774
30801
|
function formatTimeout(ms) {
|
|
30775
30802
|
const seconds = Math.round(ms / 1e3);
|
|
30776
30803
|
if (seconds >= 60) {
|
|
@@ -30781,7 +30808,7 @@ function formatTimeout(ms) {
|
|
|
30781
30808
|
}
|
|
30782
30809
|
async function run(args, options = {}) {
|
|
30783
30810
|
const { account, spawner = spawn, interactive = false, timeout } = options;
|
|
30784
|
-
const effectiveAccount = account ??
|
|
30811
|
+
const effectiveAccount = account ?? envOrUndefined("GOG_ACCOUNT");
|
|
30785
30812
|
const fullArgs = ["--json", "--color=never"];
|
|
30786
30813
|
if (!interactive) {
|
|
30787
30814
|
fullArgs.push("--no-input");
|
|
@@ -30793,7 +30820,8 @@ async function run(args, options = {}) {
|
|
|
30793
30820
|
const effectiveTimeout = timeout ?? TIMEOUT_MS;
|
|
30794
30821
|
return new Promise((resolve, reject) => {
|
|
30795
30822
|
const { GOG_ACCESS_TOKEN: _, ...cleanEnv } = process.env;
|
|
30796
|
-
const
|
|
30823
|
+
const childEnv = { ...cleanEnv, PATH: augmentedPath() };
|
|
30824
|
+
const child = spawner(envOrUndefined("GOG_PATH") ?? "gog", fullArgs, { env: childEnv });
|
|
30797
30825
|
const stdoutChunks = [];
|
|
30798
30826
|
const stderrChunks = [];
|
|
30799
30827
|
let settled = false;
|
|
@@ -30828,6 +30856,12 @@ async function run(args, options = {}) {
|
|
|
30828
30856
|
clearTimeout(timer);
|
|
30829
30857
|
if (settled) return;
|
|
30830
30858
|
settled = true;
|
|
30859
|
+
if (err.code === "ENOENT") {
|
|
30860
|
+
reject(new Error(
|
|
30861
|
+
"gog executable not found. Install gogcli (https://github.com/steipete/gogcli) or set GOG_PATH in your MCP client config to the absolute binary path (run `which gog` in a terminal to find it)."
|
|
30862
|
+
));
|
|
30863
|
+
return;
|
|
30864
|
+
}
|
|
30831
30865
|
reject(err);
|
|
30832
30866
|
});
|
|
30833
30867
|
});
|
|
@@ -32025,7 +32059,7 @@ function registerTasksTools(server) {
|
|
|
32025
32059
|
}
|
|
32026
32060
|
|
|
32027
32061
|
// src/server.ts
|
|
32028
|
-
var VERSION = true ? "2.0.
|
|
32062
|
+
var VERSION = true ? "2.0.8" : "0.0.0";
|
|
32029
32063
|
function createServer(options) {
|
|
32030
32064
|
return new McpServer({
|
|
32031
32065
|
name: options?.name ?? "gogcli",
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.3",
|
|
4
4
|
"name": "gogcli-mcp",
|
|
5
5
|
"display_name": "gogcli",
|
|
6
|
-
"version": "2.0.
|
|
6
|
+
"version": "2.0.8",
|
|
7
7
|
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.chrischall/gogcli-mcp",
|
|
4
|
-
"description": "Google Sheets (and more) for Claude via gogcli
|
|
4
|
+
"description": "Google Sheets (and more) for Claude via gogcli — read, write, and manage spreadsheets",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/chrischall/gogcli-mcp",
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/gogcli-mcp"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.0.
|
|
10
|
+
"version": "2.0.8",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp",
|
|
15
|
-
"version": "2.0.
|
|
15
|
+
"version": "2.0.8",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/runner.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import type { ChildProcess } from 'node:child_process';
|
|
3
|
+
import { delimiter } from 'node:path';
|
|
3
4
|
|
|
4
5
|
export type Spawner = (
|
|
5
6
|
command: string,
|
|
@@ -16,6 +17,42 @@ export interface RunOptions {
|
|
|
16
17
|
|
|
17
18
|
const TIMEOUT_MS = 30_000;
|
|
18
19
|
|
|
20
|
+
// Treat unresolved .mcpb placeholders ("${user_config.gog_path}") and empty
|
|
21
|
+
// strings the same as an unset env var. When an optional .mcpb user_config
|
|
22
|
+
// field is left blank, some clients pass the literal placeholder text through
|
|
23
|
+
// to the spawned server instead of substituting "" or omitting the key.
|
|
24
|
+
function envOrUndefined(key: string): string | undefined {
|
|
25
|
+
const value = process.env[key];
|
|
26
|
+
if (!value || value.startsWith('${')) return undefined;
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// MCP desktop clients often spawn servers with a stripped PATH that excludes
|
|
31
|
+
// Homebrew, user-local, and Go's default install dirs — so even when gog is
|
|
32
|
+
// installed, the spawned server can't find it. Augment the child's PATH with
|
|
33
|
+
// the locations where gogcli is commonly installed.
|
|
34
|
+
function augmentedPath(): string {
|
|
35
|
+
const home = process.env.HOME;
|
|
36
|
+
const candidates = [
|
|
37
|
+
process.env.PATH ?? '',
|
|
38
|
+
'/opt/homebrew/bin',
|
|
39
|
+
'/usr/local/bin',
|
|
40
|
+
home ? `${home}/.local/bin` : '',
|
|
41
|
+
home ? `${home}/go/bin` : '',
|
|
42
|
+
];
|
|
43
|
+
const seen = new Set<string>();
|
|
44
|
+
const parts: string[] = [];
|
|
45
|
+
for (const c of candidates) {
|
|
46
|
+
if (!c) continue;
|
|
47
|
+
for (const dir of c.split(delimiter)) {
|
|
48
|
+
if (!dir || seen.has(dir)) continue;
|
|
49
|
+
seen.add(dir);
|
|
50
|
+
parts.push(dir);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return parts.join(delimiter);
|
|
54
|
+
}
|
|
55
|
+
|
|
19
56
|
function formatTimeout(ms: number): string {
|
|
20
57
|
const seconds = Math.round(ms / 1000);
|
|
21
58
|
if (seconds >= 60) {
|
|
@@ -28,7 +65,7 @@ function formatTimeout(ms: number): string {
|
|
|
28
65
|
export async function run(args: string[], options: RunOptions = {}): Promise<string> {
|
|
29
66
|
const { account, spawner = spawn as unknown as Spawner, interactive = false, timeout } = options;
|
|
30
67
|
|
|
31
|
-
const effectiveAccount = account ??
|
|
68
|
+
const effectiveAccount = account ?? envOrUndefined('GOG_ACCOUNT');
|
|
32
69
|
|
|
33
70
|
const fullArgs = ['--json', '--color=never'];
|
|
34
71
|
if (!interactive) {
|
|
@@ -45,10 +82,8 @@ export async function run(args: string[], options: RunOptions = {}): Promise<str
|
|
|
45
82
|
// Strip GOG_ACCESS_TOKEN so gogcli uses stored refresh tokens instead of
|
|
46
83
|
// a potentially stale direct access token passed through MCP env config.
|
|
47
84
|
const { GOG_ACCESS_TOKEN: _, ...cleanEnv } = process.env;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// to PATH lookup instead of trying to spawn an empty executable name.
|
|
51
|
-
const child = spawner(process.env.GOG_PATH || 'gog', fullArgs, { env: cleanEnv });
|
|
85
|
+
const childEnv = { ...cleanEnv, PATH: augmentedPath() };
|
|
86
|
+
const child = spawner(envOrUndefined('GOG_PATH') ?? 'gog', fullArgs, { env: childEnv });
|
|
52
87
|
const stdoutChunks: Buffer[] = [];
|
|
53
88
|
const stderrChunks: Buffer[] = [];
|
|
54
89
|
let settled = false;
|
|
@@ -83,6 +118,14 @@ export async function run(args: string[], options: RunOptions = {}): Promise<str
|
|
|
83
118
|
clearTimeout(timer);
|
|
84
119
|
if (settled) return;
|
|
85
120
|
settled = true;
|
|
121
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
122
|
+
reject(new Error(
|
|
123
|
+
'gog executable not found. Install gogcli (https://github.com/steipete/gogcli) ' +
|
|
124
|
+
'or set GOG_PATH in your MCP client config to the absolute binary path ' +
|
|
125
|
+
'(run `which gog` in a terminal to find it).',
|
|
126
|
+
));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
86
129
|
reject(err);
|
|
87
130
|
});
|
|
88
131
|
});
|
package/tests/runner.test.ts
CHANGED
|
@@ -24,7 +24,7 @@ describe('run', () => {
|
|
|
24
24
|
expect(spawner).toHaveBeenCalledWith(
|
|
25
25
|
'gog',
|
|
26
26
|
['--json', '--color=never', '--no-input', 'sheets', 'get', 'id1', 'A1'],
|
|
27
|
-
expect.objectContaining({ env:
|
|
27
|
+
expect.objectContaining({ env: expect.any(Object) }),
|
|
28
28
|
);
|
|
29
29
|
});
|
|
30
30
|
|
|
@@ -143,6 +143,39 @@ describe('run', () => {
|
|
|
143
143
|
}
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
+
it('falls back to "gog" on PATH when GOG_PATH is an unresolved .mcpb placeholder', async () => {
|
|
147
|
+
const spawner = makeSpawner(0, '{}');
|
|
148
|
+
const originalEnv = process.env.GOG_PATH;
|
|
149
|
+
process.env.GOG_PATH = '${user_config.gog_path}';
|
|
150
|
+
try {
|
|
151
|
+
await run(['sheets', 'metadata', 'id1'], { spawner });
|
|
152
|
+
expect(spawner).toHaveBeenCalledWith('gog', expect.any(Array), expect.any(Object));
|
|
153
|
+
} finally {
|
|
154
|
+
if (originalEnv === undefined) {
|
|
155
|
+
delete process.env.GOG_PATH;
|
|
156
|
+
} else {
|
|
157
|
+
process.env.GOG_PATH = originalEnv;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('omits --account when GOG_ACCOUNT is an unresolved .mcpb placeholder', async () => {
|
|
163
|
+
const spawner = makeSpawner(0, '{}');
|
|
164
|
+
const originalEnv = process.env.GOG_ACCOUNT;
|
|
165
|
+
process.env.GOG_ACCOUNT = '${user_config.gog_account}';
|
|
166
|
+
try {
|
|
167
|
+
await run(['sheets', 'metadata', 'id1'], { spawner });
|
|
168
|
+
const callArgs = (spawner as ReturnType<typeof vi.fn>).mock.calls[0][1] as string[];
|
|
169
|
+
expect(callArgs).not.toContain('--account');
|
|
170
|
+
} finally {
|
|
171
|
+
if (originalEnv === undefined) {
|
|
172
|
+
delete process.env.GOG_ACCOUNT;
|
|
173
|
+
} else {
|
|
174
|
+
process.env.GOG_ACCOUNT = originalEnv;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
146
179
|
it('returns stdout on exit code 0', async () => {
|
|
147
180
|
const spawner = makeSpawner(0, '{"values":[["hello"]]}');
|
|
148
181
|
const result = await run(['sheets', 'get', 'id1', 'A1'], { spawner });
|
|
@@ -173,6 +206,93 @@ describe('run', () => {
|
|
|
173
206
|
.rejects.toThrow('gog not found');
|
|
174
207
|
});
|
|
175
208
|
|
|
209
|
+
it('wraps ENOENT spawn errors with an install-or-set-GOG_PATH hint', async () => {
|
|
210
|
+
const spawner = vi.fn(() => {
|
|
211
|
+
const proc = new EventEmitter() as ReturnType<Spawner>;
|
|
212
|
+
(proc as unknown as { stdout: EventEmitter; stderr: EventEmitter }).stdout = new EventEmitter();
|
|
213
|
+
(proc as unknown as { stdout: EventEmitter; stderr: EventEmitter }).stderr = new EventEmitter();
|
|
214
|
+
setTimeout(() => {
|
|
215
|
+
const err = new Error('spawn gog ENOENT') as NodeJS.ErrnoException;
|
|
216
|
+
err.code = 'ENOENT';
|
|
217
|
+
proc.emit('error', err);
|
|
218
|
+
}, 0);
|
|
219
|
+
return proc;
|
|
220
|
+
}) as unknown as Spawner;
|
|
221
|
+
await expect(run(['sheets', 'get', 'id', 'A1'], { spawner }))
|
|
222
|
+
.rejects.toThrow(/gog executable not found.*Install gogcli.*GOG_PATH/s);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('augments child PATH with common gogcli install dirs', async () => {
|
|
226
|
+
const spawner = makeSpawner(0, '{}');
|
|
227
|
+
const originalHome = process.env.HOME;
|
|
228
|
+
const originalPath = process.env.PATH;
|
|
229
|
+
process.env.HOME = '/Users/test';
|
|
230
|
+
process.env.PATH = '/usr/bin:/bin';
|
|
231
|
+
try {
|
|
232
|
+
await run(['sheets', 'metadata', 'id1'], { spawner });
|
|
233
|
+
const passedEnv = (spawner as ReturnType<typeof vi.fn>).mock.calls[0][2].env as NodeJS.ProcessEnv;
|
|
234
|
+
const passedPath = passedEnv.PATH!;
|
|
235
|
+
expect(passedPath).toContain('/usr/bin');
|
|
236
|
+
expect(passedPath).toContain('/opt/homebrew/bin');
|
|
237
|
+
expect(passedPath).toContain('/usr/local/bin');
|
|
238
|
+
expect(passedPath).toContain('/Users/test/.local/bin');
|
|
239
|
+
expect(passedPath).toContain('/Users/test/go/bin');
|
|
240
|
+
} finally {
|
|
241
|
+
if (originalHome === undefined) delete process.env.HOME;
|
|
242
|
+
else process.env.HOME = originalHome;
|
|
243
|
+
if (originalPath === undefined) delete process.env.PATH;
|
|
244
|
+
else process.env.PATH = originalPath;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('does not duplicate dirs that are already on PATH', async () => {
|
|
249
|
+
const spawner = makeSpawner(0, '{}');
|
|
250
|
+
const originalPath = process.env.PATH;
|
|
251
|
+
process.env.PATH = '/opt/homebrew/bin:/usr/bin';
|
|
252
|
+
try {
|
|
253
|
+
await run(['sheets', 'metadata', 'id1'], { spawner });
|
|
254
|
+
const passedEnv = (spawner as ReturnType<typeof vi.fn>).mock.calls[0][2].env as NodeJS.ProcessEnv;
|
|
255
|
+
const passedPath = passedEnv.PATH!;
|
|
256
|
+
const homebrewCount = passedPath.split(':').filter(d => d === '/opt/homebrew/bin').length;
|
|
257
|
+
expect(homebrewCount).toBe(1);
|
|
258
|
+
} finally {
|
|
259
|
+
if (originalPath === undefined) delete process.env.PATH;
|
|
260
|
+
else process.env.PATH = originalPath;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('augments PATH even when HOME is unset', async () => {
|
|
265
|
+
const spawner = makeSpawner(0, '{}');
|
|
266
|
+
const originalHome = process.env.HOME;
|
|
267
|
+
const originalPath = process.env.PATH;
|
|
268
|
+
delete process.env.HOME;
|
|
269
|
+
process.env.PATH = '/usr/bin';
|
|
270
|
+
try {
|
|
271
|
+
await run(['sheets', 'metadata', 'id1'], { spawner });
|
|
272
|
+
const passedEnv = (spawner as ReturnType<typeof vi.fn>).mock.calls[0][2].env as NodeJS.ProcessEnv;
|
|
273
|
+
const passedPath = passedEnv.PATH!;
|
|
274
|
+
expect(passedPath).toContain('/opt/homebrew/bin');
|
|
275
|
+
expect(passedPath).not.toContain('.local/bin');
|
|
276
|
+
} finally {
|
|
277
|
+
if (originalHome !== undefined) process.env.HOME = originalHome;
|
|
278
|
+
if (originalPath === undefined) delete process.env.PATH;
|
|
279
|
+
else process.env.PATH = originalPath;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('handles empty PATH gracefully', async () => {
|
|
284
|
+
const spawner = makeSpawner(0, '{}');
|
|
285
|
+
const originalPath = process.env.PATH;
|
|
286
|
+
delete process.env.PATH;
|
|
287
|
+
try {
|
|
288
|
+
await run(['sheets', 'metadata', 'id1'], { spawner });
|
|
289
|
+
const passedEnv = (spawner as ReturnType<typeof vi.fn>).mock.calls[0][2].env as NodeJS.ProcessEnv;
|
|
290
|
+
expect(passedEnv.PATH).toContain('/opt/homebrew/bin');
|
|
291
|
+
} finally {
|
|
292
|
+
if (originalPath !== undefined) process.env.PATH = originalPath;
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
176
296
|
it('ignores close event if error event already settled the promise', async () => {
|
|
177
297
|
const spawner = vi.fn(() => {
|
|
178
298
|
const proc = new EventEmitter() as ReturnType<Spawner>;
|
package/dist/index.d.ts
DELETED
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/lib.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { createBaseServer, VERSION } from './server.js';
|
|
2
|
-
export { run } from './runner.js';
|
|
3
|
-
export type { RunOptions, Spawner } from './runner.js';
|
|
4
|
-
export { accountParam, runOrDiagnose, toText, toError } from './tools/utils.js';
|
|
5
|
-
export type { ToolResult } from './tools/utils.js';
|
|
6
|
-
//# sourceMappingURL=lib.d.ts.map
|
package/dist/lib.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChF,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/runner.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ChildProcess } from 'node:child_process';
|
|
2
|
-
export type Spawner = (command: string, args: string[], options: {
|
|
3
|
-
env: NodeJS.ProcessEnv;
|
|
4
|
-
}) => ChildProcess;
|
|
5
|
-
export interface RunOptions {
|
|
6
|
-
account?: string;
|
|
7
|
-
spawner?: Spawner;
|
|
8
|
-
interactive?: boolean;
|
|
9
|
-
timeout?: number;
|
|
10
|
-
}
|
|
11
|
-
export declare function run(args: string[], options?: RunOptions): Promise<string>;
|
|
12
|
-
//# sourceMappingURL=runner.d.ts.map
|
package/dist/runner.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,MAAM,OAAO,GAAG,CACpB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAChC,YAAY,CAAC;AAElB,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAaD,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DnF"}
|
package/dist/server.d.ts
DELETED
package/dist/server.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYpE,eAAO,MAAM,OAAO,QAAmE,CAAC;AAExF,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAgBzF"}
|
package/dist/tools/auth.d.ts
DELETED
package/dist/tools/auth.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/tools/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwEzD"}
|
package/dist/tools/calendar.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../src/tools/calendar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2H7D"}
|
package/dist/tools/contacts.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contacts.d.ts","sourceRoot":"","sources":["../../src/tools/contacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkE7D"}
|
package/dist/tools/docs.d.ts
DELETED
package/dist/tools/docs.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/tools/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoGzD"}
|
package/dist/tools/drive.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"drive.d.ts","sourceRoot":"","sources":["../../src/tools/drive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgH1D"}
|
package/dist/tools/gmail.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gmail.d.ts","sourceRoot":"","sources":["../../src/tools/gmail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8D1D"}
|
package/dist/tools/sheets.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sheets.d.ts","sourceRoot":"","sources":["../../src/tools/sheets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAuG3D"}
|
package/dist/tools/tasks.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsF1D"}
|
package/dist/tools/utils.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export type ToolResult = {
|
|
3
|
-
content: [{
|
|
4
|
-
type: 'text';
|
|
5
|
-
text: string;
|
|
6
|
-
}];
|
|
7
|
-
};
|
|
8
|
-
export declare const accountParam: z.ZodOptional<z.ZodString>;
|
|
9
|
-
export declare function toText(output: string): ToolResult;
|
|
10
|
-
export declare function toError(err: unknown): ToolResult;
|
|
11
|
-
export declare function runOrDiagnose(args: string[], options: {
|
|
12
|
-
account?: string;
|
|
13
|
-
}): Promise<ToolResult>;
|
|
14
|
-
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/tools/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC;AAEvE,eAAO,MAAM,YAAY,4BAExB,CAAC;AAEF,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAEjD;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAEhD;AAQD,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,OAAO,CAAC,UAAU,CAAC,CAerB"}
|