@devflow-tools/cli 0.16.8 → 0.16.9
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/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +59 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/mcp.d.ts +10 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +33 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/plugin/install.d.ts.map +1 -1
- package/dist/commands/plugin/install.js +6 -7
- package/dist/commands/plugin/install.js.map +1 -1
- package/dist/commands/plugin/uninstall.d.ts.map +1 -1
- package/dist/commands/plugin/uninstall.js +6 -7
- package/dist/commands/plugin/uninstall.js.map +1 -1
- package/dist/commands/plugin-guide.d.ts.map +1 -1
- package/dist/commands/plugin-guide.js +8 -5
- package/dist/commands/plugin-guide.js.map +1 -1
- package/dist/commands/skill-health.d.ts +21 -0
- package/dist/commands/skill-health.d.ts.map +1 -0
- package/dist/commands/skill-health.js +106 -0
- package/dist/commands/skill-health.js.map +1 -0
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +101 -29
- package/dist/commands/skill.js.map +1 -1
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/plugin-registry.d.ts.map +1 -1
- package/dist/lib/plugin-registry.js +3 -10
- package/dist/lib/plugin-registry.js.map +1 -1
- package/dist/plugin-files/.claude-plugin/plugin.json +1 -1
- package/dist/plugin-files/dist/command-registry.json +116 -20
- package/dist/plugin-files/dist/hooks/hook-client.js +6 -6
- package/dist/plugin-files/dist/hooks/hook-daemon.js +9 -9
- package/dist/plugin-files/dist/hooks/post-tool-use.js +3 -3
- package/dist/plugin-files/dist/hooks/pre-tool-use.js +2 -2
- package/dist/plugin-files/dist/hooks/user-prompt-submit.js +2 -2
- package/dist/plugin-files/package.json +7 -7
- package/package.json +27 -27
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {createServer,createConnection}from'net';import {join,dirname}from'path';import {mkdirSync,lstatSync,openSync,writeFileSync,fstatSync,closeSync,readFileSync,unlinkSync,existsSync,rmSync,renameSync,readdirSync,appendFileSync as appendFileSync$1}from'fs';import {getLocalApiKey,getDaemonSocketPath,getDaemonPidPath,loadConfig,getProjectStateDir,getDaemonRegistryPath,getProjectHash}from'@devflow-tools/sdk';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {fileURLToPath}from'url';import {spawn}from'child_process';import {mkdirSync as mkdirSync$1,appendFileSync,readdirSync as readdirSync$1,renameSync as renameSync$1,unlinkSync as unlinkSync$1,writeFileSync as writeFileSync$1,readFileSync as readFileSync$1,openSync as openSync$1,closeSync as closeSync$1,rmSync as rmSync$1,statSync,existsSync as existsSync$1}from'node:fs';import {dirname as dirname$1,basename,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID,createHash}from'crypto';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';var le=1e4,et=3e4,x=500;function tt(r,e,t){return new Promise(n=>{try{let o=new URL(r),s=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":t,"Content-Length":Buffer.byteLength(e)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(e),s.end();}catch{n(null);}})}var b=class{constructor(e){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,n,o,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:e,sessionId:t,skillName:n,startedAt:o,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(e,t="completed",n=Date.now(),o){let s={executionId:e,status:t,finishedAt:n,metadata:o},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(e,t,n){let o={id:e,projectRoot:t,startedAt:n,label:t.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",o);return s&&this.postHttp("/api/telemetry/sessions",o),s}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.postHttp("/api/telemetry/tool-call/output",e),this.scheduleMetricAggregation()),t}async endSession(e,t=Date.now()){let n={sessionId:e,finishedAt:t},o=this.commitOrCache("session_end",n);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),o}async flushCache(){try{let n=this.getDatabase(),o=n.listTelemetryFailures({unresolvedOnly:!0,limit:x}).reverse();for(let s of o)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(x);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(o=>o.endsWith(".json")).map(o=>{let s=join(n,o);try{return {cacheFile:s,envelope:nt(JSON.parse(readFileSync(s,"utf8")),o)}}catch{return null}})).filter(n=>n!==null).sort((n,o)=>n.envelope.timestamp-o.envelope.timestamp||ue(n.envelope.operation)-ue(o.envelope.operation));for(let{cacheFile:n,envelope:o}of t)try{let s=this.getDatabase();s.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),s.resolveTelemetryFailure(o.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(e,t){try{return this.applyOperation(e,t),!0}catch(n){return this.cacheOperation(e,t,n),false}}applyOperation(e,t){let n=this.getDatabase();switch(e){case "tool_call":this.ensureParentSession(n,t),n.insertToolCallEvent(t);return;case "execution_start":this.ensureParentSession(n,t),n.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running",requiredMcpTools:t.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":n.ensureSession(t),n.insertRun({id:de(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":n.closeSession(t.sessionId,t.finishedAt),n.updateRun(de(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}ensureParentSession(e,t){let n=typeof t.sessionId=="string"?t.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let o=typeof t.projectRoot=="string"&&t.projectRoot.trim()?t.projectRoot:typeof t.input?.projectRoot=="string"&&t.input.projectRoot.trim()?t.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";e.ensureSession({id:n,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(t.startedAt??t.timestamp??Date.now())});}cacheOperation(e,t,n){let o=Date.now(),s={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:n instanceof Error?n.message:String(n),timestamp:o};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:e,payload:t,error:s.failure,createdAt:o}),i.trimTelemetryFailures(x),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-x)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},et).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(e,t){tt(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=le?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,le)}...`}}};function ue(r){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(r)}function de(r){return `hook-run:${r}`}function nt(r,e){if(!r||typeof r!="object")throw new Error("Invalid telemetry cache envelope");let t=r;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let n=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let o=t.payload??{},s=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:n,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function $(r){return r??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function T(r){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir($(r))}var ct=1;function lt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function me(r=lt()){let e=[join(r,"dist","command-registry.json"),join(r,"command-registry.json")];for(let t of e)try{if(!existsSync(t))continue;let n=JSON.parse(readFileSync(t,"utf8"));if(n.version!==ct||!ut(n.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function ut(r){return !r||typeof r!="object"||Array.isArray(r)?false:Object.values(r).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return pe(t.mcpTools)&&pe(t.blockedNative)})}function pe(r){return Array.isArray(r)&&r.every(e=>typeof e=="string"&&e.length>0)}function he(r){let e=me();return e?e[r]?.mcpTools??[]:[]}var J=join(homedir(),".devflow"),G=join(J,"server-refs.json"),yt="http://127.0.0.1:13337/api/health",vt=600*1e3,wt=500;function _t(){existsSync(J)||mkdirSync(J,{recursive:true});}function C(){try{if(existsSync(G))return JSON.parse(readFileSync(G,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function fe(r){_t(),writeFileSync(G,JSON.stringify(r));}var P=class{constructor(e){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=e;}setOnShutdown(e){this.onShutdown=e;}addRef(e){let t=C();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),fe(t),this.resetIdleTimer();}removeRef(e){let t=C();t.sessions=t.sessions.filter(n=>n!==e),t.lastActivity=Date.now(),fe(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return C().sessions.length}async ensureRunning(){return await this.healthCheck()?true:(await this.startServer(),this.waitForReady())}get isRunning(){return this.process!==null&&!this.process.killed}async stop(){this.idleTimer&&clearTimeout(this.idleTimer),this.process&&(this.process.kill("SIGTERM"),await new Promise(e=>{let t=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),e();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(t),e();}):(clearTimeout(t),e());}),this.process=null);}async startServer(){let e=join(this.projectRoot,"apps","server","dist","main.js"),t=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),n=existsSync(e)?e:t;this.process=spawn("node",[n],{cwd:this.projectRoot,env:{...process.env,NODE_ENV:process.env.NODE_ENV||"development"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",o=>{}),this.process.stderr?.on("data",o=>{}),this.process.on("exit",o=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let e=Date.now()+3e4;for(;Date.now()<e;){if(await this.healthCheck())return true;await new Promise(t=>setTimeout(t,wt));}return false}healthCheck(){return new Promise(e=>{let t=new URL(yt),n=request({hostname:t.hostname,port:t.port,path:t.pathname,method:"GET",timeout:2e3},o=>{e(o.statusCode===200);});n.on("error",()=>e(false)),n.on("timeout",()=>{n.destroy(),e(false);}),n.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(C().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},vt).unref());}};function ge(r,e,t){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:r,timestamp:Date.now(),duration:t,success:e.success!==!1,metadata:e});}finally{n.close();}}catch{}}var z=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",Dt=getLocalApiKey(),xt=8,Ct=6e4,ye=new Map;function Mt(r){let e=new URL(r,z).pathname,t=ye.get(e);return t||(t={failures:0,lastFailure:0,openUntil:0,status:"closed"},ye.set(e,t)),t}function K(r,e,t){if(e.status===t)return;let n=e.status;e.status=t,ge("http_circuit_transition",{path:new URL(r,z).pathname,previous:n,status:t,failures:e.failures,openUntil:e.openUntil});}function H(r,e){e.failures++,e.lastFailure=Date.now(),e.failures>=xt&&(e.openUntil=Date.now()+Ct,K(r,e,"open"));}function Pt(r,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&K(r,e,"closed");}var B=join(homedir(),".devflow","errors");function ve(r,e){try{existsSync(B)||mkdirSync(B,{recursive:!0});let t=`${new Date().toISOString()} | ${r} | ${e?.message??String(e)}
|
|
2
|
-
`;appendFileSync$1(join(B,"http-errors.log"),t);}catch{}}function _e(r,e,t={}){let n=t.timeout??5e3,o=t.maxRetries??2,s=t.circuitBreaker??true,i=Mt(r);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&K(r,i,"half_open");let a=c=>new Promise(u=>{let l=JSON.stringify(e),m=new URL(r,z),p=request({hostname:m.hostname,port:m.port,path:m.pathname+m.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":Dt,"Content-Length":Buffer.byteLength(l)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let d=new Error(`HTTP ${f.statusCode}`);if(ve(r,d),c>0){let h=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},h);}else H(r,i),u();return}Pt(r,i),u();});p.on("error",f=>{if(ve(r,f),c>0){let d=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},d);}else H(r,i),u();}),p.on("timeout",()=>{p.destroy(),c>0?a(c-1).then(u):(H(r,i),u());}),p.write(l),p.end();});return a(o)}function O(r){if(!r||typeof r!="object"||Array.isArray(r))return {};let e=r,t={};for(let n of ["lastMcpCall","bypassCount"])if(n in e){let o=e[n];if(o==null)continue;t[n]=typeof o=="number"&&Number.isFinite(o)&&o>=0?o:0;}return t}function jt(r,e){return r.lastMcpCall===e.lastMcpCall&&r.bypassCount===e.bypassCount}function Ft(r){try{return existsSync(r)?O(JSON.parse(readFileSync(r,"utf-8"))):{}}catch{return {}}}function Nt(r,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${r} skipped: ${t}`);}function Se(r){let e=T(r);for(let t of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(e,t))&&unlinkSync(join(e,t));}catch{}}function Lt(r,e){if(r.getHookReceipt(e)){Se(e);return}let t=join(T(e),"receipt.json");if(!existsSync(t))return;let n=Ft(t);r.updateHookReceipt(e,()=>n),Se(e);}function be(r,e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Lt(n,r);let o={},s=n.updateHookReceipt(r,a=>(o=O(a),O(e(o)))),i=O(s);return {receipt:i,applied:!0,changed:!jt(o,i)}}catch(o){return Nt(t,o),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function A(r,e){return be(r,()=>e,"write").applied}function Y(r,e){return be(r,e,"update")}var Ee={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ut(r){switch(r){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var k=class{constructor(e,t,n){this.projectRoot=e;this.sessionId=t;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let e;try{e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let t=e.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(t&&t.expiresAt>Date.now())return "context_ready";t&&e.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{e?.close();}catch{}}return "context_gathering"}evaluate(e,t){let n=this.getPhase();if(t||e==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&Ee[e])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${e}.`};if(n==="context_ready")return {permissionDecision:"allow"};let o=Ee[e];if(!o)return {permissionDecision:"allow"};let s=Y(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ut(e);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${e}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${o} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ht=10,Bt=5e3,zt=3e4,Kt=new Int32Array(new SharedArrayBuffer(4)),Ce=0;function Vt(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.rootPath=="string"&&e.rootPath.length>0&&typeof e.projectHash=="string"&&e.projectHash.length>0&&typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.startedAt=="number"&&Number.isFinite(e.startedAt)&&e.startedAt>=0}function Me(r){if(!existsSync$1(r))return {entries:[],needsRepair:false};try{let e=JSON.parse(readFileSync$1(r,"utf8"));if(!Array.isArray(e))return {entries:[],needsRepair:!0};let t=e.filter(Vt);return {entries:t,needsRepair:t.length!==e.length}}catch{return {entries:[],needsRepair:true}}}function Pe(r,e){mkdirSync$1(dirname$1(r),{recursive:true});let t=`${r}.${process.pid}.${Date.now()}.${Ce++}.tmp`,n;try{n=openSync$1(t,"wx"),writeFileSync$1(n,JSON.stringify(e,null,2)),closeSync$1(n),n=void 0,renameSync$1(t,r);}finally{try{n!==void 0&&closeSync$1(n);}finally{rmSync$1(t,{force:true});}}}function Oe(r){try{return process.kill(r,0),!0}catch(e){return e.code!=="ESRCH"}}function Ae(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.createdAt=="number"&&Number.isFinite(e.createdAt)&&typeof e.token=="string"&&e.token.length>0}function Te(r,e){try{return readFileSync$1(r,"utf8")!==e?!1:(rmSync$1(r),!0)}catch(t){return t.code==="ENOENT"}}function Xt(r){try{let e=readFileSync$1(r,"utf8"),t;try{let o=JSON.parse(e);Ae(o)&&(t=o);}catch{}return t?Oe(t.pid)?!1:Te(r,e):Date.now()-statSync(r).mtimeMs>zt&&Te(r,e)}catch(e){return e.code==="ENOENT"}}function Yt(r){mkdirSync$1(dirname$1(r),{recursive:true});let e=`${r}.lock`,t=Date.now()+Bt;for(;;){let n=`${process.pid}:${Date.now()}:${Ce++}`,o;try{return o=openSync$1(e,"wx"),writeFileSync$1(o,JSON.stringify({pid:process.pid,createdAt:Date.now(),token:n})),closeSync$1(o),o=void 0,{path:e,token:n}}catch(s){if(o!==void 0)try{closeSync$1(o);}finally{rmSync$1(e,{force:true});}if(s.code!=="EEXIST")throw s}if(!Xt(e)){if(Date.now()>=t)throw new Error(`Timed out acquiring daemon registry lock: ${e}`);Atomics.wait(Kt,0,0,Ht);}}}function Qt(r){try{let e=readFileSync$1(r.path,"utf8"),t=JSON.parse(e);Ae(t)&&t.token===r.token&&rmSync$1(r.path);}catch(e){if(e.code!=="ENOENT")throw e}}function je(r){let e=getDaemonRegistryPath(),t=Yt(e);try{return r(e)}finally{Qt(t);}}function Fe(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r&&Oe(o.pid));n.push({rootPath:r,projectHash:getProjectHash(r),pid:e,startedAt:Date.now()}),Pe(t,n);});}function Ne(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r||e!==void 0&&o.pid!==e);Pe(t,n);});}async function L(r){let e=!r.memory,t=r.memory??new MemoryGate(r.projectRoot),n=0,o=0;try{await t.forceWarmUp(),r.trigger==="session_end"&&(n=await t.releasePendingDistillLeases(r.sessionId)),o=t.getPendingEventCount();}finally{e&&t.close();}let s={pendingEvents:o,releasedLeases:n,trigger:r.trigger,sessionId:r.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:c,projectRoot:r.projectRoot,sessionId:r.sessionId,trigger:r.trigger,pendingEvents:o,releasedLeases:n,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...s,projectRoot:r.projectRoot}});}finally{i.close();}}catch{}return s}function Q(r){if(r.pendingEvents===0)return "";let e=r.sessionId?`\uFF0CsessionId=${r.sessionId}`:"";return `Memory distill checkpoint: ${r.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${e}\u3002\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210\u540E\uFF0C\u8C03\u7528 mcp__devflow__memory_request_distill\uFF1B\u6309\u8FD4\u56DE\u7684 nextPage \u5206\u9875\u8BFB\u53D6\u5168\u90E8\u4E8B\u4EF6\uFF0C\u63D0\u70BC observations\uFF0C\u518D\u8C03\u7528 mcp__devflow__memory_save_distilled \u5E76\u786E\u8BA4\u4FDD\u5B58\u7ED3\u679C\u3002`}function ln(r){return Buffer.from(r,"utf8").toString("base64url")}var I=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(T(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let n=this.get(t);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let o=Date.now(),s={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,s),this.persist(s),s}startExecution(e,t,n){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(n)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let o=this.snapshotPath(t);if(!existsSync(o))return null;try{let s=JSON.parse(readFileSync(o,"utf8"));return s.sessionId!==t||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(t,s),s)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let n={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),n}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join(this.sessionsDir,`${ln(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),n=`${t}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(e),{mode:384}),renameSync(n,t);}};var mn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",hn=getLocalApiKey();function fn(r,e){return new Promise(t=>{let n=JSON.stringify(e),o=new URL(r,mn),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":hn,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());s.on("error",()=>t()),s.write(n),s.end();})}async function Z(r,e=$(),t=true,n){let o;try{o=JSON.parse(r);}catch{return}let s=o.session_id?.trim();if(!s)return;let i=new I(e),a=i.get(s),c=n?.telemetry??new b,u=!n?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let m=l.listToolCallEventsBySession(s),p=a?.executionId;if(p){let f=m.filter(S=>S.executionId===p),d=f.length,h=f.filter(S=>S.isMcpTool).length,g=d-h,y=f.filter(S=>S.toolType==="subagent").length,_=Date.now(),E=l.getSkillExecution(p),v=E?.startedAt!=null?Math.max(0,_-E.startedAt):0,w=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:_,totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:w.rate}),await fn("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:_,status:"completed",summary:{totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:w.rate}});}catch{}}t&&l.deleteHookReceipt(e),l.deleteContextReceipt(e,s);}catch{}finally{l?.close();}try{if(await L({projectRoot:e,sessionId:s,trigger:"session_end",memory:n?.memory}),n?.memory)await n.memory.closeSession(s);else {let m=new(await import('@devflow-tools/memory-engine')).MemoryGate(e);try{await m.closeSession(s);}finally{m.close();}}}catch{}try{await c.endSession(s),await c.flushAndAggregate();}catch{}}finally{u&&c.close(),i.removeSession(s);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let r="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{r+=e;}),process.stdin.on("end",async()=>{await Z(r.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var bn=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,Tn=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu;function qe(r){let e=r.trim();return e&&bn.test(e)&&!Tn.test(e)?e:null}function Je(r){let e=createHash$1("sha256").update(r).digest("hex").slice(0,16),t=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",e);return join$1(t,"memory-intents.jsonl")}function In(r){try{return readFileSync$1(r,"utf8").split(`
|
|
3
|
-
`).filter(Boolean).flatMap(e=>{try{let t=JSON.parse(e);return typeof t.content=="string"&&typeof t.createdAt=="number"?[t]:[]}catch{return []}})}catch{return []}}function
|
|
4
|
-
`,{mode:384});}function We(r){let e=
|
|
1
|
+
import {createServer,createConnection}from'net';import {join,dirname}from'path';import {mkdirSync,lstatSync,openSync,writeFileSync,fstatSync,closeSync,readFileSync,unlinkSync,existsSync,rmSync,renameSync,readdirSync,appendFileSync as appendFileSync$1}from'fs';import {getLocalApiKey,getDaemonSocketPath,getDaemonPidPath,loadConfig,getProjectStateDir,getDaemonRegistryPath,getProjectHash}from'@devflow-tools/sdk';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {fileURLToPath}from'url';import {spawn}from'child_process';import {mkdirSync as mkdirSync$1,appendFileSync,readdirSync as readdirSync$1,renameSync as renameSync$1,unlinkSync as unlinkSync$1,writeFileSync as writeFileSync$1,readFileSync as readFileSync$1,openSync as openSync$1,closeSync as closeSync$1,rmSync as rmSync$1,statSync,existsSync as existsSync$1}from'node:fs';import {dirname as dirname$1,basename,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID,createHash}from'crypto';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';var le=1e4,et=3e4,x=500;function tt(r,e,t){return new Promise(n=>{try{let o=new URL(r),s=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":t,"Content-Length":Buffer.byteLength(e)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(e),s.end();}catch{n(null);}})}var b=class{constructor(e){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,n,o,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:e,sessionId:t,skillName:n,startedAt:o,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(e,t="completed",n=Date.now(),o){let s={executionId:e,status:t,finishedAt:n,metadata:o},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(e,t,n){let o={id:e,projectRoot:t,startedAt:n,label:t.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",o);return s&&this.postHttp("/api/telemetry/sessions",o),s}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.postHttp("/api/telemetry/tool-call/output",e),this.scheduleMetricAggregation()),t}async endSession(e,t=Date.now()){let n={sessionId:e,finishedAt:t},o=this.commitOrCache("session_end",n);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),o}async flushCache(){try{let n=this.getDatabase(),o=n.listTelemetryFailures({unresolvedOnly:!0,limit:x}).reverse();for(let s of o)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(x);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(o=>o.endsWith(".json")).map(o=>{let s=join(n,o);try{return {cacheFile:s,envelope:nt(JSON.parse(readFileSync(s,"utf8")),o)}}catch{return null}})).filter(n=>n!==null).sort((n,o)=>n.envelope.timestamp-o.envelope.timestamp||ue(n.envelope.operation)-ue(o.envelope.operation));for(let{cacheFile:n,envelope:o}of t)try{let s=this.getDatabase();s.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),s.resolveTelemetryFailure(o.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(e,t){try{return this.applyOperation(e,t),!0}catch(n){return this.cacheOperation(e,t,n),false}}applyOperation(e,t){let n=this.getDatabase();switch(e){case "tool_call":this.ensureParentSession(n,t),n.insertToolCallEvent(t);return;case "execution_start":this.ensureParentSession(n,t),n.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running",requiredMcpTools:t.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":n.ensureSession(t),n.insertRun({id:de(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":n.closeSession(t.sessionId,t.finishedAt),n.updateRun(de(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}ensureParentSession(e,t){let n=typeof t.sessionId=="string"?t.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let o=typeof t.projectRoot=="string"&&t.projectRoot.trim()?t.projectRoot:typeof t.input?.projectRoot=="string"&&t.input.projectRoot.trim()?t.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";e.ensureSession({id:n,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(t.startedAt??t.timestamp??Date.now())});}cacheOperation(e,t,n){let o=Date.now(),s={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:n instanceof Error?n.message:String(n),timestamp:o};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:e,payload:t,error:s.failure,createdAt:o}),i.trimTelemetryFailures(x),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-x)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},et).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(e,t){tt(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=le?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,le)}...`}}};function ue(r){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(r)}function de(r){return `hook-run:${r}`}function nt(r,e){if(!r||typeof r!="object")throw new Error("Invalid telemetry cache envelope");let t=r;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let n=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let o=t.payload??{},s=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:n,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function $(r){return r??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function T(r){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir($(r))}var ct=2,lt=1;function ut(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function me(r=ut()){let e=[join(r,"dist","command-registry.json"),join(r,"command-registry.json")];for(let t of e)try{if(!existsSync(t))continue;let n=JSON.parse(readFileSync(t,"utf8"));if(n.version!==ct&&n.version!==lt||!dt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function dt(r){return !r||typeof r!="object"||Array.isArray(r)?false:Object.values(r).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return pe(t.mcpTools)&&pe(t.blockedNative)&&(t.source===void 0||t.source==="core"||t.source==="plugin")&&(t.requiresContext===void 0||typeof t.requiresContext=="boolean")&&(t.evidenceMode===void 0||["static","runtime","mixed"].includes(t.evidenceMode))})}function pe(r){return Array.isArray(r)&&r.every(e=>typeof e=="string"&&e.length>0)}function he(r){let e=me();return e?e[r]?.mcpTools??[]:[]}var G=join(homedir(),".devflow"),J=join(G,"server-refs.json"),vt="http://127.0.0.1:13337/api/health",_t=600*1e3,wt=500;function St(){existsSync(G)||mkdirSync(G,{recursive:true});}function C(){try{if(existsSync(J))return JSON.parse(readFileSync(J,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function fe(r){St(),writeFileSync(J,JSON.stringify(r));}var P=class{constructor(e){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=e;}setOnShutdown(e){this.onShutdown=e;}addRef(e){let t=C();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),fe(t),this.resetIdleTimer();}removeRef(e){let t=C();t.sessions=t.sessions.filter(n=>n!==e),t.lastActivity=Date.now(),fe(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return C().sessions.length}async ensureRunning(){return await this.healthCheck()?true:(await this.startServer(),this.waitForReady())}get isRunning(){return this.process!==null&&!this.process.killed}async stop(){this.idleTimer&&clearTimeout(this.idleTimer),this.process&&(this.process.kill("SIGTERM"),await new Promise(e=>{let t=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),e();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(t),e();}):(clearTimeout(t),e());}),this.process=null);}async startServer(){let e=join(this.projectRoot,"apps","server","dist","main.js"),t=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),n=existsSync(e)?e:t;this.process=spawn("node",[n],{cwd:this.projectRoot,env:{...process.env,NODE_ENV:process.env.NODE_ENV||"development"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",o=>{}),this.process.stderr?.on("data",o=>{}),this.process.on("exit",o=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let e=Date.now()+3e4;for(;Date.now()<e;){if(await this.healthCheck())return true;await new Promise(t=>setTimeout(t,wt));}return false}healthCheck(){return new Promise(e=>{let t=new URL(vt),n=request({hostname:t.hostname,port:t.port,path:t.pathname,method:"GET",timeout:2e3},o=>{e(o.statusCode===200);});n.on("error",()=>e(false)),n.on("timeout",()=>{n.destroy(),e(false);}),n.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(C().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},_t).unref());}};function ge(r,e,t){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:r,timestamp:Date.now(),duration:t,success:e.success!==!1,metadata:e});}finally{n.close();}}catch{}}var V=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",xt=getLocalApiKey(),Ct=8,Mt=6e4,ye=new Map;function Pt(r){let e=new URL(r,V).pathname,t=ye.get(e);return t||(t={failures:0,lastFailure:0,openUntil:0,status:"closed"},ye.set(e,t)),t}function z(r,e,t){if(e.status===t)return;let n=e.status;e.status=t,ge("http_circuit_transition",{path:new URL(r,V).pathname,previous:n,status:t,failures:e.failures,openUntil:e.openUntil});}function H(r,e){e.failures++,e.lastFailure=Date.now(),e.failures>=Ct&&(e.openUntil=Date.now()+Mt,z(r,e,"open"));}function Ot(r,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&z(r,e,"closed");}var B=join(homedir(),".devflow","errors");function ve(r,e){try{existsSync(B)||mkdirSync(B,{recursive:!0});let t=`${new Date().toISOString()} | ${r} | ${e?.message??String(e)}
|
|
2
|
+
`;appendFileSync$1(join(B,"http-errors.log"),t);}catch{}}function we(r,e,t={}){let n=t.timeout??5e3,o=t.maxRetries??2,s=t.circuitBreaker??true,i=Pt(r);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&z(r,i,"half_open");let a=c=>new Promise(u=>{let l=JSON.stringify(e),m=new URL(r,V),p=request({hostname:m.hostname,port:m.port,path:m.pathname+m.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":xt,"Content-Length":Buffer.byteLength(l)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let d=new Error(`HTTP ${f.statusCode}`);if(ve(r,d),c>0){let h=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},h);}else H(r,i),u();return}Ot(r,i),u();});p.on("error",f=>{if(ve(r,f),c>0){let d=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},d);}else H(r,i),u();}),p.on("timeout",()=>{p.destroy(),c>0?a(c-1).then(u):(H(r,i),u());}),p.write(l),p.end();});return a(o)}function O(r){if(!r||typeof r!="object"||Array.isArray(r))return {};let e=r,t={};for(let n of ["lastMcpCall","bypassCount"])if(n in e){let o=e[n];if(o==null)continue;t[n]=typeof o=="number"&&Number.isFinite(o)&&o>=0?o:0;}return t}function Ft(r,e){return r.lastMcpCall===e.lastMcpCall&&r.bypassCount===e.bypassCount}function Nt(r){try{return existsSync(r)?O(JSON.parse(readFileSync(r,"utf-8"))):{}}catch{return {}}}function Lt(r,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${r} skipped: ${t}`);}function Se(r){let e=T(r);for(let t of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(e,t))&&unlinkSync(join(e,t));}catch{}}function Ut(r,e){if(r.getHookReceipt(e)){Se(e);return}let t=join(T(e),"receipt.json");if(!existsSync(t))return;let n=Nt(t);r.updateHookReceipt(e,()=>n),Se(e);}function be(r,e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Ut(n,r);let o={},s=n.updateHookReceipt(r,a=>(o=O(a),O(e(o)))),i=O(s);return {receipt:i,applied:!0,changed:!Ft(o,i)}}catch(o){return Lt(t,o),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function A(r,e){return be(r,()=>e,"write").applied}function X(r,e){return be(r,e,"update")}var Ee={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function $t(r){switch(r){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var k=class{constructor(e,t,n){this.projectRoot=e;this.sessionId=t;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let e;try{e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let t=e.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(t&&t.expiresAt>Date.now())return "context_ready";t&&e.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{e?.close();}catch{}}return "context_gathering"}evaluate(e,t){let n=this.getPhase();if(t||e==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&Ee[e])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${e}.`};if(n==="context_ready")return {permissionDecision:"allow"};let o=Ee[e];if(!o)return {permissionDecision:"allow"};let s=X(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=$t(e);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${e}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${o} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Bt=10,Vt=5e3,zt=3e4,Kt=new Int32Array(new SharedArrayBuffer(4)),Ce=0;function Yt(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.rootPath=="string"&&e.rootPath.length>0&&typeof e.projectHash=="string"&&e.projectHash.length>0&&typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.startedAt=="number"&&Number.isFinite(e.startedAt)&&e.startedAt>=0}function Me(r){if(!existsSync$1(r))return {entries:[],needsRepair:false};try{let e=JSON.parse(readFileSync$1(r,"utf8"));if(!Array.isArray(e))return {entries:[],needsRepair:!0};let t=e.filter(Yt);return {entries:t,needsRepair:t.length!==e.length}}catch{return {entries:[],needsRepair:true}}}function Pe(r,e){mkdirSync$1(dirname$1(r),{recursive:true});let t=`${r}.${process.pid}.${Date.now()}.${Ce++}.tmp`,n;try{n=openSync$1(t,"wx"),writeFileSync$1(n,JSON.stringify(e,null,2)),closeSync$1(n),n=void 0,renameSync$1(t,r);}finally{try{n!==void 0&&closeSync$1(n);}finally{rmSync$1(t,{force:true});}}}function Oe(r){try{return process.kill(r,0),!0}catch(e){return e.code!=="ESRCH"}}function Ae(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.createdAt=="number"&&Number.isFinite(e.createdAt)&&typeof e.token=="string"&&e.token.length>0}function Te(r,e){try{return readFileSync$1(r,"utf8")!==e?!1:(rmSync$1(r),!0)}catch(t){return t.code==="ENOENT"}}function Xt(r){try{let e=readFileSync$1(r,"utf8"),t;try{let o=JSON.parse(e);Ae(o)&&(t=o);}catch{}return t?Oe(t.pid)?!1:Te(r,e):Date.now()-statSync(r).mtimeMs>zt&&Te(r,e)}catch(e){return e.code==="ENOENT"}}function Qt(r){mkdirSync$1(dirname$1(r),{recursive:true});let e=`${r}.lock`,t=Date.now()+Vt;for(;;){let n=`${process.pid}:${Date.now()}:${Ce++}`,o;try{return o=openSync$1(e,"wx"),writeFileSync$1(o,JSON.stringify({pid:process.pid,createdAt:Date.now(),token:n})),closeSync$1(o),o=void 0,{path:e,token:n}}catch(s){if(o!==void 0)try{closeSync$1(o);}finally{rmSync$1(e,{force:true});}if(s.code!=="EEXIST")throw s}if(!Xt(e)){if(Date.now()>=t)throw new Error(`Timed out acquiring daemon registry lock: ${e}`);Atomics.wait(Kt,0,0,Bt);}}}function Zt(r){try{let e=readFileSync$1(r.path,"utf8"),t=JSON.parse(e);Ae(t)&&t.token===r.token&&rmSync$1(r.path);}catch(e){if(e.code!=="ENOENT")throw e}}function je(r){let e=getDaemonRegistryPath(),t=Qt(e);try{return r(e)}finally{Zt(t);}}function Fe(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r&&Oe(o.pid));n.push({rootPath:r,projectHash:getProjectHash(r),pid:e,startedAt:Date.now()}),Pe(t,n);});}function Ne(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r||e!==void 0&&o.pid!==e);Pe(t,n);});}async function L(r){let e=!r.memory,t=r.memory??new MemoryGate(r.projectRoot),n=0,o=0;try{await t.forceWarmUp(),r.trigger==="session_end"&&(n=await t.releasePendingDistillLeases(r.sessionId)),o=t.getPendingEventCount();}finally{e&&t.close();}let s={pendingEvents:o,releasedLeases:n,trigger:r.trigger,sessionId:r.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:c,projectRoot:r.projectRoot,sessionId:r.sessionId,trigger:r.trigger,pendingEvents:o,releasedLeases:n,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...s,projectRoot:r.projectRoot}});}finally{i.close();}}catch{}return s}function Q(r){if(r.pendingEvents===0)return "";let e=r.sessionId?`\uFF0CsessionId=${r.sessionId}`:"";return `Memory distill checkpoint: ${r.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${e}\u3002\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210\u540E\uFF0C\u8C03\u7528 mcp__devflow__memory_request_distill\uFF1B\u6309\u8FD4\u56DE\u7684 nextPage \u5206\u9875\u8BFB\u53D6\u5168\u90E8\u4E8B\u4EF6\uFF0C\u63D0\u70BC observations\uFF0C\u518D\u8C03\u7528 mcp__devflow__memory_save_distilled \u5E76\u786E\u8BA4\u4FDD\u5B58\u7ED3\u679C\u3002`}function un(r){return Buffer.from(r,"utf8").toString("base64url")}var I=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(T(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let n=this.get(t);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let o=Date.now(),s={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,s),this.persist(s),s}startExecution(e,t,n){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(n)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let o=this.snapshotPath(t);if(!existsSync(o))return null;try{let s=JSON.parse(readFileSync(o,"utf8"));return s.sessionId!==t||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(t,s),s)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let n={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),n}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join(this.sessionsDir,`${un(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),n=`${t}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(e),{mode:384}),renameSync(n,t);}};var hn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",fn=getLocalApiKey();function gn(r,e){return new Promise(t=>{let n=JSON.stringify(e),o=new URL(r,hn),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":fn,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());s.on("error",()=>t()),s.write(n),s.end();})}async function Z(r,e=$(),t=true,n){let o;try{o=JSON.parse(r);}catch{return}let s=o.session_id?.trim();if(!s)return;let i=new I(e),a=i.get(s),c=n?.telemetry??new b,u=!n?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let m=l.listToolCallEventsBySession(s),p=a?.executionId;if(p){let f=m.filter(S=>S.executionId===p),d=f.length,h=f.filter(S=>S.isMcpTool).length,g=d-h,y=f.filter(S=>S.toolType==="subagent").length,w=Date.now(),E=l.getSkillExecution(p),v=E?.startedAt!=null?Math.max(0,w-E.startedAt):0,_=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:w,totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:_.rate}),await gn("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:w,status:"completed",summary:{totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:_.rate}});}catch{}}t&&l.deleteHookReceipt(e),l.deleteContextReceipt(e,s);}catch{}finally{l?.close();}try{if(await L({projectRoot:e,sessionId:s,trigger:"session_end",memory:n?.memory}),n?.memory)await n.memory.closeSession(s);else {let m=new(await import('@devflow-tools/memory-engine')).MemoryGate(e);try{await m.closeSession(s);}finally{m.close();}}}catch{}try{await c.endSession(s),await c.flushAndAggregate();}catch{}}finally{u&&c.close(),i.removeSession(s);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let r="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{r+=e;}),process.stdin.on("end",async()=>{await Z(r.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var Tn=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,In=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu;function qe(r){let e=r.trim();return e&&Tn.test(e)&&!In.test(e)?e:null}function Ge(r){let e=createHash$1("sha256").update(r).digest("hex").slice(0,16),t=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",e);return join$1(t,"memory-intents.jsonl")}function kn(r){try{return readFileSync$1(r,"utf8").split(`
|
|
3
|
+
`).filter(Boolean).flatMap(e=>{try{let t=JSON.parse(e);return typeof t.content=="string"&&typeof t.createdAt=="number"?[t]:[]}catch{return []}})}catch{return []}}function Je(r,e){let t=Ge(r);mkdirSync$1(dirname$1(t),{recursive:true}),appendFileSync(t,`${JSON.stringify(e)}
|
|
4
|
+
`,{mode:384});}function We(r){let e=Ge(r),t=`${e}.claim-`,n=`${basename(e)}.claim-`,o=(()=>{try{return readdirSync$1(dirname$1(e)).filter(a=>a.startsWith(n)&&!a.includes(".tmp-")).sort()[0]}catch{return}})(),s=o?join$1(dirname$1(e),o):`${t}${Date.now()}-${process.pid}`;if(!o)try{renameSync$1(e,s);}catch{return null}let i=kn(s);if(i.length===0){try{unlinkSync$1(s);}catch{}return null}return {path:s,intents:i}}function He(r){if(r.intents.shift(),r.intents.length===0){try{unlinkSync$1(r.path);}catch{}return}let e=`${r.path}.tmp-${process.pid}`;writeFileSync$1(e,r.intents.map(t=>JSON.stringify(t)).join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`,{mode:384}),renameSync$1(e,r.path);}function U(r){let e=r.trim().replace(/^\//,"");if(!e.startsWith("devflow:"))return null;let t=e.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(t)?`devflow:${t.toLowerCase()}`:null}function Be(r){let e=r.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!e)return null;let t=U(e[1]);return t?{rawName:e[1],skillName:t}:null}function D(r,e){return e?`evt_tool_${createHash("sha256").update(`${r}\0${e}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var
|
|
7
|
-
`);if(t=m.pop()??"",Buffer.byteLength(t,"utf8")>
|
|
6
|
+
`,{mode:384}),renameSync$1(e,r.path);}function U(r){let e=r.trim().replace(/^\//,"");if(!e.startsWith("devflow:"))return null;let t=e.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(t)?`devflow:${t.toLowerCase()}`:null}function Be(r){let e=r.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!e)return null;let t=U(e[1]);return t?{rawName:e[1],skillName:t}:null}function D(r,e){return e?`evt_tool_${createHash("sha256").update(`${r}\0${e}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var Wn=600*1e3,Hn=2e3,Bn=250,Vn=2e3,zn=1e3,Ve=1024*1024,Kn=2*1024*1024,Yn=6e4,Xn=100,Qn={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Zn(r){switch(r){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}function oe(r){return typeof r=="string"&&r.trim().length>0}function eo(r,e,t=process.env,n){if(!r.startsWith("mcp__devflow__"))return e;let o=e!==null&&typeof e=="object"&&!Array.isArray(e)?e:{};if(oe(o.projectRoot))return o;let s=oe(t.CLAUDE_PROJECT_DIR)?t.CLAUDE_PROJECT_DIR:n;return oe(s)&&(o.projectRoot=s),o}var re=class{constructor(e,t){this.socketIdentity=null;this.pidIdentity=null;this.server=null;this.activeSessionIds=new Set;this.eventMap=[];this.idleTimer=null;this.telemetryFlushTimer=null;this.successfulTelemetryOperations=0;this.shutdownPromise=null;this.explicitMemoryGate=null;this.memoryWarmup=null;this.explicitIntentFlush=null;this.projectRoot=e,this.socketPath=getDaemonSocketPath(e),this.pidPath=getDaemonPidPath(e),this.telemetry=new b,this.runtimeStore=new I(this.projectRoot),this.serverManager=new P(e),t&&this.handleSessionStart(t);}async start(){mkdirSync(dirname(this.socketPath),{recursive:true,mode:448}),await this.cleanStaleStartupResources(),this.server=createServer(e=>this.handleConnection(e));try{await this.listen(),this.writePidFile(),Fe(this.projectRoot,process.pid);}catch(e){throw await this.closeServer(),this.cleanupOwnedResources(),e}this.server.on("error",e=>{console.error("[devflow-daemon] Server error:",e.message);}),console.error("[devflow-daemon] Listening on",this.socketPath),await this.telemetry.flushAndAggregate(),this.telemetryFlushTimer=setInterval(()=>{this.flushTelemetry();},Yn),this.telemetryFlushTimer.unref(),this.resetIdleTimer();}handleConnection(e){let t="",n=false,o=false,s=0,i=Promise.resolve();e.setEncoding("utf8");let a=()=>{o||(o=true,clearTimeout(c),e.destroy());},c=setTimeout(()=>{a();},Hn).unref(),u=async l=>{if(!o){o=true,clearTimeout(c);try{await this.writeFrame(e,l);}catch{}finally{e.destroy();}}};e.on("data",l=>{if(o)return;t+=l;let m=t.split(`
|
|
7
|
+
`);if(t=m.pop()??"",Buffer.byteLength(t,"utf8")>Ve){a();return}if(m.length===0)return;let p=0;for(let f of m){let d=Buffer.byteLength(f,"utf8");if(d>Ve){a();return}p+=d+1;}if(s+p>Kn){a();return}s+=p,i=i.then(async()=>{for(let f of m){if(o)return;if(!n){let d;try{d=JSON.parse(f);}catch{await u({error:"handshake_invalid"});return}if(d?.type!=="hello"||typeof d.rootPath!="string"){await u({error:"handshake_invalid"});return}if(d.rootPath!==this.projectRoot){await u({error:"handshake_root_mismatch",expected:this.projectRoot});return}n=true,clearTimeout(c),this.resetIdleTimer();continue}if(f.trim())try{let d=JSON.parse(f),h=await this.handleRequest(d);h!==null&&!o&&await this.writeFrame(e,h);}catch(d){o||await this.writeFrame(e,{error:d.message});}}}).catch(()=>{a();}).finally(()=>{s-=p;});}),e.on("close",()=>clearTimeout(c)),e.on("error",()=>{});}writeFrame(e,t){return new Promise((n,o)=>{if(e.destroyed||!e.writable){o(new Error("Socket is not writable"));return}let s=false,i=true,a=false,c=setTimeout(()=>{e.destroy(),l(new Error("Timed out writing daemon response"));},zn),u=()=>{clearTimeout(c),e.off("close",m),e.off("error",p),e.off("drain",f);},l=h=>{a||!h&&(!s||!i)||(a=true,u(),h?o(h):n());},m=()=>l(new Error("Socket closed during write")),p=h=>l(h),f=()=>{i=true,l();};e.once("close",m),e.once("error",p),e.write(`${JSON.stringify(t)}
|
|
8
8
|
`,h=>{if(h){l(h);return}s=true,l();})||(i=false,e.once("drain",f));})}async cleanStaleStartupResources(){let e=this.getPathIdentity(this.socketPath);if(e){if(await this.isSocketAcceptingConnections())throw new Error(`Daemon socket already active for ${this.projectRoot}`);this.removePathIfOwned(this.socketPath,e,"stale socket");}let t=this.getPathIdentity(this.pidPath);t&&this.removePathIfOwned(this.pidPath,t,"stale PID file");}getPathIdentity(e){try{let t=lstatSync(e);return {dev:t.dev,ino:t.ino}}catch(t){if(t.code==="ENOENT")return null;throw t}}pathMatchesIdentity(e,t){if(!t)return false;try{let n=this.getPathIdentity(e);return n?.dev===t.dev&&n.ino===t.ino}catch(n){return console.error("[devflow-daemon] Failed to verify path ownership:",n.message),false}}writePidFile(){let e=openSync(this.pidPath,"wx",384);try{writeFileSync(e,`${process.pid}
|
|
9
|
-
`);let t=fstatSync(e);this.pidIdentity={dev:t.dev,ino:t.ino};}finally{closeSync(e);}}removePathIfOwned(e,t,n,o){try{return !this.pathMatchesIdentity(e,t)||o!==void 0&&readFileSync(e,"utf8")!==o||!this.pathMatchesIdentity(e,t)?!1:(unlinkSync(e),!0)}catch(s){return s.code!=="ENOENT"&&console.error(`[devflow-daemon] Failed to remove ${n}:`,s.message),false}}isSocketAcceptingConnections(){return new Promise((e,t)=>{let n=createConnection(this.socketPath),o=setTimeout(()=>{n.destroy(),t(new Error(`Timed out probing daemon socket ${this.socketPath}`));},Hn),s=i=>{clearTimeout(o),n.destroy(),e(i);};n.once("connect",()=>s(true)),n.once("error",i=>{let a=i.code;a==="ENOENT"||a==="ECONNREFUSED"?s(false):(clearTimeout(o),t(i));});})}listen(){return new Promise((e,t)=>{let n=this.server;if(!n){t(new Error("Daemon server is not initialized"));return}let o=i=>{n.off("listening",s),t(i);},s=()=>{n.off("error",o);try{let i=this.getPathIdentity(this.socketPath);if(!i)throw new Error(`Daemon socket missing after listen: ${this.socketPath}`);this.socketIdentity=i,e();}catch(i){t(i);}};n.once("error",o),n.once("listening",s),n.listen(this.socketPath);})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=null,!(this.activeSessionIds.size>0)&&(this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},Gn).unref());}async handleRequest(e){switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return this.handlePostToolUse(e.input);case "post-tool-use-failure":return this.handlePostToolUseFailure(e.input);case "stop":return await this.handleStop(e.input??""),{status:"ok"};case "memory-snapshot":return this.handleMemorySnapshot();case "session-start":return this.handleSessionStart(e.input??"");case "session-end":return await this.handleSessionEnd(e.input??""),{status:"ok"};case "user-prompt-submit":return this.handleUserPromptSubmit(e.input);case "pre-compact":return this.handlePreCompact(e.input);default:return {error:"unknown request type"}}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);t.tool_input=Zn(t.tool_name,t.tool_input,process.env,this.projectRoot);let n=this.trackSession(t.session_id);if(t.tool_name==="Skill"){let g=t.tool_input?.skill,y=g?U(g):null;y&&this.startSkillExecution(n,y);}let o=this.runtimeStore.get(n);t.tool_name.startsWith("mcp__devflow__")&&(t.tool_input._devflow_session_id=n,t.tool_input._devflow_execution_id=o?.executionId??n,t.tool_use_id&&(t.tool_input._devflow_tool_use_id=t.tool_use_id));let s=t.tool_name.startsWith("mcp__"),i=s?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,a=new k(this.projectRoot,n,o?.executionId),c=a.getPhase(),u=a.evaluate(t.tool_name,s),l;u.permissionDecision==="deny"?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u.reason??"MCP context required"}}:u.additionalContext?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:u.additionalContext}}:l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}},t.tool_name.startsWith("mcp__devflow__")&&(l.hookSpecificOutput.updatedInput=t.tool_input);let m={...t.tool_input};delete m._devflow_session_id,delete m._devflow_execution_id,delete m._devflow_tool_use_id;let p=o?.executionId??n,d={eventId:D(n,t.tool_use_id),executionId:p,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:t.tool_name,toolType:s?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:s,mcpToolName:i,mcpEnforced:s&&c==="context_gathering",mcpFallback:!s&&c==="context_gathering",kind:Qn(t.tool_name),input:m,duration:0,blocked:l&&l.hookSpecificOutput?.permissionDecision==="deny"},h=await this.telemetry.sendEvent({...d,blockReason:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0});return h&&!d.blocked&&(this.eventMap.push({toolName:t.tool_name,toolUseId:t.tool_use_id,eventId:h,sessionId:n,timestamp:d.timestamp}),this.noteTelemetryOperation()),h&&d.blocked&&await this.telemetry.completeEvent({eventId:h,sessionId:n,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-d.timestamp,completedAt:Date.now()}),l}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}trackSession(e){let t=e?.trim()||(this.activeSessionIds.size===1?[...this.activeSessionIds][0]:"");if(!t)throw new Error("session_id_required");return this.registerSession(t),t}registerSession(e){this.activeSessionIds.has(e)||(this.activeSessionIds.size===0&&A(this.projectRoot,{bypassCount:0}),this.activeSessionIds.add(e),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.serverManager.addRef(e),this.telemetry.sendSessionStart(e,this.projectRoot,Date.now()).catch(()=>{})),this.runtimeStore.registerSession(e);}startSkillExecution(e,t){let n=this.runtimeStore.get(e)?.executionId,o=this.runtimeStore.startExecution(e,t,he(t));o.executionId&&o.executionId!==n&&this.telemetry.sendExecutionStart(o.executionId,e,t,o.lastActivityAt,this.projectRoot,o.requiredMcpTools).catch(()=>{});}async handleSessionStart(e){try{let t=JSON.parse(e);if(typeof t.session_id!="string"||!t.session_id.trim())return {error:"session_id_required"};let n=t.session_id.trim();this.registerSession(n);let o=await this.getMemoryGate();await o.ensureSession(n,this.projectRoot,"Claude Code session"),await o.releasePendingDistillLeases(n);let s=o.getPendingEventCount();return {status:"ok",...s>0?{additionalContext:Q({pendingEvents:s,releasedLeases:0,trigger:"session_end"})}:{}}}catch{return {error:"session_start_invalid"}}}popEventFromMap(e,t,n){let o=this.eventMap.filter(i=>i.toolName===e&&i.sessionId===t);if(o.length===0)return null;let s=n?o.find(i=>i.toolUseId===n):o.sort((i,a)=>i.timestamp-a.timestamp)[0];return s?(this.eventMap=this.eventMap.filter(i=>i.eventId!==s.eventId),s):null}enforcePostToolUse(e){if(!Yn[e])return null;let t=Math.floor(Date.now()/1e3),n=Y(this.projectRoot,i=>t-(i.lastMcpCall??0)<30?i:{...i,bypassCount:(i.bypassCount??0)+1});if(!n.applied||!n.changed)return null;let o=n.receipt.bypassCount??0;if(o===1)return null;let s;switch(e){case "Agent":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return o>=3?s=`${s} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:o>=2?s=`${s} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:s=`${s} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,s}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let n=t.tool_name||"",o=t.tool_input||{},s=t.tool_response,i=this.trackSession(t.session_id);if(n==="Skill"){let v=o?.skill,w=v?U(v):null;w&&(this.startSkillExecution(i,w),A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}n.startsWith("mcp__")&&new k(this.projectRoot).recordMcpCall();let a=this.popEventFromMap(n,i,t.tool_use_id);if(a){let v=typeof s=="string"?s:JSON.stringify(s),w=v.length>5e3?{_truncated:true,_originalSize:v.length,text:v.slice(0,5e3)}:s,S=Date.now()-a.timestamp;await this.telemetry.completeEvent({eventId:a.eventId,sessionId:i,output:w,duration:S,completedAt:Date.now()})&&this.noteTelemetryOperation();}let c=typeof s=="object"&&s!==null?s:null,u=c?.exitCode,l=c?.stderr,m=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,p=o.command||"",f=JSON.stringify(s),d=Buffer.byteLength(f,"utf8"),h=this.countResults(s),g,y,_=true;if(n.startsWith("mcp__"))g="mcp_call",y={mcpTool:n.replace(/^mcp__[^_]+__/,""),query:o.query??null,resultCount:h,resultSizeBytes:d};else if(n==="Read"||n==="Write"||n==="Edit")g=n==="Read"?"file_read":"file_write",y={filePath:o.file_path??null,fileContentSize:d};else if(n==="Bash"){let v=m.test(p),w=u!==void 0&&u!==0;_=!v||w,g=w?"bash_error":"bash_command",y={command:p||n,exitCode:u??null,stderr:l??null};}else n==="Agent"?(g="subagent",y={subagentType:o.subagent_type??null,description:typeof o.description=="string"?o.description.slice(0,200):null}):n==="WebSearch"||n==="WebFetch"?(g=n==="WebSearch"?"web_search":"web_fetch",y={query:typeof o.query=="string"?o.query.slice(0,200):null}):(_=false,g="tool_use",y={});if(_){let v={id:t.tool_use_id?`memory:${D(i,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i,tool:n,kind:g,payload:y,command:p||void 0,exitCode:u,stderr:l??void 0,durationMs:0,createdAt:Date.now()};this.postMemoryEvent(v);}let E=this.enforcePostToolUse(n);return E?(console.error("[devflow-daemon] Enforcement:",E),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(E)}}):null}async handlePostToolUseFailure(e){let t;try{t=JSON.parse(e);}catch{return null}let n=this.trackSession(t.session_id),o=t.tool_name??"unknown",s=t.error??`${o} failed`,i=this.popEventFromMap(o,n,t.tool_use_id);if(i)await this.telemetry.completeEvent({eventId:i.eventId,sessionId:n,error:s,duration:Date.now()-i.timestamp,completedAt:Date.now()})&&this.noteTelemetryOperation();else {let a=D(n,t.tool_use_id);await this.telemetry.sendEvent({eventId:a,executionId:this.runtimeStore.get(n)?.executionId??n,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:o,toolType:o.startsWith("mcp__")?"mcp":"direct",isMcpTool:o.startsWith("mcp__"),mcpToolName:o.startsWith("mcp__")?o.replace(/^mcp__[^_]+__/,""):void 0,mcpEnforced:false,mcpFallback:false,kind:"error",input:t.tool_input??{},duration:0,error:s,blocked:false});}return this.postMemoryEvent({id:t.tool_use_id?`memory:${D(n,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:n,tool:o,command:typeof t.tool_input?.command=="string"?t.tool_input.command:o,exitCode:-1,stderr:s,durationMs:i?Date.now()-i.timestamp:0,createdAt:Date.now(),kind:o==="Bash"?"bash_error":"tool_use",payload:{command:t.tool_input?.command??o,exitCode:-1,stderr:s}}),null}countResults(e){if(!e)return 0;let t=e;if(typeof e=="string")try{t=JSON.parse(e);}catch{return 1}let n=t,o=["files","results","data","result","memories","nodes","chunks","findings","symbols"],s=Array.isArray(n.data)?n.data:n.structuredContent??n;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of o)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=this.countResults(u));return a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}async postMemoryEvent(e){_e(`/api/memory/session-events?rootPath=${encodeURIComponent(this.projectRoot)}`,e);try{await(await this.getMemoryGate()).recordEvent(e);}catch(t){console.error("[devflow-daemon] Local memory event write failed:",t.message);}}async getMemoryGate(){let e=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);return this.memoryWarmup??=e.forceWarmUp().catch(t=>{throw this.memoryWarmup=null,t}),await this.memoryWarmup,e}async handleUserPromptSubmit(e){try{let t=JSON.parse(e),n=typeof t.prompt=="string"?t.prompt:"",o=typeof t.session_id=="string"&&t.session_id.trim()?this.trackSession(t.session_id):null,s=Be(n);if(o&&s&&this.startSkillExecution(o,s.skillName),n.trim()){let i=qe(n);i&&Ge(this.projectRoot,{content:i,sessionId:o??void 0,createdAt:Date.now()}),o&&await(await this.getMemoryGate()).recordUserMessage(n.slice(0,2e3),o,n.slice(0,200));}}catch{}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}}}async handlePreCompact(e){let t;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}await this.flushTelemetry(),this.startExplicitIntentFlush(),await this.explicitIntentFlush;let n=await this.getMemoryGate(),o=await L({projectRoot:this.projectRoot,sessionId:t,trigger:"pre_compact",memory:n}),s=Q(o);return {hookSpecificOutput:{hookEventName:"PreCompact",...s?{additionalContext:s}:{}}}}noteTelemetryOperation(){this.successfulTelemetryOperations++,this.successfulTelemetryOperations>=Xn&&this.flushTelemetry();}async flushTelemetry(){this.successfulTelemetryOperations=0,await this.telemetry.flushAndAggregate();}startExplicitIntentFlush(){this.explicitIntentFlush||(this.explicitIntentFlush=this.flushExplicitMemoryIntents().finally(()=>{this.explicitIntentFlush=null;}));}async handleStop(e){let t=null;try{let n=JSON.parse(e);typeof n.session_id=="string"&&n.session_id.trim()&&(t=n.session_id.trim());}catch{}if(t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null,t){let n=this.runtimeStore.completeExecution(t);n?.executionId&&(await this.telemetry.sendExecutionComplete(n.executionId),this.deleteContextReceipt(t,n.executionId));}this.startExplicitIntentFlush(),await this.explicitIntentFlush;}async flushExplicitMemoryIntents(){this.flushTelemetry();let e=We(this.projectRoot);if(e)try{let t=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);for(await t.forceWarmUp();e.intents.length>0;){let n=e.intents[0];await t.saveExplicitMemoryIntent(n.content,n.sessionId),He(e);}}catch(t){console.error("[devflow-daemon] Explicit memory intent save failed:",t.message);}}deleteContextReceipt(e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),n.deleteContextReceipt(this.projectRoot,e,t);}catch{}finally{n?.close();}}async handleMemorySnapshot(){let e=loadConfig(this.projectRoot).sessionStart?.injectMemories??true;if(e===false)return {status:"ok",enabled:false,markdown:""};let t=typeof e=="object"?e.topN:void 0,n=typeof e=="object"?e.budgetTokens:void 0,o=Number.isFinite(t)?Math.max(0,Math.floor(t)):10,s=Number.isFinite(n)?Math.max(1,Math.floor(n)):800;try{let i=this.explicitMemoryGate??=new MemoryGate(this.projectRoot),c={...await i.getAll({purpose:"session_bootstrap",budgetTokens:s,limit:o}),_devflow_unique:{memory_version:1,structured_storage:!0,project_context_included:!0},_accuracy:{data_freshness_ms:0,source_layer:"memory",degradation:typeof i.getObservationVectorCoverage=="function"?i.getObservationVectorCoverage().missing>0?"bm25_only":"none":"partial"}};if(c.memories.length===0)return {status:"ok",enabled:!0,markdown:`## Project memory
|
|
9
|
+
`);let t=fstatSync(e);this.pidIdentity={dev:t.dev,ino:t.ino};}finally{closeSync(e);}}removePathIfOwned(e,t,n,o){try{return !this.pathMatchesIdentity(e,t)||o!==void 0&&readFileSync(e,"utf8")!==o||!this.pathMatchesIdentity(e,t)?!1:(unlinkSync(e),!0)}catch(s){return s.code!=="ENOENT"&&console.error(`[devflow-daemon] Failed to remove ${n}:`,s.message),false}}isSocketAcceptingConnections(){return new Promise((e,t)=>{let n=createConnection(this.socketPath),o=setTimeout(()=>{n.destroy(),t(new Error(`Timed out probing daemon socket ${this.socketPath}`));},Bn),s=i=>{clearTimeout(o),n.destroy(),e(i);};n.once("connect",()=>s(true)),n.once("error",i=>{let a=i.code;a==="ENOENT"||a==="ECONNREFUSED"?s(false):(clearTimeout(o),t(i));});})}listen(){return new Promise((e,t)=>{let n=this.server;if(!n){t(new Error("Daemon server is not initialized"));return}let o=i=>{n.off("listening",s),t(i);},s=()=>{n.off("error",o);try{let i=this.getPathIdentity(this.socketPath);if(!i)throw new Error(`Daemon socket missing after listen: ${this.socketPath}`);this.socketIdentity=i,e();}catch(i){t(i);}};n.once("error",o),n.once("listening",s),n.listen(this.socketPath);})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=null,!(this.activeSessionIds.size>0)&&(this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},Wn).unref());}async handleRequest(e){switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return this.handlePostToolUse(e.input);case "post-tool-use-failure":return this.handlePostToolUseFailure(e.input);case "stop":return await this.handleStop(e.input??""),{status:"ok"};case "memory-snapshot":return this.handleMemorySnapshot();case "session-start":return this.handleSessionStart(e.input??"");case "session-end":return await this.handleSessionEnd(e.input??""),{status:"ok"};case "user-prompt-submit":return this.handleUserPromptSubmit(e.input);case "pre-compact":return this.handlePreCompact(e.input);default:return {error:"unknown request type"}}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);t.tool_input=eo(t.tool_name,t.tool_input,process.env,this.projectRoot);let n=this.trackSession(t.session_id);if(t.tool_name==="Skill"){let g=t.tool_input?.skill,y=g?U(g):null;y&&this.startSkillExecution(n,y);}let o=this.runtimeStore.get(n);t.tool_name.startsWith("mcp__devflow__")&&(t.tool_input._devflow_session_id=n,t.tool_input._devflow_execution_id=o?.executionId??n,t.tool_use_id&&(t.tool_input._devflow_tool_use_id=t.tool_use_id));let s=t.tool_name.startsWith("mcp__"),i=s?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,a=new k(this.projectRoot,n,o?.executionId),c=a.getPhase(),u=a.evaluate(t.tool_name,s),l;u.permissionDecision==="deny"?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u.reason??"MCP context required"}}:u.additionalContext?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:u.additionalContext}}:l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}},t.tool_name.startsWith("mcp__devflow__")&&(l.hookSpecificOutput.updatedInput=t.tool_input);let m={...t.tool_input};delete m._devflow_session_id,delete m._devflow_execution_id,delete m._devflow_tool_use_id;let p=o?.executionId??n,d={eventId:D(n,t.tool_use_id),executionId:p,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:t.tool_name,toolType:s?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:s,mcpToolName:i,mcpEnforced:s&&c==="context_gathering",mcpFallback:!s&&c==="context_gathering",kind:Zn(t.tool_name),input:m,duration:0,blocked:l&&l.hookSpecificOutput?.permissionDecision==="deny"},h=await this.telemetry.sendEvent({...d,blockReason:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0});return h&&!d.blocked&&(this.eventMap.push({toolName:t.tool_name,toolUseId:t.tool_use_id,eventId:h,sessionId:n,timestamp:d.timestamp}),this.noteTelemetryOperation()),h&&d.blocked&&await this.telemetry.completeEvent({eventId:h,sessionId:n,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-d.timestamp,completedAt:Date.now()}),l}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}trackSession(e){let t=e?.trim()||(this.activeSessionIds.size===1?[...this.activeSessionIds][0]:"");if(!t)throw new Error("session_id_required");return this.registerSession(t),t}registerSession(e){this.activeSessionIds.has(e)||(this.activeSessionIds.size===0&&A(this.projectRoot,{bypassCount:0}),this.activeSessionIds.add(e),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.serverManager.addRef(e),this.telemetry.sendSessionStart(e,this.projectRoot,Date.now()).catch(()=>{})),this.runtimeStore.registerSession(e);}startSkillExecution(e,t){let n=this.runtimeStore.get(e)?.executionId,o=this.runtimeStore.startExecution(e,t,he(t));o.executionId&&o.executionId!==n&&this.telemetry.sendExecutionStart(o.executionId,e,t,o.lastActivityAt,this.projectRoot,o.requiredMcpTools).catch(()=>{});}async handleSessionStart(e){try{let t=JSON.parse(e);if(typeof t.session_id!="string"||!t.session_id.trim())return {error:"session_id_required"};let n=t.session_id.trim();this.registerSession(n);let o=await this.getMemoryGate();await o.ensureSession(n,this.projectRoot,"Claude Code session"),await o.releasePendingDistillLeases(n);let s=o.getPendingEventCount();return {status:"ok",...s>0?{additionalContext:Q({pendingEvents:s,releasedLeases:0,trigger:"session_end"})}:{}}}catch{return {error:"session_start_invalid"}}}popEventFromMap(e,t,n){let o=this.eventMap.filter(i=>i.toolName===e&&i.sessionId===t);if(o.length===0)return null;let s=n?o.find(i=>i.toolUseId===n):o.sort((i,a)=>i.timestamp-a.timestamp)[0];return s?(this.eventMap=this.eventMap.filter(i=>i.eventId!==s.eventId),s):null}enforcePostToolUse(e){if(!Qn[e])return null;let t=Math.floor(Date.now()/1e3),n=X(this.projectRoot,i=>t-(i.lastMcpCall??0)<30?i:{...i,bypassCount:(i.bypassCount??0)+1});if(!n.applied||!n.changed)return null;let o=n.receipt.bypassCount??0;if(o===1)return null;let s;switch(e){case "Agent":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return o>=3?s=`${s} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:o>=2?s=`${s} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:s=`${s} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,s}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let n=t.tool_name||"",o=t.tool_input||{},s=t.tool_response,i=this.trackSession(t.session_id);if(n==="Skill"){let v=o?.skill,_=v?U(v):null;_&&(this.startSkillExecution(i,_),A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}n.startsWith("mcp__")&&new k(this.projectRoot).recordMcpCall();let a=this.popEventFromMap(n,i,t.tool_use_id);if(a){let v=typeof s=="string"?s:JSON.stringify(s),_=v.length>5e3?{_truncated:true,_originalSize:v.length,text:v.slice(0,5e3)}:s,S=Date.now()-a.timestamp;await this.telemetry.completeEvent({eventId:a.eventId,sessionId:i,output:_,duration:S,completedAt:Date.now()})&&this.noteTelemetryOperation();}let c=typeof s=="object"&&s!==null?s:null,u=c?.exitCode,l=c?.stderr,m=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,p=o.command||"",f=JSON.stringify(s),d=Buffer.byteLength(f,"utf8"),h=this.countResults(s),g,y,w=true;if(n.startsWith("mcp__"))g="mcp_call",y={mcpTool:n.replace(/^mcp__[^_]+__/,""),query:o.query??null,resultCount:h,resultSizeBytes:d};else if(n==="Read"||n==="Write"||n==="Edit")g=n==="Read"?"file_read":"file_write",y={filePath:o.file_path??null,fileContentSize:d};else if(n==="Bash"){let v=m.test(p),_=u!==void 0&&u!==0;w=!v||_,g=_?"bash_error":"bash_command",y={command:p||n,exitCode:u??null,stderr:l??null};}else n==="Agent"?(g="subagent",y={subagentType:o.subagent_type??null,description:typeof o.description=="string"?o.description.slice(0,200):null}):n==="WebSearch"||n==="WebFetch"?(g=n==="WebSearch"?"web_search":"web_fetch",y={query:typeof o.query=="string"?o.query.slice(0,200):null}):(w=false,g="tool_use",y={});if(w){let v={id:t.tool_use_id?`memory:${D(i,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i,tool:n,kind:g,payload:y,command:p||void 0,exitCode:u,stderr:l??void 0,durationMs:0,createdAt:Date.now()};this.postMemoryEvent(v);}let E=this.enforcePostToolUse(n);return E?(console.error("[devflow-daemon] Enforcement:",E),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(E)}}):null}async handlePostToolUseFailure(e){let t;try{t=JSON.parse(e);}catch{return null}let n=this.trackSession(t.session_id),o=t.tool_name??"unknown",s=t.error??`${o} failed`,i=this.popEventFromMap(o,n,t.tool_use_id);if(i)await this.telemetry.completeEvent({eventId:i.eventId,sessionId:n,error:s,duration:Date.now()-i.timestamp,completedAt:Date.now()})&&this.noteTelemetryOperation();else {let a=D(n,t.tool_use_id);await this.telemetry.sendEvent({eventId:a,executionId:this.runtimeStore.get(n)?.executionId??n,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:o,toolType:o.startsWith("mcp__")?"mcp":"direct",isMcpTool:o.startsWith("mcp__"),mcpToolName:o.startsWith("mcp__")?o.replace(/^mcp__[^_]+__/,""):void 0,mcpEnforced:false,mcpFallback:false,kind:"error",input:t.tool_input??{},duration:0,error:s,blocked:false});}return this.postMemoryEvent({id:t.tool_use_id?`memory:${D(n,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:n,tool:o,command:typeof t.tool_input?.command=="string"?t.tool_input.command:o,exitCode:-1,stderr:s,durationMs:i?Date.now()-i.timestamp:0,createdAt:Date.now(),kind:o==="Bash"?"bash_error":"tool_use",payload:{command:t.tool_input?.command??o,exitCode:-1,stderr:s}}),null}countResults(e){if(!e)return 0;let t=e;if(typeof e=="string")try{t=JSON.parse(e);}catch{return 1}let n=t,o=["files","results","data","result","memories","nodes","chunks","findings","symbols"],s=Array.isArray(n.data)?n.data:n.structuredContent??n;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of o)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=this.countResults(u));return a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}async postMemoryEvent(e){we(`/api/memory/session-events?rootPath=${encodeURIComponent(this.projectRoot)}`,e);try{await(await this.getMemoryGate()).recordEvent(e);}catch(t){console.error("[devflow-daemon] Local memory event write failed:",t.message);}}async getMemoryGate(){let e=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);return this.memoryWarmup??=e.forceWarmUp().catch(t=>{throw this.memoryWarmup=null,t}),await this.memoryWarmup,e}async handleUserPromptSubmit(e){try{let t=JSON.parse(e),n=typeof t.prompt=="string"?t.prompt:"",o=typeof t.session_id=="string"&&t.session_id.trim()?this.trackSession(t.session_id):null,s=Be(n);if(o&&s&&this.startSkillExecution(o,s.skillName),n.trim()){let i=qe(n);i&&Je(this.projectRoot,{content:i,sessionId:o??void 0,createdAt:Date.now()}),o&&await(await this.getMemoryGate()).recordUserMessage(n.slice(0,2e3),o,n.slice(0,200));}}catch{}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}}}async handlePreCompact(e){let t;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}await this.flushTelemetry(),this.startExplicitIntentFlush(),await this.explicitIntentFlush;let n=await this.getMemoryGate(),o=await L({projectRoot:this.projectRoot,sessionId:t,trigger:"pre_compact",memory:n}),s=Q(o);return {hookSpecificOutput:{hookEventName:"PreCompact",...s?{additionalContext:s}:{}}}}noteTelemetryOperation(){this.successfulTelemetryOperations++,this.successfulTelemetryOperations>=Xn&&this.flushTelemetry();}async flushTelemetry(){this.successfulTelemetryOperations=0,await this.telemetry.flushAndAggregate();}startExplicitIntentFlush(){this.explicitIntentFlush||(this.explicitIntentFlush=this.flushExplicitMemoryIntents().finally(()=>{this.explicitIntentFlush=null;}));}async handleStop(e){let t=null;try{let n=JSON.parse(e);typeof n.session_id=="string"&&n.session_id.trim()&&(t=n.session_id.trim());}catch{}if(t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null,t){let n=this.runtimeStore.completeExecution(t);n?.executionId&&(await this.telemetry.sendExecutionComplete(n.executionId),this.deleteContextReceipt(t,n.executionId));}this.startExplicitIntentFlush(),await this.explicitIntentFlush;}async flushExplicitMemoryIntents(){this.flushTelemetry();let e=We(this.projectRoot);if(e)try{let t=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);for(await t.forceWarmUp();e.intents.length>0;){let n=e.intents[0];await t.saveExplicitMemoryIntent(n.content,n.sessionId),He(e);}}catch(t){console.error("[devflow-daemon] Explicit memory intent save failed:",t.message);}}deleteContextReceipt(e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),n.deleteContextReceipt(this.projectRoot,e,t);}catch{}finally{n?.close();}}async handleMemorySnapshot(){let e=loadConfig(this.projectRoot).sessionStart?.injectMemories??true;if(e===false)return {status:"ok",enabled:false,markdown:""};let t=typeof e=="object"?e.topN:void 0,n=typeof e=="object"?e.budgetTokens:void 0,o=Number.isFinite(t)?Math.max(0,Math.floor(t)):10,s=Number.isFinite(n)?Math.max(1,Math.floor(n)):800;try{let i=this.explicitMemoryGate??=new MemoryGate(this.projectRoot),c={...await i.getAll({purpose:"session_bootstrap",budgetTokens:s,limit:o}),_devflow_unique:{memory_version:1,structured_storage:!0,project_context_included:!0},_accuracy:{data_freshness_ms:0,source_layer:"memory",degradation:typeof i.getObservationVectorCoverage=="function"?i.getObservationVectorCoverage().missing>0?"bm25_only":"none":"partial"}};if(c.memories.length===0)return {status:"ok",enabled:!0,markdown:`## Project memory
|
|
10
10
|
\u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`};let u=c.memories.map(l=>`- **${l.title||l.type}**: ${l.content} (confidence ${l.confidence.toFixed(2)}, ${l.source})`);return {status:"ok",enabled:!0,markdown:`## Project memory (auto-injected, ${c.memories.length} items, ${c.tokenCount} tokens)
|
|
11
11
|
${u.join(`
|
|
12
12
|
`)}`}}catch(i){return console.error("[devflow-daemon] Memory snapshot failed:",i.message),{status:"degraded",enabled:true,markdown:`## Project memory
|
|
13
|
-
\u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`}}}async handleSessionEnd(e){let t=null;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null;let n=t===null?this.activeSessionIds.size<=1:this.activeSessionIds.size<=1&&this.activeSessionIds.has(t);this.startExplicitIntentFlush(),await this.explicitIntentFlush;let o=await this.getMemoryGate(),s=t?this.runtimeStore.get(t)?.executionId:void 0;s&&await this.telemetry.sendExecutionComplete(s),t&&this.deleteContextReceipt(t,s),await Z(e,this.projectRoot,n,{telemetry:this.telemetry,memory:o}),t&&(this.serverManager.removeRef(t),this.activeSessionIds.delete(t),this.eventMap=this.eventMap.filter(i=>i.sessionId!==t),this.runtimeStore.removeSession(t)),this.activeSessionIds.size===0?setImmediate(()=>{this.shutdown();}):this.resetIdleTimer();}shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.telemetryFlushTimer&&(clearInterval(this.telemetryFlushTimer),this.telemetryFlushTimer=null),await this.flushTelemetry(),await this.closeServer(),this.cleanupOwnedResources(),await this.serverManager.stop(),await this.explicitIntentFlush,this.explicitMemoryGate?.close(),this.explicitMemoryGate=null,this.telemetry.close(),process.exit(0);}closeServer(){let e=this.server;return !e||!e.listening||!this.pathMatchesIdentity(this.socketPath,this.socketIdentity)?Promise.resolve():(this.server=null,new Promise(t=>{let n=false,o=()=>{n||(n=true,clearTimeout(s),t());},s=setTimeout(o,
|
|
14
|
-
`);}};if(process.env.DEVFLOW_HOOK_DAEMON_DISABLE_AUTOSTART!=="1"){let r=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),e=new re(r,process.argv[3]);process.once("SIGTERM",()=>{e.shutdown();}),process.once("SIGINT",()=>{e.shutdown();}),e.start().catch(t=>{console.error("[devflow-daemon] Failed to start:",t),process.exit(1);});}export{re as HookDaemon,
|
|
13
|
+
\u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`}}}async handleSessionEnd(e){let t=null;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null;let n=t===null?this.activeSessionIds.size<=1:this.activeSessionIds.size<=1&&this.activeSessionIds.has(t);this.startExplicitIntentFlush(),await this.explicitIntentFlush;let o=await this.getMemoryGate(),s=t?this.runtimeStore.get(t)?.executionId:void 0;s&&await this.telemetry.sendExecutionComplete(s),t&&this.deleteContextReceipt(t,s),await Z(e,this.projectRoot,n,{telemetry:this.telemetry,memory:o}),t&&(this.serverManager.removeRef(t),this.activeSessionIds.delete(t),this.eventMap=this.eventMap.filter(i=>i.sessionId!==t),this.runtimeStore.removeSession(t)),this.activeSessionIds.size===0?setImmediate(()=>{this.shutdown();}):this.resetIdleTimer();}shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.telemetryFlushTimer&&(clearInterval(this.telemetryFlushTimer),this.telemetryFlushTimer=null),await this.flushTelemetry(),await this.closeServer(),this.cleanupOwnedResources(),await this.serverManager.stop(),await this.explicitIntentFlush,this.explicitMemoryGate?.close(),this.explicitMemoryGate=null,this.telemetry.close(),process.exit(0);}closeServer(){let e=this.server;return !e||!e.listening||!this.pathMatchesIdentity(this.socketPath,this.socketIdentity)?Promise.resolve():(this.server=null,new Promise(t=>{let n=false,o=()=>{n||(n=true,clearTimeout(s),t());},s=setTimeout(o,Vn);e.close(o);}))}cleanupOwnedResources(){try{Ne(this.projectRoot,process.pid);}catch(e){console.error("[devflow-daemon] Failed to unregister:",e.message);}this.removePathIfOwned(this.socketPath,this.socketIdentity,"socket"),this.removePathIfOwned(this.pidPath,this.pidIdentity,"PID file",`${process.pid}
|
|
14
|
+
`);}};if(process.env.DEVFLOW_HOOK_DAEMON_DISABLE_AUTOSTART!=="1"){let r=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),e=new re(r,process.argv[3]);process.once("SIGTERM",()=>{e.shutdown();}),process.once("SIGINT",()=>{e.shutdown();}),e.start().catch(t=>{console.error("[devflow-daemon] Failed to start:",t),process.exit(1);});}export{re as HookDaemon,eo as injectProjectRootIfMissing};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync,appendFileSync}from'fs';import {join,dirname}from'path';import {getLogWriter}from'@devflow-tools/telemetry';import {request}from'http';import {homedir}from'os';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {randomUUID,createHash}from'crypto';import {fileURLToPath}from'url';function H(o,t,e){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:o,timestamp:Date.now(),duration:e,success:t.success!==!1,metadata:t});}finally{n.close();}}catch{}}var
|
|
2
|
-
`;appendFileSync(join(O,"http-errors.log"),e);}catch{}}function K(o,t,e={}){let n=e.timeout??5e3,r=e.maxRetries??2,s=e.circuitBreaker??true,i=Se(o);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&M(o,i,"half_open");let a=c=>new Promise(l=>{let u=JSON.stringify(t),d=new URL(o,F),f=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":we,"Content-Length":Buffer.byteLength(u)},timeout:n},g=>{if(g.resume(),g.statusCode&&g.statusCode>=400){let y=new Error(`HTTP ${g.statusCode}`);if(J(o,y),c>0){let b=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},b);}else A(o,i),l();return}Re(o,i),l();});f.on("error",g=>{if(J(o,g),c>0){let y=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},y);}else A(o,i),l();}),f.on("timeout",()=>{f.destroy(),c>0?a(c-1).then(l):(A(o,i),l());}),f.write(u),f.end();});return a(r)}function j(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function h(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(j(o))}function R(o){if(!o||typeof o!="object"||Array.isArray(o))return {};let t=o,e={};for(let n of ["lastMcpCall","bypassCount"])if(n in t){let r=t[n];if(r==null)continue;e[n]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ee(o,t){return o.lastMcpCall===t.lastMcpCall&&o.bypassCount===t.bypassCount}function ke(o){try{return existsSync(o)?R(JSON.parse(readFileSync(o,"utf-8"))):{}}catch{return {}}}function Ie(o,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${o} skipped: ${e}`);}function z(o){let t=h(o);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function Te(o,t){if(o.getHookReceipt(t)){z(t);return}let e=join(h(t),"receipt.json");if(!existsSync(e))return;let n=ke(e);o.updateHookReceipt(t,()=>n),z(t);}function Y(o,t,e){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Te(n,o);let r={},s=n.updateHookReceipt(o,a=>(r=R(a),R(t(r)))),i=R(s);return {receipt:i,applied:!0,changed:!Ee(r,i)}}catch(r){return Ie(e,r),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function U(o,t){return Y(o,()=>t,"write").applied}function L(o,t){return Y(o,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ae(o){switch(o){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var D=class{constructor(t,e,n){this.projectRoot=t;this.sessionId=e;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let n=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(n==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let s=L(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ae(t);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){U(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var te=1e4,Ue=3e4,C=500;function Le(o,t,e){return new Promise(n=>{try{let r=new URL(o),s=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(t),s.end();}catch{n(null);}})}var x=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,n,r,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:n,startedAt:r,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",n=Date.now(),r){let s={executionId:t,status:e,finishedAt:n,metadata:r},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",r);return s&&this.postHttp("/api/telemetry/sessions",r),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let n={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",n);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let n=this.getDatabase(),r=n.listTelemetryFailures({unresolvedOnly:!0,limit:C}).reverse();for(let s of r)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(C);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(r=>r.endsWith(".json")).map(r=>{let s=join(n,r);try{return {cacheFile:s,envelope:$e(JSON.parse(readFileSync(s,"utf8")),r)}}catch{return null}})).filter(n=>n!==null).sort((n,r)=>n.envelope.timestamp-r.envelope.timestamp||ne(n.envelope.operation)-ne(r.envelope.operation));for(let{cacheFile:n,envelope:r}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),s.resolveTelemetryFailure(r.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(n){return this.cacheOperation(t,e,n),false}}applyOperation(t,e){let n=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(n,e),n.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(n,e),n.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":n.ensureSession(e),n.insertRun({id:re(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":n.closeSession(e.sessionId,e.finishedAt),n.updateRun(re(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let n=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:n,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,n){let r=Date.now(),s={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:n instanceof Error?n.message:String(n),timestamp:r};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:r}),i.trimTelemetryFailures(C),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-C)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Ue).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){Le(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=te?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,te)}...`}}};function ne(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function re(o){return `hook-run:${o}`}function $e(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let n=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},s=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:n,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function Be(o){return Buffer.from(o,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(h(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let n=this.get(e);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let r=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,n){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let n=this.sessions.get(e);if(n)return n;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let s=JSON.parse(readFileSync(r,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let n={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),n}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Be(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),n=`${e}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(t),{mode:384}),renameSync(n,e);}};var Qe=1;function et(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function ie(o=et()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let n=JSON.parse(readFileSync(e,"utf8"));if(n.version!==Qe||!tt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function tt(o){return !o||typeof o!="object"||Array.isArray(o)?false:Object.values(o).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return se(e.mcpTools)&&se(e.blockedNative)})}function se(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function ae(o){let t=ie();return t?t[o]?.mcpTools??[]:[]}function ce(o){let t=o.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function le(o,t){return t?`evt_tool_${createHash("sha256").update(`${o}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var lt=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function ut(o,t,e,n){try{let r=join(h(o),"event-map.json");if(!existsSync(r))return null;let s=JSON.parse(readFileSync(r,"utf-8")),i=s.filter(l=>l.toolName===e&&(!l.sessionId||l.sessionId===t));if(i.length===0)return null;let a=n?i.find(l=>l.toolUseId===n):i.sort((l,u)=>l.timestamp-u.timestamp)[0];if(!a)return null;let c=s.findIndex(l=>l.eventId===a.eventId);return c!==-1&&s.splice(c,1),writeFileSync(r,JSON.stringify(s)),{eventId:a.eventId,timestamp:a.timestamp}}catch{return null}}function ue(o){if(!o)return 0;let t=o;if(typeof o=="string")try{t=JSON.parse(o);}catch{return 1}let e=t,n=["files","results","data","result","memories","nodes","chunks","findings","symbols","keySymbols"],r=["reasoning","riskHints","nextActions"],s=Array.isArray(e.data)?e.data:e.structuredContent??e;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of n)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let u of r)Array.isArray(i[u])&&(a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=ue(u));let l=!!(i._devflow||i._devflow_unique||i.taskType);return a===0&&l&&!c?1:a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}function pt(o,t){if(!lt.has(t))return null;let e=L(o,r=>({...r,bypassCount:(r.bypassCount??0)+1}));if(!e.applied)return null;let n=e.receipt.bypassCount??0;return n>20&&n%10===0?`MCP \u5DE5\u5177\u4F7F\u7528\u7387\u4F4E\uFF1A${n} \u6B21\u76F4\u63A5\u5DE5\u5177\u8C03\u7528\u3002\u5EFA\u8BAE\u4F7F\u7528 get_project_context \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`:null}async function dt(o){if(!o)return null;let t;try{t=JSON.parse(o);}catch{return null}let e=t.tool_name||"",n=j(),r=t.tool_input||{},s=t.tool_response||{},i=t.session_id?.trim()||null,a=new E(n);if(i&&a.registerSession(i),e==="Skill"){let p=typeof r=="object"&&r.skill?r.skill:"",m=ce(p);i&&m&&(a.startExecution(i,m,ae(m)),U(n,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}e.startsWith("mcp__")&&new D(n).recordMcpCall();let c=i?a.get(i):null,l=i?ut(n,i,e,t.tool_use_id):null,u=i&&t.tool_use_id&&!l?(()=>{try{let p=openGlobalDevFlowDatabase();try{return p.getToolCallEventByToolUseId(i,t.tool_use_id)}finally{p.close();}}catch{return null}})():null,d=l??u,f=typeof s=="object"&&s!==null?s:null,g=JSON.stringify(s),y=Buffer.byteLength(g,"utf8"),b=ue(s);if(c?.executionId!==void 0||e.startsWith("mcp__devflow__")||e==="Skill"){let p=getLogWriter(n),m=c?.executionId??`auto-${i??Date.now()}`;p.toolCall({tool:e,runId:m,stepId:`step-${m}`,input:r,output:s,duration:d?Math.max(0,Date.now()-d.timestamp):0,success:!f?.error,resultCount:b,resultSizeBytes:y,error:f?.error});}if(d&&i){let p=typeof s=="string"?s:JSON.stringify(s),m=p.length>5e3?{_truncated:true,_originalSize:p.length,text:p.slice(0,5e3)}:s;await new x().completeEvent({eventId:d.eventId,sessionId:i,output:m,duration:d?Math.max(0,Date.now()-d.timestamp):0,completedAt:Date.now()});}let k=typeof r=="object"&&r.command?r.command:"",S=f?.exitCode,q=f?.stderr,pe=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,v,w,I=true;if(e.startsWith("mcp__"))v="mcp_call",w={mcpTool:e.replace(/^mcp__[^_]+__/,""),query:typeof r=="object"&&r.query?r.query:null,resultCount:b,resultSizeBytes:y};else if(e==="Read"||e==="Write"||e==="Edit")v=e==="Read"?"file_read":"file_write",w={filePath:typeof r=="object"&&r.file_path?r.file_path:null,fileContentSize:y};else if(e==="Bash"){let p=pe.test(k),m=S!==void 0&&S!==0;I=!p||m,v=m?"bash_error":"bash_command",w={command:k||e,exitCode:S??null,stderr:q??null};}else e==="Agent"?(v="subagent",w={subagentType:typeof r=="object"&&r.subagent_type?r.subagent_type:null,description:typeof r=="object"&&r.description?String(r.description).slice(0,200):null}):e==="WebSearch"||e==="WebFetch"?(v=e==="WebSearch"?"web_search":"web_fetch",w={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,v="tool_use",w={});if(I){let p={id:t.tool_use_id?`memory:${le(i??"",t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i??"",tool:e,kind:v,payload:w,command:k||void 0,exitCode:S,stderr:q??void 0,durationMs:d?Math.max(0,Date.now()-d.timestamp):0,createdAt:Date.now()};i&&K(`/api/memory/session-events?rootPath=${encodeURIComponent(n)}`,p);}let T=pt(n,e);return T&&console.error("[devflow] Enforcement:",T),T}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await dt(o.trim()||process.argv[2]||"");if(t){let e=JSON.stringify(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:e}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
|
|
3
|
-
export{
|
|
1
|
+
import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync,appendFileSync}from'fs';import {join,dirname}from'path';import {getLogWriter}from'@devflow-tools/telemetry';import {request}from'http';import {homedir}from'os';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {randomUUID,createHash}from'crypto';import {fileURLToPath}from'url';function H(o,t,e){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:o,timestamp:Date.now(),duration:e,success:t.success!==!1,metadata:t});}finally{n.close();}}catch{}}var M=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",_e=getLocalApiKey(),we=8,be=6e4,W=new Map;function Se(o){let t=new URL(o,M).pathname,e=W.get(t);return e||(e={failures:0,lastFailure:0,openUntil:0,status:"closed"},W.set(t,e)),e}function F(o,t,e){if(t.status===e)return;let n=t.status;t.status=e,H("http_circuit_transition",{path:new URL(o,M).pathname,previous:n,status:e,failures:t.failures,openUntil:t.openUntil});}function A(o,t){t.failures++,t.lastFailure=Date.now(),t.failures>=we&&(t.openUntil=Date.now()+be,F(o,t,"open"));}function Re(o,t){let e=t.status!=="closed";t.failures=0,t.openUntil=0,e&&F(o,t,"closed");}var O=join(homedir(),".devflow","errors");function J(o,t){try{existsSync(O)||mkdirSync(O,{recursive:!0});let e=`${new Date().toISOString()} | ${o} | ${t?.message??String(t)}
|
|
2
|
+
`;appendFileSync(join(O,"http-errors.log"),e);}catch{}}function B(o,t,e={}){let n=e.timeout??5e3,r=e.maxRetries??2,s=e.circuitBreaker??true,i=Se(o);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&F(o,i,"half_open");let a=c=>new Promise(l=>{let u=JSON.stringify(t),d=new URL(o,M),f=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":_e,"Content-Length":Buffer.byteLength(u)},timeout:n},g=>{if(g.resume(),g.statusCode&&g.statusCode>=400){let y=new Error(`HTTP ${g.statusCode}`);if(J(o,y),c>0){let b=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},b);}else A(o,i),l();return}Re(o,i),l();});f.on("error",g=>{if(J(o,g),c>0){let y=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},y);}else A(o,i),l();}),f.on("timeout",()=>{f.destroy(),c>0?a(c-1).then(l):(A(o,i),l());}),f.write(u),f.end();});return a(r)}function j(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function h(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(j(o))}function R(o){if(!o||typeof o!="object"||Array.isArray(o))return {};let t=o,e={};for(let n of ["lastMcpCall","bypassCount"])if(n in t){let r=t[n];if(r==null)continue;e[n]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ee(o,t){return o.lastMcpCall===t.lastMcpCall&&o.bypassCount===t.bypassCount}function ke(o){try{return existsSync(o)?R(JSON.parse(readFileSync(o,"utf-8"))):{}}catch{return {}}}function Ie(o,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${o} skipped: ${e}`);}function K(o){let t=h(o);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function Te(o,t){if(o.getHookReceipt(t)){K(t);return}let e=join(h(t),"receipt.json");if(!existsSync(e))return;let n=ke(e);o.updateHookReceipt(t,()=>n),K(t);}function Y(o,t,e){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Te(n,o);let r={},s=n.updateHookReceipt(o,a=>(r=R(a),R(t(r)))),i=R(s);return {receipt:i,applied:!0,changed:!Ee(r,i)}}catch(r){return Ie(e,r),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function U(o,t){return Y(o,()=>t,"write").applied}function L(o,t){return Y(o,t,"update")}var z={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ae(o){switch(o){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var D=class{constructor(t,e,n){this.projectRoot=t;this.sessionId=e;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let n=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&z[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(n==="context_ready")return {permissionDecision:"allow"};let r=z[t];if(!r)return {permissionDecision:"allow"};let s=L(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ae(t);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){U(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var te=1e4,Ue=3e4,C=500;function Le(o,t,e){return new Promise(n=>{try{let r=new URL(o),s=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(t),s.end();}catch{n(null);}})}var x=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,n,r,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:n,startedAt:r,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",n=Date.now(),r){let s={executionId:t,status:e,finishedAt:n,metadata:r},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",r);return s&&this.postHttp("/api/telemetry/sessions",r),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let n={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",n);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let n=this.getDatabase(),r=n.listTelemetryFailures({unresolvedOnly:!0,limit:C}).reverse();for(let s of r)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(C);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(r=>r.endsWith(".json")).map(r=>{let s=join(n,r);try{return {cacheFile:s,envelope:$e(JSON.parse(readFileSync(s,"utf8")),r)}}catch{return null}})).filter(n=>n!==null).sort((n,r)=>n.envelope.timestamp-r.envelope.timestamp||ne(n.envelope.operation)-ne(r.envelope.operation));for(let{cacheFile:n,envelope:r}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),s.resolveTelemetryFailure(r.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(n){return this.cacheOperation(t,e,n),false}}applyOperation(t,e){let n=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(n,e),n.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(n,e),n.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":n.ensureSession(e),n.insertRun({id:re(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":n.closeSession(e.sessionId,e.finishedAt),n.updateRun(re(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let n=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:n,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,n){let r=Date.now(),s={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:n instanceof Error?n.message:String(n),timestamp:r};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:r}),i.trimTelemetryFailures(C),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-C)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Ue).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){Le(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=te?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,te)}...`}}};function ne(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function re(o){return `hook-run:${o}`}function $e(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let n=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},s=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:n,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function ze(o){return Buffer.from(o,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(h(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let n=this.get(e);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let r=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,n){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let n=this.sessions.get(e);if(n)return n;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let s=JSON.parse(readFileSync(r,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let n={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),n}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${ze(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),n=`${e}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(t),{mode:384}),renameSync(n,e);}};var Qe=2,et=1;function tt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function ie(o=tt()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let n=JSON.parse(readFileSync(e,"utf8"));if(n.version!==Qe&&n.version!==et||!nt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function nt(o){return !o||typeof o!="object"||Array.isArray(o)?false:Object.values(o).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return se(e.mcpTools)&&se(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function se(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function ae(o){let t=ie();return t?t[o]?.mcpTools??[]:[]}function ce(o){let t=o.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function le(o,t){return t?`evt_tool_${createHash("sha256").update(`${o}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var ut=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function pt(o,t,e,n){try{let r=join(h(o),"event-map.json");if(!existsSync(r))return null;let s=JSON.parse(readFileSync(r,"utf-8")),i=s.filter(l=>l.toolName===e&&(!l.sessionId||l.sessionId===t));if(i.length===0)return null;let a=n?i.find(l=>l.toolUseId===n):i.sort((l,u)=>l.timestamp-u.timestamp)[0];if(!a)return null;let c=s.findIndex(l=>l.eventId===a.eventId);return c!==-1&&s.splice(c,1),writeFileSync(r,JSON.stringify(s)),{eventId:a.eventId,timestamp:a.timestamp}}catch{return null}}function ue(o){if(!o)return 0;let t=o;if(typeof o=="string")try{t=JSON.parse(o);}catch{return 1}let e=t,n=["files","results","data","result","memories","nodes","chunks","findings","symbols","keySymbols"],r=["reasoning","riskHints","nextActions"],s=Array.isArray(e.data)?e.data:e.structuredContent??e;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of n)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let u of r)Array.isArray(i[u])&&(a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=ue(u));let l=!!(i._devflow||i._devflow_unique||i.taskType);return a===0&&l&&!c?1:a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}function dt(o,t){if(!ut.has(t))return null;let e=L(o,r=>({...r,bypassCount:(r.bypassCount??0)+1}));if(!e.applied)return null;let n=e.receipt.bypassCount??0;return n>20&&n%10===0?`MCP \u5DE5\u5177\u4F7F\u7528\u7387\u4F4E\uFF1A${n} \u6B21\u76F4\u63A5\u5DE5\u5177\u8C03\u7528\u3002\u5EFA\u8BAE\u4F7F\u7528 get_project_context \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`:null}async function mt(o){if(!o)return null;let t;try{t=JSON.parse(o);}catch{return null}let e=t.tool_name||"",n=j(),r=t.tool_input||{},s=t.tool_response||{},i=t.session_id?.trim()||null,a=new E(n);if(i&&a.registerSession(i),e==="Skill"){let p=typeof r=="object"&&r.skill?r.skill:"",m=ce(p);i&&m&&(a.startExecution(i,m,ae(m)),U(n,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}e.startsWith("mcp__")&&new D(n).recordMcpCall();let c=i?a.get(i):null,l=i?pt(n,i,e,t.tool_use_id):null,u=i&&t.tool_use_id&&!l?(()=>{try{let p=openGlobalDevFlowDatabase();try{return p.getToolCallEventByToolUseId(i,t.tool_use_id)}finally{p.close();}}catch{return null}})():null,d=l??u,f=typeof s=="object"&&s!==null?s:null,g=JSON.stringify(s),y=Buffer.byteLength(g,"utf8"),b=ue(s);if(c?.executionId!==void 0||e.startsWith("mcp__devflow__")||e==="Skill"){let p=getLogWriter(n),m=c?.executionId??`auto-${i??Date.now()}`;p.toolCall({tool:e,runId:m,stepId:`step-${m}`,input:r,output:s,duration:d?Math.max(0,Date.now()-d.timestamp):0,success:!f?.error,resultCount:b,resultSizeBytes:y,error:f?.error});}if(d&&i){let p=typeof s=="string"?s:JSON.stringify(s),m=p.length>5e3?{_truncated:true,_originalSize:p.length,text:p.slice(0,5e3)}:s;await new x().completeEvent({eventId:d.eventId,sessionId:i,output:m,duration:d?Math.max(0,Date.now()-d.timestamp):0,completedAt:Date.now()});}let k=typeof r=="object"&&r.command?r.command:"",S=f?.exitCode,q=f?.stderr,pe=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,v,_,I=true;if(e.startsWith("mcp__"))v="mcp_call",_={mcpTool:e.replace(/^mcp__[^_]+__/,""),query:typeof r=="object"&&r.query?r.query:null,resultCount:b,resultSizeBytes:y};else if(e==="Read"||e==="Write"||e==="Edit")v=e==="Read"?"file_read":"file_write",_={filePath:typeof r=="object"&&r.file_path?r.file_path:null,fileContentSize:y};else if(e==="Bash"){let p=pe.test(k),m=S!==void 0&&S!==0;I=!p||m,v=m?"bash_error":"bash_command",_={command:k||e,exitCode:S??null,stderr:q??null};}else e==="Agent"?(v="subagent",_={subagentType:typeof r=="object"&&r.subagent_type?r.subagent_type:null,description:typeof r=="object"&&r.description?String(r.description).slice(0,200):null}):e==="WebSearch"||e==="WebFetch"?(v=e==="WebSearch"?"web_search":"web_fetch",_={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,v="tool_use",_={});if(I){let p={id:t.tool_use_id?`memory:${le(i??"",t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i??"",tool:e,kind:v,payload:_,command:k||void 0,exitCode:S,stderr:q??void 0,durationMs:d?Math.max(0,Date.now()-d.timestamp):0,createdAt:Date.now()};i&&B(`/api/memory/session-events?rootPath=${encodeURIComponent(n)}`,p);}let T=dt(n,e);return T&&console.error("[devflow] Enforcement:",T),T}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await mt(o.trim()||process.argv[2]||"");if(t){let e=JSON.stringify(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:e}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
|
|
3
|
+
export{mt as handlePostToolUse};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync,rmSync,renameSync}from'fs';import {join,dirname as dirname$1}from'path';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {mkdirSync as mkdirSync$1,existsSync as existsSync$1,statSync,appendFileSync,chmodSync,rmSync as rmSync$1,renameSync as renameSync$1}from'node:fs';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {randomUUID,createHash}from'crypto';var U=1e4,he=3e4,b=500;function ye(n,t,e){return new Promise(o=>{try{let r=new URL(n),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let c=[];s.on("data",l=>c.push(l)),s.on("end",()=>{let l=Buffer.concat(c).toString();o(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?l:null);});});i.on("error",()=>o(null)),i.on("timeout",()=>{i.destroy(),o(null);}),i.write(t),i.end();}catch{o(null);}})}var R=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,o,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),s=[]){let c={executionId:t,sessionId:e,skillName:o,startedAt:r,projectRoot:i,requiredMcpTools:s};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",o=Date.now(),r){let i={executionId:t,status:e,finishedAt:o,metadata:r},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(t,e,o){let r={id:t,projectRoot:e,startedAt:o,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let o={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",o);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let o=this.getDatabase(),r=o.listTelemetryFailures({unresolvedOnly:!0,limit:b}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(b);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(o=>!!o))].filter(o=>existsSync(o)).flatMap(o=>readdirSync(o).filter(r=>r.endsWith(".json")).map(r=>{let i=join(o,r);try{return {cacheFile:i,envelope:_e(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||L(o.envelope.operation)-L(r.envelope.operation));for(let{cacheFile:o,envelope:r}of e)try{let i=this.getDatabase();i.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),i.resolveTelemetryFailure(r.id),unlinkSync(o);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(o){return this.cacheOperation(t,e,o),false}}applyOperation(t,e){let o=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(o,e),o.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(o,e),o.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":o.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":o.ensureSession(e),o.insertRun({id:H(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!o.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":o.closeSession(e.sessionId,e.finishedAt),o.updateRun(H(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let o=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!o)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:o,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,o){let r=Date.now(),i={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:o instanceof Error?o.message:String(o),timestamp:r};try{let s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:t,payload:e,error:i.failure,createdAt:r}),s.trimTelemetryFailures(b),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let s=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(s,JSON.stringify(i,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-b)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){ye(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=U?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,U)}...`}}};function L(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function H(n){return `hook-run:${n}`}function _e(n,t){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let o=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!o)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:o,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function k(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function p(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(k(n))}var Se=1;function xe(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function W(n=xe()){let t=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let o=JSON.parse(readFileSync(e,"utf8"));if(o.version!==Se||!ke(o.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return o.commands}catch(o){console.error(`[devflow] command registry load failed: ${o.message}`);}return null}function ke(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return q(e.mcpTools)&&q(e.blockedNative)})}function q(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function G(n){let t=W();return t?t[n]?.mcpTools??[]:[]}function y(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let t=n,e={};for(let o of ["lastMcpCall","bypassCount"])if(o in t){let r=t[o];if(r==null)continue;e[o]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Te(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Ce(n){try{return existsSync(n)?y(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function K(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function J(n){let t=p(n);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function z(n,t){if(n.getHookReceipt(t)){J(t);return}let e=join(p(t),"receipt.json");if(!existsSync(e))return;let o=Ce(e);n.updateHookReceipt(t,()=>o),J(t);}function V(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=y(c),y(t(r)))),s=y(i);return {receipt:s,applied:!0,changed:!Te(r,s)}}catch(r){return K(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function X(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(t,n),y(t.getHookReceipt(n))}catch(e){return K("update",e),{}}finally{try{t?.close();}catch{}}}function Ie(n,t){return V(n,()=>t,"write").applied}function Ae(n,t){return V(n,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function je(n){switch(n){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var S=class{constructor(t,e,o){this.projectRoot=t;this.sessionId=e;this.executionId=o;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let o=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(o==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(o==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let i=Ae(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=je(t);return s>=c?{permissionDecision:"allow",additionalContext:`\u5DF2 ${s} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){Ie(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var qe=10*1024*1024,We=5;function Ge(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function Z(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Ge(n)}.jsonl`)}function ee(n,t,e={}){let o=e.maxBytes??qe,r=e.maxFiles??We;mkdirSync$1(dirname(n),{recursive:true,mode:448});let i=Buffer.byteLength(t),s=existsSync$1(n)?statSync(n).size:0;s>0&&s+i>o&&Je(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function Je(n,t){if(t<=1){rmSync$1(n,{force:true});return}rmSync$1(`${n}.${t-1}`,{force:true});for(let e=t-2;e>=1;e-=1){let o=`${n}.${e}`;existsSync$1(o)&&renameSync$1(o,`${n}.${e+1}`);}existsSync$1(n)&&renameSync$1(n,`${n}.1`);}var A="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function te(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function Ze(n){return Buffer.from(n,"utf8").toString("base64url")}var x=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(p(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let o=this.get(e);if(o)return o.lastActivityAt=Date.now(),this.persist(o),o;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,o){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(o)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let o=this.sessions.get(e);if(o)return o;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let o={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),o}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Ze(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),o=`${e}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(t),{mode:384}),renameSync(o,e);}};function oe(n){let t=n.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function re(n,t){return t?`evt_tool_${createHash("sha256").update(`${n}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}function _(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function ie(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function it(n){let t=p(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function st(n,t,e,o,r,i){let s=join(it(n),"event-map.json"),c=[];try{existsSync(s)&&(c=JSON.parse(readFileSync(s,"utf-8")));}catch{}c.push({sessionId:t,toolName:e,toolUseId:o,eventId:r,timestamp:i}),writeFileSync(s,JSON.stringify(c));}function ct(n){switch(n){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}async function at(n){let t=te();try{if(!n||n.trim()==="")return _();let e=JSON.parse(n),o=Date.now(),r=k(),i=new R;e.tool_name.startsWith("mcp__devflow__")&&(typeof e.tool_input.projectRoot!="string"||!e.tool_input.projectRoot.trim())&&(e.tool_input.projectRoot=r);let s=e.session_id?.trim();if(!s)return ie(A);let c=new x(r),l=c.get(s)!==null;if(c.registerSession(s),l||await i.sendSessionStart(s,r,Date.now()),e.tool_name==="Skill"){let w=e.tool_input?.skill,m=w?oe(w):null;if(m){let le=c.get(s)?.executionId,h=c.startExecution(s,m,G(m));h.executionId&&h.executionId!==le&&await i.sendExecutionStart(h.executionId,s,m,h.lastActivityAt,r,h.requiredMcpTools);}}let d=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=d?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),ce=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,j=new S(r,s,d?.executionId),F=j.getPhase(),a,v=j.evaluate(e.tool_name,u);v.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:v.reason??"MCP context required"}}:v.additionalContext?a=ie(v.additionalContext):a=_(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=A);let ae=d?.executionId??s,f={eventId:re(s,e.tool_use_id),executionId:ae,sessionId:s,projectRoot:r,toolUseId:e.tool_use_id,timestamp:Date.now(),toolName:e.tool_name,toolType:u?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:ce,mcpEnforced:u&&F==="context_gathering",mcpFallback:!u&&F==="context_gathering",kind:ct(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny",blockReason:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0},D=await i.sendEvent(f);D&&!f.blocked&&st(r,s,e.tool_name,e.tool_use_id,D,f.timestamp),D&&f.blocked&&await i.completeEvent({eventId:D,sessionId:s,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-f.timestamp,completedAt:Date.now()});try{let w=X(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:d?.skillName??"(none)",requiredTools:d?.requiredMcpTools??[],isMcp:u,enforce:d?.executionId!==void 0,stateless:d?.executionId===void 0&&!u&&e.tool_name!=="Skill",bypassCount:w.bypassCount??0,decision:a.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ee(Z(r),`${JSON.stringify(m)}
|
|
2
|
-
`);}catch{}return a}catch{return _()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let n="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{n+=t;}),process.stdin.on("end",async()=>{let t=await
|
|
1
|
+
import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync,rmSync,renameSync}from'fs';import {join,dirname as dirname$1}from'path';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {mkdirSync as mkdirSync$1,existsSync as existsSync$1,statSync,appendFileSync,chmodSync,rmSync as rmSync$1,renameSync as renameSync$1}from'node:fs';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {randomUUID,createHash}from'crypto';var L=1e4,he=3e4,R=500;function ye(n,t,e){return new Promise(o=>{try{let r=new URL(n),i=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},s=>{let c=[];s.on("data",l=>c.push(l)),s.on("end",()=>{let l=Buffer.concat(c).toString();o(s.statusCode!=null&&s.statusCode>=200&&s.statusCode<300?l:null);});});i.on("error",()=>o(null)),i.on("timeout",()=>{i.destroy(),o(null);}),i.write(t),i.end();}catch{o(null);}})}var b=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,o,r,i=process.env.CLAUDE_PROJECT_DIR??process.cwd(),s=[]){let c={executionId:t,sessionId:e,skillName:o,startedAt:r,projectRoot:i,requiredMcpTools:s};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",o=Date.now(),r){let i={executionId:t,status:e,finishedAt:o,metadata:r},s=this.commitOrCache("execution_complete",i);return s&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",i),s}async sendSessionStart(t,e,o){let r={id:t,projectRoot:e,startedAt:o,label:e.split("/").pop()??"unknown"},i=this.commitOrCache("session_start",r);return i&&this.postHttp("/api/telemetry/sessions",r),i}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let o={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",o);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let o=this.getDatabase(),r=o.listTelemetryFailures({unresolvedOnly:!0,limit:R}).reverse();for(let i of r)try{this.applyOperation(i.operation,i.payload),o.resolveTelemetryFailure(i.id);}catch{}o.trimTelemetryFailures(R);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(o=>!!o))].filter(o=>existsSync(o)).flatMap(o=>readdirSync(o).filter(r=>r.endsWith(".json")).map(r=>{let i=join(o,r);try{return {cacheFile:i,envelope:_e(JSON.parse(readFileSync(i,"utf8")),r)}}catch{return null}})).filter(o=>o!==null).sort((o,r)=>o.envelope.timestamp-r.envelope.timestamp||U(o.envelope.operation)-U(r.envelope.operation));for(let{cacheFile:o,envelope:r}of e)try{let i=this.getDatabase();i.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),i.resolveTelemetryFailure(r.id),unlinkSync(o);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(o){return this.cacheOperation(t,e,o),false}}applyOperation(t,e){let o=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(o,e),o.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(o,e),o.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":o.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":o.ensureSession(e),o.insertRun({id:q(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!o.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":o.closeSession(e.sessionId,e.finishedAt),o.updateRun(q(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let o=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!o)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:o,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,o){let r=Date.now(),i={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:o instanceof Error?o.message:String(o),timestamp:r};try{let s=this.getDatabase();s.insertTelemetryFailure({id:i.id,operation:t,payload:e,error:i.failure,createdAt:r}),s.trimTelemetryFailures(R),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let s=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(s,JSON.stringify(i,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-R)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){ye(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=L?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,L)}...`}}};function U(n){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(n)}function q(n){return `hook-run:${n}`}function _e(n,t){if(!n||typeof n!="object")throw new Error("Invalid telemetry cache envelope");let e=n;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let o=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!o)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},i=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:o,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:i}}function k(n){return n??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function p(n){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(k(n))}var Se=2,xe=1;function ke(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function G(n=ke()){let t=[join(n,"dist","command-registry.json"),join(n,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let o=JSON.parse(readFileSync(e,"utf8"));if(o.version!==Se&&o.version!==xe||!Ee(o.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return o.commands}catch(o){console.error(`[devflow] command registry load failed: ${o.message}`);}return null}function Ee(n){return !n||typeof n!="object"||Array.isArray(n)?false:Object.values(n).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return H(e.mcpTools)&&H(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function H(n){return Array.isArray(n)&&n.every(t=>typeof t=="string"&&t.length>0)}function W(n){let t=G();return t?t[n]?.mcpTools??[]:[]}function y(n){if(!n||typeof n!="object"||Array.isArray(n))return {};let t=n,e={};for(let o of ["lastMcpCall","bypassCount"])if(o in t){let r=t[o];if(r==null)continue;e[o]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ce(n,t){return n.lastMcpCall===t.lastMcpCall&&n.bypassCount===t.bypassCount}function Ie(n){try{return existsSync(n)?y(JSON.parse(readFileSync(n,"utf-8"))):{}}catch{return {}}}function K(n,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${n} skipped: ${e}`);}function J(n){let t=p(n);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function z(n,t){if(n.getHookReceipt(t)){J(t);return}let e=join(p(t),"receipt.json");if(!existsSync(e))return;let o=Ie(e);n.updateHookReceipt(t,()=>o),J(t);}function V(n,t,e){let o;try{o=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(o,n);let r={},i=o.updateHookReceipt(n,c=>(r=y(c),y(t(r)))),s=y(i);return {receipt:s,applied:!0,changed:!Ce(r,s)}}catch(r){return K(e,r),{receipt:{},applied:false,changed:false}}finally{try{o?.close();}catch{}}}function X(n){let t;try{return t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),z(t,n),y(t.getHookReceipt(n))}catch(e){return K("update",e),{}}finally{try{t?.close();}catch{}}}function Ae(n,t){return V(n,()=>t,"write").applied}function je(n,t){return V(n,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Fe(n){switch(n){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var S=class{constructor(t,e,o){this.projectRoot=t;this.sessionId=e;this.executionId=o;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let o=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(o==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(o==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let i=je(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!i.applied)return {permissionDecision:"allow"};let s=i.receipt.bypassCount??0,c=Fe(t);return s>=c?{permissionDecision:"allow",additionalContext:`\u5DF2 ${s} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){Ae(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ge=10*1024*1024,We=5;function Je(n){return createHash$1("sha256").update(n).digest("hex").slice(0,16)}function Z(n){return join$1(homedir$1(),".devflow","logs","hook-debug",`${Je(n)}.jsonl`)}function ee(n,t,e={}){let o=e.maxBytes??Ge,r=e.maxFiles??We;mkdirSync$1(dirname(n),{recursive:true,mode:448});let i=Buffer.byteLength(t),s=existsSync$1(n)?statSync(n).size:0;s>0&&s+i>o&&Be(n,r),appendFileSync(n,t,{mode:384}),chmodSync(n,384);}function Be(n,t){if(t<=1){rmSync$1(n,{force:true});return}rmSync$1(`${n}.${t-1}`,{force:true});for(let e=t-2;e>=1;e-=1){let o=`${n}.${e}`;existsSync$1(o)&&renameSync$1(o,`${n}.${e+1}`);}existsSync$1(n)&&renameSync$1(n,`${n}.1`);}var A="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function te(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function et(n){return Buffer.from(n,"utf8").toString("base64url")}var x=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(p(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let o=this.get(e);if(o)return o.lastActivityAt=Date.now(),this.persist(o),o;let r=Date.now(),i={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,i),this.persist(i),i}startExecution(t,e,o){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(o)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let o=this.sessions.get(e);if(o)return o;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let i=JSON.parse(readFileSync(r,"utf8"));return i.sessionId!==e||i.projectRoot!==this.projectRoot?null:(i.requiredMcpTools=Array.isArray(i.requiredMcpTools)?i.requiredMcpTools:[],this.sessions.set(e,i),i)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let o={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),o}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${et(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),o=`${e}.${process.pid}.tmp`;writeFileSync(o,JSON.stringify(t),{mode:384}),renameSync(o,e);}};function oe(n){let t=n.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function re(n,t){return t?`evt_tool_${createHash("sha256").update(`${n}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}function _(){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}function ie(n){return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:n}}}function st(n){let t=p(n);return existsSync(t)||mkdirSync(t,{recursive:true}),t}function ct(n,t,e,o,r,i){let s=join(st(n),"event-map.json"),c=[];try{existsSync(s)&&(c=JSON.parse(readFileSync(s,"utf-8")));}catch{}c.push({sessionId:t,toolName:e,toolUseId:o,eventId:r,timestamp:i}),writeFileSync(s,JSON.stringify(c));}function at(n){switch(n){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}async function lt(n){let t=te();try{if(!n||n.trim()==="")return _();let e=JSON.parse(n),o=Date.now(),r=k(),i=new b;e.tool_name.startsWith("mcp__devflow__")&&(typeof e.tool_input.projectRoot!="string"||!e.tool_input.projectRoot.trim())&&(e.tool_input.projectRoot=r);let s=e.session_id?.trim();if(!s)return ie(A);let c=new x(r),l=c.get(s)!==null;if(c.registerSession(s),l||await i.sendSessionStart(s,r,Date.now()),e.tool_name==="Skill"){let w=e.tool_input?.skill,m=w?oe(w):null;if(m){let le=c.get(s)?.executionId,h=c.startExecution(s,m,W(m));h.executionId&&h.executionId!==le&&await i.sendExecutionStart(h.executionId,s,m,h.lastActivityAt,r,h.requiredMcpTools);}}let d=c.get(s);e.tool_name.startsWith("mcp__devflow__")&&(e.tool_input._devflow_session_id=s,e.tool_input._devflow_execution_id=d?.executionId??s,e.tool_use_id&&(e.tool_input._devflow_tool_use_id=e.tool_use_id));let u=e.tool_name.startsWith("mcp__"),ce=u?e.tool_name.replace(/^mcp__[^_]+__/,""):void 0,j=new S(r,s,d?.executionId),F=j.getPhase(),a,v=j.evaluate(e.tool_name,u);v.permissionDecision==="deny"?a={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:v.reason??"MCP context required"}}:v.additionalContext?a=ie(v.additionalContext):a=_(),e.tool_name.startsWith("mcp__devflow__")&&(a.hookSpecificOutput.updatedInput=e.tool_input),t&&a.hookSpecificOutput.permissionDecision==="allow"&&(a.hookSpecificOutput.additionalContext=A);let ae=d?.executionId??s,f={eventId:re(s,e.tool_use_id),executionId:ae,sessionId:s,projectRoot:r,toolUseId:e.tool_use_id,timestamp:Date.now(),toolName:e.tool_name,toolType:u?"mcp":e.tool_name==="Agent"?"subagent":"direct",isMcpTool:u,mcpToolName:ce,mcpEnforced:u&&F==="context_gathering",mcpFallback:!u&&F==="context_gathering",kind:at(e.tool_name),input:e.tool_input,duration:0,blocked:a.hookSpecificOutput.permissionDecision==="deny",blockReason:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:a.hookSpecificOutput.permissionDecision==="deny"?"DEVFLOW_CONTEXT_REQUIRED":void 0},D=await i.sendEvent(f);D&&!f.blocked&&ct(r,s,e.tool_name,e.tool_use_id,D,f.timestamp),D&&f.blocked&&await i.completeEvent({eventId:D,sessionId:s,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-f.timestamp,completedAt:Date.now()});try{let w=X(r),m={ts:o,tool:e.tool_name,sessionId:s,skill:d?.skillName??"(none)",requiredTools:d?.requiredMcpTools??[],isMcp:u,enforce:d?.executionId!==void 0,stateless:d?.executionId===void 0&&!u&&e.tool_name!=="Skill",bypassCount:w.bypassCount??0,decision:a.hookSpecificOutput.permissionDecision,env_CLAUDE_PLUGIN_ROOT:process.env.CLAUDE_PLUGIN_ROOT??"(unset)",cwd:process.cwd()};ee(Z(r),`${JSON.stringify(m)}
|
|
2
|
+
`);}catch{}return a}catch{return _()}}if(process.argv[1]?.endsWith("pre-tool-use")||process.argv[1]?.endsWith("pre-tool-use.js")){let n="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{n+=t;}),process.stdin.on("end",async()=>{let t=await lt(n);console.log(JSON.stringify(t));}),process.stdin.on("error",()=>{console.log(JSON.stringify(_())),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify(_())),process.exit(0);},4e3).unref();}export{lt as handlePreToolUse};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import {createHash}from'node:crypto';import {mkdirSync,appendFileSync}from'node:fs';import {homedir as homedir$1}from'node:os';import {dirname,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {existsSync,readFileSync,rmSync,mkdirSync as mkdirSync$1,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join,dirname as dirname$1}from'path';import {randomUUID}from'crypto';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {fileURLToPath}from'url';import {request}from'http';import {homedir}from'os';var L=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,J=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu;function S(s){let t=s.trim();return t&&L.test(t)&&!J.test(t)?t:null}function H(s){let t=createHash("sha256").update(s).digest("hex").slice(0,16),e=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",t);return join$1(e,"memory-intents.jsonl")}function D(s,t){let e=H(s);mkdirSync(dirname(e),{recursive:true}),appendFileSync(e,`${JSON.stringify(t)}
|
|
2
|
-
`,{mode:384});}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function b(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function W(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function f(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(W(s))}function Q(s){return Buffer.from(s,"utf8").toString("base64url")}var p=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(f(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let n=Date.now(),o={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,o),this.persist(o),o}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let o=JSON.parse(readFileSync(n,"utf8"));return o.sessionId!==e||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(e,o),o)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Q(t)}.json`)}persist(t){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function ee(s){let t=s.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function R(s){let t=s.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=ee(t[1]);return e?{rawName:t[1],skillName:e}:null}var se=1;function ie(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function x(s=ie()){let t=[join(s,"dist","command-registry.json"),join(s,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==se||!ae(r.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function ae(s){return !s||typeof s!="object"||Array.isArray(s)?false:Object.values(s).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return I(e.mcpTools)&&I(e.blockedNative)})}function I(s){return Array.isArray(s)&&s.every(t=>typeof t=="string"&&t.length>0)}function E(s){let t=x();return t?t[s]?.mcpTools??[]:[]}var C=1e4,he=3e4,d=500;function ge(s,t,e){return new Promise(r=>{try{let n=new URL(s),o=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();r(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null);}),o.write(t),o.end();}catch{r(null);}})}var h=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,n,o=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:o,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let o={executionId:t,status:e,finishedAt:r,metadata:n},i=this.commitOrCache("execution_complete",o);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",o),i}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},o=this.commitOrCache("session_start",n);return o&&this.postHttp("/api/telemetry/sessions",n),o}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:d}).reverse();for(let o of n)try{this.applyOperation(o.operation,o.payload),r.resolveTelemetryFailure(o.id);}catch{}r.trimTelemetryFailures(d);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let o=join(r,n);try{return {cacheFile:o,envelope:fe(JSON.parse(readFileSync(o,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||M(r.envelope.operation)-M(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let o=this.getDatabase();o.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),o.resolveTelemetryFailure(n.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:N(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(N(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let n=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),o={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let i=this.getDatabase();i.insertTelemetryFailure({id:o.id,operation:t,payload:e,error:o.failure,createdAt:n}),i.trimTelemetryFailures(d),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(o,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-d)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},he).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){ge(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=C?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,C)}...`}}};function M(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function N(s){return `hook-run:${s}`}function fe(s,t){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},o=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:o}}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function ve(s){let t=b()?{additionalContext:w}:{},e;try{e=JSON.parse(s);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}};let n=S(r);if(n)try{D(m,{content:n,sessionId:e.session_id,createdAt:Date.now()});}catch{}let o=e.session_id?.trim(),i=R(r);if(o&&i){let c=new p(m),P=c.get(o)?.executionId,l=c.startExecution(o,i.skillName,E(i.skillName));if(l.executionId&&l.executionId!==P){let g=new h;try{await g.sendSessionStart(o,m,l.startedAt),await g.sendExecutionStart(l.executionId,o,i.skillName,l.lastActivityAt,m,l.requiredMcpTools);}finally{g.close();}}}let a=new MemoryGate(m);try{await a.forceWarmUp(),o&&await a.recordUserMessage(r.slice(0,2e3),o,r.slice(0,200));}catch{}finally{a.close();}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let s="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{s+=t;}),process.stdin.on("end",async()=>{let t=await ve(s.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}
|
|
3
|
-
export{
|
|
2
|
+
`,{mode:384});}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function b(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}function W(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function f(s){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(W(s))}function Q(s){return Buffer.from(s,"utf8").toString("base64url")}var d=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(f(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let n=Date.now(),o={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,o),this.persist(o),o}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let o=JSON.parse(readFileSync(n,"utf8"));return o.sessionId!==e||o.projectRoot!==this.projectRoot?null:(o.requiredMcpTools=Array.isArray(o.requiredMcpTools)?o.requiredMcpTools:[],this.sessions.set(e,o),o)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Q(t)}.json`)}persist(t){mkdirSync$1(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function ee(s){let t=s.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function x(s){let t=s.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!t)return null;let e=ee(t[1]);return e?{rawName:t[1],skillName:e}:null}var se=2,ie=1;function ae(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname$1(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function R(s=ae()){let t=[join(s,"dist","command-registry.json"),join(s,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let r=JSON.parse(readFileSync(e,"utf8"));if(r.version!==se&&r.version!==ie||!ce(r.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return r.commands}catch(r){console.error(`[devflow] command registry load failed: ${r.message}`);}return null}function ce(s){return !s||typeof s!="object"||Array.isArray(s)?false:Object.values(s).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return I(e.mcpTools)&&I(e.blockedNative)&&(e.source===void 0||e.source==="core"||e.source==="plugin")&&(e.requiresContext===void 0||typeof e.requiresContext=="boolean")&&(e.evidenceMode===void 0||["static","runtime","mixed"].includes(e.evidenceMode))})}function I(s){return Array.isArray(s)&&s.every(t=>typeof t=="string"&&t.length>0)}function E(s){let t=R();return t?t[s]?.mcpTools??[]:[]}var C=1e4,ge=3e4,p=500;function fe(s,t,e){return new Promise(r=>{try{let n=new URL(s),o=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();r(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null);}),o.write(t),o.end();}catch{r(null);}})}var h=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,n,o=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:o,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let o={executionId:t,status:e,finishedAt:r,metadata:n},i=this.commitOrCache("execution_complete",o);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",o),i}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},o=this.commitOrCache("session_start",n);return o&&this.postHttp("/api/telemetry/sessions",n),o}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:p}).reverse();for(let o of n)try{this.applyOperation(o.operation,o.payload),r.resolveTelemetryFailure(o.id);}catch{}r.trimTelemetryFailures(p);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let o=join(r,n);try{return {cacheFile:o,envelope:ye(JSON.parse(readFileSync(o,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||M(r.envelope.operation)-M(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let o=this.getDatabase();o.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),o.resolveTelemetryFailure(n.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:N(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(N(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let n=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),o={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let i=this.getDatabase();i.insertTelemetryFailure({id:o.id,operation:t,payload:e,error:o.failure,createdAt:n}),i.trimTelemetryFailures(p),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync$1(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(o,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-p)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},ge).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){fe(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=C?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,C)}...`}}};function M(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function N(s){return `hook-run:${s}`}function ye(s,t){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},o=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:o}}var m=process.env.CLAUDE_PROJECT_DIR||process.cwd();async function Se(s){let t=b()?{additionalContext:w}:{},e;try{e=JSON.parse(s);}catch{return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}let r=e.prompt||"";if(!r.trim())return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}};let n=S(r);if(n)try{D(m,{content:n,sessionId:e.session_id,createdAt:Date.now()});}catch{}let o=e.session_id?.trim(),i=x(r);if(o&&i){let c=new d(m),P=c.get(o)?.executionId,l=c.startExecution(o,i.skillName,E(i.skillName));if(l.executionId&&l.executionId!==P){let g=new h;try{await g.sendSessionStart(o,m,l.startedAt),await g.sendExecutionStart(l.executionId,o,i.skillName,l.lastActivityAt,m,l.requiredMcpTools);}finally{g.close();}}}let a=new MemoryGate(m);try{await a.forceWarmUp(),o&&await a.recordUserMessage(r.slice(0,2e3),o,r.slice(0,200));}catch{}finally{a.close();}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow",...t}}}if(process.argv[1]?.endsWith("user-prompt-submit")||process.argv[1]?.endsWith("user-prompt-submit.js")){let s="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{s+=t;}),process.stdin.on("end",async()=>{let t=await Se(s.trim()||process.argv[2]||"");console.log(JSON.stringify(t)),process.exit(0);}),process.stdin.on("error",()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);}),setTimeout(()=>{console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}})),process.exit(0);},3e4).unref();}
|
|
3
|
+
export{Se as handleUserPromptSubmit};
|