playingpack 0.3.1 → 0.3.2

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{program as v}from"commander";import et from"fastify";import tt from"@fastify/cors";import nt from"@fastify/websocket";import{fastifyTRPCPlugin as ot}from"@trpc/server/adapters/fastify";import st from"get-port";var C=class{sessions=new Map;settings;listeners=new Set;pendingResolvers=new Map;preInterceptResolvers=new Map;constructor(e){this.settings={pause:"off",...e}}createSession(e,t,o,s){let r={id:e,state:"LOOKUP",timestamp:new Date().toISOString(),method:t,path:o,body:s,toolCalls:[],responseContent:""};if(typeof s=="object"&&s!==null){let i=s;typeof i.model=="string"&&(r.model=i.model)}return this.sessions.set(e,r),this.emit({type:"request_update",session:r}),r}getSession(e){return this.sessions.get(e)}getAllSessions(){return Array.from(this.sessions.values())}updateState(e,t){let o=this.sessions.get(e);o&&(o.state=t,this.emit({type:"request_update",session:o}))}addToolCall(e,t){let o=this.sessions.get(e);o&&(o.toolCalls.push(t),this.emit({type:"request_update",session:o}))}updateContent(e,t){let o=this.sessions.get(e);o&&(o.responseContent+=t)}setRawResponse(e,t){let o=this.sessions.get(e);o&&(o.responseContent=t)}complete(e,t,o){let s=this.sessions.get(e);s&&(s.state="COMPLETE",s.statusCode=t,this.emit({type:"request_update",session:s}),this.emit({type:"request_complete",requestId:e,statusCode:t,cached:o}))}error(e,t){let o=this.sessions.get(e);o&&(o.state="ERROR",o.error=t,this.emit({type:"request_update",session:o}))}shouldIntercept(e){if(this.settings.pause==="off")return!1;let t=this.sessions.get(e);return t?this.settings.pause==="tool-calls"?t.toolCalls.length>0:!0:!1}async intercept(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);if(this.updateState(e,"INTERCEPT"),t.toolCalls.length>0){let o=t.toolCalls[0];this.emit({type:"intercept",requestId:e,toolCall:{name:o?.name||"unknown",arguments:o?.arguments||"{}"},status:"paused"})}return new Promise(o=>{this.pendingResolvers.set(e,{resolve:s=>{let r=this.pendingResolvers.get(e);this.pendingResolvers.delete(e),o({action:s,mockContent:r?.mockContent})}})})}allowRequest(e){let t=this.pendingResolvers.get(e);return t?(this.updateState(e,"FLUSH"),t.resolve("allow"),!0):!1}mockRequest(e,t){let o=this.pendingResolvers.get(e);return o?(o.mockContent=t,this.updateState(e,"INJECT"),o.resolve("mock"),!0):!1}shouldPreIntercept(e){return this.settings.pause!=="off"}async preIntercept(e,t){let o=this.sessions.get(e);if(!o)throw new Error(`Session ${e} not found`);this.updateState(e,"PRE_INTERCEPT");let s=o.body,r=Array.isArray(s?.messages)?s.messages:[];return this.emit({type:"pre_intercept",requestId:e,request:{model:o.model||"unknown",messages:r},hasCachedResponse:t,status:"paused"}),new Promise(i=>{this.preInterceptResolvers.set(e,{resolve:i})})}preInterceptAllow(e){let t=this.preInterceptResolvers.get(e);return t?(this.preInterceptResolvers.delete(e),t.resolve({action:"allow"}),!0):!1}preInterceptEdit(e,t){let o=this.preInterceptResolvers.get(e);if(o){this.preInterceptResolvers.delete(e);let s=this.sessions.get(e);return s&&(s.body=t,this.emit({type:"request_update",session:s})),o.resolve({action:"edit",editedBody:t}),!0}return!1}preInterceptUseCache(e){let t=this.preInterceptResolvers.get(e);return t?(this.preInterceptResolvers.delete(e),t.resolve({action:"use_cache"}),!0):!1}preInterceptMock(e,t){let o=this.preInterceptResolvers.get(e);return o?(this.preInterceptResolvers.delete(e),this.updateState(e,"INJECT"),o.resolve({action:"mock",mockContent:t}),!0):!1}getSettings(){return{...this.settings}}updateSettings(e){this.settings={...this.settings,...e}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e){for(let t of this.listeners)try{t(e)}catch{}}cleanup(){let e=Array.from(this.sessions.entries());if(e.length>100){let t=e.filter(([o,s])=>s.state==="COMPLETE"||s.state==="ERROR").slice(0,e.length-100);for(let[o]of t)this.sessions.delete(o)}}removeSession(e){this.sessions.delete(e),this.pendingResolvers.delete(e),this.preInterceptResolvers.delete(e)}},b=null;function q(n){return b=new C(n),b}function h(){return b||(b=new C),b}function F(n){return{sessionManager:h(),req:n?.req,res:n?.res}}import{initTRPC as xe}from"@trpc/server";import{z as B}from"zod";import{z as a}from"zod";var bt=a.object({model:a.string(),messages:a.array(a.object({role:a.enum(["system","user","assistant","tool"]),content:a.union([a.string(),a.null(),a.array(a.any())]).optional(),name:a.string().optional(),tool_calls:a.array(a.any()).optional(),tool_call_id:a.string().optional()})),temperature:a.number().optional(),top_p:a.number().optional(),n:a.number().optional(),stream:a.boolean().optional(),stop:a.union([a.string(),a.array(a.string())]).optional(),max_tokens:a.number().optional(),presence_penalty:a.number().optional(),frequency_penalty:a.number().optional(),logit_bias:a.record(a.number()).optional(),user:a.string().optional(),tools:a.array(a.object({type:a.literal("function"),function:a.object({name:a.string(),description:a.string().optional(),parameters:a.any().optional()})})).optional(),tool_choice:a.union([a.literal("none"),a.literal("auto"),a.literal("required"),a.object({type:a.literal("function"),function:a.object({name:a.string()})})]).optional(),response_format:a.object({type:a.enum(["text","json_object"])}).optional(),seed:a.number().optional()}).passthrough(),$=a.object({requestId:a.string(),type:a.enum(["text","error","tool_result"]),content:a.string()}),L=a.object({requestId:a.string()}),J=a.object({requestId:a.string()}),H=a.object({requestId:a.string(),editedBody:a.record(a.unknown())}),W=a.object({requestId:a.string()}),G=a.object({requestId:a.string(),mockContent:a.string()}),we=a.object({pause:a.enum(["off","tool-calls","all"])}),z=a.object({settings:we.partial()}),ve=a.object({c:a.string(),d:a.number()}),Ce=a.object({id:a.string(),hash:a.string(),timestamp:a.string(),model:a.string(),endpoint:a.string()}),St=a.object({meta:Ce,request:a.object({body:a.unknown()}),response:a.object({status:a.number(),chunks:a.array(ve)})}),Re=a.enum(["auto","off","replay-only"]),Pe=a.enum(["off","tool-calls","all"]),x=a.object({upstream:a.string().url().optional(),tapesDir:a.string().optional(),logsDir:a.string().optional(),record:Re.optional(),headless:a.boolean().optional(),port:a.number().int().min(1).max(65535).optional(),host:a.string().optional(),pause:Pe.optional()});var m=xe.context().create(),K=m.router({getSessions:m.procedure.query(({ctx:n})=>({sessions:n.sessionManager.getAllSessions()})),getSession:m.procedure.input(B.object({id:B.string()})).query(({ctx:n,input:e})=>({session:n.sessionManager.getSession(e.id)||null})),getSettings:m.procedure.query(({ctx:n})=>({settings:n.sessionManager.getSettings()})),updateSettings:m.procedure.input(z).mutation(({ctx:n,input:e})=>(n.sessionManager.updateSettings(e.settings),{settings:n.sessionManager.getSettings()})),allowRequest:m.procedure.input(L).mutation(({ctx:n,input:e})=>({success:n.sessionManager.allowRequest(e.requestId)})),mockRequest:m.procedure.input($).mutation(({ctx:n,input:e})=>({success:n.sessionManager.mockRequest(e.requestId,e.content)})),preInterceptAllow:m.procedure.input(J).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptAllow(e.requestId)})),preInterceptEdit:m.procedure.input(H).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptEdit(e.requestId,e.editedBody)})),preInterceptUseCache:m.procedure.input(W).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptUseCache(e.requestId)})),preInterceptMock:m.procedure.input(G).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptMock(e.requestId,e.mockContent)})),health:m.procedure.query(()=>({status:"ok",timestamp:new Date().toISOString()}))});import{createParser as Ie}from"eventsource-parser";var I=class{callbacks;toolCalls=new Map;accumulatedContent="";parser;constructor(e={}){this.callbacks=e,this.parser=Ie(t=>{t.type==="event"&&this.handleEvent(t.data)})}feed(e){this.parser.feed(e)}getContent(){return this.accumulatedContent}getToolCalls(){return Array.from(this.toolCalls.values())}hasToolCalls(){return this.toolCalls.size>0}getAssembledMessage(){let e=this.getToolCalls();return e.length>0?{role:"assistant",content:null,tool_calls:e.map(t=>({id:t.id,type:"function",function:{name:t.name,arguments:t.arguments}}))}:{role:"assistant",content:this.accumulatedContent||null}}reset(){this.toolCalls.clear(),this.accumulatedContent="",this.parser.reset()}handleEvent(e){if(e==="[DONE]"){this.callbacks.onDone?.();return}try{let t=JSON.parse(e);for(let o of t.choices){let s=o.delta;if(s.content&&(this.accumulatedContent+=s.content,this.callbacks.onContent?.(s.content)),s.tool_calls)for(let r of s.tool_calls){let i=this.toolCalls.get(r.index);if(i)r.function?.arguments&&(i.arguments+=r.function.arguments,this.callbacks.onToolCallUpdate?.(r.index,r.function.arguments));else{let c={index:r.index,id:r.id||"",name:r.function?.name||"",arguments:r.function?.arguments||""};this.toolCalls.set(r.index,c),this.callbacks.onToolCall?.(c)}}}}catch(t){this.callbacks.onError?.(t instanceof Error?t:new Error(String(t)))}}};function T(n){return new I(n)}var Te="https://api.openai.com";function Y(n){return!n||n.length<8?"****":`${n.substring(0,4)}...${n.substring(n.length-4)}`}function Ee(n,e=!0){let t=new Headers,o=["authorization","content-type","accept","openai-organization","openai-project","user-agent"];for(let s of o){let r=n[s];if(r){let i=Array.isArray(r)?r[0]:r;i&&t.set(s,i)}}return e?t.set("accept","text/event-stream"):t.set("accept","application/json"),t}async function E(n){let t=`${n.upstreamUrl||Te}${n.path}`,o=typeof n.body=="object"&&n.body!==null&&"stream"in n.body&&typeof n.body.stream=="boolean"?n.body.stream:!0,s=Ee(n.headers,o),r=await fetch(t,{method:n.method,headers:s,body:JSON.stringify(n.body)});return{status:r.status,headers:r.headers,body:r.body,ok:r.ok}}async function*V(n){let e=n.getReader(),t=new TextDecoder;try{for(;;){let{done:s,value:r}=await e.read();if(s)break;yield t.decode(r,{stream:!0})}let o=t.decode();o&&(yield o)}finally{e.releaseLock()}}import{mkdir as Me,writeFile as Ae}from"fs/promises";import{dirname as je,join as Oe}from"path";import{createHash as _e}from"crypto";function _(n){if(n==null)return null;if(Array.isArray(n))return n.map(_);if(typeof n=="object"){let e=n,t={},o=Object.keys(e).filter(s=>!["stream","request_id","timestamp"].includes(s)).sort();for(let s of o)t[s]=_(e[s]);return t}return n}function S(n){let e=_(n),t=JSON.stringify(e);return _e("sha256").update(t).digest("hex")}var Ne=".playingpack/tapes",w=class{tapesDir;chunks=[];lastChunkTime=0;requestBody;model="unknown";endpoint;hash="";constructor(e=Ne){this.tapesDir=e,this.endpoint="/v1/chat/completions"}start(e){if(this.requestBody=e,this.chunks=[],this.lastChunkTime=Date.now(),this.hash=S(e),typeof e=="object"&&e!==null){let t=e;typeof t.model=="string"&&(this.model=t.model)}}recordChunk(e){let t=Date.now(),o=this.chunks.length===0?0:t-this.lastChunkTime;this.chunks.push({c:e,d:o}),this.lastChunkTime=t}async save(e=200){let t={meta:{id:crypto.randomUUID(),hash:this.hash,timestamp:new Date().toISOString(),model:this.model,endpoint:this.endpoint},request:{body:this.requestBody},response:{status:e,chunks:this.chunks}},o=this.getTapePath();return await Me(je(o),{recursive:!0}),await Ae(o,JSON.stringify(t,null,2),"utf-8"),o}getTapePath(){return Oe(this.tapesDir,`${this.hash}.json`)}getHash(){return this.hash}getChunkCount(){return this.chunks.length}};import{readFile as De,access as Ue}from"fs/promises";import{join as Z}from"path";var Q=".playingpack/tapes";async function X(n,e=Q){let t=S(n),o=Z(e,`${t}.json`);try{return await Ue(o),!0}catch{return!1}}async function qe(n,e=Q){let t=S(n),o=Z(e,`${t}.json`);try{let s=await De(o,"utf-8");return JSON.parse(s)}catch{return null}}var M=class{tape;currentIndex=0;aborted=!1;constructor(e){this.tape=e}getMeta(){return this.tape.meta}getStatus(){return this.tape.response.status}abort(){this.aborted=!0}async*replay(){this.currentIndex=0,this.aborted=!1;for(let e of this.tape.response.chunks){if(this.aborted||(e.d>0&&await this.delay(e.d),this.aborted))break;yield e.c,this.currentIndex++}}async*replayFast(){for(let e of this.tape.response.chunks){if(this.aborted)break;yield e.c}}delay(e){return new Promise(t=>setTimeout(t,e))}};async function ee(n,e){let t=await qe(n,e);return t?new M(t):null}function A(){return`chatcmpl-mock-${Date.now()}`}function oe(n,e=4){let t=[];for(let o=0;o<n.length;o+=e)t.push(n.slice(o,o+e));return t}function k(n){return`data: ${n}
2
+ import{program as v}from"commander";import et from"fastify";import tt from"@fastify/cors";import nt from"@fastify/websocket";import{fastifyTRPCPlugin as ot}from"@trpc/server/adapters/fastify";import st from"get-port";var C=class{sessions=new Map;settings;listeners=new Set;pendingResolvers=new Map;preInterceptResolvers=new Map;constructor(e){this.settings={pause:"off",...e}}createSession(e,t,o,s){let r={id:e,state:"LOOKUP",timestamp:new Date().toISOString(),method:t,path:o,body:s,toolCalls:[],responseContent:""};if(typeof s=="object"&&s!==null){let i=s;typeof i.model=="string"&&(r.model=i.model)}return this.sessions.set(e,r),this.emit({type:"request_update",session:r}),r}getSession(e){return this.sessions.get(e)}getAllSessions(){return Array.from(this.sessions.values())}updateState(e,t){let o=this.sessions.get(e);o&&(o.state=t,this.emit({type:"request_update",session:o}))}addToolCall(e,t){let o=this.sessions.get(e);o&&(o.toolCalls.push(t),this.emit({type:"request_update",session:o}))}updateContent(e,t){let o=this.sessions.get(e);o&&(o.responseContent+=t)}setRawResponse(e,t){let o=this.sessions.get(e);o&&(o.responseContent=t)}complete(e,t,o){let s=this.sessions.get(e);s&&(s.state="COMPLETE",s.statusCode=t,this.emit({type:"request_update",session:s}),this.emit({type:"request_complete",requestId:e,statusCode:t,cached:o}))}error(e,t){let o=this.sessions.get(e);o&&(o.state="ERROR",o.error=t,this.emit({type:"request_update",session:o}))}shouldIntercept(e){if(this.settings.pause==="off")return!1;let t=this.sessions.get(e);return t?this.settings.pause==="tool-calls"?t.toolCalls.length>0:!0:!1}async intercept(e){let t=this.sessions.get(e);if(!t)throw new Error(`Session ${e} not found`);if(this.updateState(e,"TOOL_CALL"),t.toolCalls.length>0){let o=t.toolCalls[0];this.emit({type:"intercept",requestId:e,toolCall:{name:o?.name||"unknown",arguments:o?.arguments||"{}"},status:"paused"})}return new Promise(o=>{this.pendingResolvers.set(e,{resolve:s=>{let r=this.pendingResolvers.get(e);this.pendingResolvers.delete(e),o({action:s,mockContent:r?.mockContent})}})})}allowRequest(e){let t=this.pendingResolvers.get(e);return t?(this.updateState(e,"FLUSH"),t.resolve("allow"),!0):!1}mockRequest(e,t){let o=this.pendingResolvers.get(e);return o?(o.mockContent=t,this.updateState(e,"INJECT"),o.resolve("mock"),!0):!1}shouldPreIntercept(e){return this.settings.pause!=="off"}async preIntercept(e,t){let o=this.sessions.get(e);if(!o)throw new Error(`Session ${e} not found`);this.updateState(e,"PAUSED");let s=o.body,r=Array.isArray(s?.messages)?s.messages:[];return this.emit({type:"pre_intercept",requestId:e,request:{model:o.model||"unknown",messages:r},hasCachedResponse:t,status:"paused"}),new Promise(i=>{this.preInterceptResolvers.set(e,{resolve:i})})}preInterceptAllow(e){let t=this.preInterceptResolvers.get(e);return t?(this.preInterceptResolvers.delete(e),t.resolve({action:"allow"}),!0):!1}preInterceptEdit(e,t){let o=this.preInterceptResolvers.get(e);if(o){this.preInterceptResolvers.delete(e);let s=this.sessions.get(e);return s&&(s.body=t,this.emit({type:"request_update",session:s})),o.resolve({action:"edit",editedBody:t}),!0}return!1}preInterceptUseCache(e){let t=this.preInterceptResolvers.get(e);return t?(this.preInterceptResolvers.delete(e),t.resolve({action:"use_cache"}),!0):!1}preInterceptMock(e,t){let o=this.preInterceptResolvers.get(e);return o?(this.preInterceptResolvers.delete(e),this.updateState(e,"INJECT"),o.resolve({action:"mock",mockContent:t}),!0):!1}getSettings(){return{...this.settings}}updateSettings(e){this.settings={...this.settings,...e}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e){for(let t of this.listeners)try{t(e)}catch{}}cleanup(){let e=Array.from(this.sessions.entries());if(e.length>100){let t=e.filter(([o,s])=>s.state==="COMPLETE"||s.state==="ERROR").slice(0,e.length-100);for(let[o]of t)this.sessions.delete(o)}}removeSession(e){this.sessions.delete(e),this.pendingResolvers.delete(e),this.preInterceptResolvers.delete(e)}},b=null;function q(n){return b=new C(n),b}function h(){return b||(b=new C),b}function L(n){return{sessionManager:h(),req:n?.req,res:n?.res}}import{initTRPC as xe}from"@trpc/server";import{z as B}from"zod";import{z as a}from"zod";var bt=a.object({model:a.string(),messages:a.array(a.object({role:a.enum(["system","user","assistant","tool"]),content:a.union([a.string(),a.null(),a.array(a.any())]).optional(),name:a.string().optional(),tool_calls:a.array(a.any()).optional(),tool_call_id:a.string().optional()})),temperature:a.number().optional(),top_p:a.number().optional(),n:a.number().optional(),stream:a.boolean().optional(),stop:a.union([a.string(),a.array(a.string())]).optional(),max_tokens:a.number().optional(),presence_penalty:a.number().optional(),frequency_penalty:a.number().optional(),logit_bias:a.record(a.number()).optional(),user:a.string().optional(),tools:a.array(a.object({type:a.literal("function"),function:a.object({name:a.string(),description:a.string().optional(),parameters:a.any().optional()})})).optional(),tool_choice:a.union([a.literal("none"),a.literal("auto"),a.literal("required"),a.object({type:a.literal("function"),function:a.object({name:a.string()})})]).optional(),response_format:a.object({type:a.enum(["text","json_object"])}).optional(),seed:a.number().optional()}).passthrough(),F=a.object({requestId:a.string(),type:a.enum(["text","error","tool_result"]),content:a.string()}),$=a.object({requestId:a.string()}),J=a.object({requestId:a.string()}),H=a.object({requestId:a.string(),editedBody:a.record(a.unknown())}),W=a.object({requestId:a.string()}),G=a.object({requestId:a.string(),mockContent:a.string()}),we=a.object({pause:a.enum(["off","tool-calls","all"])}),z=a.object({settings:we.partial()}),ve=a.object({c:a.string(),d:a.number()}),Ce=a.object({id:a.string(),hash:a.string(),timestamp:a.string(),model:a.string(),endpoint:a.string()}),St=a.object({meta:Ce,request:a.object({body:a.unknown()}),response:a.object({status:a.number(),chunks:a.array(ve)})}),Re=a.enum(["auto","off","replay-only"]),Pe=a.enum(["off","tool-calls","all"]),x=a.object({upstream:a.string().url().optional(),tapesDir:a.string().optional(),logsDir:a.string().optional(),record:Re.optional(),headless:a.boolean().optional(),port:a.number().int().min(1).max(65535).optional(),host:a.string().optional(),pause:Pe.optional()});var m=xe.context().create(),K=m.router({getSessions:m.procedure.query(({ctx:n})=>({sessions:n.sessionManager.getAllSessions()})),getSession:m.procedure.input(B.object({id:B.string()})).query(({ctx:n,input:e})=>({session:n.sessionManager.getSession(e.id)||null})),getSettings:m.procedure.query(({ctx:n})=>({settings:n.sessionManager.getSettings()})),updateSettings:m.procedure.input(z).mutation(({ctx:n,input:e})=>(n.sessionManager.updateSettings(e.settings),{settings:n.sessionManager.getSettings()})),allowRequest:m.procedure.input($).mutation(({ctx:n,input:e})=>({success:n.sessionManager.allowRequest(e.requestId)})),mockRequest:m.procedure.input(F).mutation(({ctx:n,input:e})=>({success:n.sessionManager.mockRequest(e.requestId,e.content)})),preInterceptAllow:m.procedure.input(J).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptAllow(e.requestId)})),preInterceptEdit:m.procedure.input(H).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptEdit(e.requestId,e.editedBody)})),preInterceptUseCache:m.procedure.input(W).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptUseCache(e.requestId)})),preInterceptMock:m.procedure.input(G).mutation(({ctx:n,input:e})=>({success:n.sessionManager.preInterceptMock(e.requestId,e.mockContent)})),health:m.procedure.query(()=>({status:"ok",timestamp:new Date().toISOString()}))});import{createParser as Ie}from"eventsource-parser";var I=class{callbacks;toolCalls=new Map;accumulatedContent="";parser;constructor(e={}){this.callbacks=e,this.parser=Ie(t=>{t.type==="event"&&this.handleEvent(t.data)})}feed(e){this.parser.feed(e)}getContent(){return this.accumulatedContent}getToolCalls(){return Array.from(this.toolCalls.values())}hasToolCalls(){return this.toolCalls.size>0}getAssembledMessage(){let e=this.getToolCalls();return e.length>0?{role:"assistant",content:null,tool_calls:e.map(t=>({id:t.id,type:"function",function:{name:t.name,arguments:t.arguments}}))}:{role:"assistant",content:this.accumulatedContent||null}}reset(){this.toolCalls.clear(),this.accumulatedContent="",this.parser.reset()}handleEvent(e){if(e==="[DONE]"){this.callbacks.onDone?.();return}try{let t=JSON.parse(e);for(let o of t.choices){let s=o.delta;if(s.content&&(this.accumulatedContent+=s.content,this.callbacks.onContent?.(s.content)),s.tool_calls)for(let r of s.tool_calls){let i=this.toolCalls.get(r.index);if(i)r.function?.arguments&&(i.arguments+=r.function.arguments,this.callbacks.onToolCallUpdate?.(r.index,r.function.arguments));else{let c={index:r.index,id:r.id||"",name:r.function?.name||"",arguments:r.function?.arguments||""};this.toolCalls.set(r.index,c),this.callbacks.onToolCall?.(c)}}}}catch(t){this.callbacks.onError?.(t instanceof Error?t:new Error(String(t)))}}};function T(n){return new I(n)}var Te="https://api.openai.com";function Y(n){return!n||n.length<8?"****":`${n.substring(0,4)}...${n.substring(n.length-4)}`}function Ee(n,e=!0){let t=new Headers,o=["authorization","content-type","accept","openai-organization","openai-project","user-agent"];for(let s of o){let r=n[s];if(r){let i=Array.isArray(r)?r[0]:r;i&&t.set(s,i)}}return e?t.set("accept","text/event-stream"):t.set("accept","application/json"),t}async function E(n){let t=`${n.upstreamUrl||Te}${n.path}`,o=typeof n.body=="object"&&n.body!==null&&"stream"in n.body&&typeof n.body.stream=="boolean"?n.body.stream:!0,s=Ee(n.headers,o),r=await fetch(t,{method:n.method,headers:s,body:JSON.stringify(n.body)});return{status:r.status,headers:r.headers,body:r.body,ok:r.ok}}async function*V(n){let e=n.getReader(),t=new TextDecoder;try{for(;;){let{done:s,value:r}=await e.read();if(s)break;yield t.decode(r,{stream:!0})}let o=t.decode();o&&(yield o)}finally{e.releaseLock()}}import{mkdir as Me,writeFile as Ae}from"fs/promises";import{dirname as je,join as Oe}from"path";import{createHash as _e}from"crypto";function _(n){if(n==null)return null;if(Array.isArray(n))return n.map(_);if(typeof n=="object"){let e=n,t={},o=Object.keys(e).filter(s=>!["stream","request_id","timestamp"].includes(s)).sort();for(let s of o)t[s]=_(e[s]);return t}return n}function S(n){let e=_(n),t=JSON.stringify(e);return _e("sha256").update(t).digest("hex")}var Ne=".playingpack/tapes",w=class{tapesDir;chunks=[];lastChunkTime=0;requestBody;model="unknown";endpoint;hash="";constructor(e=Ne){this.tapesDir=e,this.endpoint="/v1/chat/completions"}start(e){if(this.requestBody=e,this.chunks=[],this.lastChunkTime=Date.now(),this.hash=S(e),typeof e=="object"&&e!==null){let t=e;typeof t.model=="string"&&(this.model=t.model)}}recordChunk(e){let t=Date.now(),o=this.chunks.length===0?0:t-this.lastChunkTime;this.chunks.push({c:e,d:o}),this.lastChunkTime=t}async save(e=200){let t={meta:{id:crypto.randomUUID(),hash:this.hash,timestamp:new Date().toISOString(),model:this.model,endpoint:this.endpoint},request:{body:this.requestBody},response:{status:e,chunks:this.chunks}},o=this.getTapePath();return await Me(je(o),{recursive:!0}),await Ae(o,JSON.stringify(t,null,2),"utf-8"),o}getTapePath(){return Oe(this.tapesDir,`${this.hash}.json`)}getHash(){return this.hash}getChunkCount(){return this.chunks.length}};import{readFile as De,access as Ue}from"fs/promises";import{join as Z}from"path";var Q=".playingpack/tapes";async function X(n,e=Q){let t=S(n),o=Z(e,`${t}.json`);try{return await Ue(o),!0}catch{return!1}}async function qe(n,e=Q){let t=S(n),o=Z(e,`${t}.json`);try{let s=await De(o,"utf-8");return JSON.parse(s)}catch{return null}}var M=class{tape;currentIndex=0;aborted=!1;constructor(e){this.tape=e}getMeta(){return this.tape.meta}getStatus(){return this.tape.response.status}abort(){this.aborted=!0}async*replay(){this.currentIndex=0,this.aborted=!1;for(let e of this.tape.response.chunks){if(this.aborted||(e.d>0&&await this.delay(e.d),this.aborted))break;yield e.c,this.currentIndex++}}async*replayFast(){for(let e of this.tape.response.chunks){if(this.aborted)break;yield e.c}}delay(e){return new Promise(t=>setTimeout(t,e))}};async function ee(n,e){let t=await qe(n,e);return t?new M(t):null}function A(){return`chatcmpl-mock-${Date.now()}`}function oe(n,e=4){let t=[];for(let o=0;o<n.length;o+=e)t.push(n.slice(o,o+e));return t}function k(n){return`data: ${n}
3
3
 
4
- `}function te(n,e,t,o=null){let s={id:n,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:e,choices:[{index:0,delta:t!==null?{content:t}:{},finish_reason:o}]};return JSON.stringify(s)}function ne(n,e,t,o,s,r=!1,i=null){let c={index:0};r?(c.id=t,c.type="function",c.function={name:o,arguments:s}):c.function={arguments:s};let p={id:n,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:e,choices:[{index:0,delta:{tool_calls:[c]},finish_reason:i}]};return JSON.stringify(p)}async function*se(n,e={}){let{model:t="gpt-4",delayMs:o=20}=e,s=A(),r=oe(n),i={id:s,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t,choices:[{index:0,delta:{role:"assistant",content:""},finish_reason:null}]};yield k(JSON.stringify(i));for(let c of r)o>0&&await le(o),yield k(te(s,t,c));yield k(te(s,t,null,"stop")),yield k("[DONE]")}async function*re(n,e,t={}){let{model:o="gpt-4",delayMs:s=10}=t,r=A(),i=`call_mock_${Date.now()}`,c=oe(e,10),p={id:r,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:o,choices:[{index:0,delta:{role:"assistant",content:null},finish_reason:null}]};yield k(JSON.stringify(p)),yield k(ne(r,o,i,n,c[0]||"",!0));for(let d=1;d<c.length;d++)s>0&&await le(s),yield k(ne(r,o,i,n,c[d]||""));let l={id:r,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:o,choices:[{index:0,delta:{},finish_reason:"tool_calls"}]};yield k(JSON.stringify(l)),yield k("[DONE]")}function ae(n,e="invalid_request_error",t=null){return JSON.stringify({error:{message:n,type:e,param:null,code:t}})}function ie(n,e,t="gpt-4"){let o=A(),s=Math.floor(Date.now()/1e3),r={role:"assistant",content:e?null:n};e&&e.length>0&&(r.tool_calls=e);let i=e&&e.length>0?"tool_calls":"stop";return{id:o,object:"chat.completion",created:s,model:t,choices:[{index:0,message:r,finish_reason:i}],usage:{prompt_tokens:0,completion_tokens:0,total_tokens:0}}}function ce(n){let e=n.trim();if(e.startsWith("ERROR:"))return{type:"error",content:e.slice(6).trim()};try{let t=JSON.parse(e);if(t&&typeof t=="object"&&"function"in t)return{type:"tool_call",functionName:t.function,content:JSON.stringify(t.arguments||{})}}catch{}return{type:"text",content:e}}function le(n){return new Promise(e=>setTimeout(e,n))}import{appendFile as Fe,mkdir as $e}from"fs/promises";import{join as Le}from"path";var j=null,O=null;async function pe(n){j=n,await $e(j,{recursive:!0});let e=new Date().toISOString().split("T")[0];O=Le(j,`server-${e}.log`)}function Je(n){let e=`[${n.timestamp}] [${n.level.toUpperCase()}] ${n.message}`;return n.data!==void 0?`${e} ${JSON.stringify(n.data)}
4
+ `}function te(n,e,t,o=null){let s={id:n,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:e,choices:[{index:0,delta:t!==null?{content:t}:{},finish_reason:o}]};return JSON.stringify(s)}function ne(n,e,t,o,s,r=!1,i=null){let c={index:0};r?(c.id=t,c.type="function",c.function={name:o,arguments:s}):c.function={arguments:s};let p={id:n,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:e,choices:[{index:0,delta:{tool_calls:[c]},finish_reason:i}]};return JSON.stringify(p)}async function*se(n,e={}){let{model:t="gpt-4",delayMs:o=20}=e,s=A(),r=oe(n),i={id:s,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t,choices:[{index:0,delta:{role:"assistant",content:""},finish_reason:null}]};yield k(JSON.stringify(i));for(let c of r)o>0&&await le(o),yield k(te(s,t,c));yield k(te(s,t,null,"stop")),yield k("[DONE]")}async function*re(n,e,t={}){let{model:o="gpt-4",delayMs:s=10}=t,r=A(),i=`call_mock_${Date.now()}`,c=oe(e,10),p={id:r,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:o,choices:[{index:0,delta:{role:"assistant",content:null},finish_reason:null}]};yield k(JSON.stringify(p)),yield k(ne(r,o,i,n,c[0]||"",!0));for(let d=1;d<c.length;d++)s>0&&await le(s),yield k(ne(r,o,i,n,c[d]||""));let l={id:r,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:o,choices:[{index:0,delta:{},finish_reason:"tool_calls"}]};yield k(JSON.stringify(l)),yield k("[DONE]")}function ae(n,e="invalid_request_error",t=null){return JSON.stringify({error:{message:n,type:e,param:null,code:t}})}function ie(n,e,t="gpt-4"){let o=A(),s=Math.floor(Date.now()/1e3),r={role:"assistant",content:e?null:n};e&&e.length>0&&(r.tool_calls=e);let i=e&&e.length>0?"tool_calls":"stop";return{id:o,object:"chat.completion",created:s,model:t,choices:[{index:0,message:r,finish_reason:i}],usage:{prompt_tokens:0,completion_tokens:0,total_tokens:0}}}function ce(n){let e=n.trim();if(e.startsWith("ERROR:"))return{type:"error",content:e.slice(6).trim()};try{let t=JSON.parse(e);if(t&&typeof t=="object"&&"function"in t)return{type:"tool_call",functionName:t.function,content:JSON.stringify(t.arguments||{})}}catch{}return{type:"text",content:e}}function le(n){return new Promise(e=>setTimeout(e,n))}import{appendFile as Le,mkdir as Fe}from"fs/promises";import{join as $e}from"path";var j=null,O=null;async function pe(n){j=n,await Fe(j,{recursive:!0});let e=new Date().toISOString().split("T")[0];O=$e(j,`server-${e}.log`)}function Je(n){let e=`[${n.timestamp}] [${n.level.toUpperCase()}] ${n.message}`;return n.data!==void 0?`${e} ${JSON.stringify(n.data)}
5
5
  `:`${e}
6
- `}async function R(n,e,t){if(!O)return;let o={timestamp:new Date().toISOString(),level:n,message:e,data:t};try{await Fe(O,Je(o))}catch{}}var f={info:(n,e)=>R("info",n,e),warn:(n,e)=>R("warn",n,e),error:(n,e)=>R("error",n,e),debug:(n,e)=>R("debug",n,e)};var y={upstream:"https://api.openai.com",tapesDir:".playingpack/tapes",record:"auto"};function ge(n,e){e&&(y=e),n.post("/v1/chat/completions",async(t,o)=>{await He(t,o)}),n.get("/health",async(t,o)=>o.send({status:"ok"})),n.all("/v1/*",async(t,o)=>{await ze(t,o)})}async function He(n,e){let t=h(),o=crypto.randomUUID(),s=n.body,r=s.stream!==!1,i=n.headers.authorization||"";console.log(`[${o.slice(0,8)}] POST /v1/chat/completions`),console.log(` Model: ${s.model||"unknown"}`),console.log(` Stream: ${r}`),console.log(` Auth: ${Y(i.replace("Bearer ",""))}`),f.info("Request received",{requestId:o,path:"/v1/chat/completions",model:s.model,stream:r}),t.createSession(o,"POST","/v1/chat/completions",s),t.updateState(o,"LOOKUP");let c=y.record!=="off",p=y.record==="auto",l=y.record==="replay-only",d=c&&await X(s,y.tapesDir);if(t.shouldPreIntercept(o)){console.log(` [PRE-INTERCEPT] Pausing before LLM call (cache ${d?"available":"not available"})`),f.info("Pre-intercept",{requestId:o,model:s.model,hasTape:d});let u=await t.preIntercept(o,d);switch(console.log(` [PRE-ACTION] ${u.action}`),u.action){case"use_cache":if(d){console.log(" [CACHE] Using cached response"),await ue(n,e,o,s,r);return}console.log(" [WARN] Cache requested but no tape available, proceeding to upstream");break;case"mock":console.log(" [MOCK] Sending mock response"),await N(e,o,u.mockContent||"",r);return;case"edit":u.editedBody&&(Object.assign(s,u.editedBody),console.log(" [EDIT] Request body modified"));break;case"allow":break}}else if(d){console.log(" [CACHE HIT] Replaying from tape"),f.info("Cache hit",{requestId:o,model:s.model}),await ue(n,e,o,s,r);return}if(l&&!d){console.log(" [REPLAY-ONLY] No tape found, rejecting request"),t.error(o,"No tape found (replay-only mode)"),e.code(404).send({error:{message:"No recorded tape found for this request (replay-only mode)",type:"tape_not_found"}});return}console.log(" [CACHE MISS] Forwarding to upstream"),f.info("Cache miss",{requestId:o,model:s.model}),t.updateState(o,"CONNECT");try{let u=await E({method:"POST",path:"/v1/chat/completions",headers:n.headers,body:s,upstreamUrl:y.upstream});if(!u.ok||!u.body){t.error(o,`Upstream error: ${u.status}`),e.code(u.status).header("content-type","application/json").send(await D(u.body));return}r?await We(n,e,o,u,s,p):await Ge(n,e,o,u,s,p)}catch(u){let g=u instanceof Error?u.message:"Unknown error";console.error(` [ERROR] ${g}`),f.error("Request failed",{requestId:o,error:g}),t.error(o,g),e.code(500).send({error:{message:g,type:"proxy_error"}})}}async function We(n,e,t,o,s,r){let i=h(),c=r?new w(y.tapesDir):null;c?.start(s),i.updateState(t,"STREAMING");let p=T({onToolCall:g=>{i.addToolCall(t,g),console.log(` [TOOL CALL] ${g.name}`)},onContent:g=>{i.updateContent(t,g)}});if(!o.body){e.raw.end();return}let l=[],d=V(o.body);for await(let g of d)p.feed(g),c?.recordChunk(g),l.push(g);let u=p.getAssembledMessage();if(i.setRawResponse(t,JSON.stringify(u,null,2)),c)try{let g=await c.save(o.status);console.log(` [TAPE] Saved to ${g}`)}catch(g){console.error(" [TAPE ERROR] Failed to save tape:",g)}if(i.shouldIntercept(t)){console.log(" [INTERCEPT] Pausing for user action");let g=await i.intercept(t);if(console.log(` [ACTION] ${g.action}`),g.action==="mock"){await N(e,t,g.mockContent||"",!0);return}}e.code(o.status).header("content-type","text/event-stream").header("cache-control","no-cache").header("connection","keep-alive");for(let g of l)e.raw.write(g);i.complete(t,o.status,!1),f.info("Request completed",{requestId:t,status:o.status,cached:!1}),e.raw.end()}async function Ge(n,e,t,o,s,r){let i=h(),c=r?new w(y.tapesDir):null;if(c?.start(s),i.updateState(t,"STREAMING"),!o.body){e.code(o.status).header("content-type","application/json").send("");return}let p=await D(o.body);c?.recordChunk(p);try{let l=JSON.parse(p);if(l.choices?.[0]?.message){let d=l.choices[0].message;if(d.content&&i.updateContent(t,d.content),d.tool_calls)for(let u of d.tool_calls)i.addToolCall(t,{index:0,id:u.id,name:u.function.name,arguments:u.function.arguments}),console.log(` [TOOL CALL] ${u.function.name}`)}i.setRawResponse(t,JSON.stringify(l,null,2))}catch{i.setRawResponse(t,p)}if(c)try{let l=await c.save(o.status);console.log(` [TAPE] Saved to ${l}`)}catch(l){console.error(" [TAPE ERROR] Failed to save tape:",l)}if(i.shouldIntercept(t)){console.log(" [INTERCEPT] Pausing for user action");let l=await i.intercept(t);if(console.log(` [ACTION] ${l.action}`),l.action==="mock"){await N(e,t,l.mockContent||"",!1);return}}e.code(o.status).header("content-type","application/json").send(p),i.complete(t,o.status,!1),f.info("Request completed",{requestId:t,status:o.status,cached:!1})}async function ue(n,e,t,o,s){let r=h();r.updateState(t,"REPLAY");let i=await ee(o,y.tapesDir);if(!i){r.error(t,"Failed to load tape"),e.code(500).send({error:{message:"Tape not found",type:"proxy_error"}});return}if(s){e.code(i.getStatus()).header("content-type","text/event-stream").header("cache-control","no-cache").header("connection","keep-alive").header("x-playingpack-cached","true");let c=T({onToolCall:l=>{r.addToolCall(t,l),console.log(` [TOOL CALL] ${l.name}`)},onContent:l=>{r.updateContent(t,l)}});for await(let l of i.replay())c.feed(l),e.raw.write(l);let p=c.getAssembledMessage();r.setRawResponse(t,JSON.stringify(p,null,2)),r.complete(t,i.getStatus(),!0),f.info("Request completed",{requestId:t,status:i.getStatus(),cached:!0}),e.raw.end()}else{let c="";for await(let p of i.replay())c+=p;try{let p=JSON.parse(c);if(p.choices?.[0]?.message){let l=p.choices[0].message;if(l.content&&r.updateContent(t,l.content),l.tool_calls)for(let d of l.tool_calls)r.addToolCall(t,{index:0,id:d.id,name:d.function.name,arguments:d.function.arguments}),console.log(` [TOOL CALL] ${d.function.name}`)}r.setRawResponse(t,JSON.stringify(p,null,2))}catch{r.setRawResponse(t,c)}e.code(i.getStatus()).header("content-type","application/json").header("x-playingpack-cached","true").send(c),r.complete(t,i.getStatus(),!0),f.info("Request completed",{requestId:t,status:i.getStatus(),cached:!0})}}async function N(n,e,t,o=!0){let s=h(),r=ce(t);if(r.type==="error"){n.code(400).header("content-type","application/json").send(ae(r.content)),s.complete(e,400,!1);return}if(o){n.code(200).header("content-type","text/event-stream").header("cache-control","no-cache").header("connection","keep-alive").header("x-playingpack-mocked","true");let i=r.type==="tool_call"?re(r.functionName||"mock_function",r.content):se(r.content);for await(let c of i)n.raw.write(c);s.complete(e,200,!1),n.raw.end()}else{let i=r.type==="tool_call"?[{id:`call_mock_${Date.now()}`,type:"function",function:{name:r.functionName||"mock_function",arguments:r.content}}]:void 0,c=r.type==="text"?r.content:null,p=ie(c,i);n.code(200).header("content-type","application/json").header("x-playingpack-mocked","true").send(JSON.stringify(p)),s.complete(e,200,!1)}}async function ze(n,e){try{let t=await E({method:n.method,path:n.url,headers:n.headers,body:n.body,upstreamUrl:y.upstream});if(e.code(t.status),t.headers.forEach((o,s)=>{["content-encoding","transfer-encoding"].includes(s.toLowerCase())||e.header(s,o)}),t.body){let o=await D(t.body);e.send(o)}else e.send()}catch(t){let o=t instanceof Error?t.message:"Unknown error";e.code(500).send({error:{message:o,type:"proxy_error"}})}}async function D(n){if(!n)return"";let e=n.getReader(),t=new TextDecoder,o="";for(;;){let{done:s,value:r}=await e.read();if(s)break;o+=t.decode(r,{stream:!0})}return o}import{join as de,dirname as Be}from"path";import{fileURLToPath as Ke}from"url";import{access as Ye}from"fs/promises";import Ve from"@fastify/static";var Ze=Be(Ke(import.meta.url));function fe(){return de(Ze,"../public")}async function Qe(){try{return await Ye(de(fe(),"index.html")),!0}catch{return!1}}async function me(n){let e=fe();return await Qe()?(await n.register(Ve,{root:e,prefix:"/",wildcard:!1}),n.setNotFoundHandler((o,s)=>o.url.startsWith("/v1")||o.url.startsWith("/api")||o.url.startsWith("/ws")?s.code(404).send({error:"Not Found"}):s.sendFile("index.html")),console.log(" UI available at root path"),!0):(console.log(" UI not available (development mode)"),console.log(" Run the UI separately: cd packages/web && pnpm dev"),!1)}var P=new Set;function he(n){P.add(n);let e=h(),t=e.getAllSessions();for(let s of t)U(n,{type:"request_update",session:s});let o=e.subscribe(s=>{U(n,s)});n.on("message",s=>{try{let r=JSON.parse(s.toString());Xe(n,r)}catch{}}),n.on("close",()=>{P.delete(n),o()}),n.on("error",()=>{P.delete(n),o()})}function Xe(n,e){if(typeof e!="object"||e===null)return;let t=e,o=h();switch(t.type){case"allow":typeof t.requestId=="string"&&o.allowRequest(t.requestId);break;case"mock":typeof t.requestId=="string"&&typeof t.content=="string"&&o.mockRequest(t.requestId,t.content);break;case"pre_allow":typeof t.requestId=="string"&&o.preInterceptAllow(t.requestId);break;case"pre_edit":typeof t.requestId=="string"&&typeof t.editedBody=="object"&&o.preInterceptEdit(t.requestId,t.editedBody);break;case"pre_use_cache":typeof t.requestId=="string"&&o.preInterceptUseCache(t.requestId);break;case"pre_mock":typeof t.requestId=="string"&&typeof t.mockContent=="string"&&o.preInterceptMock(t.requestId,t.mockContent);break;case"ping":U(n,{type:"pong"});break}}function U(n,e){if(n.readyState===n.OPEN)try{n.send(JSON.stringify(e))}catch{P.delete(n)}}async function ye(n){let e=n.port,t=n.host;q({pause:n.pause}),await pe(n.logsDir),await f.info("Server starting",{upstream:n.upstream,tapesDir:n.tapesDir,record:n.record,headless:n.headless});let o=await st({port:e});o!==e&&console.log(` Port ${e} in use, using ${o}`);let s=et({logger:!1,bodyLimit:50*1024*1024});return await s.register(tt,{origin:!0,credentials:!0}),await s.register(nt),s.get("/ws",{websocket:!0},r=>{he(r)}),await s.register(ot,{prefix:"/api/trpc",trpcOptions:{router:K,createContext:F}}),ge(s,{upstream:n.upstream,tapesDir:n.tapesDir,record:n.record}),n.headless?console.log(" Running in headless mode (no UI)"):await me(s),await s.listen({port:o,host:t}),await f.info("Server listening",{port:o,host:t}),{server:s,port:o,host:t}}import{readFile as rt}from"fs/promises";import{existsSync as at}from"fs";import{join as ke}from"path";import{createJiti as it}from"jiti";var ct=["playingpack.config.ts","playingpack.config.mts","playingpack.config.js","playingpack.config.mjs"],lt=["playingpack.config.jsonc","playingpack.config.json",".playingpackrc.json",".playingpackrc"],pt={upstream:"https://api.openai.com",tapesDir:".playingpack/tapes",logsDir:".playingpack/logs",record:"auto",headless:!1,port:4747,host:"0.0.0.0",pause:"off"};function ut(n){let e=n.replace(/\/\*[\s\S]*?\*\//g,"");return e=e.replace(/(?<!["'])\/\/.*$/gm,""),e}async function gt(n){for(let e of ct){let t=ke(n,e);if(at(t))try{let s=await it(import.meta.url,{interopDefault:!0}).import(t),r=s&&typeof s=="object"&&"default"in s?s.default:s;if(!r||typeof r!="object"){console.warn(` Warning: ${e} must export a config object`);continue}return{config:x.parse(r),filename:e}}catch(o){console.warn(` Warning: Error loading ${e}:`,o.message)}}return null}async function dt(n){for(let e of lt){let t=ke(n,e);try{let o=await rt(t,"utf-8"),s=JSON.parse(ut(o));return{config:x.parse(s),filename:e}}catch(o){o.code!=="ENOENT"&&(o instanceof SyntaxError||o.name==="ZodError")&&console.warn(` Warning: Invalid config in ${e}:`,o.message)}}return null}async function ft(n){let e=await gt(n);if(e)return console.log(` Config loaded from ${e.filename}`),e.config;let t=await dt(n);return t?(console.log(` Config loaded from ${t.filename}`),t.config):{}}async function be(n={}){let e=process.cwd(),t=await ft(e),o={...pt,...t};return n.port!==void 0&&(o.port=n.port),n.host!==void 0&&(o.host=n.host),n.ui!==void 0&&(o.headless=!n.ui),n.upstream!==void 0&&(o.upstream=n.upstream),n.tapesDir!==void 0&&(o.tapesDir=n.tapesDir),n.record!==void 0&&(o.record=n.record),o}var Se="1.0.0";v.name("playingpack").description("Chrome DevTools for AI Agents - Local reverse proxy and debugger").version(Se);v.command("start").description("Start the PlayingPack proxy server").option("-p, --port <port>","Port to listen on").option("-h, --host <host>","Host to bind to").option("--no-ui","Run without UI server (headless mode for CI/CD)").option("--upstream <url>","Upstream API URL (default: https://api.openai.com)").option("--tapes-dir <path>","Directory for tape storage (default: .playingpack/tapes)").option("--record <mode>","Recording mode: auto, off, replay-only (default: auto)").action(async n=>{console.log(),console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.log(" \u2551 \u2551"),console.log(" \u2551 \u2593\u2593\u2593\u2593 PlayingPack - The Flight Simulator \u2593\u2593\u2593\u2593 \u2551"),console.log(" \u2551 Chrome DevTools for AI Agents \u2551"),console.log(" \u2551 \u2551"),console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),console.log();try{let e=await be({port:n.port?parseInt(n.port,10):void 0,host:n.host,ui:n.ui,upstream:n.upstream,tapesDir:n.tapesDir,record:n.record}),{port:t,host:o}=await ye(e),s=`http://localhost:${t}`,r=o==="0.0.0.0"?`http://<your-ip>:${t}`:`http://${o}:${t}`;console.log(),console.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log(" \u2502 Server running! \u2502"),console.log(" \u2502 \u2502"),console.log(` \u2502 Local: ${s.padEnd(44)}\u2502`),console.log(` \u2502 Network: ${r.padEnd(44)}\u2502`),console.log(" \u2502 \u2502"),console.log(" \u2502 To use with your AI agent, set: \u2502"),console.log(` \u2502 baseURL = "${s}/v1"`.padEnd(60)+"\u2502"),e.headless||(console.log(" \u2502 \u2502"),console.log(" \u2502 Dashboard: Open the local URL in your browser \u2502")),console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),console.log(),console.log(" Waiting for requests..."),console.log();let i=async()=>{console.log(`
6
+ `}async function R(n,e,t){if(!O)return;let o={timestamp:new Date().toISOString(),level:n,message:e,data:t};try{await Le(O,Je(o))}catch{}}var f={info:(n,e)=>R("info",n,e),warn:(n,e)=>R("warn",n,e),error:(n,e)=>R("error",n,e),debug:(n,e)=>R("debug",n,e)};var y={upstream:"https://api.openai.com",tapesDir:".playingpack/tapes",record:"auto"};function ge(n,e){e&&(y=e),n.post("/v1/chat/completions",async(t,o)=>{await He(t,o)}),n.get("/health",async(t,o)=>o.send({status:"ok"})),n.all("/v1/*",async(t,o)=>{await ze(t,o)})}async function He(n,e){let t=h(),o=crypto.randomUUID(),s=n.body,r=s.stream!==!1,i=n.headers.authorization||"";console.log(`[${o.slice(0,8)}] POST /v1/chat/completions`),console.log(` Model: ${s.model||"unknown"}`),console.log(` Stream: ${r}`),console.log(` Auth: ${Y(i.replace("Bearer ",""))}`),f.info("Request received",{requestId:o,path:"/v1/chat/completions",model:s.model,stream:r}),t.createSession(o,"POST","/v1/chat/completions",s),t.updateState(o,"LOOKUP");let c=y.record!=="off",p=y.record==="auto",l=y.record==="replay-only",d=c&&await X(s,y.tapesDir);if(t.shouldPreIntercept(o)){console.log(` [PRE-INTERCEPT] Pausing before LLM call (cache ${d?"available":"not available"})`),f.info("Pre-intercept",{requestId:o,model:s.model,hasTape:d});let u=await t.preIntercept(o,d);switch(console.log(` [PRE-ACTION] ${u.action}`),u.action){case"use_cache":if(d){console.log(" [CACHE] Using cached response"),await ue(n,e,o,s,r);return}console.log(" [WARN] Cache requested but no tape available, proceeding to upstream");break;case"mock":console.log(" [MOCK] Sending mock response"),await N(e,o,u.mockContent||"",r);return;case"edit":u.editedBody&&(Object.assign(s,u.editedBody),console.log(" [EDIT] Request body modified"));break;case"allow":break}}else if(d){console.log(" [CACHE HIT] Replaying from tape"),f.info("Cache hit",{requestId:o,model:s.model}),await ue(n,e,o,s,r);return}if(l&&!d){console.log(" [REPLAY-ONLY] No tape found, rejecting request"),t.error(o,"No tape found (replay-only mode)"),e.code(404).send({error:{message:"No recorded tape found for this request (replay-only mode)",type:"tape_not_found"}});return}console.log(" [CACHE MISS] Forwarding to upstream"),f.info("Cache miss",{requestId:o,model:s.model}),t.updateState(o,"CONNECT");try{let u=await E({method:"POST",path:"/v1/chat/completions",headers:n.headers,body:s,upstreamUrl:y.upstream});if(!u.ok||!u.body){t.error(o,`Upstream error: ${u.status}`),e.code(u.status).header("content-type","application/json").send(await D(u.body));return}r?await We(n,e,o,u,s,p):await Ge(n,e,o,u,s,p)}catch(u){let g=u instanceof Error?u.message:"Unknown error";console.error(` [ERROR] ${g}`),f.error("Request failed",{requestId:o,error:g}),t.error(o,g),e.code(500).send({error:{message:g,type:"proxy_error"}})}}async function We(n,e,t,o,s,r){let i=h(),c=r?new w(y.tapesDir):null;c?.start(s),i.updateState(t,"STREAMING");let p=T({onToolCall:g=>{i.addToolCall(t,g),console.log(` [TOOL CALL] ${g.name}`)},onContent:g=>{i.updateContent(t,g)}});if(!o.body){e.raw.end();return}let l=[],d=V(o.body);for await(let g of d)p.feed(g),c?.recordChunk(g),l.push(g);let u=p.getAssembledMessage();if(i.setRawResponse(t,JSON.stringify(u,null,2)),c)try{let g=await c.save(o.status);console.log(` [TAPE] Saved to ${g}`)}catch(g){console.error(" [TAPE ERROR] Failed to save tape:",g)}if(i.shouldIntercept(t)){console.log(" [INTERCEPT] Pausing for user action");let g=await i.intercept(t);if(console.log(` [ACTION] ${g.action}`),g.action==="mock"){await N(e,t,g.mockContent||"",!0);return}}e.code(o.status).header("content-type","text/event-stream").header("cache-control","no-cache").header("connection","keep-alive");for(let g of l)e.raw.write(g);i.complete(t,o.status,!1),f.info("Request completed",{requestId:t,status:o.status,cached:!1}),e.raw.end()}async function Ge(n,e,t,o,s,r){let i=h(),c=r?new w(y.tapesDir):null;if(c?.start(s),i.updateState(t,"STREAMING"),!o.body){e.code(o.status).header("content-type","application/json").send("");return}let p=await D(o.body);c?.recordChunk(p);try{let l=JSON.parse(p);if(l.choices?.[0]?.message){let d=l.choices[0].message;if(d.content&&i.updateContent(t,d.content),d.tool_calls)for(let u of d.tool_calls)i.addToolCall(t,{index:0,id:u.id,name:u.function.name,arguments:u.function.arguments}),console.log(` [TOOL CALL] ${u.function.name}`)}i.setRawResponse(t,JSON.stringify(l,null,2))}catch{i.setRawResponse(t,p)}if(c)try{let l=await c.save(o.status);console.log(` [TAPE] Saved to ${l}`)}catch(l){console.error(" [TAPE ERROR] Failed to save tape:",l)}if(i.shouldIntercept(t)){console.log(" [INTERCEPT] Pausing for user action");let l=await i.intercept(t);if(console.log(` [ACTION] ${l.action}`),l.action==="mock"){await N(e,t,l.mockContent||"",!1);return}}e.code(o.status).header("content-type","application/json").send(p),i.complete(t,o.status,!1),f.info("Request completed",{requestId:t,status:o.status,cached:!1})}async function ue(n,e,t,o,s){let r=h();r.updateState(t,"REPLAY");let i=await ee(o,y.tapesDir);if(!i){r.error(t,"Failed to load tape"),e.code(500).send({error:{message:"Tape not found",type:"proxy_error"}});return}if(s){e.code(i.getStatus()).header("content-type","text/event-stream").header("cache-control","no-cache").header("connection","keep-alive").header("x-playingpack-cached","true");let c=T({onToolCall:l=>{r.addToolCall(t,l),console.log(` [TOOL CALL] ${l.name}`)},onContent:l=>{r.updateContent(t,l)}});for await(let l of i.replay())c.feed(l),e.raw.write(l);let p=c.getAssembledMessage();r.setRawResponse(t,JSON.stringify(p,null,2)),r.complete(t,i.getStatus(),!0),f.info("Request completed",{requestId:t,status:i.getStatus(),cached:!0}),e.raw.end()}else{let c="";for await(let p of i.replay())c+=p;try{let p=JSON.parse(c);if(p.choices?.[0]?.message){let l=p.choices[0].message;if(l.content&&r.updateContent(t,l.content),l.tool_calls)for(let d of l.tool_calls)r.addToolCall(t,{index:0,id:d.id,name:d.function.name,arguments:d.function.arguments}),console.log(` [TOOL CALL] ${d.function.name}`)}r.setRawResponse(t,JSON.stringify(p,null,2))}catch{r.setRawResponse(t,c)}e.code(i.getStatus()).header("content-type","application/json").header("x-playingpack-cached","true").send(c),r.complete(t,i.getStatus(),!0),f.info("Request completed",{requestId:t,status:i.getStatus(),cached:!0})}}async function N(n,e,t,o=!0){let s=h(),r=ce(t);if(r.type==="error"){n.code(400).header("content-type","application/json").send(ae(r.content)),s.complete(e,400,!1);return}if(o){n.code(200).header("content-type","text/event-stream").header("cache-control","no-cache").header("connection","keep-alive").header("x-playingpack-mocked","true");let i=r.type==="tool_call"?re(r.functionName||"mock_function",r.content):se(r.content);for await(let c of i)n.raw.write(c);s.complete(e,200,!1),n.raw.end()}else{let i=r.type==="tool_call"?[{id:`call_mock_${Date.now()}`,type:"function",function:{name:r.functionName||"mock_function",arguments:r.content}}]:void 0,c=r.type==="text"?r.content:null,p=ie(c,i);n.code(200).header("content-type","application/json").header("x-playingpack-mocked","true").send(JSON.stringify(p)),s.complete(e,200,!1)}}async function ze(n,e){try{let t=await E({method:n.method,path:n.url,headers:n.headers,body:n.body,upstreamUrl:y.upstream});if(e.code(t.status),t.headers.forEach((o,s)=>{["content-encoding","transfer-encoding"].includes(s.toLowerCase())||e.header(s,o)}),t.body){let o=await D(t.body);e.send(o)}else e.send()}catch(t){let o=t instanceof Error?t.message:"Unknown error";e.code(500).send({error:{message:o,type:"proxy_error"}})}}async function D(n){if(!n)return"";let e=n.getReader(),t=new TextDecoder,o="";for(;;){let{done:s,value:r}=await e.read();if(s)break;o+=t.decode(r,{stream:!0})}return o}import{join as de,dirname as Be}from"path";import{fileURLToPath as Ke}from"url";import{access as Ye}from"fs/promises";import Ve from"@fastify/static";var Ze=Be(Ke(import.meta.url));function fe(){return de(Ze,"../public")}async function Qe(){try{return await Ye(de(fe(),"index.html")),!0}catch{return!1}}async function me(n){let e=fe();return await Qe()?(await n.register(Ve,{root:e,prefix:"/",wildcard:!1}),n.setNotFoundHandler((o,s)=>o.url.startsWith("/v1")||o.url.startsWith("/api")||o.url.startsWith("/ws")?s.code(404).send({error:"Not Found"}):s.sendFile("index.html")),console.log(" UI available at root path"),!0):(console.log(" UI not available (development mode)"),console.log(" Run the UI separately: cd packages/web && pnpm dev"),!1)}var P=new Set;function he(n){P.add(n);let e=h(),t=e.getAllSessions();for(let s of t)U(n,{type:"request_update",session:s});let o=e.subscribe(s=>{U(n,s)});n.on("message",s=>{try{let r=JSON.parse(s.toString());Xe(n,r)}catch{}}),n.on("close",()=>{P.delete(n),o()}),n.on("error",()=>{P.delete(n),o()})}function Xe(n,e){if(typeof e!="object"||e===null)return;let t=e,o=h();switch(t.type){case"allow":typeof t.requestId=="string"&&o.allowRequest(t.requestId);break;case"mock":typeof t.requestId=="string"&&typeof t.content=="string"&&o.mockRequest(t.requestId,t.content);break;case"pre_allow":typeof t.requestId=="string"&&o.preInterceptAllow(t.requestId);break;case"pre_edit":typeof t.requestId=="string"&&typeof t.editedBody=="object"&&o.preInterceptEdit(t.requestId,t.editedBody);break;case"pre_use_cache":typeof t.requestId=="string"&&o.preInterceptUseCache(t.requestId);break;case"pre_mock":typeof t.requestId=="string"&&typeof t.mockContent=="string"&&o.preInterceptMock(t.requestId,t.mockContent);break;case"ping":U(n,{type:"pong"});break}}function U(n,e){if(n.readyState===n.OPEN)try{n.send(JSON.stringify(e))}catch{P.delete(n)}}async function ye(n){let e=n.port,t=n.host;q({pause:n.pause}),await pe(n.logsDir),await f.info("Server starting",{upstream:n.upstream,tapesDir:n.tapesDir,record:n.record,headless:n.headless});let o=await st({port:e});o!==e&&console.log(` Port ${e} in use, using ${o}`);let s=et({logger:!1,bodyLimit:50*1024*1024});return await s.register(tt,{origin:!0,credentials:!0}),await s.register(nt),s.get("/ws",{websocket:!0},r=>{he(r)}),await s.register(ot,{prefix:"/api/trpc",trpcOptions:{router:K,createContext:L}}),ge(s,{upstream:n.upstream,tapesDir:n.tapesDir,record:n.record}),n.headless?console.log(" Running in headless mode (no UI)"):await me(s),await s.listen({port:o,host:t}),await f.info("Server listening",{port:o,host:t}),{server:s,port:o,host:t}}import{readFile as rt}from"fs/promises";import{existsSync as at}from"fs";import{join as ke}from"path";import{createJiti as it}from"jiti";var ct=["playingpack.config.ts","playingpack.config.mts","playingpack.config.js","playingpack.config.mjs"],lt=["playingpack.config.jsonc","playingpack.config.json",".playingpackrc.json",".playingpackrc"],pt={upstream:"https://api.openai.com",tapesDir:".playingpack/tapes",logsDir:".playingpack/logs",record:"auto",headless:!1,port:4747,host:"0.0.0.0",pause:"off"};function ut(n){let e=n.replace(/\/\*[\s\S]*?\*\//g,"");return e=e.replace(/(?<!["'])\/\/.*$/gm,""),e}async function gt(n){for(let e of ct){let t=ke(n,e);if(at(t))try{let s=await it(import.meta.url,{interopDefault:!0}).import(t),r=s&&typeof s=="object"&&"default"in s?s.default:s;if(!r||typeof r!="object"){console.warn(` Warning: ${e} must export a config object`);continue}return{config:x.parse(r),filename:e}}catch(o){console.warn(` Warning: Error loading ${e}:`,o.message)}}return null}async function dt(n){for(let e of lt){let t=ke(n,e);try{let o=await rt(t,"utf-8"),s=JSON.parse(ut(o));return{config:x.parse(s),filename:e}}catch(o){o.code!=="ENOENT"&&(o instanceof SyntaxError||o.name==="ZodError")&&console.warn(` Warning: Invalid config in ${e}:`,o.message)}}return null}async function ft(n){let e=await gt(n);if(e)return console.log(` Config loaded from ${e.filename}`),e.config;let t=await dt(n);return t?(console.log(` Config loaded from ${t.filename}`),t.config):{}}async function be(n={}){let e=process.cwd(),t=await ft(e),o={...pt,...t};return n.port!==void 0&&(o.port=n.port),n.host!==void 0&&(o.host=n.host),n.ui!==void 0&&(o.headless=!n.ui),n.upstream!==void 0&&(o.upstream=n.upstream),n.tapesDir!==void 0&&(o.tapesDir=n.tapesDir),n.record!==void 0&&(o.record=n.record),o}var Se="1.0.0";v.name("playingpack").description("Chrome DevTools for AI Agents - Local reverse proxy and debugger").version(Se);v.command("start").description("Start the PlayingPack proxy server").option("-p, --port <port>","Port to listen on").option("-h, --host <host>","Host to bind to").option("--no-ui","Run without UI server (headless mode for CI/CD)").option("--upstream <url>","Upstream API URL (default: https://api.openai.com)").option("--tapes-dir <path>","Directory for tape storage (default: .playingpack/tapes)").option("--record <mode>","Recording mode: auto, off, replay-only (default: auto)").action(async n=>{console.log(),console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.log(" \u2551 \u2551"),console.log(" \u2551 \u2593\u2593\u2593\u2593 PlayingPack - The Flight Simulator \u2593\u2593\u2593\u2593 \u2551"),console.log(" \u2551 Chrome DevTools for AI Agents \u2551"),console.log(" \u2551 \u2551"),console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),console.log();try{let e=await be({port:n.port?parseInt(n.port,10):void 0,host:n.host,ui:n.ui,upstream:n.upstream,tapesDir:n.tapesDir,record:n.record}),{port:t,host:o}=await ye(e),s=`http://localhost:${t}`,r=o==="0.0.0.0"?`http://<your-ip>:${t}`:`http://${o}:${t}`;console.log(),console.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),console.log(" \u2502 Server running! \u2502"),console.log(" \u2502 \u2502"),console.log(` \u2502 Local: ${s.padEnd(44)}\u2502`),console.log(` \u2502 Network: ${r.padEnd(44)}\u2502`),console.log(" \u2502 \u2502"),console.log(" \u2502 To use with your AI agent, set: \u2502"),console.log(` \u2502 baseURL = "${s}/v1"`.padEnd(60)+"\u2502"),e.headless||(console.log(" \u2502 \u2502"),console.log(" \u2502 Dashboard: Open the local URL in your browser \u2502")),console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),console.log(),console.log(" Waiting for requests..."),console.log();let i=async()=>{console.log(`
7
7
  Shutting down...`),process.exit(0)};process.on("SIGINT",i),process.on("SIGTERM",i)}catch(e){console.error("Failed to start server:",e),process.exit(1)}});v.command("version").description("Show version").action(()=>{console.log(`playingpack v${Se}`)});process.argv.length===2?v.parse(["node","playingpack","start"]):v.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playingpack",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Chrome DevTools for AI Agents - Local reverse proxy and debugger for LLM API calls",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,16 +29,6 @@
29
29
  "url": "https://github.com/playingpack/playingpack/issues"
30
30
  },
31
31
  "author": "Geoptly Intelligence Inc.",
32
- "scripts": {
33
- "dev": "tsx watch src/index.ts",
34
- "build": "tsup",
35
- "start": "node dist/index.js",
36
- "test": "vitest",
37
- "test:run": "vitest run",
38
- "test:coverage": "vitest run --coverage",
39
- "typecheck": "tsc --noEmit",
40
- "prepublishOnly": "cd ../.. && pnpm run build:all"
41
- },
42
32
  "dependencies": {
43
33
  "jiti": "^2.4.2",
44
34
  "@fastify/cors": "^9.0.1",
@@ -77,5 +67,14 @@
77
67
  "publishConfig": {
78
68
  "access": "public",
79
69
  "registry": "https://registry.npmjs.org/"
70
+ },
71
+ "scripts": {
72
+ "dev": "tsx watch src/index.ts",
73
+ "build": "tsup",
74
+ "start": "node dist/index.js",
75
+ "test": "vitest",
76
+ "test:run": "vitest run",
77
+ "test:coverage": "vitest run --coverage",
78
+ "typecheck": "tsc --noEmit"
80
79
  }
81
- }
80
+ }
@@ -1 +1 @@
1
- @import"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap";*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,SF Mono,Monaco,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-48{height:12rem}.h-64{height:16rem}.h-full{height:100%}.h-screen{height:100vh}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-80{width:20rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-500\/30{border-color:#f59e0b4d}.border-blue-400{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.border-blue-500\/30{border-color:#3b82f64d}.border-cyan-500\/30{border-color:#06b6d44d}.border-gray-500\/30{border-color:#6b72804d}.border-green-500\/30{border-color:#22c55e4d}.border-orange-500\/30{border-color:#f973164d}.border-pp-light{--tw-border-opacity: 1;border-color:rgb(42 42 42 / var(--tw-border-opacity, 1))}.border-purple-500\/20{border-color:#a855f733}.border-purple-500\/30{border-color:#a855f74d}.border-red-500\/20{border-color:#ef444433}.border-red-500\/30{border-color:#ef44444d}.border-teal-500\/30{border-color:#14b8a64d}.border-l-amber-500{--tw-border-opacity: 1;border-left-color:rgb(245 158 11 / var(--tw-border-opacity, 1))}.border-l-blue-500{--tw-border-opacity: 1;border-left-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-l-orange-500{--tw-border-opacity: 1;border-left-color:rgb(249 115 22 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-blue-500\/10{background-color:#3b82f61a}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-cyan-500\/10{background-color:#06b6d41a}.bg-gray-500\/10{background-color:#6b72801a}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity, 1))}.bg-orange-500\/10{background-color:#f973161a}.bg-pp-accent{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-pp-dark{--tw-bg-opacity: 1;background-color:rgb(15 15 15 / var(--tw-bg-opacity, 1))}.bg-pp-darker{--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity, 1))}.bg-pp-gray{--tw-bg-opacity: 1;background-color:rgb(26 26 26 / var(--tw-bg-opacity, 1))}.bg-purple-500\/10{background-color:#a855f71a}.bg-purple-600{--tw-bg-opacity: 1;background-color:rgb(147 51 234 / var(--tw-bg-opacity, 1))}.bg-purple-900\/50{background-color:#581c8780}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-teal-500\/10{background-color:#14b8a61a}.bg-teal-600{--tw-bg-opacity: 1;background-color:rgb(13 148 136 / var(--tw-bg-opacity, 1))}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.font-mono{font-family:JetBrains Mono,SF Mono,Monaco,monospace}.font-sans{font-family:Inter,system-ui,sans-serif}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.tracking-tight{letter-spacing:-.025em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-cyan-400{--tw-text-opacity: 1;color:rgb(34 211 238 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-pp-accent{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-purple-300{--tw-text-opacity: 1;color:rgb(216 180 254 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-teal-400{--tw-text-opacity: 1;color:rgb(45 212 191 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:root{font-size:14px;line-height:1.5;font-weight:400;color:#e0e0e0;background-color:#0f0f0f;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{margin:0;min-height:100vh}#root{min-height:100vh}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#1a1a1a}::-webkit-scrollbar-thumb{background:#3a3a3a;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#4a4a4a}.json-key{color:#7dd3fc}.json-string{color:#86efac}.json-number{color:#fbbf24}.json-boolean{color:#c084fc}.json-null{color:#94a3b8}.status-pending{color:#fbbf24}.status-success{color:#22c55e}.status-error{color:#ef4444}.status-paused{color:#3b82f6}@keyframes pulse-border{0%,to{border-color:#3b82f680}50%{border-color:#3b82f6}}.paused-border{animation:pulse-border 2s ease-in-out infinite}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-pp-gray\/50:hover{background-color:#1a1a1a80}.hover\:bg-pp-light:hover{--tw-bg-opacity: 1;background-color:rgb(42 42 42 / var(--tw-bg-opacity, 1))}.hover\:bg-purple-700:hover{--tw-bg-opacity: 1;background-color:rgb(126 34 206 / var(--tw-bg-opacity, 1))}.hover\:bg-teal-700:hover{--tw-bg-opacity: 1;background-color:rgb(15 118 110 / var(--tw-bg-opacity, 1))}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}
1
+ @import"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap";*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,SF Mono,Monaco,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-48{height:12rem}.h-64{height:16rem}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:0px}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-80{width:20rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-500\/30{border-color:#f59e0b4d}.border-blue-400{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.border-blue-500\/30{border-color:#3b82f64d}.border-cyan-500\/30{border-color:#06b6d44d}.border-gray-500\/30{border-color:#6b72804d}.border-green-500\/30{border-color:#22c55e4d}.border-orange-500\/30{border-color:#f973164d}.border-pp-light{--tw-border-opacity: 1;border-color:rgb(42 42 42 / var(--tw-border-opacity, 1))}.border-purple-500\/20{border-color:#a855f733}.border-purple-500\/30{border-color:#a855f74d}.border-red-500\/20{border-color:#ef444433}.border-red-500\/30{border-color:#ef44444d}.border-teal-500\/30{border-color:#14b8a64d}.border-l-amber-500{--tw-border-opacity: 1;border-left-color:rgb(245 158 11 / var(--tw-border-opacity, 1))}.border-l-blue-500{--tw-border-opacity: 1;border-left-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-l-orange-500{--tw-border-opacity: 1;border-left-color:rgb(249 115 22 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-500\/10{background-color:#3b82f61a}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-cyan-500\/10{background-color:#06b6d41a}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.bg-gray-500\/10{background-color:#6b72801a}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity, 1))}.bg-orange-500\/10{background-color:#f973161a}.bg-pp-accent{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-pp-dark{--tw-bg-opacity: 1;background-color:rgb(15 15 15 / var(--tw-bg-opacity, 1))}.bg-pp-darker{--tw-bg-opacity: 1;background-color:rgb(10 10 10 / var(--tw-bg-opacity, 1))}.bg-pp-gray{--tw-bg-opacity: 1;background-color:rgb(26 26 26 / var(--tw-bg-opacity, 1))}.bg-purple-400{--tw-bg-opacity: 1;background-color:rgb(192 132 252 / var(--tw-bg-opacity, 1))}.bg-purple-500\/10{background-color:#a855f71a}.bg-purple-600{--tw-bg-opacity: 1;background-color:rgb(147 51 234 / var(--tw-bg-opacity, 1))}.bg-purple-900\/50{background-color:#581c8780}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-teal-400{--tw-bg-opacity: 1;background-color:rgb(45 212 191 / var(--tw-bg-opacity, 1))}.bg-teal-500\/10{background-color:#14b8a61a}.bg-teal-600{--tw-bg-opacity: 1;background-color:rgb(13 148 136 / var(--tw-bg-opacity, 1))}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.font-mono{font-family:JetBrains Mono,SF Mono,Monaco,monospace}.font-sans{font-family:Inter,system-ui,sans-serif}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.tracking-tight{letter-spacing:-.025em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-cyan-400{--tw-text-opacity: 1;color:rgb(34 211 238 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-pp-accent{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-purple-300{--tw-text-opacity: 1;color:rgb(216 180 254 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-teal-400{--tw-text-opacity: 1;color:rgb(45 212 191 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:root{font-size:14px;line-height:1.5;font-weight:400;color:#e0e0e0;background-color:#0f0f0f;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{margin:0;min-height:100vh}#root{min-height:100vh}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#1a1a1a}::-webkit-scrollbar-thumb{background:#3a3a3a;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#4a4a4a}.json-key{color:#7dd3fc}.json-string{color:#86efac}.json-number{color:#fbbf24}.json-boolean{color:#c084fc}.json-null{color:#94a3b8}.status-pending{color:#fbbf24}.status-success{color:#22c55e}.status-error{color:#ef4444}.status-paused{color:#3b82f6}@keyframes pulse-border{0%,to{border-color:#3b82f680}50%{border-color:#3b82f6}}.paused-border{animation:pulse-border 2s ease-in-out infinite}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-pp-gray\/50:hover{background-color:#1a1a1a80}.hover\:bg-pp-light:hover{--tw-bg-opacity: 1;background-color:rgb(42 42 42 / var(--tw-bg-opacity, 1))}.hover\:bg-purple-700:hover{--tw-bg-opacity: 1;background-color:rgb(126 34 206 / var(--tw-bg-opacity, 1))}.hover\:bg-teal-700:hover{--tw-bg-opacity: 1;background-color:rgb(15 118 110 / var(--tw-bg-opacity, 1))}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}