@sleep2agi/agent-node 2.5.0-preview.20 → 2.5.0-preview.22

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 CHANGED
@@ -44,7 +44,7 @@ CLI flags:
44
44
  |---|---|---|
45
45
  | `--alias` | required | unique name in the hub |
46
46
  | `--hub` | `http://127.0.0.1:9200` | CommHub URL |
47
- | `--runtime` | `claude-agent-sdk` | `claude-agent-sdk` / `codex-sdk` / `claude-code-cli` / `grok-build-acp` / `http-api` |
47
+ | `--runtime` | `claude-agent-sdk` | `claude-agent-sdk` / `codex-sdk` / `codex-app-server` / `claude-code-cli` / `grok-build-acp` / `http-api` |
48
48
  | `--model` | runtime default | passed through to the SDK |
49
49
  | `--tools` | (none) | `all` or comma-separated list |
50
50
  | `--max-turns` | `50` | upper bound per task |
@@ -56,6 +56,7 @@ CLI flags:
56
56
  |---|---|---|---|
57
57
  | `claude-agent-sdk` | [@anthropic-ai/claude-agent-sdk](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) | verified | Anthropic-compatible API; works with MiniMax, DeepSeek, GLM, Kimi, Anthropic, OpenRouter, or custom endpoints |
58
58
  | `codex-sdk` | [@openai/codex-sdk](https://www.npmjs.com/package/@openai/codex-sdk) | unverified end-to-end | unit tests pass, no full E2E with real codex auth |
59
+ | `codex-app-server` | local `codex app-server` + TUI bridge | preview; production locked | canonical RFC-030 runtime. The current CLI entry starts the Phase-0 direct path; Policy Gateway, §8 review, merge and production approval remain locked |
59
60
  | `claude-code-cli` | local `claude` CLI | unverified end-to-end | runs locally for Claude Pro subscribers (v0.8.2 fixed the session-resume default-loss bug; see [changelog](https://anet.sh/en/changelog)) |
60
61
  | `grok-build-acp` | local `grok agent stdio` | stable runtime, native MCP injection boundary remains preview | requires Grok Build CLI login; stable for receive/reply, session persistence, and explicit CommHub delegation handled by agent-node |
61
62
  | `http-api` | OpenAI/Anthropic-compatible HTTP | experimental | reads `ANTHROPIC_*`, `OPENAI_*`, or `MINIMAX_CODING_API_KEY` environment variables |
package/dist/cli.js CHANGED
@@ -131,7 +131,7 @@ ${B}`;break;case"retry":if(Fj(B))Q[E]=B;break;case"id":if(Yj(B))Q[E]=B;break;cas
131
131
  `));return I.name="CodexSdkUnavailable",I}function A0(A){if(!A)return"unknown";if(A instanceof Error)return A.message||A.name||"unknown Error";if(typeof A==="string")return A;try{return JSON.stringify(A).slice(0,240)}catch{return String(A)}}function _M(A){return/[^A-Za-z0-9_\-./@~]/.test(A)?`'${A.replace(/'/g,"'\\''")}'`:A}function kM(A){return async(Q,I)=>{let E=I.map(_M).join(" "),B=`npm install --no-save --prefix ${_M(Q)} ${E}`;A(B,{stdio:"pipe",timeout:90000})}}async function e9(A,Q,I={}){let E=Math.max(1,Math.min(100,I.limit??100)),B=Math.max(1,Math.min(50,I.topN??10));try{let g=I.serverSupportsFromNodeId===!0&&!!A.nodeId?{from_node_id:A.nodeId,limit:E}:{from_name:A.alias,limit:E},D=await Q(g),U=Array.isArray(D?.tasks)?D.tasks:[],F=(Y)=>{let N=typeof Y.from_node_id==="string"?Y.from_node_id:"",J=typeof Y.from_name==="string"?Y.from_name:"";if(N&&A.nodeId)return N===A.nodeId;if(J)return J===A.alias;return!1};return U.filter((Y)=>!!Y&&typeof Y.task_id==="string"&&typeof Y.to_name==="string"&&(Y.status==="delivered"||Y.status==="started")).filter(F).slice(0,B)}catch{return[]}}function A6(A){if(!A.length)return null;return["【⚠ 节点 resume 提醒 — 你停机前已派出但仍在等回复的任务】","",A.map((I,E)=>{let B=String(I.task_id||"?").slice(0,8),C=String(I.to_name||"?"),g=I.created_at?`, sent ${I.created_at}`:"",D=vk(I.content);return` ${E+1}. → ${C} [task ${B}${g}]: ${D}`}).join(`
132
132
  `),"","重要约束 (do NOT re-dispatch):","- 上面这些任务 **已经发出去过**, 对方尚未回复。**不要再次** 用 commhub_send_task 发同样内容。",'- 如果需要跟进进度, 用 commhub_send_message(alias=对方, message="任务 <task_id> 进度?") 发一条**轻问询**, **不是** send_task。',"- server 端有自动去重护栏会拒绝重复 send_task, 但请不要靠它兜底 — 直接按上面规则处理。",'- 如果用户现在派的新任务跟上面某条**实质相同**, 直接告诉用户 "task <id> 已派给 <对方>, 仍在等回复, 不重新派", 不要 send_task。'].join(`
133
133
  `)}function vk(A){let Q=String(A??"").replace(/```/g,"ʼʼʼ").replace(/\s+/g," ").trim(),I=120;return Q.length>120?Q.slice(0,117)+"...":Q||"(empty content)"}class OM{opts;cachedAlias;cachedAt;fetchInFlight=null;cacheTtlMs;constructor(A){this.opts=A;this.cachedAlias=A.initialAlias,this.cachedAt=0,this.cacheTtlMs=A.cacheTtlMs??30000}current(){return this.cachedAlias}async refresh(A=Date.now()){if(!this.opts.nodeId)return this.cachedAlias;let Q=A-this.cachedAt;if(this.cacheTtlMs>0&&this.cachedAt>0&&Q>=0&&Q<this.cacheTtlMs)return this.cachedAlias;if(this.fetchInFlight)return this.fetchInFlight;let I=(async()=>{try{let E=await this.opts.fetchCanonicalAlias(this.opts.nodeId);if(E&&E.length>0&&E!==this.cachedAlias)this.opts.onDrift?.(this.cachedAlias,E,"fetch"),this.cachedAlias=E}catch(E){this.opts.warn?.(`currentAlias: fetch failed (${E?.message??E}); keeping cached value "${this.cachedAlias}"`)}finally{this.cachedAt=A,this.fetchInFlight=null}return this.cachedAlias})();return this.fetchInFlight=I,I}set(A,Q=Date.now()){if(!A)return;if(A!==this.cachedAlias)this.opts.onDrift?.(this.cachedAlias,A,"snapshot"),this.cachedAlias=A;this.cachedAt=Q}get nodeId(){return this.opts.nodeId}ageMs(A=Date.now()){if(this.cachedAt===0)return Number.POSITIVE_INFINITY;return Math.max(0,A-this.cachedAt)}isFresh(A=Date.now()){if(this.cachedAt===0)return!1;let Q=A-this.cachedAt;return Q>=0&&Q<this.cacheTtlMs}}function Q6(A,Q,I){if(!Array.isArray(A))return{exists:!1,reason:"no_sessions_field"};if(A.length===0)return{exists:!1,reason:"empty_sessions"};let E=(Q??"").trim(),B=(I??"").trim();if(!E)return{exists:!1,reason:"not_in_sessions"};let C=!1;for(let g of A){let D=typeof g?.alias==="string"?g.alias.trim():"";if(!D)continue;if(D===E){if(D===B){C=!0;continue}return{exists:!0,source:"session"}}}return{exists:!1,reason:C?"self_only":"not_in_sessions"}}function ng(A){if(!A)return!1;return/\b(429|529)\b|rate[_\s-]?limit|quota[_\s-]?(exceeded|exhaust)|overloaded(_error)?|too[_\s-]?many[_\s-]?requests|usage[_\s-]?limit|insufficient[_\s-]?quota|capacity[_\s-]?exceeded|token\s*plan.*(上限|exceeded)/i.test(A)}function PM(A){if(!A||typeof A!=="string")return null;let Q=A.match(/\b(2056)\b/);if(Q)return`MiniMax code ${Q[1]}`;let I=A.match(/\b(insufficient_quota|rate_limit_exceeded|billing_hard_limit_reached)\b/);if(I)return I[1];let E=A.match(/\b(429|529)\b/);if(E)return`HTTP ${E[1]}`;return null}function xM(A){if(!A||typeof A!=="string")return null;let Q=A.match(/已达到[^。\n]{2,140}/);if(Q)return Q[0].slice(0,200);let I=A.match(/(insufficient_quota|exceeded[^\n]{2,140})/i);if(I)return I[0].slice(0,200);return null}function xI(A){let Q=(A||"").toLowerCase();if(Q.includes("intern-ai.org.cn"))return"→ 检查 chat.intern-ai.org.cn 配额/Token Plan 上限/降并发";if(Q.includes("minimaxi")||Q.includes("minimax"))return"→ 检查 platform.minimaxi.com 配额/Token Plan 上限/降并发";if(Q.includes("deepseek"))return"→ 检查 platform.deepseek.com 配额/账户余额/降并发";if(Q.includes("anthropic"))return"→ 检查 console.anthropic.com 配额/usage limit/降并发";if(Q.includes("xiaomimimo")||Q.includes("mimo"))return"→ 检查 platform.xiaomimimo.com 配额/降并发";return"→ 检查 vendor 配额/限流 dashboard / 降并发"}function yM(A,Q){let I=Q?.baseUrl;if(A.errorMessage){if(ng(A.errorMessage))return{kind:"soft-fail-quota",reason:A.errorMessage,hint:xI(I)};return{kind:"error",reason:A.errorMessage}}let E=A.usage??{},B=E.input_tokens??null,C=E.output_tokens??null,g=A.totalCostUsd??null;if(B===0&&C===0&&g===0)return{kind:"soft-fail-empty",reason:"vendor returned in=0 out=0 cost=0 — upstream silent reject (suspect 429/quota/auth without explicit error)",hint:xI(I)};if(A.result===null||A.result===void 0||A.result==="")return{kind:"soft-fail-empty",reason:"empty vendor result despite success signal",hint:xI(I)};return{kind:"success"}}function B0(A,Q){switch(A.kind){case"soft-fail-quota":{let I=A.reason||"",E=xM(I),B=PM(I),C=B?` [${B}]`:"",g=E??I.slice(0,80);return`[额度用尽]${C} ${g}`.trim()}case"soft-fail-empty":return"抱歉,本轮暂时没能给出答复,请再问我一次。";case"error":return"请求出错,请稍后重试。";case"success":return""}}function bM(A,Q){let I=Q.usage?.input_tokens??0,E=Q.usage?.output_tokens??0;switch(A.kind){case"soft-fail-quota":{let B=A.reason||"",C=xM(B),g=PM(B),D=g?` [${g}]`:"",U=C??B.slice(0,80);return`执行出错: [额度用尽]${D} ${Q.runtime}: ${U}${A.hint?` — ${A.hint}`:""}`}case"soft-fail-empty":return`执行出错: ${Q.runtime} 返回空响应 (in=${I} out=${E}). `+"可能原因: (a) vendor 内容过滤 (请求含 credential 字面, 见 [outbound-mask] 日志) "+"(b) 静默限流/配额撞顶 (查 vendor dashboard 用量) "+"(c) 模型仅生成 reasoning/thinking 无 text (加大 max_tokens 或换 prompt). "+`${A.hint||""}`.trim();case"error":return`执行出错: ${Q.runtime} — ${(A.reason||"未知错误").slice(0,200)}`;case"success":return""}}class aE extends Error{label;timeoutMs;constructor(A,Q){super(`${Q?`[${Q}] `:""}timed out after ${A}ms`);this.name="TimeoutError",this.label=Q,this.timeoutMs=A}}async function I0(A,Q,I,E){let B=new AbortController,C;if(E?.externalSignal)if(E.externalSignal.aborted)B.abort();else C=()=>B.abort(),E.externalSignal.addEventListener("abort",C,{once:!0});if(Q<=0)try{return await A(B.signal)}finally{if(C&&E?.externalSignal)E.externalSignal.removeEventListener("abort",C)}let g,D=new Promise((U,F)=>{g=setTimeout(()=>{B.abort(),F(new aE(Q,I))},Q)});try{return await Promise.race([A(B.signal),D])}finally{if(g)clearTimeout(g);if(C&&E?.externalSignal)E.externalSignal.removeEventListener("abort",C)}}function E0(A){let Q=A.minMs??0,I=A.maxMs??Number.MAX_SAFE_INTEGER,E=(C)=>{if(C<Q)return{value:Q,clamped:!0};if(C>I)return{value:I,clamped:!0};return{value:C,clamped:!1}};if(A.envValue!==void 0&&A.envValue!==null&&A.envValue!==""){let C=Number(A.envValue);if(Number.isFinite(C)&&C>=0){let g=E(C);return{valueMs:g.value,source:"env",clamped:g.clamped}}}if(typeof A.flagValue==="number"&&Number.isFinite(A.flagValue)&&A.flagValue>=0){let C=E(A.flagValue);return{valueMs:C.value,source:"flag",clamped:C.clamped}}let B=E(A.defaultMs);return{valueMs:B.value,source:"default",clamped:B.clamped}}var jk=(A)=>new Promise((Q)=>setTimeout(Q,Math.max(0,A)));async function hM(A){let Q=A.baseDelayMs??1000,I=A.maxDelayMs??30000,E=A.jitterRatio??0.25,B=A.abandonAfterMs??Number.POSITIVE_INFINITY,C=A.sleep??jk,g=A.random??Math.random,D=A.now??Date.now,U=Q,F=null;while(!A.shutdownGate()){let Y=!1,N={markStable(){if(Y)return;Y=!0,U=Q,F=null}};try{await A.runOnce(N)}catch($){A.onError?.($)}if(A.shutdownGate())break;if(!Y){if(F===null)F=D();if(D()-F>B){A.onAbandon?.();return}}let J=E>0?U*E*(g()*2-1):0,G=Math.max(100,Math.round(U+J));A.onRetryWait?.(G,U),await C(G),U=Math.min(U*2,I)}}import{existsSync as fM,copyFileSync as I6,readFileSync as B6,writeFileSync as Sk,renameSync as _k,unlinkSync as qk}from"node:fs";var pg=75,E6=new Set(["permissionMode","dangerouslySkipPermissions","maxTurns","budget","timeout"]),Tk=new Set(["permissionMode","dangerouslySkipPermissions","timeout"]),kk=new Set(["telegram","feishu"]);function C6(A){if(A.model!==void 0){if(typeof A.model!=="string"||A.model.length===0||A.model.length>200)return{field:"model",reason:"must be a non-empty string ≤ 200 chars"}}if(A.channels!==void 0){if(!Array.isArray(A.channels))return{field:"channels",reason:"must be an array of strings"};if(A.channels.length>16)return{field:"channels",reason:"too many entries (max 16)"};for(let I of A.channels){if(typeof I!=="string")return{field:"channels",reason:"must contain only strings"};if(!kk.has(I))return{field:`channels.${I}`,reason:"not in local editable channels allowlist"}}}let Q=A.flags||{};for(let[I,E]of Object.entries(Q)){if(!E6.has(I))return{field:`flags.${I}`,reason:"not in local allowlist"};switch(I){case"permissionMode":if(typeof E!=="string"||!["default","auto","bypassPermissions","acceptEdits","plan"].includes(E))return{field:"flags.permissionMode",reason:"invalid enum"};break;case"dangerouslySkipPermissions":if(typeof E!=="boolean")return{field:`flags.${I}`,reason:"must be boolean"};break;case"maxTurns":if(typeof E!=="number"||!Number.isInteger(E)||E<0||E>1e4)return{field:"flags.maxTurns",reason:"must be int in [0, 10000]"};break;case"budget":if(typeof E!=="number"||E<0||E>1e6)return{field:"flags.budget",reason:"must be number in [0, 1_000_000]"};break;case"timeout":if(typeof E!=="number"||!Number.isInteger(E)||E<0||E>3600000)return{field:"flags.timeout",reason:"must be int ms in [0, 3_600_000]"};break}}return null}function g6(A){let Q=A.model!==void 0,I=A.flags||{},E=Object.keys(I),B=A.channels!==void 0;if(!Q&&E.length===0&&!B)return"restart_only";if(Q)return"restart";if(B)return"restart";for(let C of E)if(Tk.has(C))return"restart";return"hot"}function D6(A,Q){let I=`${A}.tmp.${process.pid}.${Date.now()}`;try{Sk(I,JSON.stringify(Q,null,2)+`
134
- `),_k(I,A)}catch(E){try{if(fM(I))qk(I)}catch{}throw E}}function U6(A){if(!fM(A))return{backedUp:!1};return I6(A,`${A}.prev`),{backedUp:!0}}function F6(A){let Q;try{let E=B6(A,"utf-8");return{config:JSON.parse(E),source:"primary"}}catch(E){Q=String(E?.message||E)}let I=`${A}.prev`;if(!fM(I))throw Error(`config.json parse failed (${Q}) and no .prev backup exists`);try{let E=B6(I,"utf-8"),B=JSON.parse(E);return I6(I,A),{config:B,source:"prev",primaryError:Q}}catch(E){throw Error(`config.json parse failed (${Q}); .prev parse also failed (${String(E?.message||E)})`)}}function Y6(A){if(typeof A!=="string")return null;let Q=A.indexOf(":");if(Q<0)return A||null;if(Q===0||Q===A.length-1)return null;return A.slice(0,Q)}function J6(A,Q){let I=JSON.parse(JSON.stringify(A||{}));if(Q.model!==void 0)I.model=Q.model;if(Q.flags)I.flags={...I.flags||{},...Q.flags};if(Q.channels!==void 0){let E=new Map,B=Array.isArray(I.channels)?I.channels:[];for(let C of B){let g=Y6(C);if(g&&!E.has(g))E.set(g,String(C))}I.channels=Q.channels.map((C)=>E.get(C)??C)}return I}function N6(A,Q,I){let E={model:typeof A?.model==="string"?A.model:null,flags:{},config_revision:I,config_update_capable:Q,role:typeof A?.role==="string"?A.role:null,channels:(()=>{let F=A?.channels;if(!Array.isArray(F))return[];let Y=new Set,N=[];for(let J of F){let G=Y6(J);if(!G||Y.has(G))continue;Y.add(G),N.push(G)}return N.sort()})()},B={},C=A?.runtimes_supported;if(Array.isArray(C)&&C.every((F)=>typeof F==="string"))B.runtimes_supported=C;let g=A?.allowed_secret_keys;if(Array.isArray(g)&&g.every((F)=>typeof F==="string"))B.allowed_secret_keys=g;let D=A?.max_concurrent_children;if(typeof D==="number"&&Number.isFinite(D)&&D>0)B.max_concurrent_children=D;if(Object.keys(B).length>0)E.daemon_capabilities=B;let U=A?.flags||{};for(let F of E6)if(F in U)E.flags[F]=U[F];return E}function G6(A){let Q=A.parsedAccess?.allowFrom,I=Ok({channel:"telegram",channelDir:A.channelDir,allowFrom:Q});return{allowFromRaw:Q,bootWarn:I}}function $6(A){if(Array.isArray(A))return{list:A.filter((I)=>typeof I==="string"&&I.length>0).map((I)=>String(I)),malformed:!1};if(A===void 0||A===null)return{list:[],malformed:!1};return{list:[],malformed:!0}}function w6(A){let{list:Q,malformed:I}=$6(A.allowFrom);if(Q.length===0)return{allow:!1,kind:"empty-fail-closed",reason:I?"allowFrom malformed (not a string[]) — fail-closed":'allowFrom empty — fail-closed (set ["*"] to open to all, or add specific ids)'};if(Q.includes("*"))return{allow:!0,kind:"wildcard-allow",reason:'allowFrom=["*"]'};let E=A.senderId||"",B=(A.senderUsername||"").trim();if(E&&Q.includes(E))return{allow:!0,kind:"explicit-allow",reason:`sender id ${E} in allowFrom`};if(B&&Q.includes(B))return{allow:!0,kind:"explicit-allow",reason:`sender username ${B} in allowFrom`};return{allow:!1,kind:"denied",reason:`sender id=${E} username=${B||"(none)"} not in allowFrom`}}function Ok(A){let{list:Q,malformed:I}=$6(A.allowFrom);if(Q.length>0)return null;let E=I?"malformed (not a string[])":"empty";return`[${A.channel}] FAIL-CLOSED: ${A.channelDir}/access.json allowFrom is ${E} — `+'ALL inbound messages will be denied. To open the channel: edit access.json and set `"allowFrom": ["*"]` for any-sender, or add specific sender ids. Pre-v0.11 behaviour was fail-open; this is a deliberate security change (see release notes).'}import{mkdirSync as e7,appendFileSync as jr}from"fs";var __dirname="/home/vansin/rfc030-work/agent-node/src";var QJ=Er(),qQ=process.argv.slice(2),DA={},n7=[],p7="2.1.0";try{let A=new URL(".",import.meta.url).pathname;for(let Q of["../package.json","../../package.json"])try{let I=JSON.parse(ZI($A(A,Q),"utf-8"));if(I.version){p7=I.version;break}}catch{}}catch{}for(let A=0;A<qQ.length;A++){if(qQ[A]==="--version"||qQ[A]==="-v")console.log(`agent-node v${p7}`),process.exit(0);if(qQ[A]==="-h"||qQ[A]==="--help")console.log(`
134
+ `),_k(I,A)}catch(E){try{if(fM(I))qk(I)}catch{}throw E}}function U6(A){if(!fM(A))return{backedUp:!1};return I6(A,`${A}.prev`),{backedUp:!0}}function F6(A){let Q;try{let E=B6(A,"utf-8");return{config:JSON.parse(E),source:"primary"}}catch(E){Q=String(E?.message||E)}let I=`${A}.prev`;if(!fM(I))throw Error(`config.json parse failed (${Q}) and no .prev backup exists`);try{let E=B6(I,"utf-8"),B=JSON.parse(E);return I6(I,A),{config:B,source:"prev",primaryError:Q}}catch(E){throw Error(`config.json parse failed (${Q}); .prev parse also failed (${String(E?.message||E)})`)}}function Y6(A){if(typeof A!=="string")return null;let Q=A.indexOf(":");if(Q<0)return A||null;if(Q===0||Q===A.length-1)return null;return A.slice(0,Q)}function J6(A,Q){let I=JSON.parse(JSON.stringify(A||{}));if(Q.model!==void 0)I.model=Q.model;if(Q.flags)I.flags={...I.flags||{},...Q.flags};if(Q.channels!==void 0){let E=new Map,B=Array.isArray(I.channels)?I.channels:[];for(let C of B){let g=Y6(C);if(g&&!E.has(g))E.set(g,String(C))}I.channels=Q.channels.map((C)=>E.get(C)??C)}return I}function N6(A,Q,I){let E={model:typeof A?.model==="string"?A.model:null,flags:{},config_revision:I,config_update_capable:Q,role:typeof A?.role==="string"?A.role:null,channels:(()=>{let F=A?.channels;if(!Array.isArray(F))return[];let Y=new Set,N=[];for(let J of F){let G=Y6(J);if(!G||Y.has(G))continue;Y.add(G),N.push(G)}return N.sort()})()},B={},C=A?.runtimes_supported;if(Array.isArray(C)&&C.every((F)=>typeof F==="string"))B.runtimes_supported=C;let g=A?.allowed_secret_keys;if(Array.isArray(g)&&g.every((F)=>typeof F==="string"))B.allowed_secret_keys=g;let D=A?.max_concurrent_children;if(typeof D==="number"&&Number.isFinite(D)&&D>0)B.max_concurrent_children=D;if(Object.keys(B).length>0)E.daemon_capabilities=B;let U=A?.flags||{};for(let F of E6)if(F in U)E.flags[F]=U[F];return E}function G6(A){let Q=A.parsedAccess?.allowFrom,I=Ok({channel:"telegram",channelDir:A.channelDir,allowFrom:Q});return{allowFromRaw:Q,bootWarn:I}}function $6(A){if(Array.isArray(A))return{list:A.filter((I)=>typeof I==="string"&&I.length>0).map((I)=>String(I)),malformed:!1};if(A===void 0||A===null)return{list:[],malformed:!1};return{list:[],malformed:!0}}function w6(A){let{list:Q,malformed:I}=$6(A.allowFrom);if(Q.length===0)return{allow:!1,kind:"empty-fail-closed",reason:I?"allowFrom malformed (not a string[]) — fail-closed":'allowFrom empty — fail-closed (set ["*"] to open to all, or add specific ids)'};if(Q.includes("*"))return{allow:!0,kind:"wildcard-allow",reason:'allowFrom=["*"]'};let E=A.senderId||"",B=(A.senderUsername||"").trim();if(E&&Q.includes(E))return{allow:!0,kind:"explicit-allow",reason:`sender id ${E} in allowFrom`};if(B&&Q.includes(B))return{allow:!0,kind:"explicit-allow",reason:`sender username ${B} in allowFrom`};return{allow:!1,kind:"denied",reason:`sender id=${E} username=${B||"(none)"} not in allowFrom`}}function Ok(A){let{list:Q,malformed:I}=$6(A.allowFrom);if(Q.length>0)return null;let E=I?"malformed (not a string[])":"empty";return`[${A.channel}] FAIL-CLOSED: ${A.channelDir}/access.json allowFrom is ${E} — `+'ALL inbound messages will be denied. To open the channel: edit access.json and set `"allowFrom": ["*"]` for any-sender, or add specific sender ids. Pre-v0.11 behaviour was fail-open; this is a deliberate security change (see release notes).'}import{mkdirSync as e7,appendFileSync as jr}from"fs";var __dirname="/tmp/rfc030-publish/agent-node/src";var QJ=Er(),qQ=process.argv.slice(2),DA={},n7=[],p7="2.1.0";try{let A=new URL(".",import.meta.url).pathname;for(let Q of["../package.json","../../package.json"])try{let I=JSON.parse(ZI($A(A,Q),"utf-8"));if(I.version){p7=I.version;break}}catch{}}catch{}for(let A=0;A<qQ.length;A++){if(qQ[A]==="--version"||qQ[A]==="-v")console.log(`agent-node v${p7}`),process.exit(0);if(qQ[A]==="-h"||qQ[A]==="--help")console.log(`
135
135
  @sleep2agi/agent-node — AI Agent 节点,一行命令加入 CommHub 网络
136
136
 
137
137
  用法:
@@ -140,7 +140,7 @@ ${B}`;break;case"retry":if(Fj(B))Q[E]=B;break;case"id":if(Yj(B))Q[E]=B;break;cas
140
140
  选项:
141
141
  --config <path> 配置文件 (.anet/nodes/<name>/config.json)
142
142
  --alias <name> Agent 别名 / CommHub alias (必需)
143
- --runtime <type> claude-agent-sdk (default) | codex-sdk | grok-build-acp
143
+ --runtime <type> claude-agent-sdk (default) | codex-sdk | codex-app-server | grok-build-acp
144
144
  --model <name> AI 模型 (codex 默认: gpt-5.5, claude-agent-sdk 默认: claude-sonnet-4-6)
145
145
  --hub <url> CommHub URL
146
146
  --tools <list> 工具列表,逗号分隔 ("all" = 全部)
@@ -156,6 +156,7 @@ ${B}`;break;case"retry":if(Fj(B))Q[E]=B;break;case"id":if(Yj(B))Q[E]=B;break;cas
156
156
  Runtime:
157
157
  claude-agent-sdk Claude Agent SDK — Claude/MiniMax/Anthropic 兼容 API
158
158
  codex-sdk Codex SDK — GPT-5.4,复用 codex 登录态
159
+ codex-app-server Codex app-server/TUI runtime — RFC-030 preview(生产门锁定)
159
160
  grok-build-acp Grok Build ACP — xAI Grok Build via "grok agent stdio"
160
161
  `),process.exit(0);if(qQ[A]==="--new-session"){DA["new-session"]="true";continue}if(qQ[A]==="--channel"&&A+1<qQ.length){n7.push(qQ[++A]);continue}if(qQ[A].startsWith("--")&&A+1<qQ.length)DA[qQ[A].slice(2)]=qQ[++A]}function o7(A){return A.replace(/^~(?=\/|$)/,QJ)}function Cr(A){let Q=A.indexOf(":");if(Q<0)return{type:A,raw:A};if(Q===0||Q===A.length-1)throw Error(`invalid channel spec "${A}" (expected type or type:path)`);return{type:A.slice(0,Q),path:o7(A.slice(Q+1)),raw:A}}function tC(A){if(!oC(A))return null;try{return JSON.parse(ZI(A,"utf-8"))}catch{return null}}var O={},CA="",aY=0;if(DA.config){let A=DA.config.startsWith("/")?DA.config:$A(process.cwd(),DA.config);try{let Q=F6(A);if(O=Q.config,CA=A,Q.source==="prev")console.warn(`[agent-node] ⚠ RFC-024 self-heal — primary config ${A} unparseable (${Q.primaryError||"?"}); restored from .prev sidecar`);console.log(`[agent-node] Config: ${A} (source=${Q.source})`)}catch(Q){let I=tC(A);if(I)O=I,CA=A,console.log(`[agent-node] Config: ${A} (plain load)`);else console.warn(`[agent-node] ⚠ config not loaded: ${Q?.message||Q}`)}}function gr(){if(DA.alias)return{value:DA.alias,source:"--alias flag"};if(process.env.COMMHUB_ALIAS)return{value:process.env.COMMHUB_ALIAS,source:"COMMHUB_ALIAS env"};if(process.env.ALIAS)return{value:process.env.ALIAS,source:"ALIAS env"};if(O.alias)return{value:O.alias,source:`config.json (${CA||"?"})`};return{value:"",source:"(none — will error)"}}var{value:AA,source:Dr}=gr();if(AA&&O.alias&&O.alias!==AA){let A=`[agent-node] #203 alias mismatch: --alias="${AA}" but ${CA||"config.json"}.alias="${O.alias}". Fix the config file (or set --alias to match) before starting the node.`;if(process.env.ANET_ALLOW_ALIAS_MISMATCH==="1")console.warn(`${A} (ANET_ALLOW_ALIAS_MISMATCH=1 → continuing with "${AA}")`);else console.error(A),console.error("[agent-node] Set ANET_ALLOW_ALIAS_MISMATCH=1 to bypass (not recommended)."),process.exit(2)}if(!DA.config&&AA){let A=$A(process.cwd(),".anet","nodes",AA,"config.json"),Q=$A(process.cwd(),".anet","profiles",`${AA}.json`),I=oC(A)?A:Q,E=tC(I);if(E)O={...E,...O},CA=I,console.log(`[agent-node] Config: ${I}`)}var rC=tC($A(QJ,".anet","config.json"))||{};if(rC.hub&&!O.hub)O.hub=rC.hub;if(rC.token&&!O.token)O.token=rC.token;if(!DA.config&&!Object.keys(O).length){let A=tC($A(process.cwd(),".agent-node.json"));if(A)O=A,console.log("[agent-node] 配置: .agent-node.json")}if(O.env&&typeof O.env==="object"){let A=!1;for(let[Q,I]of Object.entries(O.env)){if(typeof I==="string"&&(/^(sk-|utok_|ntok_|atok_|ak-|gsk_|key-|Bearer\s)/i.test(I)||Q.match(/(_TOKEN|_KEY|_SECRET|AUTH)$/i)))A=!0;if(process.env[Q])continue;if(typeof I==="string")process.env[Q]=o7(I);else if(I&&typeof I==="object"&&typeof I._envRef==="string"){let E=I._envRef,B=process.env[E];if(B===void 0||B==="")console.error(`[anet] FATAL: config.json env.${Q} references env var "${E}" but it is not set in this shell.`),console.error(`[anet] Fix: export ${E}=<your-value> then re-run anet node start`),console.error("[anet] (set the value matching the previous plain secret you migrated away from)"),process.exit(1);process.env[Q]=B}}if(A)console.warn("[anet] ⚠ DEPRECATED: config.json env contains plain secret values that are persisted on disk."),console.warn("[anet] Migrate to envRef form to keep secrets out of the JSON file:"),console.warn(`[anet] anet node migrate-token-to-envref ${AA||"<alias>"}`),console.warn("[anet] Or inspect candidates across all nodes:"),console.warn("[anet] anet doctor")}if(!AA)console.error(`错误: 必须指定 --alias
161
162
  用法: npx @sleep2agi/agent-node --alias "我的Agent"`),process.exit(1);var r7=DA.runtime||process.env.RUNTIME||O.runtime||"claude-agent-sdk",Ur={"claude-agent-sdk":"claude","claude-sdk":"claude","agent-sdk":"claude",claude:"claude","codex-sdk":"codex",codex:"codex","grok-build-acp":"grok","grok-build":"grok",grok:"grok","opencode-cli":"opencode",opencode:"opencode","codex-app-server":"codex-app-server","codex-appserver":"codex-app-server","codex-tui":"codex-app-server"},XA=Ur[r7]||"claude",BJ=r7,Fr=XA==="codex-app-server",TQ=DA.url||DA.hub||process.env.COMMHUB_URL||O.hub||"http://127.0.0.1:9200",WB=DA.model||process.env.MODEL||O.model,v7={type:"preset",preset:"claude_code"};var lW=DA.tools||(Array.isArray(O.tools)?O.tools.join(","):O.tools)||"",bW=lW==="all"?null:lW.split(",").filter(Boolean),JE=lW==="all"?v7:bW&&bW.length?bW:v7,j7=DA["max-turns"]?parseInt(DA["max-turns"]):void 0,S7=DA["max-budget"]?parseFloat(DA["max-budget"]):void 0;function Yr(){if(j7!==void 0)return j7;let A=O.flags?.maxTurns??O.maxTurns??"50",Q=typeof A==="number"?A:parseInt(String(A));return Number.isFinite(Q)?Q:50}function Jr(){if(S7!==void 0)return S7;let A=O.flags?.budget??O.flags?.maxBudgetUsd??O.maxBudgetUsd??"0",Q=typeof A==="number"?A:parseFloat(String(A));return Number.isFinite(Q)?Q:0}var _7=DA["claude-timeout-ms"]?parseInt(DA["claude-timeout-ms"]):process.env.CLAUDE_TIMEOUT_MS?parseInt(process.env.CLAUDE_TIMEOUT_MS):void 0;function Nr(){if(_7!==void 0)return _7;let A=O.flags?.timeout??O.flags?.claudeTimeoutMs??O.claudeTimeoutMs??"300000",Q=typeof A==="number"?A:parseInt(String(A));return Number.isFinite(Q)?Q:300000}var q7=Nr(),UE=parseInt(DA["claude-max-retries"]||process.env.CLAUDE_MAX_RETRIES||O.flags?.claudeMaxRetries||O.claudeMaxRetries||"2");function Gr(){return E0({envValue:DA["codex-timeout-ms"]||process.env.CODEX_TIMEOUT_MS,flagValue:typeof O.flags?.timeout==="number"?O.flags.timeout:typeof O.flags?.codexTimeoutMs==="number"?O.flags.codexTimeoutMs:void 0,defaultMs:300000}).valueMs}var T7=Gr(),$r=(()=>{let A=process.env.GROK_HANDSHAKE_TIMEOUT_MS!==void 0&&process.env.GROK_HANDSHAKE_TIMEOUT_MS!=="",Q=typeof O.flags?.grokHandshakeTimeoutMs==="number";if(!A&&!Q)return;return E0({envValue:process.env.GROK_HANDSHAKE_TIMEOUT_MS,flagValue:typeof O.flags?.grokHandshakeTimeoutMs==="number"?O.flags.grokHandshakeTimeoutMs:void 0,defaultMs:45000}).valueMs})(),wr=E0({envValue:process.env.TELEGRAM_GETUPDATES_TIMEOUT_MS,defaultMs:45000}).valueMs,Mr=DA["new-session"]==="true",MQ=Mr?"":XA==="grok"?DA.session||O.grokSession||O.session||O.resume||O.sessionId||"":DA.session||O.session||O.resume||O.sessionId||"",tY=DA.prompt||O.systemPrompt||"",aA=O.token||rC.token||process.env.COMMHUB_TOKEN||"";if(process.env.COMMHUB_TOKEN&&O.token&&process.env.COMMHUB_TOKEN!==O.token)console.warn(`[${AA}] ⚠ COMMHUB_TOKEN env override ignored (using node config token). Unset COMMHUB_TOKEN to silence this warning.`);function Vr(){if(!CA)return!1;let A=tC(CA),Q=typeof A?.token==="string"?A.token:"";if(!Q||Q===aA)return!1;return aA=Q,O.token=Q,q(`reloaded node token from ${CA}`),!0}var sW=DA["log-dir"]||$A(process.cwd(),".anet","nodes",AA,"logs"),Wr=CA?d7(CA):$A(process.cwd(),".anet","nodes",AA),eW=DA["goals-path"]||O.flags?.goalsPath||O.goalsPath||$A(Wr,"goals.json"),Lr=Math.max(1e4,parseInt(DA["goal-tick-ms"]||process.env.ANET_GOAL_TICK_MS||O.flags?.goalTickMs||"30000")),aQ=new ZM(eW),a7=[],t7=new Set,Xr={debug:0,info:1,warn:2,error:3},Zr=Xr[DA["log-level"]||process.env.LOG_LEVEL||O.logLevel||"info"]??1,AL=[...(Array.isArray(O.channels)?O.channels:[]).filter((A)=>!A.startsWith("server:")&&!A.startsWith("plugin:")),...n7],QL=AL.map((A)=>{try{return Cr(A)}catch(Q){console.error(`[agent-node] ${Q.message}`),process.exit(1)}});function IJ(A){if(!CA||!A)return;try{let Q=JSON.parse(ZI(CA,"utf-8"));if(Q.session===A)return;Q.session=A,aC(CA,JSON.stringify(Q,null,2)+`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-node",
3
- "version": "2.5.0-preview.20",
3
+ "version": "2.5.0-preview.22",
4
4
  "description": "AI Agent runtime for CommHub networks. Supports 4 runtimes: Claude Code CLI, Claude Agent SDK, Codex SDK, and Grok Build ACP.",
5
5
  "bin": {
6
6
  "agent-node": "dist/cli.js"