Replace static AddSources/CodeEditor with SandboxEditor that uses Monaco with real TypeScript types generated from OpenAPI specs. - generate-types.ts script fetches Cloudflare/Vercel/GitHub specs and produces .d.ts bundles via the executor core pipeline - Types served as static assets, fetched on demand per source toggle - Full IntelliSense: autocomplete, parameter types, return types - 2,603 Cloudflare + 281 Vercel + 1,079 GitHub typed tool methods
Unified · Split
| @@ -1,3 +1,4 @@ | ||
| 1 | 1 | dist/ |
| 2 | 2 | node_modules/ |
| 3 | 3 | .astro/ |
| 4 | +public/types/ | |
| @@ -7,11 +7,13 @@ · 6 unmodified lines | ||
| 7 | 7 | "dev": "astro dev", |
| 8 | 8 | "build": "astro build", |
| 9 | 9 | "preview": "astro preview", |
| 10 | − "typecheck": "astro check" | |
| 10 | + "typecheck": "astro check", | |
| 11 | + "generate-types": "bun run scripts/generate-types.ts" | |
| 11 | 12 | }, |
| 12 | 13 | "dependencies": { |
| 13 | 14 | "@astrojs/react": "^4.2.1", |
| 14 | 15 | "@astrojs/tailwind": "^6.0.2", |
| 16 | + "@monaco-editor/react": "^4.7.0", | |
| 15 | 17 | "astro": "^5.10.1", |
| 16 | 18 | "react": "^19.1.0", |
| 17 | 19 | "react-dom": "^19.1.0", |
| @@ -0,0 +1,168 @@ | ||
| 1 | +/** | |
| 2 | + * Fetches real OpenAPI specs and generates .d.ts type bundles for the Monaco editor. | |
| 3 | + * | |
| 4 | + * Usage: bun run scripts/generate-types.ts | |
| 5 | + * | |
| 6 | + * Outputs: | |
| 7 | + * public/types/<source>.d.ts — full type bundle per source (served as static asset) | |
| 8 | + * public/types/manifest.json — metadata about each source (tool count, file size) | |
| 9 | + */ | |
| 10 | + | |
| 11 | +import { prepareOpenApiSpec } from "../../../../executor/packages/core/src/openapi-prepare"; | |
| 12 | +import { buildOpenApiToolsFromPrepared } from "../../../../executor/packages/core/src/openapi/tool-builder"; | |
| 13 | +import { buildWorkspaceTypeBundle } from "../../../../executor/packages/core/src/tool-typing/typebundle"; | |
| 14 | +import type { OpenApiToolSourceConfig } from "../../../../executor/packages/core/src/tool/source-types"; | |
| 15 | +import { mkdirSync } from "fs"; | |
| 16 | +import { join, dirname } from "path"; | |
| 17 | + | |
| 18 | +interface SourceSpec { | |
| 19 | + name: string; | |
| 20 | + label: string; | |
| 21 | + icon: string; | |
| 22 | + description: string; | |
| 23 | + specUrl: string; | |
| 24 | + baseUrl?: string; | |
| 25 | +} | |
| 26 | + | |
| 27 | +const SOURCES: SourceSpec[] = [ | |
| 28 | + { | |
| 29 | + name: "cloudflare", | |
| 30 | + label: "Cloudflare", | |
| 31 | + icon: "☁", | |
| 32 | + description: "DNS, Workers, R2, KV, Zones, WAF, and more", | |
| 33 | + specUrl: | |
| 34 | + "https://raw.githubusercontent.com/cloudflare/api-schemas/main/openapi.json", | |
| 35 | + baseUrl: "https://api.cloudflare.com/client/v4", | |
| 36 | + }, | |
| 37 | + { | |
| 38 | + name: "vercel", | |
| 39 | + label: "Vercel", | |
| 40 | + icon: "▲", | |
| 41 | + description: "Deployments, Domains, Environment Variables, Projects", | |
| 42 | + specUrl: "https://openapi.vercel.sh/", | |
| 43 | + baseUrl: "https://api.vercel.com", | |
| 44 | + }, | |
| 45 | + { | |
| 46 | + name: "github", | |
| 47 | + label: "GitHub", | |
| 48 | + icon: "⬡", | |
| 49 | + description: "Repos, Issues, Pull Requests, Actions, Packages", | |
| 50 | + specUrl: | |
| 51 | + "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json", | |
| 52 | + baseUrl: "https://api.github.com", | |
| 53 | + }, | |
| 54 | +]; | |
| 55 | + | |
| 56 | +const OUT_DIR = join( | |
| 57 | + dirname(new URL(import.meta.url).pathname), | |
| 58 | + "../public/types", | |
| 59 | +); | |
| 60 | + | |
| 61 | +interface ManifestEntry { | |
| 62 | + name: string; | |
| 63 | + label: string; | |
| 64 | + icon: string; | |
| 65 | + description: string; | |
| 66 | + toolCount: number; | |
| 67 | + fileSizeKb: number; | |
| 68 | + file: string; | |
| 69 | +} | |
| 70 | + | |
| 71 | +async function processSource(source: SourceSpec) { | |
| 72 | + console.log(`\n${"=".repeat(60)}`); | |
| 73 | + console.log(`Processing: ${source.name}`); | |
| 74 | + console.log(`${"=".repeat(60)}`); | |
| 75 | + | |
| 76 | + // Step 1: Prepare the spec | |
| 77 | + console.log(" → Fetching and preparing spec..."); | |
| 78 | + let spec: string | Record<string, unknown> = source.specUrl; | |
| 79 | + | |
| 80 | + if (!/\.json($|[?#])/i.test(source.specUrl)) { | |
| 81 | + console.log(" → URL doesn't end in .json, fetching manually..."); | |
| 82 | + const response = await fetch(source.specUrl); | |
| 83 | + if (!response.ok) { | |
| 84 | + throw new Error( | |
| 85 | + `Failed to fetch spec: ${response.status} ${response.statusText}`, | |
| 86 | + ); | |
| 87 | + } | |
| 88 | + spec = (await response.json()) as Record<string, unknown>; | |
| 89 | + } | |
| 90 | + | |
| 91 | + const prepared = await prepareOpenApiSpec(spec, source.name, { | |
| 92 | + includeDts: true, | |
| 93 | + profile: "full", | |
| 94 | + }); | |
| 95 | + | |
| 96 | + console.log(` → DTS status: ${prepared.dtsStatus}`); | |
| 97 | + console.log(` → Servers: ${prepared.servers.join(", ") || "(none)"}`); | |
| 98 | + | |
| 99 | + // Step 2: Build tool definitions | |
| 100 | + console.log(" → Building tool definitions..."); | |
| 101 | + const sourceConfig: OpenApiToolSourceConfig = { | |
| 102 | + type: "openapi", | |
| 103 | + name: source.name, | |
| 104 | + spec: source.specUrl, | |
| 105 | + baseUrl: source.baseUrl, | |
| 106 | + }; | |
| 107 | + | |
| 108 | + const tools = buildOpenApiToolsFromPrepared(sourceConfig, prepared); | |
| 109 | + console.log(` → Tools built: ${tools.length}`); | |
| 110 | + | |
| 111 | + // Step 3: Build .d.ts type bundle | |
| 112 | + console.log(" → Building type bundle..."); | |
| 113 | + const sourceKey = `openapi:${source.name}`; | |
| 114 | + const dtsBundle = buildWorkspaceTypeBundle({ | |
| 115 | + tools, | |
| 116 | + openApiDtsBySource: prepared.dts ? { [sourceKey]: prepared.dts } : {}, | |
| 117 | + }); | |
| 118 | + | |
| 119 | + const sizeKb = dtsBundle.length / 1024; | |
| 120 | + console.log(` → Bundle size: ${sizeKb.toFixed(1)} KB`); | |
| 121 | + | |
| 122 | + return { toolCount: tools.length, dtsBundle, sizeKb }; | |
| 123 | +} | |
| 124 | + | |
| 125 | +async function main() { | |
| 126 | + mkdirSync(OUT_DIR, { recursive: true }); | |
| 127 | + | |
| 128 | + const manifest: ManifestEntry[] = []; | |
| 129 | + | |
| 130 | + for (const source of SOURCES) { | |
| 131 | + try { | |
| 132 | + const result = await processSource(source); | |
| 133 | + const fileName = `${source.name}.d.ts`; | |
| 134 | + const outPath = join(OUT_DIR, fileName); | |
| 135 | + await Bun.write(outPath, result.dtsBundle); | |
| 136 | + console.log(` ✓ Written: ${outPath}`); | |
| 137 | + | |
| 138 | + manifest.push({ | |
| 139 | + name: source.name, | |
| 140 | + label: source.label, | |
| 141 | + icon: source.icon, | |
| 142 | + description: source.description, | |
| 143 | + toolCount: result.toolCount, | |
| 144 | + fileSizeKb: Math.round(result.sizeKb), | |
| 145 | + file: fileName, | |
| 146 | + }); | |
| 147 | + } catch (err) { | |
| 148 | + console.error(` ✗ Failed to process ${source.name}:`, err); | |
| 149 | + } | |
| 150 | + } | |
| 151 | + | |
| 152 | + // Write manifest | |
| 153 | + const manifestPath = join(OUT_DIR, "manifest.json"); | |
| 154 | + await Bun.write(manifestPath, JSON.stringify(manifest, null, 2)); | |
| 155 | + console.log(`\n✓ Manifest: ${manifestPath}`); | |
| 156 | + console.log(`\nSummary:`); | |
| 157 | + for (const entry of manifest) { | |
| 158 | + console.log( | |
| 159 | + ` ${entry.label}: ${entry.toolCount} tools, ${entry.fileSizeKb} KB`, | |
| 160 | + ); | |
| 161 | + } | |
| 162 | + console.log("\nDone!"); | |
| 163 | +} | |
| 164 | + | |
| 165 | +main().catch((err) => { | |
| 166 | + console.error("Fatal error:", err); | |
| 167 | + process.exit(1); | |
| 168 | +}); | |
| @@ -1,147 +0,0 @@ | ||
| 1 | −import { useState } from "react"; | |
| 2 | − | |
| 3 | −interface Source { | |
| 4 | − id: string; | |
| 5 | − name: string; | |
| 6 | − icon: string; | |
| 7 | − toolCount: number; | |
| 8 | − description: string; | |
| 9 | −} | |
| 10 | − | |
| 11 | −const SOURCES: Source[] = [ | |
| 12 | − { | |
| 13 | − id: "cloudflare", | |
| 14 | − name: "Cloudflare", | |
| 15 | − icon: "☁", | |
| 16 | − toolCount: 1347, | |
| 17 | − description: "DNS, Workers, R2, KV, Zones, WAF, and more", | |
| 18 | − }, | |
| 19 | − { | |
| 20 | − id: "vercel", | |
| 21 | − name: "Vercel", | |
| 22 | − icon: "▲", | |
| 23 | − toolCount: 289, | |
| 24 | − description: "Deployments, Domains, Environment Variables, Projects", | |
| 25 | − }, | |
| 26 | − { | |
| 27 | − id: "posthog", | |
| 28 | − name: "PostHog", | |
| 29 | − icon: "🦔", | |
| 30 | − toolCount: 156, | |
| 31 | − description: "Events, Funnels, Feature Flags, Session Recordings", | |
| 32 | − }, | |
| 33 | − { | |
| 34 | − id: "github", | |
| 35 | − name: "GitHub", | |
| 36 | − icon: "⬡", | |
| 37 | − toolCount: 892, | |
| 38 | − description: "Repos, Issues, Pull Requests, Actions, Packages", | |
| 39 | − }, | |
| 40 | − { | |
| 41 | − id: "stripe", | |
| 42 | − name: "Stripe", | |
| 43 | − icon: "◈", | |
| 44 | − toolCount: 614, | |
| 45 | − description: "Charges, Subscriptions, Invoices, Customers, Payouts", | |
| 46 | − }, | |
| 47 | − { | |
| 48 | − id: "linear", | |
| 49 | − name: "Linear", | |
| 50 | − icon: "◇", | |
| 51 | − toolCount: 203, | |
| 52 | − description: "Issues, Projects, Cycles, Teams, Labels", | |
| 53 | − }, | |
| 54 | −]; | |
| 55 | − | |
| 56 | −export function AddSources() { | |
| 57 | − const [selected, setSelected] = useState<Set<string>>( | |
| 58 | − new Set(["cloudflare", "vercel", "posthog"]), | |
| 59 | − ); | |
| 60 | − | |
| 61 | − const toggle = (id: string) => { | |
| 62 | − setSelected((prev) => { | |
| 63 | − const next = new Set(prev); | |
| 64 | − if (next.has(id)) next.delete(id); | |
| 65 | − else next.add(id); | |
| 66 | − return next; | |
| 67 | − }); | |
| 68 | − }; | |
| 69 | − | |
| 70 | − const totalTools = SOURCES.filter((s) => selected.has(s.id)).reduce( | |
| 71 | − (sum, s) => sum + s.toolCount, | |
| 72 | − 0, | |
| 73 | − ); | |
| 74 | − | |
| 75 | − return ( | |
| 76 | − <div> | |
| 77 | − <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3"> | |
| 78 | − {SOURCES.map((source) => { | |
| 79 | − const isSelected = selected.has(source.id); | |
| 80 | − return ( | |
| 81 | − <button | |
| 82 | − key={source.id} | |
| 83 | − onClick={() => toggle(source.id)} | |
| 84 | − className={`group text-left p-4 rounded-lg border transition-all duration-200 ${ | |
| 85 | − isSelected | |
| 86 | − ? "border-accent/40 bg-accent/[0.06]" | |
| 87 | − : "border-white/[0.08] bg-white/[0.02] hover:border-white/20" | |
| 88 | − }`} | |
| 89 | − > | |
| 90 | − <div className="flex items-center justify-between mb-2"> | |
| 91 | − <div className="flex items-center gap-2.5"> | |
| 92 | − <span className="text-lg">{source.icon}</span> | |
| 93 | − <span className="font-medium text-sm text-[#f5f5f5]"> | |
| 94 | − {source.name} | |
| 95 | − </span> | |
| 96 | − </div> | |
| 97 | − <div | |
| 98 | − className={`w-4 h-4 rounded border-2 flex items-center justify-center transition-all ${ | |
| 99 | − isSelected | |
| 100 | − ? "border-accent bg-accent" | |
| 101 | − : "border-white/20 bg-transparent" | |
| 102 | − }`} | |
| 103 | − > | |
| 104 | − {isSelected && ( | |
| 105 | − <svg | |
| 106 | − className="w-2.5 h-2.5 text-white" | |
| 107 | − fill="none" | |
| 108 | − viewBox="0 0 24 24" | |
| 109 | − stroke="currentColor" | |
| 110 | − strokeWidth={3} | |
| 111 | − > | |
| 112 | − <path | |
| 113 | − strokeLinecap="round" | |
| 114 | − strokeLinejoin="round" | |
| 115 | − d="M5 13l4 4L19 7" | |
| 116 | − /> | |
| 117 | − </svg> | |
| 118 | − )} | |
| 119 | − </div> | |
| 120 | − </div> | |
| 121 | − <p className="text-xs text-white/40 leading-relaxed"> | |
| 122 | − {source.description} | |
| 123 | − </p> | |
| 124 | − <div className="mt-2 text-[0.65rem] uppercase tracking-widest text-white/25"> | |
| 125 | − {source.toolCount.toLocaleString()} tools | |
| 126 | − </div> | |
| 127 | − </button> | |
| 128 | − ); | |
| 129 | − })} | |
| 130 | − </div> | |
| 131 | − <div className="mt-6 flex items-center justify-between text-sm"> | |
| 132 | − <span className="text-white/40"> | |
| 133 | − <span className="text-[#f5f5f5] font-medium"> | |
| 134 | − {selected.size} | |
| 135 | − </span>{" "} | |
| 136 | − sources selected | |
| 137 | − </span> | |
| 138 | − <span className="text-white/40"> | |
| 139 | − <span className="text-accent font-mono font-medium"> | |
| 140 | − {totalTools.toLocaleString()} | |
| 141 | − </span>{" "} | |
| 142 | − tools available | |
| 143 | − </span> | |
| 144 | − </div> | |
| 145 | − </div> | |
| 146 | − ); | |
| 147 | −} | |
| @@ -1,148 +0,0 @@ | ||
| 1 | −import { useState, useEffect, useRef } from "react"; | |
| 2 | − | |
| 3 | −const CODE_EXAMPLE = `// Your agent writes TypeScript — not JSON tool calls | |
| 4 | −const zones = await tools.cloudflare.list_zones({ | |
| 5 | − account: { id: env.CF_ACCOUNT_ID }, | |
| 6 | −}) | |
| 7 | − | |
| 8 | −const zone = zones.result.find(z => z.name === "example.com") | |
| 9 | − | |
| 10 | −// Create a DNS record | |
| 11 | −await tools.cloudflare.create_dns_record({ | |
| 12 | − zone_id: zone.id, | |
| 13 | − type: "A", | |
| 14 | − name: "api", | |
| 15 | − content: "192.0.2.1", | |
| 16 | − proxied: true, | |
| 17 | −}) | |
| 18 | − | |
| 19 | −// Check deployment status | |
| 20 | −const deploys = await tools.vercel.listDeployments({ | |
| 21 | − teamId: env.VERCEL_TEAM_ID, | |
| 22 | − limit: 5, | |
| 23 | −}) | |
| 24 | − | |
| 25 | −return { | |
| 26 | − zone: zone.name, | |
| 27 | − dnsRecordCreated: true, | |
| 28 | − recentDeploys: deploys.deployments.map(d => ({ | |
| 29 | − url: d.url, | |
| 30 | − state: d.readyState, | |
| 31 | − created: d.created, | |
| 32 | − })), | |
| 33 | −}`; | |
| 34 | − | |
| 35 | −const OUTPUT = `{ | |
| 36 | − "zone": "example.com", | |
| 37 | − "dnsRecordCreated": true, | |
| 38 | − "recentDeploys": [ | |
| 39 | − { | |
| 40 | − "url": "my-app-abc123.vercel.app", | |
| 41 | − "state": "READY", | |
| 42 | − "created": 1703044800000 | |
| 43 | − }, | |
| 44 | − { | |
| 45 | − "url": "my-app-def456.vercel.app", | |
| 46 | − "state": "READY", | |
| 47 | − "created": 1703041200000 | |
| 48 | − } | |
| 49 | − ] | |
| 50 | −}`; | |
| 51 | − | |
| 52 | −export function CodeEditor() { | |
| 53 | − const [isRunning, setIsRunning] = useState(false); | |
| 54 | − const [showOutput, setShowOutput] = useState(false); | |
| 55 | − const outputRef = useRef<HTMLDivElement>(null); | |
| 56 | − | |
| 57 | − const handleRun = () => { | |
| 58 | − setIsRunning(true); | |
| 59 | − setShowOutput(false); | |
| 60 | − setTimeout(() => { | |
| 61 | − setIsRunning(false); | |
| 62 | − setShowOutput(true); | |
| 63 | − }, 1500); | |
| 64 | − }; | |
| 65 | − | |
| 66 | − useEffect(() => { | |
| 67 | − if (showOutput && outputRef.current) { | |
| 68 | − outputRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" }); | |
| 69 | − } | |
| 70 | − }, [showOutput]); | |
| 71 | − | |
| 72 | − return ( | |
| 73 | − <div> | |
| 74 | − <div className="bg-surface border border-white/[0.06] rounded-lg overflow-hidden"> | |
| 75 | − {/* Editor header */} | |
| 76 | − <div className="px-4 py-3 bg-white/[0.02] border-b border-white/[0.06] flex items-center justify-between"> | |
| 77 | − <div className="flex items-center gap-3"> | |
| 78 | − <span className="text-[0.65rem] uppercase tracking-widest text-white/30"> | |
| 79 | − execute.ts | |
| 80 | − </span> | |
| 81 | − <span className="text-[0.6rem] px-1.5 py-0.5 rounded bg-white/[0.06] text-white/30"> | |
| 82 | − TypeScript | |
| 83 | − </span> | |
| 84 | − </div> | |
| 85 | − <button | |
| 86 | − onClick={handleRun} | |
| 87 | − disabled={isRunning} | |
| 88 | − className={`px-3 py-1.5 text-xs font-medium rounded transition-all ${ | |
| 89 | − isRunning | |
| 90 | − ? "bg-white/10 text-white/40 cursor-wait" | |
| 91 | − : "bg-[#f5f5f5] text-surface hover:bg-white/90 active:scale-[0.98]" | |
| 92 | − }`} | |
| 93 | − > | |
| 94 | − {isRunning ? ( | |
| 95 | − <span className="flex items-center gap-1.5"> | |
| 96 | − <span className="w-3 h-3 border-2 border-white/30 border-t-white/80 rounded-full animate-spin" /> | |
| 97 | − Running... | |
| 98 | − </span> | |
| 99 | − ) : ( | |
| 100 | − "▶ Run" | |
| 101 | − )} | |
| 102 | − </button> | |
| 103 | − </div> | |
| 104 | − {/* Code body */} | |
| 105 | − <div className="p-4 overflow-x-auto"> | |
| 106 | − <pre className="!m-0 !p-0 !bg-transparent !border-none"> | |
| 107 | − <code className="!bg-transparent !p-0 font-mono text-[0.8rem] leading-7 text-white/70"> | |
| 108 | − {CODE_EXAMPLE} | |
| 109 | − </code> | |
| 110 | − </pre> | |
| 111 | − </div> | |
| 112 | − </div> | |
| 113 | − {/* Output */} | |
| 114 | − {(isRunning || showOutput) && ( | |
| 115 | − <div ref={outputRef} className="mt-3 bg-surface border border-white/[0.06] rounded-lg overflow-hidden animate-log-fade-in"> | |
| 116 | − <div className="px-4 py-3 bg-white/[0.02] border-b border-white/[0.06] flex items-center gap-2"> | |
| 117 | − <span className="text-[0.65rem] uppercase tracking-widest text-white/30"> | |
| 118 | − output | |
| 119 | − </span> | |
| 120 | − {isRunning && ( | |
| 121 | − <span className="w-3 h-3 border-2 border-white/20 border-t-green-400 rounded-full animate-spin" /> | |
| 122 | − )} | |
| 123 | − {showOutput && ( | |
| 124 | − <span className="text-[0.65rem] text-green-400"> | |
| 125 | − ✓ completed in 1.2s | |
| 126 | − </span> | |
| 127 | − )} | |
| 128 | − </div> | |
| 129 | − <div className="p-4 overflow-x-auto"> | |
| 130 | − {isRunning ? ( | |
| 131 | − <div className="font-mono text-xs text-white/30 space-y-2"> | |
| 132 | − <div className="animate-pulse">→ Resolving tool: cloudflare.list_zones...</div> | |
| 133 | − <div className="animate-pulse" style={{ animationDelay: "0.3s" }}>→ Policy check: allow (matched role "Infrastructure Admin")</div> | |
| 134 | − <div className="animate-pulse" style={{ animationDelay: "0.6s" }}>→ Executing with credentials: cf_api_token_****</div> | |
| 135 | − </div> | |
| 136 | − ) : ( | |
| 137 | − <pre className="!m-0 !p-0 !bg-transparent !border-none"> | |
| 138 | − <code className="!bg-transparent !p-0 font-mono text-[0.8rem] leading-7 text-green-400/70"> | |
| 139 | − {OUTPUT} | |
| 140 | − </code> | |
| 141 | − </pre> | |
| 142 | − )} | |
| 143 | − </div> | |
| 144 | − </div> | |
| 145 | − )} | |
| 146 | − </div> | |
| 147 | − ); | |
| 148 | −} | |
| @@ -0,0 +1,331 @@ | ||
| 1 | +import { useState, useEffect, useRef, useCallback } from "react"; | |
| 2 | +import Editor, { type OnMount, type Monaco } from "@monaco-editor/react"; | |
| 3 | + | |
| 4 | +// --------------------------------------------------------------------------- | |
| 5 | +// Types | |
| 6 | +// --------------------------------------------------------------------------- | |
| 7 | + | |
| 8 | +interface SourceManifestEntry { | |
| 9 | + name: string; | |
| 10 | + label: string; | |
| 11 | + icon: string; | |
| 12 | + description: string; | |
| 13 | + toolCount: number; | |
| 14 | + fileSizeKb: number; | |
| 15 | + file: string; | |
| 16 | +} | |
| 17 | + | |
| 18 | +interface LoadedSource { | |
| 19 | + name: string; | |
| 20 | + dts: string; | |
| 21 | + disposable?: { dispose(): void }; | |
| 22 | +} | |
| 23 | + | |
| 24 | +// --------------------------------------------------------------------------- | |
| 25 | +// Default code shown in the editor | |
| 26 | +// --------------------------------------------------------------------------- | |
| 27 | + | |
| 28 | +const DEFAULT_CODE = `// Select sources above to enable autocomplete for real API types. | |
| 29 | +// Try typing "tools." and see what's available. | |
| 30 | + | |
| 31 | +const zones = await tools.cloudflare.zones.list_zones({}) | |
| 32 | + | |
| 33 | +const zone = zones.result?.find(z => z.name === "example.com") | |
| 34 | + | |
| 35 | +if (zone) { | |
| 36 | + await tools.cloudflare.dns.create_dns_record({ | |
| 37 | + zone_id: zone.id, | |
| 38 | + type: "A", | |
| 39 | + name: "api", | |
| 40 | + content: "192.0.2.1", | |
| 41 | + proxied: true, | |
| 42 | + }) | |
| 43 | +} | |
| 44 | + | |
| 45 | +const deploys = await tools.vercel.deployments.list_deployments({ | |
| 46 | + limit: 5, | |
| 47 | +}) | |
| 48 | + | |
| 49 | +return { | |
| 50 | + zone: zone?.name, | |
| 51 | + recentDeploys: deploys.deployments?.map(d => ({ | |
| 52 | + url: d.url, | |
| 53 | + state: d.readyState, | |
| 54 | + })), | |
| 55 | +} | |
| 56 | +`; | |
| 57 | + | |
| 58 | +// --------------------------------------------------------------------------- | |
| 59 | +// Component | |
| 60 | +// --------------------------------------------------------------------------- | |
| 61 | + | |
| 62 | +const FALLBACK_MANIFEST: SourceManifestEntry[] = [ | |
| 63 | + { name: "cloudflare", label: "Cloudflare", icon: "☁", description: "DNS, Workers, R2, KV, Zones, WAF", toolCount: 2603, fileSizeKb: 11196, file: "cloudflare.d.ts" }, | |
| 64 | + { name: "vercel", label: "Vercel", icon: "▲", description: "Deployments, Domains, Projects", toolCount: 281, fileSizeKb: 2511, file: "vercel.d.ts" }, | |
| 65 | + { name: "github", label: "GitHub", icon: "⬡", description: "Repos, Issues, Pull Requests, Actions", toolCount: 1079, fileSizeKb: 6301, file: "github.d.ts" }, | |
| 66 | +]; | |
| 67 | + | |
| 68 | +export function SandboxEditor() { | |
| 69 | + const [manifest, setManifest] = useState<SourceManifestEntry[]>(FALLBACK_MANIFEST); | |
| 70 | + const [selected, setSelected] = useState<Set<string>>(new Set(["cloudflare", "vercel", "github"])); | |
| 71 | + const [loading, setLoading] = useState<Set<string>>(new Set()); | |
| 72 | + const [loaded, setLoaded] = useState<Map<string, LoadedSource>>(new Map()); | |
| 73 | + const monacoRef = useRef<Monaco | null>(null); | |
| 74 | + const editorRef = useRef<Parameters<OnMount>[0] | null>(null); | |
| 75 | + | |
| 76 | + // Fetch manifest on mount (overrides fallback with real data if available) | |
| 77 | + useEffect(() => { | |
| 78 | + fetch("/types/manifest.json") | |
| 79 | + .then((r) => { | |
| 80 | + if (!r.ok) throw new Error(`${r.status}`); | |
| 81 | + return r.json(); | |
| 82 | + }) | |
| 83 | + .then((data: SourceManifestEntry[]) => setManifest(data)) | |
| 84 | + .catch(() => { | |
| 85 | + // Keep fallback manifest — editor still shows, just no types loaded | |
| 86 | + }); | |
| 87 | + }, []); | |
| 88 | + | |
| 89 | + // Load/unload type definitions when selection changes | |
| 90 | + useEffect(() => { | |
| 91 | + const monaco = monacoRef.current; | |
| 92 | + if (!monaco) return; | |
| 93 | + | |
| 94 | + // Load newly selected sources | |
| 95 | + for (const name of selected) { | |
| 96 | + if (loaded.has(name) || loading.has(name)) continue; | |
| 97 | + | |
| 98 | + const entry = manifest.find((e) => e.name === name); | |
| 99 | + if (!entry) continue; | |
| 100 | + | |
| 101 | + setLoading((prev) => new Set(prev).add(name)); | |
| 102 | + | |
| 103 | + fetch(`/types/${entry.file}`) | |
| 104 | + .then((r) => r.text()) | |
| 105 | + .then((dts) => { | |
| 106 | + const uri = `file:///types/${name}.d.ts`; | |
| 107 | + const disposable = | |
| 108 | + monaco.languages.typescript.typescriptDefaults.addExtraLib( | |
| 109 | + dts, | |
| 110 | + uri, | |
| 111 | + ); | |
| 112 | + | |
| 113 | + setLoaded((prev) => { | |
| 114 | + const next = new Map(prev); | |
| 115 | + next.set(name, { name, dts, disposable }); | |
| 116 | + return next; | |
| 117 | + }); | |
| 118 | + setLoading((prev) => { | |
| 119 | + const next = new Set(prev); | |
| 120 | + next.delete(name); | |
| 121 | + return next; | |
| 122 | + }); | |
| 123 | + }) | |
| 124 | + .catch((err) => { | |
| 125 | + console.error(`Failed to load types for ${name}:`, err); | |
| 126 | + setLoading((prev) => { | |
| 127 | + const next = new Set(prev); | |
| 128 | + next.delete(name); | |
| 129 | + return next; | |
| 130 | + }); | |
| 131 | + }); | |
| 132 | + } | |
| 133 | + | |
| 134 | + // Dispose deselected sources | |
| 135 | + for (const [name, source] of loaded) { | |
| 136 | + if (!selected.has(name)) { | |
| 137 | + source.disposable?.dispose(); | |
| 138 | + setLoaded((prev) => { | |
| 139 | + const next = new Map(prev); | |
| 140 | + next.delete(name); | |
| 141 | + return next; | |
| 142 | + }); | |
| 143 | + } | |
| 144 | + } | |
| 145 | + }, [selected, manifest, loaded, loading]); | |
| 146 | + | |
| 147 | + const toggle = useCallback((name: string) => { | |
| 148 | + setSelected((prev) => { | |
| 149 | + const next = new Set(prev); | |
| 150 | + if (next.has(name)) next.delete(name); | |
| 151 | + else next.add(name); | |
| 152 | + return next; | |
| 153 | + }); | |
| 154 | + }, []); | |
| 155 | + | |
| 156 | + const handleMount: OnMount = (editor, monaco) => { | |
| 157 | + editorRef.current = editor; | |
| 158 | + monacoRef.current = monaco; | |
| 159 | + | |
| 160 | + // Configure TypeScript compiler | |
| 161 | + monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ | |
| 162 | + target: monaco.languages.typescript.ScriptTarget.ESNext, | |
| 163 | + module: monaco.languages.typescript.ModuleKind.ESNext, | |
| 164 | + moduleResolution: | |
| 165 | + monaco.languages.typescript.ModuleResolutionKind.NodeJs, | |
| 166 | + allowNonTsExtensions: true, | |
| 167 | + strict: true, | |
| 168 | + noEmit: true, | |
| 169 | + esModuleInterop: true, | |
| 170 | + lib: ["esnext"], | |
| 171 | + }); | |
| 172 | + | |
| 173 | + // Disable built-in lib fetching — we provide our own types | |
| 174 | + monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true); | |
| 175 | + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ | |
| 176 | + noSemanticValidation: false, | |
| 177 | + noSyntaxValidation: false, | |
| 178 | + }); | |
| 179 | + }; | |
| 180 | + | |
| 181 | + const totalTools = manifest | |
| 182 | + .filter((s) => selected.has(s.name)) | |
| 183 | + .reduce((sum, s) => sum + s.toolCount, 0); | |
| 184 | + | |
| 185 | + return ( | |
| 186 | + <div> | |
| 187 | + {/* Source toggles */} | |
| 188 | + <div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4"> | |
| 189 | + {manifest.map((source) => { | |
| 190 | + const isSelected = selected.has(source.name); | |
| 191 | + const isLoading = loading.has(source.name); | |
| 192 | + const isLoaded = loaded.has(source.name); | |
| 193 | + | |
| 194 | + return ( | |
| 195 | + <button | |
| 196 | + key={source.name} | |
| 197 | + onClick={() => toggle(source.name)} | |
| 198 | + className={`group text-left p-3 rounded-lg border transition-all duration-200 ${ | |
| 199 | + isSelected | |
| 200 | + ? "border-accent/40 bg-accent/[0.06]" | |
| 201 | + : "border-white/[0.08] bg-white/[0.02] hover:border-white/20" | |
| 202 | + }`} | |
| 203 | + > | |
| 204 | + <div className="flex items-center justify-between"> | |
| 205 | + <div className="flex items-center gap-2"> | |
| 206 | + <span className="text-base">{source.icon}</span> | |
| 207 | + <span className="font-medium text-sm text-[#f5f5f5]"> | |
| 208 | + {source.label} | |
| 209 | + </span> | |
| 210 | + {isLoading && ( | |
| 211 | + <span className="w-3 h-3 border-2 border-white/20 border-t-accent rounded-full animate-spin" /> | |
| 212 | + )} | |
| 213 | + {isSelected && isLoaded && ( | |
| 214 | + <span className="text-[0.6rem] text-green-400/60"> | |
| 215 | + ✓ | |
| 216 | + </span> | |
| 217 | + )} | |
| 218 | + </div> | |
| 219 | + <div | |
| 220 | + className={`w-4 h-4 rounded border-2 flex items-center justify-center transition-all ${ | |
| 221 | + isSelected | |
| 222 | + ? "border-accent bg-accent" | |
| 223 | + : "border-white/20 bg-transparent" | |
| 224 | + }`} | |
| 225 | + > | |
| 226 | + {isSelected && ( | |
| 227 | + <svg | |
| 228 | + className="w-2.5 h-2.5 text-white" | |
| 229 | + fill="none" | |
| 230 | + viewBox="0 0 24 24" | |
| 231 | + stroke="currentColor" | |
| 232 | + strokeWidth={3} | |
| 233 | + > | |
| 234 | + <path | |
| 235 | + strokeLinecap="round" | |
| 236 | + strokeLinejoin="round" | |
| 237 | + d="M5 13l4 4L19 7" | |
| 238 | + /> | |
| 239 | + </svg> | |
| 240 | + )} | |
| 241 | + </div> | |
| 242 | + </div> | |
| 243 | + <div className="mt-1.5 flex items-center justify-between"> | |
| 244 | + <span className="text-xs text-white/40"> | |
| 245 | + {source.description} | |
| 246 | + </span> | |
| 247 | + </div> | |
| 248 | + <div className="mt-1 text-[0.6rem] uppercase tracking-widest text-white/25"> | |
| 249 | + {source.toolCount.toLocaleString()} tools | |
| 250 | + </div> | |
| 251 | + </button> | |
| 252 | + ); | |
| 253 | + })} | |
| 254 | + </div> | |
| 255 | + | |
| 256 | + {/* Stats bar */} | |
| 257 | + <div className="flex items-center justify-between text-xs text-white/40 mb-3 px-1"> | |
| 258 | + <span> | |
| 259 | + <span className="text-[#f5f5f5] font-medium">{selected.size}</span>{" "} | |
| 260 | + source{selected.size !== 1 ? "s" : ""} selected | |
| 261 | + </span> | |
| 262 | + <span> | |
| 263 | + <span className="text-accent font-mono font-medium"> | |
| 264 | + {totalTools.toLocaleString()} | |
| 265 | + </span>{" "} | |
| 266 | + typed tools available | |
| 267 | + </span> | |
| 268 | + </div> | |
| 269 | + | |
| 270 | + {/* Monaco Editor */} | |
| 271 | + <div className="bg-surface border border-white/[0.06] rounded-lg overflow-hidden"> | |
| 272 | + <div className="px-4 py-3 bg-white/[0.02] border-b border-white/[0.06] flex items-center justify-between"> | |
| 273 | + <div className="flex items-center gap-3"> | |
| 274 | + <span className="text-[0.65rem] uppercase tracking-widest text-white/30"> | |
| 275 | + sandbox.ts | |
| 276 | + </span> | |
| 277 | + <span className="text-[0.6rem] px-1.5 py-0.5 rounded bg-white/[0.06] text-white/30"> | |
| 278 | + TypeScript | |
| 279 | + </span> | |
| 280 | + </div> | |
| 281 | + <div className="flex items-center gap-2 text-[0.6rem] text-white/25"> | |
| 282 | + {loading.size > 0 && ( | |
| 283 | + <span className="flex items-center gap-1.5"> | |
| 284 | + <span className="w-2.5 h-2.5 border-2 border-white/20 border-t-accent rounded-full animate-spin" /> | |
| 285 | + Loading types... | |
| 286 | + </span> | |
| 287 | + )} | |
| 288 | + {loading.size === 0 && loaded.size > 0 && ( | |
| 289 | + <span className="text-green-400/50">Types loaded</span> | |
| 290 | + )} | |
| 291 | + </div> | |
| 292 | + </div> | |
| 293 | + <Editor | |
| 294 | + height="420px" | |
| 295 | + defaultLanguage="typescript" | |
| 296 | + defaultValue={DEFAULT_CODE} | |
| 297 | + theme="vs-dark" | |
| 298 | + onMount={handleMount} | |
| 299 | + options={{ | |
| 300 | + minimap: { enabled: false }, | |
| 301 | + fontSize: 13, | |
| 302 | + lineHeight: 24, | |
| 303 | + padding: { top: 16, bottom: 16 }, | |
| 304 | + scrollBeyondLastLine: false, | |
| 305 | + wordWrap: "on", | |
| 306 | + tabSize: 2, | |
| 307 | + automaticLayout: true, | |
| 308 | + suggestOnTriggerCharacters: true, | |
| 309 | + quickSuggestions: true, | |
| 310 | + renderLineHighlight: "none", | |
| 311 | + overviewRulerBorder: false, | |
| 312 | + hideCursorInOverviewRuler: true, | |
| 313 | + scrollbar: { | |
| 314 | + verticalScrollbarSize: 6, | |
| 315 | + horizontalScrollbarSize: 6, | |
| 316 | + }, | |
| 317 | + fontFamily: "'JetBrains Mono', monospace", | |
| 318 | + fontLigatures: true, | |
| 319 | + }} | |
| 320 | + /> | |
| 321 | + </div> | |
| 322 | + | |
| 323 | + {/* Hint */} | |
| 324 | + <div className="mt-4 px-4 py-3 bg-blue-500/[0.05] border-l-2 border-blue-400 text-sm text-white/60"> | |
| 325 | + These are <span className="text-[#f5f5f5] font-medium">real types</span> generated | |
| 326 | + from the actual OpenAPI specs — {totalTools.toLocaleString()} typed tool methods across {selected.size} sources. | |
| 327 | + Try typing <code className="text-accent">tools.</code> and using autocomplete. | |
| 328 | + </div> | |
| 329 | + </div> | |
| 330 | + ); | |
| 331 | +} | |
| @@ -2,9 +2,8 @@ · 1 unmodified line | ||
| 2 | 2 | import Base from "../layouts/Base.astro"; |
| 3 | 3 | import Hero from "../components/Hero.astro"; |
| 4 | 4 | import Section from "../components/Section.astro"; |
| 5 | −import { AddSources } from "../components/AddSources"; | |
| 5 | +import { SandboxEditor } from "../components/SandboxEditor"; | |
| 6 | 6 | import { SystemPrompt } from "../components/SystemPrompt"; |
| 7 | −import { CodeEditor } from "../components/CodeEditor"; | |
| 8 | 7 | import { ExampleQueries } from "../components/ExampleQueries"; |
| 9 | 8 | import { ApprovalFlow } from "../components/ApprovalFlow"; |
| 10 | 9 | import { UsageCards } from "../components/UsageCards"; |
| @@ -33,20 +32,15 @@ import { UsageCards } from "../components/UsageCards"; · 22 unmodified lines | ||
| 33 | 32 | </p> |
| 34 | 33 | |
| 35 | 34 | <!-- ====================================================== |
| 36 | − ADD SOURCES | |
| 35 | + SANDBOX EDITOR — Sources + Monaco with real types | |
| 37 | 36 | ====================================================== --> |
| 38 | − <h2>Add any API as a source</h2> | |
| 37 | + <h2>Add sources, get real types</h2> | |
| 39 | 38 | <p> |
| 40 | − Connect OpenAPI specs, MCP servers, or GraphQL endpoints. Each source is compiled into typed, discoverable tools — not raw schema definitions pasted into context. | |
| 39 | + Connect OpenAPI specs, MCP servers, or GraphQL endpoints. Each source is compiled into a full TypeScript type bundle — real types from the real spec, loaded into a real editor. Toggle sources and see the types update live. | |
| 41 | 40 | </p> |
| 42 | 41 | |
| 43 | − <div class="my-8 bg-surface-elevated border border-white/[0.08] rounded-lg overflow-hidden"> | |
| 44 | − <div class="px-5 py-4 bg-white/[0.02] border-b border-white/[0.06]"> | |
| 45 | − <h4 class="font-serif text-lg font-normal m-0">Add sources</h4> | |
| 46 | − </div> | |
| 47 | − <div class="p-5"> | |
| 48 | − <AddSources client:visible /> | |
| 49 | − </div> | |
| 42 | + <div class="my-8"> | |
| 43 | + <SandboxEditor client:only="react" /> | |
| 50 | 44 | </div> |
| 51 | 45 | |
| 52 | 46 | <!-- ====================================================== |
| @@ -61,18 +55,6 @@ import { UsageCards } from "../components/UsageCards"; · 8 unmodified lines | ||
| 61 | 55 | <SystemPrompt client:visible /> |
| 62 | 56 | </div> |
| 63 | 57 | |
| 64 | − <!-- ====================================================== | |
| 65 | − CODE EDITOR | |
| 66 | − ====================================================== --> | |
| 67 | − <h2>Your agent writes code, not JSON</h2> | |
| 68 | − <p> | |
| 69 | − Instead of rigid JSON tool calls that fail on the first typo, your agent writes TypeScript in a sandboxed runtime. It gets autocomplete-style discovery, can chain multiple operations, handle errors, and return structured results — all in a single execution. | |
| 70 | − </p> | |
| 71 | − | |
| 72 | − <div class="my-8"> | |
| 73 | − <CodeEditor client:visible /> | |
| 74 | − </div> | |
| 75 | − | |
| 76 | 58 | <blockquote> |
| 77 | 59 | The sandbox has no filesystem, no process access, no imports. The only way to reach the outside world is through the <code>tools</code> object — and every call goes through your policies. |
| 78 | 60 | </blockquote> |
| @@ -85,6 +85,7 @@ · 84 unmodified lines | ||
| 85 | 85 | "dependencies": { |
| 86 | 86 | "@astrojs/react": "^4.2.1", |
| 87 | 87 | "@astrojs/tailwind": "^6.0.2", |
| 88 | + "@monaco-editor/react": "^4.7.0", | |
| 88 | 89 | "astro": "^5.10.1", |
| 89 | 90 | "react": "^19.1.0", |
| 90 | 91 | "react-dom": "^19.1.0", |
| @@ -531,6 +532,10 @@ · 440 unmodified lines | ||
| 531 | 532 | |
| 532 | 533 | "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], |
| 533 | 534 | |
| 535 | + "@monaco-editor/loader": ["@monaco-editor/loader@1.7.0", "", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="], | |
| 536 | + | |
| 537 | + "@monaco-editor/react": ["@monaco-editor/react@4.7.0", "", { "dependencies": { "@monaco-editor/loader": "^1.5.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA=="], | |
| 538 | + | |
| 534 | 539 | "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], |
| 535 | 540 | |
| 536 | 541 | "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], |
| @@ -787,6 +792,8 @@ · 250 unmodified lines | ||
| 787 | 792 | |
| 788 | 793 | "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], |
| 789 | 794 | |
| 795 | + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], | |
| 796 | + | |
| 790 | 797 | "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], |
| 791 | 798 | |
| 792 | 799 | "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], |
| @@ -1109,6 +1116,8 @@ · 316 unmodified lines | ||
| 1109 | 1116 | |
| 1110 | 1117 | "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], |
| 1111 | 1118 | |
| 1119 | + "dompurify": ["dompurify@3.2.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw=="], | |
| 1120 | + | |
| 1112 | 1121 | "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], |
| 1113 | 1122 | |
| 1114 | 1123 | "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], |
| @@ -1387,6 +1396,8 @@ · 272 unmodified lines | ||
| 1387 | 1396 | |
| 1388 | 1397 | "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], |
| 1389 | 1398 | |
| 1399 | + "marked": ["marked@14.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="], | |
| 1400 | + | |
| 1390 | 1401 | "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], |
| 1391 | 1402 | |
| 1392 | 1403 | "mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], |
| @@ -1499,6 +1510,8 @@ · 106 unmodified lines | ||
| 1499 | 1510 | |
| 1500 | 1511 | "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], |
| 1501 | 1512 | |
| 1513 | + "monaco-editor": ["monaco-editor@0.55.1", "", { "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" } }, "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A=="], | |
| 1514 | + | |
| 1502 | 1515 | "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], |
| 1503 | 1516 | |
| 1504 | 1517 | "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], |
| @@ -1801,6 +1814,8 @@ · 296 unmodified lines | ||
| 1801 | 1814 | |
| 1802 | 1815 | "stat-mode": ["stat-mode@0.3.0", "", {}, "sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng=="], |
| 1803 | 1816 | |
| 1817 | + "state-local": ["state-local@1.0.7", "", {}, "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="], | |
| 1818 | + | |
| 1804 | 1819 | "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], |
| 1805 | 1820 | |
| 1806 | 1821 | "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], |