@openhands/agent-canvas 1.0.0-rc.3 → 1.0.0-rc.4
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 +2 -2
- package/README.windows.md +2 -2
- package/build/assets/{analytics-consent-form-modal-Ce-_Skcd.js → analytics-consent-form-modal-DIfCKp6g.js} +1 -1
- package/build/assets/{automations-list-Ces5shz5.js → automations-list-CoYj4o97.js} +1 -1
- package/build/assets/{home-sJAFzI6v.js → home-D77Xasws.js} +1 -1
- package/build/assets/install-server-modal-BjmEphcZ.js +1 -0
- package/build/assets/{manifest-d3e8504d.js → manifest-510dd26f.js} +1 -1
- package/build/assets/{mcp-kJYdM8aJ.js → mcp-CiokQDek.js} +2 -2
- package/build/assets/{onboarding-CZ_7nd-M.js → onboarding-CGUaLHfQ.js} +1 -1
- package/build/assets/{recommended-automations-launcher-B1kfmFTQ.js → recommended-automations-launcher-C1_CKGHa.js} +1 -1
- package/build/assets/{root-CIZ7Rv1X.js → root-DXsSPxni.js} +1 -1
- package/build/assets/{root-layout-B5ijp9At.js → root-layout-N4s3RLlj.js} +2 -2
- package/build/assets/{telemetry-j9SLrtY7.js → telemetry-BoedM0mF.js} +1 -1
- package/build/index.html +3 -3
- package/config/defaults.json +2 -6
- package/dist/api/mcp-service/mcp-service.api.cjs +1 -1
- package/dist/api/mcp-service/mcp-service.api.cjs.map +1 -1
- package/dist/api/mcp-service/mcp-service.api.js +19 -14
- package/dist/api/mcp-service/mcp-service.api.js.map +1 -1
- package/dist/components/features/mcp-page/custom-server-editor.cjs +1 -1
- package/dist/components/features/mcp-page/custom-server-editor.cjs.map +1 -1
- package/dist/components/features/mcp-page/custom-server-editor.js +63 -62
- package/dist/components/features/mcp-page/custom-server-editor.js.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +4 -2
- package/dist/package.js.map +1 -1
- package/package.json +4 -2
- package/scripts/check-sdk-version-sync.mjs +6 -6
- package/scripts/dev-safe.mjs +32 -8
- package/scripts/dev-with-automation.mjs +25 -1
- package/scripts/logger.mjs +77 -0
- package/build/assets/install-server-modal-BjEzlLF5.js +0 -1
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Usage:
|
|
21
21
|
* node scripts/check-sdk-version-sync.mjs
|
|
22
|
-
* EXPECTED_SDK_VERSION=1.
|
|
22
|
+
* EXPECTED_SDK_VERSION=1.26.0 node scripts/check-sdk-version-sync.mjs
|
|
23
23
|
* node scripts/check-sdk-version-sync.mjs --check-pypi
|
|
24
24
|
*
|
|
25
25
|
* Environment variables:
|
|
@@ -79,7 +79,7 @@ Triggering from other repos:
|
|
|
79
79
|
-H "Authorization: token \$GITHUB_TOKEN" \\
|
|
80
80
|
-H "Accept: application/vnd.github.v3+json" \\
|
|
81
81
|
https://api.github.com/repos/OpenHands/agent-canvas/dispatches \\
|
|
82
|
-
-d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.
|
|
82
|
+
-d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.26.0"}}'
|
|
83
83
|
`);
|
|
84
84
|
process.exit(0);
|
|
85
85
|
}
|
|
@@ -268,9 +268,9 @@ async function fetchPyPIDependencies(packageName, version) {
|
|
|
268
268
|
* Parse PyPI requires_dist array and extract SDK package versions
|
|
269
269
|
*
|
|
270
270
|
* PyPI returns dependencies in PEP 508 format like:
|
|
271
|
-
* "openhands-sdk>=1.
|
|
272
|
-
* "openhands-tools==1.
|
|
273
|
-
* "openhands-workspace (>=1.
|
|
271
|
+
* "openhands-sdk>=1.26.0,<2.0.0"
|
|
272
|
+
* "openhands-tools==1.26.0"
|
|
273
|
+
* "openhands-workspace (>=1.26.0)"
|
|
274
274
|
*/
|
|
275
275
|
function parseSdkVersionsFromRequiresDist(requiresDist) {
|
|
276
276
|
const versions = {};
|
|
@@ -284,7 +284,7 @@ function parseSdkVersionsFromRequiresDist(requiresDist) {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
// Extract the version number - look for patterns like:
|
|
287
|
-
// ">=1.
|
|
287
|
+
// ">=1.26.0", "==1.26.0", "(>=1.26.0)", "~=1.26.0"
|
|
288
288
|
// After the package name and before any comma or closing paren
|
|
289
289
|
const versionPattern = /[><=~!]+\s*([0-9]+(?:\.[0-9]+)*)/;
|
|
290
290
|
const match = dep.match(versionPattern);
|
package/scripts/dev-safe.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
// Docker entrypoint can run it as a CLI. Re-exported below for back-compat
|
|
26
26
|
// (dev-with-automation.mjs and tests still import it from here).
|
|
27
27
|
import { buildRuntimeServicesInfo } from "./runtime-services-info.mjs";
|
|
28
|
+
import { fileLog, stripAnsi } from "./logger.mjs";
|
|
28
29
|
|
|
29
30
|
// ── Centralized config (single source of truth for versions, ports, etc.) ───
|
|
30
31
|
const __dev_safe_dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -419,7 +420,7 @@ export function validateFrontendDependencies(
|
|
|
419
420
|
* edits are picked up without a manual reinstall. The agent-server itself
|
|
420
421
|
* is rebuilt from local source on each invocation (--reinstall).
|
|
421
422
|
* - OH_AGENT_SERVER_GIT_REF: Git commit SHA or branch name
|
|
422
|
-
* - OH_AGENT_SERVER_VERSION: Specific PyPI version (e.g., "1.
|
|
423
|
+
* - OH_AGENT_SERVER_VERSION: Specific PyPI version (e.g., "1.26.0")
|
|
423
424
|
*
|
|
424
425
|
* If none are set, defaults to the released version specified by
|
|
425
426
|
* DEFAULT_AGENT_SERVER_VERSION. Set OH_AGENT_SERVER_GIT_REF to use a
|
|
@@ -462,7 +463,7 @@ export function buildAgentServerCommand(env = process.env) {
|
|
|
462
463
|
// All four must come from the same ref so inter-package APIs stay in sync.
|
|
463
464
|
//
|
|
464
465
|
// --reinstall is required because the git branch may carry the same version
|
|
465
|
-
// string as the current PyPI release (e.g. both "1.
|
|
466
|
+
// string as the current PyPI release (e.g. both "1.26.0"). Without it, uv
|
|
466
467
|
// silently reuses the cached PyPI wheels and the git ref is never actually
|
|
467
468
|
// used, even though it was explicitly requested.
|
|
468
469
|
const baseGitUrl = `git+${AGENT_SERVER_GIT_REPO}@${gitRef}`;
|
|
@@ -834,13 +835,16 @@ function spawnProcess(command, args, options = {}) {
|
|
|
834
835
|
|
|
835
836
|
child.once("error", (error) => {
|
|
836
837
|
if (isEnoentError(error) && command === "uvx") {
|
|
837
|
-
|
|
838
|
+
const msg = formatMissingUvxGuidance(options?.cwd);
|
|
839
|
+
console.error(msg);
|
|
840
|
+
fileLog("error", stripAnsi(msg));
|
|
838
841
|
} else if (isEnoentError(error)) {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
);
|
|
842
|
+
const msg = `Failed to start ${command}. Make sure it is installed and on your PATH.`;
|
|
843
|
+
console.error(msg);
|
|
844
|
+
fileLog("error", msg);
|
|
842
845
|
} else {
|
|
843
846
|
console.error(`Failed to start ${command}:`, error);
|
|
847
|
+
fileLog("error", `Failed to start ${command}: ${error.message}`);
|
|
844
848
|
}
|
|
845
849
|
});
|
|
846
850
|
|
|
@@ -849,9 +853,12 @@ function spawnProcess(command, args, options = {}) {
|
|
|
849
853
|
|
|
850
854
|
async function main() {
|
|
851
855
|
console.log("Starting isolated agent-server + frontend dev stack...");
|
|
856
|
+
fileLog("info", "Starting isolated agent-server + frontend dev stack...");
|
|
852
857
|
validateFrontendDependencies();
|
|
853
858
|
console.log("Frontend dependencies found.");
|
|
859
|
+
fileLog("info", "Frontend dependencies found.");
|
|
854
860
|
console.log("Allocating ports...");
|
|
861
|
+
fileLog("info", "Allocating ports...");
|
|
855
862
|
|
|
856
863
|
// Use async config builder with dynamic port allocation
|
|
857
864
|
const config = await buildSafeDevConfigAsync();
|
|
@@ -890,6 +897,16 @@ async function main() {
|
|
|
890
897
|
console.log(`- secret key: ${secretKeySource}`);
|
|
891
898
|
console.log(`- session API key: ${sessionKeySource}`);
|
|
892
899
|
console.log("");
|
|
900
|
+
fileLog(
|
|
901
|
+
"info",
|
|
902
|
+
[
|
|
903
|
+
"Agent-server stack config:",
|
|
904
|
+
` agent-server: ${agentServerCmd.source}`,
|
|
905
|
+
` backend: ${config.backendBaseUrl}`,
|
|
906
|
+
` working dir: ${config.workingDir}`,
|
|
907
|
+
` state dir: ${config.stateDir}`,
|
|
908
|
+
].join("\n"),
|
|
909
|
+
);
|
|
893
910
|
|
|
894
911
|
const backend = spawnProcess(
|
|
895
912
|
agentServerCmd.command,
|
|
@@ -990,7 +1007,9 @@ async function main() {
|
|
|
990
1007
|
|
|
991
1008
|
backend.once("exit", (code) => {
|
|
992
1009
|
if (!shuttingDown) {
|
|
993
|
-
|
|
1010
|
+
const msg = `agent-server exited unexpectedly with code ${code ?? 0}`;
|
|
1011
|
+
console.error(msg);
|
|
1012
|
+
fileLog("error", msg);
|
|
994
1013
|
shutdown();
|
|
995
1014
|
process.exitCode = code ?? 1;
|
|
996
1015
|
}
|
|
@@ -1077,7 +1096,12 @@ if (
|
|
|
1077
1096
|
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
1078
1097
|
) {
|
|
1079
1098
|
main().catch((error) => {
|
|
1080
|
-
|
|
1099
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1100
|
+
console.error(msg);
|
|
1101
|
+
fileLog("error", `Fatal error: ${msg}`);
|
|
1102
|
+
if (error instanceof Error && error.stack) {
|
|
1103
|
+
fileLog("error", error.stack);
|
|
1104
|
+
}
|
|
1081
1105
|
process.exit(1);
|
|
1082
1106
|
});
|
|
1083
1107
|
}
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
isProcessRunning,
|
|
69
69
|
signalProcessTree,
|
|
70
70
|
} from "./dev-process-utils.mjs";
|
|
71
|
+
import { fileLog, stripAnsi } from "./logger.mjs";
|
|
71
72
|
|
|
72
73
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
73
74
|
const projectRoot = resolve(__dirname, "..");
|
|
@@ -105,18 +106,22 @@ const c = {
|
|
|
105
106
|
function logService(name, message, color = c.reset) {
|
|
106
107
|
const ts = new Date().toISOString().split("T")[1].split(".")[0];
|
|
107
108
|
console.log(`${c.dim}${ts}${c.reset} ${color}[${name}]${c.reset} ${message}`);
|
|
109
|
+
fileLog("info", `[${name}] ${stripAnsi(message)}`);
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
function logStep(step, message) {
|
|
111
113
|
console.log(`${c.cyan}[${step}]${c.reset} ${message}`);
|
|
114
|
+
fileLog("info", `[${step}] ${message}`);
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
function logSuccess(message) {
|
|
115
118
|
console.log(`${c.green}✓${c.reset} ${message}`);
|
|
119
|
+
fileLog("info", `✓ ${message}`);
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
function logError(message) {
|
|
119
123
|
console.error(`${c.red}✗${c.reset} ${message}`);
|
|
124
|
+
fileLog("error", `✗ ${stripAnsi(message)}`);
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
/**
|
|
@@ -472,7 +477,9 @@ function checkPrerequisites({
|
|
|
472
477
|
|
|
473
478
|
if (checkUvx) {
|
|
474
479
|
if (!commandExists("uvx")) {
|
|
475
|
-
|
|
480
|
+
const uvxGuidance = formatMissingUvxGuidance(projectRoot);
|
|
481
|
+
console.error(uvxGuidance);
|
|
482
|
+
fileLog("error", stripAnsi(uvxGuidance));
|
|
476
483
|
process.exit(1);
|
|
477
484
|
}
|
|
478
485
|
logSuccess("uvx found");
|
|
@@ -862,6 +869,7 @@ function shutdown() {
|
|
|
862
869
|
|
|
863
870
|
console.log("");
|
|
864
871
|
console.log(`${c.yellow}Shutting down...${c.reset}`);
|
|
872
|
+
fileLog("info", "Shutting down...");
|
|
865
873
|
|
|
866
874
|
for (const [name, proc] of processes) {
|
|
867
875
|
logService(name, "Stopping...", c.dim);
|
|
@@ -1133,6 +1141,20 @@ function printBanner(config) {
|
|
|
1133
1141
|
console.log(`${c.dim}State directory: ${config.stateDir}${c.reset}`);
|
|
1134
1142
|
console.log(`${c.dim}Press Ctrl+C to stop${c.reset}`);
|
|
1135
1143
|
console.log("");
|
|
1144
|
+
|
|
1145
|
+
// Write a compact plain-text summary to the log file.
|
|
1146
|
+
const summary = [
|
|
1147
|
+
`${stackName} — started`,
|
|
1148
|
+
` Ingress: http://localhost:${config.ingressPort}/`,
|
|
1149
|
+
...(config.launchFrontend
|
|
1150
|
+
? [` Main UI: http://localhost:${config.ingressPort}/`]
|
|
1151
|
+
: []),
|
|
1152
|
+
...(config.launchAutomation
|
|
1153
|
+
? [` API Docs: http://localhost:${config.ingressPort}/api/automation/docs`]
|
|
1154
|
+
: []),
|
|
1155
|
+
` State directory: ${config.stateDir}`,
|
|
1156
|
+
];
|
|
1157
|
+
fileLog("info", summary.join("\n"));
|
|
1136
1158
|
}
|
|
1137
1159
|
|
|
1138
1160
|
async function main(options = {}) {
|
|
@@ -1186,6 +1208,7 @@ async function main(options = {}) {
|
|
|
1186
1208
|
console.log("");
|
|
1187
1209
|
console.log(`${c.cyan}${c.bold}${titleWithMode}${c.reset}`);
|
|
1188
1210
|
console.log("");
|
|
1211
|
+
fileLog("info", titleWithMode);
|
|
1189
1212
|
|
|
1190
1213
|
// Setup phase
|
|
1191
1214
|
checkPrerequisites({
|
|
@@ -1397,6 +1420,7 @@ if (isMainModule) {
|
|
|
1397
1420
|
logError(`Fatal error: ${err.message}`);
|
|
1398
1421
|
if (err.stack) {
|
|
1399
1422
|
console.error(c.dim + err.stack + c.reset);
|
|
1423
|
+
fileLog("error", err.stack);
|
|
1400
1424
|
}
|
|
1401
1425
|
process.exit(1);
|
|
1402
1426
|
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared file logger for agent-canvas dev scripts.
|
|
3
|
+
*
|
|
4
|
+
* Writes log output to a daily-rotating file under <project-root>/logs/
|
|
5
|
+
* alongside the existing console output (which is unchanged).
|
|
6
|
+
*
|
|
7
|
+
* File naming: logs/agent-canvas.YYYY-MM-DD.log
|
|
8
|
+
* Retention: 7 days (files older than 7 days are automatically deleted)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { mkdirSync } from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import process from "node:process";
|
|
15
|
+
import { createLogger, format } from "winston";
|
|
16
|
+
import DailyRotateFile from "winston-daily-rotate-file";
|
|
17
|
+
|
|
18
|
+
// Mirror the state-directory logic from dev-safe.mjs so log files live
|
|
19
|
+
// alongside all other agent-canvas runtime state (e.g. ~/.openhands/agent-canvas).
|
|
20
|
+
// The same env var (OH_CANVAS_SAFE_STATE_DIR) overrides both.
|
|
21
|
+
const stateDir =
|
|
22
|
+
process.env.OH_CANVAS_SAFE_STATE_DIR ||
|
|
23
|
+
join(homedir(), ".openhands", "agent-canvas");
|
|
24
|
+
const logDir = join(stateDir, "logs");
|
|
25
|
+
|
|
26
|
+
// Ensure the logs directory exists before the transport tries to open a file.
|
|
27
|
+
mkdirSync(logDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
// Matches any ANSI CSI escape sequence (colors, cursor movement, etc.).
|
|
30
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Remove ANSI escape codes so log files contain clean plain text.
|
|
34
|
+
* @param {string} str
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export function stripAnsi(str) {
|
|
38
|
+
return typeof str === "string" ? str.replace(ANSI_RE, "") : String(str);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const fileTransport = new DailyRotateFile({
|
|
42
|
+
dirname: logDir,
|
|
43
|
+
filename: "agent-canvas.%DATE%.log",
|
|
44
|
+
datePattern: "YYYY-MM-DD",
|
|
45
|
+
maxFiles: "7d",
|
|
46
|
+
// Audit file tracks which rotated files exist; kept alongside log files.
|
|
47
|
+
auditFile: join(logDir, ".log-audit.json"),
|
|
48
|
+
createSymlink: false,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const fileLogger = createLogger({
|
|
52
|
+
level: "debug",
|
|
53
|
+
format: format.combine(
|
|
54
|
+
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
55
|
+
format.printf(
|
|
56
|
+
({ timestamp, level, message }) =>
|
|
57
|
+
`${timestamp} [${level.toUpperCase().padEnd(5)}] ${message}`,
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
transports: [fileTransport],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Swallow any transport-level errors (e.g. disk full) so a logging failure
|
|
64
|
+
// never crashes the dev server.
|
|
65
|
+
fileLogger.on("error", () => {});
|
|
66
|
+
fileTransport.on("error", () => {});
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Write a message to the rotating log file.
|
|
70
|
+
* ANSI escape codes are stripped automatically; console output is unaffected.
|
|
71
|
+
*
|
|
72
|
+
* @param {'info' | 'warn' | 'error' | 'debug'} level
|
|
73
|
+
* @param {string} message
|
|
74
|
+
*/
|
|
75
|
+
export function fileLog(level, message) {
|
|
76
|
+
fileLogger.log(level, stripAnsi(message));
|
|
77
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-BFRubm34.js";import{t}from"./react-CM_dJw1Z.js";import{t as n}from"./useMutation-7hG0GuPx.js";import{o as r,t as i}from"./declaration-IA661TBv.js";import{n as a}from"./QueryClientProvider-7oLZ1RtQ.js";import{t as o}from"./useTranslation-CbJtty1g.js";import{t as s}from"./retrieve-axios-error-message-DbnSBc_1.js";import{n as c,t as l}from"./vendor~root-layout~home~mcp~automations-list-DGOI7kQz.js";import{n as u,r as d}from"./custom-toast-handlers-fgD4IYsu.js";import{t as f}from"./utils-CVcuFUYj.js";import{n as p}from"./agent-server-client-options-Dvgeb3x4.js";import{t as m}from"./mcp-client-xEdbDxOL.js";import{a as h}from"./query-keys-CQaji0wJ.js";import{t as ee}from"./modal-backdrop-B1si6TUd.js";import{n as te}from"./modal-classes-9XTtWCtF.js";import{t as g}from"./brand-button-BoiPxAGm.js";import{t as _}from"./styled-tooltip-GXy1qxsO.js";import{t as ne}from"./modal-close-button-DY-rVmld.js";import{t as v}from"./settings-input-DsoZj8Dm.js";import{t as y}from"./v4-BygpdDmc.js";import{t as b}from"./settings-service.api-4u2RKC8k.js";import{a as x,i as S,r as C}from"./use-settings-0qFqC-r6.js";import{t as w}from"./secrets-service-rJqZtDmI.js";function T(e){return[...e.sse_servers.map((e,t)=>({id:`sse-${t}`,type:`sse`,url:typeof e==`string`?e:e.url,api_key:typeof e==`object`?e.api_key:void 0})),...e.stdio_servers.map((e,t)=>({id:`stdio-${t}`,type:`stdio`,name:e.name,command:e.command,args:e.args,env:e.env})),...e.shttp_servers.map((e,t)=>({id:`shttp-${t}`,type:`shttp`,url:typeof e==`string`?e:e.url,api_key:typeof e==`object`?e.api_key:void 0,timeout:typeof e==`object`?e.timeout:void 0}))]}var E=e(t(),1),D=r();function O(e){return e.connectionOptions.filter(e=>e.provider===`mcp`&&!!e.transport)}function k(e){let t=O(e);return t.find(t=>t.id===e.defaultConnectionOptionId)??t[0]}function A(e){return e.auth.strategy!==`oauth2`}function j(e){let t=O(e),n=t.find(t=>t.id===e.defaultConnectionOptionId);return n&&A(n)?n:t.find(A)}function M(e){return k(e)?.transport}var N=`https://mcp.linear.app/sse`,P=`https://mcp.linear.app/mcp`,re=`https://linear.app/docs/mcp`;function ie(e){return e.id===`linear`?{...e,docsUrl:re,installHint:`Authenticate with a Linear API key (Linear → Settings → Security & access) — sent as a Bearer token. Optional when the endpoint accepts your OAuth session.`,connectionOptions:e.connectionOptions.map(e=>e.transport?.kind===`sse`&&I(e.transport.url,N)?{...e,auth:{...e.auth,strategy:`bearer`},transport:{kind:`shttp`,url:P,apiKeyOptional:e.transport.apiKeyOptional}}:e)}:e}function ae(e){return e.map(ie).filter(e=>!!k(e))}var F=e=>{try{return new URL(e)}catch{return null}};function I(e,t){let n=typeof e==`string`?e:``,r=typeof t==`string`?t:``;if(!n||!r)return!1;let i=F(n),a=F(r);return!i||!a?n.replace(/\/+$/,``)===r.replace(/\/+$/,``):i.protocol===a.protocol&&i.host===a.host&&i.pathname.replace(/\/+$/,``)===a.pathname.replace(/\/+$/,``)}function oe(e,t){return t.find(t=>R(e,t))??null}function L(e,t){for(let n of O(e)){let e=oe(n.transport,t);if(e)return e}return null}function R(e,t){if(e.kind===`shttp`){let n=e.url;return t.type===`shttp`&&!!t.url&&I(t.url,n)}if(e.kind===`sse`){let n=e.url;return t.type===`sse`&&!!t.url&&I(t.url,n)}return t.type===`stdio`&&t.name===e.serverName}function z(e,t){return!e.runtimeAvailability||e.runtimeAvailability===`all`?!0:e.runtimeAvailability===t}function B(e){return e.trim().toLowerCase()}function V(e){return e.map((e,t)=>({entry:e,index:t})).sort((e,t)=>(t.entry.popularityRank??0)-(e.entry.popularityRank??0)||e.index-t.index).map(({entry:e})=>e)}function H(e,t){return t.find(t=>t.id===e)}function U(e,t){let n=B(t);return n?[e.name,e.description,e.id,...e.keywords??[]].join(` `).toLowerCase().includes(n):!0}function W(e,t,n){let r=B(n);return r?[e.type,`name`in e?e.name:void 0,`command`in e?e.command:void 0,`args`in e?e.args?.join(` `):void 0,`url`in e?e.url:void 0,t?.name,t?.description,t?.id,...t?.keywords??[]].filter(Boolean).join(` `).toLowerCase().includes(r):!0}function G(e,t){return t.find(t=>O(t).some(t=>R(t.transport,e)))}function K({fieldKey:e,checked:t,onToggle:n}){let{t:r}=o(`openhands`);return(0,D.jsxs)(`label`,{"data-testid":`mcp-install-save-secret-${e}`,className:f(`flex items-center gap-2 px-3 py-2 mt-0.5 rounded-lg border cursor-pointer transition-colors`,t?`border-green-500/35 bg-green-500/10`:`border-[var(--oh-border)] bg-transparent hover:bg-white/[0.03]`),children:[(0,D.jsx)(`input`,{className:`sr-only`,id:`mcp-save-secret-checkbox-${e}`,type:`checkbox`,checked:t,onChange:e=>n(e.target.checked)}),(0,D.jsx)(`span`,{"aria-hidden":`true`,className:f(`relative inline-flex h-[22px] w-[40px] shrink-0 items-center rounded-full border transition-colors duration-200`,t?`border-green-500 bg-green-500`:`border-[var(--oh-border)] bg-surface-raised`),children:(0,D.jsx)(`span`,{className:f(`inline-block size-4 rounded-full transition-transform duration-200`,t?`translate-x-[21px] bg-white`:`translate-x-[2px] bg-[var(--oh-muted)]`)})}),(0,D.jsx)(`span`,{className:`text-sm`,children:r(i.MCP$ALSO_SAVE_AS_SECRET)}),(0,D.jsx)(`code`,{className:f(`ml-auto text-[11px] font-mono tracking-tight border rounded px-1.5 py-0.5`,t?`text-green-500 border-green-500/35 bg-white/[0.04]`:`text-tertiary-alt border-[var(--oh-border)]`),children:e}),(0,D.jsx)(_,{content:r(i.MCP$SAVE_AS_SECRET_TOOLTIP),placement:`top`,children:(0,D.jsx)(`button`,{type:`button`,"aria-label":r(i.MCP$SAVE_AS_SECRET_TOOLTIP),className:`flex items-center justify-center size-[15px] shrink-0 rounded-full border border-[var(--oh-muted)] text-tertiary-alt text-[9px] font-bold cursor-help`,onClick:e=>e.preventDefault(),children:`?`})})]})}var se={xs:`h-4 w-4 rounded [&>svg]:h-2.5 [&>svg]:w-2.5`,sm:`h-5 w-5 rounded-md [&>svg]:h-3 [&>svg]:w-3`,md:`h-10 w-10 rounded-lg [&>svg]:h-5 [&>svg]:w-5`};function q({entry:e,size:t=`md`,className:n,fallback:r,testId:i}){return(0,D.jsx)(`span`,{"aria-hidden":`true`,title:e?.name,"data-testid":i,className:f(`inline-flex shrink-0 items-center justify-center overflow-hidden`,`border border-white/10 shadow-[inset_0_1px_0_rgba(255,255,255,0.18)]`,se[t],n),style:{backgroundColor:e?.iconBg??`var(--oh-color-tertiary)`,color:e?.iconColor??`#FFFFFF`},children:e?c[e.id]??r??l:r??l})}function J(){let e=a(),{data:t}=C();return n({mutationFn:async e=>{if(!t)return;let n=S(t.agent_settings?.mcp_config),r={sse_servers:[...n.sse_servers],stdio_servers:[...n.stdio_servers],shttp_servers:[...n.shttp_servers]};if(e.type===`sse`){let t={url:e.url,...e.api_key&&{api_key:e.api_key}};r.sse_servers.push(t)}else if(e.type===`stdio`){let t={name:e.name,command:e.command,...e.args&&{args:e.args},...e.env&&{env:e.env}};r.stdio_servers.push(t)}else if(e.type===`shttp`){let t={url:e.url,...e.api_key&&{api_key:e.api_key},...e.timeout!==void 0&&{timeout:e.timeout}};r.shttp_servers.push(t)}await b.saveSettings({agent_settings_diff:{mcp_config:x(r)}})},onSuccess:()=>{e.invalidateQueries({queryKey:h.personal()})}})}function ce(e){return e.type===`stdio`?{type:`stdio`,command:e.command,...e.args?.length&&{args:e.args},...e.env&&Object.keys(e.env).length>0&&{env:e.env}}:{type:e.type,url:e.url,...e.api_key?{api_key:e.api_key}:{}}}var le=class{static async testServer(e){let{host:t,apiKey:n}=p(),r=new m({host:t,...n?{apiKey:n}:{}});try{return await r.testServer({server:ce(e),...e.name?{name:e.name}:{},...e.timeout?{timeout:e.timeout}:{}})}finally{r.close()}}};function Y(){return n({mutationFn:e=>le.testServer(e)})}function X(e){return e.length<=3?e.join(`, `):`${e.slice(0,3).join(`, `)} … (+${e.length-3})`}function ue(){let{t:e}=o(`openhands`),t=a();return(0,E.useCallback)((n,r,a)=>{let o=n.filter(e=>a[e.key]&&(r[e.key]??``).trim());o.length!==0&&Promise.allSettled(o.map(e=>w.createSecret(e.key,r[e.key].trim(),e.label))).then(n=>{let r=o.filter((e,t)=>n[t].status===`fulfilled`).map(e=>e.key),a=o.filter((e,t)=>n[t].status===`rejected`).map(e=>e.key);r.length>0&&(t.invalidateQueries({queryKey:[`secrets-search`]}),t.invalidateQueries({queryKey:[`secrets`]}),d(e(i.MCP$SECRETS_SAVED,{keys:X(r)}))),a.length>0&&u(e(i.MCP$SECRETS_SAVE_FAILED,{keys:X(a)}))})},[e,t])}function Z(e){let t=/\[([^\]]+)\]\(([^)]+)\)/g,n=[],r=0;for(let i of e.matchAll(t))i.index>r&&n.push(e.slice(r,i.index)),n.push((0,D.jsx)(`a`,{href:/^https?:\/\//i.test(i[2])?i[2]:`#`,target:`_blank`,rel:`noreferrer`,className:`underline hover:text-white transition-colors`,children:i[1]},i.index)),r=i.index+i[0].length;return r<e.length&&n.push(e.slice(r)),n}function Q(e){return e?.transport.kind!==`shttp`&&e?.transport.kind!==`sse`?!1:[`api_key`,`bearer`,`basic`].includes(e.auth.strategy)}function $(e){return e.transport.kind===`stdio`?e.auth.apiKeyOptional??!1:e.auth.apiKeyOptional??e.transport.apiKeyOptional??!1}function de(e){let t={},n={},r=j(e),i=r?.transport;if(i?.kind===`stdio`){for(let e of i.envFields??[])t[e.key]=``,n[e.key]=e.type===`password`;for(let e of i.argFields??[])t[e.key]=``}else Q(r)&&(t.api_key=``);return{values:t,errors:{},savedAsSecret:n}}function fe({entry:e,onClose:t,onSuccess:n}){let{t:r}=o(`openhands`),{mutate:a,isPending:c}=J(),{mutate:l,isPending:u}=Y(),f=ue(),[p,m]=E.useState(()=>de(e)),h=E.useRef(p);h.current=p;let[_,b]=E.useState(null),x=j(e),S=x?.transport,C=u||c,w=(e,t)=>{m(n=>({...n,values:{...n.values,[e]:t},errors:{...n.errors,[e]:null}})),b(null)},T=(e,t)=>{m(n=>({...n,savedAsSecret:{...n.savedAsSecret,[e]:t}}))},O=e=>{switch(e.error_kind){case`timeout`:return r(i.MCP$TEST_ERROR_TIMEOUT);case`connection`:return r(i.MCP$TEST_ERROR_CONNECTION);default:return r(i.MCP$TEST_ERROR_UNKNOWN,{error:e.error})}},k=o=>{l(o,{onSuccess:c=>{if(!c.ok){b(O(c));return}a(o,{onSuccess:()=>{d(r(i.MCP$INSTALL_SUCCESS)),n?.(e),t(),S?.kind===`stdio`&&f(S.envFields??[],h.current.values,h.current.savedAsSecret)},onError:e=>{b(s(e)||r(i.ERROR$GENERIC))}})},onError:e=>{b(s(e)||r(i.ERROR$GENERIC))}})},A=()=>{if(S?.kind!==`shttp`&&S?.kind!==`sse`||!x)return;let e=p.values.api_key?.trim()??``,t=Q(x);if(t&&!$(x)&&!e){m(e=>({...e,errors:{api_key:r(i.MCP$ERROR_FIELD_REQUIRED)}}));return}k({id:`${S.kind}-${y()}`,type:S.kind,url:S.url,...t&&e&&{api_key:e}})},M=()=>{if(S?.kind!==`stdio`)return;let e=S,t={};for(let n of e.envFields??[])n.required&&!(p.values[n.key]??``).trim()&&(t[n.key]=r(i.MCP$ERROR_FIELD_REQUIRED));for(let n of e.argFields??[])n.required&&!(p.values[n.key]??``).trim()&&(t[n.key]=r(i.MCP$ERROR_FIELD_REQUIRED));if(Object.values(t).some(Boolean)){m(e=>({...e,errors:t}));return}let n={};for(let t of e.envFields??[]){let e=p.values[t.key]?.trim();e&&(n[t.key]=e)}let a=[];for(let t of e.argFields??[]){let e=p.values[t.key]?.trim();if(e)for(let t of e.split(/\s+/))t&&a.push(t)}k({id:`stdio-${y()}`,type:`stdio`,name:e.serverName,command:e.command,args:[...e.args,...a],...Object.keys(n).length>0&&{env:n}})};return(0,D.jsx)(ee,{onClose:t,"aria-label":e.name,children:(0,D.jsxs)(`form`,{"data-testid":`mcp-install-modal`,"data-marketplace-id":e.id,onSubmit:e=>(e.preventDefault(),b(null),S?.kind===`shttp`||S?.kind===`sse`?A():M()),className:`relative bg-base-secondary p-6 rounded-xl flex flex-col gap-4 border border-[var(--oh-border)] w-[520px] max-w-[90vw] max-h-[85vh] overflow-y-auto custom-scrollbar`,children:[(0,D.jsx)(ne,{onClose:t,testId:`mcp-install-modal-close`,disabled:C}),(0,D.jsxs)(`div`,{className:`flex items-start gap-3 pr-6`,children:[(0,D.jsx)(q,{entry:e}),(0,D.jsxs)(`div`,{className:`flex flex-col flex-1`,children:[(0,D.jsx)(`h2`,{className:te,children:e.name}),(0,D.jsx)(`p`,{className:`text-xs text-tertiary-light`,children:e.description})]})]}),e.installHint&&(0,D.jsx)(`p`,{className:`text-xs text-tertiary-light`,children:e.installHint}),e.docsUrl&&(0,D.jsx)(`a`,{href:e.docsUrl,target:`_blank`,rel:`noreferrer`,className:`text-xs text-[var(--oh-muted)] hover:text-white hover:underline self-start transition-colors`,children:r(i.MCP$VIEW_DOCS)}),(0,D.jsx)(`div`,{className:`flex flex-col gap-3`,children:(()=>{if(S?.kind===`shttp`||S?.kind===`sse`){let e=Q(x),t=x?$(x):!1;return(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(v,{testId:`mcp-install-field-url`,name:`url`,type:`url`,label:r(i.SETTINGS$MCP_URL),value:S.url,onChange:()=>{},isDisabled:!0,className:`w-full`}),e?(0,D.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,D.jsx)(v,{testId:`mcp-install-field-api_key`,name:`api_key`,type:`password`,label:r(i.SETTINGS$MCP_API_KEY),value:p.values.api_key??``,onChange:e=>w(`api_key`,e),placeholder:r(i.SETTINGS$MCP_API_KEY_PLACEHOLDER),showOptionalTag:t,required:!t,className:`w-full`}),p.errors.api_key&&(0,D.jsx)(`p`,{className:`text-xs text-red-500`,children:p.errors.api_key})]}):null]})}if(S?.kind!==`stdio`)return null;let e=S;return(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(v,{testId:`mcp-install-field-command-readonly`,name:`command-readonly`,type:`text`,label:r(i.MCP$COMMAND_LABEL),value:`${e.command} ${e.args.join(` `)}`.trim(),onChange:()=>{},isDisabled:!0,className:`w-full`}),(e.envFields??[]).map(e=>(0,D.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,D.jsx)(v,{testId:`mcp-install-field-${e.key}`,name:e.key,type:e.type===`password`?`password`:`text`,label:e.label,value:p.values[e.key]??``,onChange:t=>w(e.key,t),placeholder:e.placeholder,required:e.required,showOptionalTag:!e.required,className:`w-full`}),e.helperText&&(0,D.jsx)(`p`,{className:`text-xs text-tertiary-alt`,children:Z(e.helperText)}),p.errors[e.key]&&(0,D.jsx)(`p`,{className:`text-xs text-red-500`,children:p.errors[e.key]}),e.key in p.savedAsSecret&&(0,D.jsx)(K,{fieldKey:e.key,checked:p.savedAsSecret[e.key],onToggle:t=>T(e.key,t)})]},e.key)),(e.argFields??[]).map(e=>(0,D.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,D.jsx)(v,{testId:`mcp-install-field-${e.key}`,name:e.key,type:e.type===`password`?`password`:`text`,label:e.label,value:p.values[e.key]??``,onChange:t=>w(e.key,t),placeholder:e.placeholder,required:e.required,showOptionalTag:!e.required,className:`w-full`}),e.helperText&&(0,D.jsx)(`p`,{className:`text-xs text-tertiary-alt`,children:Z(e.helperText)}),p.errors[e.key]&&(0,D.jsx)(`p`,{className:`text-xs text-red-500`,children:p.errors[e.key]})]},e.key))]})})()}),_&&(0,D.jsx)(`p`,{"data-testid":`mcp-install-modal-error`,className:`text-sm text-red-500 whitespace-pre-wrap`,children:_}),(0,D.jsxs)(`div`,{className:`flex justify-end gap-2 mt-2`,children:[(0,D.jsx)(g,{type:`button`,variant:`secondary`,onClick:t,testId:`mcp-install-cancel`,children:r(i.BUTTON$CANCEL)}),(0,D.jsx)(g,{type:`submit`,variant:`primary`,isDisabled:C,testId:`mcp-install-submit`,children:r(u?i.MCP$VERIFYING:c?i.SETTINGS$SAVING:i.MCP$INSTALL_BUTTON)})]})]})})}export{G as a,V as c,W as d,z as f,q as i,H as l,T as m,Y as n,L as o,U as p,J as r,M as s,fe as t,ae as u};
|