zero-config-cli-bridge 1.4.0 → 2.0.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,15 @@
1
+ /**
2
+ * True server-side Human-in-the-Loop gate.
3
+ *
4
+ * The MCP connection stays pending (agent receives nothing) until a human
5
+ * physically types 'y' at the terminal where this server runs.
6
+ *
7
+ * Uses /dev/tty for input — stdin is occupied by the MCP protocol and must
8
+ * not be consumed. /dev/tty provides direct TTY access regardless of how
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.
13
+ */
14
+ export declare function requestApproval(preview: string): Promise<boolean>;
15
+ //# sourceMappingURL=approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwCvE"}
@@ -0,0 +1,48 @@
1
+ import { createReadStream } from 'fs';
2
+ import { createInterface } from 'readline';
3
+ /**
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.
8
+ *
9
+ * Uses /dev/tty for input — stdin is occupied by the MCP protocol and must
10
+ * not be consumed. /dev/tty provides direct TTY access regardless of how
11
+ * stdin/stdout are redirected, exactly as sudo(8) and ssh(1) do.
12
+ *
13
+ * If no TTY is available (headless server, CI environment), the request is
14
+ * denied by default — fail-closed, not fail-open.
15
+ */
16
+ export async function requestApproval(preview) {
17
+ process.stderr.write('\n\x1b[33m━━━ APPROVAL REQUIRED ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n' +
18
+ '\x1b[33mAn agent wants to execute a write operation:\x1b[0m\n\n' +
19
+ ` \x1b[1m${preview}\x1b[0m\n\n` +
20
+ 'Type \x1b[32my\x1b[0m to approve, anything else to deny: ');
21
+ return new Promise((resolve) => {
22
+ let tty = null;
23
+ try {
24
+ tty = createReadStream('/dev/tty');
25
+ }
26
+ catch {
27
+ process.stderr.write('\x1b[31mNo TTY available — denied by default.\x1b[0m\n');
28
+ resolve(false);
29
+ return;
30
+ }
31
+ const rl = createInterface({ input: tty });
32
+ rl.once('line', (line) => {
33
+ rl.close();
34
+ tty?.destroy();
35
+ const approved = line.trim().toLowerCase() === 'y';
36
+ process.stderr.write(approved
37
+ ? '\x1b[32m✓ Approved — executing.\x1b[0m\n'
38
+ : '\x1b[31m✗ Denied — operation cancelled.\x1b[0m\n');
39
+ resolve(approved);
40
+ });
41
+ tty.on('error', () => {
42
+ rl.close();
43
+ process.stderr.write('\x1b[31mTTY error — denied by default.\x1b[0m\n');
44
+ resolve(false);
45
+ });
46
+ });
47
+ }
48
+ //# sourceMappingURL=approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,IAAI,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6EAA6E;QAC7E,iEAAiE;QACjE,YAAY,OAAO,aAAa;QAChC,2DAA2D,CAC5D,CAAC;IAEF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,GAAG,GAA+C,IAAI,CAAC;QAE3D,IAAI,CAAC;YACH,GAAG,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;YAC/E,OAAO,CAAC,KAAK,CAAC,CAAC;YACf,OAAO;QACT,CAAC;QAED,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAE3C,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,EAAE,OAAO,EAAE,CAAC;YAEf,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC;YACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,QAAQ;gBACN,CAAC,CAAC,0CAA0C;gBAC5C,CAAC,CAAC,kDAAkD,CACvD,CAAC;YACF,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACxE,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -3,12 +3,13 @@ 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 };
8
6
  /**
9
7
  * Executes a binary directly with an args array.
10
8
  * NO shell intermediary — shell injection is structurally impossible.
11
- * stdout is passed through unmodified; item-level truncation is the caller's responsibility.
9
+ *
10
+ * stdout is accumulated faithfully up to MAX_STDOUT_BYTES.
11
+ * If the ceiling is hit, the subprocess is killed with SIGKILL and the
12
+ * promise rejects — callers route this to an error envelope.
12
13
  */
13
14
  export declare function executeCommand(bin: string, args: string[], timeoutMs?: number): Promise<ExecuteResult>;
14
15
  //# sourceMappingURL=executor.d.ts.map
