blade-code 0.4.1 → 0.4.2
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/dist/blade.js +4 -3084
- package/dist/chunk-1en8fh5f.js +6 -0
- package/dist/chunk-2tpgtpcp.js +49 -0
- package/dist/chunk-3wnjpk9b.js +1 -0
- package/dist/chunk-3xqfqpa7.js +2 -0
- package/dist/chunk-4nsh53k6.js +4 -0
- package/dist/chunk-52k9k0w8.js +1 -0
- package/dist/chunk-5mtjqqav.js +8 -0
- package/dist/chunk-7ycmx7c2.js +2 -0
- package/dist/chunk-93b0x9b4.js +157 -0
- package/dist/chunk-akr185xe.js +2 -0
- package/dist/chunk-bhjw4b73.js +1 -0
- package/dist/chunk-d7zcd9xp.js +1 -0
- package/dist/chunk-dq65yfh6.js +1 -0
- package/dist/chunk-e2vtbvda.js +24 -0
- package/dist/chunk-ena4sxgr.js +41 -0
- package/dist/chunk-esnkk33h.js +21 -0
- package/dist/chunk-g5pasj7t.js +1 -0
- package/dist/chunk-j25kww7j.js +582 -0
- package/dist/chunk-j2ag7hh6.js +2 -0
- package/dist/chunk-j79jvd54.js +2063 -0
- package/dist/chunk-m99qhdkf.js +76 -0
- package/dist/chunk-n6e89s1v.js +6 -0
- package/dist/chunk-nx2zqct5.js +6 -0
- package/dist/chunk-q52j8tp4.js +1 -0
- package/dist/chunk-qwfywxs2.js +2 -0
- package/dist/chunk-set7v1rf.js +3 -0
- package/dist/chunk-t3zaj1t7.js +4 -0
- package/dist/chunk-th432d0b.js +1 -0
- package/dist/chunk-tmjmp9gq.js +1 -0
- package/dist/chunk-vd38synx.js +1 -0
- package/dist/chunk-vfdhz37s.js +3 -0
- package/dist/chunk-vwn39c84.js +2 -0
- package/dist/chunk-w7h3v075.js +10 -0
- package/dist/chunk-wr7w4af5.js +2 -0
- package/dist/chunk-wxp0yadf.js +1 -0
- package/dist/chunk-yddnm9n2.js +1 -0
- package/dist/chunk-z6tkw0b6.js +13 -0
- package/dist/chunk-zxf08z5f.js +34 -0
- package/dist/chunk-zyc1whnr.js +6 -0
- package/dist/web/assets/CodeBlockHighlighter-C86flFwh.js +1 -0
- package/dist/web/assets/FilePreview-BLcxez_T.js +7 -0
- package/dist/web/assets/MarkdownRenderer-BkQt-J8a.js +1 -0
- package/dist/web/assets/McpModal-B4ReoSE5.js +5 -0
- package/dist/web/assets/SettingsModal-QzwdsQwR.js +1 -0
- package/dist/web/assets/SettingsStore-E-aIqvyl.js +1 -0
- package/dist/web/assets/SkillsModal-CzE0m00G.js +1 -0
- package/dist/web/assets/TerminalPanel-DXA1C6Cg.js +1 -0
- package/dist/web/assets/dialog-CSpPttY0.js +1 -0
- package/dist/web/assets/index-DehGtSvJ.js +10 -0
- package/dist/web/assets/index-DmXmCQOW.css +1 -0
- package/dist/web/assets/useRequest-P_-JRihD.js +1 -0
- package/dist/web/assets/vendor-icons-DlzC8I7m.js +166 -0
- package/dist/web/assets/vendor-markdown-DIcugY1e.js +36 -0
- package/dist/web/assets/vendor-monaco-w6PwaWGP.js +11 -0
- package/dist/web/assets/vendor-radix-CKNFO4jS.js +45 -0
- package/dist/web/assets/vendor-react-Dm1xU4bP.js +49 -0
- package/dist/web/assets/vendor-xterm-9CEnUXvW.css +32 -0
- package/dist/web/assets/vendor-xterm-DuYZoU4T.js +49 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-CVfbjhXj.js +0 -11
- package/dist/web/assets/index-C_uNajWk.js +0 -360
- package/dist/web/assets/index-QhJxewcm.css +0 -32
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{e as zf,f as Tf}from"./chunk-vwn39c84.js";import{B as s,C as Mf,G as Ff,H as g,J as Uf,K as qf,M as Bf,P as h,W as Lf,z as wf}from"./chunk-j79jvd54.js";import{Y as T}from"./chunk-zxf08z5f.js";import{ba as u}from"./chunk-3xqfqpa7.js";import{Ba as xf,Ca as Rf,Ga as z,za as S}from"./chunk-m99qhdkf.js";import{La as _f,Na as t,Sa as Ef}from"./chunk-j25kww7j.js";import{xb as R}from"./chunk-7ycmx7c2.js";import{Bb as x}from"./chunk-set7v1rf.js";import{Db as jf}from"./chunk-j2ag7hh6.js";var I0={port:{type:"number",describe:"Port to listen on (0 for auto)",default:0},hostname:{type:"string",describe:"Hostname to listen on",default:"127.0.0.1"},cors:{type:"array",describe:"Additional domains to allow for CORS",default:[]}};function v0(V){return{port:V.port??0,hostname:V.hostname??"127.0.0.1",cors:V.cors??[]}}import{Hono as T0}from"hono";import{cors as y0}from"hono/cors";import{existsSync as gf,readFileSync as e}from"node:fs";import{createServer as _0}from"node:http";import{networkInterfaces as D0}from"node:os";import{dirname as b0,extname as w0,join as w}from"node:path";import{fileURLToPath as L0}from"node:url";import{WebSocketServer as P0}from"ws";import{z as c}from"zod";class p extends Error{code;statusCode;constructor(V,$,f=500){super($);this.code=V;this.statusCode=f;this.name="BladeServerError"}toObject(){return{error:{code:this.code,message:this.message}}}}class P extends p{constructor(V,$){super("NOT_FOUND",$?`${V} not found: ${$}`:`${V} not found`,404)}}class A extends p{constructor(V){super("BAD_REQUEST",V,400)}}var g0=c.object({error:c.object({code:c.string(),message:c.string()})});import{Hono as cf}from"hono";import{z as k}from"zod";var $f=x("Service"),af=k.object({updates:k.record(k.any()),options:k.object({scope:k.enum(["local","project","global"]).optional(),immediate:k.boolean().optional()}).optional()}),Gf=()=>{let V=new cf;return V.get("/",async($)=>{try{let f=S();return $.json(f||{})}catch(f){return $f.error("[ConfigRoutes] Failed to get config:",f),$.json({})}}),V.put("/",async($)=>{try{let f=await $.req.json(),O=af.safeParse(f);if(!O.success)throw new A("Invalid config update format");let{updates:M,options:J}=O.data;return await z().updateConfig(M,J),$.json({success:!0,updates:M})}catch(f){throw $f.error("[ConfigRoutes] Failed to update config:",f),f}}),V.get("/permissions",async($)=>{try{let f=S();return $.json(f?.permissions||{})}catch(f){return $f.error("[ConfigRoutes] Failed to get permissions:",f),$.json({})}}),V};import{Hono as tf}from"hono";var Nf=()=>{let V=new tf;return V.get("/health",($)=>{return $.json({healthy:!0,version:u()})}),V.get("/info",($)=>{return $.json({version:u(),platform:process.platform,arch:process.arch,nodeVersion:process.version,cwd:R()})}),V};import{Hono as nf}from"hono";var o=x("Service"),mf=()=>{let V=new nf;return V.get("/",async($)=>{try{let O=T.getInstance().getAllServers(),M=Array.from(O.entries()).map(([J,j])=>({id:J,name:J,status:j.status==="connected"?"connected":j.status==="connecting"?"connecting":j.lastError?"error":"offline",endpoint:j.config.command?`${j.config.command} ${(j.config.args||[]).join(" ")}`.trim():j.config.url||"Unknown",description:`MCP server: ${J}`,tools:j.tools.map((Y)=>Y.name),connectedAt:j.connectedAt?.toISOString(),error:j.lastError?.message}));return $.json(M)}catch(f){return o.error("[McpRoutes] Failed to get MCP servers:",f),$.json([])}}),V.post("/:name/connect",async($)=>{try{let f=$.req.param("name");return await T.getInstance().connectServer(f),$.json({success:!0})}catch(f){return o.error("[McpRoutes] Failed to connect MCP server:",f),$.json({success:!1,error:f.message},500)}}),V.post("/:name/disconnect",async($)=>{try{let f=$.req.param("name");return await T.getInstance().disconnectServer(f),$.json({success:!0})}catch(f){return o.error("[McpRoutes] Failed to disconnect MCP server:",f),$.json({success:!1,error:f.message},500)}}),V.delete("/:name",async($)=>{try{let f=$.req.param("name");await T.getInstance().unregisterServer(f);let M=S();if(M?.mcpServers){let{[f]:J,...j}=M.mcpServers;await z().updateConfig({mcpServers:j})}return $.json({success:!0})}catch(f){return o.error("[McpRoutes] Failed to delete MCP server:",f),$.json({success:!1,error:f.message},500)}}),V.post("/",async($)=>{try{let f=await $.req.json(),{name:O,config:M}=f;if(!O||!M)return $.json({success:!1,error:"Missing name or config"},400);await T.getInstance().registerServer(O,M);let j=S();return await z().updateConfig({mcpServers:{...j?.mcpServers,[O]:M}}),$.json({success:!0})}catch(f){return o.error("[McpRoutes] Failed to add MCP server:",f),$.json({success:!1,error:f.message},500)}}),V};import{Hono as sf}from"hono";var a=x("Service"),Xf=()=>{let V=new sf;return V.get("/",async($)=>{try{let f=xf(),O=Rf();return $.json({configured:f,current:O})}catch(f){return a.error("[ModelsRoutes] Failed to get models:",f),$.json({configured:[],current:null})}}),V.post("/",async($)=>{try{let f=await $.req.json(),{provider:O,name:M,model:J,baseUrl:j,apiKey:Y}=f;if(!J)throw new A("model is required");let K=await z().addModel({provider:O||"openai-compatible",name:M||J,model:J,baseUrl:j||void 0,apiKey:Y||void 0});return $.json({success:!0,model:K})}catch(f){throw a.error("[ModelsRoutes] Failed to add model:",f),f}}),V.put("/:modelId",async($)=>{try{let f=$.req.param("modelId"),O=await $.req.json(),{baseUrl:M,apiKey:J,model:j}=O;return await z().updateModel(f,{baseUrl:M||void 0,apiKey:J||void 0,model:j||void 0}),$.json({success:!0})}catch(f){throw a.error("[ModelsRoutes] Failed to update model:",f),f}}),V.delete("/:modelId",async($)=>{try{let f=$.req.param("modelId");return await z().removeModel(f),$.json({success:!0})}catch(f){throw a.error("[ModelsRoutes] Failed to delete model:",f),f}}),V};import{Hono as Z0}from"hono";import{z as C}from"zod";import{Hono as ef}from"hono";import{streamSSE as f0}from"hono/streaming";import{LRUCache as $0}from"lru-cache";import{nanoid as d}from"nanoid";import{z as U}from"zod";var X=x("Service"),V0=U.object({title:U.string().optional(),projectPath:U.string().optional()}),O0=U.object({content:U.string(),attachments:U.array(U.object({type:U.enum(["file","image","url"]),path:U.string().optional(),url:U.string().optional(),content:U.string().optional(),mimeType:U.string().optional(),name:U.string().optional()})).optional(),permissionMode:U.enum(["default","autoEdit","plan","spec","yolo"]).optional()}),M0=U.object({title:U.string().optional()}),B=new Map,D=new $0({max:100,ttl:1800000,dispose:(V,$)=>{if(V.status==="running"||V.status==="waiting_permission")V.abortController.abort(),X.debug(`[SessionRoutes] Run ${$} disposed due to cache eviction`)}}),j0=(V)=>{if(!V||typeof V!=="object")return V;let $={...V},f=200000;if(typeof $.oldContent==="string"&&$.oldContent.length>f)delete $.oldContent;if(typeof $.newContent==="string"&&$.newContent.length>f)delete $.newContent;return $};function J0(V){if(typeof V==="string")return V;return V.filter(($)=>$.type==="text").map(($)=>$.text).join(`
|
|
2
|
+
`)}function Q0(V,$){let f=($??[]).filter((M)=>M.type==="image"&&typeof M.content==="string").map((M)=>({type:"image_url",image_url:{url:M.content}}));if(f.length===0)return V;let O=[];if(V.trim())O.push({type:"text",text:V});return O.push(...f),O}var Wf=()=>{let V=new ef,$=new Map;return V.get("/",async(f)=>{try{let O=await h.listSessions(),M=new Set(O.filter((G)=>G.relationType==="subagent").map((G)=>G.sessionId)),J=Array.from(B.values()).filter((G)=>!M.has(G.id)&&G.relationType!=="subagent").map((G)=>({sessionId:G.id,projectPath:G.projectPath,title:G.title,parentId:void 0,relationType:void 0,status:void 0,agentType:void 0,model:void 0,messageCount:G.messages.length,firstMessageTime:G.createdAt.toISOString(),lastMessageTime:new Date().toISOString(),hasErrors:!1,isActive:!0})),j=new Set(J.map((G)=>G.sessionId)),Y=O.filter((G)=>!B.has(G.sessionId)&&!j.has(G.sessionId)&&G.relationType!=="subagent"),K=new Set(j),Q=Y.filter((G)=>{if(K.has(G.sessionId))return!1;return K.add(G.sessionId),!0}),Z=[...J,...Q];return f.json(Z)}catch(O){return X.error("[SessionRoutes] Failed to list sessions:",O),f.json([])}}),V.post("/",async(f)=>{try{let O=await f.req.json(),M=V0.safeParse(O);if(!M.success)throw new A("Invalid request body");let{title:J,projectPath:j}=M.data,Y=d(12),K=j||f.get("directory")||R(),Q={id:Y,projectPath:K,title:J||`Session ${Y.slice(0,6)}`,createdAt:new Date,messages:[]};return B.set(Y,Q),f.json({sessionId:Y,projectPath:K,title:Q.title,parentId:void 0,relationType:void 0,status:void 0,agentType:void 0,model:void 0,messageCount:0,firstMessageTime:Q.createdAt.toISOString(),lastMessageTime:Q.createdAt.toISOString(),hasErrors:!1})}catch(O){throw X.error("[SessionRoutes] Failed to create session:",O),O}}),V.get("/:sessionId",async(f)=>{let O=f.req.param("sessionId"),M=B.get(O);if(M)return f.json({sessionId:M.id,projectPath:M.projectPath,title:M.title,messageCount:M.messages.length,firstMessageTime:M.createdAt.toISOString(),lastMessageTime:new Date().toISOString(),hasErrors:!1,isActive:!0});try{let j=(await h.listSessions()).find((Y)=>Y.sessionId===O);if(!j)throw new P("Session",O);return f.json(j)}catch(J){if(J instanceof P)throw J;throw X.error("[SessionRoutes] Failed to get session:",J),J}}),V.patch("/:sessionId",async(f)=>{let O=f.req.param("sessionId");try{let M=await f.req.json(),J=M0.safeParse(M);if(!J.success)throw new A("Invalid request body");let{title:j}=J.data,Y=B.get(O);if(Y&&j)Y.title=j;return f.json({success:!0,title:j})}catch(M){throw X.error("[SessionRoutes] Failed to update session:",M),M}}),V.delete("/:sessionId",async(f)=>{let O=f.req.param("sessionId"),M=B.get(O);if(M?.currentRunId){let j=D.get(M.currentRunId);if(j)j.abortController.abort(),g.publish(O,"run.cancelled",{runId:j.id}),D.delete(M.currentRunId)}try{await h.deleteSession(O)}catch(j){X.warn("[SessionRoutes] Failed to delete session file:",j)}B.delete(O);let J=$.get(O);if(J){if(await J.dispose(),$.delete(O),$.size===0)await T.getInstance().disconnectAll()}return f.json({success:!0})}),V.get("/:sessionId/message",async(f)=>{let O=f.req.param("sessionId");try{let M=await h.loadSession(O);return f.json(M)}catch(M){return X.error("[SessionRoutes] Failed to get messages:",M),f.json([])}}),V.get("/:sessionId/events",async(f)=>{let O=f.req.param("sessionId"),M=B.get(O);if(!M){let J=f.get("directory")||R();M={id:O,projectPath:J,title:`Session ${O.slice(0,6)}`,createdAt:new Date,messages:[]},B.set(O,M)}return f0(f,async(J)=>{await J.writeSSE({data:JSON.stringify({type:"connected",properties:{sessionId:O,timestamp:Date.now()}})});let Y=g.subscribe((Q)=>{if(Q.sessionId!==O)return;J.writeSSE({data:JSON.stringify({type:Q.type,properties:{sessionId:Q.sessionId,...Q.properties}})}).catch(()=>{})}),K=setInterval(()=>{if(!J.aborted)J.writeSSE({data:JSON.stringify({type:"heartbeat",properties:{timestamp:Date.now()}})}).catch(()=>{})},15000);J.onAbort(()=>{clearInterval(K),Y()});while(!0)if(await new Promise((Q)=>setTimeout(Q,1000)),J.aborted)break})}),V.post("/:sessionId/message",async(f)=>{let O=f.req.param("sessionId"),M=await f.req.json(),J=O0.safeParse(M);if(!J.success)throw new A("Invalid message format");let{content:j,attachments:Y,permissionMode:K}=J.data,Q=K||"default",Z=f.get("directory")||R(),G=Q0(j,Y),m=B.get(O);if(!m){let N=await h.loadSession(O).catch(()=>[]);m={id:O,projectPath:Z,title:`Session ${O.slice(0,6)}`,createdAt:new Date,messages:N},B.set(O,m)}let q=d(12),L=new AbortController,E={id:q,sessionId:O,status:"running",abortController:L,createdAt:new Date};return D.set(q,E),m.currentRunId=q,Y0(E,m,G,Q,$).catch((N)=>{X.error(`[SessionRoutes] Run ${q} failed:`,N)}),f.json({runId:q,status:"running"},202)}),V.post("/:sessionId/abort",async(f)=>{let O=f.req.param("sessionId"),M=B.get(O);if(M?.currentRunId){let J=D.get(M.currentRunId);if(J)J.abortController.abort(),J.status="cancelled",g.publish(O,"run.cancelled",{runId:J.id})}return f.json({success:!0})}),V.get("/:sessionId/status",async(f)=>{let O=f.req.param("sessionId"),M=B.get(O);if(!M?.currentRunId)return f.json({sessionId:O,status:"idle"});let J=D.get(M.currentRunId);return f.json({sessionId:O,runId:M.currentRunId,status:J?.status||"idle"})}),V};async function Y0(V,$,f,O,M){let{abortController:J,sessionId:j,id:Y}=V,K=d(12),Q=d(12),Z=(G,m)=>{g.publish(j,G,m)};try{Z("message.created",{messageId:K,role:"user",content:J0(f)}),Z("session.status",{status:"running"}),Z("message.created",{messageId:Q,role:"assistant",content:""});let G=M.get(j);if(!G)G=await Ff.create({sessionId:j}),M.set(j,G);let m=await Bf.createWithRuntime(G,{sessionId:j}),q=async(N)=>{let F=d(12),l=300000;V.status="waiting_permission";let ff=new Promise((Kf)=>{let rf=setTimeout(()=>{X.warn(`[SessionRoutes] Permission ${F} timed out after 300000ms`),Z("permission.timeout",{requestId:F}),Kf({approved:!1,reason:"timeout"})},300000);V.pendingPermission={permissionId:F,resolve:(lf)=>{clearTimeout(rf),Kf(lf)},details:N}});Z("permission.asked",{requestId:F,toolName:N.toolName,description:N.message,args:N.args,details:N}),X.info(`[SessionRoutes] Permission request created: ${F}, runId: ${Y}`);let Zf=await ff;return X.info(`[SessionRoutes] Permission response received: ${F}, approved: ${Zf.approved}`),V.status="running",V.pendingPermission=void 0,Zf},L={messages:[...$.messages],userId:"web-user",sessionId:j,workspaceRoot:$.projectPath,signal:J.signal,permissionMode:O,confirmationHandler:{requestConfirmation:q}},E=await Ef(m.chatStream(f,L,{stream:!0}),async(N)=>{switch(N.kind){case"content_delta":Z("message.delta",{messageId:Q,delta:N.delta});break;case"thinking_delta":Z("thinking.delta",{delta:N.delta});break;case"tool_start":if("function"in N.toolCall)Z("tool.start",{messageId:Q,toolName:N.toolCall.function.name,toolCallId:N.toolCall.id,arguments:N.toolCall.function.arguments,toolKind:N.toolKind});break;case"tool_result":if("function"in N.toolCall)Z("tool.result",{messageId:Q,toolName:N.toolCall.function.name,toolCallId:N.toolCall.id,success:!N.result.error,summary:N.result.metadata?.summary,output:qf(Uf(N.toolCall.function.name,N.result)),metadata:j0(N.result.metadata)});break;case"token_usage":Z("token.usage",{...N.usage});break;case"task_update":Z("task.updated",{tasks:N.tasks});break;default:break}});$.messages=[...L.messages],Z("message.complete",{messageId:Q}),Z("thinking.completed",{}),V.status="completed",Z("session.completed",{runId:Y,outputTruncated:E.metadata?.outputTruncated??!1}),Z("session.status",{status:"idle"})}catch(G){X.error("[SessionRoutes] Agent execution error:",G),V.status="failed",Z("session.error",{error:G instanceof Error?G.message:"Unknown error"}),Z("session.status",{status:"error"})}}function Af(V,$,f){X.info(`[SessionRoutes] Looking for permission ${$} in session ${V}`),X.info(`[SessionRoutes] Active runs: ${D.size}`);for(let[O,M]of D.entries())if(X.info(`[SessionRoutes] Checking run ${O}: sessionId=${M.sessionId}, pendingPermission=${M.pendingPermission?.permissionId}`),M.sessionId===V&&M.pendingPermission?.permissionId===$)return M.pendingPermission.resolve(f),X.info(`[SessionRoutes] Permission ${$} responded, runId: ${M.id}`),!0;return X.error(`[SessionRoutes] Permission not found: ${$}`),!1}var Vf=x("Service"),K0=C.object({approved:C.boolean(),remember:C.boolean().optional(),scope:C.enum(["once","session"]).optional(),targetMode:C.enum(["default","autoEdit","plan","spec","yolo"]).optional(),feedback:C.string().optional(),answers:C.record(C.union([C.string(),C.array(C.string())])).optional()}),Cf=()=>{let V=new Z0;return V.post("/:permissionId",async($)=>{let f=$.req.param("permissionId"),O=$.req.query("sessionId");Vf.info(`[PermissionRoutes] Received permission response: permissionId=${f}, sessionId=${O}`);try{let M=await $.req.json(),J=K0.safeParse(M);if(!J.success)throw new A("Invalid permission response format");let{approved:j,remember:Y,scope:K,targetMode:Q,feedback:Z,answers:G}=J.data;if(!O)throw new A("sessionId query parameter is required");if(!Af(O,f,{approved:j,reason:Z,scope:K,targetMode:Q,feedback:Z,answers:G}))throw new P("Permission request",f);return Vf.info(`[PermissionRoutes] Permission ${f} ${j?"approved":"denied"}`),$.json({success:!0,approved:j,remember:Y})}catch(M){throw Vf.error("[PermissionRoutes] Failed to respond to permission:",M),M}}),V};import{Hono as G0}from"hono";var Hf=x("Service"),yf=()=>{let V=new G0;return V.get("/",async($)=>{try{let f=await zf();return $.json(f)}catch(f){return Hf.error("[ProviderRoutes] Failed to list providers:",f),$.json([])}}),V.get("/:provider/models",async($)=>{let f=$.req.param("provider");try{let O=await Tf(f);return $.json(O)}catch(O){return Hf.error("[ProviderRoutes] Failed to list models:",O),$.json([])}}),V};import{Hono as N0}from"hono";import*as y from"node:fs/promises";import{homedir as m0}from"node:os";import*as n from"node:path";var b=x("Service"),Of=n.join(m0(),".blade","skills-config.json");async function Df(){try{let V=await y.readFile(Of,"utf-8");return JSON.parse(V)}catch{return{disabled:[]}}}async function x0(V){await y.mkdir(n.dirname(Of),{recursive:!0}),await y.writeFile(Of,JSON.stringify(V,null,2))}async function R0(V,$){let f=await Df();if($)f.disabled=f.disabled.filter((O)=>O!==V);else if(!f.disabled.includes(V))f.disabled.push(V);await x0(f)}var I=null,X0=300000;async function E0(){if(I&&Date.now()-I.timestamp<X0)return I.data;try{let V=await fetch("https://api.github.com/repos/anthropics/skills/contents/skills",{headers:{Accept:"application/vnd.github.v3+json","User-Agent":"Blade-Skills-Catalog"}});if(!V.ok)return b.warn(`GitHub API returned ${V.status}`),I?.data||[];let $=await V.json(),f=[];for(let O of $){if(O.type!=="dir")continue;let M=`Official ${O.name} skill`;try{let J=await fetch(`https://raw.githubusercontent.com/anthropics/skills/main/skills/${O.name}/SKILL.md`,{headers:{"User-Agent":"Blade-Skills-Catalog"}});if(J.ok){let Y=(await J.text()).match(/^#[^\n]*\n+([^\n#]+)/);if(Y)M=Y[1].trim().slice(0,100)}}catch{}f.push({name:O.name,description:M,tag:"Official",author:"Anthropic"})}return I={data:f,timestamp:Date.now()},f}catch(V){return b.warn("Failed to fetch skills catalog from GitHub:",V),I?.data||[]}}var bf=()=>{let V=new N0;return V.get("/",async($)=>{try{let f=t();await f.initialize();let O=f.getAll(),M=await Df(),J=O.map((j)=>({id:j.name,name:j.name,enabled:!M.disabled.includes(j.name),description:j.description||"",version:j.version||"1.0.0",provider:"Local",location:j.source==="builtin"?"Built-in":j.basePath,capabilities:["prompts"],allowedTools:j.allowedTools||[]}));return $.json(J)}catch(f){return b.error("[SkillsRoutes] Failed to get skills:",f),$.json([])}}),V.post("/:name/toggle",async($)=>{try{let f=$.req.param("name"),O=await $.req.json();return await R0(f,O.enabled),$.json({success:!0,enabled:O.enabled})}catch(f){return b.error("[SkillsRoutes] Failed to toggle skill:",f),$.json({success:!1,error:f.message},500)}}),V.delete("/:name",async($)=>{try{let f=$.req.param("name"),O=`${process.env.HOME}/.blade/skills/${f}`;return await y.rm(O,{recursive:!0,force:!0}),await t().refresh(),$.json({success:!0})}catch(f){return b.error("[SkillsRoutes] Failed to uninstall skill:",f),$.json({success:!1,error:f.message},500)}}),V.post("/install",async($)=>{try{let f=await $.req.json(),O=_f(),M=t(),J=!1;if(f.source==="catalog"&&f.name){if(J=await O.installOfficialSkill(f.name),!J)return $.json({success:!1,error:"Failed to install skill from catalog"},500)}else if(f.source==="repo"&&f.url){if(J=await O.installFromRepo(f.url,f.name),!J)return $.json({success:!1,error:"Failed to install skill from repository. Make sure the repo contains a SKILL.md file."},500)}else if(f.source==="local"&&f.path){if(J=await O.installFromLocal(f.path,f.name),!J)return $.json({success:!1,error:"Failed to install skill from local path. Make sure the path exists and contains a SKILL.md file."},500)}else return $.json({success:!1,error:"Invalid install parameters. Required: source with corresponding url/path/name"},400);return await M.refresh(),$.json({success:!0})}catch(f){return b.error("[SkillsRoutes] Failed to install skill:",f),$.json({success:!1,error:f.message},500)}}),V.get("/catalog",async($)=>{try{let f=await E0();return $.json(f)}catch(f){return b.error("[SkillsRoutes] Failed to get catalog:",f),$.json([])}}),V};import F0 from"fast-glob";import{Hono as U0}from"hono";import{execSync as q0}from"node:child_process";import{readdir as B0}from"node:fs/promises";import i from"node:path";var r=x("Service"),W0=(V)=>{try{return q0("git rev-parse --abbrev-ref HEAD",{cwd:V,encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}},A0=[...s.map((V)=>`${V}/**`),...s,...Mf.map((V)=>`**/${V}`)],C0=new Set(["/theme","/model","/permissions","/mcp","/skills","/exit","/resume","/ide","/login","/logout"]),Pf=()=>{let V=new U0;return V.get("/commands",async($)=>{try{let f=$.req.query("q")||"",O=Lf(f).filter((M)=>!C0.has(M.command));return $.json(O)}catch(f){return r.error("[SuggestionsRoutes] Failed to get command suggestions:",f),$.json([])}}),V.get("/files",async($)=>{try{let f=$.req.query("q")||"",O=$.get("directory")||R(),M=Math.min(Number($.req.query("limit"))||100,1000),j=(await F0("**/*",{cwd:O,dot:!1,followSymbolicLinks:!1,onlyFiles:!1,markDirectories:!0,unique:!0,ignore:A0})).map((Q)=>Q.replace(/\\/g,"/"));if(!f)return $.json(j.slice(0,M));let Y=f.toLowerCase(),K=j.filter((Q)=>Q.toLowerCase().includes(Y)).slice(0,M);return $.json(K)}catch(f){return r.error("[SuggestionsRoutes] Failed to get file suggestions:",f),$.json([])}}),V.get("/files/tree",async($)=>{try{let f=$.get("directory")||R(),O=$.req.query("path")||"",M=O?i.join(f,O):f,j=(await B0(M,{withFileTypes:!0})).map((Q)=>({name:Q.name,path:O?`${O}/${Q.name}`:Q.name,type:Q.isDirectory()?"dir":"file"}));j.sort((Q,Z)=>{if(Q.type!==Z.type)return Q.type==="dir"?-1:1;return Q.name.localeCompare(Z.name)});let Y=new Set(s),K=j.filter((Q)=>{let Z=Q.name;if(Z.startsWith("."))return!1;if(Y.has(Z))return!1;for(let G of Mf)if(Z.match(new RegExp(G.replace("*",".*"))))return!1;return!0});return $.json(K)}catch(f){return r.error("[SuggestionsRoutes] Failed to get file tree:",f),$.json([])}}),V.get("/files/content",async($)=>{try{let f=$.req.query("path");if(!f)return $.json({error:"Missing file path"},400);let O=$.get("directory")||R(),M=i.resolve(O,f),J=i.relative(O,M);if(J.startsWith("..")||i.isAbsolute(J))return $.json({error:"Invalid file path"},400);let j=wf(),Y=await j.stat(M);if(!Y?.isFile)return $.json({error:"File not found"},404);let K=200000,Q=await j.readTextFile(M),Z=Q.length>K;return $.json({path:f,content:Z?Q.slice(0,K):Q,truncated:Z,size:Y.size})}catch(f){return r.error("[SuggestionsRoutes] Failed to get file content:",f),$.json({error:"Failed to read file"},500)}}),V.get("/git-info",async($)=>{try{let f=$.get("directory")||R(),O=W0(f);return $.json({branch:O})}catch(f){return r.error("[SuggestionsRoutes] Failed to get git info:",f),$.json({branch:null})}}),V};import{Hono as H0}from"hono";var _=x("Service"),v=new Map,kf=2097152,Sf=65536;function uf(){return typeof globalThis.Bun<"u"}async function z0(V,$,f){if(uf()){let{spawn:J}=await import("bun-pty"),j=J(V,$,{name:"xterm-256color",cwd:f.cwd,env:f.env});return{pid:j.pid,write:(Y)=>j.write(Y),resize:(Y,K)=>j.resize(Y,K),kill:()=>j.kill(),onData:(Y)=>j.onData(Y),onExit:(Y)=>j.onExit(Y)}}let M=(await import("node-pty")).spawn(V,$,{name:"xterm-256color",cwd:f.cwd,env:f.env,cols:80,rows:24});return{pid:M.pid,write:(J)=>M.write(J),resize:(J,j)=>M.resize(J,j),kill:()=>M.kill(),onData:(J)=>{M.onData(J)},onExit:(J)=>{M.onExit(J)}}}var Qf=void 0,Jf;if(uf())try{let{createBunWebSocket:V}=await import("hono/bun"),$=V();Jf=$.upgradeWebSocket,Qf=$.websocket}catch(V){_.warn("[Terminal] Failed to initialize Bun WebSocket support:",V)}async function hf(V,$){let f=`term-${Date.now()}`;_.info(`[Terminal] New connection: ${f}, cwd: ${V}`);let O=process.platform==="win32"?"powershell.exe":process.env.SHELL||"/bin/zsh",M=O.endsWith("sh")?["-l"]:[],J=await z0(O,M,{cwd:V,env:{...process.env,TERM:"xterm-256color",COLORTERM:"truecolor"}}),j={id:f,process:J,cwd:V,buffer:"",subscribers:new Set};if(v.set(f,j),j.subscribers.add($),J.onData((Y)=>{let K=!1;for(let Q of j.subscribers)try{Q.send(Y),K=!0}catch{j.subscribers.delete(Q)}if(K)return;if(j.buffer+=Y,j.buffer.length>kf)j.buffer=j.buffer.slice(-kf)}),J.onExit(({exitCode:Y})=>{_.info(`[Terminal] Process exited: ${f}, code: ${Y}`);for(let K of j.subscribers)try{K.close()}catch{}v.delete(f)}),j.buffer){let Y=j.buffer;j.buffer="";for(let K=0;K<Y.length;K+=Sf)$.send(Y.slice(K,K+Sf))}return{session:j,cleanup:()=>{if(_.info(`[Terminal] Connection closed: ${f}`),j.subscribers.delete($),j.subscribers.size===0){try{j.process.kill()}catch{}v.delete(f)}}}}function If(V,$){V.on("connection",async(f,O)=>{let M=new URL(O.url||"/",`http://${O.headers.host}`);if(!M.pathname.endsWith("/terminal/ws")){f.close();return}let J=M.searchParams.get("cwd")||$()||R();try{let{session:j,cleanup:Y}=await hf(J,{send:(K)=>f.send(K),close:()=>f.close()});f.on("message",(K)=>{try{j.process.write(String(K))}catch(Q){_.error("[Terminal] Failed to write to PTY:",Q)}}),f.on("close",Y),f.on("error",Y)}catch(j){_.error("[Terminal] Failed to spawn PTY:",j),f.close()}})}var vf=()=>{let V=new H0;if(V.get("/status",($)=>{let f=Array.from(v.values()).map((O)=>({id:O.id,cwd:O.cwd}));return $.json({active:f.length,terminals:f})}),V.post("/kill/:terminalId",($)=>{let f=$.req.param("terminalId"),O=v.get(f);if(O){try{O.process.kill()}catch{}for(let M of O.subscribers)M.close();return v.delete(f),$.json({success:!0})}return $.json({success:!1,error:"Terminal not found"},404)}),Jf)V.get("/ws",Jf(($)=>{let f=$.req.query("cwd")||$.get("directory")||R(),O;return{async onOpen(M,J){try{O=await hf(f,{send:(j)=>J.send(j),close:()=>J.close()})}catch(j){_.error("[Terminal] Failed to spawn PTY:",j),J.close()}},onMessage(M){if(!O)return;try{O.session.process.write(String(M.data))}catch(J){_.error("[Terminal] Failed to write to PTY:",J)}},onClose(){O?.cleanup()}}}));else V.get("/ws",($)=>{return $.json({message:"WebSocket endpoint - connect via ws:// protocol",hint:"Use WebSocket client to connect to this endpoint"})});return V};var W=x("Service"),Yf=[];function k0(){let V=b0(L0(import.meta.url)),$=[w(V,"web"),w(R(),"dist/web"),w(R(),"packages/cli/dist/web")];for(let f of $){let O=w(f,"index.html");if(gf(O)){if(e(O,"utf-8").includes("/assets/"))return W.debug(`[Server] Found web dist at: ${f}`),f}}return null}function S0(){let V=new T0;V.onError((f,O)=>{if(W.error("[Server] Request error:",f),f instanceof p)return O.json(f.toObject(),f.statusCode);let M=f instanceof Error?f.message:String(f);return O.json({error:{code:"INTERNAL_ERROR",message:M}},500)}),V.use(async(f,O)=>{let M=process.env.BLADE_SERVER_PASSWORD;if(!M)return O();let J=f.req.path;if(J==="/"||J.startsWith("/assets/")||J.endsWith(".html")||J.endsWith(".js")||J.endsWith(".css"))return O();let j=f.req.header("Authorization");if(!j?.startsWith("Basic "))return f.header("WWW-Authenticate",'Basic realm="Blade Server"'),f.json({error:{code:"UNAUTHORIZED",message:"Authentication required"}},401);let Y=Buffer.from(j.slice(6),"base64").toString(),[K,Q]=Y.split(":"),Z=process.env.BLADE_SERVER_USERNAME??"blade";if(K!==Z||Q!==M)return f.json({error:{code:"UNAUTHORIZED",message:"Invalid credentials"}},401);return O()}),V.use(async(f,O)=>{let M=f.req.path==="/health"||f.req.path==="/global/health"||f.req.path.startsWith("/assets/");if(!M)W.debug(`[Server] ${f.req.method} ${f.req.path}`);let J=Date.now();if(await O(),!M){let j=Date.now()-J;W.debug(`[Server] ${f.req.method} ${f.req.path} - ${f.res.status} (${j}ms)`)}}),V.use(y0({origin(f){if(!f)return;if(f.startsWith("http://localhost:"))return f;if(f.startsWith("http://127.0.0.1:"))return f;if(f==="tauri://localhost"||f==="http://tauri.localhost")return f;if(Yf.includes(f))return f;return}})),V.use(async(f,O)=>{let M=f.req.query("directory")||f.req.header("x-blade-directory")||R();try{M=decodeURIComponent(M)}catch{}return f.set("directory",M),O()}),V.route("/global",Nf()),V.route("/sessions",Wf()),V.route("/configs",Gf()),V.route("/permissions",Cf()),V.route("/providers",yf()),V.route("/models",Xf()),V.route("/suggestions",Pf()),V.route("/terminal",vf()),V.route("/mcp",mf()),V.route("/skills",bf()),V.get("/health",(f)=>{return f.json({healthy:!0,version:u()})});let $=k0();if($)W.info(`[Server] Serving static files from ${$}`),V.get("/assets/*",(f)=>{let O=w($,f.req.path);if(!gf(O))return f.json({error:{code:"NOT_FOUND",message:`File not found: ${f.req.path}`}},404);let M=e(O),J=w0(O).toLowerCase(),Y={".js":"application/javascript",".css":"text/css",".html":"text/html",".json":"application/json",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".ico":"image/x-icon",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".eot":"application/vnd.ms-fontobject"}[J]||"application/octet-stream";return new Response(M,{headers:{"Content-Type":Y,"Cache-Control":"public, max-age=31536000, immutable"}})}),V.get("/",(f)=>{let O=w($,"index.html"),M=e(O,"utf-8");return f.html(M)}),V.get("*",(f)=>{let O=f.req.path;if(O.includes("."))return f.json({error:{code:"NOT_FOUND",message:`File not found: ${O}`}},404);let M=w($,"index.html"),J=e(M,"utf-8");return f.html(J)});else W.warn('[Server] Web UI not found. Run "cd packages/cli/web && bun run build" to enable web interface.'),V.get("/",(f)=>{return f.json({message:"Blade API Server",version:u(),webUI:!1,hint:'Web UI not built. Run "cd packages/cli/web && bun run build" to enable.',endpoints:{health:"/health",sessions:"/sessions",configs:"/configs",permissions:"/permissions",providers:"/providers"}})}),V.all("*",(f)=>{return f.json({error:{code:"NOT_FOUND",message:`Route not found: ${f.req.path}`}},404)});return V}function u0(){let V=D0(),$=[];for(let f of Object.keys(V)){let O=V[f];if(!O)continue;for(let M of O){if(M.internal||M.family!=="IPv4")continue;if(M.address.startsWith("172."))continue;$.push(M.address)}}return $}function pf(){return typeof globalThis.Bun<"u"}function of(V,$){let f=globalThis.Bun,O=(J)=>{try{return f.serve({hostname:$.hostname,port:J,fetch:V.fetch,idleTimeout:0,websocket:Qf})}catch(j){W.error("Failed to start Bun server:",j);return}},M=$.port===0?O(4096)??O(0):O($.port);if(!M)throw Error(`Failed to start Bun server on port ${$.port}`);return{url:M.url,port:M.port,hostname:M.hostname,stop:async()=>{M.stop(!0)}}}function h0(V,$){return new Promise((f,O)=>{let M=_0(async(Q,Z)=>{let G=new URL(Q.url||"/",`http://${Q.headers.host}`),m=new Headers;for(let[E,N]of Object.entries(Q.headers))if(N)if(Array.isArray(N))for(let F of N)m.append(E,F);else m.set(E,N);let q;if(Q.method!=="GET"&&Q.method!=="HEAD")q=(await new Promise((N)=>{let F=[];Q.on("data",(l)=>F.push(l)),Q.on("end",()=>N(Buffer.concat(F)))})).toString();let L=new Request(G.toString(),{method:Q.method,headers:m,body:q});try{let E=await V.fetch(L);if(Z.statusCode=E.status,E.headers.forEach((N,F)=>{Z.setHeader(F,N)}),E.body){let N=E.body.getReader(),F=async()=>{let{done:l,value:ff}=await N.read();if(l){Z.end();return}return Z.write(ff),F()};await F()}else{let N=await E.text();Z.end(N)}}catch(E){W.error("[Server] Node request error:",E),Z.statusCode=500,Z.end(JSON.stringify({error:{code:"INTERNAL_ERROR",message:"Internal server error"}}))}}),J=new P0({noServer:!0}),j=R();If(J,()=>j),M.on("upgrade",(Q,Z,G)=>{if(new URL(Q.url||"/",`http://${Q.headers.host}`).pathname==="/terminal/ws")J.handleUpgrade(Q,Z,G,(q)=>{J.emit("connection",q,Q)});else Z.destroy()});let Y=(Q)=>{return new Promise((Z,G)=>{M.once("error",(m)=>{if(m.code==="EADDRINUSE")G(m);else G(m)}),M.listen(Q,$.hostname,()=>{let m=M.address();if(m&&typeof m==="object")Z(m.port);else Z(Q)})})};(async()=>{let Q;if($.port===0)try{Q=await Y(4096)}catch{Q=await Y(0)}else Q=await Y($.port);let Z=new URL(`http://${$.hostname==="0.0.0.0"?"localhost":$.hostname}:${Q}`);f({url:Z,port:Q,hostname:$.hostname,stop:async()=>{return new Promise((G)=>{J.close(()=>{M.close(()=>G())})})}})})().catch(O)})}var df;((DV)=>{let V,$;function f(){if(!$)$=S0();return $}DV.getApp=f;function O(j){Yf=j.cors??[];let Y=f();if(pf())V=of(Y,j),W.info(`[Server] Blade server listening on ${V.url} (Bun runtime)`);else throw Error("Blade web server requires Bun runtime. Please run with Bun: `bun run blade web` or install Bun from https://bun.sh");let K=V;return{url:K.url,port:K.port,hostname:K.hostname,stop:async()=>{if(V)await V.stop(),V=void 0,$=void 0,W.info("[Server] Blade server stopped")}}}DV.listen=O;async function M(j){Yf=j.cors??[];let Y=f();if(pf())V=of(Y,j),W.info(`[Server] Blade server listening on ${V.url} (Bun runtime)`);else V=await h0(Y,j),W.info(`[Server] Blade server listening on ${V.url} (Node.js runtime)`);let K=V;return{url:K.url,port:K.port,hostname:K.hostname,stop:async()=>{if(V)await V.stop(),V=void 0,$=void 0,W.info("[Server] Blade server stopped")}}}DV.listenAsync=M;function J(){return V!==void 0}DV.isRunning=J})(df||={});export{I0 as a,v0 as b,u0 as c,df as d};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{g as o}from"./chunk-qwfywxs2.js";import"./chunk-j2ag7hh6.js";import{execSync as r}from"child_process";var l={command:"update",describe:"Check for updates and install if available",handler:async()=>{console.log("Checking for updates...");try{let e=await o(!0);if(console.log(`Current version: ${e.currentVersion}`),e.error){console.log(`Warning: ${e.error}`);return}if(e.latestVersion)console.log(`Latest version: ${e.latestVersion}`);if(e.hasUpdate&&e.latestVersion){console.log(""),console.log(`\x1B[33mUpdate available: ${e.currentVersion} -> ${e.latestVersion}\x1B[0m`),console.log(""),console.log("Updating...");try{r("npm install -g blade-code@latest --registry https://registry.npmjs.org",{stdio:"inherit"}),console.log(""),console.log("Update complete!")}catch(t){console.error("Error: Update failed. Please run manually:"),console.error(" npm install -g blade-code@latest"),process.exit(1)}}else console.log("You are running the latest version of Blade")}catch(e){console.error(`Error: Failed to check for updates: ${e instanceof Error?e.message:"未知错误"}`),process.exit(1)}}};export{l as updateCommands};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{aa as a,ba as b,ca as c,da as d,ea as e,fa as f}from"./chunk-3xqfqpa7.js";import"./chunk-j2ag7hh6.js";export{b as getVersion,c as getPackageName,a as getPackageInfo,e as getFormattedVersion,d as getDescription,f as getCopyright};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ga as a}from"./chunk-2tpgtpcp.js";import"./chunk-m99qhdkf.js";import"./chunk-e2vtbvda.js";import"./chunk-7ycmx7c2.js";import"./chunk-set7v1rf.js";import"./chunk-j2ag7hh6.js";export{a as SpecManager};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import{xb as Y}from"./chunk-7ycmx7c2.js";import{Bb as M}from"./chunk-set7v1rf.js";var D={currentModelId:"",models:[],temperature:0,maxContextTokens:128000,maxOutputTokens:void 0,stream:!0,topP:0.9,topK:50,timeout:180000,theme:"dracula",uiTheme:"system",language:"zh-CN",fontSize:14,autoSaveSessions:!0,notifyBuild:!0,notifyErrors:!1,notifySounds:!1,privacyTelemetry:!1,privacyCrash:!0,debug:!1,mcpEnabled:!1,mcpServers:{},permissions:{allow:["Bash(pwd)","Bash(which *)","Bash(whoami)","Bash(hostname)","Bash(uname *)","Bash(date)","Bash(echo *)","Bash(ls *)","Bash(tree *)","Bash(git status)","Bash(git status -*)","Bash(git log *)","Bash(git diff *)","Bash(git show *)","Bash(git branch -* *)","Bash(git branch)","Bash(git tag -l *)","Bash(git tag --list *)","Bash(git stash list *)","Bash(git stash show *)","Bash(git rev-parse *)","Bash(git describe *)","Bash(git blame *)","Bash(git ls-files *)","Bash(git config --get *)","Bash(git config --list *)","Bash(git shortlog *)","Bash(git merge-base *)","Bash(git cat-file *)","Bash(git for-each-ref *)","Bash(git grep *)","Bash(git worktree list *)","Bash(git reflog show *)","Bash(git reflog)","Bash(git rev-list *)","Bash(git ls-remote *)","Bash(git remote -v)","Bash(git remote --verbose)","Bash(git remote)","Bash(gh pr view *)","Bash(gh pr list *)","Bash(gh pr diff *)","Bash(gh pr checks *)","Bash(gh pr status *)","Bash(gh issue view *)","Bash(gh issue list *)","Bash(gh issue status *)","Bash(gh run list *)","Bash(gh run view *)","Bash(gh repo view *)","Bash(gh auth status)","Bash(npm list *)","Bash(bun pm ls *)","Bash(npm view *)","Bash(npm outdated *)","Bash(pnpm list *)","Bash(yarn list *)","Bash(pip list *)","Bash(pip show *)"],ask:["Bash(curl *)","Bash(wget *)","Bash(aria2c *)","Bash(axel *)","Bash(rm -rf *)","Bash(rm -r *)","Bash(rm --recursive *)","Bash(nc *)","Bash(netcat *)","Bash(telnet *)","Bash(ncat *)"],deny:["Read(./.env)","Read(./.env.*)","Bash(rm -rf /)","Bash(rm -rf /*)","Bash(sudo *)","Bash(chmod 777 *)","Bash(bash *)","Bash(sh *)","Bash(zsh *)","Bash(fish *)","Bash(dash *)","Bash(eval *)","Bash(source *)","Bash(mkfs *)","Bash(fdisk *)","Bash(dd *)","Bash(format *)","Bash(parted *)","Bash(open http*)","Bash(open https*)","Bash(xdg-open http*)","Bash(xdg-open https*)"]},permissionMode:"default",hooks:{enabled:!1,defaultTimeout:60,timeoutBehavior:"ignore",failureBehavior:"ignore",maxConcurrentHooks:5,PreToolUse:[],PostToolUse:[],PostToolUseFailure:[],PermissionRequest:[],UserPromptSubmit:[],SessionStart:[],SessionEnd:[],Stop:[],SubagentStop:[],Notification:[],Compaction:[]},env:{},disableAllHooks:!1,maxTurns:-1};import{promises as F}from"fs";import{merge as N}from"lodash-es";import j from"os";import R from"path";class V{static instance=null;constructor(){}static getInstance(){if(!V.instance)V.instance=new V;return V.instance}static resetInstance(){V.instance=null}async initialize(){try{let H=await this.loadConfigFiles(),q=await this.loadSettingsFiles(),z={...D,...H,...q};if(this.resolveEnvInterpolation(z),z.debug)console.log("[ConfigManager] Configuration loaded successfully");return z}catch(H){return console.error("[ConfigManager] Failed to initialize:",H),D}}async loadConfigFiles(){let H=R.join(j.homedir(),".blade","config.json"),q=R.join(Y(),".blade","config.json"),z={},J=await this.loadJsonFile(H);if(J)z={...z,...J};let K=await this.loadJsonFile(q);if(K){let Q={...z.mcpServers||{},...K.mcpServers||{}};if(z={...z,...K},Object.keys(Q).length>0)z.mcpServers=Q}return z}async loadSettingsFiles(){let H=R.join(j.homedir(),".blade","settings.json"),q=R.join(Y(),".blade","settings.json"),z=R.join(Y(),".blade","settings.local.json"),J={},K=await this.loadJsonFile(H);if(K)J=this.mergeSettings(J,K);let Q=await this.loadJsonFile(q);if(Q)J=this.mergeSettings(J,Q);let W=await this.loadJsonFile(z);if(W)J=this.mergeSettings(J,W);return J}mergeSettings(H,q){let z=JSON.parse(JSON.stringify(H));if(q.permissions){if(!z.permissions)z.permissions={allow:[],ask:[],deny:[]};if(q.permissions.allow){let J=[...z.permissions.allow||[],...q.permissions.allow];z.permissions.allow=Array.from(new Set(J))}if(q.permissions.ask){let J=[...z.permissions.ask||[],...q.permissions.ask];z.permissions.ask=Array.from(new Set(J))}if(q.permissions.deny){let J=[...z.permissions.deny||[],...q.permissions.deny];z.permissions.deny=Array.from(new Set(J))}}if(q.hooks)z.hooks=N({},z.hooks,q.hooks);if(q.env)z.env=N({},z.env,q.env);if(q.mcpServers)z.mcpServers={...z.mcpServers||{},...q.mcpServers};if(q.disableAllHooks!==void 0)z.disableAllHooks=q.disableAllHooks;if(q.permissionMode!==void 0)z.permissionMode=q.permissionMode;if(q.maxTurns!==void 0)z.maxTurns=q.maxTurns;return z}resolveEnvInterpolation(H){let q=/\$\{?([A-Z_][A-Z0-9_]*)(:-([^}]+))?\}?/g,z=(J)=>{if(typeof J==="string")return J.replace(q,(K,Q,W,X)=>{return process.env[Q]||X||K});return J};for(let[J,K]of Object.entries(H))if(typeof K==="string")H[J]=z(K)}async loadJsonFile(H){try{if(await this.fileExists(H)){let q=await F.readFile(H,"utf-8");return JSON.parse(q)}}catch(q){console.warn(`[ConfigManager] Failed to load ${H}:`,q)}return null}async fileExists(H){try{return await F.access(H),!0}catch{return!1}}validateConfig(H){let q=[];if(!H.models||H.models.length===0)q.push("没有可用的模型配置");if(H.models&&H.models.length>0){if(!H.currentModelId)q.push("未设置当前模型 ID");else if(!H.models.find((J)=>J.id===H.currentModelId))q.push("当前模型 ID 无效")}if(q.length>0)throw Error(`配置验证失败:
|
|
2
|
+
${q.map((z)=>` - ${z}`).join(`
|
|
3
|
+
`)}
|
|
4
|
+
|
|
5
|
+
`+`请通过以下方式之一提供配置:
|
|
6
|
+
`+` 1. 首次启动: 运行 blade,系统会自动引导配置
|
|
7
|
+
`+` 2. 添加模型: blade 后输入 /model add
|
|
8
|
+
`+` 3. 初始化向导: 输入 /init
|
|
9
|
+
|
|
10
|
+
`+`配置文件示例 (~/.blade/config.json):
|
|
11
|
+
`+`{
|
|
12
|
+
"currentModelId": "model-id-123",
|
|
13
|
+
"models": [
|
|
14
|
+
{
|
|
15
|
+
"id": "model-id-123",
|
|
16
|
+
`+` "name": "默认模型",
|
|
17
|
+
`+` "provider": "openai-compatible",
|
|
18
|
+
"apiKey": "your-api-key",
|
|
19
|
+
"baseUrl": "https://api.example.com/v1",
|
|
20
|
+
"model": "model-name"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
`)}}function w(H,q={}){let z={...H};if(q.debug!==void 0)z.debug=q.debug===""?!0:q.debug;if(q.yolo===!0)z.permissionMode="yolo";else if(q.permissionMode!==void 0)z.permissionMode=q.permissionMode;if(q.maxTurns!==void 0)z.maxTurns=q.maxTurns;return z.systemPrompt=q.systemPrompt,z.appendSystemPrompt=q.appendSystemPrompt,z.resumeSessionId=q.sessionId,z.forkSession=q.forkSession,z.allowedTools=q.allowedTools,z.disallowedTools=q.disallowedTools,z.mcpConfigPaths=q.mcpConfig,z.strictMcpConfig=q.strictMcpConfig,z.addDirs=q.addDir,z.outputFormat=q.outputFormat,z.inputFormat=q.inputFormat,z.print=q.print,z.includePartialMessages=q.includePartialMessages,z.replayUserMessages=q.replayUserMessages,z.agentsConfig=q.agents,z.settingSources=q.settingSources,z}import{Mutex as U}from"async-mutex";import{merge as T}from"lodash-es";import{promises as G}from"node:fs";import _ from"node:os";import $ from"node:path";import E from"write-file-atomic";var I=M("Service"),B={models:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},currentModelId:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},temperature:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},maxContextTokens:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},maxOutputTokens:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},timeout:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},theme:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},uiTheme:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},language:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},debug:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},fontSize:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},autoSaveSessions:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},notifyBuild:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},notifyErrors:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},notifySounds:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},privacyTelemetry:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},privacyCrash:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},permissionMode:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},permissions:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!0},hooks:{target:"settings",defaultScope:"local",mergeStrategy:"deep-merge",persistable:!0},env:{target:"settings",defaultScope:"local",mergeStrategy:"deep-merge",persistable:!0},disableAllHooks:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!0},maxTurns:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!0},mcpServers:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!0},stream:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!1},topP:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!1},topK:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!1},mcpEnabled:{target:"config",defaultScope:"global",mergeStrategy:"replace",persistable:!1},systemPrompt:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},appendSystemPrompt:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},initialMessage:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},resumeSessionId:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},forkSession:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},allowedTools:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},disallowedTools:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},mcpConfigPaths:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},strictMcpConfig:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},addDirs:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},outputFormat:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},inputFormat:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},print:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},includePartialMessages:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},replayUserMessages:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},agentsConfig:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1},settingSources:{target:"settings",defaultScope:"local",mergeStrategy:"replace",persistable:!1}},t=new Set(Object.entries(B).filter(([H,q])=>q.persistable).map(([H])=>H)),i=new Set(Object.entries(B).filter(([H,q])=>!q.persistable).map(([H])=>H));class Z{static instance=null;pendingUpdates=new Map;timers=new Map;fileLocks=new Map;lastSaveError=null;debounceDelay=300;constructor(){}static getInstance(){if(!Z.instance)Z.instance=new Z;return Z.instance}static resetInstance(){if(Z.instance)for(let H of Z.instance.timers.values())clearTimeout(H);Z.instance=null}async save(H,q={}){this.validatePersistableFields(H);let z=this.groupUpdatesByTarget(H,q.scope);if(q.immediate)await Promise.all(Array.from(z.entries()).map(([J,K])=>this.flushTarget(J,K)));else for(let[J,K]of z)this.scheduleSave(J,K)}async flush(){for(let q of this.timers.values())clearTimeout(q);this.timers.clear();let H=Array.from(this.pendingUpdates.entries()).map(([q,z])=>this.flushTarget(q,z));this.pendingUpdates.clear(),await Promise.all(H)}getLastSaveError(){return this.lastSaveError}clearLastSaveError(){this.lastSaveError=null}async appendPermissionRule(H,q={}){let z=q.scope??"local",J=this.resolveFilePath("settings",z);await this.flushTargetWithModifier(J,(K)=>{let Q=K.permissions??{allow:[],ask:[],deny:[]},W={allow:this.dedupeArray([...Q.allow||[],H]),ask:Q.ask||[],deny:Q.deny||[]};return{...K,permissions:W}})}async appendLocalPermissionRule(H,q={}){await this.appendPermissionRule(H,{...q,scope:"local"})}validatePersistableFields(H){for(let q of Object.keys(H)){let z=B[q];if(!z)throw Error(`Unknown config field: ${q}`);if(!z.persistable)throw Error(`Field "${q}" is non-persistable and cannot be saved to config files. Non-persistable fields are runtime-only and only valid for the current session.`)}}groupUpdatesByTarget(H,q){let z=new Map;for(let[J,K]of Object.entries(H)){let Q=B[J];if(!Q)continue;let W=q??Q.defaultScope,X=this.resolveFilePath(Q.target,W);if(!z.has(X))z.set(X,{});z.get(X)[J]=K}return z}resolveFilePath(H,q){if(H==="config")return q==="global"?$.join(_.homedir(),".blade","config.json"):$.join(Y(),".blade","config.json");switch(q){case"local":return $.join(Y(),".blade","settings.local.json");case"project":return $.join(Y(),".blade","settings.json");case"global":return $.join(_.homedir(),".blade","settings.json");default:return $.join(Y(),".blade","settings.local.json")}}scheduleSave(H,q){let z=this.pendingUpdates.get(H)??{},J=this.mergePendingUpdates(z,q);this.pendingUpdates.set(H,J);let K=this.timers.get(H);if(K)clearTimeout(K);let Q=setTimeout(async()=>{let W=this.pendingUpdates.get(H);if(W){this.pendingUpdates.delete(H),this.timers.delete(H);try{await this.flushTarget(H,W),this.lastSaveError=null}catch(X){let b=X instanceof Error?X:Error(String(X));this.lastSaveError=b,I.error(`Failed to save config to ${H}:`,b.message),I.error("Stack trace:",b.stack)}}},this.debounceDelay);this.timers.set(H,Q)}mergePendingUpdates(H,q){let z={...H};for(let[J,K]of Object.entries(q)){let Q=B[J];if(!Q){z[J]=K;continue}switch(Q.mergeStrategy){case"replace":z[J]=K;break;case"append-dedupe":this.applyAppendDedupe(z,J,K);break;case"deep-merge":this.applyDeepMerge(z,J,K);break}}return z}async flushTarget(H,q){let z=this.fileLocks.get(H);if(!z)z=new U,this.fileLocks.set(H,z);await z.runExclusive(async()=>{await this.performWrite(H,q)})}async flushTargetWithModifier(H,q){let z=this.fileLocks.get(H);if(!z)z=new U,this.fileLocks.set(H,z);await z.runExclusive(async()=>{await G.mkdir($.dirname(H),{recursive:!0,mode:493});let J={};try{let Q=await G.readFile(H,"utf-8");J=JSON.parse(Q)}catch{J={}}let K=q(J);await this.atomicWrite(H,K)})}async performWrite(H,q){await G.mkdir($.dirname(H),{recursive:!0,mode:493});let z={};try{let K=await G.readFile(H,"utf-8");z=JSON.parse(K)}catch{z={}}let J={...z};for(let[K,Q]of Object.entries(q)){let W=B[K];if(!W){J[K]=Q;continue}switch(W.mergeStrategy){case"replace":J[K]=Q;break;case"append-dedupe":this.applyAppendDedupe(J,K,Q);break;case"deep-merge":this.applyDeepMerge(J,K,Q);break}}await this.atomicWrite(H,J)}applyAppendDedupe(H,q,z){if(q==="permissions"&&typeof z==="object"&&z!==null){let J=H[q]??{allow:[],ask:[],deny:[]},K=z;H[q]={allow:this.dedupeArray([...J.allow||[],...K.allow||[]]),ask:this.dedupeArray([...J.ask||[],...K.ask||[]]),deny:this.dedupeArray([...J.deny||[],...K.deny||[]])}}else if(Array.isArray(z)){let J=Array.isArray(H[q])?H[q]:[];H[q]=this.dedupeArray([...J,...z])}else H[q]=z}applyDeepMerge(H,q,z){if(typeof z==="object"&&z!==null&&!Array.isArray(z)){let J=typeof H[q]==="object"&&H[q]!==null?H[q]:{};H[q]=T({},J,z)}else H[q]=z}dedupeArray(H){return Array.from(new Set(H))}async atomicWrite(H,q){await E(H,JSON.stringify(q,null,2),{mode:384,encoding:"utf-8"})}}function L(){return Z.getInstance()}export{D as Ha,V as Ia,w as Ja,L as Ka};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import{xb as l}from"./chunk-7ycmx7c2.js";import{Bb as x}from"./chunk-set7v1rf.js";import{Db as N}from"./chunk-j2ag7hh6.js";import{nanoid as j}from"nanoid";var U;((R)=>{R.PreToolUse="PreToolUse";R.PostToolUse="PostToolUse";R.PostToolUseFailure="PostToolUseFailure";R.PermissionRequest="PermissionRequest";R.UserPromptSubmit="UserPromptSubmit";R.SessionStart="SessionStart";R.SessionEnd="SessionEnd";R.Stop="Stop";R.SubagentStop="SubagentStop";R.Notification="Notification";R.Compaction="Compaction"})(U||={});var w;((W)=>{W.Approve="approve";W.Block="block";W.Async="async"})(w||={});var M;((W)=>{W.Allow="allow";W.Deny="deny";W.Ask="ask"})(M||={});var C={enabled:!1,defaultTimeout:60,timeoutBehavior:"ignore",failureBehavior:"ignore",maxConcurrentHooks:5,PreToolUse:[],PostToolUse:[{name:"builtin:code-review-sensor",matcher:{tools:"Edit|Write"},hooks:[{type:"prompt",prompt:"审查此代码变更,重点检查:"+"1) 安全漏洞(命令注入、XSS、SQL 注入、硬编码密钥/密码)"+"2) 明显的逻辑错误(无限循环、off-by-one、空引用)"+"3) 类型安全问题。"+"如果发现严重问题,将问题描述放在 hookSpecificOutput.additionalContext 中。"+"如果没有严重问题,返回 approve。",model:void 0,timeout:15}]}],PostToolUseFailure:[],PermissionRequest:[],UserPromptSubmit:[],SessionStart:[],SessionEnd:[],Stop:[],SubagentStop:[],Notification:[],Compaction:[]};function S(Q,Y){return{...Q,...Y,PreToolUse:Y.PreToolUse??Q.PreToolUse,PostToolUse:Y.PostToolUse??Q.PostToolUse,PostToolUseFailure:Y.PostToolUseFailure??Q.PostToolUseFailure,PermissionRequest:Y.PermissionRequest??Q.PermissionRequest,UserPromptSubmit:Y.UserPromptSubmit??Q.UserPromptSubmit,SessionStart:Y.SessionStart??Q.SessionStart,SessionEnd:Y.SessionEnd??Q.SessionEnd,Stop:Y.Stop??Q.Stop,SubagentStop:Y.SubagentStop??Q.SubagentStop,Notification:Y.Notification??Q.Notification,Compaction:Y.Compaction??Q.Compaction}}function m(){let Q={};if(process.env.BLADE_DISABLE_HOOKS==="true")Q.enabled=!1;if(process.env.BLADE_HOOK_TIMEOUT){let Y=parseInt(process.env.BLADE_HOOK_TIMEOUT,10);if(!isNaN(Y)&&Y>0)Q.defaultTimeout=Y}return Q}class E{executedHooks=new Map;canExecute(Q,Y){if(!this.executedHooks.has(Q))this.executedHooks.set(Q,new Set);if(this.executedHooks.get(Q).has(Y))return console.warn(`[HookGuard] Hook ${Y} for tool ${Q} already executed, skipping`),!1;return!0}markExecuted(Q,Y){let Z=this.executedHooks.get(Q);if(Z)Z.add(Y)}cleanup(Q){this.executedHooks.delete(Q)}cleanupAll(){this.executedHooks.clear()}}import{z as $}from"zod";var L=$.object({hook_event_name:$.nativeEnum(U),hook_execution_id:$.string(),timestamp:$.string(),project_dir:$.string(),session_id:$.string(),permission_mode:$.enum(["default","autoEdit","yolo","plan"]),_metadata:$.object({blade_version:$.string(),hook_timeout_ms:$.number()}).optional()}),u=L.extend({hook_event_name:$.literal("PreToolUse"),tool_name:$.string(),tool_use_id:$.string(),tool_input:$.record($.unknown())}),i=L.extend({hook_event_name:$.literal("PostToolUse"),tool_name:$.string(),tool_use_id:$.string(),tool_input:$.record($.unknown()),tool_response:$.unknown()}),a=L.extend({hook_event_name:$.literal("Stop"),reason:$.string().optional()}),c=L.extend({hook_event_name:$.literal("PostToolUseFailure"),tool_name:$.string(),tool_use_id:$.string(),tool_input:$.record($.unknown()),error:$.string(),error_type:$.string().optional(),is_interrupt:$.boolean(),is_timeout:$.boolean()}),s=L.extend({hook_event_name:$.literal("PermissionRequest"),tool_name:$.string(),tool_use_id:$.string(),tool_input:$.record($.unknown())}),r=L.extend({hook_event_name:$.literal("UserPromptSubmit"),user_prompt:$.string(),has_images:$.boolean(),image_count:$.number()}),n=L.extend({hook_event_name:$.literal("SessionStart"),is_resume:$.boolean(),resume_session_id:$.string().optional()}),o=L.extend({hook_event_name:$.literal("SessionEnd"),reason:$.enum(["user_exit","error","max_turns","idle_timeout","ctrl_c","clear","logout","other"])}),e=L.extend({hook_event_name:$.literal("SubagentStop"),agent_type:$.string(),task_description:$.string().optional(),success:$.boolean(),result_summary:$.string().optional(),error:$.string().optional()}),t=L.extend({hook_event_name:$.literal("Notification"),notification_type:$.enum(["permission_prompt","idle_prompt","auth_success","elicitation_dialog","info","warning","error"]),title:$.string().optional(),message:$.string()}),QQ=L.extend({hook_event_name:$.literal("Compaction"),trigger:$.enum(["manual","auto"]),messages_before:$.number(),tokens_before:$.number()}),CQ=$.discriminatedUnion("hook_event_name",[u,i,a,c,s,r,n,o,e,t,QQ]),YQ=$.object({hookEventName:$.literal("PreToolUse"),permissionDecision:$.nativeEnum(M).optional(),permissionDecisionReason:$.string().optional(),updatedInput:$.record($.unknown()).optional()}),ZQ=$.object({hookEventName:$.literal("PostToolUse"),additionalContext:$.string().optional(),updatedOutput:$.unknown().optional()}),WQ=$.object({hookEventName:$.literal("Stop"),continue:$.boolean().optional(),continueReason:$.string().optional()}),XQ=$.object({hookEventName:$.literal("SubagentStop"),continue:$.boolean().optional(),continueReason:$.string().optional(),additionalContext:$.string().optional()}),$Q=$.object({hookEventName:$.literal("PermissionRequest"),permissionDecision:$.enum(["approve","deny","ask"]).optional(),permissionDecisionReason:$.string().optional()}),qQ=$.object({hookEventName:$.literal("UserPromptSubmit"),updatedPrompt:$.string().optional(),contextInjection:$.string().optional()}),JQ=$.object({hookEventName:$.literal("SessionStart"),env:$.record($.string()).optional()}),GQ=$.object({hookEventName:$.literal("Compaction"),blockCompaction:$.boolean().optional(),blockReason:$.string().optional()}),FQ=$.object({decision:$.object({behavior:$.nativeEnum(w)}).optional(),systemMessage:$.string().optional(),hookSpecificOutput:$.discriminatedUnion("hookEventName",[YQ,ZQ,WQ,XQ,$Q,qQ,JQ,GQ]).optional(),suppressOutput:$.boolean().optional()}),KQ=$.object({type:$.literal("command"),command:$.string(),timeout:$.number().positive().optional(),statusMessage:$.string().optional()}),VQ=$.object({type:$.literal("prompt"),prompt:$.string(),model:$.string().optional(),timeout:$.number().positive().optional()}),RQ=$.discriminatedUnion("type",[KQ,VQ]),I=$.union([$.string(),$.array($.string())]),_Q=$.object({tools:I.optional(),paths:I.optional(),commands:I.optional()}),D=$.object({name:$.string().optional(),matcher:_Q.optional(),hooks:$.array(RQ)}),SQ=$.object({enabled:$.boolean().optional(),defaultTimeout:$.number().positive().optional(),timeoutBehavior:$.enum(["ignore","deny","ask"]).optional(),failureBehavior:$.enum(["ignore","deny","ask"]).optional(),maxConcurrentHooks:$.number().positive().optional(),PreToolUse:$.array(D).optional(),PostToolUse:$.array(D).optional(),PostToolUseFailure:$.array(D).optional(),PermissionRequest:$.array(D).optional(),UserPromptSubmit:$.array(D).optional(),SessionStart:$.array(D).optional(),SessionEnd:$.array(D).optional(),Stop:$.array(D).optional(),SubagentStop:$.array(D).optional(),Notification:$.array(D).optional(),Compaction:$.array(D).optional()});function k(Q){let Y=FQ.safeParse(Q);if(Y.success)return{success:!0,data:Y.data};return{success:!1,error:Y.error}}class f{parse(Q,Y,Z){if(Q.timedOut){let X=Z?.timeoutBehavior||"ignore",q="Hook timeout";if(X==="deny")return{success:!1,blocking:!0,error:"Hook timeout",stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y};else if(X==="ask")return{success:!1,blocking:!1,needsConfirmation:!0,warning:"Hook timeout. Continue?",stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y};else return{success:!1,blocking:!1,warning:"Hook timeout",stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y}}let W=this.tryParseJSON(Q.stdout);if(W){let X=k(W);if(!X.success){let J="error"in X?X.error.message:"Unknown validation error",G=Z?.failureBehavior||"ignore",F=`Invalid hook output JSON: ${J}`;if(G==="deny")return{success:!1,blocking:!0,error:F,stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y};else if(G==="ask")return{success:!1,blocking:!1,needsConfirmation:!0,warning:`${F}. Continue?`,stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y};else return{success:!1,blocking:!1,warning:F,stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y}}let q=X.data;if(q.decision?.behavior==="block")return{success:!1,blocking:!0,error:q.systemMessage||"Hook blocked execution",output:q,stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y};if(q.decision?.behavior==="async")return{success:!0,output:q,stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y};return{success:!0,output:q,stdout:Q.stdout,stderr:Q.stderr,exitCode:Q.exitCode,hook:Y}}return this.parseByExitCode(Q,Y,Z)}parseByExitCode(Q,Y,Z){let W=Q.exitCode;switch(W){case 0:return{success:!0,stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y};case 2:return{success:!1,blocking:!0,error:Q.stderr||Q.stdout||"Hook returned exit code 2",stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y};case 124:{let X=Z?.timeoutBehavior||"ignore",q="Hook timeout";if(X==="deny")return{success:!1,blocking:!0,error:"Hook timeout",stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y};else if(X==="ask")return{success:!1,blocking:!1,needsConfirmation:!0,warning:"Hook timeout. Continue?",stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y};else return{success:!1,blocking:!1,warning:"Hook timeout",stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y}}default:{let X=Z?.failureBehavior||"ignore",q=Q.stderr||Q.stdout||`Hook failed with exit code ${W}`;if(X==="deny")return{success:!1,blocking:!0,error:q,stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y};else if(X==="ask")return{success:!1,blocking:!1,needsConfirmation:!0,warning:`${q}. Continue?`,stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y};else return{success:!1,blocking:!1,warning:q,stdout:Q.stdout,stderr:Q.stderr,exitCode:W,hook:Y}}}}tryParseJSON(Q){try{let Y=Q.trim();if(!Y)return null;let Z=Y.match(/^\s*(\{[\s\S]*\})\s*$/);if(!Z)return null;return JSON.parse(Z[1])}catch{return null}}}import{spawn as DQ}from"child_process";class z{content="";maxSize;constructor(Q){this.maxSize=Q}append(Q){if(this.content.length<this.maxSize){let Y=this.maxSize-this.content.length;this.content+=Q.substring(0,Y)}}getContent(){return this.content}isFull(){return this.content.length>=this.maxSize}}class H{MAX_STDOUT_SIZE=1048576;MAX_STDERR_SIZE=1048576;MAX_INPUT_SIZE=102400;async execute(Q,Y,Z,W){let X=JSON.stringify(Y);if(X.length>this.MAX_INPUT_SIZE)throw Error(`Hook input too large: ${X.length} bytes (max ${this.MAX_INPUT_SIZE})`);let q=this.createSafeEnv(Y),J=DQ(Q,[],{shell:!0,env:q,cwd:Z.projectDir,timeout:W}),G=new z(this.MAX_STDOUT_SIZE),F=new z(this.MAX_STDERR_SIZE);J.stdout.setEncoding("utf8"),J.stderr.setEncoding("utf8"),J.stdout.on("data",(K)=>{G.append(K)}),J.stderr.on("data",(K)=>{F.append(K)});try{J.stdin.write(X),J.stdin.end()}catch(K){throw J.kill("SIGTERM"),Error(`Failed to write hook input: ${K}`)}return new Promise((K,O)=>{let R=!1,_=!1,V=null,A=()=>{if(V&&Z.abortSignal)Z.abortSignal.removeEventListener("abort",V),V=null},B=setTimeout(()=>{R=!0,J.kill("SIGKILL")},W);if(J.on("close",(b)=>{if(_)return;_=!0,clearTimeout(B),A(),K({stdout:G.getContent(),stderr:F.getContent(),exitCode:R?124:b??1,timedOut:R})}),J.on("error",(b)=>{if(_)return;_=!0,clearTimeout(B),A(),O(b)}),Z.abortSignal)V=()=>{if(_)return;_=!0,clearTimeout(B),A(),J.kill("SIGTERM"),K({stdout:G.getContent(),stderr:"Hook cancelled by abort signal",exitCode:1,timedOut:!1})},Z.abortSignal.addEventListener("abort",V)})}createSafeEnv(Q){return{BLADE_PROJECT_DIR:Q.project_dir,BLADE_SESSION_ID:Q.session_id,BLADE_HOOK_EVENT:Q.hook_event_name,BLADE_TOOL_NAME:"tool_name"in Q?Q.tool_name:"",BLADE_TOOL_USE_ID:"tool_use_id"in Q?Q.tool_use_id:"",PATH:process.env.PATH||"",HOME:process.env.HOME||"",USER:process.env.USER||"",SHELL:process.env.SHELL||"/bin/sh"}}}class P extends Error{constructor(Q){super(Q);this.name="HttpHookSecurityError"}}function h(Q,Y={}){let Z;try{Z=new URL(Q)}catch{throw new P(`Invalid URL: ${Q}`)}if(Z.protocol!=="https:"&&Z.protocol!=="http:")throw new P(`Only http/https supported, got: ${Z.protocol}`);let W=Z.hostname.toLowerCase();if(LQ(W,Y.allowedHosts))return;if(Z.protocol==="http:"&&!Y.allowHttp)throw new P(`HTTP (non-TLS) blocked for ${W}; use https:// or add to allowedHosts`);if(jQ(W)&&!Y.allowLoopback)throw new P(`Loopback address blocked: ${W}; enable policy.allowLoopback or add to allowedHosts`);if(AQ(W)&&!Y.allowPrivateRanges)throw new P(`Private/link-local address blocked: ${W}; enable policy.allowPrivateRanges or add to allowedHosts`)}function LQ(Q,Y){if(!Y||Y.length===0)return!1;return Y.some((Z)=>{let W=Z.toLowerCase();if(W===Q)return!0;if(W.startsWith("*.")){let X=W.slice(1);return Q.endsWith(X)&&Q.length>X.length}return!1})}function jQ(Q){if(Q==="localhost")return!0;if(Q==="::1"||Q==="[::1]")return!0;if(/^127\.\d+\.\d+\.\d+$/.test(Q))return!0;return!1}function AQ(Q){let Y=Q.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);if(!Y)return!1;let[Z,W]=Y.slice(1).map(Number);if(Z===10)return!0;if(Z===172&&W>=16&&W<=31)return!0;if(Z===192&&W===168)return!0;if(Z===169&&W===254)return!0;return!1}function p(Q){if(!Q)return{};let Y={};for(let[Z,W]of Object.entries(Q))Y[Z]=W.replace(/\$\{([A-Z_][A-Z0-9_]*)\}/gi,(X,q)=>{return process.env[q]??`\${${q}}`});return Y}var OQ=x("Execution"),BQ={PreToolUse:'{ "permissionDecision": "approve" | "deny" | "ask", "permissionDecisionReason": "...", "updatedInput": { ... } }',PostToolUse:'{ "additionalContext": "注入到工具结果的额外信息", "updatedOutput": "修改后的工具输出" }',Stop:'{ "continue": true, "continueReason": "继续执行的原因" } // continue: true 表示阻止停止',SubagentStop:'{ "continue": true, "continueReason": "...", "additionalContext": "..." }',PermissionRequest:'{ "permissionDecision": "approve" | "deny" | "ask", "permissionDecisionReason": "..." }',UserPromptSubmit:'{ "updatedPrompt": "修改后的提示词", "contextInjection": "注入的上下文" }',Compaction:'{ "blockCompaction": true, "blockReason": "阻止压缩的原因" }'};class v{processExecutor=new H;outputParser=new f;chatServiceCache=new Map;async executePreToolHooks(Q,Y,Z){if(Q.length===0)return{decision:"allow"};let W="tool_input"in Y?Y.tool_input:{},X=[];for(let q of Q)try{let J={...Y,...W&&{tool_input:W}},G=await this.executeHook(q,J,Z);if(!G.success){if(G.blocking)return{decision:"deny",reason:G.error};if(G.needsConfirmation)return{decision:"ask",reason:G.warning||G.error};if(G.warning)X.push(G.warning);continue}let F=G.output?.hookSpecificOutput;if(F&&"permissionDecision"in F){switch(F.permissionDecision){case"deny":return{decision:"deny",reason:F.permissionDecisionReason};case"ask":return{decision:"ask",reason:F.permissionDecisionReason};case"allow":break}if("updatedInput"in F&&F.updatedInput)W={...W,...F.updatedInput}}}catch(J){let G=J instanceof Error?J.message:String(J);if(X.push(`Hook failed: ${G}`),Z.config.failureBehavior==="deny")return{decision:"deny",reason:G};else if(Z.config.failureBehavior==="ask")return{decision:"ask",reason:`Hook failed: ${G}. Continue?`}}return{decision:"allow",modifiedInput:W,warning:X.length>0?X.join(`
|
|
2
|
+
`):void 0}}async executePostToolHooks(Q,Y,Z){if(Q.length===0)return{};let W=Z.config.maxConcurrentHooks||5,X=await this.executeHooksConcurrently(Q,Y,Z,W),q=[],J=void 0,G=[];for(let F of X){if(!F.success&&F.warning){G.push(F.warning);continue}let K=F.output?.hookSpecificOutput;if(K&&"additionalContext"in K){if(K.additionalContext)q.push(K.additionalContext);if("updatedOutput"in K&&K.updatedOutput!==void 0)J=K.updatedOutput}}return{additionalContext:q.length>0?q.join(`
|
|
3
|
+
|
|
4
|
+
`):void 0,modifiedOutput:J,warning:G.length>0?G.join(`
|
|
5
|
+
`):void 0}}async executeStopHooks(Q,Y,Z){if(Q.length===0)return{shouldStop:!0};let W=[];for(let X of Q)try{let q=await this.executeHook(X,Y,Z);if(!q.success){if(q.warning)W.push(q.warning);continue}let J=q.output?.hookSpecificOutput;if(J&&"continue"in J&&J.continue===!1)return{shouldStop:!1,continueReason:J.continueReason,warning:W.length>0?W.join(`
|
|
6
|
+
`):void 0}}catch(q){let J=q instanceof Error?q.message:String(q);W.push(`Hook failed: ${J}`)}return{shouldStop:!0,warning:W.length>0?W.join(`
|
|
7
|
+
`):void 0}}async executeSubagentStopHooks(Q,Y,Z){if(Q.length===0)return{shouldStop:!0};let W=[],X=[];for(let q of Q)try{let J=await this.executeHook(q,Y,Z);if(!J.success){if(J.warning)W.push(J.warning);continue}let G=J.output?.hookSpecificOutput;if(G&&"continue"in G){if(G.continue===!1)return{shouldStop:!1,continueReason:G.continueReason,warning:W.length>0?W.join(`
|
|
8
|
+
`):void 0};if("additionalContext"in G&&G.additionalContext)X.push(G.additionalContext)}}catch(J){let G=J instanceof Error?J.message:String(J);W.push(`Hook failed: ${G}`)}return{shouldStop:!0,additionalContext:X.length>0?X.join(`
|
|
9
|
+
|
|
10
|
+
`):void 0,warning:W.length>0?W.join(`
|
|
11
|
+
`):void 0}}async executePermissionRequestHooks(Q,Y,Z){if(Q.length===0)return{decision:"ask"};let W=[];for(let X of Q)try{let q=await this.executeHook(X,Y,Z);if(!q.success){if(q.warning)W.push(q.warning);continue}let J=q.output?.hookSpecificOutput;if(J&&"permissionDecision"in J){let G=J.permissionDecision;if(G==="approve"||G==="deny")return{decision:G,reason:J.permissionDecisionReason,warning:W.length>0?W.join(`
|
|
12
|
+
`):void 0}}}catch(q){let J=q instanceof Error?q.message:String(q);W.push(`Hook failed: ${J}`)}return{decision:"ask",warning:W.length>0?W.join(`
|
|
13
|
+
`):void 0}}async executeUserPromptSubmitHooks(Q,Y,Z){if(Q.length===0)return{proceed:!0};let W=[],X=[],q;for(let J of Q)try{let G=await this.executeHook(J,Y,Z);if(!G.success){if(G.blocking)return{proceed:!1,warning:G.error};if(G.warning)W.push(G.warning);continue}if(G.stdout&&G.stdout.trim())X.push(G.stdout.trim());let F=G.output?.hookSpecificOutput;if(F&&"updatedPrompt"in F){if(F.updatedPrompt)q=F.updatedPrompt;if(F.contextInjection)X.push(F.contextInjection)}}catch(G){let F=G instanceof Error?G.message:String(G);W.push(`Hook failed: ${F}`)}return{proceed:!0,updatedPrompt:q,contextInjection:X.length>0?X.join(`
|
|
14
|
+
|
|
15
|
+
`):void 0,warning:W.length>0?W.join(`
|
|
16
|
+
`):void 0}}async executeSessionStartHooks(Q,Y,Z){if(Q.length===0)return{proceed:!0};let W=[],X={};for(let q of Q)try{let J=await this.executeHook(q,Y,Z);if(!J.success){if(J.blocking)return{proceed:!1,warning:J.error};if(J.warning)W.push(J.warning);continue}let G=J.output?.hookSpecificOutput;if(G&&"env"in G&&G.env)Object.assign(X,G.env)}catch(J){let G=J instanceof Error?J.message:String(J);W.push(`Hook failed: ${G}`)}return{proceed:!0,env:Object.keys(X).length>0?X:void 0,warning:W.length>0?W.join(`
|
|
17
|
+
`):void 0}}async executeSessionEndHooks(Q,Y,Z){if(Q.length===0)return{};let W=[],X=Z.config.maxConcurrentHooks||5,q=await this.executeHooksConcurrently(Q,Y,Z,X);for(let J of q)if(!J.success&&J.warning)W.push(J.warning);return{warning:W.length>0?W.join(`
|
|
18
|
+
`):void 0}}async executePostToolUseFailureHooks(Q,Y,Z){if(Q.length===0)return{};let W=[],X=[],q=Z.config.maxConcurrentHooks||5,J=await this.executeHooksConcurrently(Q,Y,Z,q);for(let G of J){if(!G.success&&G.warning){W.push(G.warning);continue}if(G.stdout&&G.stdout.trim())X.push(G.stdout.trim())}return{additionalContext:X.length>0?X.join(`
|
|
19
|
+
|
|
20
|
+
`):void 0,warning:W.length>0?W.join(`
|
|
21
|
+
`):void 0}}async executeNotificationHooks(Q,Y,Z){let W="message"in Y?Y.message:"";if(Q.length===0)return{suppress:!1,message:W};let X=[],q=!1,J=W;for(let G of Q)try{let F=await this.executeHook(G,Y,Z);if(!F.success){if(F.warning)X.push(F.warning);continue}if(F.output?.suppressOutput){q=!0;break}if(F.stdout&&F.stdout.trim())J=F.stdout.trim()}catch(F){let K=F instanceof Error?F.message:String(F);X.push(`Hook failed: ${K}`)}return{suppress:q,message:J,warning:X.length>0?X.join(`
|
|
22
|
+
`):void 0}}async executeCompactionHooks(Q,Y,Z){if(Q.length===0)return{blockCompaction:!1};let W=[];for(let X of Q)try{let q=await this.executeHook(X,Y,Z);if(!q.success){if(q.warning)W.push(q.warning);continue}let J=q.output?.hookSpecificOutput;if(J&&"blockCompaction"in J&&J.blockCompaction)return{blockCompaction:!0,blockReason:J.blockReason,warning:W.length>0?W.join(`
|
|
23
|
+
`):void 0}}catch(q){let J=q instanceof Error?q.message:String(q);W.push(`Hook failed: ${J}`)}return{blockCompaction:!1,warning:W.length>0?W.join(`
|
|
24
|
+
`):void 0}}async executeHook(Q,Y,Z){if(Q.type==="command")return this.executeCommandHook(Q,Y,Z);if(Q.type==="prompt")return this.executePromptHook(Q,Y,Z);if(Q.type==="function")return this.executeFunctionHook(Q,Y,Z);if(Q.type==="http")return this.executeHttpHook(Q,Y,Z);throw Error(`Hook type ${Q.type} not supported`)}async executeCommandHook(Q,Y,Z){let W=(Q.timeout??Z.config.defaultTimeout??60)*1000;try{let X=await this.processExecutor.execute(Q.command,Y,Z,W);return this.outputParser.parse(X,Q,{timeoutBehavior:Z.config.timeoutBehavior,failureBehavior:Z.config.failureBehavior})}catch(X){return{success:!1,blocking:!1,error:X instanceof Error?X.message:String(X),hook:Q}}}async executePromptHook(Q,Y,Z){let W=(Q.timeout??Z.config.defaultTimeout??60)*1000;try{let X=await this.getOrCreateChatService(Q.model),q="hook_event_name"in Y?String(Y.hook_event_name):"Unknown",J=this.buildPromptHookSystemMessage(Q,q),G=JSON.stringify(Y,null,2),F=new AbortController,K=setTimeout(()=>F.abort(),W),O;try{O=(await X.chat([{role:"system",content:J},{role:"user",content:G}],void 0,F.signal)).content}catch(_){if(F.signal.aborted||_ instanceof Error&&_.name==="AbortError")return this.outputParser.parse({stdout:"",stderr:"",exitCode:0,timedOut:!0},Q,{timeoutBehavior:Z.config.timeoutBehavior,failureBehavior:Z.config.failureBehavior});throw _}finally{clearTimeout(K)}let R=this.extractJsonFromLLMResponse(O);return this.outputParser.parse({stdout:R,stderr:"",exitCode:0,timedOut:!1},Q,{timeoutBehavior:Z.config.timeoutBehavior,failureBehavior:Z.config.failureBehavior})}catch(X){return OQ.warn(`[PromptHook] 执行失败: ${X instanceof Error?X.message:String(X)}`),{success:!1,blocking:!1,error:X instanceof Error?X.message:String(X),hook:Q}}}async executeFunctionHook(Q,Y,Z){let W=(Q.timeout??10)*1000;try{let X=await this.runFunctionWithTimeout(Q.handler,Y,Z,W);if(X===void 0||X===null)return{success:!0,hook:Q};let q={stdout:JSON.stringify(X),stderr:"",exitCode:0,timedOut:!1};return this.outputParser.parse(q,Q,{timeoutBehavior:Z.config.timeoutBehavior,failureBehavior:Z.config.failureBehavior})}catch(X){if(X.isTimeout){let q={stdout:"",stderr:`Function hook timeout after ${W}ms`,exitCode:124,timedOut:!0};return this.outputParser.parse(q,Q,{timeoutBehavior:Z.config.timeoutBehavior,failureBehavior:Z.config.failureBehavior})}return{success:!1,blocking:!1,error:X instanceof Error?X.message:String(X),hook:Q}}}runFunctionWithTimeout(Q,Y,Z,W){return new Promise((X,q)=>{let J=!1,G=setTimeout(()=>{if(J)return;J=!0;let K=Object.assign(Error(`Function hook timed out after ${W}ms`),{isTimeout:!0});q(K)},W),F=()=>{if(J)return;J=!0,clearTimeout(G),q(Error("Function hook aborted"))};Z.abortSignal?.addEventListener("abort",F,{once:!0}),Promise.resolve().then(()=>Q(Y,Z)).then((K)=>{if(J)return;J=!0,clearTimeout(G),Z.abortSignal?.removeEventListener("abort",F),X(K)},(K)=>{if(J)return;J=!0,clearTimeout(G),Z.abortSignal?.removeEventListener("abort",F),q(K)})})}async executeHttpHook(Q,Y,Z){let W=(Q.timeout??10)*1000,X=Q.maxResponseBytes??262144,q=Math.max(1,(Q.retries??0)+1);try{h(Q.url,Z.config.httpPolicy)}catch(K){return{success:!1,blocking:!0,error:K instanceof Error?K.message:String(K),hook:Q}}let J={"Content-Type":"application/json",...p(Q.headers)},G=JSON.stringify(Y),F;for(let K=0;K<q;K++){if(K>0){let V=Math.min(2**K*200,5000);await new Promise((A)=>setTimeout(A,V))}let O=new AbortController,R=setTimeout(()=>O.abort(),W),_=()=>O.abort();Z.abortSignal?.addEventListener("abort",_,{once:!0});try{let V=await fetch(Q.url,{method:"POST",headers:J,body:G,redirect:"manual",signal:O.signal});if(V.status>=300&&V.status<400){F=Error(`HTTP hook received redirect (${V.status}); redirects are disabled`);continue}if(!V.ok){if(F=Error(`HTTP hook returned ${V.status} ${V.statusText}`),V.status>=400&&V.status<500)break;continue}let B={stdout:await PQ(V,X),stderr:"",exitCode:0,timedOut:!1};return this.outputParser.parse(B,Q,{timeoutBehavior:Z.config.timeoutBehavior,failureBehavior:Z.config.failureBehavior})}catch(V){let A=V.name==="AbortError"||V.code==="ABORT_ERR";if(A&&Z.abortSignal?.aborted){F=Error("HTTP hook aborted by caller");break}if(A){let B={stdout:"",stderr:`HTTP hook timeout after ${W}ms`,exitCode:124,timedOut:!0};if(K===q-1)return this.outputParser.parse(B,Q,{timeoutBehavior:Z.config.timeoutBehavior,failureBehavior:Z.config.failureBehavior});F=Error(`HTTP hook timeout attempt ${K+1}`);continue}F=V}finally{clearTimeout(R),Z.abortSignal?.removeEventListener("abort",_)}}return{success:!1,blocking:!1,error:F?.message??"HTTP hook failed",hook:Q}}async getOrCreateChatService(Q){let Y=Q||"__default__",Z=this.chatServiceCache.get(Y);if(Z)return Z;let{ensureStoreInitialized:W,getCurrentModel:X,getModelById:q}=await import("./chunk-vd38synx.js"),{createChatServiceAsync:J}=await import("./chunk-52k9k0w8.js");await W();let G=Q?q(Q)||X():X();if(!G)throw Error("PromptHook: 无法获取模型配置。请确保至少配置了一个模型。");let F=await J({provider:G.provider,apiKey:G.apiKey,baseUrl:G.baseUrl,model:G.model,temperature:G.temperature,maxContextTokens:G.maxContextTokens,maxOutputTokens:G.maxOutputTokens});return this.chatServiceCache.set(Y,F),F}buildPromptHookSystemMessage(Q,Y){let Z=BQ[Y]||"{ ... 根据事件类型返回相应字段 }";return`你是一个代码质量评估器,作为 Hook 在 ${Y} 事件中被触发。
|
|
25
|
+
|
|
26
|
+
`+`## 评估指令
|
|
27
|
+
${Q.prompt}
|
|
28
|
+
|
|
29
|
+
`+`## 输出格式
|
|
30
|
+
`+`必须返回一个 JSON 对象(不要包含任何其他文本):
|
|
31
|
+
`+`{
|
|
32
|
+
"decision": { "behavior": "approve" | "block" },
|
|
33
|
+
`+` "systemMessage": "可选的说明信息",
|
|
34
|
+
`+` "hookSpecificOutput": ${Z}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
`+`重要规则:
|
|
38
|
+
`+`- 只输出 JSON,不要包含 markdown 代码块或其他文本
|
|
39
|
+
`+`- 如果没有发现问题,使用 "approve"
|
|
40
|
+
`+'- 只在发现严重问题时使用 "block"'}extractJsonFromLLMResponse(Q){let Y=Q.trim(),Z=Y.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?\s*```$/);if(Z)return Z[1].trim();return Y}async executeHooksConcurrently(Q,Y,Z,W){let X=[],q=new Set;for(let J of Q){if(q.size>=W)await Promise.race(q);let G=this.executeHook(J,Y,Z).catch((K)=>({success:!1,blocking:!1,error:K instanceof Error?K.message:String(K),hook:J})),F=G.then(()=>{q.delete(F)}).catch(()=>{q.delete(F)});q.add(F),X.push(G)}return Promise.all(X)}}async function PQ(Q,Y){let Z=Q.body?.getReader();if(!Z)return"";let W=new TextDecoder,X=0,q="";while(!0){let{value:J,done:G}=await Z.read();if(G)break;if(X+=J.byteLength,X>Y){try{Z.cancel()}catch{}q+=W.decode(J.slice(0,Y-(X-J.byteLength)));break}q+=W.decode(J,{stream:!0})}return q+=W.decode(),q}import g from"picomatch";var TQ=/^([A-Za-z0-9_|]+)\((.+)\)$/;class d{matches(Q,Y){if(!Q)return!0;if(Q.tools&&Y.toolName){if(!this.matchTools(Q.tools,Y))return!1}if(Q.paths&&Y.filePath){if(!this.matchPaths(Q.paths,Y.filePath))return!1}if(Q.commands&&Y.command){if(!this.matchCommands(Q.commands,Y.command))return!1}return!0}matchTools(Q,Y){if(Array.isArray(Q))return Q.some((Z)=>this.matchToolWithParams(Z,Y));return this.matchToolWithParams(Q,Y)}matchPaths(Q,Y){if(Array.isArray(Q))return Q.some((W)=>{return g(W)(Y)});return g(Q)(Y)}matchCommands(Q,Y){if(Array.isArray(Q))return Q.some((Z)=>this.matchPattern(Y,Z));return this.matchPattern(Y,Q)}matchToolWithParams(Q,Y){let{toolName:Z,command:W,filePath:X}=Y,q=TQ.exec(Q);if(!q)return this.matchPattern(Z,Q);let[,J,G]=q;if(!this.matchPattern(Z,J))return!1;let F=this.getArgValue(Z,W,X);if(!F)return!1;return this.matchGlobOrPattern(F,G)}getArgValue(Q,Y,Z){if(Q==="Bash"||Q==="BashTool")return Y;if(["Read","Edit","Write","Glob","Grep"].includes(Q))return Z;return Y||Z}matchGlobOrPattern(Q,Y){if(/[*?[\]{}!]/.test(Y))try{return g(Y,{bash:!0,dot:!0})(Q)}catch{}if(Y.endsWith("*")){let Z=Y.slice(0,-1);return Q.startsWith(Z)}return Q===Y}matchPattern(Q,Y){if(Y==="*")return!0;if(!Y.includes("|")&&!/[.*+?^${}()|[\]\\]/.test(Y))return Q===Y;if(Y.includes("|"))return Y.split("|").map((W)=>W.trim()).includes(Q);try{return new RegExp(Y).test(Q)}catch{return Q===Y}}}class T{static instance=null;config=C;executor=new v;guard=new E;matcher=new d;sessionDisabled=!1;constructor(){}static getInstance(){if(!T.instance)T.instance=new T;return T.instance}loadConfig(Q){let Y=S(C,Q),Z=m();Y=S(Y,Z),this.config=Y}isEnabled(){if(!this.config.enabled)return!1;if(this.sessionDisabled)return!1;return!0}disable(){this.sessionDisabled=!0,console.log("[HookManager] Hooks disabled for this session")}enable(){this.sessionDisabled=!1,console.log("[HookManager] Hooks enabled for this session")}getConfig(){return this.config}registerFunction(Q,Y,Z,W){let X={type:"function",handler:Z,timeout:W?.timeout},q={name:W?.name??`inline-${Q}-${Date.now()}`,matcher:Y,hooks:[X]},J=this.config[Q]??[];return this.config[Q]=[...J,q],()=>{let G=this.config[Q];if(!G)return;this.config[Q]=G.filter((F)=>F!==q)}}async reloadConfig(){let Q=await import("node:fs/promises"),Y=await import("node:path");try{let Z=Y.join(l(),".blade","settings.local.json"),W=await Q.readFile(Z,"utf-8"),X=JSON.parse(W);if(X.hooks)this.loadConfig(X.hooks)}catch{}}async executePreToolHooks(Q,Y,Z,W){if(!this.isEnabled())return{decision:"allow"};if(W.permissionMode==="plan")return{decision:"allow"};if(!this.guard.canExecute(Y,"PreToolUse"))return{decision:"allow"};let X={hook_event_name:"PreToolUse",hook_execution_id:j(),timestamp:new Date().toISOString(),tool_name:Q,tool_use_id:Y,tool_input:Z,project_dir:W.projectDir,session_id:W.sessionId,permission_mode:W.permissionMode},q=this.getMatchingHooks("PreToolUse",{toolName:Q,filePath:this.extractFilePath(Z),command:this.extractCommand(Q,Z)});if(q.length===0)return{decision:"allow"};let J={projectDir:W.projectDir,sessionId:W.sessionId,permissionMode:W.permissionMode,config:this.config,abortSignal:W.abortSignal};try{let G=await this.executor.executePreToolHooks(q,X,J);if(this.guard.markExecuted(Y,"PreToolUse"),W.permissionMode==="yolo"){if(G.decision==="deny")return G;return{decision:"allow",modifiedInput:G.modifiedInput,warning:G.warning,reason:G.reason}}return G}catch(G){return console.error("[HookManager] Error executing PreToolUse hooks:",G),{decision:"allow",warning:`Hook execution failed: ${G instanceof Error?G.message:String(G)}`}}}async executePostToolHooks(Q,Y,Z,W,X){if(!this.isEnabled())return{};if(X.permissionMode==="plan")return{};if(!this.guard.canExecute(Y,"PostToolUse"))return{};let q={hook_event_name:"PostToolUse",hook_execution_id:j(),timestamp:new Date().toISOString(),tool_name:Q,tool_use_id:Y,tool_input:Z,tool_response:W,project_dir:X.projectDir,session_id:X.sessionId,permission_mode:X.permissionMode},J=this.getMatchingHooks("PostToolUse",{toolName:Q,filePath:this.extractFilePath(Z),command:this.extractCommand(Q,Z)});if(J.length===0)return{};let G={projectDir:X.projectDir,sessionId:X.sessionId,permissionMode:X.permissionMode,config:this.config,abortSignal:X.abortSignal};try{let F=await this.executor.executePostToolHooks(J,q,G);return this.guard.markExecuted(Y,"PostToolUse"),F}catch(F){return console.error("[HookManager] Error executing PostToolUse hooks:",F),{warning:`Hook execution failed: ${F instanceof Error?F.message:String(F)}`}}finally{this.guard.cleanup(Y)}}async executeStopHooks(Q){if(!this.isEnabled())return{shouldStop:!0};let Y={hook_event_name:"Stop",hook_execution_id:j(),timestamp:new Date().toISOString(),project_dir:Q.projectDir,session_id:Q.sessionId,permission_mode:Q.permissionMode,reason:Q.reason},Z=this.getMatchingHooks("Stop",{});if(Z.length===0)return{shouldStop:!0};let W={projectDir:Q.projectDir,sessionId:Q.sessionId,permissionMode:Q.permissionMode,config:this.config,abortSignal:Q.abortSignal};try{return await this.executor.executeStopHooks(Z,Y,W)}catch(X){return console.error("[HookManager] Error executing Stop hooks:",X),{shouldStop:!0,warning:`Hook execution failed: ${X instanceof Error?X.message:String(X)}`}}}async executeSubagentStopHooks(Q,Y){if(!this.isEnabled())return{shouldStop:!0};let Z={hook_event_name:"SubagentStop",hook_execution_id:j(),timestamp:new Date().toISOString(),project_dir:Y.projectDir,session_id:Y.sessionId,permission_mode:Y.permissionMode,agent_type:Q,task_description:Y.taskDescription,success:Y.success,result_summary:Y.resultSummary,error:Y.error},W=this.getMatchingHooks("SubagentStop",{});if(W.length===0)return{shouldStop:!0};let X={projectDir:Y.projectDir,sessionId:Y.sessionId,permissionMode:Y.permissionMode,config:this.config,abortSignal:Y.abortSignal};try{return await this.executor.executeSubagentStopHooks(W,Z,X)}catch(q){return console.error("[HookManager] Error executing SubagentStop hooks:",q),{shouldStop:!0,warning:`Hook execution failed: ${q instanceof Error?q.message:String(q)}`}}}async executePermissionRequestHooks(Q,Y,Z,W){if(!this.isEnabled())return{decision:"ask"};let X={hook_event_name:"PermissionRequest",hook_execution_id:j(),timestamp:new Date().toISOString(),tool_name:Q,tool_use_id:Y,tool_input:Z,project_dir:W.projectDir,session_id:W.sessionId,permission_mode:W.permissionMode},q=this.getMatchingHooks("PermissionRequest",{toolName:Q,filePath:this.extractFilePath(Z),command:this.extractCommand(Q,Z)});if(q.length===0)return{decision:"ask"};let J={projectDir:W.projectDir,sessionId:W.sessionId,permissionMode:W.permissionMode,config:this.config,abortSignal:W.abortSignal};try{return await this.executor.executePermissionRequestHooks(q,X,J)}catch(G){return console.error("[HookManager] Error executing PermissionRequest hooks:",G),{decision:"ask",warning:`Hook execution failed: ${G instanceof Error?G.message:String(G)}`}}}async executeUserPromptSubmitHooks(Q,Y){if(!this.isEnabled())return{proceed:!0};let Z={hook_event_name:"UserPromptSubmit",hook_execution_id:j(),timestamp:new Date().toISOString(),user_prompt:Q,has_images:Y.hasImages,image_count:Y.imageCount,project_dir:Y.projectDir,session_id:Y.sessionId,permission_mode:Y.permissionMode},W=this.getMatchingHooks("UserPromptSubmit",{});if(W.length===0)return{proceed:!0};let X={projectDir:Y.projectDir,sessionId:Y.sessionId,permissionMode:Y.permissionMode,config:this.config,abortSignal:Y.abortSignal};try{return await this.executor.executeUserPromptSubmitHooks(W,Z,X)}catch(q){return console.error("[HookManager] Error executing UserPromptSubmit hooks:",q),{proceed:!0,warning:`Hook execution failed: ${q instanceof Error?q.message:String(q)}`}}}async executeSessionStartHooks(Q){if(!this.isEnabled())return{proceed:!0};let Y={hook_event_name:"SessionStart",hook_execution_id:j(),timestamp:new Date().toISOString(),project_dir:Q.projectDir,session_id:Q.sessionId,permission_mode:Q.permissionMode,is_resume:Q.isResume,resume_session_id:Q.resumeSessionId},Z=this.getMatchingHooks("SessionStart",{});if(Z.length===0)return{proceed:!0};let W={projectDir:Q.projectDir,sessionId:Q.sessionId,permissionMode:Q.permissionMode,config:this.config,abortSignal:Q.abortSignal};try{return await this.executor.executeSessionStartHooks(Z,Y,W)}catch(X){return console.error("[HookManager] Error executing SessionStart hooks:",X),{proceed:!0,warning:`Hook execution failed: ${X instanceof Error?X.message:String(X)}`}}}async executeSessionEndHooks(Q,Y){if(!this.isEnabled())return{};let Z={hook_event_name:"SessionEnd",hook_execution_id:j(),timestamp:new Date().toISOString(),project_dir:Y.projectDir,session_id:Y.sessionId,permission_mode:Y.permissionMode,reason:Q},W=this.getMatchingHooks("SessionEnd",{});if(W.length===0)return{};let X={projectDir:Y.projectDir,sessionId:Y.sessionId,permissionMode:Y.permissionMode,config:this.config,abortSignal:Y.abortSignal};try{return await this.executor.executeSessionEndHooks(W,Z,X),{}}catch(q){return console.error("[HookManager] Error executing SessionEnd hooks:",q),{warning:`Hook execution failed: ${q instanceof Error?q.message:String(q)}`}}}async executePostToolUseFailureHooks(Q,Y,Z,W,X){if(!this.isEnabled())return{};let q={hook_event_name:"PostToolUseFailure",hook_execution_id:j(),timestamp:new Date().toISOString(),tool_name:Q,tool_use_id:Y,tool_input:Z,error:W,error_type:X.errorType,is_interrupt:X.isInterrupt,is_timeout:X.isTimeout,project_dir:X.projectDir,session_id:X.sessionId,permission_mode:X.permissionMode},J=this.getMatchingHooks("PostToolUseFailure",{toolName:Q,filePath:this.extractFilePath(Z),command:this.extractCommand(Q,Z)});if(J.length===0)return{};let G={projectDir:X.projectDir,sessionId:X.sessionId,permissionMode:X.permissionMode,config:this.config,abortSignal:X.abortSignal};try{return await this.executor.executePostToolUseFailureHooks(J,q,G)}catch(F){return console.error("[HookManager] Error executing PostToolUseFailure hooks:",F),{warning:`Hook execution failed: ${F instanceof Error?F.message:String(F)}`}}}async executeNotificationHooks(Q,Y,Z){if(!this.isEnabled())return{suppress:!1,message:Y};let W={hook_event_name:"Notification",hook_execution_id:j(),timestamp:new Date().toISOString(),project_dir:Z.projectDir,session_id:Z.sessionId,permission_mode:Z.permissionMode,notification_type:Q,title:Z.title,message:Y},X=this.getMatchingHooks("Notification",{});if(X.length===0)return{suppress:!1,message:Y};let q={projectDir:Z.projectDir,sessionId:Z.sessionId,permissionMode:Z.permissionMode,config:this.config,abortSignal:Z.abortSignal};try{return await this.executor.executeNotificationHooks(X,W,q)}catch(J){return console.error("[HookManager] Error executing Notification hooks:",J),{suppress:!1,message:Y,warning:`Hook execution failed: ${J instanceof Error?J.message:String(J)}`}}}async executeCompactionHooks(Q,Y){if(!this.isEnabled())return{blockCompaction:!1};let Z={hook_event_name:"Compaction",hook_execution_id:j(),timestamp:new Date().toISOString(),project_dir:Y.projectDir,session_id:Y.sessionId,permission_mode:Y.permissionMode,trigger:Q,messages_before:Y.messagesBefore,tokens_before:Y.tokensBefore},W=this.getMatchingHooks("Compaction",{});if(W.length===0)return{blockCompaction:!1};let X={projectDir:Y.projectDir,sessionId:Y.sessionId,permissionMode:Y.permissionMode,config:this.config,abortSignal:Y.abortSignal};try{return await this.executor.executeCompactionHooks(W,Z,X)}catch(q){return console.error("[HookManager] Error executing Compaction hooks:",q),{blockCompaction:!1,warning:`Hook execution failed: ${q instanceof Error?q.message:String(q)}`}}}getMatchingHooks(Q,Y){let Z=this.config[Q]||[],W=[];for(let X of Z)if(this.matcher.matches(X.matcher,Y))W.push(...X.hooks);return W}extractFilePath(Q){let Y=["file_path","path","filePath","source","target"];for(let Z of Y){let W=Q[Z];if(typeof W==="string")return W}return}extractCommand(Q,Y){if(Q==="Bash"||Q==="BashTool"){let Z=Y.command;if(typeof Z==="string")return Z}return}cleanup(){this.guard.cleanupAll()}}
|
|
41
|
+
export{U as mb,T as nb};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import{sb as Y,xb as Z}from"./chunk-7ycmx7c2.js";import{execSync as A}from"child_process";import{existsSync as N,realpathSync as E}from"fs";import{basename as I,isAbsolute as _,resolve as F}from"path";import*as Q from"os";import*as H from"path";function T(){let B=Z(),q=J(B);return{workingDirectory:B,projectRoot:q,platform:`${Q.platform()} (${Q.arch()})`,nodeVersion:process.version,currentDate:new Date().toISOString().split("T")[0],homeDirectory:Q.homedir()}}function U(B,q){try{return A(q,{cwd:B,encoding:"utf8",stdio:["pipe","pipe","ignore"]}).trim()}catch{return null}}function f(B={}){let{includeGitSnapshot:q=!1}=B,z=T(),K=N(H.join(z.projectRoot,".git")),$=I(process.env.SHELL||"unknown"),V=["package.json","tsconfig.json","BLADE.md",".env.example"].filter((M)=>N(H.join(z.projectRoot,M))).map((M)=>`- \`${H.join(z.projectRoot,M)}\``),L=`# Environment
|
|
2
|
+
You have been invoked in the following environment:
|
|
3
|
+
- Primary working directory: ${z.workingDirectory}
|
|
4
|
+
- Is a git repository: ${K?"true":"false"}
|
|
5
|
+
- Platform: ${z.platform}
|
|
6
|
+
- Shell: ${$}
|
|
7
|
+
- Node.js: ${z.nodeVersion}`;if(q&&K){let M=U(z.projectRoot,"git rev-parse --abbrev-ref HEAD"),W=U(z.projectRoot,"git status --short"),X=U(z.projectRoot,"git log --oneline -n 3");if(M)L+=`
|
|
8
|
+
- Current branch: ${M}`;if(W)L+=`
|
|
9
|
+
|
|
10
|
+
Working tree status:
|
|
11
|
+
${W}`;if(X)L+=`
|
|
12
|
+
|
|
13
|
+
Recent commits:
|
|
14
|
+
${X}`}if(L+=`
|
|
15
|
+
|
|
16
|
+
When using file tools (read, write, edit), provide absolute paths based on: \`${z.workingDirectory}/\``,V.length>0)L+=`
|
|
17
|
+
|
|
18
|
+
Key project files at root for quick reference:
|
|
19
|
+
${V.join(`
|
|
20
|
+
`)}`;return L}function J(B){let q=B,z=null;while(q!==H.dirname(q)){if(N(H.join(q,".git")))return q;if(N(H.join(q,".blade")))return q;if(N(H.join(q,".claude")))return q;if(!z&&N(H.join(q,"package.json")))z=q;q=H.dirname(q)}return z??B}function k(B,q){let z=_(B)?B:F(q||process.cwd(),B),K;try{K=E(z)}catch{K=z}Y(K)}
|
|
21
|
+
export{f as Z,J as _,k as $};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-j2ag7hh6.js";var d={command:"install [target]",describe:"Install Blade native build. Use [target] to specify version (stable, latest, or specific version)",builder:(b)=>{return b.positional("target",{describe:"Version to install",type:"string",default:"stable",choices:["stable","latest"]}).option("force",{type:"boolean",describe:"Force reinstall",default:!1}).example([["$0 install","Install stable version"],["$0 install latest","Install latest version"],["$0 install --force","Force reinstall stable version"]])},handler:async(b)=>{console.log(`Installing Blade ${b.target}...`);try{if(b.force)console.log("Force reinstall enabled");console.log("Downloading..."),console.log("Installing..."),console.log("Installation completed successfully")}catch(c){console.error(`Error: Installation failed: ${c instanceof Error?c.message:"未知错误"}`),process.exit(1)}}};export{d as installCommands};
|