@nudge-ui/mcp 0.2.3 → 0.2.5
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 +18 -0
- package/dist/bridge.d.ts +6 -52
- package/dist/bridge.js +292 -802
- package/dist/bridgeSession.d.ts +96 -0
- package/dist/bridgeSession.js +438 -0
- package/dist/cli.d.ts +3 -29
- package/dist/cli.js +54 -168
- package/dist/cli.mjs +237 -1372
- package/dist/discovery.d.ts +5 -6
- package/dist/discovery.js +5 -8
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/internal/agent-protocol.d.ts +50 -13
- package/dist/internal/agent-protocol.js +145 -19
- package/dist/project.d.ts +1 -1
- package/dist/project.js +2 -2
- package/dist/projectSessionClient.d.ts +5 -4
- package/dist/projectSessionClient.js +38 -13
- package/dist/protocol.d.ts +2 -1
- package/dist/protocol.js +2 -1
- package/dist/server.d.ts +3 -27
- package/dist/server.js +65 -136
- package/dist/sessionRegistry.js +1 -21
- package/package.json +1 -16
package/dist/cli.js
CHANGED
|
@@ -1,48 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { basename, resolve } from "node:path";
|
|
4
|
-
import { defaultBridgePort } from "./internal/agent-protocol.js";
|
|
1
|
+
import { resolve } from "node:path";
|
|
5
2
|
import { diagnoseProjectSessions, discoverProjectSessions } from "./discovery.js";
|
|
6
|
-
import {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const response = await dependencies.fetch(pageUrl, {
|
|
20
|
-
method: "GET",
|
|
21
|
-
signal: AbortSignal.timeout(PAGE_PROBE_TIMEOUT_MS),
|
|
22
|
-
});
|
|
23
|
-
if (!response.ok)
|
|
24
|
-
throw new Error(`page returned ${response.status}`);
|
|
25
|
-
const command = dependencies.platform === "darwin"
|
|
26
|
-
? { executable: "open", args: [pageUrl] }
|
|
27
|
-
: dependencies.platform === "win32"
|
|
28
|
-
? { executable: "cmd", args: ["/c", "start", "", pageUrl] }
|
|
29
|
-
: { executable: "xdg-open", args: [pageUrl] };
|
|
30
|
-
const child = dependencies.spawn(command.executable, command.args, {
|
|
31
|
-
detached: true,
|
|
32
|
-
stdio: "ignore",
|
|
33
|
-
});
|
|
34
|
-
child.unref();
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
const detail = error instanceof Error ? ` (${error.message})` : "";
|
|
39
|
-
dependencies.warn(`[nudge-ui] paired page is unavailable${detail}; open ${pageUrl} manually`);
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function projectIdFor(root) {
|
|
44
|
-
const name = basename(root);
|
|
45
|
-
return name.length > 0 ? name : `project-${createHash("sha256").update(root).digest("hex").slice(0, 24)}`;
|
|
3
|
+
import { createDiscoveredAgentAdapter } from "./server.js";
|
|
4
|
+
const REMOVED_OPTIONS = new Set(["--origin", "--project-id", "--host", "--port", "--command-timeout-ms", "--workspace-root"]);
|
|
5
|
+
const REMOVED_ENVIRONMENT = [
|
|
6
|
+
"NUDGE_UI_PROJECT_ID",
|
|
7
|
+
"NUDGE_UI_ORIGIN",
|
|
8
|
+
"NUDGE_UI_WORKSPACE_ROOT",
|
|
9
|
+
"NUDGE_UI_BRIDGE_HOST",
|
|
10
|
+
"NUDGE_UI_BRIDGE_PORT",
|
|
11
|
+
"NUDGE_UI_COMMAND_TIMEOUT_MS",
|
|
12
|
+
];
|
|
13
|
+
function removedConfiguration(name) {
|
|
14
|
+
return new TypeError(`${name} configured the removed coupled or startup-workspace mode. Rerun \`npx nudge-ui agent setup\` in the project to register the reusable adapter.`);
|
|
46
15
|
}
|
|
47
16
|
function valueAfter(argv, index, flag) {
|
|
48
17
|
const value = argv[index + 1];
|
|
@@ -50,93 +19,42 @@ function valueAfter(argv, index, flag) {
|
|
|
50
19
|
throw new TypeError(`${flag} requires a value`);
|
|
51
20
|
return value;
|
|
52
21
|
}
|
|
53
|
-
|
|
54
|
-
if (!/^\d+$/.test(value))
|
|
55
|
-
throw new TypeError(`${flag} must be a non-negative integer`);
|
|
56
|
-
const parsed = Number(value);
|
|
57
|
-
if (!Number.isSafeInteger(parsed) || parsed > maximum)
|
|
58
|
-
throw new TypeError(`${flag} is out of range`);
|
|
59
|
-
return parsed;
|
|
60
|
-
}
|
|
61
|
-
/** Parses only project-scoped, host-neutral CLI options. */
|
|
22
|
+
/** Serving takes no options; `doctor` accepts a diagnostic scope. */
|
|
62
23
|
export function parseCliArguments(argv = process.argv.slice(2), environment = process.env) {
|
|
63
24
|
const command = argv[0] === "doctor" ? "doctor" : "serve";
|
|
64
25
|
const argumentsToParse = command === "doctor" ? argv.slice(1) : argv;
|
|
65
|
-
|
|
66
|
-
let projectId = environment.NUDGE_UI_PROJECT_ID ?? projectIdFor(root);
|
|
67
|
-
let origin = environment.NUDGE_UI_ORIGIN;
|
|
68
|
-
// SAFETY: NUDGE_UI_BRIDGE_HOST is trusted operator config for a loopback-bound dev bridge; CliOptions["host"] is its declared address union.
|
|
69
|
-
let host = (environment.NUDGE_UI_BRIDGE_HOST ?? "127.0.0.1");
|
|
70
|
-
let port = environment.NUDGE_UI_BRIDGE_PORT === undefined
|
|
71
|
-
? DEFAULT_PORT
|
|
72
|
-
: parseInteger(environment.NUDGE_UI_BRIDGE_PORT, "NUDGE_UI_BRIDGE_PORT", 65_535);
|
|
73
|
-
let commandTimeoutMs = environment.NUDGE_UI_COMMAND_TIMEOUT_MS === undefined
|
|
74
|
-
? DEFAULT_COMMAND_TIMEOUT_MS
|
|
75
|
-
: parseInteger(environment.NUDGE_UI_COMMAND_TIMEOUT_MS, "NUDGE_UI_COMMAND_TIMEOUT_MS", 60_000);
|
|
76
|
-
let workspaceRoot = environment.NUDGE_UI_WORKSPACE_ROOT === undefined ? undefined : root;
|
|
26
|
+
let workspaceRoot;
|
|
77
27
|
let help = false;
|
|
78
|
-
let explicitPort = environment.NUDGE_UI_BRIDGE_PORT !== undefined;
|
|
79
28
|
for (let index = 0; index < argumentsToParse.length; index += 1) {
|
|
80
29
|
const argument = argumentsToParse[index];
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
break;
|
|
94
|
-
case "--workspace-root":
|
|
95
|
-
workspaceRoot = resolve(valueAfter(argumentsToParse, index, argument));
|
|
96
|
-
index += 1;
|
|
97
|
-
break;
|
|
98
|
-
case "--host": {
|
|
99
|
-
const candidate = valueAfter(argumentsToParse, index, argument);
|
|
100
|
-
if (candidate !== "127.0.0.1" && candidate !== "::1" && candidate !== "localhost") {
|
|
101
|
-
throw new TypeError("--host must be 127.0.0.1, ::1, or localhost");
|
|
102
|
-
}
|
|
103
|
-
host = candidate;
|
|
104
|
-
index += 1;
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
case "--port":
|
|
108
|
-
port = parseInteger(valueAfter(argumentsToParse, index, argument), argument, 65_535);
|
|
109
|
-
explicitPort = true;
|
|
110
|
-
index += 1;
|
|
111
|
-
break;
|
|
112
|
-
case "--command-timeout-ms":
|
|
113
|
-
commandTimeoutMs = parseInteger(valueAfter(argumentsToParse, index, argument), argument, 60_000);
|
|
114
|
-
if (commandTimeoutMs === 0)
|
|
115
|
-
throw new TypeError("--command-timeout-ms must be greater than zero");
|
|
116
|
-
index += 1;
|
|
117
|
-
break;
|
|
118
|
-
default:
|
|
119
|
-
throw new TypeError(`Unknown option ${argument}`);
|
|
30
|
+
if (argument === "--help" || argument === "-h") {
|
|
31
|
+
help = true;
|
|
32
|
+
}
|
|
33
|
+
else if (command === "doctor" && argument === "--workspace-root") {
|
|
34
|
+
workspaceRoot = resolve(valueAfter(argumentsToParse, index, argument));
|
|
35
|
+
index += 1;
|
|
36
|
+
}
|
|
37
|
+
else if (REMOVED_OPTIONS.has(argument)) {
|
|
38
|
+
throw removedConfiguration(argument);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
throw new TypeError(`Unknown option ${argument}`);
|
|
120
42
|
}
|
|
121
43
|
}
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
44
|
+
if (command === "serve") {
|
|
45
|
+
const removed = REMOVED_ENVIRONMENT.find((name) => environment[name] !== undefined);
|
|
46
|
+
if (removed)
|
|
47
|
+
throw removedConfiguration(removed);
|
|
48
|
+
}
|
|
49
|
+
return { command, ...(workspaceRoot === undefined ? {} : { workspaceRoot }), help };
|
|
127
50
|
}
|
|
128
51
|
export const CLI_USAGE = `Usage: nudge-mcp [doctor] [options]
|
|
129
52
|
|
|
130
|
-
Starts a standard MCP stdio adapter
|
|
131
|
-
|
|
53
|
+
Starts a standard MCP stdio adapter that discovers the bridges owned by running
|
|
54
|
+
projects. Every tool call names the checkout or application it targets.
|
|
132
55
|
|
|
133
56
|
Options:
|
|
134
|
-
--
|
|
135
|
-
--origin <origin> Exact app origin required for browser pairing
|
|
136
|
-
--workspace-root <path> Optional legacy project restriction; omitted for reusable adapters
|
|
137
|
-
--host <loopback> 127.0.0.1, ::1, or localhost
|
|
138
|
-
--port <port> Bridge port; default is deterministic per project, 0 is ephemeral
|
|
139
|
-
--command-timeout-ms <ms> Canvas acknowledgement timeout
|
|
57
|
+
--workspace-root <path> doctor only: limit diagnostics to one checkout or application
|
|
140
58
|
--help Show this message
|
|
141
59
|
`;
|
|
142
60
|
/** Runs the CLI until the stdio MCP connection closes or the process stops. */
|
|
@@ -162,74 +80,42 @@ export async function runCli(argv = process.argv.slice(2), environment = process
|
|
|
162
80
|
...(matches.length === 0
|
|
163
81
|
? ["[nudge-ui] no reachable compatible bridge matches; inspect the diagnostics above or start the development server"]
|
|
164
82
|
: matches.map((session) => {
|
|
165
|
-
const state = session.status.connection === "
|
|
166
|
-
? "
|
|
167
|
-
: session.status.
|
|
83
|
+
const state = session.status.connection === "delivered"
|
|
84
|
+
? "awaiting acknowledgment"
|
|
85
|
+
: session.status.connection === "working"
|
|
86
|
+
? "working"
|
|
87
|
+
: session.status.listenerActive ? "listening" : "available";
|
|
168
88
|
return `[nudge-ui] ${state}: ${session.projectId} (${session.origin})${session.claimed ? " [claimed]" : ""}`;
|
|
169
89
|
})),
|
|
170
90
|
].join("\n") + "\n");
|
|
171
91
|
return;
|
|
172
92
|
}
|
|
173
|
-
|
|
174
|
-
const adapter = await createDiscoveredAgentAdapter({ workspaceRoot: options.workspaceRoot });
|
|
175
|
-
let stopping = false;
|
|
176
|
-
let resolveAdapterWait = null;
|
|
177
|
-
const stop = () => {
|
|
178
|
-
if (stopping)
|
|
179
|
-
return;
|
|
180
|
-
stopping = true;
|
|
181
|
-
void adapter.close();
|
|
182
|
-
resolveAdapterWait?.();
|
|
183
|
-
};
|
|
184
|
-
process.once("SIGINT", stop);
|
|
185
|
-
process.once("SIGTERM", stop);
|
|
186
|
-
try {
|
|
187
|
-
await adapter.start();
|
|
188
|
-
await new Promise((resolveAdapter) => {
|
|
189
|
-
const finish = () => resolveAdapter();
|
|
190
|
-
// Keep a direct resolver so signal handlers terminate even though
|
|
191
|
-
// installing a signal handler suppresses Node's default process exit.
|
|
192
|
-
resolveAdapterWait = finish;
|
|
193
|
-
process.stdin.once("end", finish);
|
|
194
|
-
process.once("exit", finish);
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
finally {
|
|
198
|
-
process.off("SIGINT", stop);
|
|
199
|
-
process.off("SIGTERM", stop);
|
|
200
|
-
await adapter.close();
|
|
201
|
-
}
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
process.stderr.write("[nudge-ui] --origin uses deprecated coupled mode. Rerun agent setup to use project-owned bridges and avoid port collisions.\n");
|
|
205
|
-
const companion = createAgentCompanion({
|
|
206
|
-
...options,
|
|
207
|
-
workspaceRoot: options.workspaceRoot ?? resolve(environment.INIT_CWD ?? process.cwd()),
|
|
208
|
-
onControllerUnavailable: (pageUrl) => openPairedPage(pageUrl).then(() => undefined),
|
|
209
|
-
});
|
|
93
|
+
const adapter = createDiscoveredAgentAdapter();
|
|
210
94
|
let stopping = false;
|
|
95
|
+
let resolveAdapterWait = null;
|
|
211
96
|
const stop = () => {
|
|
212
97
|
if (stopping)
|
|
213
98
|
return;
|
|
214
99
|
stopping = true;
|
|
215
|
-
void
|
|
100
|
+
void adapter.close();
|
|
101
|
+
resolveAdapterWait?.();
|
|
216
102
|
};
|
|
217
103
|
process.once("SIGINT", stop);
|
|
218
104
|
process.once("SIGTERM", stop);
|
|
219
105
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
process.once("exit",
|
|
106
|
+
await adapter.start();
|
|
107
|
+
await new Promise((resolveAdapter) => {
|
|
108
|
+
const finish = () => resolveAdapter();
|
|
109
|
+
// Keep a direct resolver so signal handlers terminate even though
|
|
110
|
+
// installing a signal handler suppresses Node's default process exit.
|
|
111
|
+
resolveAdapterWait = finish;
|
|
112
|
+
process.stdin.once("end", finish);
|
|
113
|
+
process.once("exit", finish);
|
|
228
114
|
});
|
|
229
115
|
}
|
|
230
116
|
finally {
|
|
231
117
|
process.off("SIGINT", stop);
|
|
232
118
|
process.off("SIGTERM", stop);
|
|
233
|
-
await
|
|
119
|
+
await adapter.close();
|
|
234
120
|
}
|
|
235
121
|
}
|