@@ -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;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"}
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;AAoBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,GAAE,MAAmB,GAC7B,OAAO,CAAC,aAAa,CAAC,CA8CxB"}
package/dist/executor.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { spawn } from 'child_process';
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 stdoutitem-level truncation in index.ts is the sole guard.
2
+ // stdout carries structured JSON. Byte-level truncation corrupts JSON structure,
3
+ // so we monitor cumulative size and kill the process if it exceeds the ceiling.
4
+ // At that point we reject — index.ts routes the error through the envelope.
5
5
  //
6
- // stderr carries error messages and diagnostic text (human-readable, bounded).
6
+ // Practical ceiling: STATIC_FIELDS × 30 items 300 KB in normal operation.
7
+ // 5 MB is unreachable in normal use and prevents OOM from runaway output.
8
+ const MAX_STDOUT_BYTES = 5 * 1024 * 1024; // 5 MB
9
+ // stderr carries error messages (human-readable, bounded by design).
7
10
  const MAX_STDERR_CHARS = 4_096;
8
11
  const TIMEOUT_MS = 15_000;
9
- const TIMEOUT_MS_PROBE = 5_000; // shorter timeout for capability probe calls
10
- export { TIMEOUT_MS_PROBE };
11
12
  function truncateStderr(s) {
12
13
  if (s.length <= MAX_STDERR_CHARS)
13
14
  return s;
@@ -16,7 +17,10 @@ function truncateStderr(s) {
16
17
  /**
17
18
  * Executes a binary directly with an args array.
18
19
  * NO shell intermediary — shell injection is structurally impossible.
19
- * stdout is passed through unmodified; item-level truncation is the caller's responsibility.
20
+ *
21
+ * stdout is accumulated faithfully up to MAX_STDOUT_BYTES.
22
+ * If the ceiling is hit, the subprocess is killed with SIGKILL and the
23
+ * promise rejects — callers route this to an error envelope.
20
24
  */
21
25
  export function executeCommand(bin, args, timeoutMs = TIMEOUT_MS) {
22
26
  return new Promise((resolve, reject) => {
@@ -26,17 +30,30 @@ export function executeCommand(bin, args, timeoutMs = TIMEOUT_MS) {
26
30
  });
27
31
  let stdoutBuf = '';
28
32
  let stderrBuf = '';
29
- proc.stdout.on('data', (chunk) => { stdoutBuf += chunk.toString(); });
30
- proc.stderr.on('data', (chunk) => { stderrBuf += chunk.toString(); });
33
+ let sizeExceeded = false;
34
+ proc.stdout.on('data', (chunk) => {
35
+ stdoutBuf += chunk.toString();
36
+ if (stdoutBuf.length > MAX_STDOUT_BYTES) {
37
+ sizeExceeded = true;
38
+ proc.kill('SIGKILL');
39
+ }
40
+ });
41
+ proc.stderr.on('data', (chunk) => {
42
+ stderrBuf += chunk.toString();
43
+ });
31
44
  const timer = setTimeout(() => {
32
45
  proc.kill('SIGKILL');
33
46
  reject(new Error(`Command timed out after ${timeoutMs}ms`));
34
47
  }, timeoutMs);
35
48
  proc.on('close', (code) => {
36
49
  clearTimeout(timer);
50
+ if (sizeExceeded) {
51
+ reject(new Error(`stdout exceeded ${MAX_STDOUT_BYTES}-byte limit. Use --limit or filters to reduce output.`));
52
+ return;
53
+ }
37
54
  resolve({
38
- stdout: stdoutBuf, // unmodified
39
- stderr: truncateStderr(stderrBuf), // bounded
55
+ stdout: stdoutBuf,
56
+ stderr: truncateStderr(stderrBuf),
40
57
  exitCode: code ?? 1,
41
58
  });
42
59
  });
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAQtC,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAEjD,qEAAqE;AACrE,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,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;;;;;;;GAOG;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;QACnB,IAAI,YAAY,GAAG,KAAK,CAAC;QAEzB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACvC,SAAS,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,SAAS,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;gBACxC,YAAY,GAAG,IAAI,CAAC;gBACpB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACvC,SAAS,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,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,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,gBAAgB,uDAAuD,CAAC,CAAC,CAAC;gBAC9G,OAAO;YACT,CAAC;YACD,OAAO,CAAC;gBACN,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC;gBACjC,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,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":"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"}
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.4.0",
3
+ "version": "2.0.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",