zero-config-cli-bridge 1.5.0 → 2.1.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/approval.d.ts +16 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +174 -0
- package/dist/approval.js.map +1 -0
- package/dist/index.js +37 -39
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +6 -15
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +140 -35
- package/dist/schema.js.map +1 -1
- package/dist/security.d.ts +11 -4
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +20 -10
- package/dist/security.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True server-side Human-in-the-Loop gate for headless MCP environments.
|
|
3
|
+
*
|
|
4
|
+
* When Claude Desktop (or any MCP client) spawns this server as a background
|
|
5
|
+
* process, there is no TTY attached. This function:
|
|
6
|
+
* 1. Binds a temporary HTTP server on a random localhost port.
|
|
7
|
+
* 2. Opens the system browser to the approval page.
|
|
8
|
+
* 3. Blocks until the human clicks Approve or Deny — the MCP response
|
|
9
|
+
* is held pending; the agent receives nothing in the meantime.
|
|
10
|
+
* 4. Denies by default on timeout (2 min) or server error.
|
|
11
|
+
*
|
|
12
|
+
* The one-time token in the URL prevents other localhost processes from
|
|
13
|
+
* silently approving or denying without user interaction.
|
|
14
|
+
*/
|
|
15
|
+
export declare function requestApproval(preview: string): Promise<boolean>;
|
|
16
|
+
//# sourceMappingURL=approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AA6GA;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAsEvE"}
|
package/dist/approval.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { createServer } from 'http';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import { randomBytes } from 'crypto';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
5
|
+
const APPROVAL_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes — deny on timeout
|
|
6
|
+
function detectPlatform() {
|
|
7
|
+
if (process.platform === 'darwin')
|
|
8
|
+
return 'mac';
|
|
9
|
+
if (process.platform === 'win32')
|
|
10
|
+
return 'windows';
|
|
11
|
+
try {
|
|
12
|
+
const v = readFileSync('/proc/version', 'utf-8').toLowerCase();
|
|
13
|
+
if (v.includes('microsoft') || v.includes('wsl'))
|
|
14
|
+
return 'wsl';
|
|
15
|
+
}
|
|
16
|
+
catch { /* not Linux or /proc unavailable */ }
|
|
17
|
+
return 'linux';
|
|
18
|
+
}
|
|
19
|
+
function openBrowser(url) {
|
|
20
|
+
const p = detectPlatform();
|
|
21
|
+
// WSL: explorer.exe is the most reliable path to the Windows default browser
|
|
22
|
+
const cmd = p === 'mac' ? `open '${url}'` :
|
|
23
|
+
p === 'windows' ? `start "" "${url}"` :
|
|
24
|
+
p === 'wsl' ? `explorer.exe '${url}'` :
|
|
25
|
+
`xdg-open '${url}' || sensible-browser '${url}'`;
|
|
26
|
+
exec(cmd, (err) => {
|
|
27
|
+
if (err) {
|
|
28
|
+
process.stderr.write(`\x1b[33mCould not open browser automatically.\x1b[0m\n`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// ── Port discovery ──────────────────────────────────────────────────────────
|
|
33
|
+
function findFreePort() {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const s = createServer();
|
|
36
|
+
s.listen(0, '127.0.0.1', () => {
|
|
37
|
+
const addr = s.address();
|
|
38
|
+
const port = typeof addr === 'object' && addr ? addr.port : null;
|
|
39
|
+
s.close(() => {
|
|
40
|
+
if (port)
|
|
41
|
+
resolve(port);
|
|
42
|
+
else
|
|
43
|
+
reject(new Error('Could not determine free port'));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
s.on('error', reject);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// ── Approval UI ─────────────────────────────────────────────────────────────
|
|
50
|
+
function approvalPage(preview, token) {
|
|
51
|
+
const safe = preview.replace(/&/g, '&').replace(/</g, '<');
|
|
52
|
+
return `<!DOCTYPE html>
|
|
53
|
+
<html lang="en">
|
|
54
|
+
<head>
|
|
55
|
+
<meta charset="utf-8">
|
|
56
|
+
<title>Agent Approval Required</title>
|
|
57
|
+
<style>
|
|
58
|
+
body { font-family: system-ui, -apple-system, sans-serif; max-width: 640px;
|
|
59
|
+
margin: 60px auto; padding: 0 24px; color: #1e293b; }
|
|
60
|
+
h1 { color: #b45309; margin-bottom: 8px; }
|
|
61
|
+
p { color: #475569; line-height: 1.6; }
|
|
62
|
+
pre { background: #f1f5f9; border: 1px solid #cbd5e1; border-radius: 8px;
|
|
63
|
+
padding: 16px; font-size: 13px; overflow-x: auto; white-space: pre-wrap; }
|
|
64
|
+
.row { display: flex; gap: 12px; margin-top: 28px; }
|
|
65
|
+
button { flex: 1; padding: 14px; font-size: 15px; font-weight: 600;
|
|
66
|
+
border: none; border-radius: 8px; cursor: pointer; transition: opacity .15s; }
|
|
67
|
+
button:hover { opacity: .82; }
|
|
68
|
+
.ok { background: #16a34a; color: #fff; }
|
|
69
|
+
.no { background: #dc2626; color: #fff; }
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<h1>⚠️ Agent Approval Required</h1>
|
|
74
|
+
<p>An AI agent is requesting permission to execute a <strong>write operation</strong>
|
|
75
|
+
using your local GitHub credentials:</p>
|
|
76
|
+
<pre>${safe}</pre>
|
|
77
|
+
<p>Approve only if you initiated this action and understand its consequences.</p>
|
|
78
|
+
<div class="row">
|
|
79
|
+
<form method="POST" action="/approve/${token}" style="flex:1">
|
|
80
|
+
<button class="ok" type="submit">✓ Approve</button>
|
|
81
|
+
</form>
|
|
82
|
+
<form method="POST" action="/deny/${token}" style="flex:1">
|
|
83
|
+
<button class="no" type="submit">✗ Deny</button>
|
|
84
|
+
</form>
|
|
85
|
+
</div>
|
|
86
|
+
</body>
|
|
87
|
+
</html>`;
|
|
88
|
+
}
|
|
89
|
+
function donePage(approved) {
|
|
90
|
+
const [icon, color, msg] = approved
|
|
91
|
+
? ['✓', '#16a34a', 'Approved — operation is executing.']
|
|
92
|
+
: ['✗', '#dc2626', 'Denied — operation was cancelled.'];
|
|
93
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Done</title></head>
|
|
94
|
+
<body style="font-family:system-ui;text-align:center;padding:60px;color:${color}">
|
|
95
|
+
<h1 style="font-size:64px;margin:0">${icon}</h1>
|
|
96
|
+
<p style="font-size:20px">${msg}</p>
|
|
97
|
+
<p style="color:#64748b">You can close this tab.</p>
|
|
98
|
+
</body></html>`;
|
|
99
|
+
}
|
|
100
|
+
// ── Public API ──────────────────────────────────────────────────────────────
|
|
101
|
+
/**
|
|
102
|
+
* True server-side Human-in-the-Loop gate for headless MCP environments.
|
|
103
|
+
*
|
|
104
|
+
* When Claude Desktop (or any MCP client) spawns this server as a background
|
|
105
|
+
* process, there is no TTY attached. This function:
|
|
106
|
+
* 1. Binds a temporary HTTP server on a random localhost port.
|
|
107
|
+
* 2. Opens the system browser to the approval page.
|
|
108
|
+
* 3. Blocks until the human clicks Approve or Deny — the MCP response
|
|
109
|
+
* is held pending; the agent receives nothing in the meantime.
|
|
110
|
+
* 4. Denies by default on timeout (2 min) or server error.
|
|
111
|
+
*
|
|
112
|
+
* The one-time token in the URL prevents other localhost processes from
|
|
113
|
+
* silently approving or denying without user interaction.
|
|
114
|
+
*/
|
|
115
|
+
export async function requestApproval(preview) {
|
|
116
|
+
const token = randomBytes(24).toString('hex');
|
|
117
|
+
const port = await findFreePort();
|
|
118
|
+
const base = `http://127.0.0.1:${port}`;
|
|
119
|
+
const url = `${base}/${token}`;
|
|
120
|
+
return new Promise((resolve) => {
|
|
121
|
+
let settled = false;
|
|
122
|
+
const settle = (approved, res) => {
|
|
123
|
+
if (settled)
|
|
124
|
+
return;
|
|
125
|
+
settled = true;
|
|
126
|
+
if (res) {
|
|
127
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
128
|
+
res.end(donePage(approved));
|
|
129
|
+
}
|
|
130
|
+
// Give browser time to receive the response page before closing server
|
|
131
|
+
setTimeout(() => server.close(), 500);
|
|
132
|
+
process.stderr.write(approved
|
|
133
|
+
? '\x1b[32m✓ Approved — executing write operation.\x1b[0m\n\n'
|
|
134
|
+
: '\x1b[31m✗ Denied — write operation cancelled.\x1b[0m\n\n');
|
|
135
|
+
resolve(approved);
|
|
136
|
+
};
|
|
137
|
+
const server = createServer((req, res) => {
|
|
138
|
+
if (req.method === 'GET' && req.url === `/${token}`) {
|
|
139
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
140
|
+
res.end(approvalPage(preview, token));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (req.method === 'POST' && req.url === `/approve/${token}`) {
|
|
144
|
+
settle(true, res);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (req.method === 'POST' && req.url === `/deny/${token}`) {
|
|
148
|
+
settle(false, res);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
res.writeHead(404);
|
|
152
|
+
res.end();
|
|
153
|
+
});
|
|
154
|
+
server.listen(port, '127.0.0.1', () => {
|
|
155
|
+
process.stderr.write('\n\x1b[33m━━━ APPROVAL REQUIRED ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n' +
|
|
156
|
+
`\x1b[1m${preview}\x1b[0m\n\n` +
|
|
157
|
+
`Opening browser for approval…\n` +
|
|
158
|
+
`If the browser does not open, visit:\n \x1b[36m${url}\x1b[0m\n` +
|
|
159
|
+
`Waiting (timeout 2 min — deny on timeout)…\n`);
|
|
160
|
+
openBrowser(url);
|
|
161
|
+
});
|
|
162
|
+
server.on('error', (err) => {
|
|
163
|
+
process.stderr.write(`Approval server error: ${err.message}\n`);
|
|
164
|
+
settle(false);
|
|
165
|
+
});
|
|
166
|
+
setTimeout(() => {
|
|
167
|
+
if (!settled) {
|
|
168
|
+
process.stderr.write('\x1b[31mApproval timed out — denied by default.\x1b[0m\n');
|
|
169
|
+
settle(false);
|
|
170
|
+
}
|
|
171
|
+
}, APPROVAL_TIMEOUT_MS);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,16 +3,14 @@ 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 {
|
|
7
|
-
import { buildToolDefinitions, buildGhArgs } from './schema.js';
|
|
6
|
+
import { getOperationTier, validateArgs } from './security.js';
|
|
7
|
+
import { buildToolDefinitions, buildGhArgs, buildCommandPreview } from './schema.js';
|
|
8
|
+
import { requestApproval } from './approval.js';
|
|
8
9
|
const MAX_JSON_ITEMS = 30;
|
|
9
|
-
const MAX_SERIALISED_CHARS = 200_000;
|
|
10
|
+
const MAX_SERIALISED_CHARS = 200_000;
|
|
10
11
|
/**
|
|
11
|
-
* Converts gh stdout (JSON) into a bounded
|
|
12
|
-
* Called only
|
|
13
|
-
*
|
|
14
|
-
* stdout is passed unmodified from executor — no byte-level truncation
|
|
15
|
-
* has occurred. item-level truncation is the sole guard here.
|
|
12
|
+
* Converts gh stdout (always JSON when --json flag is used) into a bounded envelope.
|
|
13
|
+
* Called only on exitCode === 0.
|
|
16
14
|
*/
|
|
17
15
|
function stdoutToEnvelope(stdout) {
|
|
18
16
|
if (!stdout.trim()) {
|
|
@@ -23,16 +21,11 @@ function stdoutToEnvelope(stdout) {
|
|
|
23
21
|
parsed = JSON.parse(stdout);
|
|
24
22
|
}
|
|
25
23
|
catch {
|
|
26
|
-
// gh returned non-JSON despite --json flag (should not happen in normal operation)
|
|
27
24
|
return {
|
|
28
25
|
data: null,
|
|
29
|
-
meta: {
|
|
30
|
-
truncated: false,
|
|
31
|
-
error: `Unexpected non-JSON output from gh: ${stdout.slice(0, 200)}`,
|
|
32
|
-
},
|
|
26
|
+
meta: { truncated: false, error: `Unexpected non-JSON output: ${stdout.slice(0, 200)}` },
|
|
33
27
|
};
|
|
34
28
|
}
|
|
35
|
-
// Array response: truncate at item level — primary case for list commands
|
|
36
29
|
if (Array.isArray(parsed)) {
|
|
37
30
|
const truncated = parsed.length > MAX_JSON_ITEMS;
|
|
38
31
|
const data = truncated ? parsed.slice(0, MAX_JSON_ITEMS) : parsed;
|
|
@@ -47,37 +40,33 @@ function stdoutToEnvelope(stdout) {
|
|
|
47
40
|
},
|
|
48
41
|
};
|
|
49
42
|
}
|
|
50
|
-
// Non-array
|
|
43
|
+
// Non-array (e.g. single created resource from write command)
|
|
51
44
|
const serialised = JSON.stringify(parsed);
|
|
52
45
|
if (serialised.length > MAX_SERIALISED_CHARS) {
|
|
53
46
|
return {
|
|
54
47
|
data: null,
|
|
55
|
-
meta: {
|
|
56
|
-
truncated: true,
|
|
57
|
-
error: `Response object too large (${serialised.length} chars). Use filters to narrow results.`,
|
|
58
|
-
},
|
|
48
|
+
meta: { truncated: true, error: `Response too large (${serialised.length} chars).` },
|
|
59
49
|
};
|
|
60
50
|
}
|
|
61
51
|
return { data: parsed, meta: { truncated: false } };
|
|
62
52
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Wraps an error string in the standard envelope.
|
|
65
|
-
* stderr is already bounded to 4KB by executor.
|
|
66
|
-
*/
|
|
67
53
|
function stderrToEnvelope(stderr, stdout) {
|
|
68
54
|
const error = (stderr || stdout || 'Command failed with no output').trim();
|
|
69
55
|
return { data: null, meta: { truncated: false, error } };
|
|
70
56
|
}
|
|
71
|
-
function
|
|
57
|
+
function envelopeResponse(envelope, isError) {
|
|
72
58
|
return {
|
|
73
59
|
content: [{ type: 'text', text: JSON.stringify(envelope, null, 2) }],
|
|
74
60
|
isError,
|
|
75
61
|
};
|
|
76
62
|
}
|
|
77
|
-
|
|
63
|
+
function errorEnvelope(message) {
|
|
64
|
+
return envelopeResponse({ data: null, meta: { truncated: false, error: message } }, true);
|
|
65
|
+
}
|
|
66
|
+
// Tool registry populated synchronously at startup — no subprocess overhead.
|
|
78
67
|
const tools = buildToolDefinitions();
|
|
79
68
|
const toolRegistry = new Map(tools.map((t) => [t.name, t]));
|
|
80
|
-
const server = new Server({ name: 'zero-config-cli-bridge', version: '
|
|
69
|
+
const server = new Server({ name: 'zero-config-cli-bridge', version: '2.0.0' }, { capabilities: { tools: {} } });
|
|
81
70
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
82
71
|
tools: Array.from(toolRegistry.values()).map(({ name, description, inputSchema }) => ({
|
|
83
72
|
name,
|
|
@@ -89,33 +78,42 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
89
78
|
const { name: toolName, arguments: rawArgs } = request.params;
|
|
90
79
|
const args = (rawArgs ?? {});
|
|
91
80
|
const tool = toolRegistry.get(toolName);
|
|
92
|
-
if (!tool)
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
if (!tool)
|
|
82
|
+
return errorEnvelope(`Unknown tool "${toolName}".`);
|
|
83
|
+
// Security: verify subcommand is in allow-list and get its tier
|
|
84
|
+
let tier;
|
|
96
85
|
try {
|
|
97
|
-
validateSubcommand(tool.subcommand.join(' '));
|
|
98
86
|
validateArgs(args);
|
|
87
|
+
tier = getOperationTier(tool.subcommand.join(' '));
|
|
99
88
|
}
|
|
100
89
|
catch (err) {
|
|
101
|
-
return
|
|
90
|
+
return errorEnvelope(err instanceof Error ? err.message : String(err));
|
|
91
|
+
}
|
|
92
|
+
// Tier 3: never executes (not exposed as tools, but guard defensively)
|
|
93
|
+
if (tier === 3) {
|
|
94
|
+
return errorEnvelope('Irreversible operations are not permitted.');
|
|
95
|
+
}
|
|
96
|
+
// Tier 2: block until human physically approves at the terminal
|
|
97
|
+
if (tier === 2) {
|
|
98
|
+
const preview = buildCommandPreview(tool, args);
|
|
99
|
+
const approved = await requestApproval(preview);
|
|
100
|
+
if (!approved) {
|
|
101
|
+
return errorEnvelope('Operation denied by human operator.');
|
|
102
|
+
}
|
|
102
103
|
}
|
|
103
|
-
//
|
|
104
|
+
// Execute — direct spawn, no shell
|
|
104
105
|
const ghArgs = buildGhArgs(tool, args);
|
|
105
106
|
let result;
|
|
106
107
|
try {
|
|
107
108
|
result = await executeCommand('gh', ghArgs);
|
|
108
109
|
}
|
|
109
110
|
catch (err) {
|
|
110
|
-
return
|
|
111
|
+
return errorEnvelope(`Execution error: ${err instanceof Error ? err.message : String(err)}`);
|
|
111
112
|
}
|
|
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
113
|
if (result.exitCode !== 0) {
|
|
116
|
-
return
|
|
114
|
+
return envelopeResponse(stderrToEnvelope(result.stderr, result.stdout), true);
|
|
117
115
|
}
|
|
118
|
-
return
|
|
116
|
+
return envelopeResponse(stdoutToEnvelope(result.stdout), false);
|
|
119
117
|
});
|
|
120
118
|
async function main() {
|
|
121
119
|
const transport = new StdioServerTransport();
|
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;AAC/C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
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
|
+
import type { OperationTier } from './security.js';
|
|
15
2
|
export interface ToolDefinition {
|
|
16
3
|
name: string;
|
|
17
4
|
description: string;
|
|
@@ -24,7 +11,9 @@ export interface ToolDefinition {
|
|
|
24
11
|
required?: string[];
|
|
25
12
|
};
|
|
26
13
|
subcommand: string[];
|
|
27
|
-
|
|
14
|
+
tier: OperationTier;
|
|
15
|
+
/** For read (Tier 0) tools: --json fields to request */
|
|
16
|
+
jsonFields?: string[];
|
|
28
17
|
}
|
|
29
18
|
export declare function buildToolDefinitions(): ToolDefinition[];
|
|
30
19
|
/**
|
|
@@ -33,4 +22,6 @@ export declare function buildToolDefinitions(): ToolDefinition[];
|
|
|
33
22
|
* misinterpreted as a separate flag by gh's argument parser.
|
|
34
23
|
*/
|
|
35
24
|
export declare function buildGhArgs(tool: ToolDefinition, args: Record<string, unknown>): string[];
|
|
25
|
+
/** Human-readable preview of the command an agent intends to execute */
|
|
26
|
+
export declare function buildCommandPreview(tool: ToolDefinition, args: Record<string, unknown>): string;
|
|
36
27
|
//# 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":"AAAA
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,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,IAAI,EAAE,aAAa,CAAC;IACpB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAwBD,wBAAgB,oBAAoB,IAAI,cAAc,EAAE,CA0GvD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAkDzF;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE/F"}
|
package/dist/schema.js
CHANGED
|
@@ -1,20 +1,6 @@
|
|
|
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
|
-
*/
|
|
15
|
-
// Fields verified against `gh issue list --json` and `gh pr list --json` output.
|
|
1
|
+
// Fields verified against `gh issue list --json` and `gh pr list --json`.
|
|
16
2
|
// Excludes: `id` (requires read:project scope), `body` (unbounded text).
|
|
17
|
-
const
|
|
3
|
+
const READ_FIELDS = {
|
|
18
4
|
'issue list': [
|
|
19
5
|
'number', 'title', 'state', 'labels', 'assignees',
|
|
20
6
|
'author', 'createdAt', 'updatedAt', 'closedAt', 'url',
|
|
@@ -26,15 +12,22 @@ const STATIC_FIELDS = {
|
|
|
26
12
|
'baseRefName', 'headRefName', 'isDraft', 'mergedAt', 'reviewDecision',
|
|
27
13
|
],
|
|
28
14
|
};
|
|
15
|
+
// Fields returned by write commands (small, bounded — single created resource)
|
|
16
|
+
const WRITE_RESPONSE_FIELDS = {
|
|
17
|
+
'issue create': ['number', 'url', 'title', 'state'],
|
|
18
|
+
'pr create': ['number', 'url', 'title', 'state', 'baseRefName', 'headRefName'],
|
|
19
|
+
'issue comment': ['url', 'body'],
|
|
20
|
+
};
|
|
29
21
|
export function buildToolDefinitions() {
|
|
30
22
|
return [
|
|
23
|
+
// ── Tier 0: Read ────────────────────────────────────────────────────────
|
|
31
24
|
{
|
|
32
25
|
name: 'gh_issue_list',
|
|
33
26
|
description: 'List GitHub issues as structured JSON. ' +
|
|
34
|
-
'
|
|
35
|
-
'Read-only. Does not create, edit, or delete issues.',
|
|
27
|
+
'Read-only — executes immediately without approval.',
|
|
36
28
|
subcommand: ['issue', 'list'],
|
|
37
|
-
|
|
29
|
+
tier: 0,
|
|
30
|
+
jsonFields: READ_FIELDS['issue list'],
|
|
38
31
|
inputSchema: {
|
|
39
32
|
type: 'object',
|
|
40
33
|
properties: {
|
|
@@ -49,10 +42,10 @@ export function buildToolDefinitions() {
|
|
|
49
42
|
{
|
|
50
43
|
name: 'gh_pr_list',
|
|
51
44
|
description: 'List GitHub pull requests as structured JSON. ' +
|
|
52
|
-
'
|
|
53
|
-
'Read-only. Does not create, edit, merge, or close pull requests.',
|
|
45
|
+
'Read-only — executes immediately without approval.',
|
|
54
46
|
subcommand: ['pr', 'list'],
|
|
55
|
-
|
|
47
|
+
tier: 0,
|
|
48
|
+
jsonFields: READ_FIELDS['pr list'],
|
|
56
49
|
inputSchema: {
|
|
57
50
|
type: 'object',
|
|
58
51
|
properties: {
|
|
@@ -64,6 +57,66 @@ export function buildToolDefinitions() {
|
|
|
64
57
|
},
|
|
65
58
|
},
|
|
66
59
|
},
|
|
60
|
+
// ── Tier 2: Write (requires human TTY approval) ──────────────────────────
|
|
61
|
+
{
|
|
62
|
+
name: 'gh_issue_create',
|
|
63
|
+
description: 'Create a GitHub issue. ' +
|
|
64
|
+
'⚠️ WRITE OPERATION — blocks until a human approves at the terminal. ' +
|
|
65
|
+
'Uses local `gh` authentication. Requires title.',
|
|
66
|
+
subcommand: ['issue', 'create'],
|
|
67
|
+
tier: 2,
|
|
68
|
+
jsonFields: WRITE_RESPONSE_FIELDS['issue create'],
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
title: { type: 'string', description: 'Issue title (required).' },
|
|
73
|
+
body: { type: 'string', description: 'Issue body text.' },
|
|
74
|
+
repo: { type: 'string', description: 'OWNER/REPO. Omit to use current directory.' },
|
|
75
|
+
label: { type: 'string', description: 'Label name to apply.' },
|
|
76
|
+
assignee: { type: 'string', description: 'Assignee login.' },
|
|
77
|
+
},
|
|
78
|
+
required: ['title'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'gh_pr_create',
|
|
83
|
+
description: 'Create a GitHub pull request. ' +
|
|
84
|
+
'⚠️ WRITE OPERATION — blocks until a human approves at the terminal. ' +
|
|
85
|
+
'Uses local `gh` authentication. Requires title.',
|
|
86
|
+
subcommand: ['pr', 'create'],
|
|
87
|
+
tier: 2,
|
|
88
|
+
jsonFields: WRITE_RESPONSE_FIELDS['pr create'],
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
title: { type: 'string', description: 'PR title (required).' },
|
|
93
|
+
body: { type: 'string', description: 'PR body text.' },
|
|
94
|
+
base: { type: 'string', description: 'Base branch (default: repo default branch).' },
|
|
95
|
+
head: { type: 'string', description: 'Head branch (default: current branch).' },
|
|
96
|
+
repo: { type: 'string', description: 'OWNER/REPO. Omit to use current directory.' },
|
|
97
|
+
draft: { type: 'boolean', description: 'Open as draft PR.' },
|
|
98
|
+
},
|
|
99
|
+
required: ['title'],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'gh_issue_comment',
|
|
104
|
+
description: 'Add a comment to a GitHub issue. ' +
|
|
105
|
+
'⚠️ WRITE OPERATION — blocks until a human approves at the terminal. ' +
|
|
106
|
+
'Uses local `gh` authentication. Requires issue number and body.',
|
|
107
|
+
subcommand: ['issue', 'comment'],
|
|
108
|
+
tier: 2,
|
|
109
|
+
jsonFields: WRITE_RESPONSE_FIELDS['issue comment'],
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
issue: { type: 'number', description: 'Issue number (required).' },
|
|
114
|
+
body: { type: 'string', description: 'Comment text (required).' },
|
|
115
|
+
repo: { type: 'string', description: 'OWNER/REPO. Omit to use current directory.' },
|
|
116
|
+
},
|
|
117
|
+
required: ['issue', 'body'],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
67
120
|
];
|
|
68
121
|
}
|
|
69
122
|
/**
|
|
@@ -72,19 +125,71 @@ export function buildToolDefinitions() {
|
|
|
72
125
|
* misinterpreted as a separate flag by gh's argument parser.
|
|
73
126
|
*/
|
|
74
127
|
export function buildGhArgs(tool, args) {
|
|
75
|
-
const parts = [...tool.subcommand
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
128
|
+
const parts = [...tool.subcommand];
|
|
129
|
+
// All tools request JSON output for structured responses
|
|
130
|
+
if (tool.jsonFields && tool.jsonFields.length > 0) {
|
|
131
|
+
parts.push(`--json=${tool.jsonFields.join(',')}`);
|
|
132
|
+
}
|
|
133
|
+
switch (tool.name) {
|
|
134
|
+
case 'gh_issue_list':
|
|
135
|
+
if (args['repo'])
|
|
136
|
+
parts.push(`--repo=${String(args['repo'])}`);
|
|
137
|
+
if (args['limit'])
|
|
138
|
+
parts.push(`--limit=${String(args['limit'])}`);
|
|
139
|
+
if (args['state'])
|
|
140
|
+
parts.push(`--state=${String(args['state'])}`);
|
|
141
|
+
if (args['label'])
|
|
142
|
+
parts.push(`--label=${String(args['label'])}`);
|
|
143
|
+
if (args['assignee'])
|
|
144
|
+
parts.push(`--assignee=${String(args['assignee'])}`);
|
|
145
|
+
break;
|
|
146
|
+
case 'gh_pr_list':
|
|
147
|
+
if (args['repo'])
|
|
148
|
+
parts.push(`--repo=${String(args['repo'])}`);
|
|
149
|
+
if (args['limit'])
|
|
150
|
+
parts.push(`--limit=${String(args['limit'])}`);
|
|
151
|
+
if (args['state'])
|
|
152
|
+
parts.push(`--state=${String(args['state'])}`);
|
|
153
|
+
if (args['base'])
|
|
154
|
+
parts.push(`--base=${String(args['base'])}`);
|
|
155
|
+
if (args['assignee'])
|
|
156
|
+
parts.push(`--assignee=${String(args['assignee'])}`);
|
|
157
|
+
break;
|
|
158
|
+
case 'gh_issue_create':
|
|
159
|
+
parts.push(`--title=${String(args['title'])}`);
|
|
160
|
+
if (args['body'])
|
|
161
|
+
parts.push(`--body=${String(args['body'])}`);
|
|
162
|
+
if (args['repo'])
|
|
163
|
+
parts.push(`--repo=${String(args['repo'])}`);
|
|
164
|
+
if (args['label'])
|
|
165
|
+
parts.push(`--label=${String(args['label'])}`);
|
|
166
|
+
if (args['assignee'])
|
|
167
|
+
parts.push(`--assignee=${String(args['assignee'])}`);
|
|
168
|
+
break;
|
|
169
|
+
case 'gh_pr_create':
|
|
170
|
+
parts.push(`--title=${String(args['title'])}`);
|
|
171
|
+
if (args['body'])
|
|
172
|
+
parts.push(`--body=${String(args['body'])}`);
|
|
173
|
+
if (args['base'])
|
|
174
|
+
parts.push(`--base=${String(args['base'])}`);
|
|
175
|
+
if (args['head'])
|
|
176
|
+
parts.push(`--head=${String(args['head'])}`);
|
|
177
|
+
if (args['repo'])
|
|
178
|
+
parts.push(`--repo=${String(args['repo'])}`);
|
|
179
|
+
if (args['draft'])
|
|
180
|
+
parts.push('--draft');
|
|
181
|
+
break;
|
|
182
|
+
case 'gh_issue_comment':
|
|
183
|
+
parts.push(String(args['issue']));
|
|
184
|
+
parts.push(`--body=${String(args['body'])}`);
|
|
185
|
+
if (args['repo'])
|
|
186
|
+
parts.push(`--repo=${String(args['repo'])}`);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
88
189
|
return parts;
|
|
89
190
|
}
|
|
191
|
+
/** Human-readable preview of the command an agent intends to execute */
|
|
192
|
+
export function buildCommandPreview(tool, args) {
|
|
193
|
+
return 'gh ' + buildGhArgs(tool, args).join(' ');
|
|
194
|
+
}
|
|
90
195
|
//# sourceMappingURL=schema.js.map
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/security.d.ts
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
* Security layer.
|
|
3
3
|
*
|
|
4
4
|
* With direct spawn(bin, args[]) there is no shell to inject into.
|
|
5
|
-
* This layer provides
|
|
6
|
-
* 1.
|
|
7
|
-
* 2.
|
|
5
|
+
* This layer provides defence-in-depth:
|
|
6
|
+
* 1. Whitelists the exact subcommand paths allowed to execute.
|
|
7
|
+
* 2. Associates each subcommand with an operation tier.
|
|
8
|
+
* 3. Validates individual argument values for anomalous content.
|
|
9
|
+
*
|
|
10
|
+
* Tiers:
|
|
11
|
+
* 0 READ — executes immediately, no approval
|
|
12
|
+
* 2 WRITE — blocks until human approves via TTY
|
|
13
|
+
* 3 IRREVERSIBLE — never executes; not exposed as tools
|
|
8
14
|
*/
|
|
9
|
-
export
|
|
15
|
+
export type OperationTier = 0 | 2 | 3;
|
|
16
|
+
export declare function getOperationTier(subcommand: string): OperationTier;
|
|
10
17
|
export declare function validateArgs(args: Record<string, unknown>): void;
|
|
11
18
|
//# sourceMappingURL=security.d.ts.map
|
package/dist/security.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAUtC,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAMlE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAWhE"}
|
package/dist/security.js
CHANGED
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
* Security layer.
|
|
3
3
|
*
|
|
4
4
|
* With direct spawn(bin, args[]) there is no shell to inject into.
|
|
5
|
-
* This layer provides
|
|
6
|
-
* 1.
|
|
7
|
-
* 2.
|
|
5
|
+
* This layer provides defence-in-depth:
|
|
6
|
+
* 1. Whitelists the exact subcommand paths allowed to execute.
|
|
7
|
+
* 2. Associates each subcommand with an operation tier.
|
|
8
|
+
* 3. Validates individual argument values for anomalous content.
|
|
9
|
+
*
|
|
10
|
+
* Tiers:
|
|
11
|
+
* 0 READ — executes immediately, no approval
|
|
12
|
+
* 2 WRITE — blocks until human approves via TTY
|
|
13
|
+
* 3 IRREVERSIBLE — never executes; not exposed as tools
|
|
8
14
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'
|
|
12
|
-
'
|
|
15
|
+
const SUBCOMMAND_TIERS = new Map([
|
|
16
|
+
['issue list', 0],
|
|
17
|
+
['pr list', 0],
|
|
18
|
+
['issue create', 2],
|
|
19
|
+
['pr create', 2],
|
|
20
|
+
['issue comment', 2],
|
|
13
21
|
]);
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
export function getOperationTier(subcommand) {
|
|
23
|
+
const tier = SUBCOMMAND_TIERS.get(subcommand);
|
|
24
|
+
if (tier === undefined) {
|
|
25
|
+
throw new Error(`Error: Subcommand "${subcommand}" is not in the allow-list.`);
|
|
17
26
|
}
|
|
27
|
+
return tier;
|
|
18
28
|
}
|
|
19
29
|
export function validateArgs(args) {
|
|
20
30
|
for (const [, val] of Object.entries(args)) {
|
package/dist/security.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,gBAAgB,GAAuC,IAAI,GAAG,CAAC;IACnE,CAAC,YAAY,EAAK,CAAC,CAAC;IACpB,CAAC,SAAS,EAAQ,CAAC,CAAC;IACpB,CAAC,cAAc,EAAG,CAAC,CAAC;IACpB,CAAC,WAAW,EAAM,CAAC,CAAC;IACpB,CAAC,eAAe,EAAE,CAAC,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,6BAA6B,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAA6B;IACxD,KAAK,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAClE,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|