iroh-room-bridge 0.1.0 → 0.1.1
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/README.md +1 -1
- package/dist/cli.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ npx iroh-room-bridge --room 我的房间 --nick 小派 \
|
|
|
9
9
|
--brain acp --acp-bin claude-code-acp
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
- 第一次运行不用手抄配置:本机缺配置时自动从 `https://
|
|
12
|
+
- 第一次运行不用手抄配置:本机缺配置时自动从 `https://agentchat.stream/relay-config.json`
|
|
13
13
|
补齐中继/入口/历史(已有配置不动)。
|
|
14
14
|
- 需要另一个静态二进制 `iroh-agent` 当"腿"(协议实现):
|
|
15
15
|
`curl -sSL https://get.editor.vip/iroh/agent-install.sh | bash`(或 `irm …/agent-install.ps1 | iex`)。
|
package/dist/cli.mjs
CHANGED
|
@@ -8,7 +8,7 @@ var Y=(e,t,r)=>()=>{if(e)try{t=e(e=0)}catch(n){r=[n]}if(r)throw r[0];return t};i
|
|
|
8
8
|
(上面这轮还没答完就超时了,我先把它停住)`:`这轮超时了(${a}),我先停下它。`;if(o)return o;throw Error(`ACP 这一轮失败:${a}`)}return o||null}async close(){if(this.#n)this.#g("session/cancel",{sessionId:this.#n});this.#s?.close(),this.#t?.stdin.end(),this.#t?.kill("SIGTERM");let e=this.#t;setTimeout(()=>e?.kill("SIGKILL"),1500).unref?.(),this.#t=null,this.#n=null}}async function W(e){if(!e.cwd?.startsWith("/"))throw Error("--acp-cwd 必须是绝对路径");return new J(e)}var Se,Pe;var q=Y(()=>{Se=new Set(["read","search","think"]),Pe=["Read","Glob","Grep","LS","List","NotebookRead"]});import{readFileSync as Me}from"node:fs";import w from"node:process";import{Buffer as F}from"node:buffer";import{join as Q}from"node:path";class M{#e;#t=new Set;#s=[];#n=0;#u=[];#a=0;#l=!1;#r=!1;#p=0;#c=new Set;#m=[];#o=new Set;constructor(e){this.#e=e}start(){if(this.#l)return;this.#l=!0,this.#e.client.on("event",this.#g)}async stop(){await this.#e.brain.close?.()}#i=(...e)=>{this.#e.log?.(...e)};#g=(e)=>{if(e.type==="message"&&!e.mine){this.#f(e.room,e.message);return}if(e.type==="fileInvite"){this.#w(e.room,e.meta);return}if(e.type==="fatal")this.#i(`[adapter] fatal:${e.reason}(等待自动重启)`)};async#w(e,t){if(!this.#d(t.file_id))return;let r={room:e,fileId:t.file_id,name:t.name,size:t.size,mime:t.mime??"",sender:t.sender},n;try{n=this.#e.onFileInvite?await this.#e.onFileInvite(r):this.#b(r)}catch(o){this.#h(r.fileId),this.#i(`[adapter] 文件策略异常(本次忽略 ${r.name}):${o.message}`);return}if(n==="ignore"){this.#i(`[adapter] 文件邀约(忽略):${r.name}(${r.size} 字节,来自 ${r.sender.slice(0,8)}…)`);return}if(n==="reject"){let o=this.#$(r);try{await this.#e.client.rejectFile(r.fileId,o),this.#i(`[adapter] 已拒绝 ${r.name}:${o}`)}catch(a){this.#i(`[adapter] 拒绝失败(忽略):${a.message}`)}return}let s=this.#e.filesMaxConcurrent??2;if(this.#o.size>=s){try{await this.#e.client.rejectFile(r.fileId,"接收忙,稍后请重发")}catch{}this.#i(`[adapter] 并发已满(${s}),拒绝 ${r.name}`);return}let i=this.#e.filesMaxTotalBytes??536870912;if(this.#p+r.size>i){try{await this.#e.client.rejectFile(r.fileId,`本端累计接收已达上限(${v(i)}),请先清理`)}catch{}this.#i(`[adapter] 累计接收已达上限(${v(this.#p)} + ${v(r.size)} > ${v(i)}),拒绝 ${r.name}`);return}this.#o.add(r.fileId);try{let o=this.#e.filesSaveDir?Q(this.#e.filesSaveDir,Z(r.name)):void 0,a=await this.#e.client.acceptFile(r.fileId,o);this.#p+=a.bytes,this.#i(`[adapter] 已接收 ${r.name} → ${a.path}(${a.bytes} 字节)`)}catch(o){this.#h(r.fileId),this.#i(`[adapter] 接收 ${r.name} 失败:${o.message}`)}finally{this.#o.delete(r.fileId)}}#b(e){let t=this.#e.files??"off";if(t==="off")return"ignore";if(t==="reject")return"reject";let r=this.#e.filesAllowFrom;if(r&&r.length>0&&!r.includes(e.sender))return"reject";if(e.size>(this.#e.filesMaxBytes??67108864))return"reject";return"accept"}#$(e){if((this.#e.files??"off")==="reject")return"文件接收已关闭";let t=this.#e.filesAllowFrom;if(t&&t.length>0&&!t.includes(e.sender))return"发送方不在白名单";let r=this.#e.filesMaxBytes??67108864;if(e.size>r)return`文件超过大小上限(${v(e.size)} > ${v(r)})`;return"文件接收已关闭"}#d(e){if(this.#c.has(e))return!1;if(this.#c.add(e),this.#m.push(e),this.#m.length>128){let t=this.#m.shift();if(t)this.#c.delete(t)}return!0}#h(e){this.#c.delete(e),this.#m=this.#m.filter((t)=>t!==e)}async#f(e,t){if((this.#e.ignoreBotPrefixes??["[bot]"]).some((a)=>t.nickname.startsWith(a)))return;if(!this.#k(t.id))return;let n=this.#x(t);if(n===null)return;let s=this.#e.cooldownMs??3000,i=Date.now();if(i-this.#n<s){this.#i(`[adapter] 冷却中(${s-(i-this.#n)}ms),忽略来自 ${t.nickname} 的触发`);return}if(this.#r){this.#i(`[adapter] 上一轮还没处理完,忽略来自 ${t.nickname} 的触发`);return}let o=this.#e.budgetTurns??0;if(o>0){let a=this.#e.budgetWindowMs??3600000;if(this.#u=this.#u.filter((l)=>i-l<a),this.#u.length>=o){let l=Math.max(1,Math.round(a/60000));if(this.#i(`[adapter] 预算用尽:最近 ${l} 分钟内已起 ${o} 轮,忽略来自 ${t.nickname} 的触发`),i-this.#a>60000){this.#a=i;try{await this.#e.client.say(`⏳ 用量预算用尽(最近 ${l} 分钟 ${o} 轮)—— 稍后再来,或让所有者调高 --budget-turns`)}catch(c){this.#i(`[adapter] 预算提示发送失败:${c.message}`)}}return}this.#u.push(i)}this.#n=i,this.#r=!0;try{await this.#v(e,t,n)}finally{this.#r=!1}}async#v(e,t,r){let n={room:e,id:t.id,from:t.from,nickname:t.nickname,text:r,ts:t.ts,agentNick:this.#e.client.hello?.nickname??"",raw:t},s={say:(l)=>this.#e.client.say(l),history:(l,c)=>this.#e.client.history(l,c),status:()=>this.#e.client.status(),log:(...l)=>this.#i("[brain]",...l)},i;try{i=await this.#e.brain.onMessage(n,s)}catch(l){this.#i(`[adapter] brain 处理失败(本次不回):${l.message}`);return}if(typeof i!=="string")return;let o=i.trim();if(!o)return;let a=ee(o,this.#e.maxReplyBytes??30000);if(a!==o)this.#i(`[adapter] 回复超过上限,已截断到 ${this.#e.maxReplyBytes??30000} 字节`);try{let l=await this.#e.client.say(a);this.#i(`[adapter] 已回复 ${n.nickname}:id=${l.id}`)}catch(l){this.#i(`[adapter] 发送失败:${l.message}`)}}#x(e){let t=String(e.text??"").trim(),r=String(this.#e.client?.hello?.endpointId??"");if(r&&Array.isArray(e.mentions)&&e.mentions.includes(r))return this.#y(t);let n=this.#y(t);if(n!==t)return n;let s=this.#e.mention??null;if(s&&t.includes(`@${s}`))return t.replaceAll(`@${s}`,"").trim();if(this.#e.respondToAll)return t;return null}#y(e){let t=this.#A();if(!t)return e;return e.replace(new RegExp(`@${t}(?![0-9a-f])`,"g"),"").trim()}#A(){let e=this.#e.client?.hello?.endpointId??"";return String(e).slice(0,8).toLowerCase()}#k(e){if(this.#t.has(e))return!1;if(this.#t.add(e),this.#s.push(e),this.#s.length>1024){let t=this.#s.shift();if(t)this.#t.delete(t)}return!0}}function v(e){if(e>=1073741824)return`${(e/1024/1024/1024).toFixed(1)} GiB`;if(e>=1048576)return`${(e/1024/1024).toFixed(1)} MiB`;if(e>=1024)return`${(e/1024).toFixed(1)} KiB`;return`${e} B`}function Z(e){let r=[...e.split(/[/\\]/).pop()?.trim()??""].filter((n)=>n.charCodeAt(0)>=32).join("");if(!r||r==="."||r==="..")return"file";return r}function ee(e,t){if(F.byteLength(e,"utf8")<=t)return e;let r=0,n=e.length;while(r<n){let s=r+n+1>>1;if(F.byteLength(e.slice(0,s),"utf8")<=t)r=s;else n=s-1}return`${e.slice(0,r)}
|
|
9
9
|
…(已截断)`}import{spawn as te}from"node:child_process";import{EventEmitter as re,once as N}from"node:events";import ne from"node:process";import{createInterface as L}from"node:readline";var A="v6";var k=1;class d extends Error{code;constructor(e,t){super(`${e}: ${t}`);this.name="AgentError",this.code=e}}var ie=[1000,2000,4000,8000,15000,30000];function R(e){return new Promise((t)=>setTimeout(t,e))}class E extends re{#e;#t=null;#s=new Map;#n=[];#u=0;#a=null;#l=!1;#r=!1;#p=0;#c=null;#m=0;#o;constructor(e){super();this.#e=e,this.#o=e.log??((t)=>console.error(t))}get hello(){return this.#a}get running(){return this.#t!==null&&this.#a!==null}async start(){this.#r=!1,this.#i();let e=await this.#d(60000);if(e.chatProtocol!==A)throw new d("protocolMismatch",`agent 报告 chatProtocol=${e.chatProtocol},适配器只支持 ${A};请同步升级(见 docs/deploy.md)`);if(e.v!==k)throw new d("protocolMismatch",`线协议 v${e.v} != v${k}`);return this.#x(),e}request(e,t={},r){let n=this.#t;if(!n||!this.#a)return Promise.reject(new d("notRunning","agent 进程未就绪"));let s=String(++this.#u),i=JSON.stringify({v:k,id:s,cmd:e,...t});return new Promise((o,a)=>{let l=setTimeout(()=>{this.#s.delete(s),a(new d("timeout",`${e} 超过 ${r??this.#e.requestTimeoutMs??120000}ms 未回复`))},r??this.#e.requestTimeoutMs??120000);this.#s.set(s,{resolve:o,reject:a,timer:l});try{n.stdin.write(`${i}
|
|
10
10
|
`,(c)=>{if(!c)return;clearTimeout(l),this.#s.delete(s),a(c)})}catch(c){clearTimeout(l),this.#s.delete(s),a(c)}})}say(e,t){let r=t?.length?{text:e,mentions:t}:{text:e};return this.request("say",r,60000)}acceptFile(e,t){return this.request("accept_file",t?{fileId:e,savePath:t}:{fileId:e},600000)}rejectFile(e,t){return this.request("reject_file",t?{fileId:e,reason:t}:{fileId:e},30000)}history(e=200,t){return this.request("history",t?{limit:e,before:t}:{limit:e},60000)}status(){return this.request("status",{},30000)}ping(){return this.request("ping",{},1e4)}async stop(){this.#r=!0,this.#y();let e=this.#t;if(!e)return;try{await this.request("shutdown",{reason:"adapter stopping"},5000)}catch{}if(!await Promise.race([N(e,"close").then(()=>!0),R(5000).then(()=>!1)]))this.#o("[client] agent 进程 5s 未退出,SIGKILL"),e.kill("SIGKILL"),await Promise.race([N(e,"close"),R(2000)]);this.#v(new d("stopped","客户端已停止"))}#i(){let e=["serve",...this.#e.args??[]];this.#o(`[client] 启动:${this.#e.bin} ${e.join(" ")}`);let t=te(this.#e.bin,e,{env:{...ne.env,...this.#e.env},stdio:["pipe","pipe","pipe"]});this.#t=t,this.#a=null,L({input:t.stdout}).on("line",(r)=>this.#g(r)),L({input:t.stderr}).on("line",(r)=>this.#o(`[agent] ${r}`)),t.on("error",(r)=>{this.#o(`[client] 进程错误:${r.message}`),this.#f(new d("spawnFailed",r.message))}),t.on("close",(r,n)=>this.#b(r,n))}#g(e){let t=e.trim();if(!t)return;let r;try{r=JSON.parse(t)}catch{this.#o(`[client] 非协议行(已丢弃):${t.slice(0,200)}`);return}switch(r.type){case"hello":this.#a=r,this.#l=!0,this.#h(r),this.emit("hello",r);break;case"reply":this.#w(r);break;case"event":this.emit("event",r.event);break;default:this.#o(`[client] 未知帧类型:${JSON.stringify(r).slice(0,200)}`)}}#w(e){if(e.id==null){this.#o(`[client] 无 id 的 reply(已丢弃):${e.error?.code??""}`);return}let t=this.#s.get(e.id);if(!t){this.#o(`[client] 未知 reply id=${e.id}(已丢弃)`);return}if(this.#s.delete(e.id),clearTimeout(t.timer),e.ok)t.resolve(e.value);else t.reject(new d(e.error?.code??"internal",e.error?.message??""))}#b(e,t){let r=this.#l;if(this.#o(`[client] agent 进程退出(code=${e} signal=${t})`),this.#t=null,this.#a=null,this.#y(),this.#v(new d("processExited","agent 进程已退出")),this.#f(new d("processExited","agent 进程已退出")),this.emit("exit",{code:e,signal:t}),!this.#r&&r)this.#$()}async#$(){while(!this.#r){let e=this.#e.restartBackoffMs??ie,t=e[Math.min(this.#p,e.length-1)];if(this.#p+=1,this.#o(`[client] ${t}ms 后重启(第 ${this.#p} 次)`),await R(t),this.#r)return;this.#i();try{let r=await this.#d(60000);this.#o(`[client] 已重启并重新握手:endpointId=${r.endpointId}`),this.#p=0,this.emit("restarted",r);return}catch(r){this.#o(`[client] 重启失败:${r.message}`)}}}#d(e){if(this.#a)return Promise.resolve(this.#a);return new Promise((t,r)=>{let n=setTimeout(()=>{this.#n=this.#n.filter((s)=>s.timer!==n),r(new d("helloTimeout",`${e}ms 内没有收到 hello`))},e);this.#n.push({resolve:t,reject:r,timer:n})})}#h(e){let t=this.#n;this.#n=[];for(let r of t)clearTimeout(r.timer),r.resolve(e)}#f(e){let t=this.#n;this.#n=[];for(let r of t)clearTimeout(r.timer),r.reject(e)}#v(e){for(let t of this.#s.values())clearTimeout(t.timer),t.reject(e);this.#s.clear()}#x(){let e=this.#e.pingIntervalMs??45000;if(e<=0)return;this.#y(),this.#m=0,this.#c=setInterval(()=>{this.ping().then(()=>{this.#m=0}).catch((t)=>{if(this.#r)return;if(this.#m+=1,this.#o(`[client] ping 失败 ${this.#m}/2:${t.message}`),this.#m>=2)this.#m=0,this.#o("[client] ping 连续失败,强制重启 agent 进程"),this.#t?.kill("SIGKILL")})},e),this.#c.unref?.()}#y(){if(this.#c)clearInterval(this.#c),this.#c=null}}import{spawn as se}from"node:child_process";import oe from"node:process";function I(e,t){return e.replaceAll("{text}",t.text).replaceAll("{raw}",t.rawText??t.text).replaceAll("{nick}",t.nickname).replaceAll("{room}",t.room).replaceAll("{bot}",t.agentNick??"")}class C{#e;#t;#s;#n;#u;#a;#l;#r;#p=Promise.resolve();constructor(e){this.#e=e.command,this.#t=e.args??[],this.#s=e.promptTemplate??"{text}",this.#n=e.input??"stdin",this.#u=e.timeoutMs??120000,this.#a=e.cwd,this.#l=e.env,this.#r=e.maxOutputBytes??32768}onMessage(e,t){let r=I(this.#s,{...e,rawText:e.raw.text}),n=this.#p.then(()=>this.#c(r,t));return this.#p=n.catch(()=>{return}),n}#c(e,t){return new Promise((r)=>{let n=[...this.#t];if(this.#n==="arg"){let p=!1;if(n=n.map((m)=>{if(!m.includes("{prompt}"))return m;return p=!0,m.replaceAll("{prompt}",e)}),!p)n.push(e)}let s;try{s=se(this.#e,n,{cwd:this.#a,env:{...oe.env,...this.#l},stdio:["pipe","pipe","pipe"]})}catch(p){t.log(`[command] 启动失败:${p.message}`),r(null);return}let i="",o="",a=!1,l=!1,c=setTimeout(()=>{l=!0,s.kill("SIGKILL")},this.#u);if(s.stdout?.on("data",(p)=>{if(i.length>=this.#r*2){a=!0;return}i+=p.toString("utf8")}),s.stderr?.on("data",(p)=>{if(o.length<4096)o+=p.toString("utf8")}),s.on("error",(p)=>{clearTimeout(c),t.log(`[command] ${this.#e} 出错:${p.message}`),r(null)}),s.on("close",(p)=>{if(clearTimeout(c),l){t.log(`[command] ${this.#e} 超时(${this.#u}ms),已杀`),r(null);return}if(p!==0){t.log(`[command] ${this.#e} 退出码 ${p}:${o.trim().slice(0,500)}`),r(null);return}let m=i.trim();if(!m){r(null);return}if(Buffer.byteLength(m,"utf8")>this.#r||a){r(m.slice(0,this.#r));return}r(m)}),this.#n==="stdin")s.stdin?.write(e);s.stdin?.end()})}}function j(e){if(!Array.isArray(e))throw Error("规则文件必须是 JSON 数组");return e.map((t,r)=>{let n=t,s=n?.match,i=n?.reply;if(typeof s!=="string")throw Error(`规则 #${r} 的 match 必须是字符串`);if(typeof i!=="string"&&typeof i!=="function")throw Error(`规则 #${r} 的 reply 必须是字符串`);return{match:ae(s),reply:i}})}function ae(e){if(e.length>=2&&e.startsWith("/")&&e.lastIndexOf("/")>0){let t=e.lastIndexOf("/"),r=e.slice(1,t),n=e.slice(t+1).replaceAll("g","").replaceAll("y","");return new RegExp(r,n)}return e}class T{#e;constructor(e){this.#e=e}async onMessage(e){for(let t of this.#e){if(!(typeof t.match==="string"?e.text.toLowerCase().includes(t.match.toLowerCase()):t.match.test(e.text)))continue;let n=typeof t.reply==="function"?t.reply(e):t.reply;return I(n,e)}return null}}function D(){return[{match:/^ping\b/i,reply:"pong"}]}import{chmodSync as le,mkdirSync as ce,readFileSync as pe,renameSync as me,writeFileSync as ue}from"node:fs";import{dirname as ge,join as x}from"node:path";import P from"node:process";function de(e=P.env){let t=e.IROH_AGENT_HOME?.trim();if(t)return t;if(P.platform==="win32"){let n=e.APPDATA?.trim()||x(e.USERPROFILE?.trim()||".","AppData","Roaming");return x(n,"iroh-agent")}let r=e.XDG_CONFIG_HOME?.trim()||x(e.HOME?.trim()||"/root",".config");return x(r,"iroh-agent")}function _(e){if(!e||typeof e!=="object")return null;let t=e,r=typeof t.id==="string"?t.id.trim():"",n=typeof t.relay==="string"?t.relay.trim():"";return r?{id:r,relay:n}:null}function he(e){if(!e||typeof e!=="object")return null;let t=e,r=Array.isArray(t.relays)?t.relays.map((n)=>{if(typeof n==="string")return n;if(n&&typeof n==="object"&&n.enabled!==!1){let s=n.url;return typeof s==="string"?s:""}return""}).map((n)=>n.trim()).filter((n)=>n.startsWith("https://")||n.startsWith("http://")):[];if(!r.length)return null;return{relays:r,relayToken:typeof t.relay_token==="string"?t.relay_token:"",rendezvous:_(t.rendezvous),history:_(t.history),anchor:_(t.anchor)}}function fe(e){return Array.isArray(e.relays)?e.relays.filter((t)=>typeof t==="string"&&t.trim()!==""):[]}function S(e,t){let r=e[t];return!!r&&typeof r==="object"&&typeof r.id==="string"&&!!r.id}function ye(e){let t=[];if(!fe(e).length)t.push("relays");if(!(typeof e.relay_token==="string"&&e.relay_token.trim()))t.push("relay_token");if(!S(e,"rendezvous")&&!S(e,"anchor"))t.push("rendezvous");if(!S(e,"history"))t.push("history");return t}function we(e){let t;try{t=pe(e,"utf8")}catch{return{cfg:{},corrupt:!1}}try{let r=JSON.parse(t);if(r&&typeof r==="object"&&!Array.isArray(r))return{cfg:r,corrupt:!1};return{cfg:{},corrupt:!0}}catch{return{cfg:{},corrupt:!0}}}async function H(e){let t=e.env??P.env,r=e.log??(()=>{}),n=(e.server??"").trim(),s=e.homeDir??de(t),i=x(s,"config.json");if(!n||n==="off")return{action:"disabled",path:i,filled:[]};for(let u of["IROH_AGENT_RELAY","IROH_AGENT_RENDEZVOUS_ID","IROH_AGENT_ANCHOR_ID"])if(t[u]?.trim())return{action:"skipped",path:i,filled:[],reason:`环境变量 ${u} 已提供配置`};let{cfg:o,corrupt:a}=we(i);if(a){let u=`${i}.corrupt-${Date.now()}`;try{me(i,u),r(`[bootstrap] ⚠️ ${i} 不是合法 JSON,已备份到 ${u},将重建`)}catch{r(`[bootstrap] ⚠️ ${i} 不是合法 JSON 且备份失败,将直接重建`)}}let l=ye(o);if(!l.length&&!e.refresh)return{action:"skipped",path:i,filled:[],reason:"本机已有可用配置"};let c=`${n.replace(/\/+$/,"")}/relay-config.json`,p=e.fetchImpl??fetch,m;try{let u=await p(c,{headers:{accept:"application/json"},signal:AbortSignal.timeout(e.timeoutMs??8000)});if(!u.ok)return{action:"failed",path:i,filled:[],reason:`${c} 返回 HTTP ${u.status}`};m=he(await u.json())}catch(u){return{action:"failed",path:i,filled:[],reason:`${c} 拉取失败(${u.message})`}}if(!m)return{action:"failed",path:i,filled:[],reason:`${c} 里没有可用中继`};let g={...o},h=[],f=(u)=>e.refresh===!0||l.includes(u);if(f("relays"))g.relays=m.relays,h.push("relays");if(f("relay_token")&&m.relayToken)g.relay_token=m.relayToken,h.push("relay_token");if(f("rendezvous")&&m.rendezvous)g.rendezvous=m.rendezvous,h.push("rendezvous");if(f("history")&&m.history)g.history=m.history,h.push("history");if((e.refresh===!0||!S(g,"anchor"))&&m.anchor)g.anchor=m.anchor,h.push("anchor");if(!h.length)return{action:"skipped",path:i,filled:[],reason:"拉到的字段本机都已存在"};try{if(ce(ge(i),{recursive:!0}),ue(i,`${JSON.stringify(g,null,2)}
|
|
11
|
-
`,{mode:384}),P.platform!=="win32")le(i,384)}catch(u){return{action:"failed",path:i,filled:[],reason:`写入 ${i} 失败(${u.message})`}}return{action:"fetched",path:i,filled:h}}function be(e,t){let r=String(e??"").replace(/[\r\n]+/g," ").trim().slice(0,160),n=new Set,s=[];for(let i of t??[]){let o=String(i??"").trim().slice(0,16);if(!o||n.has(o))continue;if(n.add(o),s.push(o),s.length>=8)break}return{bio:r,tags:s}}var $e=(e)=>new Promise((t)=>setTimeout(t,e));function G(e,t){let{bio:r,tags:n}=be(t.bio,t.tags);if(!r&&!n.length)return()=>{};let s=t.log??((l)=>console.error(l)),i=!1,o=async()=>{let l=Math.max(1,t.attempts??4);for(let c=1;c<=l;c+=1)try{let p=await e.request("profile",{bio:r,tags:n},15000),m=typeof p?.bio==="string"?p.bio:r,g=Array.isArray(p?.tags)?p.tags:n;s(`[cli] 名片已发布:bio=${JSON.stringify(m)}${g.length?` tags=${JSON.stringify(g)}`:""}`);return}catch(p){if((p.code??"")==="unsupportedCmd"){s("[cli] ⚠️ 这枚 iroh-agent 太老(没有 profile 命令),名片没发出去;"+"升级后重跑一次即可(见 docs/bring-your-own-agent.md)");return}if(c===l){s(`[cli] ⚠️ 名片发布失败(不影响对话):${p.message}`);return}await $e((t.retryDelayMs??800)*c)}},a=(l)=>{if(l?.type!=="joined"||i)return;i=!0,o().finally(()=>{i=!1})};return e.on("event",a),()=>e.off("event",a)}var Re="https://
|
|
11
|
+
`,{mode:384}),P.platform!=="win32")le(i,384)}catch(u){return{action:"failed",path:i,filled:[],reason:`写入 ${i} 失败(${u.message})`}}return{action:"fetched",path:i,filled:h}}function be(e,t){let r=String(e??"").replace(/[\r\n]+/g," ").trim().slice(0,160),n=new Set,s=[];for(let i of t??[]){let o=String(i??"").trim().slice(0,16);if(!o||n.has(o))continue;if(n.add(o),s.push(o),s.length>=8)break}return{bio:r,tags:s}}var $e=(e)=>new Promise((t)=>setTimeout(t,e));function G(e,t){let{bio:r,tags:n}=be(t.bio,t.tags);if(!r&&!n.length)return()=>{};let s=t.log??((l)=>console.error(l)),i=!1,o=async()=>{let l=Math.max(1,t.attempts??4);for(let c=1;c<=l;c+=1)try{let p=await e.request("profile",{bio:r,tags:n},15000),m=typeof p?.bio==="string"?p.bio:r,g=Array.isArray(p?.tags)?p.tags:n;s(`[cli] 名片已发布:bio=${JSON.stringify(m)}${g.length?` tags=${JSON.stringify(g)}`:""}`);return}catch(p){if((p.code??"")==="unsupportedCmd"){s("[cli] ⚠️ 这枚 iroh-agent 太老(没有 profile 命令),名片没发出去;"+"升级后重跑一次即可(见 docs/bring-your-own-agent.md)");return}if(c===l){s(`[cli] ⚠️ 名片发布失败(不影响对话):${p.message}`);return}await $e((t.retryDelayMs??800)*c)}},a=(l)=>{if(l?.type!=="joined"||i)return;i=!0,o().finally(()=>{i=!1})};return e.on("event",a),()=>e.off("event",a)}var Re="https://agentchat.stream",U=`agent-pi · 把 Agent 接进 iroh 聊天室(常驻成员)
|
|
12
12
|
|
|
13
13
|
用法:
|
|
14
14
|
room-bridge --room <房间> [--nick 名字] [--brain rule|command|pi-sdk|acp] [选项]
|
|
@@ -30,7 +30,7 @@ var Y=(e,t,r)=>()=>{if(e)try{t=e(e=0)}catch(n){r=[n]}if(r)throw r[0];return t};i
|
|
|
30
30
|
接入引导(第一次用建议带着):
|
|
31
31
|
--server URL 本机缺配置时,从这个站点拉 relay-config.json 自动补好
|
|
32
32
|
中继/令牌/入口/历史(默认 $IROH_AGENT_SERVER 或
|
|
33
|
-
https://
|
|
33
|
+
https://agentchat.stream;传 off 关闭)
|
|
34
34
|
--refresh-config 强制重新拉取并覆盖中继/令牌/入口/历史(nickname 不动)
|
|
35
35
|
|
|
36
36
|
名片(房间里的人靠"名字 / 简介 / 发言"认识它):
|