granttap-mcp 0.8.117 → 0.8.119
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/capabilities/README.md +4 -0
- package/apps/bridge/src/capabilities/descriptors.ts +23 -1
- package/apps/bridge/src/capabilities/index.ts +2 -0
- package/apps/bridge/src/engine/distribution/artifacts.ts +2 -2
- package/apps/bridge/src/mesh/catalog/index.ts +8 -4
- package/apps/bridge/src/mesh/catalog/monitor-inspection/README.md +14 -0
- package/apps/bridge/src/mesh/catalog/monitor-inspection/index.ts +48 -0
- package/apps/bridge/src/mesh/catalog/repository-inspection.ts +9 -3
- package/apps/bridge/src/mesh/runtime/index.ts +5 -5
- package/apps/bridge/src/monitor/support/runtime.ts +12 -6
- 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
|
@@ -18,3 +18,7 @@ do not borrow an implementation's description or icon. These are display data,
|
|
|
18
18
|
not instructions or evidence that a capability was used.
|
|
19
19
|
|
|
20
20
|
License: this module is distributed under the GrantTap Commercial Source License in the repository-root `LICENSE` file.
|
|
21
|
+
|
|
22
|
+
Catalog publication warms native Codex MCP configuration asynchronously through
|
|
23
|
+
`refreshCodexMcpDescriptors`, preserving its existing retry window and last known
|
|
24
|
+
rows while allowing request-delivery callbacks to run during a slow native query.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -65,6 +65,28 @@ function codexMcpDescriptors(): McpDescriptor[] {
|
|
|
65
65
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
let codexRefresh: Promise<void> | undefined;
|
|
69
|
+
|
|
70
|
+
/** Warm native configuration asynchronously before a catalog consumes the synchronous projection. */
|
|
71
|
+
export function refreshCodexMcpDescriptors(): Promise<void> {
|
|
72
|
+
if (codexCache && Date.now() - codexCache.at < CACHE_MS) return Promise.resolve();
|
|
73
|
+
if (codexRefresh) return codexRefresh;
|
|
74
|
+
codexRefresh = new Promise<void>(resolve => {
|
|
75
|
+
const finish = (error: Error | null, output = '') => {
|
|
76
|
+
let rows = codexCache?.rows ?? [];
|
|
77
|
+
if (!error) {
|
|
78
|
+
try { const parsed = JSON.parse(output); if (Array.isArray(parsed)) rows = parsed; } catch { /* Keep last known rows. */ }
|
|
79
|
+
}
|
|
80
|
+
codexCache = { at: Date.now(), rows };
|
|
81
|
+
resolve();
|
|
82
|
+
};
|
|
83
|
+
try { execFile(resolveCodexBinary(), ['mcp', 'list', '--json'],
|
|
84
|
+
{ encoding: 'utf8', timeout: 15_000, maxBuffer: 4 * 1024 * 1024 }, finish); }
|
|
85
|
+
catch { finish(new Error('Native configuration is unavailable.')); }
|
|
86
|
+
}).finally(() => { codexRefresh = undefined; });
|
|
87
|
+
return codexRefresh;
|
|
88
|
+
}
|
|
89
|
+
|
|
68
90
|
function codexMcpRows(): CodexMcpRow[] {
|
|
69
91
|
if (codexCache && Date.now() - codexCache.at < CACHE_MS) return codexCache.rows;
|
|
70
92
|
try {
|
|
@@ -8,8 +8,8 @@ export type EngineArtifact = {
|
|
|
8
8
|
|
|
9
9
|
/** Release hashes are part of the reviewed MCP, never supplied by a remote manifest. */
|
|
10
10
|
export const ENGINE_ARTIFACTS: readonly EngineArtifact[] = [
|
|
11
|
-
{ version: "0.1.
|
|
12
|
-
sha256: "
|
|
11
|
+
{ version: "0.1.7", platform: "darwin", arch: "arm64",
|
|
12
|
+
sha256: "bf7fcf3d08d78a437da21fcb88057d77840be4b22a0f7388f383059e685a76ef", bytes: 12201856 },
|
|
13
13
|
{ version: "0.1.2", platform: "darwin", arch: "x64",
|
|
14
14
|
sha256: "b3235a15936a77ec323d5ecbc54a0bc19994fe6e5a36044da964b68524b4c1dd", bytes: 13944984 },
|
|
15
15
|
{ version: "0.1.2", platform: "linux", arch: "x64",
|
|
@@ -20,7 +20,10 @@ import type { MeshStore } from "../store";
|
|
|
20
20
|
import { queueProjectBindingSync } from "../../engine/runtime/engine-projects";
|
|
21
21
|
import { isCursorTaskCloneId } from "../../sessions/cursor/catalog";
|
|
22
22
|
import { localTaskTransfers } from '../../task-transfer';
|
|
23
|
-
export { inspectRepositoryAsync } from "./repository-inspection";
|
|
23
|
+
export { inspectRepositoryAsync, inspectWorkingTreeAsync } from "./repository-inspection";
|
|
24
|
+
export { prepareMonitorInspection } from "./monitor-inspection";
|
|
25
|
+
import type { CatalogInspection } from "./monitor-inspection";
|
|
26
|
+
export type { CatalogInspection } from "./monitor-inspection";
|
|
24
27
|
|
|
25
28
|
export type RepositoryFacts = {
|
|
26
29
|
root: string;
|
|
@@ -120,7 +123,7 @@ export function linkSessionsToProjects(
|
|
|
120
123
|
store: MeshStore,
|
|
121
124
|
sessions: SessionInfo[],
|
|
122
125
|
computerId: string,
|
|
123
|
-
inspect: (cwd: string) => RepositoryFacts = inspectRepository,
|
|
126
|
+
inspect: ((cwd: string) => RepositoryFacts) | CatalogInspection = inspectRepository,
|
|
124
127
|
formerNames: string[] = formerComputerNames(),
|
|
125
128
|
): SessionInfo[] {
|
|
126
129
|
localTaskTransfers().reconcileCatalogOwnership(store, computerId);
|
|
@@ -143,13 +146,14 @@ export function linkSessionsToProjects(
|
|
|
143
146
|
const repositories = new Map<string, RepositoryFacts>();
|
|
144
147
|
const workingTrees = new Map<string, boolean | undefined>();
|
|
145
148
|
const inspectWorkingTree = (cwd: string): boolean | undefined => {
|
|
146
|
-
if (!workingTrees.has(cwd)) workingTrees.set(cwd,
|
|
149
|
+
if (!workingTrees.has(cwd)) workingTrees.set(cwd, typeof inspect === "function"
|
|
150
|
+
? hasUncommittedWork(cwd) : inspect.workingTree(cwd));
|
|
147
151
|
return workingTrees.get(cwd);
|
|
148
152
|
};
|
|
149
153
|
const inspectOnce = (cwd: string): RepositoryFacts => {
|
|
150
154
|
let facts = repositories.get(cwd);
|
|
151
155
|
if (!facts) {
|
|
152
|
-
facts = inspect(cwd);
|
|
156
|
+
facts = typeof inspect === "function" ? inspect(cwd) : inspect.repository(cwd);
|
|
153
157
|
repositories.set(cwd, facts);
|
|
154
158
|
}
|
|
155
159
|
return facts;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Monitor repository inspection
|
|
2
|
+
|
|
3
|
+
Public entry: `prepareMonitorInspection` through the catalog entry point. The
|
|
4
|
+
monitor prepares Git observations asynchronously with the existing two-command
|
|
5
|
+
pool. Each catalog reads uncommitted work afresh. Only stable repository metadata
|
|
6
|
+
is reused for at most 30 seconds; missing repositories retry after five seconds.
|
|
7
|
+
An explicit refresh bypasses reuse. Retired directories are removed, older
|
|
8
|
+
in-flight results cannot replace a forced refresh, and cache size is bounded.
|
|
9
|
+
|
|
10
|
+
These are catalog observations. Explicit Git and Task transfer operations retain
|
|
11
|
+
their fresh native inspection and never use this observation cache as authority.
|
|
12
|
+
Tests cover fresh unsaved work, forced refresh, expiry, retirement and ordering.
|
|
13
|
+
|
|
14
|
+
License: GrantTap Commercial Source License in the repository LICENSE.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { inspectRepositoryAsync, inspectWorkingTreeAsync } from '../repository-inspection';
|
|
2
|
+
import { inspectRepository, hasUncommittedWork, type RepositoryFacts } from '..';
|
|
3
|
+
|
|
4
|
+
type Readers = { repository: (cwd: string) => Promise<RepositoryFacts>;
|
|
5
|
+
workingTree: (cwd: string) => Promise<boolean | undefined>; now: () => number };
|
|
6
|
+
export type CatalogInspection = { repository: (cwd: string) => RepositoryFacts;
|
|
7
|
+
workingTree: (cwd: string) => boolean | undefined };
|
|
8
|
+
type Observation = { facts: RepositoryFacts; observedAt: number; expiresAt: number };
|
|
9
|
+
|
|
10
|
+
/** Observation only. Explicit Git and transfer operations continue to inspect native state freshly. */
|
|
11
|
+
export function createMonitorInspection(readers: Readers = {
|
|
12
|
+
repository: inspectRepositoryAsync, workingTree: inspectWorkingTreeAsync, now: Date.now,
|
|
13
|
+
}) {
|
|
14
|
+
const metadata = new Map<string, Observation>();
|
|
15
|
+
const pending = new Map<string, Promise<RepositoryFacts>>();
|
|
16
|
+
let admitted = new Set<string>();
|
|
17
|
+
const repository = (cwd: string, force: boolean): Promise<RepositoryFacts> => {
|
|
18
|
+
const cached = metadata.get(cwd);
|
|
19
|
+
if (!force && cached && readers.now() >= cached.observedAt && readers.now() < cached.expiresAt) return Promise.resolve(cached.facts);
|
|
20
|
+
const active = pending.get(cwd);
|
|
21
|
+
if (!force && active) return active;
|
|
22
|
+
const next = readers.repository(cwd).then(facts => {
|
|
23
|
+
const observed = Object.freeze({ ...facts }), now = readers.now();
|
|
24
|
+
if (pending.get(cwd) === next && admitted.has(cwd) && (metadata.has(cwd) || metadata.size < 512)) {
|
|
25
|
+
metadata.set(cwd, { facts: observed, observedAt: now, expiresAt: now + (facts.worktree ? 30_000 : 5_000) });
|
|
26
|
+
}
|
|
27
|
+
return observed;
|
|
28
|
+
}).finally(() => { if (pending.get(cwd) === next) pending.delete(cwd); });
|
|
29
|
+
pending.set(cwd, next);
|
|
30
|
+
return next;
|
|
31
|
+
};
|
|
32
|
+
return async (directories: string[], force = false): Promise<CatalogInspection> => {
|
|
33
|
+
admitted = new Set(directories);
|
|
34
|
+
for (const cwd of metadata.keys()) if (!admitted.has(cwd)) metadata.delete(cwd);
|
|
35
|
+
const facts = new Map(await Promise.all([...admitted].map(async cwd => [cwd, await repository(cwd, force)] as const)));
|
|
36
|
+
const roots = new Set([...facts.values()].flatMap(value => value.worktree ? [value.worktree] : []));
|
|
37
|
+
const workingTrees = new Map(await Promise.all([...roots].map(async root => [root, await readers.workingTree(root)] as const)));
|
|
38
|
+
const values = new Map([...facts].map(([cwd, value]) => [cwd, { repository: value,
|
|
39
|
+
workingTree: value.worktree ? workingTrees.get(value.worktree) : undefined }] as const));
|
|
40
|
+
for (const value of [...values.values()]) {
|
|
41
|
+
if (value.repository.worktree && !values.has(value.repository.worktree)) values.set(value.repository.worktree, value);
|
|
42
|
+
}
|
|
43
|
+
return { repository: cwd => values.get(cwd)?.repository ?? inspectRepository(cwd),
|
|
44
|
+
workingTree: cwd => values.has(cwd) ? values.get(cwd)!.workingTree : hasUncommittedWork(cwd) };
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const prepareMonitorInspection = createMonitorInspection();
|
|
@@ -7,18 +7,18 @@ const waiting: Array<() => void> = [];
|
|
|
7
7
|
const pending = new Map<string, Promise<RepositoryFacts>>();
|
|
8
8
|
|
|
9
9
|
/** Native read-only Git work stays bounded without blocking relay callbacks. */
|
|
10
|
-
function git(cwd: string, args: string[]): Promise<string | undefined> {
|
|
10
|
+
function git(cwd: string, args: string[], status = false): Promise<string | undefined> {
|
|
11
11
|
return new Promise(resolve => {
|
|
12
12
|
const run = () => {
|
|
13
13
|
running++;
|
|
14
14
|
const done = (error: Error | null, stdout = "") => {
|
|
15
15
|
running--;
|
|
16
16
|
waiting.shift()?.();
|
|
17
|
-
resolve(error ? undefined : stdout.trim() || undefined);
|
|
17
|
+
resolve(error ? undefined : status ? stdout : stdout.trim() || undefined);
|
|
18
18
|
};
|
|
19
19
|
try {
|
|
20
20
|
execFile("git", ["-C", cwd, ...args], {
|
|
21
|
-
encoding: "utf8", timeout: 2_000, maxBuffer: 1024 * 1024, windowsHide: true,
|
|
21
|
+
encoding: "utf8", timeout: status ? 5_000 : 2_000, maxBuffer: (status ? 4 : 1) * 1024 * 1024, windowsHide: true,
|
|
22
22
|
}, done);
|
|
23
23
|
} catch { done(new Error("Repository command could not start")); }
|
|
24
24
|
};
|
|
@@ -47,3 +47,9 @@ export function inspectRepositoryAsync(cwd: string): Promise<RepositoryFacts> {
|
|
|
47
47
|
pending.set(cwd, next);
|
|
48
48
|
return next;
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
/** A fresh working-tree observation shares the bounded, nonblocking Git command pool. */
|
|
52
|
+
export async function inspectWorkingTreeAsync(cwd: string): Promise<boolean | undefined> {
|
|
53
|
+
const output = await git(cwd, ['status', '--porcelain=v1', '--untracked-files=normal', '-z'], true);
|
|
54
|
+
return output === undefined ? undefined : output.length > 0;
|
|
55
|
+
}
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from "../../reply";
|
|
22
22
|
import { isProviderEnabled } from "../../config/runtime";
|
|
23
23
|
import { sendMeshPayload } from "../../host/session-keys";
|
|
24
|
-
import { linkSessionsToProjects } from "../catalog";
|
|
24
|
+
import { linkSessionsToProjects, type CatalogInspection } from "../catalog";
|
|
25
25
|
import { catalogFromEndpoint } from "../catalog/models";
|
|
26
26
|
import { saveProjectCapabilityRequest } from "../catalog/project/requests";
|
|
27
27
|
import { computerId } from "../identity/computer";
|
|
@@ -84,8 +84,8 @@ const defaultDependencies: MeshRuntimeDependencies = {
|
|
|
84
84
|
export function createMeshRuntime(deps: MeshRuntimeDependencies) {
|
|
85
85
|
const flow = createHandoffFlow(deps);
|
|
86
86
|
const { capsulePrompt, acceptHandoff, answerAgentQuestion } = flow;
|
|
87
|
-
const catalog = (sessions: SessionInfo[]) =>
|
|
88
|
-
linkSessionsToProjects(deps.store(), sessions, deps.computer());
|
|
87
|
+
const catalog = (sessions: SessionInfo[], inspection?: CatalogInspection) =>
|
|
88
|
+
linkSessionsToProjects(deps.store(), sessions, deps.computer(), inspection);
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
return {
|
|
@@ -165,8 +165,8 @@ export function createLocalMeshRuntime(overrides: Partial<MeshRuntimeDependencie
|
|
|
165
165
|
|
|
166
166
|
const defaultRuntime = createLocalMeshRuntime();
|
|
167
167
|
|
|
168
|
-
export function meshCatalog(sessions: SessionInfo[]): SessionInfo[] {
|
|
169
|
-
return defaultRuntime.catalog(sessions);
|
|
168
|
+
export function meshCatalog(sessions: SessionInfo[], inspection?: CatalogInspection): SessionInfo[] {
|
|
169
|
+
return defaultRuntime.catalog(sessions, inspection);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
export function meshSnapshots(): MeshSnapshot[] {
|
|
@@ -7,7 +7,8 @@ import { RelayClient } from "../../../../../packages/core/relay-client";
|
|
|
7
7
|
import type { Payload, SessionInfo, SessionsStatus } from "../../../../../packages/protocol/schema";
|
|
8
8
|
import { loadRuntimeConfig, autoAcceptLevelFor } from "../../config";
|
|
9
9
|
import { monitorLeadership } from "./leadership";
|
|
10
|
-
import { mcpServersForSession, workspaceSkills } from "../../capabilities";
|
|
10
|
+
import { mcpServersForSession, workspaceSkills, refreshCodexMcpDescriptors } from "../../capabilities";
|
|
11
|
+
import { prepareMonitorInspection, type CatalogInspection } from "../../mesh/catalog";
|
|
11
12
|
import { inspectAgentIntegrations } from "../../install";
|
|
12
13
|
import { withCloudRunners } from "../../cloud-runners";
|
|
13
14
|
import { currentConfigRevision } from "../../config/commands";
|
|
@@ -48,12 +49,13 @@ export type SessionMonitor = {
|
|
|
48
49
|
function decorateSessions(
|
|
49
50
|
sessions: SessionsStatus["sessions"],
|
|
50
51
|
subscriptions: Set<string>,
|
|
52
|
+
inspection?: CatalogInspection,
|
|
51
53
|
): SessionsStatus["sessions"] {
|
|
52
54
|
const runtime = loadRuntimeConfig();
|
|
53
55
|
const visible = sessions.filter((session) =>
|
|
54
56
|
runtime.providerSettings[session.agent as keyof typeof runtime.providerSettings] !== false
|
|
55
57
|
);
|
|
56
|
-
const coordinated = runtime.meshEnabled ? meshCatalog(visible) : visible;
|
|
58
|
+
const coordinated = runtime.meshEnabled ? meshCatalog(visible, inspection) : visible;
|
|
57
59
|
return coordinated.map((session) => {
|
|
58
60
|
const base: SessionInfo = {
|
|
59
61
|
...session,
|
|
@@ -88,13 +90,17 @@ async function monitorSnapshot(input: {
|
|
|
88
90
|
if (loadRuntimeConfig().meshEnabled) localMeshStore().flush();
|
|
89
91
|
const { sessions, tokensRecent } = await scanSessionsCooperatively();
|
|
90
92
|
const runtime = loadRuntimeConfig();
|
|
93
|
+
const historical = includeHistory ? boundedCatalogHistory(await history(forceHistory), subscriptions) : undefined;
|
|
94
|
+
const all = [...sessions, ...(historical ?? [])].filter(session =>
|
|
95
|
+
runtime.providerSettings[session.agent as keyof typeof runtime.providerSettings] !== false);
|
|
96
|
+
if (all.some(session => session.agent === 'codex')) await refreshCodexMcpDescriptors();
|
|
97
|
+
const directories = all.flatMap(session => [session.worktree, session.cwd].filter((path): path is string => !!path));
|
|
98
|
+
const inspection = runtime.meshEnabled ? await prepareMonitorInspection(directories, forceHistory) : undefined;
|
|
91
99
|
return {
|
|
92
100
|
type: "sessions.status",
|
|
93
101
|
machine: computerDisplayName(),
|
|
94
|
-
sessions: decorateSessions(sessions, subscriptions),
|
|
95
|
-
history:
|
|
96
|
-
? decorateSessions(boundedCatalogHistory(await history(forceHistory), subscriptions), subscriptions)
|
|
97
|
-
: undefined,
|
|
102
|
+
sessions: decorateSessions(sessions, subscriptions, inspection),
|
|
103
|
+
history: historical ? decorateSessions(historical, subscriptions, inspection) : undefined,
|
|
98
104
|
tokensRecent,
|
|
99
105
|
tokenWindowHours: TOKEN_WINDOW_HOURS,
|
|
100
106
|
tokensAllTime: tokensRecent,
|
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.119
|
|
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.119\";\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.119";
|
|
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;
|