mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 16:38:34 +08:00
feat(mcp): render markdown links in helperText; update Slack catalog pin (#1012)
* feat(mcp): render markdown links in helperText; bump extensions to slack field-order PR commit - Add renderHelperText() to install-server-modal.tsx that converts [text](url) patterns into <a> elements with target=_blank, so the Slack workspace-ID helper text (and any future catalog entries) can embed clickable docs links inline. - Bump @openhands/extensions to commit 2d43e9c (branch slack-catalog-field-order-and-helper-links, PR #285) which: • moves SLACK_TEAM_ID before SLACK_BOT_TOKEN in the install modal • replaces the plain SLACK_TEAM_ID helper text with linked copy: 'First visit [here](...#find-your-url) to get your Slack URL and then visit [here](...#find-your-workspace-or-org-id) to get your workspace ID.' - Removes stale integrity hash from package-lock.json for the @openhands/extensions entry; npm install will recompute it. Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to d186872 (SLACK_BOT_TOKEN helperText) Add inline linked helperText for SLACK_BOT_TOKEN in slack.json (PR #285, commit d186872): 'You'll need to create or update a Slack App as shown [here](https://github.com/zencoderai/slack-mcp-server#slack-bot-setup).' Drops the now-redundant helperLink field. Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to b45d3a1 (SLACK_TEAM_ID helperText rewrite) Update SLACK_TEAM_ID helperText to named links: 'First get your [Slack URL](...). Then use that to get your [Workspace ID](...).' Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to 84a0a6e (SLACK_BOT_TOKEN named link) Update SLACK_BOT_TOKEN helperText to: "You'll need to create or update a [Slack App](...#slack-bot-setup)." Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to e07f427 (SLACK_BOT_TOKEN helperText) Update SLACK_BOT_TOKEN helperText to: "You'll need to create or update a [Slack App](...) to get a Bot token" Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to 5efd1b8 Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285). Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to 952c759 Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285). Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to f30dbfb Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285). Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to 02715f4 Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285). Co-authored-by: openhands <openhands@all-hands.dev> * chore: bump @openhands/extensions to cb092c8 Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285). Co-authored-by: openhands <openhands@all-hands.dev> * fix(mcp): validate URL scheme in renderHelperText; use matchAll - Guard href against javascript:/data: XSS via /^https?:\/\//i test - Replace exec-in-while with matchAll to drop the eslint-disable comment Addresses review bot feedback on PR #1012. Co-authored-by: openhands <openhands@all-hands.dev> * fix(mcp): use double quotes for fallback href to satisfy Prettier Co-authored-by: openhands <openhands@all-hands.dev> * chore: update @openhands/extensions to latest main (62594156) Co-authored-by: openhands <openhands@all-hands.dev> --------- Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
committed by
GitHub
co-authored by
openhands
parent
667c5162fa
commit
c0c413f406
Generated
+3
-3
@@ -12,7 +12,7 @@
|
||||
"@heroui/react": "2.8.10",
|
||||
"@microlink/react-json-view": "1.31.20",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#e14f740c59b4bfd7369d4bb6aea5eeb33dd05909",
|
||||
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#62594156a187722344736bc2ff5edfb12c0cc75c",
|
||||
"@openhands/typescript-client": "1.24.3",
|
||||
"@react-router/node": "7.14.2",
|
||||
"@react-router/serve": "7.14.2",
|
||||
@@ -3441,8 +3441,8 @@
|
||||
},
|
||||
"node_modules/@openhands/extensions": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "git+https://github.com/OpenHands/extensions.git#e14f740c59b4bfd7369d4bb6aea5eeb33dd05909",
|
||||
"integrity": "sha512-PC0pmJD1AiNP9aDfdDfDPP1iF40m/QN3vyv/xPN9sigLBtCmNBugSIySWEGVIGydARYkVi+NHL2KorPtlAOFAg==",
|
||||
"resolved": "git+https://github.com/OpenHands/extensions.git#62594156a187722344736bc2ff5edfb12c0cc75c",
|
||||
"integrity": "sha512-K5XnP/YX8vVV/3VWWbT6QA+fVzlil63EL5KRO77HU+FrKFCMTJTOCJH7pxwCoAJGa0Wu0hV+udpcRYI+une35A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.20.0"
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
"@heroui/react": "2.8.10",
|
||||
"@microlink/react-json-view": "1.31.20",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#e14f740c59b4bfd7369d4bb6aea5eeb33dd05909",
|
||||
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#62594156a187722344736bc2ff5edfb12c0cc75c",
|
||||
"@openhands/typescript-client": "1.24.3",
|
||||
"@react-router/node": "7.14.2",
|
||||
"@react-router/serve": "7.14.2",
|
||||
|
||||
@@ -23,6 +23,39 @@ import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message"
|
||||
import { useSaveFieldsAsSecrets } from "#/hooks/mutation/use-save-fields-as-secrets";
|
||||
import { modalTitleLgClassName } from "#/utils/modal-classes";
|
||||
|
||||
/**
|
||||
* Renders a helperText string as React nodes, converting any `[text](url)`
|
||||
* markdown links into real `<a>` elements. Plain text segments are left as-is.
|
||||
* Only `http:` and `https:` URLs are rendered as links; anything else falls
|
||||
* back to `#` to guard against `javascript:` / `data:` XSS vectors.
|
||||
*/
|
||||
function renderHelperText(text: string): React.ReactNode {
|
||||
const linkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
|
||||
const parts: React.ReactNode[] = [];
|
||||
let lastIndex = 0;
|
||||
for (const match of text.matchAll(linkPattern)) {
|
||||
if (match.index > lastIndex) {
|
||||
parts.push(text.slice(lastIndex, match.index));
|
||||
}
|
||||
parts.push(
|
||||
<a
|
||||
key={match.index}
|
||||
href={/^https?:\/\//i.test(match[2]) ? match[2] : "#"}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="underline hover:text-white transition-colors"
|
||||
>
|
||||
{match[1]}
|
||||
</a>,
|
||||
);
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
if (lastIndex < text.length) {
|
||||
parts.push(text.slice(lastIndex));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
interface InstallServerModalProps {
|
||||
entry: MarketplaceEntry;
|
||||
onClose: () => void;
|
||||
@@ -321,7 +354,9 @@ export function InstallServerModal({
|
||||
className="w-full"
|
||||
/>
|
||||
{field.helperText && (
|
||||
<p className="text-xs text-tertiary-alt">{field.helperText}</p>
|
||||
<p className="text-xs text-tertiary-alt">
|
||||
{renderHelperText(field.helperText)}
|
||||
</p>
|
||||
)}
|
||||
{state.errors[field.key] && (
|
||||
<p className="text-xs text-red-500">{state.errors[field.key]}</p>
|
||||
@@ -352,7 +387,9 @@ export function InstallServerModal({
|
||||
className="w-full"
|
||||
/>
|
||||
{field.helperText && (
|
||||
<p className="text-xs text-tertiary-alt">{field.helperText}</p>
|
||||
<p className="text-xs text-tertiary-alt">
|
||||
{renderHelperText(field.helperText)}
|
||||
</p>
|
||||
)}
|
||||
{state.errors[field.key] && (
|
||||
<p className="text-xs text-red-500">{state.errors[field.key]}</p>
|
||||
|
||||
Reference in New Issue
Block a user