context-mode 1.0.116 → 1.0.117
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/server.js +6 -1
- package/cli.bundle.mjs +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +1 -1
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.117"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "context-mode",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.117",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.117",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.117",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.117",
|
|
4
4
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
package/build/server.js
CHANGED
|
@@ -2552,7 +2552,12 @@ server.registerTool("ctx_stats", {
|
|
|
2552
2552
|
}
|
|
2553
2553
|
}
|
|
2554
2554
|
catch { /* never block ctx_stats */ }
|
|
2555
|
-
|
|
2555
|
+
// v1.0.117: pass projectDir as cwd so the narrative renderer's
|
|
2556
|
+
// "started in <path>" line matches the user's actual project, not
|
|
2557
|
+
// the MCP server's chdir'd plugin install dir. getProjectDir()
|
|
2558
|
+
// includes v1.0.115's transcript heuristic which reads the literal
|
|
2559
|
+
// cwd from Claude Code's session jsonl.
|
|
2560
|
+
text = formatReport(report, VERSION, _latestVersion, { lifetime, mcpUsage, multiAdapter, conversation, realBytes, cwd: projectDir });
|
|
2556
2561
|
}
|
|
2557
2562
|
finally {
|
|
2558
2563
|
sdb.close();
|
package/cli.bundle.mjs
CHANGED
|
@@ -945,7 +945,7 @@ THINK IN CODE \u2014 NON-NEGOTIABLE: When commands produce data you need to anal
|
|
|
945
945
|
`),c=Buffer.byteLength(a),u=a.split(`
|
|
946
946
|
`).length;if(i&&o.length===0)return K("ctx_batch_execute",{content:[{type:"text",text:`Batch timed out after ${r}ms. No output captured.`}],isError:!0});mr(c);let d=ms(),l=`batch:${t.map(_=>_.label).join(",").slice(0,80)}`,m=d.index({content:a,source:l}),f=d.getChunksBySource(m.sourceId),p=["## Indexed Sections",""],h=[];for(let _ of f){let b=Buffer.byteLength(_.content);p.push(`- ${_.title} (${(b/1024).toFixed(1)}KB)`),h.push(_.title)}let g=g$(d,e,l),y=d.getDistinctiveTerms?d.getDistinctiveTerms(m.sourceId):[],x=[`Executed ${t.length} commands (${u} lines, ${(c/1024).toFixed(1)}KB). Indexed ${m.totalChunks} sections. Searched ${e.length} queries.`,"",...p,"",...g,y.length>0?`
|
|
947
947
|
Searchable terms for follow-up: ${y.join(", ")}`:""].join(`
|
|
948
|
-
`);return K("ctx_batch_execute",{content:[{type:"text",text:x}]})}catch(s){let o=s instanceof Error?s.message:String(s);return K("ctx_batch_execute",{content:[{type:"text",text:`Batch execution error: ${o}`}],isError:!0})}});rt.registerTool("ctx_stats",{title:"Session Statistics",description:"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, and context savings ratio.",inputSchema:U.object({})},async()=>{let t;try{let e=fr(),r=ls(e),n=ju({projectDir:e,sessionsDir:ct()});if(ze(n)){let s=Xt(),o=new s(n,{readonly:!0});try{let i=new vo(o),a=i.queryAll(se),c=i.getMcpToolUsage(),u=Ki({sessionsDir:ct()}),d;try{d=Zu()}catch{}let l,m;try{let f=process.env.CLAUDE_SESSION_ID;if(f||(f=o.prepare("SELECT session_id FROM session_events WHERE session_id LIKE '________-____-____-____-____________' ORDER BY created_at DESC LIMIT 1").get()?.session_id),f){l=YE({sessionId:f,sessionsDir:ct(),worktreeHash:r});let p=uy({sessionId:f,sessionsDir:ct(),worktreeHash:r}),h=uy({sessionsDir:ct()});m={conversation:p,lifetime:h}}}catch{}t=qu(a,Ur,Hr,{lifetime:u,mcpUsage:c,multiAdapter:d,conversation:l,realBytes:m})}finally{o.close()}}else{let o=new vo(u$()).queryAll(se),i=Ki({sessionsDir:ct()}),a;try{a=Zu()}catch{}t=qu(o,Ur,Hr,{lifetime:i,multiAdapter:a})}}catch{let r=new vo(u$()).queryAll(se),n;try{n=Ki({sessionsDir:ct()})}catch{}let s;try{s=Zu()}catch{}t=qu(r,Ur,Hr,n||s?{lifetime:n,multiAdapter:s}:void 0)}return K("ctx_stats",{content:[{type:"text",text:t}]})});rt.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns a plain-text status report with [OK]/[FAIL]/[WARN] prefixes (renderer-safe across MCP clients). No CLI execution needed.",inputSchema:U.object({})},async()=>{let t=["context-mode doctor",""],e=ze(ut(Nt,"package.json"))?Nt:Qt(Nt),r=11,n=(Gu.length/r*100).toFixed(0);t.push(`[OK] Runtimes: ${Gu.length}/${r} (${n}%) \u2014 ${Gu.join(", ")}`),_s()?t.push("[OK] Performance: FAST (Bun)"):t.push("[WARN] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");{let o=new po({runtimes:ea});try{let i=await o.execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(i.exitCode===0&&i.stdout.trim()==="ok")t.push("[OK] Server test: PASS");else{let a=i.stderr?.trim()?` (${i.stderr.trim().slice(0,200)})`:"";t.push(`[FAIL] Server test: FAIL \u2014 exit ${i.exitCode}${a}`)}}catch(i){t.push(`[FAIL] Server test: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{o.cleanupBackgrounded()}}{let o;try{let i=Xt();o=new i(":memory:"),o.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)"),o.exec("INSERT INTO fts_test(content) VALUES ('hello world')");let a=o.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();a&&a.content==="hello world"?t.push("[OK] FTS5 / SQLite: PASS \u2014 native module works"):t.push("[FAIL] FTS5 / SQLite: FAIL \u2014 unexpected result")}catch(i){t.push(`[FAIL] FTS5 / SQLite: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{try{o?.close()}catch{}}}let s=await mF();if(s){for(let i of s.validateHooks(e)){let a=i.status==="pass"?"[OK]":i.status==="warn"?"[WARN]":"[FAIL]",c=i.fix?` \u2014 fix: ${i.fix}`:"";t.push(`${a} ${i.check}: ${i.message}${c}`)}let o=ga(s,e);o.length===0&&t.push("[OK] Hook scripts: no direct .mjs script paths to verify");for(let i of o){let a=ut(e,i);ze(a)?t.push(`[OK] Hook script: PASS \u2014 ${a}`):t.push(`[FAIL] Hook script: FAIL \u2014 not found at ${a}`)}}else t.push("[WARN] Hooks: adapter detection unavailable");return t.push(`[OK] Version: v${Ur}`),K("ctx_doctor",{content:[{type:"text",text:t.join(`
|
|
948
|
+
`);return K("ctx_batch_execute",{content:[{type:"text",text:x}]})}catch(s){let o=s instanceof Error?s.message:String(s);return K("ctx_batch_execute",{content:[{type:"text",text:`Batch execution error: ${o}`}],isError:!0})}});rt.registerTool("ctx_stats",{title:"Session Statistics",description:"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, and context savings ratio.",inputSchema:U.object({})},async()=>{let t;try{let e=fr(),r=ls(e),n=ju({projectDir:e,sessionsDir:ct()});if(ze(n)){let s=Xt(),o=new s(n,{readonly:!0});try{let i=new vo(o),a=i.queryAll(se),c=i.getMcpToolUsage(),u=Ki({sessionsDir:ct()}),d;try{d=Zu()}catch{}let l,m;try{let f=process.env.CLAUDE_SESSION_ID;if(f||(f=o.prepare("SELECT session_id FROM session_events WHERE session_id LIKE '________-____-____-____-____________' ORDER BY created_at DESC LIMIT 1").get()?.session_id),f){l=YE({sessionId:f,sessionsDir:ct(),worktreeHash:r});let p=uy({sessionId:f,sessionsDir:ct(),worktreeHash:r}),h=uy({sessionsDir:ct()});m={conversation:p,lifetime:h}}}catch{}t=qu(a,Ur,Hr,{lifetime:u,mcpUsage:c,multiAdapter:d,conversation:l,realBytes:m,cwd:e})}finally{o.close()}}else{let o=new vo(u$()).queryAll(se),i=Ki({sessionsDir:ct()}),a;try{a=Zu()}catch{}t=qu(o,Ur,Hr,{lifetime:i,multiAdapter:a})}}catch{let r=new vo(u$()).queryAll(se),n;try{n=Ki({sessionsDir:ct()})}catch{}let s;try{s=Zu()}catch{}t=qu(r,Ur,Hr,n||s?{lifetime:n,multiAdapter:s}:void 0)}return K("ctx_stats",{content:[{type:"text",text:t}]})});rt.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns a plain-text status report with [OK]/[FAIL]/[WARN] prefixes (renderer-safe across MCP clients). No CLI execution needed.",inputSchema:U.object({})},async()=>{let t=["context-mode doctor",""],e=ze(ut(Nt,"package.json"))?Nt:Qt(Nt),r=11,n=(Gu.length/r*100).toFixed(0);t.push(`[OK] Runtimes: ${Gu.length}/${r} (${n}%) \u2014 ${Gu.join(", ")}`),_s()?t.push("[OK] Performance: FAST (Bun)"):t.push("[WARN] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");{let o=new po({runtimes:ea});try{let i=await o.execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(i.exitCode===0&&i.stdout.trim()==="ok")t.push("[OK] Server test: PASS");else{let a=i.stderr?.trim()?` (${i.stderr.trim().slice(0,200)})`:"";t.push(`[FAIL] Server test: FAIL \u2014 exit ${i.exitCode}${a}`)}}catch(i){t.push(`[FAIL] Server test: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{o.cleanupBackgrounded()}}{let o;try{let i=Xt();o=new i(":memory:"),o.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)"),o.exec("INSERT INTO fts_test(content) VALUES ('hello world')");let a=o.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();a&&a.content==="hello world"?t.push("[OK] FTS5 / SQLite: PASS \u2014 native module works"):t.push("[FAIL] FTS5 / SQLite: FAIL \u2014 unexpected result")}catch(i){t.push(`[FAIL] FTS5 / SQLite: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{try{o?.close()}catch{}}}let s=await mF();if(s){for(let i of s.validateHooks(e)){let a=i.status==="pass"?"[OK]":i.status==="warn"?"[WARN]":"[FAIL]",c=i.fix?` \u2014 fix: ${i.fix}`:"";t.push(`${a} ${i.check}: ${i.message}${c}`)}let o=ga(s,e);o.length===0&&t.push("[OK] Hook scripts: no direct .mjs script paths to verify");for(let i of o){let a=ut(e,i);ze(a)?t.push(`[OK] Hook script: PASS \u2014 ${a}`):t.push(`[FAIL] Hook script: FAIL \u2014 not found at ${a}`)}}else t.push("[WARN] Hooks: adapter detection unavailable");return t.push(`[OK] Version: v${Ur}`),K("ctx_doctor",{content:[{type:"text",text:t.join(`
|
|
949
949
|
`)}]})});rt.registerTool("ctx_upgrade",{title:"Upgrade Plugin",description:"Upgrade context-mode to the latest version. Returns a shell command to execute. You MUST run the returned command using your shell tool (Bash, shell_execute, run_in_terminal, etc.) and display the output as a checklist. Tell the user to restart their session after upgrade.",inputSchema:U.object({})},async()=>{let t=ze(ut(Nt,"package.json"))?Nt:Qt(Nt),e=ut(t,"cli.bundle.mjs"),r=ut(t,"build","cli.js");try{let o=ct(),i=Ce(Qt(o),"insight-cache");ze(i)&&(wy(4747),Ku(i,{recursive:!0,force:!0}))}catch{}let n;if(ze(e))n=`${Fe(e)} upgrade`;else if(ze(r))n=`${Fe(r)} upgrade`;else{let i=['import{execFileSync}from"node:child_process";','import{cpSync,rmSync,existsSync,mkdtempSync,readFileSync,writeFileSync}from"node:fs";','import{join}from"node:path";','import{tmpdir}from"node:os";',`const P=${JSON.stringify(t)};`,'const T=mkdtempSync(join(tmpdir(),"ctx-upgrade-"));',"try{",'console.log("- [x] Starting inline upgrade (no CLI found)");','execFileSync("git",["clone","--depth","1","https://github.com/mksglu/context-mode.git",T],{stdio:"inherit"});','console.log("- [x] Cloned latest source");','execFileSync(process.platform==="win32"?"npm.cmd":"npm",["install"],{cwd:T,stdio:"inherit",shell:process.platform==="win32"});','execFileSync(process.platform==="win32"?"npm.cmd":"npm",["run","build"],{cwd:T,stdio:"inherit",shell:process.platform==="win32"});','console.log("- [x] Built from source");','const pkg=JSON.parse(readFileSync(join(T,"package.json"),"utf8"));','const items=[...(Array.isArray(pkg.files)?pkg.files:[]),"src","package.json"];',"for(const item of items){const from=join(T,item);const to=join(P,item);if(existsSync(from)){rmSync(to,{recursive:true,force:true});cpSync(from,to,{recursive:true,force:true});}}",'writeFileSync(join(P,".mcp.json"),JSON.stringify({mcpServers:{"context-mode":{command:"node",args:["${CLAUDE_PLUGIN_ROOT}/start.mjs"]}}},null,2)+"\\n");','console.log("- [x] Copied package files");','execFileSync(process.platform==="win32"?"npm.cmd":"npm",["install","--production"],{cwd:P,stdio:"inherit",shell:process.platform==="win32"});','console.log("- [x] Installed production dependencies");','console.log("## context-mode upgrade complete");',"}catch(e){",'console.error("- [ ] Upgrade failed:",e.message);',"process.exit(1);","}finally{","try{rmSync(T,{recursive:true,force:true})}catch{}","}"].join(`
|
|
950
950
|
`),a=ut(t,".ctx-upgrade-inline.mjs"),{writeFileSync:c}=await import("node:fs");c(a,i),n=Fe(a)}let s=["## ctx-upgrade","","Run this command using your shell execution tool:","","```",n,"```","","After the command completes, display results as a markdown checklist:","- `[x]` for success, `[ ]` for failure","- Example format:"," ```"," ## context-mode upgrade"," - [x] Pulled latest from GitHub"," - [x] Built and installed v0.9.24"," - [x] npm global updated"," - [x] Hooks configured"," - [x] Doctor: all checks PASS"," ```","- Tell the user to restart their session to pick up the new version."].join(`
|
|
951
951
|
`);return K("ctx_upgrade",{content:[{type:"text",text:s}]})});rt.registerTool("ctx_purge",{title:"Purge Knowledge Base",description:"Permanently deletes ALL session data for this project: FTS5 knowledge base (indexed content), session events DB (analytics, metadata, resume snapshots), and session events markdown. Resets in-memory stats. This is irreversible.",inputSchema:U.object({confirm:U.boolean().describe("Must be true to confirm the destructive operation.")})},async({confirm:t})=>{if(!t)return K("ctx_purge",{content:[{type:"text",text:"Purge cancelled. Pass confirm: true to proceed."}]});let e;try{e=fy()}catch{}if(pr){try{pr.cleanup()}catch{}pr=null}let r=e?Qt(e):void 0,{deleted:n}=EE({projectDir:fr(),sessionsDir:ct(),storePath:e,contentDir:r,legacyContentDir:Ce(Qu(),".context-mode","content"),contentHash:us(fr())});se.calls={},se.bytesReturned={},se.bytesIndexed=0,se.bytesSandboxed=0,se.cacheHits=0,se.cacheBytesSaved=0,se.sessionStart=Date.now(),n.push("session stats");try{let s=p$();ze(s)&&Yi(s)}catch{}return K("ctx_purge",{content:[{type:"text",text:`Purged: ${n.join(", ")}. All session data for this project has been permanently deleted.`}]})});Ji=5e3;rt.registerTool("ctx_insight",{title:"Open Insight Dashboard",description:"Opens the context-mode Insight dashboard in the browser. Shows personal analytics: session activity, tool usage, error rate, parallel work patterns, project focus, and actionable insights. First run installs dependencies (~30s). Subsequent runs open instantly.",inputSchema:U.object({port:U.coerce.number().int().min(1).max(65535).optional().describe("Port to serve on (default: 4747)"),sessionDir:U.string().optional().describe("Override INSIGHT_SESSION_DIR: directory containing context-mode session .db files"),contentDir:U.string().optional().describe("Override INSIGHT_CONTENT_DIR: directory containing context-mode content/index .db files"),insightSessionDir:U.string().optional().describe("Alias for sessionDir / INSIGHT_SESSION_DIR"),insightContentDir:U.string().optional().describe("Alias for contentDir / INSIGHT_CONTENT_DIR")})},async({port:t,sessionDir:e,contentDir:r,insightSessionDir:n,insightContentDir:s})=>{let o=t||4747,i=e||n,a=r||s,c=ze(ut(Nt,"package.json"))?Nt:Qt(Nt),u=ut(c,"insight"),d=i?ut(i):ct(),l=a?ut(a):Ce(Qt(d),"content"),m=Ce(Qt(d),"insight-cache");if(!ze(Ce(u,"server.mjs")))return K("ctx_insight",{content:[{type:"text",text:"Error: Insight source not found in plugin. Try upgrading context-mode."}]});try{let f=[],p=!1;Xi(m,{recursive:!0});let h=e$(Ce(u,"server.mjs")).mtimeMs,g=ze(Ce(m,"server.mjs"))?e$(Ce(m,"server.mjs")).mtimeMs:0;if(h>g&&(f.push("Copying source files..."),sF(u,m,{recursive:!0,force:!0}),f.push("Source files copied."),p=!0),!ze(Ce(m,"node_modules"))||p){f.push("Installing dependencies (first run, ~30s)...");try{t$(process.platform==="win32"?"npm.cmd install --production=false":"npm install --production=false",{cwd:m,stdio:"pipe",timeout:3e5})}catch{try{Ku(Ce(m,"node_modules"),{recursive:!0,force:!0})}catch{}throw new Error("npm install failed \u2014 please retry")}if(!ze(Ce(m,"node_modules","vite"))||!ze(Ce(m,"node_modules","better-sqlite3")))throw Ku(Ce(m,"node_modules"),{recursive:!0,force:!0}),new Error("npm install incomplete \u2014 please retry");f.push("Dependencies installed.")}f.push("Building dashboard..."),t$("npx vite build",{cwd:m,stdio:"pipe",timeout:6e4}),f.push("Build complete.");let x=!1;try{let{request:T}=await import("node:http");await new Promise((z,P)=>{let O=T(`http://127.0.0.1:${o}/api/overview`,{timeout:2e3},V=>{V.resume(),z()});O.on("error",()=>P()),O.on("timeout",()=>{O.destroy(),P()}),O.end()}),x=!0}catch{}if(x&&p){f.push("Killing stale dashboard server (source updated)...");let T=wy(o);if(T.attemptedPids.length>0&&T.killedPids.length===0)return K("ctx_insight",{content:[{type:"text",text:`Could not free port ${o} (kill failed for ${T.attemptedPids.join(", ")}: ${T.errors.join("; ")}). Try ctx_insight({ port: ${o+1} }) or stop the process manually.`}]});if(T.errors.length>0&&T.attemptedPids.length===0)return K("ctx_insight",{content:[{type:"text",text:`Cannot reclaim port ${o}: ${T.errors.join("; ")}. Stop the process manually or pick another port.`}]});await new Promise(z=>setTimeout(z,500)),f.push(`Stale server killed (${T.killedPids.length} pid${T.killedPids.length===1?"":"s"}).`)}else if(x){f.push("Dashboard already running.");let T=`http://localhost:${o}`,z=gy(T),P=z.ok?"":` (auto-open failed: ${z.reason}; navigate manually)`;return K("ctx_insight",{content:[{type:"text",text:`Dashboard already running at ${T}${P}`}]})}if(Fr&&Fr.pid&&!Fr.killed)try{Fr.kill("SIGTERM")}catch{}let{spawn:_}=await import("node:child_process"),b=_("node",[Ce(m,"server.mjs")],{cwd:m,env:{...process.env,PORT:String(o),INSIGHT_SESSION_DIR:d,INSIGHT_CONTENT_DIR:l,INSIGHT_PARENT_PID:String(process.pid)},detached:!0,stdio:"ignore"});b.on("error",()=>{}),b.unref(),Fr=b,await new Promise(T=>setTimeout(T,1500));try{let{request:T}=await import("node:http");await new Promise((z,P)=>{let O=T(`http://127.0.0.1:${o}/api/overview`,{timeout:3e3},V=>{z(),V.resume()});O.on("error",P),O.on("timeout",()=>{O.destroy(),P(new Error("timeout"))}),O.end()})}catch{return K("ctx_insight",{content:[{type:"text",text:`Port ${o} appears to be in use. Either a previous dashboard is still running, or another service is using this port.
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.117",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.117",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
|
|
6
6
|
"author": "Mert Koseoğlu",
|
package/server.bundle.mjs
CHANGED
|
@@ -943,7 +943,7 @@ THINK IN CODE \u2014 NON-NEGOTIABLE: When commands produce data you need to anal
|
|
|
943
943
|
`),c=Buffer.byteLength(a),u=a.split(`
|
|
944
944
|
`).length;if(i&&s.length===0)return B("ctx_batch_execute",{content:[{type:"text",text:`Batch timed out after ${r}ms. No output captured.`}],isError:!0});Yt(c);let l=Fn(),d=`batch:${t.map(x=>x.label).join(",").slice(0,80)}`,f=l.index({content:a,source:d}),m=l.getChunksBySource(f.sourceId),p=["## Indexed Sections",""],h=[];for(let x of m){let b=Buffer.byteLength(x.content);p.push(`- ${x.title} (${(b/1024).toFixed(1)}KB)`),h.push(x.title)}let g=sj(l,e,d),y=l.getDistinctiveTerms?l.getDistinctiveTerms(f.sourceId):[],_=[`Executed ${t.length} commands (${u} lines, ${(c/1024).toFixed(1)}KB). Indexed ${f.totalChunks} sections. Searched ${e.length} queries.`,"",...p,"",...g,y.length>0?`
|
|
945
945
|
Searchable terms for follow-up: ${y.join(", ")}`:""].join(`
|
|
946
|
-
`);return B("ctx_batch_execute",{content:[{type:"text",text:_}]})}catch(o){let s=o instanceof Error?o.message:String(o);return B("ctx_batch_execute",{content:[{type:"text",text:`Batch execution error: ${s}`}],isError:!0})}});function Ek(){return{prepare:()=>({run:()=>{},get:(...t)=>({cnt:0,compact_count:0,minutes:null,rate:0,avg:0,outcome:"exploratory"}),all:()=>[]})}}We.registerTool("ctx_stats",{title:"Session Statistics",description:"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, and context savings ratio.",inputSchema:D.object({})},async()=>{let t;try{let e=Xt(),r=An(e),n=pc({projectDir:e,sessionsDir:Qe()});if(Ie(n)){let o=Wt(),s=new o(n,{readonly:!0});try{let i=new Uo(s),a=i.queryAll(te),c=i.getMcpToolUsage(),u=ai({sessionsDir:Qe()}),l;try{l=Ic()}catch{}let d,f;try{let m=process.env.CLAUDE_SESSION_ID;if(m||(m=s.prepare("SELECT session_id FROM session_events WHERE session_id LIKE '________-____-____-____-____________' ORDER BY created_at DESC LIMIT 1").get()?.session_id),m){d=mk({sessionId:m,sessionsDir:Qe(),worktreeHash:r});let p=qm({sessionId:m,sessionsDir:Qe(),worktreeHash:r}),h=qm({sessionsDir:Qe()});f={conversation:p,lifetime:h}}}catch{}t=Nc(a,kr,wr,{lifetime:u,mcpUsage:c,multiAdapter:l,conversation:d,realBytes:f})}finally{s.close()}}else{let s=new Uo(Ek()).queryAll(te),i=ai({sessionsDir:Qe()}),a;try{a=Ic()}catch{}t=Nc(s,kr,wr,{lifetime:i,multiAdapter:a})}}catch{let r=new Uo(Ek()).queryAll(te),n;try{n=ai({sessionsDir:Qe()})}catch{}let o;try{o=Ic()}catch{}t=Nc(r,kr,wr,n||o?{lifetime:n,multiAdapter:o}:void 0)}return B("ctx_stats",{content:[{type:"text",text:t}]})});We.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns a plain-text status report with [OK]/[FAIL]/[WARN] prefixes (renderer-safe across MCP clients). No CLI execution needed.",inputSchema:D.object({})},async()=>{let t=["context-mode doctor",""],e=Ie(et(St,"package.json"))?St:Dt(St),r=11,n=(jc.length/r*100).toFixed(0);t.push(`[OK] Runtimes: ${jc.length}/${r} (${n}%) \u2014 ${jc.join(", ")}`),oc()?t.push("[OK] Performance: FAST (Bun)"):t.push("[WARN] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");{let s=new Vs({runtimes:pi});try{let i=await s.execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(i.exitCode===0&&i.stdout.trim()==="ok")t.push("[OK] Server test: PASS");else{let a=i.stderr?.trim()?` (${i.stderr.trim().slice(0,200)})`:"";t.push(`[FAIL] Server test: FAIL \u2014 exit ${i.exitCode}${a}`)}}catch(i){t.push(`[FAIL] Server test: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{s.cleanupBackgrounded()}}{let s;try{let i=Wt();s=new i(":memory:"),s.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)"),s.exec("INSERT INTO fts_test(content) VALUES ('hello world')");let a=s.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();a&&a.content==="hello world"?t.push("[OK] FTS5 / SQLite: PASS \u2014 native module works"):t.push("[FAIL] FTS5 / SQLite: FAIL \u2014 unexpected result")}catch(i){t.push(`[FAIL] FTS5 / SQLite: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{try{s?.close()}catch{}}}let o=await HD();if(o){for(let i of o.validateHooks(e)){let a=i.status==="pass"?"[OK]":i.status==="warn"?"[WARN]":"[FAIL]",c=i.fix?` \u2014 fix: ${i.fix}`:"";t.push(`${a} ${i.check}: ${i.message}${c}`)}let s=ok(o,e);s.length===0&&t.push("[OK] Hook scripts: no direct .mjs script paths to verify");for(let i of s){let a=et(e,i);Ie(a)?t.push(`[OK] Hook script: PASS \u2014 ${a}`):t.push(`[FAIL] Hook script: FAIL \u2014 not found at ${a}`)}}else t.push("[WARN] Hooks: adapter detection unavailable");return t.push(`[OK] Version: v${kr}`),B("ctx_doctor",{content:[{type:"text",text:t.join(`
|
|
946
|
+
`);return B("ctx_batch_execute",{content:[{type:"text",text:_}]})}catch(o){let s=o instanceof Error?o.message:String(o);return B("ctx_batch_execute",{content:[{type:"text",text:`Batch execution error: ${s}`}],isError:!0})}});function Ek(){return{prepare:()=>({run:()=>{},get:(...t)=>({cnt:0,compact_count:0,minutes:null,rate:0,avg:0,outcome:"exploratory"}),all:()=>[]})}}We.registerTool("ctx_stats",{title:"Session Statistics",description:"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, and context savings ratio.",inputSchema:D.object({})},async()=>{let t;try{let e=Xt(),r=An(e),n=pc({projectDir:e,sessionsDir:Qe()});if(Ie(n)){let o=Wt(),s=new o(n,{readonly:!0});try{let i=new Uo(s),a=i.queryAll(te),c=i.getMcpToolUsage(),u=ai({sessionsDir:Qe()}),l;try{l=Ic()}catch{}let d,f;try{let m=process.env.CLAUDE_SESSION_ID;if(m||(m=s.prepare("SELECT session_id FROM session_events WHERE session_id LIKE '________-____-____-____-____________' ORDER BY created_at DESC LIMIT 1").get()?.session_id),m){d=mk({sessionId:m,sessionsDir:Qe(),worktreeHash:r});let p=qm({sessionId:m,sessionsDir:Qe(),worktreeHash:r}),h=qm({sessionsDir:Qe()});f={conversation:p,lifetime:h}}}catch{}t=Nc(a,kr,wr,{lifetime:u,mcpUsage:c,multiAdapter:l,conversation:d,realBytes:f,cwd:e})}finally{s.close()}}else{let s=new Uo(Ek()).queryAll(te),i=ai({sessionsDir:Qe()}),a;try{a=Ic()}catch{}t=Nc(s,kr,wr,{lifetime:i,multiAdapter:a})}}catch{let r=new Uo(Ek()).queryAll(te),n;try{n=ai({sessionsDir:Qe()})}catch{}let o;try{o=Ic()}catch{}t=Nc(r,kr,wr,n||o?{lifetime:n,multiAdapter:o}:void 0)}return B("ctx_stats",{content:[{type:"text",text:t}]})});We.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns a plain-text status report with [OK]/[FAIL]/[WARN] prefixes (renderer-safe across MCP clients). No CLI execution needed.",inputSchema:D.object({})},async()=>{let t=["context-mode doctor",""],e=Ie(et(St,"package.json"))?St:Dt(St),r=11,n=(jc.length/r*100).toFixed(0);t.push(`[OK] Runtimes: ${jc.length}/${r} (${n}%) \u2014 ${jc.join(", ")}`),oc()?t.push("[OK] Performance: FAST (Bun)"):t.push("[WARN] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");{let s=new Vs({runtimes:pi});try{let i=await s.execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(i.exitCode===0&&i.stdout.trim()==="ok")t.push("[OK] Server test: PASS");else{let a=i.stderr?.trim()?` (${i.stderr.trim().slice(0,200)})`:"";t.push(`[FAIL] Server test: FAIL \u2014 exit ${i.exitCode}${a}`)}}catch(i){t.push(`[FAIL] Server test: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{s.cleanupBackgrounded()}}{let s;try{let i=Wt();s=new i(":memory:"),s.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)"),s.exec("INSERT INTO fts_test(content) VALUES ('hello world')");let a=s.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();a&&a.content==="hello world"?t.push("[OK] FTS5 / SQLite: PASS \u2014 native module works"):t.push("[FAIL] FTS5 / SQLite: FAIL \u2014 unexpected result")}catch(i){t.push(`[FAIL] FTS5 / SQLite: FAIL \u2014 ${i instanceof Error?i.message:i}`)}finally{try{s?.close()}catch{}}}let o=await HD();if(o){for(let i of o.validateHooks(e)){let a=i.status==="pass"?"[OK]":i.status==="warn"?"[WARN]":"[FAIL]",c=i.fix?` \u2014 fix: ${i.fix}`:"";t.push(`${a} ${i.check}: ${i.message}${c}`)}let s=ok(o,e);s.length===0&&t.push("[OK] Hook scripts: no direct .mjs script paths to verify");for(let i of s){let a=et(e,i);Ie(a)?t.push(`[OK] Hook script: PASS \u2014 ${a}`):t.push(`[FAIL] Hook script: FAIL \u2014 not found at ${a}`)}}else t.push("[WARN] Hooks: adapter detection unavailable");return t.push(`[OK] Version: v${kr}`),B("ctx_doctor",{content:[{type:"text",text:t.join(`
|
|
947
947
|
`)}]})});We.registerTool("ctx_upgrade",{title:"Upgrade Plugin",description:"Upgrade context-mode to the latest version. Returns a shell command to execute. You MUST run the returned command using your shell tool (Bash, shell_execute, run_in_terminal, etc.) and display the output as a checklist. Tell the user to restart their session after upgrade.",inputSchema:D.object({})},async()=>{let t=Ie(et(St,"package.json"))?St:Dt(St),e=et(t,"cli.bundle.mjs"),r=et(t,"build","cli.js");try{let s=Qe(),i=Ee(Dt(s),"insight-cache");Ie(i)&&(Nk(4747),Mc(i,{recursive:!0,force:!0}))}catch{}let n;if(Ie(e))n=`${Ne(e)} upgrade`;else if(Ie(r))n=`${Ne(r)} upgrade`;else{let i=['import{execFileSync}from"node:child_process";','import{cpSync,rmSync,existsSync,mkdtempSync,readFileSync,writeFileSync}from"node:fs";','import{join}from"node:path";','import{tmpdir}from"node:os";',`const P=${JSON.stringify(t)};`,'const T=mkdtempSync(join(tmpdir(),"ctx-upgrade-"));',"try{",'console.log("- [x] Starting inline upgrade (no CLI found)");','execFileSync("git",["clone","--depth","1","https://github.com/mksglu/context-mode.git",T],{stdio:"inherit"});','console.log("- [x] Cloned latest source");','execFileSync(process.platform==="win32"?"npm.cmd":"npm",["install"],{cwd:T,stdio:"inherit",shell:process.platform==="win32"});','execFileSync(process.platform==="win32"?"npm.cmd":"npm",["run","build"],{cwd:T,stdio:"inherit",shell:process.platform==="win32"});','console.log("- [x] Built from source");','const pkg=JSON.parse(readFileSync(join(T,"package.json"),"utf8"));','const items=[...(Array.isArray(pkg.files)?pkg.files:[]),"src","package.json"];',"for(const item of items){const from=join(T,item);const to=join(P,item);if(existsSync(from)){rmSync(to,{recursive:true,force:true});cpSync(from,to,{recursive:true,force:true});}}",'writeFileSync(join(P,".mcp.json"),JSON.stringify({mcpServers:{"context-mode":{command:"node",args:["${CLAUDE_PLUGIN_ROOT}/start.mjs"]}}},null,2)+"\\n");','console.log("- [x] Copied package files");','execFileSync(process.platform==="win32"?"npm.cmd":"npm",["install","--production"],{cwd:P,stdio:"inherit",shell:process.platform==="win32"});','console.log("- [x] Installed production dependencies");','console.log("## context-mode upgrade complete");',"}catch(e){",'console.error("- [ ] Upgrade failed:",e.message);',"process.exit(1);","}finally{","try{rmSync(T,{recursive:true,force:true})}catch{}","}"].join(`
|
|
948
948
|
`),a=et(t,".ctx-upgrade-inline.mjs"),{writeFileSync:c}=await import("node:fs");c(a,i),n=Ne(a)}let o=["## ctx-upgrade","","Run this command using your shell execution tool:","","```",n,"```","","After the command completes, display results as a markdown checklist:","- `[x]` for success, `[ ]` for failure","- Example format:"," ```"," ## context-mode upgrade"," - [x] Pulled latest from GitHub"," - [x] Built and installed v0.9.24"," - [x] npm global updated"," - [x] Hooks configured"," - [x] Doctor: all checks PASS"," ```","- Tell the user to restart their session to pick up the new version."].join(`
|
|
949
949
|
`);return B("ctx_upgrade",{content:[{type:"text",text:o}]})});We.registerTool("ctx_purge",{title:"Purge Knowledge Base",description:"Permanently deletes ALL session data for this project: FTS5 knowledge base (indexed content), session events DB (analytics, metadata, resume snapshots), and session events markdown. Resets in-memory stats. This is irreversible.",inputSchema:D.object({confirm:D.boolean().describe("Must be true to confirm the destructive operation.")})},async({confirm:t})=>{if(!t)return B("ctx_purge",{content:[{type:"text",text:"Purge cancelled. Pass confirm: true to proceed."}]});let e;try{e=Gm()}catch{}if(Jt){try{Jt.cleanup()}catch{}Jt=null}let r=e?Dt(e):void 0,{deleted:n}=NS({projectDir:Xt(),sessionsDir:Qe(),storePath:e,contentDir:r,legacyContentDir:Ee(Zc(),".context-mode","content"),contentHash:In(Xt())});te.calls={},te.bytesReturned={},te.bytesIndexed=0,te.bytesSandboxed=0,te.cacheHits=0,te.cacheBytesSaved=0,te.sessionStart=Date.now(),n.push("session stats");try{let o=$k();Ie(o)&&ui(o)}catch{}return B("ctx_purge",{content:[{type:"text",text:`Purged: ${n.join(", ")}. All session data for this project has been permanently deleted.`}]})});var ci=5e3;function xj(t,e){return e==="darwin"?[{cmd:"open",args:[t]}]:e==="win32"?[{cmd:"cmd",args:["/c","start","",t]}]:[{cmd:"xdg-open",args:[t]},{cmd:"sensible-browser",args:[t]}]}function Tk(t,e=process.platform,r=Rk){let n=xj(t,e),o=[];for(let{cmd:s,args:i}of n)try{let a=r(s,i,{stdio:"ignore",timeout:ci});if(!a.error&&a.status===0)return{ok:!0,method:s};let c=a.error?.message??`status=${a.status===null?"signaled":a.status}`;o.push(`${s}: ${c}`)}catch(a){o.push(`${s}: ${a instanceof Error?a.message:String(a)}`)}return{ok:!1,method:"none",reason:o.join("; ")}}function Nk(t,e=process.platform,r=Rk){let n={killedPids:[],attemptedPids:[],errors:[]};if(!Number.isInteger(t)||t<1||t>65535)return n.errors.push(`invalid port: ${t}`),n;try{if(e==="win32"){let o=r("netstat",["-ano"],{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:ci});if(o.error)return n.errors.push(`netstat: ${o.error.message}`),n;if(o.status!==0||typeof o.stdout!="string")return n;let s=`:${t}`,i=new Set;for(let a of o.stdout.split(/\r?\n/)){let c=a.trim();if(!c)continue;let u=c.split(/\s+/);if(u.length<5)continue;let l=u[0],d=u[1],f=u[2],m=u[u.length-1];l==="TCP"&&d.endsWith(s)&&(f!=="0.0.0.0:0"&&f!=="[::]:0"||/^\d+$/.test(m)&&i.add(m))}for(let a of i){n.attemptedPids.push(a);try{let c=r("taskkill",["/F","/PID",a],{stdio:"ignore",timeout:ci});c.error||c.status!==0?n.errors.push(`taskkill ${a}: ${c.error?.message??`status=${c.status}`}`):n.killedPids.push(a)}catch(c){n.errors.push(`taskkill ${a}: ${c instanceof Error?c.message:String(c)}`)}}}else{let o=r("lsof",["-ti",`:${t}`],{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:ci});if(o.error)return n.errors.push(`lsof: ${o.error.message}`),n;if(o.status!==0||typeof o.stdout!="string")return n;let s=o.stdout.split(/\r?\n/).filter(i=>/^\d+$/.test(i));for(let i of s){n.attemptedPids.push(i);try{let a=r("kill",[i],{stdio:"ignore",timeout:ci});a.error||a.status!==0?n.errors.push(`kill ${i}: ${a.error?.message??`status=${a.status}`}`):n.killedPids.push(i)}catch(a){n.errors.push(`kill ${i}: ${a instanceof Error?a.message:String(a)}`)}}}}catch(o){n.errors.push(o instanceof Error?o.message:String(o))}return n}We.registerTool("ctx_insight",{title:"Open Insight Dashboard",description:"Opens the context-mode Insight dashboard in the browser. Shows personal analytics: session activity, tool usage, error rate, parallel work patterns, project focus, and actionable insights. First run installs dependencies (~30s). Subsequent runs open instantly.",inputSchema:D.object({port:D.coerce.number().int().min(1).max(65535).optional().describe("Port to serve on (default: 4747)"),sessionDir:D.string().optional().describe("Override INSIGHT_SESSION_DIR: directory containing context-mode session .db files"),contentDir:D.string().optional().describe("Override INSIGHT_CONTENT_DIR: directory containing context-mode content/index .db files"),insightSessionDir:D.string().optional().describe("Alias for sessionDir / INSIGHT_SESSION_DIR"),insightContentDir:D.string().optional().describe("Alias for contentDir / INSIGHT_CONTENT_DIR")})},async({port:t,sessionDir:e,contentDir:r,insightSessionDir:n,insightContentDir:o})=>{let s=t||4747,i=e||n,a=r||o,c=Ie(et(St,"package.json"))?St:Dt(St),u=et(c,"insight"),l=i?et(i):Qe(),d=a?et(a):Ee(Dt(l),"content"),f=Ee(Dt(l),"insight-cache");if(!Ie(Ee(u,"server.mjs")))return B("ctx_insight",{content:[{type:"text",text:"Error: Insight source not found in plugin. Try upgrading context-mode."}]});try{let m=[],p=!1;li(f,{recursive:!0});let h=gk(Ee(u,"server.mjs")).mtimeMs,g=Ie(Ee(f,"server.mjs"))?gk(Ee(f,"server.mjs")).mtimeMs:0;if(h>g&&(m.push("Copying source files..."),ID(u,f,{recursive:!0,force:!0}),m.push("Source files copied."),p=!0),!Ie(Ee(f,"node_modules"))||p){m.push("Installing dependencies (first run, ~30s)...");try{yk(process.platform==="win32"?"npm.cmd install --production=false":"npm install --production=false",{cwd:f,stdio:"pipe",timeout:3e5})}catch{try{Mc(Ee(f,"node_modules"),{recursive:!0,force:!0})}catch{}throw new Error("npm install failed \u2014 please retry")}if(!Ie(Ee(f,"node_modules","vite"))||!Ie(Ee(f,"node_modules","better-sqlite3")))throw Mc(Ee(f,"node_modules"),{recursive:!0,force:!0}),new Error("npm install incomplete \u2014 please retry");m.push("Dependencies installed.")}m.push("Building dashboard..."),yk("npx vite build",{cwd:f,stdio:"pipe",timeout:6e4}),m.push("Build complete.");let _=!1;try{let{request:I}=await import("node:http");await new Promise((Z,T)=>{let P=I(`http://127.0.0.1:${s}/api/overview`,{timeout:2e3},V=>{V.resume(),Z()});P.on("error",()=>T()),P.on("timeout",()=>{P.destroy(),T()}),P.end()}),_=!0}catch{}if(_&&p){m.push("Killing stale dashboard server (source updated)...");let I=Nk(s);if(I.attemptedPids.length>0&&I.killedPids.length===0)return B("ctx_insight",{content:[{type:"text",text:`Could not free port ${s} (kill failed for ${I.attemptedPids.join(", ")}: ${I.errors.join("; ")}). Try ctx_insight({ port: ${s+1} }) or stop the process manually.`}]});if(I.errors.length>0&&I.attemptedPids.length===0)return B("ctx_insight",{content:[{type:"text",text:`Cannot reclaim port ${s}: ${I.errors.join("; ")}. Stop the process manually or pick another port.`}]});await new Promise(Z=>setTimeout(Z,500)),m.push(`Stale server killed (${I.killedPids.length} pid${I.killedPids.length===1?"":"s"}).`)}else if(_){m.push("Dashboard already running.");let I=`http://localhost:${s}`,Z=Tk(I),T=Z.ok?"":` (auto-open failed: ${Z.reason}; navigate manually)`;return B("ctx_insight",{content:[{type:"text",text:`Dashboard already running at ${I}${T}`}]})}if(br&&br.pid&&!br.killed)try{br.kill("SIGTERM")}catch{}let{spawn:x}=await import("node:child_process"),b=x("node",[Ee(f,"server.mjs")],{cwd:f,env:{...process.env,PORT:String(s),INSIGHT_SESSION_DIR:l,INSIGHT_CONTENT_DIR:d,INSIGHT_PARENT_PID:String(process.pid)},detached:!0,stdio:"ignore"});b.on("error",()=>{}),b.unref(),br=b,await new Promise(I=>setTimeout(I,1500));try{let{request:I}=await import("node:http");await new Promise((Z,T)=>{let P=I(`http://127.0.0.1:${s}/api/overview`,{timeout:3e3},V=>{Z(),V.resume()});P.on("error",T),P.on("timeout",()=>{P.destroy(),T(new Error("timeout"))}),P.end()})}catch{return B("ctx_insight",{content:[{type:"text",text:`Port ${s} appears to be in use. Either a previous dashboard is still running, or another service is using this port.
|