@zibby/core 0.1.22 → 0.1.23

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.
@@ -1,11 +1,11 @@
1
- import{AgentStrategy as Y}from"./base.js";import{execSync as L,spawn as z}from"child_process";import{zodToJsonSchema as q}from"zod-to-json-schema";import{existsSync as C,mkdirSync as K,readFileSync as R,rmSync as H,writeFileSync as D}from"fs";import{join as w}from"path";import{logger as i}from"../../utils/logger.js";import{DEFAULT_MODELS as U,GEMINI_MODEL_MAP as W}from"../../constants.js";import{getSkill as Z}from"../skill-registry.js";import{StructuredOutputFormatter as B}from"./utils/structured-output-formatter.js";import{StreamingParser as Q}from"../../utils/streaming-parser.js";function V(p){if(!p)return null;const e=String(p),c=e.match(/```(?:json)?\s*([\s\S]*?)```/i);if(c?.[1])try{return JSON.parse(c[1].trim())}catch{}const a=e.indexOf("{");if(a<0)return null;let n=0,r=!1,t=!1,l=-1;for(let s=a;s<e.length;s++){const u=e[s];if(r){t?t=!1:u==="\\"?t=!0:u==='"'&&(r=!1);continue}if(u==='"'){r=!0;continue}if(u==="{"){n===0&&(l=s),n+=1;continue}if(u==="}"){if(n===0)continue;if(n-=1,n===0&&l>=0){const h=e.slice(l,s+1);try{return JSON.parse(h)}catch{l=-1}}}}return null}function X(p){const e=String(p||"").trim();if(!e)return null;try{return JSON.parse(e)}catch{return V(e)}}function ee(p){try{const e=JSON.parse(p);if(typeof e=="string")return e;if(typeof e?.response=="string")return e.response;if(typeof e?.text=="string")return e.text;if(typeof e?.output=="string")return e.output;if(Array.isArray(e?.candidates)&&e.candidates.length>0){const c=e.candidates[0];if(typeof c?.content=="string")return c.content;if(Array.isArray(c?.content?.parts)){const a=c.content.parts.map(n=>typeof n?.text=="string"?n.text:"").join("");if(a.trim())return a}}}catch{}return p}class me extends Y{constructor(){super("gemini","Gemini (Google)",70)}canHandle(e){if(!!!(process.env.GEMINI_API_KEY||process.env.GOOGLE_API_KEY))return i.debug("GeminiAgentStrategy: GEMINI_API_KEY or GOOGLE_API_KEY not set"),!1;try{return L("gemini --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),!0}catch{return i.warn("[Gemini] gemini CLI not found. Install: npm install -g @google/gemini-cli"),!1}}async invoke(e,c={}){const{model:a,workspace:n=process.cwd(),schema:r=null,skills:t=null,sessionPath:l=null,nodeName:s=null,timeout:u=600*1e3}=c;let h=a;(!h||h==="auto")&&(h=U.GEMINI);const N=W[h]||h,A=String(process.env.GEMINI_API_KEY||"").trim(),J=String(process.env.GOOGLE_API_KEY||"").trim(),_=this._resolveSkillsToMcp(t,{sessionPath:l,workspace:n,nodeName:s}),F=Object.keys(_).length>0;let E=e;const b=r&&typeof r.parse=="function";let v=null;if(r){let o;try{const f=b?q(r,{target:"openAi"}):r;o=JSON.stringify(f,null,2)}catch{o="{}"}if(F){E+=`
1
+ import{AgentStrategy as Y}from"./base.js";import{execSync as F,spawn as z}from"child_process";import{zodToJsonSchema as H}from"zod-to-json-schema";import{existsSync as C,mkdirSync as K,readFileSync as L,rmSync as q,writeFileSync as R}from"fs";import{join as G}from"path";import{logger as i}from"../../utils/logger.js";import{DEFAULT_MODELS as U,GEMINI_MODEL_MAP as W}from"../../constants.js";import{getSkill as Z}from"../skill-registry.js";import{StructuredOutputFormatter as B}from"./utils/structured-output-formatter.js";import{StreamingParser as Q}from"../../utils/streaming-parser.js";function V(g){if(!g)return null;const e=String(g),a=e.match(/```(?:json)?\s*([\s\S]*?)```/i);if(a?.[1])try{return JSON.parse(a[1].trim())}catch{}const l=e.indexOf("{");if(l<0)return null;let n=0,t=!1,r=!1,o=-1;for(let c=l;c<e.length;c++){const u=e[c];if(t){r?r=!1:u==="\\"?r=!0:u==='"'&&(t=!1);continue}if(u==='"'){t=!0;continue}if(u==="{"){n===0&&(o=c),n+=1;continue}if(u==="}"){if(n===0)continue;if(n-=1,n===0&&o>=0){const m=e.slice(o,c+1);try{return JSON.parse(m)}catch{o=-1}}}}return null}function X(g){const e=String(g||"").trim();if(!e)return null;try{return JSON.parse(e)}catch{return V(e)}}function ee(g){try{const e=JSON.parse(g);if(typeof e=="string")return e;if(typeof e?.response=="string")return e.response;if(typeof e?.text=="string")return e.text;if(typeof e?.output=="string")return e.output;if(Array.isArray(e?.candidates)&&e.candidates.length>0){const a=e.candidates[0];if(typeof a?.content=="string")return a.content;if(Array.isArray(a?.content?.parts)){const l=a.content.parts.map(n=>typeof n?.text=="string"?n.text:"").join("");if(l.trim())return l}}}catch{}return g}class me extends Y{constructor(){super("gemini","Gemini (Google)",70)}canHandle(e){if(!!!(process.env.GEMINI_API_KEY||process.env.GOOGLE_API_KEY))return i.debug("GeminiAgentStrategy: GEMINI_API_KEY or GOOGLE_API_KEY not set"),!1;try{return F("gemini --version",{encoding:"utf-8",timeout:5e3,stdio:"pipe"}),!0}catch{return i.warn("[Gemini] gemini CLI not found. Install: npm install -g @google/gemini-cli"),!1}}async invoke(e,a={}){const{model:l,workspace:n=process.cwd(),schema:t=null,skills:r=null,sessionPath:o=null,nodeName:c=null,timeout:u=600*1e3}=a;let m=l;(!m||m==="auto")&&(m=U.GEMINI);const _=W[m]||m,A=String(process.env.GEMINI_API_KEY||"").trim(),J=String(process.env.GOOGLE_API_KEY||"").trim(),N=this._resolveSkillsToMcp(r,{sessionPath:o,workspace:n,nodeName:c}),T=Object.keys(N).length>0;let I=e;const b=t&&typeof t.parse=="function";let O=null;if(t){let s;try{const p=b?H(t,{target:"openAi"}):t;s=JSON.stringify(p,null,2)}catch{s="{}"}if(T){I+=`
2
2
 
3
3
  Write valid JSON that matches this schema:
4
- ${o}`;const f=`zibby-result-${Date.now()}.json`,g=w(n,".zibby","tmp");v=w(g,f),K(g,{recursive:!0}),E+=B.generateFileOutputInstructions(r,v)}else E+=`
4
+ ${s}`;const p=`zibby-result-${Date.now()}.json`,d=G(n,".zibby","tmp");O=G(d,p),K(d,{recursive:!0}),I+=B.generateFileOutputInstructions(t,O)}else I+=`
5
5
 
6
6
  Return ONLY valid JSON (no markdown, no commentary) that matches this schema:
7
- ${o}`}const $=this._createGeminiConfigDir(n,_),y=["--output-format","json"];N&&N!=="auto"&&y.push("--model",N);const M=Object.keys(_);if(M.length>0){y.push("--approval-mode","yolo");for(const o of M)y.push("--allowed-mcp-server-names",o);i.info(`[Gemini] Enabling MCP servers: ${M.join(", ")}`)}else t&&t.length>0&&i.warn(`[Gemini] Skills requested but no MCP servers configured: ${t.join(", ")}`);y.push("-p",E);const I={...process.env,GEMINI_CONFIG_DIR:$};A?(I.GEMINI_API_KEY=A,delete I.GOOGLE_API_KEY):J&&(I.GOOGLE_API_KEY=J,delete I.GEMINI_API_KEY),i.debug(`[Gemini] Command: gemini ${y.slice(0,8).join(" ")}... (${y.length} total args)`),i.debug(`[Gemini] Config dir: ${$}`),i.debug(`[Gemini] GEMINI_CONFIG_DIR env: ${I.GEMINI_CONFIG_DIR}`);let P="",O=null;try{P=await new Promise((f,g)=>{const G=z("gemini",y,{cwd:n,env:I,stdio:["ignore","pipe","pipe"]});let x="",j="";const k=setTimeout(()=>{try{G.kill("SIGTERM")}catch{}},u);G.stdout.on("data",d=>{x+=d.toString()}),G.stderr.on("data",d=>{j+=d.toString()}),G.on("error",d=>{clearTimeout(k),g(d)}),G.on("close",d=>{if(clearTimeout(k),d===0)return f(x.trim());g(new Error(`gemini failed with code ${d}: ${(j||x).trim()}`))})})}catch(o){O=o}finally{try{H($,{recursive:!0,force:!0})}catch{}}const m=ee(P).trim();if(!r){if(O)throw O;return m}if(v){const o=C(v);if(i.info(`[Gemini] Result file: ${o?"present":"missing"} at ${v}`),o)try{const f=R(v,"utf-8").trim(),g=JSON.parse(f),G=b?r.parse(g):g;return i.info("[Gemini] Structured output recovered from result file"),{raw:m,structured:G}}catch(f){i.warn(`[Gemini] Result file parse/validation failed: ${f.message}`)}else O||i.warn("[Gemini] Result file missing; falling back to stream-parsed JSON")}let S=null;if(r){const o=new Q;o.zodSchema=r,o.processChunk(m),o.flush(),S=o.getResult()}if(i.info(`[Gemini] Raw stdout length: ${P.length} chars`),i.info(`[Gemini] Extracted text length: ${m.length} chars`),i.info(`[Gemini] StreamParser result: ${S?"extracted":"null"}`),S||(m.length<2e3?i.info(`[Gemini] Raw text preview:
8
- ${m}`):i.info(`[Gemini] Raw text preview (first 1000 chars):
9
- ${m.slice(0,1e3)}`),S=X(m)),!S)throw O||(i.error("[Gemini] Failed to extract valid JSON from output"),new Error("Gemini did not return valid JSON for structured output"));const T=b?r.parse(S):S;return{raw:m,structured:T}}_resolveSkillsToMcp(e,c={}){if(!Array.isArray(e)||e.length===0)return{};const a={};for(const n of e){const r=Z(n);if(!r||typeof r.resolve!="function")continue;const t=r.resolve(c);if(!t)continue;const l=r.cursorKey||r.serverName||n,s={command:t.command};t.args?.length&&(s.args=t.args),t.env&&Object.keys(t.env).length>0&&(s.env=t.env),t.cwd&&(s.cwd=t.cwd),a[l]=s}return a}_createGeminiConfigDir(e,c){const a=`${Date.now()}-${Math.random().toString(16).slice(2,10)}`,n=w(e||process.cwd(),".zibby","tmp",`gemini-config-${a}`);K(n,{recursive:!0});const r=w(n,"settings.json");let t={};const l=w(process.env.HOME||"",".gemini","settings.json");if(C(l))try{t=JSON.parse(R(l,"utf-8"))}catch{t={}}const s={...t,mcpServers:{...t.mcpServers&&typeof t.mcpServers=="object"?t.mcpServers:{},...c||{}}};D(r,`${JSON.stringify(s,null,2)}
10
- `,"utf-8");const u=w(e||process.cwd(),".zibby","tmp","gemini-settings-debug.json");try{D(u,`${JSON.stringify(s,null,2)}
11
- `,"utf-8")}catch{}return i.debug(`[Gemini] Created isolated config with ${Object.keys(s.mcpServers||{}).length} MCP servers`),i.debug(`[Gemini] MCP servers: ${JSON.stringify(Object.keys(s.mcpServers||{}),null,2)}`),n}}export{me as GeminiAgentStrategy};
7
+ ${s}`}const M=this._createGeminiConfigDir(n,N),y=["--output-format","json"];_&&_!=="auto"&&y.push("--model",_);const $=Object.keys(N);if($.length>0){y.push("--approval-mode","yolo");for(const s of $)y.push("--allowed-mcp-server-names",s);i.info(`[Gemini] Enabling MCP servers: ${$.join(", ")}`)}else r&&r.length>0&&i.warn(`[Gemini] Skills requested but no MCP servers configured: ${r.join(", ")}`);y.push("-p",I);const E={...process.env,GEMINI_CLI_HOME:M};A?(E.GEMINI_API_KEY=A,delete E.GOOGLE_API_KEY):J&&(E.GOOGLE_API_KEY=J,delete E.GEMINI_API_KEY),i.debug(`[Gemini] Command: gemini ${y.slice(0,8).join(" ")}... (${y.length} total args)`),i.debug(`[Gemini] Config home: ${M}`),i.debug(`[Gemini] GEMINI_CLI_HOME env: ${E.GEMINI_CLI_HOME}`);let P="",w=null;try{P=await new Promise((p,d)=>{const v=z("gemini",y,{cwd:n,env:E,stdio:["ignore","pipe","pipe"]});let x="",j="";const k=setTimeout(()=>{try{v.kill("SIGTERM")}catch{}},u);v.stdout.on("data",h=>{x+=h.toString()}),v.stderr.on("data",h=>{j+=h.toString()}),v.on("error",h=>{clearTimeout(k),d(h)}),v.on("close",h=>{if(clearTimeout(k),h===0)return p(x.trim());d(new Error(`gemini failed with code ${h}: ${(j||x).trim()}`))})})}catch(s){w=s}finally{try{q(M,{recursive:!0,force:!0})}catch{}}const f=ee(P).trim();if(!t){if(w)throw w;return f}if(O){const s=C(O);if(i.info(`[Gemini] Result file: ${s?"present":"missing"} at ${O}`),s)try{const p=L(O,"utf-8").trim(),d=JSON.parse(p),v=b?t.parse(d):d;return i.info("[Gemini] Structured output recovered from result file"),{raw:f,structured:v}}catch(p){i.warn(`[Gemini] Result file parse/validation failed: ${p.message}`)}else w||i.warn("[Gemini] Result file missing; falling back to stream-parsed JSON")}let S=null;if(t){const s=new Q;s.zodSchema=t,s.processChunk(f),s.flush(),S=s.getResult()}if(i.info(`[Gemini] Raw stdout length: ${P.length} chars`),i.info(`[Gemini] Extracted text length: ${f.length} chars`),i.info(`[Gemini] StreamParser result: ${S?"extracted":"null"}`),S||(f.length<2e3?i.info(`[Gemini] Raw text preview:
8
+ ${f}`):i.info(`[Gemini] Raw text preview (first 1000 chars):
9
+ ${f.slice(0,1e3)}`),S=X(f)),!S)throw w||(i.error("[Gemini] Failed to extract valid JSON from output"),new Error("Gemini did not return valid JSON for structured output"));const D=b?t.parse(S):S;return{raw:f,structured:D}}_resolveSkillsToMcp(e,a={}){if(!Array.isArray(e)||e.length===0)return{};const l={};for(const n of e){const t=Z(n);if(!t||typeof t.resolve!="function")continue;const r=t.resolve(a);if(!r)continue;const o=t.cursorKey||t.serverName||n,c={command:r.command};r.args?.length&&(c.args=r.args),r.env&&Object.keys(r.env).length>0&&(c.env=r.env),r.cwd&&(c.cwd=r.cwd),l[o]=c}return l}_createGeminiConfigDir(e,a){const l=`${Date.now()}-${Math.random().toString(16).slice(2,10)}`,n=G(e||process.cwd(),".zibby","tmp",`gemini-home-${l}`),t=G(n,".gemini");K(t,{recursive:!0});const r=G(t,"settings.json");let o={};const c=G(process.env.HOME||"",".gemini","settings.json");if(C(c))try{o=JSON.parse(L(c,"utf-8"))}catch{o={}}const u={...o,mcpServers:{...o.mcpServers&&typeof o.mcpServers=="object"?o.mcpServers:{},...a||{}}};R(r,`${JSON.stringify(u,null,2)}
10
+ `,"utf-8");const m=G(e||process.cwd(),".zibby","tmp","gemini-settings-debug.json");try{R(m,`${JSON.stringify(u,null,2)}
11
+ `,"utf-8")}catch{}return i.debug(`[Gemini] Created isolated config with ${Object.keys(u.mcpServers||{}).length} MCP servers`),i.debug(`[Gemini] MCP servers: ${JSON.stringify(Object.keys(u.mcpServers||{}),null,2)}`),n}}export{me as GeminiAgentStrategy};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/core",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "Core test automation engine with multi-agent and multi-MCP support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",