mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 11:05:31 +08:00
fd0930a61a
Integrate KiloClaw as a standalone editor panel in the VS Code extension, enabling real-time chat with the KiloClaw bot via Stream Chat. The panel follows the established Agent Manager pattern with proper panel serialization, webview lifecycle management, and a race condition guard for deferred backend connections.
236 lines
6.8 KiB
JavaScript
236 lines
6.8 KiB
JavaScript
const esbuild = require("esbuild")
|
|
const path = require("path")
|
|
const { solidPlugin } = require("esbuild-plugin-solid")
|
|
|
|
const production = process.argv.includes("--production")
|
|
const watch = process.argv.includes("--watch")
|
|
|
|
/**
|
|
* Force all solid-js imports (from kilo-ui and the webview) to resolve to
|
|
* the **same** copy so SolidJS contexts are shared across packages.
|
|
* Without this, the monorepo hoists separate copies (pnpm vs bun) and
|
|
* createContext / useContext can't see each other.
|
|
*
|
|
* @type {import('esbuild').Plugin}
|
|
*/
|
|
const solidDedupePlugin = {
|
|
name: "solid-dedupe",
|
|
setup(build) {
|
|
// Resolve these bare specifiers to the kilo-vscode-local copy
|
|
const solidRoot = path.dirname(require.resolve("solid-js/package.json"))
|
|
const aliases = {
|
|
"solid-js": path.join(solidRoot, "dist", "solid.js"),
|
|
"solid-js/web": path.join(solidRoot, "web", "dist", "web.js"),
|
|
"solid-js/store": path.join(solidRoot, "store", "dist", "store.js"),
|
|
}
|
|
|
|
build.onResolve({ filter: /^solid-js(\/web|\/store)?$/ }, (args) => {
|
|
const key = args.path
|
|
if (aliases[key]) {
|
|
return { path: aliases[key] }
|
|
}
|
|
})
|
|
},
|
|
}
|
|
|
|
/**
|
|
* @type {import('esbuild').Plugin}
|
|
*/
|
|
const esbuildProblemMatcherPlugin = {
|
|
name: "esbuild-problem-matcher",
|
|
|
|
setup(build) {
|
|
build.onStart(() => {
|
|
console.log("[watch] build started")
|
|
})
|
|
build.onEnd((result) => {
|
|
result.errors.forEach(({ text, location }) => {
|
|
console.error(`✘ [ERROR] ${text}`)
|
|
if (location) {
|
|
console.error(` ${location.file}:${location.line}:${location.column}:`)
|
|
}
|
|
})
|
|
console.log("[watch] build finished")
|
|
})
|
|
},
|
|
}
|
|
|
|
/**
|
|
* Stub the pierre worker module so the Diff/Code components work without
|
|
* web workers in the VS Code webview. The `@pierre/diffs` library handles
|
|
* undefined worker pools gracefully (renders without syntax highlighting).
|
|
*
|
|
* We stub the entire worker module rather than just the URL import because
|
|
* `new Worker('')` would throw at runtime.
|
|
*
|
|
* @type {import('esbuild').Plugin}
|
|
*/
|
|
const pierreWorkerStubPlugin = {
|
|
name: "pierre-worker-stub",
|
|
setup(build) {
|
|
// Stub the Vite-specific ?worker&url import
|
|
build.onResolve({ filter: /\?worker&url$/ }, (args) => ({
|
|
path: args.path,
|
|
namespace: "worker-url-stub",
|
|
}))
|
|
build.onLoad({ filter: /.*/, namespace: "worker-url-stub" }, () => ({
|
|
contents: "export default ''",
|
|
loader: "js",
|
|
}))
|
|
|
|
// Stub the pierre worker module so getWorkerPool always returns undefined
|
|
build.onResolve({ filter: /pierre\/worker$/ }, (args) => {
|
|
// Only stub the local UI worker module, not @pierre/diffs/worker
|
|
if (args.path.includes("@pierre")) return
|
|
return {
|
|
path: args.path,
|
|
namespace: "pierre-worker-stub",
|
|
}
|
|
})
|
|
build.onLoad({ filter: /.*/, namespace: "pierre-worker-stub" }, () => ({
|
|
contents: `
|
|
export function getWorkerPool() { return undefined }
|
|
export function getWorkerPools() { return { unified: undefined, split: undefined } }
|
|
export function workerFactory() { return undefined }
|
|
`,
|
|
loader: "js",
|
|
}))
|
|
},
|
|
}
|
|
|
|
const svgSpritePlugin = {
|
|
name: "svg-sprite-inline",
|
|
setup(build) {
|
|
build.onLoad({ filter: /sprite\.svg$/ }, (args) => {
|
|
const content = require("fs").readFileSync(args.path, "utf8")
|
|
return {
|
|
contents: `
|
|
const svg = ${JSON.stringify(content)};
|
|
const inject = () => {
|
|
if (!document.getElementById("kilo-sprite")) {
|
|
const el = document.createElement("div");
|
|
el.id = "kilo-sprite";
|
|
el.style.display = "none";
|
|
el.innerHTML = svg;
|
|
document.body.appendChild(el);
|
|
}
|
|
};
|
|
if (document.body) inject();
|
|
else document.addEventListener("DOMContentLoaded", inject);
|
|
export default "";
|
|
`,
|
|
loader: "js",
|
|
}
|
|
})
|
|
},
|
|
}
|
|
|
|
const cssPackageResolvePlugin = {
|
|
name: "css-package-resolve",
|
|
setup(build) {
|
|
build.onResolve({ filter: /^@/, namespace: "file" }, (args) => {
|
|
if (args.kind === "import-rule") {
|
|
return build.resolve(args.path, {
|
|
kind: "import-statement",
|
|
resolveDir: args.resolveDir,
|
|
})
|
|
}
|
|
})
|
|
},
|
|
}
|
|
|
|
function createBrowserWebviewContext(entryPoint, outfile) {
|
|
return esbuild.context({
|
|
entryPoints: [entryPoint],
|
|
bundle: true,
|
|
format: "iife",
|
|
minify: production,
|
|
sourcemap: !production,
|
|
sourcesContent: false,
|
|
platform: "browser",
|
|
outfile,
|
|
logLevel: "silent",
|
|
loader: {
|
|
".woff": "file",
|
|
".woff2": "file",
|
|
".ttf": "file",
|
|
},
|
|
plugins: [
|
|
solidDedupePlugin,
|
|
pierreWorkerStubPlugin,
|
|
svgSpritePlugin,
|
|
cssPackageResolvePlugin,
|
|
solidPlugin(),
|
|
esbuildProblemMatcherPlugin,
|
|
],
|
|
})
|
|
}
|
|
|
|
async function main() {
|
|
// Build extension
|
|
const extensionCtx = await esbuild.context({
|
|
entryPoints: ["src/extension.ts"],
|
|
bundle: true,
|
|
format: "cjs",
|
|
minify: production,
|
|
sourcemap: !production,
|
|
sourcesContent: false,
|
|
platform: "node",
|
|
outfile: "dist/extension.js",
|
|
external: ["vscode"],
|
|
logLevel: "silent",
|
|
plugins: [esbuildProblemMatcherPlugin],
|
|
})
|
|
|
|
// Build Agent Manager webview (SolidJS, shares components with sidebar)
|
|
const agentManagerCtx = await createBrowserWebviewContext(
|
|
"webview-ui/agent-manager/index.tsx",
|
|
"dist/agent-manager.js",
|
|
)
|
|
|
|
// Build KiloClaw webview (SolidJS, standalone chat panel)
|
|
const kiloClawCtx = await createBrowserWebviewContext("webview-ui/kiloclaw/index.tsx", "dist/kiloclaw.js")
|
|
|
|
// Build Diff Viewer webview (SolidJS, reuses Agent Manager diff components)
|
|
const diffViewerCtx = await createBrowserWebviewContext("webview-ui/diff-viewer/index.tsx", "dist/diff-viewer.js")
|
|
|
|
// Build Diff Virtual webview (lightweight single-file diff for permission approval)
|
|
const diffVirtualCtx = await createBrowserWebviewContext("webview-ui/diff-virtual/index.tsx", "dist/diff-virtual.js")
|
|
|
|
// Build webview
|
|
const webviewCtx = await createBrowserWebviewContext("webview-ui/src/index.tsx", "dist/webview.js")
|
|
|
|
if (watch) {
|
|
await Promise.all([
|
|
extensionCtx.watch(),
|
|
webviewCtx.watch(),
|
|
agentManagerCtx.watch(),
|
|
diffViewerCtx.watch(),
|
|
diffVirtualCtx.watch(),
|
|
kiloClawCtx.watch(),
|
|
])
|
|
} else {
|
|
await Promise.all([
|
|
extensionCtx.rebuild(),
|
|
webviewCtx.rebuild(),
|
|
agentManagerCtx.rebuild(),
|
|
kiloClawCtx.rebuild(),
|
|
diffViewerCtx.rebuild(),
|
|
diffVirtualCtx.rebuild(),
|
|
])
|
|
await Promise.all([
|
|
extensionCtx.dispose(),
|
|
webviewCtx.dispose(),
|
|
agentManagerCtx.dispose(),
|
|
kiloClawCtx.dispose(),
|
|
diffViewerCtx.dispose(),
|
|
diffVirtualCtx.dispose(),
|
|
])
|
|
}
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e)
|
|
process.exit(1)
|
|
})
|