@humain/terminal 0.0.14 → 0.0.16
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/CHANGELOG.md +13 -0
- package/dist/bundle/chunks/{chunk-6TW5AYGH.js → chunk-Z4RA2T5Q.js} +252 -40
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +30 -9
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +27 -0
- package/dist/humain/mcp-adapter/vendor/README.md +13 -13
- package/dist/humain/mcp-adapter/vendor/commands.ts +58 -21
- package/dist/humain/mcp-adapter/vendor/config.ts +18 -4
- package/dist/humain/mcp-adapter/vendor/direct-tools.ts +26 -6
- package/dist/humain/mcp-adapter/vendor/dist/config.d.ts +4 -0
- package/dist/humain/mcp-adapter/vendor/dist/config.js +13 -4
- package/dist/humain/mcp-adapter/vendor/dist/config.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/dist/types.d.ts +11 -3
- package/dist/humain/mcp-adapter/vendor/dist/types.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/error-signal.ts +15 -4
- package/dist/humain/mcp-adapter/vendor/errors.ts +141 -0
- package/dist/humain/mcp-adapter/vendor/host-html-template.ts +58 -5
- package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +1017 -160
- package/dist/humain/mcp-adapter/vendor/index.ts +2 -3
- package/dist/humain/mcp-adapter/vendor/init.ts +5 -0
- package/dist/humain/mcp-adapter/vendor/mcp-panel.ts +30 -9
- package/dist/humain/mcp-adapter/vendor/mcp-setup-panel.ts +66 -28
- package/dist/humain/mcp-adapter/vendor/mcp-status.ts +2 -0
- package/dist/humain/mcp-adapter/vendor/package.json +3 -2
- package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +66 -13
- package/dist/humain/mcp-adapter/vendor/sandbox-proxy-template.ts +217 -0
- package/dist/humain/mcp-adapter/vendor/server-manager.ts +337 -6
- package/dist/humain/mcp-adapter/vendor/skills/mcp-scripting/SKILL.md +1 -0
- package/dist/humain/mcp-adapter/vendor/types.ts +18 -3
- package/dist/humain/mcp-adapter/vendor/ui-resource-handler.ts +18 -2
- package/dist/humain/mcp-adapter/vendor/ui-server.ts +179 -54
- package/dist/humain/mcp-adapter/vendor/ui-session.ts +28 -8
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/humain/mcp-adapter/UPSTREAM.md +1 -1
- package/src/humain/mcp-adapter/upstream.json +4 -4
- package/src/humain/mcp-adapter/vendor/CHANGELOG.md +27 -0
|
@@ -12,6 +12,11 @@ import type { ConsentManager } from "./consent-manager.ts";
|
|
|
12
12
|
import { ServerError, wrapError } from "./errors.ts";
|
|
13
13
|
import { formatAuthRequiredMessage, normalizeToolArguments } from "./utils.ts";
|
|
14
14
|
import { buildHostHtmlTemplate, buildCspMetaContent } from "./host-html-template.ts";
|
|
15
|
+
import {
|
|
16
|
+
buildSandboxProxyCsp,
|
|
17
|
+
buildSandboxProxyHtml,
|
|
18
|
+
SANDBOX_PROXY_PATH,
|
|
19
|
+
} from "./sandbox-proxy-template.ts";
|
|
15
20
|
import { logger } from "./logger.ts";
|
|
16
21
|
import type { McpServerManager } from "./server-manager.ts";
|
|
17
22
|
import type { McpExtensionState } from "./state.ts";
|
|
@@ -99,6 +104,11 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
99
104
|
const eventLog: Array<{ id: number; name: string; payload: unknown }> = [];
|
|
100
105
|
let latestCheckpointEventId: number | undefined;
|
|
101
106
|
let streamSummary: UiStreamSummary | undefined;
|
|
107
|
+
let server: http.Server | null = null;
|
|
108
|
+
let sandboxProxyServer: http.Server | null = null;
|
|
109
|
+
let sandboxProxyUrl: string | null = null;
|
|
110
|
+
let closeTimer: NodeJS.Timeout | null = null;
|
|
111
|
+
let listenersClosed = false;
|
|
102
112
|
|
|
103
113
|
// Track messages from UI for retrieval
|
|
104
114
|
const sessionMessages: UiSessionMessages = {
|
|
@@ -266,6 +276,32 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
266
276
|
watchdog = null;
|
|
267
277
|
};
|
|
268
278
|
|
|
279
|
+
const closeListeners = () => {
|
|
280
|
+
if (listenersClosed) return;
|
|
281
|
+
listenersClosed = true;
|
|
282
|
+
stopWatchdog();
|
|
283
|
+
if (closeTimer) {
|
|
284
|
+
clearTimeout(closeTimer);
|
|
285
|
+
closeTimer = null;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
server?.close();
|
|
289
|
+
} catch {}
|
|
290
|
+
try {
|
|
291
|
+
sandboxProxyServer?.close();
|
|
292
|
+
} catch {}
|
|
293
|
+
closeSse();
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const scheduleListenerClose = () => {
|
|
297
|
+
if (closeTimer || listenersClosed) return;
|
|
298
|
+
closeTimer = setTimeout(() => {
|
|
299
|
+
closeTimer = null;
|
|
300
|
+
closeListeners();
|
|
301
|
+
}, 20);
|
|
302
|
+
closeTimer.unref();
|
|
303
|
+
};
|
|
304
|
+
|
|
269
305
|
const markCompleted = (reason: string) => {
|
|
270
306
|
if (completed) return;
|
|
271
307
|
log.debug("Session completed", { reason });
|
|
@@ -273,9 +309,10 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
273
309
|
completed = true;
|
|
274
310
|
stopWatchdog();
|
|
275
311
|
options.onComplete?.(reason);
|
|
312
|
+
scheduleListenerClose();
|
|
276
313
|
};
|
|
277
314
|
|
|
278
|
-
const
|
|
315
|
+
const hostServer = http.createServer(async (req, res) => {
|
|
279
316
|
try {
|
|
280
317
|
const method = req.method || "GET";
|
|
281
318
|
const hostHeader = req.headers.host;
|
|
@@ -302,6 +339,10 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
302
339
|
}
|
|
303
340
|
if (!validateTokenQuery(url, sessionToken, res)) return;
|
|
304
341
|
touchHeartbeat();
|
|
342
|
+
if (!sandboxProxyUrl) {
|
|
343
|
+
sendText(res, 503, "Sandbox proxy is not ready");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
305
346
|
|
|
306
347
|
const html = buildHostHtmlTemplate({
|
|
307
348
|
sessionToken,
|
|
@@ -314,6 +355,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
314
355
|
requireToolConsent: options.consentManager.requiresPrompt(options.serverName),
|
|
315
356
|
cacheToolConsent: options.consentManager.shouldCacheConsent(),
|
|
316
357
|
hostContext,
|
|
358
|
+
sandboxProxyUrl,
|
|
317
359
|
});
|
|
318
360
|
|
|
319
361
|
res.writeHead(200, {
|
|
@@ -498,9 +540,15 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
498
540
|
...(options.onNeedsAuth ? { onNeedsAuth: options.onNeedsAuth } : {}),
|
|
499
541
|
},
|
|
500
542
|
options.serverName,
|
|
501
|
-
(conn) =>
|
|
543
|
+
async (conn) => {
|
|
544
|
+
await options.manager.ensureListen?.(options.serverName, conn);
|
|
545
|
+
return conn.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
|
|
546
|
+
},
|
|
502
547
|
)
|
|
503
|
-
: await
|
|
548
|
+
: await (async () => {
|
|
549
|
+
await options.manager.ensureListen?.(options.serverName, connection);
|
|
550
|
+
return connection.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
|
|
551
|
+
})();
|
|
504
552
|
sendJson(res, 200, { ok: true, result });
|
|
505
553
|
} finally {
|
|
506
554
|
options.manager.decrementInFlight(options.serverName);
|
|
@@ -614,12 +662,6 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
614
662
|
: "done";
|
|
615
663
|
markCompleted(reason);
|
|
616
664
|
sendJson(res, 200, { ok: true, result: {} });
|
|
617
|
-
setTimeout(() => {
|
|
618
|
-
try {
|
|
619
|
-
server.close();
|
|
620
|
-
} catch {}
|
|
621
|
-
closeSse();
|
|
622
|
-
}, 20).unref();
|
|
623
665
|
return;
|
|
624
666
|
}
|
|
625
667
|
|
|
@@ -641,6 +683,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
641
683
|
sendJson(res, status, { ok: false, error: wrapped.message });
|
|
642
684
|
}
|
|
643
685
|
});
|
|
686
|
+
server = hostServer;
|
|
644
687
|
|
|
645
688
|
if (options.initialResultPromise) {
|
|
646
689
|
options.initialResultPromise.then(
|
|
@@ -656,10 +699,6 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
656
699
|
if (completed) return;
|
|
657
700
|
if (Date.now() - lastHeartbeatAt <= ABANDONED_GRACE_MS) return;
|
|
658
701
|
markCompleted("stale");
|
|
659
|
-
try {
|
|
660
|
-
server.close();
|
|
661
|
-
} catch {}
|
|
662
|
-
closeSse();
|
|
663
702
|
}, WATCHDOG_INTERVAL_MS);
|
|
664
703
|
watchdog.unref();
|
|
665
704
|
|
|
@@ -667,13 +706,79 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
667
706
|
const candidates = resolvePortCandidates(options.port);
|
|
668
707
|
let candidateIndex = 0;
|
|
669
708
|
|
|
709
|
+
const startSandboxProxy = (parentOrigin: string): Promise<number> => {
|
|
710
|
+
const proxy = http.createServer((req, res) => {
|
|
711
|
+
try {
|
|
712
|
+
const method = req.method || "GET";
|
|
713
|
+
const hostHeader = req.headers.host;
|
|
714
|
+
const url = new URL(req.url || "/", `http://${hostHeader || "127.0.0.1"}`);
|
|
715
|
+
if (hostHeader !== undefined && !isAllowedHost(url.hostname)) {
|
|
716
|
+
sendText(res, 403, "Invalid host");
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (method === "HEAD" && url.pathname === SANDBOX_PROXY_PATH) {
|
|
721
|
+
res.writeHead(200, {
|
|
722
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
723
|
+
"Cache-Control": "no-store",
|
|
724
|
+
"Content-Security-Policy": buildSandboxProxyCsp(),
|
|
725
|
+
});
|
|
726
|
+
res.end();
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (method === "GET" && url.pathname === SANDBOX_PROXY_PATH) {
|
|
731
|
+
res.writeHead(200, {
|
|
732
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
733
|
+
"Cache-Control": "no-store",
|
|
734
|
+
"Content-Security-Policy": buildSandboxProxyCsp(),
|
|
735
|
+
"Referrer-Policy": "no-referrer",
|
|
736
|
+
"X-Content-Type-Options": "nosniff",
|
|
737
|
+
});
|
|
738
|
+
res.end(buildSandboxProxyHtml({ parentOrigin }));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
sendJson(res, 404, { ok: false, error: "Not found" });
|
|
743
|
+
} catch (error) {
|
|
744
|
+
sendJson(res, 500, { ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
sandboxProxyServer = proxy;
|
|
748
|
+
|
|
749
|
+
return new Promise((resolveProxy, rejectProxy) => {
|
|
750
|
+
const onError = (error: NodeJS.ErrnoException) => {
|
|
751
|
+
proxy.off("listening", onListening);
|
|
752
|
+
rejectProxy(error);
|
|
753
|
+
};
|
|
754
|
+
const onListening = () => {
|
|
755
|
+
proxy.off("error", onError);
|
|
756
|
+
const address = proxy.address();
|
|
757
|
+
if (!address || typeof address === "string") {
|
|
758
|
+
rejectProxy(new ServerError("invalid sandbox proxy address"));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
resolveProxy(address.port);
|
|
762
|
+
};
|
|
763
|
+
proxy.once("error", onError);
|
|
764
|
+
proxy.listen(0, "127.0.0.1", onListening);
|
|
765
|
+
});
|
|
766
|
+
};
|
|
767
|
+
|
|
670
768
|
const listen = () => {
|
|
671
|
-
|
|
672
|
-
|
|
769
|
+
const candidate = candidates[candidateIndex];
|
|
770
|
+
if (candidate === undefined) {
|
|
771
|
+
const error = new ServerError("no UI server port candidates available");
|
|
772
|
+
closeListeners();
|
|
773
|
+
reject(error);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
hostServer.once("error", onError);
|
|
777
|
+
hostServer.listen(candidate, "127.0.0.1", onListening);
|
|
673
778
|
};
|
|
674
779
|
|
|
675
780
|
const onError = (error: NodeJS.ErrnoException) => {
|
|
676
|
-
|
|
781
|
+
hostServer.off("listening", onListening);
|
|
677
782
|
if (error.code === "EADDRINUSE" && candidateIndex < candidates.length - 1) {
|
|
678
783
|
candidateIndex += 1;
|
|
679
784
|
listen();
|
|
@@ -681,6 +786,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
681
786
|
}
|
|
682
787
|
log.error("Failed to start server", error);
|
|
683
788
|
const port = candidates[candidateIndex];
|
|
789
|
+
closeListeners();
|
|
684
790
|
reject(new ServerError(error.message, {
|
|
685
791
|
...(port !== undefined ? { port } : {}),
|
|
686
792
|
cause: error,
|
|
@@ -688,52 +794,71 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
688
794
|
};
|
|
689
795
|
|
|
690
796
|
const onListening = () => {
|
|
691
|
-
|
|
692
|
-
const address =
|
|
797
|
+
hostServer.off("error", onError);
|
|
798
|
+
const address = hostServer.address();
|
|
693
799
|
if (!address || typeof address === "string") {
|
|
694
800
|
const err = new ServerError("invalid address");
|
|
695
801
|
log.error("Invalid server address", err);
|
|
802
|
+
closeListeners();
|
|
696
803
|
reject(err);
|
|
697
804
|
return;
|
|
698
805
|
}
|
|
699
806
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
807
|
+
const parentOrigin = `http://localhost:${address.port}`;
|
|
808
|
+
void startSandboxProxy(parentOrigin).then((proxyPort) => {
|
|
809
|
+
if (completed || listenersClosed) {
|
|
810
|
+
closeListeners();
|
|
811
|
+
reject(new ServerError("UI session completed before sandbox proxy was ready"));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
sandboxProxyUrl = `http://localhost:${proxyPort}${SANDBOX_PROXY_PATH}`;
|
|
815
|
+
log.debug("Servers started", { port: address.port, proxyPort });
|
|
816
|
+
rememberMoshiDiscoveryPort(address.port);
|
|
817
|
+
|
|
818
|
+
const handle: UiServerHandle = {
|
|
819
|
+
url: `http://localhost:${address.port}/?session=${sessionToken}`,
|
|
820
|
+
port: address.port,
|
|
821
|
+
proxyUrl: sandboxProxyUrl,
|
|
822
|
+
proxyPort,
|
|
823
|
+
sessionToken,
|
|
824
|
+
serverName: options.serverName,
|
|
825
|
+
toolName: options.toolName,
|
|
826
|
+
close: (reason?: string) => {
|
|
827
|
+
markCompleted(reason ?? "closed");
|
|
828
|
+
closeListeners();
|
|
829
|
+
},
|
|
830
|
+
sendToolInput: (args: Record<string, unknown>) => {
|
|
831
|
+
pushEvent("tool-input", { arguments: args });
|
|
832
|
+
},
|
|
833
|
+
sendToolResult: (result: CallToolResult) => {
|
|
834
|
+
pushEvent("tool-result", result);
|
|
835
|
+
},
|
|
836
|
+
sendResultPatch: (result: CallToolResult) => {
|
|
837
|
+
pushEvent("result-patch", result);
|
|
838
|
+
},
|
|
839
|
+
sendToolCancelled: (reason: string) => {
|
|
840
|
+
pushEvent("tool-cancelled", { reason });
|
|
841
|
+
},
|
|
842
|
+
sendResourceUpdated: (uri: string) => {
|
|
843
|
+
pushEvent("resource-updated", { uri });
|
|
844
|
+
},
|
|
845
|
+
sendHostContext: (context: UiHostContext) => {
|
|
846
|
+
Object.assign(hostContext, context);
|
|
847
|
+
pushEvent("host-context", context);
|
|
848
|
+
},
|
|
849
|
+
getSessionMessages: () => ({ ...sessionMessages }),
|
|
850
|
+
getStreamSummary: () => streamSummary ? { ...streamSummary, phases: [...streamSummary.phases] } : undefined,
|
|
851
|
+
};
|
|
735
852
|
|
|
736
|
-
|
|
853
|
+
resolve(handle);
|
|
854
|
+
}).catch((error) => {
|
|
855
|
+
log.error("Failed to start sandbox proxy", error instanceof Error ? error : undefined);
|
|
856
|
+
closeListeners();
|
|
857
|
+
const wrapped = error instanceof ServerError
|
|
858
|
+
? error
|
|
859
|
+
: new ServerError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
860
|
+
reject(wrapped);
|
|
861
|
+
});
|
|
737
862
|
};
|
|
738
863
|
|
|
739
864
|
listen();
|
|
@@ -21,6 +21,7 @@ import { isGlimpseAvailable, openGlimpseWindow } from "./glimpse-ui.ts";
|
|
|
21
21
|
import type { SessionRecoveryDeps } from "./session-recovery.ts";
|
|
22
22
|
import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
|
|
23
23
|
import { throwIfAborted } from "./abort.ts";
|
|
24
|
+
import { InputRequiredNeedsUiError } from "./errors.ts";
|
|
24
25
|
|
|
25
26
|
let activeGlimpseWindow: { close(): void } | null = null;
|
|
26
27
|
|
|
@@ -172,7 +173,7 @@ function probeMoshiGateway(): Promise<boolean> {
|
|
|
172
173
|
});
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
function remoteAccessHint(opts: { url: string; port: number; moshi: boolean; openError: string | null; openedOnHost?: boolean }): string {
|
|
176
|
+
function remoteAccessHint(opts: { url: string; port: number; proxyPort: number; moshi: boolean; openError: string | null; openedOnHost?: boolean }): string {
|
|
176
177
|
const lines = [
|
|
177
178
|
opts.openError !== null
|
|
178
179
|
? "Couldn't open MCP UI here. Open it from your own device:"
|
|
@@ -185,10 +186,12 @@ function remoteAccessHint(opts: { url: string; port: number; moshi: boolean; ope
|
|
|
185
186
|
lines.push(`Browser launch failed: ${opts.openError}`);
|
|
186
187
|
}
|
|
187
188
|
if (opts.moshi) {
|
|
188
|
-
lines.push(
|
|
189
|
+
lines.push(
|
|
190
|
+
`Moshi: tap the preview button in the terminal title bar and pick this MCP UI server (it must reach ports ${opts.port} and ${opts.proxyPort}).`,
|
|
191
|
+
);
|
|
189
192
|
}
|
|
190
193
|
lines.push(
|
|
191
|
-
`SSH: run \`ssh -L ${opts.port}:127.0.0.1:${opts.port} <this-host>\` on your local machine, then open the URL above.`,
|
|
194
|
+
`SSH: run \`ssh -L ${opts.port}:127.0.0.1:${opts.port} -L ${opts.proxyPort}:127.0.0.1:${opts.proxyPort} <this-host>\` on your local machine, then open the URL above.`,
|
|
192
195
|
"mosh can't forward ports - run that ssh command in a separate terminal.",
|
|
193
196
|
);
|
|
194
197
|
return lines.join("\n");
|
|
@@ -314,11 +317,13 @@ export async function maybeStartUiSession(
|
|
|
314
317
|
let active = true;
|
|
315
318
|
let nextStreamSequence = 0;
|
|
316
319
|
let handle: UiServerHandle;
|
|
320
|
+
const resourceListenerToken = randomUUID();
|
|
317
321
|
|
|
318
|
-
const
|
|
322
|
+
const cleanupListeners = () => {
|
|
319
323
|
if (streamToken) {
|
|
320
324
|
state.manager.removeUiStreamListener(streamToken);
|
|
321
325
|
}
|
|
326
|
+
state.manager.removeResourceUpdatedListener?.(resourceListenerToken);
|
|
322
327
|
};
|
|
323
328
|
|
|
324
329
|
handle = await startUiServer({
|
|
@@ -394,7 +399,7 @@ export async function maybeStartUiSession(
|
|
|
394
399
|
|
|
395
400
|
onComplete: (reason: string) => {
|
|
396
401
|
active = false;
|
|
397
|
-
|
|
402
|
+
cleanupListeners();
|
|
398
403
|
|
|
399
404
|
if (state.uiServer === handle) {
|
|
400
405
|
const messages = handle.getSessionMessages();
|
|
@@ -454,6 +459,16 @@ export async function maybeStartUiSession(
|
|
|
454
459
|
});
|
|
455
460
|
}
|
|
456
461
|
|
|
462
|
+
state.manager.registerResourceUpdatedListener?.(
|
|
463
|
+
resourceListenerToken,
|
|
464
|
+
request.serverName,
|
|
465
|
+
request.uiResourceUri,
|
|
466
|
+
(_serverName, uri) => {
|
|
467
|
+
if (!active || state.uiServer !== handle) return;
|
|
468
|
+
handle.sendResourceUpdated(uri);
|
|
469
|
+
},
|
|
470
|
+
);
|
|
471
|
+
|
|
457
472
|
state.uiServer = handle;
|
|
458
473
|
|
|
459
474
|
const viewerPref = process.env.MCP_UI_VIEWER?.toLowerCase();
|
|
@@ -466,7 +481,11 @@ export async function maybeStartUiSession(
|
|
|
466
481
|
if (uiSuppressed) {
|
|
467
482
|
viewer = "suppressed";
|
|
468
483
|
windowOpen = false;
|
|
469
|
-
state.ui?.notify(
|
|
484
|
+
state.ui?.notify(
|
|
485
|
+
`MCP UI window suppressed (MCP_UI_VIEWER=${viewerPref}). Open manually: ${handle.url}\n` +
|
|
486
|
+
`If this session is remote, run ssh -L ${handle.port}:127.0.0.1:${handle.port} -L ${handle.proxyPort}:127.0.0.1:${handle.proxyPort} <this-host> first.`,
|
|
487
|
+
"info",
|
|
488
|
+
);
|
|
470
489
|
log.info("Suppressing MCP UI window (MCP_UI_VIEWER=" + viewerPref + ")", { url: handle.url });
|
|
471
490
|
} else {
|
|
472
491
|
const remoteLikely = remoteByEnv || await hasActiveRemoteLogin();
|
|
@@ -474,6 +493,7 @@ export async function maybeStartUiSession(
|
|
|
474
493
|
state.ui?.notify(remoteAccessHint({
|
|
475
494
|
url: handle.url,
|
|
476
495
|
port: handle.port,
|
|
496
|
+
proxyPort: handle.proxyPort,
|
|
477
497
|
moshi: await probeMoshiGateway(),
|
|
478
498
|
openError,
|
|
479
499
|
openedOnHost,
|
|
@@ -554,12 +574,12 @@ export async function maybeStartUiSession(
|
|
|
554
574
|
},
|
|
555
575
|
close: (reason?: string) => {
|
|
556
576
|
active = false;
|
|
557
|
-
|
|
577
|
+
cleanupListeners();
|
|
558
578
|
handle.close(reason);
|
|
559
579
|
},
|
|
560
580
|
};
|
|
561
581
|
} catch (error) {
|
|
562
|
-
if (error instanceof UrlElicitationRequiredError || isAbortError(error, runtimeSignal)) throw error;
|
|
582
|
+
if (error instanceof UrlElicitationRequiredError || error instanceof InputRequiredNeedsUiError || isAbortError(error, runtimeSignal)) throw error;
|
|
563
583
|
const message = error instanceof Error ? error.message : String(error);
|
|
564
584
|
log.error("Failed to start UI session", error instanceof Error ? error : undefined);
|
|
565
585
|
state.ui?.notify(
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humain/terminal",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@humain/terminal",
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.16",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@humain/terminal-contracts": "0.0.3",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pi-mcp-adapter upstream tracking
|
|
2
2
|
|
|
3
3
|
HUMAIN Terminal vendors [`pi-mcp-adapter`](https://github.com/nicobailon/pi-mcp-adapter)
|
|
4
|
-
version `2.
|
|
4
|
+
version `2.32.1` so MCP support is available without a separate VM installation.
|
|
5
5
|
|
|
6
6
|
The unmodified upstream package is recorded in `upstream.json`, including the
|
|
7
7
|
registry integrity and source `gitHead`. The release currentness gate resolves
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mcp-adapter",
|
|
3
3
|
"repository": "https://github.com/nicobailon/pi-mcp-adapter",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"tarball": "https://registry.npmjs.org/pi-mcp-adapter/-/pi-mcp-adapter-2.
|
|
6
|
-
"integrity": "sha512-
|
|
7
|
-
"gitHead": "
|
|
4
|
+
"version": "2.32.1",
|
|
5
|
+
"tarball": "https://registry.npmjs.org/pi-mcp-adapter/-/pi-mcp-adapter-2.32.1.tgz",
|
|
6
|
+
"integrity": "sha512-GNLYa2U9T5ZqIhZmhx/RTenEjfakJTelq/z6Q+At5SIxyuYvrvobriDEVsnx+lqetVDizUCudTWLfdZytQu0rg==",
|
|
7
|
+
"gitHead": "10a45367e033a32026987a75d6f401e37340c86f"
|
|
8
8
|
}
|
|
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.32.1] - 2026-09-01
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- The published package now includes the updated public helper build artifacts for the `2.32.0` config and MCP Apps changes.
|
|
14
|
+
|
|
15
|
+
## [2.32.0] - 2026-09-01
|
|
16
|
+
|
|
17
|
+
### Highlights
|
|
18
|
+
- You can now enable and disable MCP servers directly from the `/mcp` panel.
|
|
19
|
+
- `/mcp setup` is clearer about where new shared servers will be saved.
|
|
20
|
+
- MCP App views that use browser storage now render reliably without exposing host session access.
|
|
21
|
+
- Long-running sessions handle MCP 2026 input flows, catalog updates, and UI resource refreshes more reliably.
|
|
22
|
+
- OAuth reuse and per-server status messages are less confusing.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- The `/mcp` panel now supports enabling and disabling servers in place with `ctrl+d` on a server row. Saving persists the `disabled` flag to the project Pi layer and reloads the session, matching `/mcp disable` and `/mcp enable`. Thanks to [@ericykim](https://github.com/ericykim) for PR #479.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- `/mcp setup` now lets you choose project `.mcp.json` or global `~/.config/mcp/mcp.json` as the write target for new shared MCP servers, while keeping Pi-owned files and compatibility inputs in the advanced flow. The bundled `mcp-scripting` skill is manual-only by default. Thanks to [@w-winter](https://github.com/w-winter) for #477.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- MCP 2026 multi-round input flows now work more reliably across proxy, direct, resource, and UI-resource calls, with clearer no-UI errors and cancellation cleanup.
|
|
32
|
+
- MCP 2026-07-28 catalog listens now recover from dropped listens, refresh quietly when catalogs change, and notify open UIs when resources update. (#468)
|
|
33
|
+
- MCP App views now load through a separate loopback sandbox proxy origin so storage APIs work without exposing host session capabilities. Thanks to [@drewbitt](https://github.com/drewbitt) for #480.
|
|
34
|
+
- Implicit OAuth now reuses URL-bound stored credentials while preserving anonymous fallback. Thanks to [@wilt00](https://github.com/wilt00) for #471.
|
|
35
|
+
- Per-server proxy lists now distinguish cached lazy tools from servers that need authentication while preserving active failure backoff. Thanks to [@inattendu](https://github.com/inattendu) for PR #474.
|
|
36
|
+
|
|
10
37
|
## [2.31.0] - 2026-08-28
|
|
11
38
|
|
|
12
39
|
### Highlights
|