zero-config-cli-bridge 1.2.0 → 1.4.0
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/dist/executor.d.ts +4 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +16 -12
- package/dist/executor.js.map +1 -1
- package/dist/index.js +76 -49
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +17 -5
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +30 -56
- package/dist/schema.js.map +1 -1
- package/package.json +1 -1
package/dist/executor.d.ts
CHANGED
|
@@ -3,9 +3,12 @@ export interface ExecuteResult {
|
|
|
3
3
|
stderr: string;
|
|
4
4
|
exitCode: number;
|
|
5
5
|
}
|
|
6
|
+
declare const TIMEOUT_MS_PROBE = 5000;
|
|
7
|
+
export { TIMEOUT_MS_PROBE };
|
|
6
8
|
/**
|
|
7
9
|
* Executes a binary directly with an args array.
|
|
8
10
|
* NO shell intermediary — shell injection is structurally impossible.
|
|
11
|
+
* stdout is passed through unmodified; item-level truncation is the caller's responsibility.
|
|
9
12
|
*/
|
|
10
|
-
export declare function executeCommand(bin: string, args: string[]): Promise<ExecuteResult>;
|
|
13
|
+
export declare function executeCommand(bin: string, args: string[], timeoutMs?: number): Promise<ExecuteResult>;
|
|
11
14
|
//# sourceMappingURL=executor.d.ts.map
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAUD,QAAA,MAAM,gBAAgB,OAAQ,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAO5B;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,GAAE,MAAmB,GAC7B,OAAO,CAAC,aAAa,CAAC,CAgCxB"}
|
package/dist/executor.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
-
// stdout carries structured JSON
|
|
3
|
-
|
|
4
|
-
//
|
|
2
|
+
// stdout carries structured JSON from --json flag.
|
|
3
|
+
// Truncating it at byte level would corrupt the JSON before index.ts can parse it.
|
|
4
|
+
// NO limit on stdout — item-level truncation in index.ts is the sole guard.
|
|
5
|
+
//
|
|
6
|
+
// stderr carries error messages and diagnostic text (human-readable, bounded).
|
|
5
7
|
const MAX_STDERR_CHARS = 4_096;
|
|
6
|
-
const RAW_TRUNCATION_MSG = '\n...[Output truncated. Use grep/jq to filter]';
|
|
7
8
|
const TIMEOUT_MS = 15_000;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
const TIMEOUT_MS_PROBE = 5_000; // shorter timeout for capability probe calls
|
|
10
|
+
export { TIMEOUT_MS_PROBE };
|
|
11
|
+
function truncateStderr(s) {
|
|
12
|
+
if (s.length <= MAX_STDERR_CHARS)
|
|
10
13
|
return s;
|
|
11
|
-
return s.slice(0,
|
|
14
|
+
return s.slice(0, MAX_STDERR_CHARS) + '\n...[stderr truncated]';
|
|
12
15
|
}
|
|
13
16
|
/**
|
|
14
17
|
* Executes a binary directly with an args array.
|
|
15
18
|
* NO shell intermediary — shell injection is structurally impossible.
|
|
19
|
+
* stdout is passed through unmodified; item-level truncation is the caller's responsibility.
|
|
16
20
|
*/
|
|
17
|
-
export function executeCommand(bin, args) {
|
|
21
|
+
export function executeCommand(bin, args, timeoutMs = TIMEOUT_MS) {
|
|
18
22
|
return new Promise((resolve, reject) => {
|
|
19
23
|
const proc = spawn(bin, args, {
|
|
20
24
|
env: { ...process.env, CI: 'true' },
|
|
@@ -26,13 +30,13 @@ export function executeCommand(bin, args) {
|
|
|
26
30
|
proc.stderr.on('data', (chunk) => { stderrBuf += chunk.toString(); });
|
|
27
31
|
const timer = setTimeout(() => {
|
|
28
32
|
proc.kill('SIGKILL');
|
|
29
|
-
reject(new Error(`Command timed out after ${
|
|
30
|
-
},
|
|
33
|
+
reject(new Error(`Command timed out after ${timeoutMs}ms`));
|
|
34
|
+
}, timeoutMs);
|
|
31
35
|
proc.on('close', (code) => {
|
|
32
36
|
clearTimeout(timer);
|
|
33
37
|
resolve({
|
|
34
|
-
stdout:
|
|
35
|
-
stderr:
|
|
38
|
+
stdout: stdoutBuf, // unmodified
|
|
39
|
+
stderr: truncateStderr(stderrBuf), // bounded
|
|
36
40
|
exitCode: code ?? 1,
|
|
37
41
|
});
|
|
38
42
|
});
|
package/dist/executor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAQtC,
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAQtC,mDAAmD;AACnD,mFAAmF;AACnF,4EAA4E;AAC5E,EAAE;AACF,+EAA+E;AAC/E,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,MAAM,gBAAgB,GAAG,KAAK,CAAC,CAAC,6CAA6C;AAE7E,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,SAAS,cAAc,CAAC,CAAS;IAC/B,IAAI,CAAC,CAAC,MAAM,IAAI,gBAAgB;QAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,yBAAyB,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,GAAW,EACX,IAAc,EACd,YAAoB,UAAU;IAE9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;YAC5B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE;YACnC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,IAAI,SAAS,GAAG,EAAE,CAAC;QACnB,IAAI,SAAS,GAAG,EAAE,CAAC;QAEnB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,GAAG,SAAS,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9E,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,GAAG,SAAS,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9E,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACrB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,SAAS,IAAI,CAAC,CAAC,CAAC;QAC9D,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAmB,EAAE,EAAE;YACvC,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC;gBACN,MAAM,EAAE,SAAS,EAAqB,aAAa;gBACnD,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,EAAK,UAAU;gBAChD,QAAQ,EAAE,IAAI,IAAI,CAAC;aACpB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAC9B,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,43 +3,81 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { executeCommand } from './executor.js';
|
|
6
|
+
import { validateSubcommand, validateArgs } from './security.js';
|
|
7
|
+
import { buildToolDefinitions, buildGhArgs } from './schema.js';
|
|
6
8
|
const MAX_JSON_ITEMS = 30;
|
|
7
|
-
const
|
|
8
|
-
const MAX_TEXT_CHARS = 2000;
|
|
9
|
-
const TEXT_TRUNCATION_MSG = '\n...[Output truncated. Use grep/jq to filter]';
|
|
9
|
+
const MAX_SERIALISED_CHARS = 200_000; // 200 KB cap for non-array JSON objects
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Converts gh stdout (JSON) into a bounded, always-valid envelope.
|
|
12
|
+
* Called only when exitCode === 0.
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* error messages and plain-text fallback output.
|
|
16
|
-
*
|
|
17
|
-
* The executor's MAX_RAW_CHARS (4096) is an independent backstop that fires
|
|
18
|
-
* only if this function is somehow bypassed (e.g. future code paths).
|
|
14
|
+
* stdout is passed unmodified from executor — no byte-level truncation
|
|
15
|
+
* has occurred. item-level truncation is the sole guard here.
|
|
19
16
|
*/
|
|
20
|
-
function
|
|
17
|
+
function stdoutToEnvelope(stdout) {
|
|
18
|
+
if (!stdout.trim()) {
|
|
19
|
+
return { data: [], meta: { truncated: false, returnedItems: 0 } };
|
|
20
|
+
}
|
|
21
|
+
let parsed;
|
|
21
22
|
try {
|
|
22
|
-
|
|
23
|
-
if (Array.isArray(parsed)) {
|
|
24
|
-
if (parsed.length > MAX_JSON_ITEMS) {
|
|
25
|
-
return JSON.stringify(parsed.slice(0, MAX_JSON_ITEMS), null, 2) + JSON_TRUNCATION_MSG;
|
|
26
|
-
}
|
|
27
|
-
return JSON.stringify(parsed, null, 2);
|
|
28
|
-
}
|
|
29
|
-
return JSON.stringify(parsed, null, 2);
|
|
23
|
+
parsed = JSON.parse(stdout);
|
|
30
24
|
}
|
|
31
25
|
catch {
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
// gh returned non-JSON despite --json flag (should not happen in normal operation)
|
|
27
|
+
return {
|
|
28
|
+
data: null,
|
|
29
|
+
meta: {
|
|
30
|
+
truncated: false,
|
|
31
|
+
error: `Unexpected non-JSON output from gh: ${stdout.slice(0, 200)}`,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
37
34
|
}
|
|
35
|
+
// Array response: truncate at item level — primary case for list commands
|
|
36
|
+
if (Array.isArray(parsed)) {
|
|
37
|
+
const truncated = parsed.length > MAX_JSON_ITEMS;
|
|
38
|
+
const data = truncated ? parsed.slice(0, MAX_JSON_ITEMS) : parsed;
|
|
39
|
+
return {
|
|
40
|
+
data,
|
|
41
|
+
meta: {
|
|
42
|
+
truncated,
|
|
43
|
+
returnedItems: data.length,
|
|
44
|
+
...(truncated
|
|
45
|
+
? { note: `Showing first ${MAX_JSON_ITEMS} items. Use --limit or filters to narrow results.` }
|
|
46
|
+
: {}),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Non-array JSON object: guard against unbounded size
|
|
51
|
+
const serialised = JSON.stringify(parsed);
|
|
52
|
+
if (serialised.length > MAX_SERIALISED_CHARS) {
|
|
53
|
+
return {
|
|
54
|
+
data: null,
|
|
55
|
+
meta: {
|
|
56
|
+
truncated: true,
|
|
57
|
+
error: `Response object too large (${serialised.length} chars). Use filters to narrow results.`,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { data: parsed, meta: { truncated: false } };
|
|
38
62
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Wraps an error string in the standard envelope.
|
|
65
|
+
* stderr is already bounded to 4KB by executor.
|
|
66
|
+
*/
|
|
67
|
+
function stderrToEnvelope(stderr, stdout) {
|
|
68
|
+
const error = (stderr || stdout || 'Command failed with no output').trim();
|
|
69
|
+
return { data: null, meta: { truncated: false, error } };
|
|
70
|
+
}
|
|
71
|
+
function envelopeToResponse(envelope, isError) {
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: 'text', text: JSON.stringify(envelope, null, 2) }],
|
|
74
|
+
isError,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Tool registry is synchronously populated at startup — no subprocess calls.
|
|
78
|
+
const tools = buildToolDefinitions();
|
|
79
|
+
const toolRegistry = new Map(tools.map((t) => [t.name, t]));
|
|
80
|
+
const server = new Server({ name: 'zero-config-cli-bridge', version: '1.4.0' }, { capabilities: { tools: {} } });
|
|
43
81
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
44
82
|
tools: Array.from(toolRegistry.values()).map(({ name, description, inputSchema }) => ({
|
|
45
83
|
name,
|
|
@@ -52,10 +90,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
52
90
|
const args = (rawArgs ?? {});
|
|
53
91
|
const tool = toolRegistry.get(toolName);
|
|
54
92
|
if (!tool) {
|
|
55
|
-
return {
|
|
56
|
-
content: [{ type: 'text', text: `Error: Unknown tool "${toolName}".` }],
|
|
57
|
-
isError: true,
|
|
58
|
-
};
|
|
93
|
+
return envelopeToResponse({ data: null, meta: { truncated: false, error: `Unknown tool "${toolName}".` } }, true);
|
|
59
94
|
}
|
|
60
95
|
// Security: whitelist subcommand + validate arg values
|
|
61
96
|
try {
|
|
@@ -63,10 +98,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
63
98
|
validateArgs(args);
|
|
64
99
|
}
|
|
65
100
|
catch (err) {
|
|
66
|
-
return {
|
|
67
|
-
content: [{ type: 'text', text: err instanceof Error ? err.message : String(err) }],
|
|
68
|
-
isError: true,
|
|
69
|
-
};
|
|
101
|
+
return envelopeToResponse({ data: null, meta: { truncated: false, error: err instanceof Error ? err.message : String(err) } }, true);
|
|
70
102
|
}
|
|
71
103
|
// Direct spawn — no shell, no injection surface
|
|
72
104
|
const ghArgs = buildGhArgs(tool, args);
|
|
@@ -75,22 +107,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
75
107
|
result = await executeCommand('gh', ghArgs);
|
|
76
108
|
}
|
|
77
109
|
catch (err) {
|
|
78
|
-
return {
|
|
79
|
-
content: [{ type: 'text', text: `Execution error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
80
|
-
isError: true,
|
|
81
|
-
};
|
|
110
|
+
return envelopeToResponse({ data: null, meta: { truncated: false, error: `Execution error: ${err instanceof Error ? err.message : String(err)}` } }, true);
|
|
82
111
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
112
|
+
// stdout and stderr are semantically distinct:
|
|
113
|
+
// exitCode === 0 → stdout is structured JSON data; stderr is ignored warnings
|
|
114
|
+
// exitCode !== 0 → stderr is the error message; stdout is typically empty
|
|
115
|
+
if (result.exitCode !== 0) {
|
|
116
|
+
return envelopeToResponse(stderrToEnvelope(result.stderr, result.stdout), true);
|
|
117
|
+
}
|
|
118
|
+
return envelopeToResponse(stdoutToEnvelope(result.stdout), false);
|
|
89
119
|
});
|
|
90
120
|
async function main() {
|
|
91
|
-
// Probe local gh binary for available capabilities before accepting requests
|
|
92
|
-
const tools = await buildToolDefinitions();
|
|
93
|
-
toolRegistry = new Map(tools.map((t) => [t.name, t]));
|
|
94
121
|
const transport = new StdioServerTransport();
|
|
95
122
|
await server.connect(transport);
|
|
96
123
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema definitions for exposed gh tools.
|
|
3
|
+
*
|
|
4
|
+
* Field list design:
|
|
5
|
+
* STATIC_FIELDS is the authoritative source of truth.
|
|
6
|
+
* It contains only fields that work with the standard `repo` OAuth scope
|
|
7
|
+
* and have been stable across gh major versions.
|
|
8
|
+
*
|
|
9
|
+
* The probe (detectJsonFields) is NOT called at startup.
|
|
10
|
+
* Rationale: the probe has no reliability guarantee — gh's output format
|
|
11
|
+
* is human-readable and can change with any release. Running an unreliable
|
|
12
|
+
* subprocess at every server start adds latency and timeout risk for zero
|
|
13
|
+
* guaranteed gain. Static fields are the correct default.
|
|
14
|
+
*/
|
|
1
15
|
export interface ToolDefinition {
|
|
2
16
|
name: string;
|
|
3
17
|
description: string;
|
|
@@ -9,16 +23,14 @@ export interface ToolDefinition {
|
|
|
9
23
|
}>;
|
|
10
24
|
required?: string[];
|
|
11
25
|
};
|
|
12
|
-
/** gh subcommand tokens, e.g. ['issue', 'list'] */
|
|
13
26
|
subcommand: string[];
|
|
14
|
-
/** JSON fields confirmed available in the local gh binary, filtered to repo-scope-safe set */
|
|
15
27
|
jsonFields: string[];
|
|
16
28
|
}
|
|
17
|
-
export declare function buildToolDefinitions():
|
|
29
|
+
export declare function buildToolDefinitions(): ToolDefinition[];
|
|
18
30
|
/**
|
|
19
31
|
* Builds the gh args array using --flag=value notation throughout.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
32
|
+
* Prevents option injection: a value starting with '-' cannot be
|
|
33
|
+
* misinterpreted as a separate flag by gh's argument parser.
|
|
22
34
|
*/
|
|
23
35
|
export declare function buildGhArgs(tool: ToolDefinition, args: Record<string, unknown>): string[];
|
|
24
36
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAClE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAiBD,wBAAgB,oBAAoB,IAAI,cAAc,EAAE,CAyCvD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAWzF"}
|
package/dist/schema.js
CHANGED
|
@@ -1,58 +1,32 @@
|
|
|
1
|
-
import { executeCommand } from './executor.js';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Schema definitions for exposed gh tools.
|
|
3
|
+
*
|
|
4
|
+
* Field list design:
|
|
5
|
+
* STATIC_FIELDS is the authoritative source of truth.
|
|
6
|
+
* It contains only fields that work with the standard `repo` OAuth scope
|
|
7
|
+
* and have been stable across gh major versions.
|
|
8
|
+
*
|
|
9
|
+
* The probe (detectJsonFields) is NOT called at startup.
|
|
10
|
+
* Rationale: the probe has no reliability guarantee — gh's output format
|
|
11
|
+
* is human-readable and can change with any release. Running an unreliable
|
|
12
|
+
* subprocess at every server start adds latency and timeout risk for zero
|
|
13
|
+
* guaranteed gain. Static fields are the correct default.
|
|
7
14
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
// Fields verified against `gh issue list --json` and `gh pr list --json` output.
|
|
16
|
+
// Excludes: `id` (requires read:project scope), `body` (unbounded text).
|
|
17
|
+
const STATIC_FIELDS = {
|
|
18
|
+
'issue list': [
|
|
19
|
+
'number', 'title', 'state', 'labels', 'assignees',
|
|
20
|
+
'author', 'createdAt', 'updatedAt', 'closedAt', 'url',
|
|
21
|
+
'comments', 'milestone',
|
|
22
|
+
],
|
|
23
|
+
'pr list': [
|
|
24
|
+
'number', 'title', 'state', 'labels', 'assignees',
|
|
25
|
+
'author', 'createdAt', 'updatedAt', 'closedAt', 'url',
|
|
26
|
+
'baseRefName', 'headRefName', 'isDraft', 'mergedAt', 'reviewDecision',
|
|
27
|
+
],
|
|
19
28
|
};
|
|
20
|
-
|
|
21
|
-
* Detects JSON fields available in the local gh binary by calling
|
|
22
|
-
* `gh <subcommand> --json` with no field argument. Recent gh versions
|
|
23
|
-
* output the available field list to stderr in this case — no error
|
|
24
|
-
* injection needed. Falls back to the static list on any failure.
|
|
25
|
-
*/
|
|
26
|
-
async function detectJsonFields(subcommand) {
|
|
27
|
-
const key = subcommand.join(' ');
|
|
28
|
-
try {
|
|
29
|
-
// `gh issue list --json` with no fields causes gh to list available fields on stderr.
|
|
30
|
-
// This is documented behaviour, not error scraping.
|
|
31
|
-
const result = await executeCommand('gh', [...subcommand, '--json']);
|
|
32
|
-
const text = result.stderr + result.stdout;
|
|
33
|
-
// Output format: "Use `--json` with one or more of: field1,field2,..."
|
|
34
|
-
// or a newline-separated list after "Available fields:"
|
|
35
|
-
const commaMatch = text.match(/--json`?\s+with[^:]*:\s*([a-zA-Z,\s]+)/i);
|
|
36
|
-
if (commaMatch) {
|
|
37
|
-
const fields = commaMatch[1]
|
|
38
|
-
.split(/[,\s]+/)
|
|
39
|
-
.map((f) => f.trim())
|
|
40
|
-
.filter((f) => /^[a-zA-Z][a-zA-Z0-9]*$/.test(f))
|
|
41
|
-
.filter((f) => REPO_SCOPE_SAFE_FIELDS.has(f));
|
|
42
|
-
if (fields.length > 0)
|
|
43
|
-
return fields;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
// gh not installed or timed out
|
|
48
|
-
}
|
|
49
|
-
return FALLBACK_FIELDS[key] ?? [];
|
|
50
|
-
}
|
|
51
|
-
export async function buildToolDefinitions() {
|
|
52
|
-
const [issueFields, prFields] = await Promise.all([
|
|
53
|
-
detectJsonFields(['issue', 'list']),
|
|
54
|
-
detectJsonFields(['pr', 'list']),
|
|
55
|
-
]);
|
|
29
|
+
export function buildToolDefinitions() {
|
|
56
30
|
return [
|
|
57
31
|
{
|
|
58
32
|
name: 'gh_issue_list',
|
|
@@ -60,7 +34,7 @@ export async function buildToolDefinitions() {
|
|
|
60
34
|
'Uses the local `gh` CLI and its existing authentication — no API key required. ' +
|
|
61
35
|
'Read-only. Does not create, edit, or delete issues.',
|
|
62
36
|
subcommand: ['issue', 'list'],
|
|
63
|
-
jsonFields:
|
|
37
|
+
jsonFields: STATIC_FIELDS['issue list'],
|
|
64
38
|
inputSchema: {
|
|
65
39
|
type: 'object',
|
|
66
40
|
properties: {
|
|
@@ -78,7 +52,7 @@ export async function buildToolDefinitions() {
|
|
|
78
52
|
'Uses the local `gh` CLI and its existing authentication — no API key required. ' +
|
|
79
53
|
'Read-only. Does not create, edit, merge, or close pull requests.',
|
|
80
54
|
subcommand: ['pr', 'list'],
|
|
81
|
-
jsonFields:
|
|
55
|
+
jsonFields: STATIC_FIELDS['pr list'],
|
|
82
56
|
inputSchema: {
|
|
83
57
|
type: 'object',
|
|
84
58
|
properties: {
|
|
@@ -94,8 +68,8 @@ export async function buildToolDefinitions() {
|
|
|
94
68
|
}
|
|
95
69
|
/**
|
|
96
70
|
* Builds the gh args array using --flag=value notation throughout.
|
|
97
|
-
*
|
|
98
|
-
*
|
|
71
|
+
* Prevents option injection: a value starting with '-' cannot be
|
|
72
|
+
* misinterpreted as a separate flag by gh's argument parser.
|
|
99
73
|
*/
|
|
100
74
|
export function buildGhArgs(tool, args) {
|
|
101
75
|
const parts = [...tool.subcommand, `--json=${tool.jsonFields.join(',')}`];
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|