zero-config-cli-bridge 2.0.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 +10 -9
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +161 -35
- package/dist/approval.js.map +1 -1
- package/package.json +1 -1
package/dist/approval.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* True server-side Human-in-the-Loop gate.
|
|
2
|
+
* True server-side Human-in-the-Loop gate for headless MCP environments.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* stdin/stdout are redirected, exactly as sudo(8) and ssh(1) do.
|
|
10
|
-
*
|
|
11
|
-
* If no TTY is available (headless server, CI environment), the request is
|
|
12
|
-
* denied by default — fail-closed, not fail-open.
|
|
12
|
+
* The one-time token in the URL prevents other localhost processes from
|
|
13
|
+
* silently approving or denying without user interaction.
|
|
13
14
|
*/
|
|
14
15
|
export declare function requestApproval(preview: string): Promise<boolean>;
|
|
15
16
|
//# sourceMappingURL=approval.d.ts.map
|
package/dist/approval.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"
|
|
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
CHANGED
|
@@ -1,48 +1,174 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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 ──────────────────────────────────────────────────────────────
|
|
3
101
|
/**
|
|
4
|
-
* True server-side Human-in-the-Loop gate.
|
|
5
|
-
*
|
|
6
|
-
* The MCP connection stays pending (agent receives nothing) until a human
|
|
7
|
-
* physically types 'y' at the terminal where this server runs.
|
|
102
|
+
* True server-side Human-in-the-Loop gate for headless MCP environments.
|
|
8
103
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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.
|
|
12
111
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
112
|
+
* The one-time token in the URL prevents other localhost processes from
|
|
113
|
+
* silently approving or denying without user interaction.
|
|
15
114
|
*/
|
|
16
115
|
export async function requestApproval(preview) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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}`;
|
|
21
120
|
return new Promise((resolve) => {
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
rl.close();
|
|
34
|
-
tty?.destroy();
|
|
35
|
-
const approved = line.trim().toLowerCase() === 'y';
|
|
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);
|
|
36
132
|
process.stderr.write(approved
|
|
37
|
-
? '\x1b[32m✓ Approved — executing.\x1b[0m\n'
|
|
38
|
-
: '\x1b[31m✗ Denied — operation cancelled.\x1b[0m\n');
|
|
133
|
+
? '\x1b[32m✓ Approved — executing write operation.\x1b[0m\n\n'
|
|
134
|
+
: '\x1b[31m✗ Denied — write operation cancelled.\x1b[0m\n\n');
|
|
39
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);
|
|
40
161
|
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
resolve(false);
|
|
162
|
+
server.on('error', (err) => {
|
|
163
|
+
process.stderr.write(`Approval server error: ${err.message}\n`);
|
|
164
|
+
settle(false);
|
|
45
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);
|
|
46
172
|
});
|
|
47
173
|
}
|
|
48
174
|
//# sourceMappingURL=approval.js.map
|
package/dist/approval.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"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"}
|