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.
@@ -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"}
@@ -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, '&amp;').replace(/</g, '&lt;');
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 { validateSubcommand, validateArgs } from './security.js';
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; // 200 KB cap for non-array JSON objects
10
+ const MAX_SERIALISED_CHARS = 200_000;
10
11
  /**
11
- * Converts gh stdout (JSON) into a bounded, always-valid envelope.
12
- * Called only when exitCode === 0.
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 JSON object: guard against unbounded size
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 envelopeToResponse(envelope, isError) {
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
- // Tool registry is synchronously populated at startup — no subprocess calls.
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: '1.4.0' }, { capabilities: { tools: {} } });
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 envelopeToResponse({ data: null, meta: { truncated: false, error: `Unknown tool "${toolName}".` } }, true);
94
- }
95
- // Security: whitelist subcommand + validate arg values
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 envelopeToResponse({ data: null, meta: { truncated: false, error: err instanceof Error ? err.message : String(err) } }, true);
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
- // Direct spawn no shell, no injection surface
104
+ // Executedirect 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 envelopeToResponse({ data: null, meta: { truncated: false, error: `Execution error: ${err instanceof Error ? err.message : String(err)}` } }, true);
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 envelopeToResponse(stderrToEnvelope(result.stderr, result.stdout), true);
114
+ return envelopeResponse(stderrToEnvelope(result.stderr, result.stdout), true);
117
115
  }
118
- return envelopeToResponse(stdoutToEnvelope(result.stdout), false);
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,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAEhF,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,oBAAoB,GAAG,OAAO,CAAC,CAAC,wCAAwC;AAiB9E;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,MAAc;IACtC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;IACpE,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,mFAAmF;QACnF,OAAO;YACL,IAAI,EAAE,IAAI;YACV,IAAI,EAAE;gBACJ,SAAS,EAAE,KAAK;gBAChB,KAAK,EAAE,uCAAuC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;aACrE;SACF,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,cAAc,CAAC;QACjD,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAClE,OAAO;YACL,IAAI;YACJ,IAAI,EAAE;gBACJ,SAAS;gBACT,aAAa,EAAE,IAAI,CAAC,MAAM;gBAC1B,GAAG,CAAC,SAAS;oBACX,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,cAAc,mDAAmD,EAAE;oBAC9F,CAAC,CAAC,EAAE,CAAC;aACR;SACF,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,UAAU,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAC7C,OAAO;YACL,IAAI,EAAE,IAAI;YACV,IAAI,EAAE;gBACJ,SAAS,EAAE,IAAI;gBACf,KAAK,EAAE,8BAA8B,UAAU,CAAC,MAAM,yCAAyC;aAChG;SACF,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,MAAc,EAAE,MAAc;IACtD,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,MAAM,IAAI,+BAA+B,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3E,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAsB,EAAE,OAAgB;IAClE,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACpE,OAAO;KACR,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;AACrC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAyB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpF,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,OAAO,EAAE,EACpD,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;AAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;QACpF,IAAI;QACJ,WAAW;QACX,WAAW;KACZ,CAAC,CAAC;CACJ,CAAC,CAAC,CAAC;AAEJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9D,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAA4B,CAAC;IAExD,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,kBAAkB,CACvB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,QAAQ,IAAI,EAAE,EAAE,EAChF,IAAI,CACL,CAAC;IACJ,CAAC;IAED,uDAAuD;IACvD,IAAI,CAAC;QACH,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9C,YAAY,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,kBAAkB,CACvB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EACnG,IAAI,CACL,CAAC;IACJ,CAAC;IAED,gDAAgD;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAEvC,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,kBAAkB,CACvB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EACzH,IAAI,CACL,CAAC;IACJ,CAAC;IAED,+CAA+C;IAC/C,gFAAgF;IAChF,4EAA4E;IAC5E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,kBAAkB,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,kBAAkB,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,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,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
- jsonFields: string[];
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
@@ -1 +1 @@
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"}
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 STATIC_FIELDS = {
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
- 'Uses the local `gh` CLI and its existing authentication no API key required. ' +
35
- 'Read-only. Does not create, edit, or delete issues.',
27
+ 'Read-onlyexecutes immediately without approval.',
36
28
  subcommand: ['issue', 'list'],
37
- jsonFields: STATIC_FIELDS['issue list'],
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
- 'Uses the local `gh` CLI and its existing authentication no API key required. ' +
53
- 'Read-only. Does not create, edit, merge, or close pull requests.',
45
+ 'Read-onlyexecutes immediately without approval.',
54
46
  subcommand: ['pr', 'list'],
55
- jsonFields: STATIC_FIELDS['pr list'],
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, `--json=${tool.jsonFields.join(',')}`];
76
- if (args['repo'] !== undefined)
77
- parts.push(`--repo=${String(args['repo'])}`);
78
- if (args['limit'] !== undefined)
79
- parts.push(`--limit=${String(args['limit'])}`);
80
- if (args['state'] !== undefined)
81
- parts.push(`--state=${String(args['state'])}`);
82
- if (args['label'] !== undefined && tool.name === 'gh_issue_list')
83
- parts.push(`--label=${String(args['label'])}`);
84
- if (args['assignee'] !== undefined)
85
- parts.push(`--assignee=${String(args['assignee'])}`);
86
- if (args['base'] !== undefined && tool.name === 'gh_pr_list')
87
- parts.push(`--base=${String(args['base'])}`);
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
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -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 defense-in-depth by:
6
- * 1. Whitelisting the exact subcommand paths allowed to execute.
7
- * 2. Validating individual argument values for anomalous content.
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 declare function validateSubcommand(subcommand: string): void;
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
@@ -1 +1 @@
1
- {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAM3D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAWhE"}
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 defense-in-depth by:
6
- * 1. Whitelisting the exact subcommand paths allowed to execute.
7
- * 2. Validating individual argument values for anomalous content.
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
- /** Only these gh subcommand paths may execute. */
10
- const ALLOWED_SUBCOMMANDS = new Set([
11
- 'issue list',
12
- 'pr list',
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 validateSubcommand(subcommand) {
15
- if (!ALLOWED_SUBCOMMANDS.has(subcommand)) {
16
- throw new Error(`Error: Subcommand "${subcommand}" is not in the read-only allow-list.`);
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)) {
@@ -1 +1 @@
1
- {"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,kDAAkD;AAClD,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IACvD,YAAY;IACZ,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CACb,sBAAsB,UAAU,uCAAuC,CACxE,CAAC;IACJ,CAAC;AACH,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"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zero-config-cli-bridge",
3
- "version": "1.5.0",
3
+ "version": "2.1.0",
4
4
  "description": "Zero Setup. Zero API Keys. Expose your local authenticated CLIs as MCP tools.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",