@priyanshumit/macos-terminal-mcp 0.3.0 → 0.5.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/README.md +14 -3
- package/dist/applescript.js +27 -1
- package/dist/applescript.js.map +1 -1
- package/dist/safety/audit.js +18 -4
- package/dist/safety/audit.js.map +1 -1
- package/dist/safety/confirm.js +32 -3
- package/dist/safety/confirm.js.map +1 -1
- package/dist/safety/patterns.js +38 -5
- package/dist/safety/patterns.js.map +1 -1
- package/dist/tools/execute.js +120 -9
- package/dist/tools/execute.js.map +1 -1
- package/dist/tools/new_tab.js +70 -0
- package/dist/tools/new_tab.js.map +1 -0
- package/dist/tools/pending.js +6 -6
- package/dist/tools/pending.js.map +1 -1
- package/dist/tools/register.js +2 -0
- package/dist/tools/register.js.map +1 -1
- package/dist/tools/safety.js +36 -21
- package/dist/tools/safety.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./assets/logo.png" alt="macos-terminal-mcp" width="200" height="200" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">macos-terminal-mcp</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@priyanshumit/macos-terminal-mcp"><img src="https://img.shields.io/npm/v/@priyanshumit/macos-terminal-mcp.svg" alt="npm version"></a>
|
|
9
|
+
<a href="https://github.com/priyanshumit/macos-terminal-mcp/actions/workflows/ci.yml"><img src="https://github.com/priyanshumit/macos-terminal-mcp/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
|
|
10
|
+
<a href="./LICENSE"><img src="https://img.shields.io/npm/l/@priyanshumit/macos-terminal-mcp.svg" alt="MIT license"></a>
|
|
11
|
+
</p>
|
|
2
12
|
|
|
3
13
|
A local MCP server that lets AI agents inspect and drive your macOS Terminal.app tabs — list windows, read scrollback, execute commands, and clear buffers, all gated by a three-tier safety model plus per-call user confirmation for any write operation.
|
|
4
14
|
|
|
5
15
|
## What it does
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
Twelve MCP tools across three categories:
|
|
8
18
|
|
|
9
19
|
### Terminal interaction
|
|
10
20
|
|
|
@@ -12,8 +22,9 @@ Eleven MCP tools across three categories:
|
|
|
12
22
|
|---|---|---|
|
|
13
23
|
| `terminal_list` | read | Enumerate every open Terminal.app tab with tty, title, busy state, and foreground processes. |
|
|
14
24
|
| `terminal_read` | read | Return the full buffer + scrollback of a specific tab, identified by tty. |
|
|
15
|
-
| `terminal_execute` | **write** | Type a command into a specific tab and press Enter.
|
|
25
|
+
| `terminal_execute` | **write** | Type a command into a specific tab and press Enter. Refuses if the target tab is busy unless `force=true`. `dry_run=true` returns the safety verdict without any side effects. |
|
|
16
26
|
| `terminal_clear` | **write** | Wipe scrollback of a specific tab via Cmd+K. Briefly steals focus. |
|
|
27
|
+
| `terminal_new_tab` | **write** | Open a new empty tab in Terminal.app and return its tty for follow-up calls. No dialog — low blast radius (user can close the tab). |
|
|
17
28
|
|
|
18
29
|
### Safety policy management
|
|
19
30
|
|
package/dist/applescript.js
CHANGED
|
@@ -3,11 +3,13 @@ export class OsascriptError extends Error {
|
|
|
3
3
|
stderr;
|
|
4
4
|
code;
|
|
5
5
|
timedOut;
|
|
6
|
-
|
|
6
|
+
aborted;
|
|
7
|
+
constructor(message, stderr, code, timedOut = false, aborted = false) {
|
|
7
8
|
super(message);
|
|
8
9
|
this.stderr = stderr;
|
|
9
10
|
this.code = code;
|
|
10
11
|
this.timedOut = timedOut;
|
|
12
|
+
this.aborted = aborted;
|
|
11
13
|
this.name = "OsascriptError";
|
|
12
14
|
}
|
|
13
15
|
}
|
|
@@ -15,12 +17,30 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
15
17
|
export function runJxa(script, options = {}) {
|
|
16
18
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
17
19
|
return new Promise((resolve, reject) => {
|
|
20
|
+
// Fast-path: already-aborted signal — don't even spawn.
|
|
21
|
+
if (options.signal?.aborted) {
|
|
22
|
+
reject(new OsascriptError("osascript aborted before launch", "", -1, false, true));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
18
25
|
const proc = spawn("osascript", ["-l", "JavaScript"], {
|
|
19
26
|
stdio: ["pipe", "pipe", "pipe"],
|
|
20
27
|
});
|
|
21
28
|
let stdout = "";
|
|
22
29
|
let stderr = "";
|
|
23
30
|
let settled = false;
|
|
31
|
+
const onAbort = () => {
|
|
32
|
+
if (settled)
|
|
33
|
+
return;
|
|
34
|
+
settled = true;
|
|
35
|
+
try {
|
|
36
|
+
proc.kill("SIGKILL");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
/* ignore */
|
|
40
|
+
}
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
reject(new OsascriptError("osascript aborted", stderr.trim(), -1, false, true));
|
|
43
|
+
};
|
|
24
44
|
const timer = setTimeout(() => {
|
|
25
45
|
if (settled)
|
|
26
46
|
return;
|
|
@@ -31,8 +51,12 @@ export function runJxa(script, options = {}) {
|
|
|
31
51
|
catch {
|
|
32
52
|
/* ignore */
|
|
33
53
|
}
|
|
54
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
34
55
|
reject(new OsascriptError(`osascript timed out after ${timeoutMs}ms`, stderr.trim(), -1, true));
|
|
35
56
|
}, timeoutMs);
|
|
57
|
+
if (options.signal) {
|
|
58
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
59
|
+
}
|
|
36
60
|
proc.stdout.on("data", (chunk) => {
|
|
37
61
|
stdout += chunk.toString("utf8");
|
|
38
62
|
});
|
|
@@ -44,6 +68,7 @@ export function runJxa(script, options = {}) {
|
|
|
44
68
|
return;
|
|
45
69
|
settled = true;
|
|
46
70
|
clearTimeout(timer);
|
|
71
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
47
72
|
reject(err);
|
|
48
73
|
});
|
|
49
74
|
proc.on("close", (code) => {
|
|
@@ -51,6 +76,7 @@ export function runJxa(script, options = {}) {
|
|
|
51
76
|
return;
|
|
52
77
|
settled = true;
|
|
53
78
|
clearTimeout(timer);
|
|
79
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
54
80
|
if (code === 0) {
|
|
55
81
|
resolve(stdout.replace(/\n$/, ""));
|
|
56
82
|
}
|
package/dist/applescript.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"applescript.js","sourceRoot":"","sources":["../src/applescript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,OAAO,cAAe,SAAQ,KAAK;IAGrB;IACA;IACA;
|
|
1
|
+
{"version":3,"file":"applescript.js","sourceRoot":"","sources":["../src/applescript.ts"],"names":[],"mappings":"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"}
|
package/dist/safety/audit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFile, mkdir } from "node:fs/promises";
|
|
1
|
+
import { appendFile, chmod, mkdir } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
function resolveDefaultPath() {
|
|
@@ -11,17 +11,31 @@ const COMMAND_TRUNCATE = 1000;
|
|
|
11
11
|
function truncate(s, max) {
|
|
12
12
|
return s.length > max ? `${s.slice(0, max)}…[+${s.length - max}]` : s;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Append a JSONL audit entry. Best-effort — failures are logged to stderr but do
|
|
16
|
+
* not throw. Timestamp is always server-generated; callers cannot override it.
|
|
17
|
+
*
|
|
18
|
+
* Directory created with mode 0o700, file with mode 0o600. If the file already
|
|
19
|
+
* exists with wider permissions (e.g. from an older version), it is tightened
|
|
20
|
+
* to 0o600 on every write (no-op once already restricted).
|
|
21
|
+
*/
|
|
14
22
|
export async function appendAudit(entry, path = AUDIT_LOG_PATH) {
|
|
23
|
+
// Spread entry first, then always overwrite timestamp last so any type-cast
|
|
24
|
+
// bypass that smuggles a "timestamp" field in entry still loses to the
|
|
25
|
+
// server-generated value.
|
|
15
26
|
const record = {
|
|
16
|
-
timestamp: entry.timestamp ?? new Date().toISOString(),
|
|
17
27
|
...entry,
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
18
29
|
};
|
|
19
30
|
if (record.command !== undefined) {
|
|
20
31
|
record.command = truncate(record.command, COMMAND_TRUNCATE);
|
|
21
32
|
}
|
|
22
33
|
try {
|
|
23
|
-
await mkdir(dirname(path), { recursive: true });
|
|
24
|
-
|
|
34
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
35
|
+
// Best-effort tighten of any pre-existing file permissions. Ignored if file
|
|
36
|
+
// doesn't yet exist (next appendFile creates it with mode 0o600 below).
|
|
37
|
+
await chmod(path, 0o600).catch(() => undefined);
|
|
38
|
+
await appendFile(path, `${JSON.stringify(record)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
25
39
|
}
|
|
26
40
|
catch (err) {
|
|
27
41
|
process.stderr.write(`[macos-terminal-mcp] audit log write failed: ${err.message}\n`);
|
package/dist/safety/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/safety/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/safety/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAmB1C,SAAS,kBAAkB;IACzB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACvC,MAAM,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9E,OAAO,IAAI,CAAC,IAAI,EAAE,oBAAoB,EAAE,WAAW,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,kBAAkB,EAAE,CAAC;AAEnD,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAoC,EACpC,OAAe,cAAc;IAE7B,4EAA4E;IAC5E,uEAAuE;IACvE,0BAA0B;IAC1B,MAAM,MAAM,GAAe;QACzB,GAAG,KAAK;QACR,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,CAAC,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,4EAA4E;QAC5E,wEAAwE;QACxE,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3F,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,gDAAiD,GAAa,CAAC,OAAO,IAAI,CAC3E,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/safety/confirm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runJxa } from "../applescript.js";
|
|
1
|
+
import { OsascriptError, runJxa } from "../applescript.js";
|
|
2
2
|
export function isWriteToolsEnabled() {
|
|
3
3
|
return process.env.WRITE_TOOLS_ENABLED === "1";
|
|
4
4
|
}
|
|
@@ -32,8 +32,37 @@ app.includeStandardAdditions = true;
|
|
|
32
32
|
`;
|
|
33
33
|
// Outer osascript timeout is dialog timeout + 10s buffer so the dialog's own
|
|
34
34
|
// givingUpAfter has a chance to fire before the spawn-level kill engages.
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
try {
|
|
36
|
+
const result = await runJxa(script, {
|
|
37
|
+
timeoutMs: (timeoutSec + 10) * 1000,
|
|
38
|
+
signal: req.signal,
|
|
39
|
+
});
|
|
40
|
+
return result.trim() === "ALLOW";
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
// Aborted via signal — treat as denial. Caller is responsible for its own
|
|
44
|
+
// out-of-band approval path (e.g. queue resolution); we just dismiss the dialog.
|
|
45
|
+
if (err instanceof OsascriptError && err.aborted) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Strip C0 control characters (including newlines, tabs, CR) from AI-supplied
|
|
53
|
+
* strings before embedding them in confirmation dialog message templates.
|
|
54
|
+
*
|
|
55
|
+
* Without this, a model can inject fake structured fields like "Queue id: ..."
|
|
56
|
+
* or "Approval: GRANTED" inside a dialog's text by including newlines in its
|
|
57
|
+
* command/description, visually impersonating system-rendered content.
|
|
58
|
+
*/
|
|
59
|
+
// Pattern is constructed via new RegExp(string) to avoid embedding literal
|
|
60
|
+
// control bytes in the source file (which Biome flags and which is also hard
|
|
61
|
+
// to audit visually). Matches C0 control characters (U+0000–U+001F) plus DEL.
|
|
62
|
+
// biome-ignore lint/complexity/useRegexLiterals: regex-literal form trips noControlCharactersInRegex on the same character class (escape sequences vs bytes both flagged). RegExp(string) avoids the false positive without obscuring intent.
|
|
63
|
+
const CONTROL_CHARS_RE = new RegExp("[\u0000-\u001F\u007F]", "g");
|
|
64
|
+
export function sanitizeAiText(s) {
|
|
65
|
+
return s.replace(CONTROL_CHARS_RE, " ");
|
|
37
66
|
}
|
|
38
67
|
export function writeToolsDisabledMessage(toolName) {
|
|
39
68
|
return (`${toolName} is disabled. Write tools (terminal_execute, terminal_clear, safety_*) ` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/safety/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/safety/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3D,MAAM,UAAU,mBAAmB;IACjC,OAAO,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,GAAG,CAAC;AACjD,CAAC;AAaD,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAmB;IACvD,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;IACxC,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC;IACrC,MAAM,UAAU,GAAG,GAAG,CAAC,cAAc,IAAI,0BAA0B,CAAC;IACpE,MAAM,MAAM,GAAG;;;;;;QAMT,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;;oBAEf,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;yBACzC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;wBACrB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;qBACvB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;;yBAErB,UAAU;;;;uCAII,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;;;;;CAK3D,CAAC;IACA,6EAA6E;IAC7E,0EAA0E;IAC1E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE;YAClC,SAAS,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,IAAI;YACnC,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,EAAE,KAAK,OAAO,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,0EAA0E;QAC1E,iFAAiF;QACjF,IAAI,GAAG,YAAY,cAAc,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,2EAA2E;AAC3E,6EAA6E;AAC7E,8EAA8E;AAC9E,8OAA8O;AAC9O,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;AAElE,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,QAAgB;IACxD,OAAO,CACL,GAAG,QAAQ,yEAAyE;QACpF,qEAAqE;QACrE,4EAA4E;QAC5E,0CAA0C,CAC3C,CAAC;AACJ,CAAC"}
|
package/dist/safety/patterns.js
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import safeRegex from "safe-regex";
|
|
5
|
+
/**
|
|
6
|
+
* Reason a pattern is rejected, or null if the pattern is valid + safe.
|
|
7
|
+
* "Safe" here means: does not contain catastrophic-backtracking constructs
|
|
8
|
+
* (e.g. (a+)+b, (\w+)+!) that would let a single pattern DoS the server.
|
|
9
|
+
*/
|
|
10
|
+
export function regexErrorReason(pattern) {
|
|
11
|
+
try {
|
|
12
|
+
new RegExp(pattern);
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
return `not a valid regex: ${e.message}`;
|
|
16
|
+
}
|
|
17
|
+
if (!safeRegex(pattern)) {
|
|
18
|
+
return "contains a ReDoS-prone construct (nested quantifiers, overlapping repeats, or ambiguous alternations)";
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export function isSafePattern(pattern) {
|
|
23
|
+
return regexErrorReason(pattern) === null;
|
|
24
|
+
}
|
|
4
25
|
export const SAFETY_CONFIG_PATH = join(homedir(), ".config", "macos-terminal-mcp", "safety.json");
|
|
5
26
|
const DEFAULT_PATTERNS = [
|
|
6
27
|
// FORBIDDEN — never run, even with confirmation. The user must run these in a terminal themselves.
|
|
@@ -98,14 +119,21 @@ export async function saveSafetyConfig(config, path = SAFETY_CONFIG_PATH) {
|
|
|
98
119
|
export function defaultPatterns() {
|
|
99
120
|
return DEFAULT_PATTERNS.map((p) => ({ ...p }));
|
|
100
121
|
}
|
|
122
|
+
function filterSafePatterns(patterns) {
|
|
123
|
+
return patterns.filter((e) => {
|
|
124
|
+
if (isSafePattern(e.pattern))
|
|
125
|
+
return true;
|
|
126
|
+
process.stderr.write(`[macos-terminal-mcp] dropping unsafe pattern from config: ${JSON.stringify(e.pattern)} (${regexErrorReason(e.pattern) ?? "unknown"})\n`);
|
|
127
|
+
return false;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
101
130
|
export function normalizeConfig(raw) {
|
|
102
131
|
if (!raw || typeof raw !== "object")
|
|
103
132
|
return { patterns: DEFAULT_PATTERNS };
|
|
104
133
|
const o = raw;
|
|
105
134
|
if (Array.isArray(o.patterns)) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
135
|
+
const valid = o.patterns.filter((e) => isValidEntry(e));
|
|
136
|
+
return { patterns: filterSafePatterns(valid) };
|
|
109
137
|
}
|
|
110
138
|
// Migrate v1 schema {allowlist: [], denylist: []} → v2 {patterns: [...]}
|
|
111
139
|
if (Array.isArray(o.allowlist) || Array.isArray(o.denylist)) {
|
|
@@ -132,7 +160,8 @@ export function normalizeConfig(raw) {
|
|
|
132
160
|
}
|
|
133
161
|
}
|
|
134
162
|
}
|
|
135
|
-
|
|
163
|
+
const filtered = filterSafePatterns(patterns);
|
|
164
|
+
return { patterns: filtered.length > 0 ? filtered : DEFAULT_PATTERNS };
|
|
136
165
|
}
|
|
137
166
|
return { patterns: DEFAULT_PATTERNS };
|
|
138
167
|
}
|
|
@@ -150,9 +179,13 @@ const LEVEL_RANK = {
|
|
|
150
179
|
forbidden: 2,
|
|
151
180
|
};
|
|
152
181
|
export function evaluateCommand(command, config) {
|
|
182
|
+
// Normalize via NFKC so fullwidth/compatibility Unicode characters fold to
|
|
183
|
+
// their ASCII equivalents. Closes the rm -rf homoglyph bypass — a model
|
|
184
|
+
// submitting Unicode lookalikes can't sneak past patterns using \b assertions.
|
|
185
|
+
const normalized = command.normalize("NFKC");
|
|
153
186
|
let result = null;
|
|
154
187
|
for (const entry of config.patterns) {
|
|
155
|
-
if (!testPattern(entry.pattern,
|
|
188
|
+
if (!testPattern(entry.pattern, normalized))
|
|
156
189
|
continue;
|
|
157
190
|
if (!result || LEVEL_RANK[entry.level] > LEVEL_RANK[result.level]) {
|
|
158
191
|
result = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/safety/patterns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/safety/patterns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,SAAS,MAAM,YAAY,CAAC;AAInC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,IAAI,CAAC;QACH,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,sBAAuB,CAAW,CAAC,OAAO,EAAE,CAAC;IACtD,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,OAAO,uGAAuG,CAAC;IACjH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,gBAAgB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;AAC5C,CAAC;AAYD,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,oBAAoB,EAAE,aAAa,CAAC,CAAC;AAElG,MAAM,gBAAgB,GAAmB;IACvC,mGAAmG;IACnG;QACE,OAAO,EAAE,kBAAkB;QAC3B,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,yDAAyD;KACvE;IACD;QACE,OAAO,EAAE,YAAY;QACrB,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,uDAAuD;KACrE;IACD;QACE,OAAO,EAAE,yBAAyB;QAClC,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,4CAA4C;KAC1D;IACD;QACE,OAAO,EAAE,qBAAqB;QAC9B,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,+BAA+B;KAC7C;IACD;QACE,OAAO,EAAE,qBAAqB;QAC9B,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,+BAA+B;KAC7C;IACD,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;IAC7E,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;IAC7E,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,sBAAsB,EAAE;IACnF,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,sBAAsB,EAAE;IACnF,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE;IAC3E,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACxF,EAAE,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE;IAClF,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE;IACjF,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE;IAC7E,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE;IAClF;QACE,OAAO,EAAE,mCAAmC;QAC5C,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,gCAAgC;KAC9C;IACD;QACE,OAAO,EAAE,8BAA8B;QACvC,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,wCAAwC;KACtD;IACD;QACE,OAAO,EAAE,+BAA+B;QACxC,KAAK,EAAE,WAAW;QAClB,WAAW,EAAE,uCAAuC;KACrD;IAED,2CAA2C;IAC3C,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,yBAAyB,EAAE;IAChF,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,yBAAyB,EAAE;IACjF,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE;IACzE,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE;IACzE,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACzE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,qBAAqB,EAAE;IAC1E,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,yBAAyB,EAAE;IAC9E,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,wBAAwB,EAAE;IAC7E,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE;IACxE,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,oBAAoB,EAAE;IACzE,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE;IAC5E,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE;IACzE,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE;IAC1E;QACE,OAAO,EAAE,4DAA4D;QACrE,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,0BAA0B;KACxC;IACD;QACE,OAAO,EAAE,4DAA4D;QACrE,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,gCAAgC;KAC9C;IACD,EAAE,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,EAAE;IACnD,EAAE,OAAO,EAAE,2BAA2B,EAAE,KAAK,EAAE,MAAM,EAAE;CACxD,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAe,kBAAkB;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IACxC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAoB,EACpB,OAAe,kBAAkB;IAEjC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAwB;IAClD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC3B,IAAI,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6DAA6D,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,SAAS,KAAK,CACzI,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC3E,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAU,EAAqB,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACpF,OAAO,EAAE,QAAQ,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;IACjD,CAAC;IACD,yEAAyE;IACzE,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5D,MAAM,QAAQ,GAAmB,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC3B,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;oBAC1B,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,CAAC;wBACV,KAAK,EAAE,mBAAmB;wBAC1B,WAAW,EAAE,2BAA2B;qBACzC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;gBAC5B,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;oBAC1B,QAAQ,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,CAAC;wBACV,KAAK,EAAE,MAAM;wBACb,WAAW,EAAE,4BAA4B;qBAC1C,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAC9C,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,YAAY,CAAC,CAAU;IAC9B,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,mBAAmB,IAAI,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC;QAClF,CAAC,CAAC,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,CACnE,CAAC;AACJ,CAAC;AAQD,MAAM,UAAU,GAAgC;IAC9C,IAAI,EAAE,CAAC;IACP,iBAAiB,EAAE,CAAC;IACpB,SAAS,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,MAAoB;IACnE,2EAA2E;IAC3E,wEAAwE;IACxE,+EAA+E;IAC/E,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM,GAAyB,IAAI,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC;YAAE,SAAS;QACtD,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAClE,MAAM,GAAG;gBACP,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,cAAc,EAAE,KAAK,CAAC,OAAO;gBAC7B,kBAAkB,EAAE,KAAK,CAAC,WAAW;aACtC,CAAC;QACJ,CAAC;IACH,CAAC;IACD,sEAAsE;IACtE,OAAO,MAAM,IAAI,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,WAAW,CAAC,OAAe,EAAE,OAAe;IACnD,IAAI,CAAC;QACH,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/tools/execute.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { OsascriptError, runJxa } from "../applescript.js";
|
|
3
3
|
import { appendAudit } from "../safety/audit.js";
|
|
4
|
-
import { confirmWithUser, isWriteToolsEnabled, writeToolsDisabledMessage, } from "../safety/confirm.js";
|
|
4
|
+
import { confirmWithUser, isWriteToolsEnabled, sanitizeAiText, writeToolsDisabledMessage, } from "../safety/confirm.js";
|
|
5
5
|
import { evaluateCommand, loadSafetyConfig } from "../safety/patterns.js";
|
|
6
6
|
import { enqueue, resolvePending } from "../safety/queue.js";
|
|
7
7
|
export function buildExecuteScript(tty, command) {
|
|
@@ -25,10 +25,30 @@ function safe(fn) { try { return fn(); } catch (e) { return null; } }
|
|
|
25
25
|
})(${JSON.stringify(tty)}, ${JSON.stringify(command)});
|
|
26
26
|
`;
|
|
27
27
|
}
|
|
28
|
+
function buildBusyCheckScript(tty) {
|
|
29
|
+
return `
|
|
30
|
+
function safe(fn) { try { return fn(); } catch (e) { return null; } }
|
|
31
|
+
(function checkBusy(targetTty) {
|
|
32
|
+
const terminal = Application("Terminal");
|
|
33
|
+
const wins = terminal.windows();
|
|
34
|
+
for (let wi = 0; wi < wins.length; wi++) {
|
|
35
|
+
const w = wins[wi];
|
|
36
|
+
const tabs = w.tabs();
|
|
37
|
+
for (let ti = 0; ti < tabs.length; ti++) {
|
|
38
|
+
const t = tabs[ti];
|
|
39
|
+
if (safe(function () { return t.tty(); }) === targetTty) {
|
|
40
|
+
return safe(function () { return t.busy(); }) ? "busy" : "idle";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return "missing";
|
|
45
|
+
})(${JSON.stringify(tty)});
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
28
48
|
function truncate(s, max) {
|
|
29
49
|
return s.length > max ? `${s.slice(0, max)}… [${s.length - max} chars elided]` : s;
|
|
30
50
|
}
|
|
31
|
-
export async function executeHandler({ tty, command }) {
|
|
51
|
+
export async function executeHandler({ tty, command, force = false, dry_run = false, }) {
|
|
32
52
|
if (!isWriteToolsEnabled()) {
|
|
33
53
|
return {
|
|
34
54
|
content: [{ type: "text", text: writeToolsDisabledMessage("terminal_execute") }],
|
|
@@ -37,6 +57,27 @@ export async function executeHandler({ tty, command }) {
|
|
|
37
57
|
}
|
|
38
58
|
const config = await loadSafetyConfig();
|
|
39
59
|
const verdict = evaluateCommand(command, config);
|
|
60
|
+
// dry_run short-circuits BEFORE any side effects (busy probe via JXA, audit
|
|
61
|
+
// log write, enqueue, dialog). Just compute and return what would happen.
|
|
62
|
+
if (dry_run) {
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: JSON.stringify({
|
|
68
|
+
dry_run: true,
|
|
69
|
+
verdict: verdict.level,
|
|
70
|
+
matchedPattern: verdict.matchedPattern ?? null,
|
|
71
|
+
matchedDescription: verdict.matchedDescription ?? null,
|
|
72
|
+
wouldRefuse: verdict.level === "forbidden",
|
|
73
|
+
wouldPromptUser: verdict.level === "requires_approval",
|
|
74
|
+
wouldAutoRun: verdict.level === "safe",
|
|
75
|
+
note: "force/busy/permission checks are NOT evaluated in dry_run — only the safety policy verdict.",
|
|
76
|
+
}, null, 2),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
40
81
|
if (verdict.level === "forbidden") {
|
|
41
82
|
const descPart = verdict.matchedDescription ? ` (${verdict.matchedDescription})` : "";
|
|
42
83
|
await appendAudit({
|
|
@@ -58,12 +99,60 @@ export async function executeHandler({ tty, command }) {
|
|
|
58
99
|
isError: true,
|
|
59
100
|
};
|
|
60
101
|
}
|
|
102
|
+
// Busy-tab check (#33). do script types into the foreground process's stdin
|
|
103
|
+
// when the tab is busy, which is rarely what callers want — fail fast unless
|
|
104
|
+
// force=true is set explicitly.
|
|
105
|
+
if (!force) {
|
|
106
|
+
let busyStatus;
|
|
107
|
+
try {
|
|
108
|
+
busyStatus = (await runJxa(buildBusyCheckScript(tty))).trim();
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
112
|
+
return {
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: `terminal_execute failed to probe target tab busy state: ${message}`,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
isError: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (busyStatus === "missing") {
|
|
123
|
+
return {
|
|
124
|
+
content: [{ type: "text", text: `No Terminal.app tab found with tty ${tty}.` }],
|
|
125
|
+
isError: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (busyStatus === "busy") {
|
|
129
|
+
await appendAudit({
|
|
130
|
+
tool: "terminal_execute",
|
|
131
|
+
outcome: "refused",
|
|
132
|
+
tty,
|
|
133
|
+
command,
|
|
134
|
+
level: verdict.level,
|
|
135
|
+
matchedPattern: verdict.matchedPattern,
|
|
136
|
+
details: { reason: "target tab is busy" },
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
type: "text",
|
|
142
|
+
text: `Refused: target tab ${tty} is busy with a running command. The command would be typed into the foreground process's stdin rather than executed. ` +
|
|
143
|
+
`Pass force=true if you genuinely want to send stdin, or wait for the running command to finish.`,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
isError: true,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
61
150
|
let resolutionSource = "auto";
|
|
62
151
|
if (verdict.level === "requires_approval") {
|
|
63
152
|
const descPart = verdict.matchedDescription
|
|
64
|
-
? `\nMatched: ${verdict.matchedPattern} (${verdict.matchedDescription})`
|
|
153
|
+
? `\nMatched: ${sanitizeAiText(verdict.matchedPattern ?? "")} (${sanitizeAiText(verdict.matchedDescription)})`
|
|
65
154
|
: verdict.matchedPattern
|
|
66
|
-
? `\nMatched pattern: ${verdict.matchedPattern}`
|
|
155
|
+
? `\nMatched pattern: ${sanitizeAiText(verdict.matchedPattern)}`
|
|
67
156
|
: "\nNo matching pattern — default policy requires approval.";
|
|
68
157
|
const { id, promise } = enqueue({
|
|
69
158
|
tty,
|
|
@@ -71,11 +160,23 @@ export async function executeHandler({ tty, command }) {
|
|
|
71
160
|
matchedPattern: verdict.matchedPattern,
|
|
72
161
|
matchedDescription: verdict.matchedDescription,
|
|
73
162
|
});
|
|
74
|
-
|
|
163
|
+
// If the queue resolves out-of-band (via pending_approve / pending_deny),
|
|
164
|
+
// abort the dialog so it disappears from the user's screen instead of
|
|
165
|
+
// dangling (reviewer #4).
|
|
166
|
+
const dialogAbort = new AbortController();
|
|
167
|
+
const dialogPromise = confirmWithUser({
|
|
75
168
|
title: "macos-terminal-mcp · terminal_execute",
|
|
76
|
-
message: `Run in ${tty}:\n\n${truncate(command, 800)}${descPart}\n\nQueue id: ${id}`,
|
|
77
|
-
|
|
169
|
+
message: `Run in ${tty}:\n\n${sanitizeAiText(truncate(command, 800))}${descPart}\n\nQueue id: ${id}`,
|
|
170
|
+
signal: dialogAbort.signal,
|
|
171
|
+
});
|
|
172
|
+
void dialogPromise.then((allowed) => resolvePending(id, allowed, "dialog"), () => undefined);
|
|
78
173
|
const result = await promise;
|
|
174
|
+
if (result.source !== "dialog") {
|
|
175
|
+
// Queue resolved first — dismiss the still-open dialog.
|
|
176
|
+
dialogAbort.abort();
|
|
177
|
+
// Let the dialog promise settle (rejected with aborted) before continuing.
|
|
178
|
+
await dialogPromise.catch(() => undefined);
|
|
179
|
+
}
|
|
79
180
|
if (!result.approved) {
|
|
80
181
|
const reasonPart = result.reason ? ` (${result.reason})` : "";
|
|
81
182
|
await appendAudit({
|
|
@@ -105,6 +206,7 @@ export async function executeHandler({ tty, command }) {
|
|
|
105
206
|
const path = verdict.level === "safe"
|
|
106
207
|
? `auto-run (safe pattern: ${verdict.matchedPattern})`
|
|
107
208
|
: `approved via ${resolutionSource}`;
|
|
209
|
+
const forcedSuffix = force ? " [force=true]" : "";
|
|
108
210
|
await appendAudit({
|
|
109
211
|
tool: "terminal_execute",
|
|
110
212
|
outcome: "success",
|
|
@@ -113,12 +215,13 @@ export async function executeHandler({ tty, command }) {
|
|
|
113
215
|
level: verdict.level,
|
|
114
216
|
matchedPattern: verdict.matchedPattern,
|
|
115
217
|
source: verdict.level === "safe" ? "auto" : resolutionSource,
|
|
218
|
+
...(force ? { details: { force: true } } : {}),
|
|
116
219
|
});
|
|
117
220
|
return {
|
|
118
221
|
content: [
|
|
119
222
|
{
|
|
120
223
|
type: "text",
|
|
121
|
-
text: `Executed in ${tty} [${path}]: ${truncate(command, 200)}`,
|
|
224
|
+
text: `Executed in ${tty} [${path}${forcedSuffix}]: ${truncate(command, 200)}`,
|
|
122
225
|
},
|
|
123
226
|
],
|
|
124
227
|
};
|
|
@@ -145,13 +248,21 @@ export async function executeHandler({ tty, command }) {
|
|
|
145
248
|
}
|
|
146
249
|
export function register(server) {
|
|
147
250
|
server.registerTool("terminal_execute", {
|
|
148
|
-
description: 'Run a shell command in a specific Terminal.app tab identified by its tty (e.g. "/dev/ttys003"). Behaves as if the command was typed by the user and Enter was pressed — output appears in the tab. Three-tier safety: "safe" patterns auto-run, "requires_approval" patterns trigger a native confirmation dialog, "forbidden" patterns are refused outright. Requires WRITE_TOOLS_ENABLED=1.',
|
|
251
|
+
description: 'Run a shell command in a specific Terminal.app tab identified by its tty (e.g. "/dev/ttys003"). Behaves as if the command was typed by the user and Enter was pressed — output appears in the tab. Three-tier safety: "safe" patterns auto-run, "requires_approval" patterns trigger a native confirmation dialog (with parallel async approval via pending_*), "forbidden" patterns are refused outright. Before any of that, the target tab is checked for busy state (a running foreground command) — busy tabs reject by default unless force=true is set. dry_run=true short-circuits before any side effects and returns just the verdict. Requires WRITE_TOOLS_ENABLED=1.',
|
|
149
252
|
inputSchema: {
|
|
150
253
|
tty: z
|
|
151
254
|
.string()
|
|
152
255
|
.regex(/^\/dev\/ttys[0-9]+$/)
|
|
153
256
|
.describe('Target tab tty, e.g. "/dev/ttys003"'),
|
|
154
257
|
command: z.string().min(1).max(8192).describe("Shell command to run in the target tab"),
|
|
258
|
+
force: z
|
|
259
|
+
.boolean()
|
|
260
|
+
.optional()
|
|
261
|
+
.describe("If true, bypass the busy-tab check. The command will be typed into the foreground process's stdin rather than executed. Default: false."),
|
|
262
|
+
dry_run: z
|
|
263
|
+
.boolean()
|
|
264
|
+
.optional()
|
|
265
|
+
.describe("If true, return the safety verdict and what WOULD happen without enqueueing, showing a dialog, or running anything. Useful for harnesses (or curious models) probing a call before allowing the real version."),
|
|
155
266
|
},
|
|
156
267
|
}, executeHandler);
|
|
157
268
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,OAAe;IAC7D,OAAO;;;;;;;;;;;;;;;;;KAiBJ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;CACnD,CAAC;AACF,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,OAAe;IAC7D,OAAO;;;;;;;;;;;;;;;;;KAiBJ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;CACnD,CAAC;AACF,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAW;IACvC,OAAO;;;;;;;;;;;;;;;;KAgBJ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;CACvB,CAAC;AACF,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAWD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,GAAG,EACH,OAAO,EACP,KAAK,GAAG,KAAK,EACb,OAAO,GAAG,KAAK,GACF;IACb,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAChF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAEjD,4EAA4E;IAC5E,0EAA0E;IAC1E,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;wBACE,OAAO,EAAE,IAAI;wBACb,OAAO,EAAE,OAAO,CAAC,KAAK;wBACtB,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI;wBAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,IAAI,IAAI;wBACtD,WAAW,EAAE,OAAO,CAAC,KAAK,KAAK,WAAW;wBAC1C,eAAe,EAAE,OAAO,CAAC,KAAK,KAAK,mBAAmB;wBACtD,YAAY,EAAE,OAAO,CAAC,KAAK,KAAK,MAAM;wBACtC,IAAI,EAAE,6FAA6F;qBACpG,EACD,IAAI,EACJ,CAAC,CACF;iBACF;aACF;SACF,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,MAAM,WAAW,CAAC;YAChB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,SAAS;YAClB,GAAG;YACH,OAAO;YACP,KAAK,EAAE,WAAW;YAClB,cAAc,EAAE,OAAO,CAAC,cAAc;SACvC,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EACF,8CAA8C,OAAO,CAAC,cAAc,GAAG,QAAQ,IAAI;wBACnF,2GAA2G;iBAC9G;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,6EAA6E;IAC7E,gCAAgC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,UAAkB,CAAC;QACvB,IAAI,CAAC;YACH,UAAU,GAAG,CAAC,MAAM,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,2DAA2D,OAAO,EAAE;qBAC3E;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sCAAsC,GAAG,GAAG,EAAE,CAAC;gBAC/E,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,WAAW,CAAC;gBAChB,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,SAAS;gBAClB,GAAG;gBACH,OAAO;gBACP,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,OAAO,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE;aAC1C,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EACF,uBAAuB,GAAG,wHAAwH;4BAClJ,iGAAiG;qBACpG;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,gBAAgB,GAA4C,MAAM,CAAC;IACvE,IAAI,OAAO,CAAC,KAAK,KAAK,mBAAmB,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,kBAAkB;YACzC,CAAC,CAAC,cAAc,cAAc,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC,KAAK,cAAc,CAAC,OAAO,CAAC,kBAAkB,CAAC,GAAG;YAC9G,CAAC,CAAC,OAAO,CAAC,cAAc;gBACtB,CAAC,CAAC,sBAAsB,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE;gBAChE,CAAC,CAAC,2DAA2D,CAAC;QAElE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;YAC9B,GAAG;YACH,OAAO;YACP,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;SAC/C,CAAC,CAAC;QAEH,0EAA0E;QAC1E,sEAAsE;QACtE,0BAA0B;QAC1B,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;QAC1C,MAAM,aAAa,GAAG,eAAe,CAAC;YACpC,KAAK,EAAE,uCAAuC;YAC9C,OAAO,EAAE,UAAU,GAAG,QAAQ,cAAc,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,GAAG,QAAQ,iBAAiB,EAAE,EAAE;YACpG,MAAM,EAAE,WAAW,CAAC,MAAM;SAC3B,CAAC,CAAC;QACH,KAAK,aAAa,CAAC,IAAI,CACrB,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,EAClD,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,wDAAwD;YACxD,WAAW,CAAC,KAAK,EAAE,CAAC;YACpB,2EAA2E;YAC3E,MAAM,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC7C,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,MAAM,WAAW,CAAC;gBAChB,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,QAAQ;gBACjB,GAAG;gBACH,OAAO;gBACP,KAAK,EAAE,mBAAmB;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,sBAAsB,MAAM,CAAC,MAAM,GAAG,UAAU,GAAG;qBAC1D;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;QAC/C,MAAM,IAAI,GACR,OAAO,CAAC,KAAK,KAAK,MAAM;YACtB,CAAC,CAAC,2BAA2B,OAAO,CAAC,cAAc,GAAG;YACtD,CAAC,CAAC,gBAAgB,gBAAgB,EAAE,CAAC;QACzC,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,MAAM,WAAW,CAAC;YAChB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,SAAS;YAClB,GAAG;YACH,OAAO;YACP,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,MAAM,EAAE,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,gBAAgB;YAC5D,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/C,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,eAAe,GAAG,KAAK,IAAI,GAAG,YAAY,MAAM,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;iBAC/E;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,GACR,GAAG,YAAY,cAAc,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YACjE,CAAC,CAAC,sFAAsF;YACxF,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,WAAW,CAAC;YAChB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,OAAO;YAChB,GAAG;YACH,OAAO;YACP,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,OAAO,GAAG,IAAI,EAAE,EAAE,CAAC;YAC/E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EACT,kpBAAkpB;QACppB,WAAW,EAAE;YACX,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,KAAK,CAAC,qBAAqB,CAAC;iBAC5B,QAAQ,CAAC,qCAAqC,CAAC;YAClD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,wCAAwC,CAAC;YACvF,KAAK,EAAE,CAAC;iBACL,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,yIAAyI,CAC1I;YACH,OAAO,EAAE,CAAC;iBACP,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,+MAA+M,CAChN;SACJ;KACF,EACD,cAAc,CACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { OsascriptError, runJxa } from "../applescript.js";
|
|
2
|
+
import { appendAudit } from "../safety/audit.js";
|
|
3
|
+
import { isWriteToolsEnabled, writeToolsDisabledMessage } from "../safety/confirm.js";
|
|
4
|
+
export const NEW_TAB_SCRIPT = `
|
|
5
|
+
function safe(fn) { try { return fn(); } catch (e) { return null; } }
|
|
6
|
+
(function newTab() {
|
|
7
|
+
const terminal = Application("Terminal");
|
|
8
|
+
terminal.activate();
|
|
9
|
+
const wins = terminal.windows();
|
|
10
|
+
// do script with empty command + no "in" → new window. With "in: front
|
|
11
|
+
// window" → new tab in that window. Prefer a new tab in the front window
|
|
12
|
+
// when one exists, otherwise let it create a new window.
|
|
13
|
+
let newTab;
|
|
14
|
+
if (wins.length > 0) {
|
|
15
|
+
newTab = terminal.doScript("", { in: wins[0] });
|
|
16
|
+
} else {
|
|
17
|
+
newTab = terminal.doScript("");
|
|
18
|
+
}
|
|
19
|
+
const tty = safe(function () { return newTab.tty(); }) || "";
|
|
20
|
+
let windowId = null;
|
|
21
|
+
try {
|
|
22
|
+
const after = terminal.windows();
|
|
23
|
+
for (let i = 0; i < after.length; i++) {
|
|
24
|
+
const ts = after[i].tabs();
|
|
25
|
+
for (let j = 0; j < ts.length; j++) {
|
|
26
|
+
if (safe(function () { return ts[j].tty(); }) === tty) {
|
|
27
|
+
windowId = safe(function () { return after[i].id(); });
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (windowId !== null) break;
|
|
32
|
+
}
|
|
33
|
+
} catch (e) { /* best-effort */ }
|
|
34
|
+
return JSON.stringify({ tty, windowId });
|
|
35
|
+
})();
|
|
36
|
+
`;
|
|
37
|
+
export async function newTabHandler() {
|
|
38
|
+
if (!isWriteToolsEnabled()) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: writeToolsDisabledMessage("terminal_new_tab") }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const json = await runJxa(NEW_TAB_SCRIPT);
|
|
46
|
+
await appendAudit({ tool: "terminal_new_tab", outcome: "success" });
|
|
47
|
+
return { content: [{ type: "text", text: json }] };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
51
|
+
const hint = err instanceof OsascriptError && /not authorized/i.test(err.stderr)
|
|
52
|
+
? "\nAutomation permission missing — System Settings → Privacy & Security → Automation."
|
|
53
|
+
: "";
|
|
54
|
+
await appendAudit({
|
|
55
|
+
tool: "terminal_new_tab",
|
|
56
|
+
outcome: "error",
|
|
57
|
+
errorMessage: message,
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
content: [{ type: "text", text: `terminal_new_tab failed: ${message}${hint}` }],
|
|
61
|
+
isError: true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function register(server) {
|
|
66
|
+
server.registerTool("terminal_new_tab", {
|
|
67
|
+
description: "Open a new empty tab in Terminal.app (in the frontmost window, or a new window if none are open). Returns {tty, windowId} for the new tab. Use the returned tty in subsequent terminal_read / terminal_execute calls. NOTE: this tool does NOT execute any command in the new tab — to run a command, call terminal_execute against the returned tty. Requires WRITE_TOOLS_ENABLED=1 but does NOT pop a confirmation dialog (low blast radius — the user can close an unwanted tab).",
|
|
68
|
+
}, newTabHandler);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=new_tab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new_tab.js","sourceRoot":"","sources":["../../src/tools/new_tab.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC7B,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAChF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QAC1C,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACpE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,GACR,GAAG,YAAY,cAAc,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YACjE,CAAC,CAAC,sFAAsF;YACxF,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,WAAW,CAAC;YAChB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,OAAO;YAChB,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,OAAO,GAAG,IAAI,EAAE,EAAE,CAAC;YAC/E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EACT,sdAAsd;KACzd,EACD,aAAa,CACd,CAAC;AACJ,CAAC"}
|
package/dist/tools/pending.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { appendAudit } from "../safety/audit.js";
|
|
3
|
-
import { confirmWithUser, isWriteToolsEnabled, writeToolsDisabledMessage, } from "../safety/confirm.js";
|
|
3
|
+
import { confirmWithUser, isWriteToolsEnabled, sanitizeAiText, writeToolsDisabledMessage, } from "../safety/confirm.js";
|
|
4
4
|
import { getPending, listPending, resolvePending } from "../safety/queue.js";
|
|
5
5
|
function asTextResult(text, isError = false) {
|
|
6
6
|
return { content: [{ type: "text", text }], ...(isError ? { isError } : {}) };
|
|
@@ -35,15 +35,15 @@ function registerApprove(server) {
|
|
|
35
35
|
return asTextResult(`No pending entry with id ${id} (expired, already resolved, or never existed).`, true);
|
|
36
36
|
}
|
|
37
37
|
const descPart = entry.matchedDescription
|
|
38
|
-
? `\nMatched: ${entry.matchedPattern} (${entry.matchedDescription})`
|
|
38
|
+
? `\nMatched: ${sanitizeAiText(entry.matchedPattern ?? "")} (${sanitizeAiText(entry.matchedDescription)})`
|
|
39
39
|
: entry.matchedPattern
|
|
40
|
-
? `\nMatched pattern: ${entry.matchedPattern}`
|
|
40
|
+
? `\nMatched pattern: ${sanitizeAiText(entry.matchedPattern)}`
|
|
41
41
|
: "";
|
|
42
42
|
const allowed = await confirmWithUser({
|
|
43
43
|
title: "macos-terminal-mcp · pending_approve",
|
|
44
44
|
message: `Approve queued command?\n\n` +
|
|
45
45
|
`Target: ${entry.tty}\n` +
|
|
46
|
-
`Command: ${truncate(entry.command, 800)}${descPart}\n` +
|
|
46
|
+
`Command: ${sanitizeAiText(truncate(entry.command, 800))}${descPart}\n` +
|
|
47
47
|
`Queue id: ${id}`,
|
|
48
48
|
});
|
|
49
49
|
if (!allowed) {
|
|
@@ -91,12 +91,12 @@ function registerDeny(server) {
|
|
|
91
91
|
if (!entry) {
|
|
92
92
|
return asTextResult(`No pending entry with id ${id} (expired, already resolved, or never existed).`, true);
|
|
93
93
|
}
|
|
94
|
-
const reasonPart = reason ? `\n\nReason: ${reason}` : "";
|
|
94
|
+
const reasonPart = reason ? `\n\nReason: ${sanitizeAiText(reason)}` : "";
|
|
95
95
|
const allowed = await confirmWithUser({
|
|
96
96
|
title: "macos-terminal-mcp · pending_deny",
|
|
97
97
|
message: `Deny queued command?\n\n` +
|
|
98
98
|
`Target: ${entry.tty}\n` +
|
|
99
|
-
`Command: ${truncate(entry.command, 800)}\n` +
|
|
99
|
+
`Command: ${sanitizeAiText(truncate(entry.command, 800))}\n` +
|
|
100
100
|
`Queue id: ${id}${reasonPart}`,
|
|
101
101
|
});
|
|
102
102
|
if (!allowed) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending.js","sourceRoot":"","sources":["../../src/tools/pending.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7E,SAAS,YAAY,CAAC,IAAY,EAAE,OAAO,GAAG,KAAK;IACjD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAChF,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,YAAY,CAAC,MAAiB;IACrC,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,WAAW,EACT,kPAAkP;QACpP,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;KAC1D,EACD,KAAK,IAA6B,EAAE;QAClC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC,CACF,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,MAAiB;IACxC,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EACT,iRAAiR;QACnR,WAAW,EAAE;YACX,EAAE,EAAE,CAAC;iBACF,MAAM,EAAE;iBACR,IAAI,EAAE;iBACN,QAAQ,CAAC,yEAAyE,CAAC;SACvF;KACF,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAA2B,EAAE;QACxC,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC;YAC3B,OAAO,YAAY,CAAC,yBAAyB,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1E,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,YAAY,CACjB,4BAA4B,EAAE,iDAAiD,EAC/E,IAAI,CACL,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,kBAAkB;YACvC,CAAC,CAAC,cAAc,KAAK,CAAC,cAAc,KAAK,KAAK,CAAC,kBAAkB,GAAG;
|
|
1
|
+
{"version":3,"file":"pending.js","sourceRoot":"","sources":["../../src/tools/pending.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/register.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as clear from "./clear.js";
|
|
2
2
|
import * as execute from "./execute.js";
|
|
3
3
|
import * as list from "./list.js";
|
|
4
|
+
import * as newTab from "./new_tab.js";
|
|
4
5
|
import * as pending from "./pending.js";
|
|
5
6
|
import * as read from "./read.js";
|
|
6
7
|
import * as safety from "./safety.js";
|
|
@@ -9,6 +10,7 @@ export function registerAll(server) {
|
|
|
9
10
|
read.register(server);
|
|
10
11
|
execute.register(server);
|
|
11
12
|
clear.register(server);
|
|
13
|
+
newTab.register(server);
|
|
12
14
|
safety.register(server);
|
|
13
15
|
pending.register(server);
|
|
14
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,WAAW,CAAC,MAAiB;IAC3C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC"}
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,WAAW,CAAC,MAAiB;IAC3C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC"}
|
package/dist/tools/safety.js
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { appendAudit } from "../safety/audit.js";
|
|
3
|
-
import { confirmWithUser, isWriteToolsEnabled, writeToolsDisabledMessage, } from "../safety/confirm.js";
|
|
4
|
-
import { loadSafetyConfig, saveSafetyConfig, } from "../safety/patterns.js";
|
|
3
|
+
import { confirmWithUser, isWriteToolsEnabled, sanitizeAiText, writeToolsDisabledMessage, } from "../safety/confirm.js";
|
|
4
|
+
import { loadSafetyConfig, regexErrorReason, saveSafetyConfig, } from "../safety/patterns.js";
|
|
5
5
|
const LEVEL_VALUES = ["safe", "requires_approval", "forbidden"];
|
|
6
|
-
function isValidRegex(pattern) {
|
|
7
|
-
try {
|
|
8
|
-
new RegExp(pattern);
|
|
9
|
-
return true;
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
6
|
function asTextResult(text, isError = false) {
|
|
16
7
|
return { content: [{ type: "text", text }], ...(isError ? { isError } : {}) };
|
|
17
8
|
}
|
|
@@ -54,19 +45,20 @@ function registerAdd(server) {
|
|
|
54
45
|
if (!isWriteToolsEnabled()) {
|
|
55
46
|
return asTextResult(writeToolsDisabledMessage("safety_add"), true);
|
|
56
47
|
}
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
const reason = regexErrorReason(pattern);
|
|
49
|
+
if (reason !== null) {
|
|
50
|
+
return asTextResult(`Refused: pattern ${JSON.stringify(pattern)} ${reason}.`, true);
|
|
59
51
|
}
|
|
60
52
|
const config = await loadSafetyConfig();
|
|
61
53
|
const existing = config.patterns.find((p) => p.pattern === pattern);
|
|
62
54
|
if (existing) {
|
|
63
55
|
return asTextResult(`Pattern already exists with level "${existing.level}". Use safety_set_level to change it.`, true);
|
|
64
56
|
}
|
|
65
|
-
const descLine = description ? `\nDescription: ${description}` : "";
|
|
57
|
+
const descLine = description ? `\nDescription: ${sanitizeAiText(description)}` : "";
|
|
66
58
|
const allowed = await confirmWithUser({
|
|
67
59
|
title: "macos-terminal-mcp · safety_add",
|
|
68
60
|
message: `Add safety pattern?\n\n` +
|
|
69
|
-
`Pattern: ${pattern}\n` +
|
|
61
|
+
`Pattern: ${sanitizeAiText(pattern)}\n` +
|
|
70
62
|
`Level: ${describeLevel(level)}` +
|
|
71
63
|
descLine,
|
|
72
64
|
});
|
|
@@ -80,8 +72,15 @@ function registerAdd(server) {
|
|
|
80
72
|
});
|
|
81
73
|
return asTextResult("User denied the safety policy change.", true);
|
|
82
74
|
}
|
|
75
|
+
// Re-read after dialog approval — the 5-minute dialog window is long
|
|
76
|
+
// enough for another concurrent mutator to have changed the file. Apply
|
|
77
|
+
// our change to the freshest config we can read, not the pre-dialog snapshot.
|
|
78
|
+
const fresh = await loadSafetyConfig();
|
|
79
|
+
if (fresh.patterns.some((p) => p.pattern === pattern)) {
|
|
80
|
+
return asTextResult(`Pattern was added by another action while this dialog was open. Re-run safety_add if you still want to apply this change.`, true);
|
|
81
|
+
}
|
|
83
82
|
const newEntry = { pattern, level, ...(description ? { description } : {}) };
|
|
84
|
-
const updated = { patterns: [...
|
|
83
|
+
const updated = { patterns: [...fresh.patterns, newEntry] };
|
|
85
84
|
await saveSafetyConfig(updated);
|
|
86
85
|
await appendAudit({
|
|
87
86
|
tool: "safety_add",
|
|
@@ -114,11 +113,13 @@ function registerRemove(server) {
|
|
|
114
113
|
const warning = existing.level === "forbidden"
|
|
115
114
|
? "⚠ WARNING: this pattern is currently FORBIDDEN. Removing it weakens safety.\n\n"
|
|
116
115
|
: "";
|
|
117
|
-
const descLine = existing.description
|
|
116
|
+
const descLine = existing.description
|
|
117
|
+
? `\nDescription: ${sanitizeAiText(existing.description)}`
|
|
118
|
+
: "";
|
|
118
119
|
const allowed = await confirmWithUser({
|
|
119
120
|
title: "macos-terminal-mcp · safety_remove",
|
|
120
121
|
message: `${warning}Remove safety pattern?\n\n` +
|
|
121
|
-
`Pattern: ${pattern}\n` +
|
|
122
|
+
`Pattern: ${sanitizeAiText(pattern)}\n` +
|
|
122
123
|
`Current level: ${describeLevel(existing.level)}` +
|
|
123
124
|
descLine,
|
|
124
125
|
});
|
|
@@ -132,8 +133,13 @@ function registerRemove(server) {
|
|
|
132
133
|
});
|
|
133
134
|
return asTextResult("User denied the safety policy change.", true);
|
|
134
135
|
}
|
|
136
|
+
// Re-read after dialog approval (see safety_add for rationale).
|
|
137
|
+
const fresh = await loadSafetyConfig();
|
|
138
|
+
if (!fresh.patterns.some((p) => p.pattern === pattern)) {
|
|
139
|
+
return asTextResult(`Pattern was already removed by another action while this dialog was open.`, true);
|
|
140
|
+
}
|
|
135
141
|
const updated = {
|
|
136
|
-
patterns:
|
|
142
|
+
patterns: fresh.patterns.filter((p) => p.pattern !== pattern),
|
|
137
143
|
};
|
|
138
144
|
await saveSafetyConfig(updated);
|
|
139
145
|
await appendAudit({
|
|
@@ -174,7 +180,7 @@ function registerSetLevel(server) {
|
|
|
174
180
|
const allowed = await confirmWithUser({
|
|
175
181
|
title: "macos-terminal-mcp · safety_set_level",
|
|
176
182
|
message: `${warning}Change pattern level?\n\n` +
|
|
177
|
-
`Pattern: ${pattern}\n` +
|
|
183
|
+
`Pattern: ${sanitizeAiText(pattern)}\n` +
|
|
178
184
|
`From: ${describeLevel(existing.level)}\n` +
|
|
179
185
|
`To: ${describeLevel(level)}`,
|
|
180
186
|
});
|
|
@@ -189,8 +195,17 @@ function registerSetLevel(server) {
|
|
|
189
195
|
});
|
|
190
196
|
return asTextResult("User denied the safety policy change.", true);
|
|
191
197
|
}
|
|
198
|
+
// Re-read after dialog approval (see safety_add for rationale).
|
|
199
|
+
const fresh = await loadSafetyConfig();
|
|
200
|
+
const freshExisting = fresh.patterns.find((p) => p.pattern === pattern);
|
|
201
|
+
if (!freshExisting) {
|
|
202
|
+
return asTextResult(`Pattern was removed by another action while this dialog was open.`, true);
|
|
203
|
+
}
|
|
204
|
+
if (freshExisting.level === level) {
|
|
205
|
+
return asTextResult(`Pattern is already at level ${level} (changed by another action while this dialog was open).`);
|
|
206
|
+
}
|
|
192
207
|
const updated = {
|
|
193
|
-
patterns:
|
|
208
|
+
patterns: fresh.patterns.map((p) => (p.pattern === pattern ? { ...p, level } : p)),
|
|
194
209
|
};
|
|
195
210
|
await saveSafetyConfig(updated);
|
|
196
211
|
await appendAudit({
|
package/dist/tools/safety.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,gBAAgB,
|
|
1
|
+
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@priyanshumit/macos-terminal-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "MCP server exposing macOS Terminal.app to AI agents via AppleScript / JXA. List windows, read scrollback, execute commands, clear buffers — with three-tier safety patterns and confirmation dialogs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
28
|
+
"@types/safe-regex": "^1.1.6",
|
|
29
|
+
"safe-regex": "^2.1.1",
|
|
28
30
|
"zod": "^3.23.0"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|