bridge-agent 0.4.4 → 0.4.5
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/bridge-mcp.cjs +4 -4
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/bridge-mcp.cjs
CHANGED
|
@@ -22353,7 +22353,7 @@ async function pollAgentMessages(ctx, since) {
|
|
|
22353
22353
|
);
|
|
22354
22354
|
}
|
|
22355
22355
|
async function listAgents(ctx) {
|
|
22356
|
-
return request(ctx, "GET",
|
|
22356
|
+
return request(ctx, "GET", workspacePath(ctx, "/agents"));
|
|
22357
22357
|
}
|
|
22358
22358
|
async function getAgentStatus(ctx, agentId) {
|
|
22359
22359
|
return request(ctx, "GET", projectPath(ctx, `/agents/${agentId}`));
|
|
@@ -22382,7 +22382,7 @@ async function getAgentOutput(ctx, agentId, lines) {
|
|
|
22382
22382
|
return request(ctx, "GET", projectPath(ctx, `/agents/${agentId}/output${qs}`));
|
|
22383
22383
|
}
|
|
22384
22384
|
async function sendAgentInput(ctx, agentId, text) {
|
|
22385
|
-
return request(ctx, "POST",
|
|
22385
|
+
return request(ctx, "POST", workspacePath(ctx, `/agents/${agentId}/input`), { text });
|
|
22386
22386
|
}
|
|
22387
22387
|
async function getAgentIdle(ctx, agentId) {
|
|
22388
22388
|
return request(ctx, "GET", projectPath(ctx, `/agents/${agentId}/idle`));
|
|
@@ -22569,7 +22569,7 @@ function registerOrchestrationTools(server, ctx) {
|
|
|
22569
22569
|
);
|
|
22570
22570
|
server.tool(
|
|
22571
22571
|
"bridge_list_agents",
|
|
22572
|
-
'List all active agents in this project. Each entry includes agentId, agentKey, status (idle/busy), assignedTodo, role, runnerCmd, and
|
|
22572
|
+
'List all active agents in this workspace (cross-project). Each entry includes agentId, agentKey, status (idle/busy/disconnected), assignedTodo, role, runnerCmd, projectId, inRun, state (idle/working), lastOutputAt, and contextUsedPct (0\u2013100, null if not a Claude agent). inRun:true means the agent is already registered in the active run. inRun:false means it was spawned after the run started or in a previous session \u2014 it can still be assigned (auto-registered on first assign). Returns agents from all projects in the workspace \u2014 not just the current project. Runner agents (role:"runner") are dev server consoles \u2014 use bridge_get_agent_output to read them.',
|
|
22573
22573
|
{},
|
|
22574
22574
|
() => safe(() => listAgents(ctx))
|
|
22575
22575
|
);
|
|
@@ -22699,7 +22699,7 @@ function registerWorkspaceTools(server, ctx) {
|
|
|
22699
22699
|
);
|
|
22700
22700
|
server.tool(
|
|
22701
22701
|
"bridge_dispatch_to_group",
|
|
22702
|
-
"Send a text message or command to every agent in a group via PTY stdin.",
|
|
22702
|
+
"Send a text message or command to every agent in a group via PTY stdin. Returns 409 agents_not_idle if any group member is mid-output (last PTY output within 3s) \u2014 retry after a delay.",
|
|
22703
22703
|
{
|
|
22704
22704
|
groupId: external_exports.string().describe("The target group ID"),
|
|
22705
22705
|
text: external_exports.string().min(1).max(4096).describe("Text to send to all group members")
|
package/dist/index.js
CHANGED
|
@@ -419,4 +419,4 @@ ${a} </dict>
|
|
|
419
419
|
</plist>
|
|
420
420
|
`;try{return(0,T.writeFileSync)(t,f,"utf-8"),!0}catch(p){return console.warn("[bridge] launchd.plist.write.failed",{error:String(p)}),!1}}function mc(){let r=ke(),e=j.default.join(Nt,r);try{return(0,ue.execSync)(`launchctl kickstart -kp gui/$(id -u)/${r} 2>/dev/null; launchctl unload "${e}" 2>/dev/null; launchctl load "${e}"`,{stdio:"pipe"}),{ok:!0,permissionDenied:!1}}catch(t){let n=String(t);return{ok:!1,permissionDenied:n.includes("Permission denied")||n.includes("not allowed")||n.includes("bootstrap")}}}function _c(r){let{out:e,err:t}=It();try{let n=(0,ue.spawn)(r,["start"],{detached:!0,stdio:"ignore",env:{...process.env,PATH:ni(),BRIDGE_DAEMON:"1"}});n.unref(),setTimeout(()=>{n.pid&&process.kill(n.pid,0)?(console.log("[bridge] daemon.pid",{pid:n.pid}),console.log("[bridge] background.ok",{log:e})):console.error("[bridge] background.failed \u2014 check: tail -f",{log:t})},2e3)}catch(n){console.error("[bridge] background.spawn.failed",{error:String(n)})}}function yc(){let r=parseInt(process.env.HEALTH_PORT??"3101",10),e=Date.now()+6e3,t=()=>{if(Date.now()>e){console.error("[bridge] health.verify.timeout \u2014 daemon may have crashed immediately");return}try{let s=require("node:http").get(`http://127.0.0.1:${r}/health`,i=>{i.statusCode===200?console.log("[bridge] health.verify.ok"):setTimeout(t,500)});s.on("error",()=>{setTimeout(t,500)}),s.setTimeout(1e3,()=>{s.destroy(),setTimeout(t,500)})}catch{setTimeout(t,500)}};setTimeout(t,1e3)}function bc(){ri();let r=new at;ei(r),r.startLivenessCheck(6e4);let e=parseInt(process.env.HEALTH_PORT??"3101",10),t=(0,ti.createServer)((n,s)=>{let i=Zs(),o=JSON.stringify({status:"ok",connected:i,uptime:process.uptime()});s.writeHead(i?200:503,{"Content-Type":"application/json"}),s.end(o)});t.listen(e,"127.0.0.1",()=>{console.log(`[bridge] health. listening on 127.0.0.1:${e}`)}),t.on("error",n=>{console.error("[bridge] health.error",{error:n.message})})}function si(){let r=process.env.BRIDGE_DAEMON==="1"||process.argv.includes("--daemon");if(!r&&!process.env.BRIDGE_PROFILE){let a=process.argv[1]??"";(a.includes("packages/daemon/dist")||a.includes("packages/daemon/src"))&&(console.warn("[bridge] WARNING: running monorepo daemon without --profile \u2014 will use prod config (~/.jerico/settings.json)."),console.warn("[bridge] If this is unintentional, stop and rerun with: node packages/daemon/dist/index.js --profile dev start"))}if(console.log("[bridge] Starting bridge-agent daemon..."),r){bc();return}fc()||(console.warn("[bridge] start.aborted.already.running"),process.exit(1));let e=pc(),t=gc(e),{ok:n,permissionDenied:s}=t?mc():{ok:!1,permissionDenied:!1},i=j.default.join(Nt,ke()),{out:o,err:c}=It();if(n){console.log("[bridge] launchd.ok \u2014 managed, auto-restart enabled"),console.log("[bridge] logs: tail -f",{out:o,err:c}),yc(),process.exit(0);return}s&&(console.warn("[bridge] launchd.permission.denied"),console.warn("[bridge] \u2192 Auto-start on login requires:"),console.warn(`[bridge] sudo launchctl bootstrap gui/$(id -u) "${i}"`),console.warn(`[bridge] Falling back to background process...
|
|
421
421
|
`)),_c(e),process.exit(0)}var ii=y(require("https")),oi=y(require("http"));xe();var wc="https://lcars.jerico.appnova.io";function Sc(r){return(r??"").trim()}async function ai(r,e=!1,t){let n=!(r&&r.trim()),s=n?wc:r.trim();console.log("[bridge] Starting auth flow..."),console.log(`[bridge] Server: ${s}${n?" (default)":""}`),console.log("[bridge] Open this URL to generate a daemon token:"),console.log(` ${s}/connect`);let i=Sc(t);i&&console.log("[bridge] Using token from --token"),e&&(i?console.log("[bridge] --no-browser ignored because --token is provided."):(console.log("[bridge] --no-browser: exiting after printing URL."),process.exit(0)));let o=i;o||(console.log(),console.log("[bridge] After authenticating, paste your token here:"),o=await vc()),o||(console.error("[bridge] No token provided. Exiting."),process.exit(1)),await Ec(s,o)||(console.error("[bridge] Token validation failed. Please try again."),process.exit(1));let l=s.replace(/^https?:\/\//,d=>d.startsWith("https")?"wss://":"ws://").replace(/\/?$/,"/ws/daemon");le({server:l,token:o,name:process.env.HOSTNAME??"My Machine"}),console.log("[bridge] Auth successful! Config saved to ~/.bridge/config.json"),console.log("[bridge] Run: bridge-agent start"),process.exit(0)}async function vc(){return new Promise(r=>{process.stdout.write("Token: ");let e="";process.stdin.setEncoding("utf-8"),process.stdin.on("data",t=>{e+=t,e.includes(`
|
|
422
|
-
`)&&(process.stdin.pause(),r(e.trim()))}),process.stdin.resume()})}async function Ec(r,e){return new Promise(t=>{let n=new URL("/api/tokens/validate",r),s=n.protocol==="https:",i=s?ii.default:oi.default,o={hostname:n.hostname,port:n.port||(s?443:80),path:n.pathname,method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`}},c=i.request(o,a=>{t(a.statusCode===200)});c.on("error",()=>t(!1)),c.end()})}var ci=y(require("https")),li=y(require("http")),Lr=y(require("fs")),Mr=y(require("path")),di=require("node:crypto");xe();function kc(r){return r.replace(/^wss?:/,e=>e==="wss:"?"https:":"http:").replace(/\/ws(\/.*)?$/,"")}async function ui(r,e,t){let n=ce(),s=(0,di.createHash)("sha256").update(n.token).digest("hex"),i=kc(n.server),o=Mr.default.resolve(t);Mr.default.isAbsolute(o)||(console.error("[bridge] link-project: path must be absolute"),process.exit(1)),Lr.default.existsSync(o)||(console.error("[bridge] link-project: path does not exist:",o),process.exit(1)),Lr.default.statSync(o).isDirectory()||(console.error("[bridge] link-project: path must be a directory:",o),process.exit(1));let a=new URL(`/api/workspaces/${r}/projects/${e}/machine-paths`,i),l=a.protocol==="https:",d=l?ci.default:li.default;n.projectPaths={...n.projectPaths??{},[e]:o},le(n),console.log("[cli] link-project.local_json_written",{projectId:e,path:o});let u=JSON.stringify({daemonId:s,localPath:o,machineFingerprint:jr()}),f=await new Promise((p,g)=>{let h=d.request({hostname:a.hostname,port:a.port||(l?443:80),path:a.pathname,method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`,"Content-Length":Buffer.byteLength(u)}},m=>{let k="";m.on("data",v=>{k+=v}),m.on("end",()=>{if(m.statusCode===200)p(200);else{try{let v=JSON.parse(k);console.error("[bridge] link-project failed:",v.error??`HTTP ${m.statusCode}`)}catch{console.error("[bridge] link-project failed:",`HTTP ${m.statusCode}`)}p(m.statusCode??0)}})});h.on("error",m=>{g(m)}),h.write(u),h.end()});f===200?(console.log("[cli] link-project.server_success",{projectId:e}),console.log("[cli] link-project.success (dual-write)"),console.log(` workspace: ${r}`),console.log(` project: ${e}`),console.log(` daemon: ${s.slice(0,16)}\u2026`),console.log(` path: ${o}`),console.log("[cli] Next spawn for this project will use the linked path."),process.exit(0)):(console.warn("[cli] link-project.server_fail",{projectId:e,statusCode:f}),console.log("[cli] Local override still active \u2014 path will work on this machine"),process.exit(0))}xe();function xc(r){return r.replace(/^wss?:/,e=>e==="wss:"?"https:":"http:").replace(/\/ws(\/.*)?$/,"")}async function hi(){let r=ce(),e=xc(r.server),t=await fetch(`${e}/api/admin/cleanup-orphans`,{method:"POST",headers:{Authorization:`Bearer ${r.token}`,"Content-Type":"application/json"},body:"{}"});t.ok||(console.error(`[cli] cleanup-orphans: HTTP ${t.status}`),process.exit(1));let{deleted:n}=await t.json();console.log(`[cli] cleanup-orphans: deleted ${n} orphaned path ${n===1?"entry":"entries"}`),process.exit(0)}var $r=require("node:child_process"),tt=require("node:fs"),Br=y(require("path"));qe();var pi=require("node:os"),Ic=Br.default.join((0,pi.homedir)(),"Library","LaunchAgents");function fi(){let r=ke(),e=r.replace(".plist",""),t=Br.default.join(Ic,r);try{(0,$r.execSync)(`launchctl bootout gui/$(id -u)/${e}`,{stdio:"pipe"}),console.log("[bridge] launchd.stopped \u2014 daemon unloaded")}catch{try{(0,tt.existsSync)(t)?((0,$r.execSync)(`launchctl unload "${t}"`,{stdio:"pipe"}),console.log("[bridge] launchd.unloaded \u2014 daemon stopped")):console.warn("[bridge] launchd.stop.failed \u2014 plist not found, daemon may not be running via launchd")}catch{console.warn("[bridge] launchd.stop.failed \u2014 daemon may not be running via launchd"),console.warn(`[bridge] Manual: launchctl bootout gui/$(id -u)/${e}`)}}let n=Ge();if((0,tt.existsSync)(n))try{(0,tt.unlinkSync)(n),console.log("[bridge] lock.cleaned")}catch{}}var ee=new tn;ee.name("bridge-agent").description("Bridge local agent \u2014 connects your AI tools to Jerico").version("0.4.
|
|
422
|
+
`)&&(process.stdin.pause(),r(e.trim()))}),process.stdin.resume()})}async function Ec(r,e){return new Promise(t=>{let n=new URL("/api/tokens/validate",r),s=n.protocol==="https:",i=s?ii.default:oi.default,o={hostname:n.hostname,port:n.port||(s?443:80),path:n.pathname,method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`}},c=i.request(o,a=>{t(a.statusCode===200)});c.on("error",()=>t(!1)),c.end()})}var ci=y(require("https")),li=y(require("http")),Lr=y(require("fs")),Mr=y(require("path")),di=require("node:crypto");xe();function kc(r){return r.replace(/^wss?:/,e=>e==="wss:"?"https:":"http:").replace(/\/ws(\/.*)?$/,"")}async function ui(r,e,t){let n=ce(),s=(0,di.createHash)("sha256").update(n.token).digest("hex"),i=kc(n.server),o=Mr.default.resolve(t);Mr.default.isAbsolute(o)||(console.error("[bridge] link-project: path must be absolute"),process.exit(1)),Lr.default.existsSync(o)||(console.error("[bridge] link-project: path does not exist:",o),process.exit(1)),Lr.default.statSync(o).isDirectory()||(console.error("[bridge] link-project: path must be a directory:",o),process.exit(1));let a=new URL(`/api/workspaces/${r}/projects/${e}/machine-paths`,i),l=a.protocol==="https:",d=l?ci.default:li.default;n.projectPaths={...n.projectPaths??{},[e]:o},le(n),console.log("[cli] link-project.local_json_written",{projectId:e,path:o});let u=JSON.stringify({daemonId:s,localPath:o,machineFingerprint:jr()}),f=await new Promise((p,g)=>{let h=d.request({hostname:a.hostname,port:a.port||(l?443:80),path:a.pathname,method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.token}`,"Content-Length":Buffer.byteLength(u)}},m=>{let k="";m.on("data",v=>{k+=v}),m.on("end",()=>{if(m.statusCode===200)p(200);else{try{let v=JSON.parse(k);console.error("[bridge] link-project failed:",v.error??`HTTP ${m.statusCode}`)}catch{console.error("[bridge] link-project failed:",`HTTP ${m.statusCode}`)}p(m.statusCode??0)}})});h.on("error",m=>{g(m)}),h.write(u),h.end()});f===200?(console.log("[cli] link-project.server_success",{projectId:e}),console.log("[cli] link-project.success (dual-write)"),console.log(` workspace: ${r}`),console.log(` project: ${e}`),console.log(` daemon: ${s.slice(0,16)}\u2026`),console.log(` path: ${o}`),console.log("[cli] Next spawn for this project will use the linked path."),process.exit(0)):(console.warn("[cli] link-project.server_fail",{projectId:e,statusCode:f}),console.log("[cli] Local override still active \u2014 path will work on this machine"),process.exit(0))}xe();function xc(r){return r.replace(/^wss?:/,e=>e==="wss:"?"https:":"http:").replace(/\/ws(\/.*)?$/,"")}async function hi(){let r=ce(),e=xc(r.server),t=await fetch(`${e}/api/admin/cleanup-orphans`,{method:"POST",headers:{Authorization:`Bearer ${r.token}`,"Content-Type":"application/json"},body:"{}"});t.ok||(console.error(`[cli] cleanup-orphans: HTTP ${t.status}`),process.exit(1));let{deleted:n}=await t.json();console.log(`[cli] cleanup-orphans: deleted ${n} orphaned path ${n===1?"entry":"entries"}`),process.exit(0)}var $r=require("node:child_process"),tt=require("node:fs"),Br=y(require("path"));qe();var pi=require("node:os"),Ic=Br.default.join((0,pi.homedir)(),"Library","LaunchAgents");function fi(){let r=ke(),e=r.replace(".plist",""),t=Br.default.join(Ic,r);try{(0,$r.execSync)(`launchctl bootout gui/$(id -u)/${e}`,{stdio:"pipe"}),console.log("[bridge] launchd.stopped \u2014 daemon unloaded")}catch{try{(0,tt.existsSync)(t)?((0,$r.execSync)(`launchctl unload "${t}"`,{stdio:"pipe"}),console.log("[bridge] launchd.unloaded \u2014 daemon stopped")):console.warn("[bridge] launchd.stop.failed \u2014 plist not found, daemon may not be running via launchd")}catch{console.warn("[bridge] launchd.stop.failed \u2014 daemon may not be running via launchd"),console.warn(`[bridge] Manual: launchctl bootout gui/$(id -u)/${e}`)}}let n=Ge();if((0,tt.existsSync)(n))try{(0,tt.unlinkSync)(n),console.log("[bridge] lock.cleaned")}catch{}}var ee=new tn;ee.name("bridge-agent").description("Bridge local agent \u2014 connects your AI tools to Jerico").version("0.4.5").option("--profile <name>","Config profile name (e.g. dev). Isolates config, lock, and fingerprint from the default prod profile.").hook("preAction",r=>{let e=r.opts().profile;e&&(process.env.BRIDGE_PROFILE=e)});ee.command("start").description("Start the bridge-agent daemon").option("--health-port <port>","Health check HTTP port (default: 3101, or 3101+offset per profile)").action(r=>{r.healthPort&&(process.env.HEALTH_PORT=r.healthPort),si()});ee.command("auth").description("Authenticate with Bridge server").option("-s, --server <url>","Server URL (default: https://lcars.jerico.appnova.io)").option("-t, --token <token>","Use token non-interactively").option("--no-browser","Print auth URL without opening browser or interactive prompt").action(r=>{ai(r.server,!r.browser,r.token)});ee.command("link-project <workspace-id> <project-id> <local-path>").description("Link a local directory to a project for this machine (Issue #152)").action((r,e,t)=>{ui(r,e,t)});ee.command("cleanup-orphans").description("Remove orphaned daemon_project_paths rows for this user").action(()=>{hi()});ee.command("status").description("Show connection status").action(async()=>{try{let{loadConfig:r}=await Promise.resolve().then(()=>(xe(),Us)),e=r();console.log("[bridge] Config found"),console.log(" Server:",e.server),console.log(" Name:",e.name)}catch{console.log("[bridge] Not authenticated. Run: bridge-agent auth")}});ee.command("stop").description("Stop the bridge-agent daemon").action(()=>{fi()});ee.parse();
|