granttap-mcp 0.8.109 → 0.8.110
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/device-management/index.ts +2 -1
- package/apps/bridge/src/device-management/login.ts +10 -1
- package/cursor-plugin/README.md +1 -1
- package/cursor-plugin/mcp.json +1 -1
- package/cursor-plugin/stdio-bootstrap.cjs +1 -1
- package/docs/device-management.md +5 -1
- package/package.json +1 -1
|
@@ -23,7 +23,7 @@ export async function handleDeviceManagement(client: RelayClient, request: Devic
|
|
|
23
23
|
const computerId = observedComputerId();
|
|
24
24
|
if (request.computerId != null && request.computerId !== computerId) return false;
|
|
25
25
|
// Sign-in material is deliberately non-durable and addressed only to the requesting paired controller.
|
|
26
|
-
const send: Send = value => client.sendToPeer(value, peer, { reliable: false, ttlMs: 60_000 });
|
|
26
|
+
const send: Send = value => client.sendToPeer(value, peer, { reliable: false, urgent: true, ttlMs: 60_000 });
|
|
27
27
|
const key = client.room + ':' + peer + ':' + request.requestId;
|
|
28
28
|
const fingerprint = createHash('sha256').update(JSON.stringify(request)).digest('hex');
|
|
29
29
|
for (const [k, value] of requests) if (Date.now() - value.at > value.ttl && !value.pending) requests.delete(k);
|
|
@@ -66,6 +66,7 @@ async function execute(request: DeviceManagementRequest, scope: string, computer
|
|
|
66
66
|
await send({ ...result('waiting', login.message), login }); return;
|
|
67
67
|
}
|
|
68
68
|
await send(result('running', request.action === 'status' ? 'Reading this computer…' : 'Working on this computer…'));
|
|
69
|
+
if (request.action === 'status') providerLogins.replay(scope);
|
|
69
70
|
let message = 'Computer status refreshed.';
|
|
70
71
|
if (request.action === 'clear-cache') {
|
|
71
72
|
if (!maintenanceIdle()) throw Error('Finish active requests and sign-in before clearing computer caches.');
|
|
@@ -16,6 +16,13 @@ export class ProviderLogins {
|
|
|
16
16
|
|
|
17
17
|
active(): boolean { return [...this.flows.values()].some(f => !terminal.has(f.progress.phase)); }
|
|
18
18
|
|
|
19
|
+
/** A phone can leave for Safari while its final event is dropped. Status refresh replays only its latest flows. */
|
|
20
|
+
replay(scope: string): void {
|
|
21
|
+
const latest = new Map<CodingAgent, Flow>();
|
|
22
|
+
for (const flow of this.flows.values()) if (flow.scope === scope) latest.set(flow.progress.agent, flow);
|
|
23
|
+
for (const flow of latest.values()) flow.emit(flow.progress);
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
start(scope: string, agent: CodingAgent, emit: Flow['emit']): ProviderLoginProgress {
|
|
20
27
|
const existing = [...this.flows.values()].find(f => f.scope === scope && f.progress.agent === agent
|
|
21
28
|
&& !terminal.has(f.progress.phase));
|
|
@@ -104,7 +111,9 @@ export class ProviderLogins {
|
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
private prune(): void {
|
|
107
|
-
for (const [id, f] of this.flows)
|
|
114
|
+
for (const [id, f] of this.flows) {
|
|
115
|
+
if (terminal.has(f.progress.phase) && this.now() >= f.progress.expiresAt) this.flows.delete(id);
|
|
116
|
+
}
|
|
108
117
|
}
|
|
109
118
|
}
|
|
110
119
|
export const providerLogins = new ProviderLogins();
|
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.110
|
|
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.110\";\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.110";
|
|
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;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Computer controls from GrantTap
|
|
2
2
|
|
|
3
|
-
MCP 0.8.
|
|
3
|
+
MCP 0.8.110 adds the paired-controller `device.manage` protocol. The iOS client
|
|
4
4
|
must also support these controls. A computer without an installed, connected
|
|
5
5
|
MCP cannot execute remote commands; install and pair MCP on that computer first.
|
|
6
6
|
|
|
@@ -43,3 +43,7 @@ without durable mailbox storage.
|
|
|
43
43
|
Regression tests cover controller/room/computer isolation, request idempotency,
|
|
44
44
|
native login challenge formats, code ownership/cancellation/expiry, UTF-8
|
|
45
45
|
streams, launchd Node PATH recovery and preservation during cache cleanup.
|
|
46
|
+
|
|
47
|
+
The latest sign-in state is replayed privately when its controller refreshes status.
|
|
48
|
+
This restores completion after a phone returns from the browser without starting
|
|
49
|
+
another login, resending a prompt, or retaining terminal authorization codes.
|