granttap-mcp 0.8.33 → 0.8.35
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 +21 -16
- package/apps/mcp/src/connection-center/README.md +6 -5
- package/apps/mcp/src/connection-center/bridge.js +1 -0
- package/apps/mcp/src/connection-center/passkey.ts +27 -0
- package/apps/mcp/src/connection-center/state.ts +3 -0
- package/apps/mcp/src/connection-center/view.js +11 -0
- package/apps/mcp/src/connection-center/widget.html +3 -4
- package/apps/mcp/src/mcp-tools/connect/index.ts +19 -0
- package/apps/mcp/src/mcp-tools/connect/widget.ts +3 -3
- package/cursor-plugin/README.md +1 -1
- package/cursor-plugin/mcp.json +1 -1
- package/cursor-plugin/stdio-bootstrap.cjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
[](https://github.com/sergii-ziborov/granttap-mcp/actions/workflows/ci.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> **See what your coding agents are doing. Step in when they need you.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
GrantTap connects local Claude Code and Codex work to a live control center on
|
|
10
|
+
Mac, iPhone, iPad, and Apple Watch. See the Task and its latest activity, answer
|
|
11
|
+
an approval or question with context, and continue the same goal as sessions or
|
|
12
|
+
computers change. Cursor and Grok Build join the view where their local
|
|
13
|
+
integrations support it.
|
|
10
14
|
|
|
11
|
-
This repository
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
credentials stay on your computer. Native controller traffic is end-to-end
|
|
16
|
-
encrypted.
|
|
15
|
+
This repository supplies the local runtime: CLI, MCP server, provider hooks,
|
|
16
|
+
adapters, and wire contracts. It powers Project Mesh, Governance, and observed
|
|
17
|
+
usage while agents and provider credentials stay on your computer. Controller
|
|
18
|
+
traffic is end-to-end encrypted.
|
|
17
19
|
|
|
18
20
|
The MCP runtime, plugins, and CLI are open source under the MIT License.
|
|
19
21
|
The Apple apps, Engine, relay, and website are separately licensed commercial
|
|
@@ -36,11 +38,13 @@ products or source. Their licenses do not change this repository's MIT grant.
|
|
|
36
38
|
<img src="docs/images/apple-watch-approval.png" width="180" alt="GrantTap approval on Apple Watch">
|
|
37
39
|
</p>
|
|
38
40
|
|
|
41
|
+
<p align="center"><em>GrantTap iPhone and Apple Watch screens with deterministic sample work; no private user tasks are shown.</em></p>
|
|
42
|
+
|
|
39
43
|
## Supported providers
|
|
40
44
|
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
45
|
+
- Claude Code and Codex: primary local control and continuation paths.
|
|
46
|
+
- Cursor: Task visibility and supported local controls, with narrower coverage.
|
|
47
|
+
- Grok Build: observable work where the installed runtime exposes it.
|
|
44
48
|
|
|
45
49
|
GrantTap reports the depth each provider actually exposes. Visibility does not
|
|
46
50
|
imply deterministic remote blocking or full mobile continuation.
|
|
@@ -147,7 +151,7 @@ computers. GrantTap builds a bounded Task Capsule from explicit task/git facts,
|
|
|
147
151
|
requires local phone authorization, creates a separate target branch/worktree,
|
|
148
152
|
starts the target execution, and returns a receipt bound to the exact capsule.
|
|
149
153
|
Cursor uses the same provider-neutral schema and trusted caller attribution.
|
|
150
|
-
Grok Build remains
|
|
154
|
+
Grok Build remains observable where available, but does not
|
|
151
155
|
yet expose a trusted caller hook, so agent-authored scoped Mesh events are not
|
|
152
156
|
offered for it. Unsupported remote-start paths fail closed instead of claiming
|
|
153
157
|
parity.
|
|
@@ -381,19 +385,20 @@ trust.
|
|
|
381
385
|
|
|
382
386
|
## MCP contract
|
|
383
387
|
|
|
384
|
-
`tools/list` returns exactly
|
|
388
|
+
`tools/list` returns exactly six public tools:
|
|
385
389
|
|
|
386
390
|
| Tool | Contract |
|
|
387
391
|
| --- | --- |
|
|
392
|
+
| `connection_status` | Open the connection card and inspect saved pairing, QR expiry, relay observations, and provider readiness without changing pairing |
|
|
388
393
|
| `connect` | Reuse the existing production pairing or return a one-time QR |
|
|
389
|
-
| `reconnect` |
|
|
394
|
+
| `reconnect` | After explicit confirmation, show a one-time QR to reconnect or add an independent controller device while retaining the current room |
|
|
390
395
|
| `notify` | Send a non-blocking status update of at most 2,000 characters |
|
|
391
396
|
| `ask_yes_no` | Ask a yes/no question and wait for the explicit answer |
|
|
392
397
|
| `ask` | Ask an open question and wait for typed or spoken text |
|
|
393
398
|
|
|
394
399
|
MCP `connect` accepts no custom routing, replacement, or key-rotation input.
|
|
395
|
-
`reconnect` is declared destructive and requires `confirmed: true`;
|
|
396
|
-
|
|
400
|
+
`reconnect` is declared destructive and requires `confirmed: true`; the
|
|
401
|
+
existing pairing remains usable during a new enrollment attempt.
|
|
397
402
|
Setup is CLI-only because it changes provider configuration and must not be
|
|
398
403
|
available to a model through prompt injection.
|
|
399
404
|
|
|
@@ -23,11 +23,12 @@ checks parent messages, refreshes pending pairing while visible, and keeps
|
|
|
23
23
|
copyable transfer material out of model-visible text and persistent UI storage.
|
|
24
24
|
Non-UI clients can still use the tools and display the returned QR image.
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
Connect with passkey on the card creates a short-lived, authenticated website
|
|
27
|
+
request. A fresh passkey assertion on granttap.com links this Mac to the user's
|
|
28
|
+
account; the site returns a machine token only to the local MCP watcher, which
|
|
29
|
+
saves it privately. The QR pairing room and provider runtime remain unchanged.
|
|
30
|
+
The separate coding-app OAuth page can also use passkey to authorize that MCP
|
|
31
|
+
client. The Codex-owned plugin management page is not this card.
|
|
31
32
|
|
|
32
33
|
Tests in `tests/` exercise isolated keys, a loopback encrypted phone connection,
|
|
33
34
|
expiry, non-mutating status, and actual DOM button/host-message behavior.
|
|
@@ -72,6 +72,7 @@ async function call(name, args = {}) {
|
|
|
72
72
|
: await request("tools/call", { name, arguments: args });
|
|
73
73
|
if (result?.isError) message("Could not complete the request. Refresh status and check the relay before retrying.");
|
|
74
74
|
else { render(result); message("Status updated."); }
|
|
75
|
+
return result;
|
|
75
76
|
} catch {
|
|
76
77
|
message("No successful response. Refresh status before retrying; pairing may already have changed.");
|
|
77
78
|
} finally {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
|
+
import { buildConnectSnapshot } from "../oauth/session/connect-snapshot";
|
|
3
|
+
import { publishConnectRequestRetry, watchConnectDecision, websiteOrigin } from "../oauth/consent/website-session";
|
|
4
|
+
|
|
5
|
+
const REQUEST_LIFETIME_MS = 15 * 60_000;
|
|
6
|
+
let pending: { url: string; expiresAt: number } | null = null;
|
|
7
|
+
|
|
8
|
+
/** Start a browser passkey ceremony for this Mac. No account credential enters MCP output. */
|
|
9
|
+
export async function startPasskeyAccountLink(): Promise<string> {
|
|
10
|
+
if (pending && pending.expiresAt > Date.now()) return pending.url;
|
|
11
|
+
const origin = websiteOrigin();
|
|
12
|
+
if (!origin) throw new Error("GrantTap account service is unavailable.");
|
|
13
|
+
const id = randomUUID();
|
|
14
|
+
const secret = randomBytes(32).toString("base64url");
|
|
15
|
+
const snapshot = () => ({ ...buildConnectSnapshot("GrantTap MCP"), purpose: "account-link" as const });
|
|
16
|
+
if (!await publishConnectRequestRetry(origin, id, snapshot(), 3, secret)) {
|
|
17
|
+
throw new Error("GrantTap account service did not accept the connection request.");
|
|
18
|
+
}
|
|
19
|
+
watchConnectDecision(origin, id, snapshot, (approved, method) => {
|
|
20
|
+
if (!approved || method !== "passkey") throw new Error("Use the account passkey to link this Mac.");
|
|
21
|
+
pending = null;
|
|
22
|
+
return { redirectUrl: `${origin}/account` };
|
|
23
|
+
}, { requestSecret: secret });
|
|
24
|
+
const url = `${origin}/connect#request=${id}`;
|
|
25
|
+
pending = { url, expiresAt: Date.now() + REQUEST_LIFETIME_MS };
|
|
26
|
+
return url;
|
|
27
|
+
}
|
|
@@ -6,6 +6,7 @@ import { inspectProviderStatusSnapshot } from "../status/provider-status";
|
|
|
6
6
|
import { packageVersion } from "../status/package-version";
|
|
7
7
|
import { connectionRuntimeStatus } from "../mcp-tools/connect/relay";
|
|
8
8
|
import { beginEnrollment, cancelEnrollment, enrollmentIsOpen } from "./enrollment";
|
|
9
|
+
import { loadAccountLink } from "../../../bridge/src/account-recovery/link";
|
|
9
10
|
|
|
10
11
|
export const connectionOutput = {
|
|
11
12
|
status: z.enum(["disconnected", "paired", "pairing", "expired", "connected"]),
|
|
@@ -23,6 +24,7 @@ export const connectionOutput = {
|
|
|
23
24
|
expiresInMinutes: z.number().int().positive().nullable(),
|
|
24
25
|
providers: z.array(z.object({ id: z.string(), status: z.string(), detail: z.string() })),
|
|
25
26
|
roomPrefix: z.string(),
|
|
27
|
+
accountLinkSaved: z.boolean(),
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
export type PendingCode = {
|
|
@@ -76,6 +78,7 @@ export class ConnectionState {
|
|
|
76
78
|
expiresInMinutes: code ? Math.max(1, Math.ceil((code.expiresAt - now) / 60_000)) : null,
|
|
77
79
|
providers: inspectProviderStatusSnapshot().providers.map(({ id, status, detail }) => ({ id, status, detail })),
|
|
78
80
|
roomPrefix: config?.room ? config.room.slice(0, 8) : "",
|
|
81
|
+
accountLinkSaved: loadAccountLink() !== null,
|
|
79
82
|
},
|
|
80
83
|
_meta: { granttap: code ? { pairingUri: code.pairingUri, qrDataUrl: code.qrDataUrl } : {} },
|
|
81
84
|
};
|
|
@@ -21,6 +21,16 @@ function render(value) {
|
|
|
21
21
|
current = state;
|
|
22
22
|
clearCode();
|
|
23
23
|
const meta = result._meta?.granttap ?? {};
|
|
24
|
+
const passkeyUrl = typeof meta.passkeyUrl === "string" && /^https:\/\/granttap\.com\/connect#request=[0-9a-f-]{36}$/i.test(meta.passkeyUrl)
|
|
25
|
+
? meta.passkeyUrl : "";
|
|
26
|
+
$("passkey-link").classList.toggle("hidden", !passkeyUrl || state.accountLinkSaved === true);
|
|
27
|
+
if (passkeyUrl) $("passkey-link").setAttribute("href", passkeyUrl);
|
|
28
|
+
else $("passkey-link").removeAttribute("href");
|
|
29
|
+
$("account-status").textContent = state.accountLinkSaved === true
|
|
30
|
+
? "Saved account link on this Mac. Current server access is checked when it is used."
|
|
31
|
+
: "No account linked on this Mac. A passkey can link it without changing the QR pairing.";
|
|
32
|
+
$("passkey").textContent = state.accountLinkSaved === true
|
|
33
|
+
? "Reauthorize with passkey" : "Connect with passkey";
|
|
24
34
|
const labels = {
|
|
25
35
|
disconnected: ["Not paired", "Add a device", "Tap Add a device. Scan the QR in GrantTap on iPhone, iPad, or Android."],
|
|
26
36
|
pairing: ["Waiting for a device", "Scan this QR", "Keep this card open. The QR is only here — not in chat."],
|
|
@@ -91,6 +101,7 @@ $("connect").addEventListener("click", () => {
|
|
|
91
101
|
call("connect");
|
|
92
102
|
});
|
|
93
103
|
$("refresh").addEventListener("click", () => call("connection_status"));
|
|
104
|
+
$("passkey").addEventListener("click", () => call("connect_with_passkey"));
|
|
94
105
|
$("reconnect").addEventListener("click", () => {
|
|
95
106
|
pendingMode = "reconnect";
|
|
96
107
|
$("confirm-copy").textContent = "Reconnect shows a new QR for the existing controller device in this room.";
|
|
@@ -27,7 +27,7 @@ details{border-top:1px solid #8884;padding-top:14px}summary{cursor:pointer;font-
|
|
|
27
27
|
</svg>
|
|
28
28
|
</div><div><div class="title">GrantTap</div><div class="status"><span class="dot"></span><span id="status">Checking connection…</span></div></div></header>
|
|
29
29
|
<section class="body">
|
|
30
|
-
<div class="copy"><h2 id="heading">Add a device</h2><p id="detail">Scan the QR in GrantTap on iPhone, iPad, or Android. It appears here as soon as pairing starts.</p><ul class="phones" id="phones"></ul><p class="empty" id="phones-empty">No saved pairing on this Mac yet.</p><div class="actions"><button class="primary" id="connect">Add a device</button><button id="reconnect">Reconnect</button><button id="new-qr" class="hidden">Create a new QR</button><button id="refresh">Refresh status</button><button id="copy" class="hidden">Copy pairing link</button></div><div class="message small" id="message" role="status"></div></div>
|
|
30
|
+
<div class="copy"><h2 id="heading">Add a device</h2><p id="detail">Scan the QR in GrantTap on iPhone, iPad, or Android. It appears here as soon as pairing starts.</p><ul class="phones" id="phones"></ul><p class="empty" id="phones-empty">No saved pairing on this Mac yet.</p><div class="actions"><button class="primary" id="connect">Add a device</button><button id="reconnect">Reconnect</button><button id="new-qr" class="hidden">Create a new QR</button><button id="refresh">Refresh status</button><button id="copy" class="hidden">Copy pairing link</button></div><p id="account-status" class="small">Checking account link…</p><div class="actions"><button id="passkey">Connect with passkey</button><a id="passkey-link" class="hidden" target="_blank" rel="noopener noreferrer">Continue on granttap.com</a></div><div class="message small" id="message" role="status"></div></div>
|
|
31
31
|
<img id="qr" class="hidden" alt="GrantTap pairing QR" />
|
|
32
32
|
</section>
|
|
33
33
|
<p id="expiry" class="small" aria-live="polite"></p>
|
|
@@ -36,9 +36,8 @@ details{border-top:1px solid #8884;padding-top:14px}summary{cursor:pointer;font-
|
|
|
36
36
|
<dt>Relay observed by this MCP</dt><dd id="relay">Not checked</dd>
|
|
37
37
|
<dt>Last encrypted message from iPhone</dt><dd id="phone">Not observed</dd></dl>
|
|
38
38
|
<h3>Provider readiness</h3><ul id="providers"></ul>
|
|
39
|
-
<p>
|
|
40
|
-
|
|
41
|
-
<p>Missing hooks or background sync? Run <code>npm install -g granttap-mcp@0.8.23</code> and <code>granttap setup</code> locally.
|
|
39
|
+
<p>QR pairs a phone directly. A passkey links this Mac to your GrantTap account so your signed-in phone can discover its computers. Provider sign-in is separate.</p>
|
|
40
|
+
<p>Missing hooks or background sync? Run <code>npm install -g granttap-mcp@0.8.35</code> and <code>granttap setup</code> locally.
|
|
42
41
|
A saved pairing alone does not prove your phone or the background helper is online.</p>
|
|
43
42
|
<p><a href="https://granttap.com" target="_blank" rel="noopener noreferrer">Website</a> ·
|
|
44
43
|
<a href="https://granttap.com/privacy" target="_blank" rel="noopener noreferrer">Privacy</a> ·
|
|
@@ -7,6 +7,7 @@ import { isMachineConfigured } from "../../status/pairing-status";
|
|
|
7
7
|
import { ConnectionState, connectionOutput } from "../../connection-center/state";
|
|
8
8
|
import { CONNECTION_WIDGET_URI } from "./widget";
|
|
9
9
|
import { resetRelay, relay } from "./relay";
|
|
10
|
+
import { startPasskeyAccountLink } from "../../connection-center/passkey";
|
|
10
11
|
|
|
11
12
|
const widgetMeta = {
|
|
12
13
|
ui: { resourceUri: CONNECTION_WIDGET_URI, visibility: ["model", "app"] },
|
|
@@ -39,6 +40,24 @@ export function registerConnectTool(server: McpServer): void {
|
|
|
39
40
|
annotations: { ...changes, readOnlyHint: true, idempotentHint: true },
|
|
40
41
|
_meta: widgetMeta,
|
|
41
42
|
}, async () => connectionResult(state));
|
|
43
|
+
server.registerTool("connect_with_passkey", {
|
|
44
|
+
title: "Link this Mac to a GrantTap account",
|
|
45
|
+
description: "Open a short-lived GrantTap account passkey request for this Mac. The passkey is entered only on granttap.com; existing QR pairings remain unchanged.",
|
|
46
|
+
inputSchema: {},
|
|
47
|
+
outputSchema: connectionOutput,
|
|
48
|
+
annotations: changes,
|
|
49
|
+
_meta: widgetMeta,
|
|
50
|
+
}, async () => {
|
|
51
|
+
try {
|
|
52
|
+
const passkeyUrl = await startPasskeyAccountLink();
|
|
53
|
+
const result = connectionResult(state);
|
|
54
|
+
const meta = result._meta?.granttap;
|
|
55
|
+
const granttap = meta && typeof meta === "object" ? meta : {};
|
|
56
|
+
return { ...result, _meta: { granttap: { ...granttap, passkeyUrl } } };
|
|
57
|
+
} catch {
|
|
58
|
+
return { isError: true, content: [{ type: "text", text: "GrantTap could not start passkey linking. Check the account service and try again." }] };
|
|
59
|
+
}
|
|
60
|
+
});
|
|
42
61
|
server.registerTool(
|
|
43
62
|
"connect",
|
|
44
63
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
|
|
3
|
-
export const CONNECTION_WIDGET_URI = "ui://granttap/connection/
|
|
3
|
+
export const CONNECTION_WIDGET_URI = "ui://granttap/connection/v4.html";
|
|
4
4
|
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ export function registerConnectionWidget(server: McpServer): void {
|
|
|
13
13
|
CONNECTION_WIDGET_URI,
|
|
14
14
|
{
|
|
15
15
|
title: "GrantTap connection center",
|
|
16
|
-
description: "GrantTap card:
|
|
16
|
+
description: "GrantTap card: QR device pairing, Mac account passkey linking, and connection status.",
|
|
17
17
|
mimeType: "text/html;profile=mcp-app",
|
|
18
18
|
},
|
|
19
19
|
async (uri) => ({
|
|
@@ -23,7 +23,7 @@ export function registerConnectionWidget(server: McpServer): void {
|
|
|
23
23
|
text: html,
|
|
24
24
|
_meta: {
|
|
25
25
|
ui: { prefersBorder: true, permissions: { clipboardWrite: {} }, csp: { connectDomains: [], resourceDomains: [] } },
|
|
26
|
-
"openai/widgetDescription": "
|
|
26
|
+
"openai/widgetDescription": "Pair a device by QR or link this Mac to your account with passkey.",
|
|
27
27
|
"openai/widgetPrefersBorder": true,
|
|
28
28
|
},
|
|
29
29
|
}],
|
package/cursor-plugin/README.md
CHANGED
|
@@ -10,7 +10,7 @@ keeps each phone response tied to the exact originating chat and prompt.
|
|
|
10
10
|
Install the supported CLI on this computer, then run:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npm install -g granttap-mcp@0.8.
|
|
13
|
+
npm install -g granttap-mcp@0.8.35
|
|
14
14
|
granttap setup
|
|
15
15
|
granttap status
|
|
16
16
|
```
|
package/cursor-plugin/mcp.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"command": "node",
|
|
5
5
|
"args": [
|
|
6
6
|
"-e",
|
|
7
|
-
"\"use strict\";\n\n// Cursor may start this MCP from any cwd. Execute JavaScript entry points with\n// Node itself: Windows batch shims require shell quoting that differs by host.\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst { delimiter, dirname, join } = require(\"node:path\");\n\nconst PACKAGE = \"granttap-mcp@0.8.
|
|
7
|
+
"\"use strict\";\n\n// Cursor may start this MCP from any cwd. Execute JavaScript entry points with\n// Node itself: Windows batch shims require shell quoting that differs by host.\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst { delimiter, dirname, join } = require(\"node:path\");\n\nconst PACKAGE = \"granttap-mcp@0.8.35\";\nconst dryRun = process.env.GRANTTAP_BOOTSTRAP_DRY_RUN === \"1\";\nconst platform = dryRun && process.env.GRANTTAP_BOOTSTRAP_PLATFORM\n ? process.env.GRANTTAP_BOOTSTRAP_PLATFORM : process.platform;\nconst nodeRoot = dryRun && process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT\n ? process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT : dirname(process.execPath);\nconst pathRoots = (process.env.PATH || \"\").split(delimiter).filter(Boolean);\nconst appData = process.env.APPDATA;\nconst roots = [nodeRoot, ...pathRoots, ...(appData ? [join(appData, \"npm\")] : [])];\nconst first = (paths) => paths.find((path) => existsSync(path));\n\nfunction launch() {\n if (platform !== \"win32\") {\n const installed = first(roots.map((root) => join(root, \"granttap-mcp\")));\n if (installed) return { command: installed, args: [] };\n const npx = first(roots.map((root) => join(root, \"npx\"))) || \"npx\";\n return { command: npx, args: [\"-y\", PACKAGE] };\n }\n const installed = first(roots.map((root) =>\n join(root, \"node_modules\", \"granttap-mcp\", \"bin\", \"granttap-mcp.mjs\")));\n if (installed) return { command: process.execPath, args: [installed] };\n const npx = first(roots.map((root) =>\n join(root, \"node_modules\", \"npm\", \"bin\", \"npx-cli.js\")));\n if (npx) return { command: process.execPath, args: [npx, \"-y\", PACKAGE] };\n throw new Error(\"Node.js/npm 20+ is required on this Windows device. Install npm and retry GrantTap.\");\n}\n\nlet target;\ntry { target = launch(); }\ncatch (error) {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n}\n\nif (dryRun) {\n process.stdout.write(JSON.stringify(target));\n process.exit(0);\n}\n\nconst child = spawn(target.command, target.args, { stdio: \"inherit\", windowsHide: true });\nchild.on(\"error\", (error) => {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n});\nchild.on(\"exit\", (code, signal) => {\n if (signal && platform !== \"win32\") {\n try { process.kill(process.pid, signal); } catch { process.exit(code ?? 1); }\n return;\n }\n process.exit(code ?? 1);\n});\n"
|
|
8
8
|
]
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -7,7 +7,7 @@ const { spawn } = require("node:child_process");
|
|
|
7
7
|
const { existsSync } = require("node:fs");
|
|
8
8
|
const { delimiter, dirname, join } = require("node:path");
|
|
9
9
|
|
|
10
|
-
const PACKAGE = "granttap-mcp@0.8.
|
|
10
|
+
const PACKAGE = "granttap-mcp@0.8.35";
|
|
11
11
|
const dryRun = process.env.GRANTTAP_BOOTSTRAP_DRY_RUN === "1";
|
|
12
12
|
const platform = dryRun && process.env.GRANTTAP_BOOTSTRAP_PLATFORM
|
|
13
13
|
? process.env.GRANTTAP_BOOTSTRAP_PLATFORM : process.platform;
|