playingpack 0.3.0 → 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.0",
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
+ }
@@ -0,0 +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))}