granttap-mcp 0.8.85 → 0.8.86
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/apps/bridge/src/mesh/catalog/README.md +10 -0
- package/apps/bridge/src/mesh/catalog/index.ts +1 -0
- package/apps/bridge/src/mesh/catalog/repository-inspection.ts +49 -0
- package/apps/bridge/src/mesh/runtime/graph/binding-sync.ts +8 -8
- package/apps/bridge/src/monitor/capability-publication/README.md +13 -0
- package/apps/bridge/src/monitor/capability-publication/index.ts +33 -0
- package/apps/bridge/src/monitor/support/runtime.ts +23 -24
- 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
|
@@ -17,6 +17,16 @@ negative probes nor old repository identities persist across refreshes.
|
|
|
17
17
|
Behavior tests: `tests/agentic/codex-workspace-binding.test.ts` and
|
|
18
18
|
`tests/project/project-mesh-core.test.ts`.
|
|
19
19
|
|
|
20
|
+
## Background Graph admission
|
|
21
|
+
|
|
22
|
+
`inspectRepositoryAsync` provides the same repository facts without blocking
|
|
23
|
+
relay callbacks. Native Git subprocesses are limited to two across the process.
|
|
24
|
+
Concurrent inspections of the same path share their in-flight result; a later
|
|
25
|
+
inspection verifies the checkout again. Missing Git facts remain unknown.
|
|
26
|
+
|
|
27
|
+
Tests for asynchronous inspection live in `tests/`; Graph admission also has
|
|
28
|
+
`runtime/graph/tests/responsiveness/checkout-inspection.test.ts`.
|
|
29
|
+
|
|
20
30
|
## License
|
|
21
31
|
|
|
22
32
|
GrantTap Commercial Source License. See [LICENSE](../../../../../LICENSE).
|
|
@@ -19,6 +19,7 @@ import { readIntegrationMap } from "../observed/integration-map";
|
|
|
19
19
|
import type { MeshStore } from "../store";
|
|
20
20
|
import { queueProjectBindingSync } from "../../engine/runtime/engine-projects";
|
|
21
21
|
import { isCursorTaskCloneId } from "../../sessions/cursor/catalog";
|
|
22
|
+
export { inspectRepositoryAsync } from "./repository-inspection";
|
|
22
23
|
|
|
23
24
|
export type RepositoryFacts = {
|
|
24
25
|
root: string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { canonicalRepositoryIdentity, sanitizedRepositoryRemote } from "../identity";
|
|
3
|
+
import type { RepositoryFacts } from ".";
|
|
4
|
+
|
|
5
|
+
let running = 0;
|
|
6
|
+
const waiting: Array<() => void> = [];
|
|
7
|
+
const pending = new Map<string, Promise<RepositoryFacts>>();
|
|
8
|
+
|
|
9
|
+
/** Native read-only Git work stays bounded without blocking relay callbacks. */
|
|
10
|
+
function git(cwd: string, args: string[]): Promise<string | undefined> {
|
|
11
|
+
return new Promise(resolve => {
|
|
12
|
+
const run = () => {
|
|
13
|
+
running++;
|
|
14
|
+
const done = (error: Error | null, stdout = "") => {
|
|
15
|
+
running--;
|
|
16
|
+
waiting.shift()?.();
|
|
17
|
+
resolve(error ? undefined : stdout.trim() || undefined);
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
execFile("git", ["-C", cwd, ...args], {
|
|
21
|
+
encoding: "utf8", timeout: 2_000, maxBuffer: 1024 * 1024, windowsHide: true,
|
|
22
|
+
}, done);
|
|
23
|
+
} catch { done(new Error("Repository command could not start")); }
|
|
24
|
+
};
|
|
25
|
+
if (running < 2) run(); else waiting.push(run);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function inspect(cwd: string): Promise<RepositoryFacts> {
|
|
30
|
+
const worktree = await git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
31
|
+
const root = worktree ?? cwd;
|
|
32
|
+
const [rawRemote, revision, branch] = await Promise.all([
|
|
33
|
+
git(root, ["remote", "get-url", "origin"]),
|
|
34
|
+
git(root, ["rev-parse", "HEAD"]),
|
|
35
|
+
git(root, ["symbolic-ref", "--quiet", "--short", "HEAD"]),
|
|
36
|
+
]);
|
|
37
|
+
return { root, worktree, revision, branch,
|
|
38
|
+
baseRemote: rawRemote ? sanitizedRepositoryRemote(rawRemote) : undefined,
|
|
39
|
+
canonicalRepositoryId: canonicalRepositoryIdentity(rawRemote, root) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Share only in-flight observations; a later refresh verifies the checkout again. */
|
|
43
|
+
export function inspectRepositoryAsync(cwd: string): Promise<RepositoryFacts> {
|
|
44
|
+
const active = pending.get(cwd);
|
|
45
|
+
if (active) return active;
|
|
46
|
+
const next = inspect(cwd).finally(() => { pending.delete(cwd); });
|
|
47
|
+
pending.set(cwd, next);
|
|
48
|
+
return next;
|
|
49
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { realpathSync } from "node:fs";
|
|
2
2
|
import type { MeshSnapshot, ProjectRepositoryGraph } from "../../../../../../packages/protocol/schema";
|
|
3
3
|
import { queueProjectBindingSync } from "../../../engine/runtime/engine-projects";
|
|
4
|
-
import {
|
|
4
|
+
import { inspectRepositoryAsync, type RepositoryFacts } from "../../catalog";
|
|
5
5
|
import { computerId } from "../../identity/computer";
|
|
6
6
|
import { matchesLocalCheckout } from "./checkout-identity";
|
|
7
7
|
|
|
@@ -18,18 +18,18 @@ export async function refreshLocalGraphBindings(
|
|
|
18
18
|
snapshot: MeshSnapshot,
|
|
19
19
|
dependencies: {
|
|
20
20
|
endpoint: () => string;
|
|
21
|
-
inspect:
|
|
21
|
+
inspect: (cwd: string) => RepositoryFacts | Promise<RepositoryFacts>;
|
|
22
22
|
sync: typeof queueProjectBindingSync;
|
|
23
|
-
} = { endpoint: computerId, inspect:
|
|
23
|
+
} = { endpoint: computerId, inspect: inspectRepositoryAsync, sync: queueProjectBindingSync },
|
|
24
24
|
): Promise<ProjectRepositoryGraph[]> {
|
|
25
25
|
const localEndpoint = dependencies.endpoint();
|
|
26
26
|
const candidates = (snapshot.bindings ?? []).filter((binding) =>
|
|
27
27
|
binding.endpointId === localEndpoint && binding.available && binding.localPathHint);
|
|
28
28
|
const failures: ProjectRepositoryGraph[] = [];
|
|
29
|
-
const checkouts = candidates.
|
|
30
|
-
const facts = readCheckout(dependencies.inspect, binding.localPathHint!);
|
|
29
|
+
const checkouts = (await Promise.all(candidates.map(async binding => {
|
|
30
|
+
const facts = await readCheckout(dependencies.inspect, binding.localPathHint!);
|
|
31
31
|
return facts ? [{ binding, facts, real: realpathSync(facts.root) }] : [];
|
|
32
|
-
});
|
|
32
|
+
}))).flat();
|
|
33
33
|
await Promise.all(checkouts.map(async ({ binding, facts, real }) => {
|
|
34
34
|
const pathIdentity = matchesLocalCheckout(binding.repositoryId, facts.root);
|
|
35
35
|
if (facts.canonicalRepositoryId !== binding.repositoryId && !pathIdentity) {
|
|
@@ -58,9 +58,9 @@ export async function refreshLocalGraphBindings(
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/** A Git checkout rooted exactly at the bound path, or nothing a Graph can be built from. */
|
|
61
|
-
function readCheckout(inspect:
|
|
61
|
+
async function readCheckout(inspect: (cwd: string) => RepositoryFacts | Promise<RepositoryFacts>, path: string) {
|
|
62
62
|
try {
|
|
63
|
-
const facts = inspect(path);
|
|
63
|
+
const facts = await inspect(path);
|
|
64
64
|
if (!facts.worktree || realpathSync(facts.root) !== realpathSync(path)) return undefined;
|
|
65
65
|
return facts;
|
|
66
66
|
} catch {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Usage publication
|
|
2
|
+
|
|
3
|
+
Public entry: `index.ts`. Complete capability totals are computed from the live
|
|
4
|
+
catalog and full discovered history. This work has one independent in-flight
|
|
5
|
+
publication per relay client. A slow history read or encrypted fan-out does not
|
|
6
|
+
hold the current chat catalog. Successful reports retain their existing cadence;
|
|
7
|
+
failures retry on a later catalog publication. Closing the monitor prevents
|
|
8
|
+
further queued reads and sends. Counts and unknown usage semantics are unchanged.
|
|
9
|
+
Concurrent catalog and Usage requests share one in-flight history read. The
|
|
10
|
+
history cache's lifetime begins when that read completes.
|
|
11
|
+
|
|
12
|
+
Behavior tests live in `tests/`.
|
|
13
|
+
Licensed under the GrantTap Commercial Source License in the repository root.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { setImmediate } from "node:timers/promises";
|
|
2
|
+
import type { RelayClient } from "../../../../../packages/core/relay-client";
|
|
3
|
+
import type { SessionInfo, SessionsStatus } from "../../../../../packages/protocol/schema";
|
|
4
|
+
import { scanCapabilityUsage, scopeCapabilityUsageToRoom } from "../../sessions";
|
|
5
|
+
|
|
6
|
+
type Publication = { running: boolean; closed: boolean; lastPublishedAt: number };
|
|
7
|
+
const publications = new WeakMap<RelayClient, Publication>();
|
|
8
|
+
|
|
9
|
+
/** A slow usage fan-out never occupies the current chat catalog's publication slot. */
|
|
10
|
+
export function publishMonitorCapabilities(client: RelayClient, status: SessionsStatus,
|
|
11
|
+
history: () => Promise<SessionInfo[]>): void {
|
|
12
|
+
const state = publications.get(client) ?? { running: false, closed: false, lastPublishedAt: 0 };
|
|
13
|
+
publications.set(client, state);
|
|
14
|
+
if (state.running || state.closed || Date.now() - state.lastPublishedAt < 30_000) return;
|
|
15
|
+
state.running = true;
|
|
16
|
+
const work = (async () => {
|
|
17
|
+
await setImmediate();
|
|
18
|
+
if (state.closed || !client.isConnected) return;
|
|
19
|
+
const older = await history();
|
|
20
|
+
if (state.closed || !client.isConnected) return;
|
|
21
|
+
const usage = scanCapabilityUsage([...status.sessions, ...older]);
|
|
22
|
+
await client.send(scopeCapabilityUsageToRoom(usage, client.room), "phone",
|
|
23
|
+
{ ttlMs: 90_000, reliable: false });
|
|
24
|
+
state.lastPublishedAt = Date.now();
|
|
25
|
+
})();
|
|
26
|
+
void work.catch(() => {}).finally(() => { state.running = false; });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function stopMonitorCapabilities(client: RelayClient): void {
|
|
30
|
+
const state = publications.get(client);
|
|
31
|
+
if (state) state.closed = true;
|
|
32
|
+
publications.delete(client);
|
|
33
|
+
}
|
|
@@ -23,7 +23,7 @@ import { publishedCatalog, rememberPublishedCatalog, resumePublishedChats,
|
|
|
23
23
|
publishCatalogHeads, stopCatalogHeads } from "../../session-catalog";
|
|
24
24
|
import { createMachineLoadPublisher } from "../../machine-load";
|
|
25
25
|
import { startMachineLoadLoop } from "../../machine-load/host/loop";
|
|
26
|
-
import {
|
|
26
|
+
import { scanSessionHistoryCooperatively, scanSessionsCooperatively, TOKEN_WINDOW_HOURS } from "../../sessions";
|
|
27
27
|
import { boundedCatalogHistory, INTERVAL_MS, publishSessionEvents } from "../handlers/catalog";
|
|
28
28
|
import { sweepAttachments } from "../handlers/receipts";
|
|
29
29
|
import { handleMonitorMessage } from "../handlers/inbound";
|
|
@@ -32,6 +32,7 @@ import { decorateTaskOriginCatalog } from '../../task-origins';
|
|
|
32
32
|
const HISTORY_INTERVAL_MS = 60_000;
|
|
33
33
|
const resumedHeads = new WeakSet<RelayClient>();
|
|
34
34
|
import { publishMonitorMeshes, stopMonitorMeshes } from "../mesh-publication";
|
|
35
|
+
import { publishMonitorCapabilities, stopMonitorCapabilities } from "../capability-publication";
|
|
35
36
|
|
|
36
37
|
export type SessionMonitor = {
|
|
37
38
|
publish: () => Promise<void>;
|
|
@@ -114,22 +115,22 @@ async function publishMonitorTick(input: {
|
|
|
114
115
|
leadership: ReturnType<typeof monitorLeadership>;
|
|
115
116
|
subscriptions: Set<string>;
|
|
116
117
|
history: (force?: boolean) => Promise<SessionsStatus["sessions"]>;
|
|
117
|
-
clocks: { lastHistoryPublishedAt: number
|
|
118
|
+
clocks: { lastHistoryPublishedAt: number };
|
|
118
119
|
loadLoop: { updateStatus: (status: SessionsStatus) => void };
|
|
119
120
|
forceHistory: boolean;
|
|
120
121
|
active: () => boolean;
|
|
121
|
-
}): Promise<{ lastHistoryPublishedAt: number
|
|
122
|
+
}): Promise<{ lastHistoryPublishedAt: number }> {
|
|
122
123
|
const { client, leadership, subscriptions, history, clocks, loadLoop, forceHistory } = input;
|
|
123
|
-
let { lastHistoryPublishedAt
|
|
124
|
-
if (!client.isConnected) return { lastHistoryPublishedAt
|
|
124
|
+
let { lastHistoryPublishedAt } = clocks;
|
|
125
|
+
if (!client.isConnected) return { lastHistoryPublishedAt };
|
|
125
126
|
await publishHeartbeat(client).catch(() => {});
|
|
126
|
-
if (!input.active()) return { lastHistoryPublishedAt
|
|
127
|
-
if (!leadership.acquire()) return { lastHistoryPublishedAt
|
|
127
|
+
if (!input.active()) return { lastHistoryPublishedAt };
|
|
128
|
+
if (!leadership.acquire()) return { lastHistoryPublishedAt };
|
|
128
129
|
await client.send(approvalsStatus(), "phone", { ttlMs: INTERVAL_MS * 3, reliable: false });
|
|
129
130
|
const initial = lastHistoryPublishedAt === 0;
|
|
130
131
|
const includeHistory = forceHistory || (!initial && Date.now() - lastHistoryPublishedAt >= HISTORY_INTERVAL_MS);
|
|
131
132
|
const snapshot = await monitorSnapshot({ includeHistory, forceHistory, subscriptions, history });
|
|
132
|
-
if (!input.active()) return { lastHistoryPublishedAt
|
|
133
|
+
if (!input.active()) return { lastHistoryPublishedAt };
|
|
133
134
|
const status = decorateTaskOriginCatalog(snapshot, client.room);
|
|
134
135
|
rememberPublishedCatalog(client, status);
|
|
135
136
|
await client.send(status, "phone", { ttlMs: INTERVAL_MS * 24, reliable: false });
|
|
@@ -146,15 +147,8 @@ async function publishMonitorTick(input: {
|
|
|
146
147
|
void refreshMcpLoad(status.sessions).catch(() => {});
|
|
147
148
|
publishCatalogHeads(client, status, subscriptions, publishSessionEvents);
|
|
148
149
|
if (loadRuntimeConfig().meshEnabled) publishMonitorMeshes(client, status);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
...status.sessions,
|
|
152
|
-
...await history(false),
|
|
153
|
-
]);
|
|
154
|
-
await client.send(scopeCapabilityUsageToRoom(usage, client.room), "phone", { ttlMs: 90_000, reliable: false });
|
|
155
|
-
lastCapabilityPublishedAt = Date.now();
|
|
156
|
-
}
|
|
157
|
-
return { lastHistoryPublishedAt, lastCapabilityPublishedAt };
|
|
150
|
+
publishMonitorCapabilities(client, status, () => history(false));
|
|
151
|
+
return { lastHistoryPublishedAt };
|
|
158
152
|
}
|
|
159
153
|
|
|
160
154
|
/**
|
|
@@ -168,8 +162,8 @@ export function startSessionMonitor(client: RelayClient): SessionMonitor {
|
|
|
168
162
|
primeSessionKeys(client);
|
|
169
163
|
publishedCatalog(client);
|
|
170
164
|
let historyCache: { generatedAt: number; sessions: SessionsStatus["sessions"] } | undefined;
|
|
165
|
+
let historyRead: Promise<SessionsStatus["sessions"]> | undefined;
|
|
171
166
|
let lastHistoryPublishedAt = 0;
|
|
172
|
-
let lastCapabilityPublishedAt = 0;
|
|
173
167
|
let closed = false;
|
|
174
168
|
const publishMachineLoad = createMachineLoadPublisher({
|
|
175
169
|
log: (line) => process.stderr.write(`[monitor] ${line}\n`),
|
|
@@ -189,11 +183,16 @@ export function startSessionMonitor(client: RelayClient): SessionMonitor {
|
|
|
189
183
|
},
|
|
190
184
|
});
|
|
191
185
|
|
|
192
|
-
const history =
|
|
193
|
-
if (
|
|
194
|
-
|
|
186
|
+
const history = (force = false): Promise<SessionsStatus["sessions"]> => {
|
|
187
|
+
if (historyRead) return historyRead;
|
|
188
|
+
if (!force && historyCache && Date.now() - historyCache.generatedAt <= 60_000) {
|
|
189
|
+
return Promise.resolve(historyCache.sessions);
|
|
195
190
|
}
|
|
196
|
-
|
|
191
|
+
historyRead = scanSessionHistoryCooperatively().then(sessions => {
|
|
192
|
+
historyCache = { generatedAt: Date.now(), sessions };
|
|
193
|
+
return sessions;
|
|
194
|
+
}).finally(() => { historyRead = undefined; });
|
|
195
|
+
return historyRead;
|
|
197
196
|
};
|
|
198
197
|
|
|
199
198
|
const publish = singleFlightPublisher(async (forceHistory: boolean): Promise<void> => {
|
|
@@ -201,10 +200,9 @@ export function startSessionMonitor(client: RelayClient): SessionMonitor {
|
|
|
201
200
|
const next = await publishMonitorTick({
|
|
202
201
|
client, leadership, subscriptions, history, loadLoop, forceHistory,
|
|
203
202
|
active: () => !closed,
|
|
204
|
-
clocks: { lastHistoryPublishedAt
|
|
203
|
+
clocks: { lastHistoryPublishedAt },
|
|
205
204
|
});
|
|
206
205
|
lastHistoryPublishedAt = next.lastHistoryPublishedAt;
|
|
207
|
-
lastCapabilityPublishedAt = next.lastCapabilityPublishedAt;
|
|
208
206
|
});
|
|
209
207
|
|
|
210
208
|
const off = client.onMessage(async (payload: Payload, peerPublicKey) => {
|
|
@@ -245,6 +243,7 @@ export function startSessionMonitor(client: RelayClient): SessionMonitor {
|
|
|
245
243
|
offConnected?.();
|
|
246
244
|
stopCatalog();
|
|
247
245
|
stopCatalogHeads(client);
|
|
246
|
+
stopMonitorCapabilities(client);
|
|
248
247
|
stopMonitorMeshes(client);
|
|
249
248
|
stopHeartbeat();
|
|
250
249
|
loadLoop.stop();
|
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.86
|
|
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.86\";\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 22.13.0+ 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.86";
|
|
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;
|