granttap-mcp 0.8.94 → 0.8.95
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/runtime/prepared-transfer/README.md +4 -0
- package/apps/bridge/src/mesh/runtime/prepared-transfer/local.ts +23 -0
- package/apps/bridge/src/mesh/runtime/prepared-transfer/prepare.ts +2 -0
- 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/packages/core/outbound-scheduler/README.md +2 -1
- package/packages/core/outbound-scheduler/index.ts +1 -1
|
@@ -25,6 +25,10 @@ provider. Rejected preparations also publish their retained Project state.
|
|
|
25
25
|
|
|
26
26
|
Capsule status is bounded independently from the complete conversation capture;
|
|
27
27
|
a long native summary cannot invalidate an otherwise transferable Git Task.
|
|
28
|
+
Local prepared handoffs persist their destination and validated receipt before
|
|
29
|
+
publishing to the phone. A blocked socket cannot hold the local preparation
|
|
30
|
+
behind a notification. Publication errors retain both durable events for the
|
|
31
|
+
next snapshot; they never replay the transfer or start a provider.
|
|
28
32
|
|
|
29
33
|
Tests: `tests/metadata.test.ts`, `tests/source-checkout.test.ts`,
|
|
30
34
|
`tests/mesh/mesh-handoff-safety.test.ts`, and catalog
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RelayClient } from '../../../../../../packages/core/relay-client';
|
|
2
|
+
import type { MeshEvent } from '../../../../../../packages/protocol/schema';
|
|
3
|
+
import type { MeshRuntimeDependencies } from '../dependencies';
|
|
4
|
+
import { createHandoffFlow } from '../handoff';
|
|
5
|
+
import { preparedHandoffMetadata } from '.';
|
|
6
|
+
|
|
7
|
+
/** Commit the local destination before phone publication can wait on a socket. */
|
|
8
|
+
export async function prepareLocalHandoff(deps: MeshRuntimeDependencies, client: RelayClient,
|
|
9
|
+
event: MeshEvent): Promise<boolean> {
|
|
10
|
+
const notifications: Parameters<MeshRuntimeDependencies['send']>[] = [];
|
|
11
|
+
const flow = createHandoffFlow({ ...deps, send: async (...notification) => { notifications.push(notification); } });
|
|
12
|
+
await flow.acceptHandoff(client, event);
|
|
13
|
+
const accepted = notifications.some(([, payload]) => payload.type === 'mesh.event'
|
|
14
|
+
&& payload.eventType === 'HANDOFF_ACCEPTED');
|
|
15
|
+
const request: Parameters<MeshRuntimeDependencies['send']> = [client, preparedHandoffMetadata(event),
|
|
16
|
+
{ ttlMs: 60 * 60_000, wake: true }];
|
|
17
|
+
for (const notification of [request, ...notifications]) {
|
|
18
|
+
// Both events are durable already. A transport error cannot undo the
|
|
19
|
+
// prepared chat or trigger a second preparation; retained snapshots recover it.
|
|
20
|
+
try { await deps.send(...notification); } catch { /* next connection publishes durable state */ }
|
|
21
|
+
}
|
|
22
|
+
return accepted;
|
|
23
|
+
}
|
|
@@ -6,6 +6,7 @@ import { prepareCloudHandoff } from "../cloud-handoff";
|
|
|
6
6
|
import { capturePreparedHandoff, preparedHandoffMetadata } from ".";
|
|
7
7
|
import { prepareSourceCapsule } from "./source";
|
|
8
8
|
import { resolveHandoffSource } from "./source-binding";
|
|
9
|
+
import { prepareLocalHandoff } from "./local";
|
|
9
10
|
|
|
10
11
|
export async function prepareHandoff(
|
|
11
12
|
deps: MeshRuntimeDependencies,
|
|
@@ -86,6 +87,7 @@ export async function prepareHandoff(
|
|
|
86
87
|
return false;
|
|
87
88
|
}
|
|
88
89
|
deps.store().acceptEvent(event);
|
|
90
|
+
if (local && request.prepareOnly) return prepareLocalHandoff(deps, client, event);
|
|
89
91
|
await deps.send(client, request.prepareOnly ? preparedHandoffMetadata(event) : event, { ttlMs: 60 * 60_000, wake: true });
|
|
90
92
|
if (local) await acceptHandoff(client, event);
|
|
91
93
|
return true;
|
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.95
|
|
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.95\";\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.95";
|
|
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;
|
package/package.json
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Public entry point: `index.ts`. Preserves per-peer encrypted envelopes while
|
|
4
4
|
pacing a connection below the managed relay's 300-frame-per-second admission
|
|
5
5
|
limit. Explicit approval and configuration messages take priority over queued
|
|
6
|
-
replaceable snapshots.
|
|
6
|
+
replaceable snapshots. Encrypted results explicitly marked for wake delivery
|
|
7
|
+
also precede replaceable snapshots, including sealed transfer receipts. TCP write completion, bounded queued bytes, cancellation
|
|
7
8
|
on disconnect, and a fresh queue on reconnect prevent stale bursts from crossing
|
|
8
9
|
connection generations. No message content or keys are logged.
|
|
9
10
|
|
|
@@ -83,6 +83,6 @@ export async function sendEncryptedPayload(queue: OutboundFrameQueue, config: Pe
|
|
|
83
83
|
if (!peers.length) throw new Error("no authorized pairing peers");
|
|
84
84
|
for (const peerPublicKey of peers) {
|
|
85
85
|
await queue.send(sealEnvelope({ ...config, peerPublicKey }, payload, to,
|
|
86
|
-
peers.length === 1 ? options : { ...options, deliveryId: undefined }), control(payload.type));
|
|
86
|
+
peers.length === 1 ? options : { ...options, deliveryId: undefined }), control(payload.type) || options.wake === true);
|
|
87
87
|
}
|
|
88
88
|
}
|