@kelceyp/caw-server 1.0.81 → 1.0.83

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/main.js CHANGED
@@ -754,4 +754,4 @@ ID Remapping (${W} IDs):
754
754
  `}return{content:[{type:"text",text:z}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},$46={name:"import_documents",title:"Import Documents",description:"Import documents and templates from .caw-exports/. Store must be empty.",schema:_x1,execute:kx1};var K46=[s16,t16,e16,$46];j6();var Nx1=A.object({name:A.string().describe("Story name"),store:A.string().optional().describe('Store code (default: "A")'),state:A.string().optional().describe("Initial state (default: first configured state)")}),fx1=async({core:$,projectId:K},{name:Y,store:q,state:J})=>{try{let z=await $.createStory(K,{name:Y,store:q||"A",state:J});return{content:[{type:"text",text:`Created story: ${z.id} (name: ${z.name}, state: ${z.fields.state})`}]}}catch(z){return{content:[{type:"text",text:`Error: ${z.message}`}],isError:!0}}},Y46={name:"create_story",title:"Create Story",description:"Create a new story",schema:Nx1,execute:fx1};j6();var Rx1=A.object({store:A.string().optional().describe('Store code (default: "A")')}),Cx1=async({core:$,projectId:K},{store:Y})=>{try{let q=await $.listStories(K,{store:Y||"A"});return{content:[{type:"text",text:JSON.stringify(q,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},q46={name:"list_stories",title:"List Stories",description:"List all stories",schema:Rx1,execute:Cx1};j6();var hx1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),Dx1=async({core:$,projectId:K},{id:Y})=>{try{let q=await $.getStory(K,{id:Y});return{content:[{type:"text",text:JSON.stringify(q,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},J46={name:"get_story",title:"Get Story",description:"Get story details",schema:hx1,execute:Dx1};j6();var Ex1=A.object({id:A.string().describe('Story ID (e.g., "As1")'),state:A.string().describe("New state value")}),Lx1=async({core:$,projectId:K},{id:Y,state:q})=>{try{let J=await $.updateStoryState(K,{id:Y,state:q});return{content:[{type:"text",text:`Updated story ${J.id} state to: ${J.fields.state}`}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},z46={name:"update_story_state",title:"Update Story State",description:"Update story state",schema:Ex1,execute:Lx1};j6();var Sx1=A.object({id:A.string().describe('Story ID (e.g., "As1")'),confirm:A.boolean().optional().describe("Set to true to confirm deletion. If false or omitted, returns impact preview.")}),Ix1=async({core:$,projectId:K},{id:Y,confirm:q})=>{try{if(!q){let z=await $.getStory(K,{id:Y});return{content:[{type:"text",text:`This will delete story: ${z.id} (${z.name})
755
755
  State: ${z.fields.state}
756
756
 
757
- To confirm, call delete_story with confirm=true`}]}}let J=await $.deleteStory(K,{id:Y});return{content:[{type:"text",text:`Deleted story: ${J.id} (${J.descendantCount} descendants also deleted)`}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},W46={name:"delete_story",title:"Delete Story",description:"Soft delete a story (can be restored later). If confirm is false or omitted, returns impact preview.",schema:Sx1,execute:Ix1};j6();var xx1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),gx1=async({core:$,projectId:K},{id:Y})=>{try{let q=await $.incrementStoryJobCount(K,{id:Y});if(!q)return{content:[{type:"text",text:`Story ${Y} not found`}],isError:!0};return{content:[{type:"text",text:`Incremented activeJobCount for story ${q.id} to: ${q.fields.activeJobCount}`}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},Z46={name:"increment_story_job_count",title:"Increment Story Job Count",description:"Increment a story's activeJobCount field (for manual tracking of agent activity)",schema:xx1,execute:gx1};j6();var yx1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),bx1=async({core:$,projectId:K},{id:Y})=>{try{let q=await $.decrementStoryJobCount(K,{id:Y});if(!q)return{content:[{type:"text",text:`Story ${Y} not found`}],isError:!0};return{content:[{type:"text",text:`Decremented activeJobCount for story ${q.id} to: ${q.fields.activeJobCount}`}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},H46={name:"decrement_story_job_count",title:"Decrement Story Job Count",description:"Decrement a story's activeJobCount field (for manual tracking of agent activity). Guards against going below zero.",schema:yx1,execute:bx1};var X46=[Y46,q46,J46,z46,W46,Z46,H46];j6();var ux1=A.object({id:A.string().describe('Asset ID (e.g., "Aa1")')}),mx1=async({core:$,projectId:K},{id:Y})=>{try{let{buffer:q,mimeType:J,sizeBytes:z,hash:W,originalFilename:Z}=await $.readAssetBinary(K,{id:Y});return{content:[{type:"image",data:q.toString("base64"),mimeType:J},{type:"text",text:JSON.stringify({id:Y,originalFilename:Z,sizeBytes:z,hash:W},null,2)}]}}catch(q){return{content:[{type:"text",text:q.code==="ASSET_NOT_FOUND"||q.code==="BLOB_NOT_FOUND"?q.message:`Error: ${q.message}`}],isError:!0}}},O46={name:"read_asset",title:"Read Asset",description:"Read an asset image by ID. Returns the image as base64-encoded data that Claude can view. Supported formats: PNG, JPEG, GIF, WebP. Maximum size: 10MB.",schema:ux1,execute:mx1};var P46=[O46];j6();var Bx1=A.object({store:A.string().optional().describe('Store code (default: "A")'),status:A.string().optional().describe("Filter by status (pending, running, completed, error, stopped)")}),Ux1=async({core:$,projectId:K},{store:Y,status:q})=>{try{let J=await $.listJobs(K,{store:Y||"A",status:q});return{content:[{type:"text",text:JSON.stringify(J,null,2)}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},w46={name:"list_jobs",title:"List Jobs",description:"List all jobs",schema:Bx1,execute:Ux1};j6();var Fx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),px1=async({core:$,projectId:K},{id:Y})=>{try{let q=await $.getJob(K,{id:Y});return{content:[{type:"text",text:JSON.stringify(q,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},G46={name:"read_job",title:"Read Job",description:"Get job details",schema:Fx1,execute:px1};j6();var Qx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),cx1=async({core:$,projectId:K},{id:Y})=>{try{return{content:[{type:"text",text:await $.readJobOutput(K,{id:Y})}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},T46={name:"read_job_output",title:"Read Job Output",description:"Read the output content from a job",schema:Qx1,execute:cx1};j6();var lx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),ix1=async({core:$,projectId:K},{id:Y})=>{try{return{content:[{type:"text",text:await $.readJobPrompt(K,{id:Y})}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},j46={name:"read_job_prompt",title:"Read Job Prompt",description:"Read the prompt content from a job",schema:lx1,execute:ix1};j6();var pr=4,CL=2000,hL=3600000;async function v46($,K){let Y=0,q=K;q=(await $.core.getJob($.projectId,{id:q})).fields?.parentJobId;while(q)Y++,q=(await $.core.getJob($.projectId,{id:q})).fields?.parentJobId;return Y}async function DL($,K,{intervalMs:Y,timeoutMs:q}){let J=Date.now(),z=["completed","error","stopped"];while(!0){let W=await $.core.getJob($.projectId,{id:K});if(z.includes(W.fields?.status))return W;if(Date.now()-J>q)throw Error(`Job ${K} did not complete within timeout (${q/60000} minutes)`);await new Promise((Z)=>setTimeout(Z,Y))}}var dx1=A.object({agent:A.enum(["claude","codex"]).describe("Agent type (claude or codex)"),prompt:A.string().describe("Prompt content"),name:A.string().optional().describe("Job name (auto-generated if omitted)"),model:A.string().optional().describe("Model for Claude agent"),workingDirectory:A.string().optional().describe("Working directory"),objectId:A.string().optional().describe("ID of entity to associate with"),streaming:A.boolean().optional().describe("Enable streaming output"),skipPermissions:A.boolean().optional().describe("Skip permission prompts (default: true). Set to false to enable permission prompts."),parentJobId:A.string().optional().describe("Parent job ID for hierarchy tracking"),blocking:A.boolean().optional().describe("Wait for job completion with 60-minute timeout (default: true). Set to false for asynchronous operation."),dockerImage:A.string().optional().describe("Docker image for containerized execution"),store:A.string().optional().describe('Store code (default: "A")')}),nx1=async({core:$,projectId:K},Y)=>{try{let{blocking:q,parentJobId:J,store:z="A",...W}=Y;if(J){let P=await v46({core:$,projectId:K},J);if(P>=pr)throw Error(`Job depth limit exceeded (max ${pr}, parent depth: ${P})`)}let Z=await $.createJob(K,{...W,store:z,parentJobId:J,blocking:q??!0});if(q===!1)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await DL({core:$,projectId:K},Z.id,{intervalMs:CL,timeoutMs:hL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},V46={name:"create_job",title:"Create Job",description:"Create and execute a job. Set blocking=true to wait for completion and return output directly (60-minute timeout). When parentJobId is provided, depth validation ensures jobs do not exceed depth limit of 4.",schema:dx1,execute:nx1};j6();var ax1=A.object({id:A.string().describe('Job ID to continue (e.g., "Aj1")'),prompt:A.string().describe("Follow-up prompt"),model:A.string().optional().describe("Model (allows changing between turns)"),blocking:A.boolean().optional().describe("Wait for job completion with 60-minute timeout (default: true). Set to false for asynchronous operation.")}),ox1=async({core:$,projectId:K},Y)=>{try{let{id:q,prompt:J,model:z,blocking:W}=Y,Z=await $.continueJob(K,{id:q,prompt:J,model:z});if(W===!1)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await DL({core:$,projectId:K},Z.id,{intervalMs:CL,timeoutMs:hL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},A46={name:"continue_job",title:"Continue Job",description:"Continue a completed or errored job with a follow-up prompt. Set blocking=true to wait for completion and return output directly (60-minute timeout).",schema:ax1,execute:ox1};var M46=[w46,G46,T46,j46,V46,A46];j6();C1();var rx1=A.object({query:A.string().describe("FTS5 search query"),field_name:A.string().optional().describe("Filter by field definition name"),store:A.string().optional().describe('Store code (default: "A")')}),sx1=async({core:$,projectId:K},{query:Y,field_name:q,store:J})=>{try{let z=await $.searchTextFields(K,{query:Y,fieldName:q,store:J||"A"});return{content:[{type:"text",text:JSON.stringify(z,null,2)}]}}catch(z){if(z.message?.includes("fts5: syntax error"))return{content:[{type:"text",text:`Error: ${I.validation('Invalid FTS5 query syntax. Valid syntax includes: simple terms (hello world), phrases ("hello world"), boolean (hello OR world), prefix (hel*), negation (hello NOT world)',"INVALID_FTS5_QUERY").message}`}],isError:!0};return{content:[{type:"text",text:`Error: ${z.message}`}],isError:!0}}},_46={name:"search_text_fields",title:"Search Text Fields",description:"Search text field content using FTS5 full-text search. Returns matching entity IDs, field names, and BM25 relevance ranking. Supports store:X filter to override store parameter.",schema:rx1,execute:sx1};j6();C1();var tx1=A.object({query:A.string().describe("FTS5 search query"),store:A.string().optional().describe('Store code (default: "A")'),limit:A.number().int().positive().optional().describe("Maximum results (default: 20)"),offset:A.number().int().min(0).optional().describe("Pagination offset (default: 0)"),start_marker:A.string().optional().describe('Snippet highlight start marker (default: "<mark>")'),end_marker:A.string().optional().describe('Snippet highlight end marker (default: "</mark>")')}),ex1=async({core:$,projectId:K},{query:Y,store:q,limit:J,offset:z,start_marker:W,end_marker:Z})=>{try{let H=await $.searchContent(K,{query:Y,store:q||"A",limit:J??20,offset:z??0,startMarker:W??"<mark>",endMarker:Z??"</mark>"});return{content:[{type:"text",text:JSON.stringify(H,null,2)}]}}catch(H){if(H.message?.includes("fts5: syntax error"))return{content:[{type:"text",text:`Error: ${I.validation('Invalid FTS5 query syntax. Valid syntax includes: simple terms (hello world), phrases ("hello world"), boolean (hello OR world), prefix (hel*), negation (hello NOT world)',"INVALID_FTS5_QUERY").message}`}],isError:!0};return{content:[{type:"text",text:`Error: ${H.message}`}],isError:!0}}},k46={name:"search",title:"Search Document Content",description:'Search document content using FTS5 full-text search. Returns matching documents with entity IDs, paths, names, BM25 relevance scores, and content snippets. Response includes debug.strategy field indicating execution path: "fts-first" (FTS query with optional filters) or "filters-only" (no FTS, filters applied directly). Query syntax: simple terms (hello), phrases ("hello world"), boolean (hello OR world), prefix (auth*), negation (hello NOT world). Filters: type:document, type:template, type:d/task, store:X (overrides store parameter), fieldName:value (string field equality). For multi-value fields, matches if ANY value equals the filter (contains semantics).',schema:tx1,execute:ex1};var N46=[_46,k46];var $g1=({core:$,projectId:K})=>{let Y=new Fr({name:"caw-server",version:"0.0.1"},{capabilities:{tools:{}}}),q=[...D16,...I16,...U16,...r16,...K46,...X46,...P46,...M46,...N46];for(let J of q)Y.registerTool(J.name,{title:J.title,description:J.description,inputSchema:J.schema},async(z,W)=>{try{let Z=W?.sessionId||W?.requestInfo?.headers?.["mcp-session-id"];return await J.execute({core:$,projectId:K,sessionId:Z},z)}catch(Z){return console.error(`Tool ${J.name} failed:`,Z),{content:[{type:"text",text:`Tool execution failed: ${Z.message}`}],isError:!0}}});return Y},f46=Object.freeze({createMcpServer:$g1});var Yg1=Qr.default("caw:mcp"),E5=Qr.default("caw:mcp:session"),qg1=({core:$})=>{let K=new Map;return Object.freeze({handleRequest:async(W,Z,{projectId:H})=>{let X=W.headers["mcp-session-id"];if(X){let T=K.get(X);if(!T)return E5("Session not found: %s",X),Z.status(404).json({jsonrpc:"2.0",error:{code:-32001,message:"Session not found. Server may have been restarted. If so, ask user to reconnect"},id:null});if(T.projectId!==H)return E5("Session %s belongs to project %s, not %s",X,T.projectId,H),Z.status(404).json({jsonrpc:"2.0",error:{code:-32001,message:"Session not found. Server may have been restarted. If so, ask user to reconnect"},id:null});E5("Routing request to existing session: %s",X),T.lastActivity=Date.now();try{return await T.transport.handleRequest(W,Z,W.body)}catch(j){console.error("MCP transport request failed:",j)}}E5("Creating new MCP session for project: %s",H);let O=null,P=new Co({sessionIdGenerator:()=>{return O=Kg1(),E5("Generated session ID: %s",O),O},onsessioninitialized:(T)=>{E5("Session initialized: %s",T),Z.setHeader("Mcp-Session-Id",T),G.sessionId=T,K.set(T,G),E5("Session stored: %s",T)}});E5("Creating new server for session");let w=f46.createMcpServer({core:$,projectId:H});try{await w.connect(P)}catch(T){return console.error("MCP server connect failed:",T),P.close?.(),w.close?.(),Z.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}let G={sessionId:null,transport:P,server:w,projectId:H,createdAt:Date.now(),lastActivity:Date.now()};Z.on("finish",()=>{if(Z.statusCode!==200&&O&&K.has(O))E5("Request failed (status %d), cleaning up session: %s",Z.statusCode,O),K.delete(O),P.close?.(),w.close?.()});try{await P.handleRequest(W,Z,W.body)}catch(T){console.error("MCP transport request failed:",T)}},getSession:(W)=>{return K.get(W)},closeSession:(W)=>{let Z=K.get(W);if(!Z){E5("Cannot close session - not found: %s",W);return}E5("Closing session: %s",W),Z.transport.close?.(),Z.server.close?.(),K.delete(W)},cleanupIdle:(W)=>{let Z=Date.now(),H=0;for(let[X,O]of K.entries())if(Z-O.lastActivity>W)E5("Cleaning up idle session: %s (idle for %dms)",X,Z-O.lastActivity),O.transport.close?.(),O.server.close?.(),K.delete(X),H++;if(H>0)Yg1("Cleaned up %d idle sessions",H)}})},R46=Object.freeze({create:qg1});var Jg1=86400000,zg1=300000,Wg1=({mcpSessionManager:$})=>{let{jobFns:K,controlFns:Y}=Gh.create({name:"cleanup-idle-mcp-sessions",interval:zg1,execute:async()=>{$.cleanupIdle(Jg1)}});return{job:Object.freeze(K),control:Y}},C46=Object.freeze({create:Wg1});var cL=X$6.default("caw:server"),Qg1=async({port:$,envDir:K,databaseFilepath:Y,assetsDir:q,lazilyCreateDb:J,enableRest:z,enableSse:W,enableStatic:Z,enableCrons:H,enableMcp:X=!0,enableCli:O=!0,staticPath:P,kyselyFactory:w})=>{let G=await Kd8.create({envDir:K,databaseFilepath:Y,assetsDir:q,lazilyCreateDb:J,kyselyFactory:w}),T=Js.default();if(T.use(Js.default.json()),T.get("/health",(k,R)=>{R.json({status:"ok"})}),T.get("/api/info",(k,R)=>{R.json({version:"1.0.81"})}),z)T.use("/api",ea8),zd8.create(T,{core:G}),Hd8.create(T,{core:G}),Xd8.create(T,{core:G}),Pd8.create(T,{core:G}),Gd8.create(T,{core:G}),jd8.create(T,{core:G}),Vd8.create(T,{core:G}),Qa8.create(T,{core:G}),la8.create(T,{core:G}),da8.create(T,{core:G}),aa8.create(T,{core:G});if(W)ra8.create(T,{core:G}),ta8.create(T,{core:G});if(Z&&P)qd8.create(T,{staticPath:P});if(X){cL("Initializing MCP subsystem...");let k=R46.create({core:G});G.registerCronJob(C46,{mcpSessionManager:k}),Ko8.create(T,{core:G,mcpSessionManager:k}),cL("MCP subsystem initialized")}if(O){cL("Initializing CLI Adapter subsystem...");let k=(await Promise.resolve().then(() => (o46(),a46))).default,R=(await Promise.resolve().then(() => (e46(),t46))).default,h=(await Promise.resolve().then(() => (z$6(),J$6))).default,L=(await Promise.resolve().then(() => (H$6(),Z$6))).default,C=h.create({mcpClientFactory:k.create(),aliasGenerator:R.create(),core:G});await C.init(),G.subscribeServerEvents({onProjectCreated:(y)=>C.addProject({objectId:y.id,name:y.name,path:y.path})}),L.create(T,{cliSessionManager:C}),cL("CLI Adapter subsystem initialized")}if(H)G.startAllCrons();let j=null,v=new Set;return Object.freeze({app:T,core:G,start:async()=>{return new Promise((k)=>{j=T.listen($,()=>{let R=j.address().port;k(R)}),j.on("connection",(R)=>{v.add(R),R.on("close",()=>{v.delete(R)})})})},stop:async()=>{if(G.stopAllCrons(),j){for(let k of v)k.destroy();return v.clear(),new Promise((k)=>{j.close(()=>{k()})})}}})},O$6=Object.freeze({create:Qg1});var lg1=3131,ig1=lL.join(lL.dirname(cg1(import.meta.url)),"public_html"),dg1=!0,ng1=!0,ag1=!0,og1=!0,rg1=!1,sg1=!0,tg1=!0,eg1=!0,Jz=($,K)=>$!==void 0?$==="true":K,$y1=()=>{let $=process.env.CAW_ENV_DIR;if(!$)console.error("ERROR: CAW_ENV_DIR environment variable is required"),console.error("Set it to the path of your environment folder, e.g.:"),console.error(' export CAW_ENV_DIR="$HOME/.caw/dev"'),process.exit(1);return{envDir:$,databaseFilepath:lL.join($,"caw.db"),assetsDir:lL.join($,"assets")}},Ky1=async($)=>{let K=await O$6.create($),Y=await K.start();console.log(`Server listening on port ${Y}`),process.on("SIGINT",()=>K.stop("SIGINT")),process.on("SIGTERM",()=>K.stop("SIGTERM"))},Yy1=()=>{let{envDir:$,databaseFilepath:K,assetsDir:Y}=$y1(),q=process.env.CAW_PORT?Number(process.env.CAW_PORT):lg1,J=process.env.STATIC_PATH||ig1,z=Jz(process.env.LAZY_CREATE_DB,dg1),W=Jz(process.env.ENABLE_REST,ng1),Z=Jz(process.env.ENABLE_MCP,ag1),H=Jz(process.env.ENABLE_CLI,og1),X=Jz(process.env.ENABLE_WEBLLM_WSS,rg1),O=Jz(process.env.ENABLE_SSE,sg1),P=Jz(process.env.ENABLE_STATIC,tg1),w=Jz(process.env.ENABLE_CRONS,eg1);return Object.freeze({port:q,staticPath:J,envDir:$,databaseFilepath:K,assetsDir:Y,lazilyCreateDb:z,enableRest:W,enableMcp:Z,enableCli:H,enableWebllmWss:X,enableSse:O,enableStatic:P,enableCrons:w})};await Ky1(Yy1()).catch(($)=>{console.error("ERROR: Failed to start server:",$),process.exit(1)});
757
+ To confirm, call delete_story with confirm=true`}]}}let J=await $.deleteStory(K,{id:Y});return{content:[{type:"text",text:`Deleted story: ${J.id} (${J.descendantCount} descendants also deleted)`}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},W46={name:"delete_story",title:"Delete Story",description:"Soft delete a story (can be restored later). If confirm is false or omitted, returns impact preview.",schema:Sx1,execute:Ix1};j6();var xx1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),gx1=async({core:$,projectId:K},{id:Y})=>{try{let q=await $.incrementStoryJobCount(K,{id:Y});if(!q)return{content:[{type:"text",text:`Story ${Y} not found`}],isError:!0};return{content:[{type:"text",text:`Incremented activeJobCount for story ${q.id} to: ${q.fields.activeJobCount}`}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},Z46={name:"increment_story_job_count",title:"Increment Story Job Count",description:"Increment a story's activeJobCount field (for manual tracking of agent activity)",schema:xx1,execute:gx1};j6();var yx1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),bx1=async({core:$,projectId:K},{id:Y})=>{try{let q=await $.decrementStoryJobCount(K,{id:Y});if(!q)return{content:[{type:"text",text:`Story ${Y} not found`}],isError:!0};return{content:[{type:"text",text:`Decremented activeJobCount for story ${q.id} to: ${q.fields.activeJobCount}`}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},H46={name:"decrement_story_job_count",title:"Decrement Story Job Count",description:"Decrement a story's activeJobCount field (for manual tracking of agent activity). Guards against going below zero.",schema:yx1,execute:bx1};var X46=[Y46,q46,J46,z46,W46,Z46,H46];j6();var ux1=A.object({id:A.string().describe('Asset ID (e.g., "Aa1")')}),mx1=async({core:$,projectId:K},{id:Y})=>{try{let{buffer:q,mimeType:J,sizeBytes:z,hash:W,originalFilename:Z}=await $.readAssetBinary(K,{id:Y});return{content:[{type:"image",data:q.toString("base64"),mimeType:J},{type:"text",text:JSON.stringify({id:Y,originalFilename:Z,sizeBytes:z,hash:W},null,2)}]}}catch(q){return{content:[{type:"text",text:q.code==="ASSET_NOT_FOUND"||q.code==="BLOB_NOT_FOUND"?q.message:`Error: ${q.message}`}],isError:!0}}},O46={name:"read_asset",title:"Read Asset",description:"Read an asset image by ID. Returns the image as base64-encoded data that Claude can view. Supported formats: PNG, JPEG, GIF, WebP. Maximum size: 10MB.",schema:ux1,execute:mx1};var P46=[O46];j6();var Bx1=A.object({store:A.string().optional().describe('Store code (default: "A")'),status:A.string().optional().describe("Filter by status (pending, running, completed, error, stopped)")}),Ux1=async({core:$,projectId:K},{store:Y,status:q})=>{try{let J=await $.listJobs(K,{store:Y||"A",status:q});return{content:[{type:"text",text:JSON.stringify(J,null,2)}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},w46={name:"list_jobs",title:"List Jobs",description:"List all jobs",schema:Bx1,execute:Ux1};j6();var Fx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),px1=async({core:$,projectId:K},{id:Y})=>{try{let q=await $.getJob(K,{id:Y});return{content:[{type:"text",text:JSON.stringify(q,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},G46={name:"read_job",title:"Read Job",description:"Get job details",schema:Fx1,execute:px1};j6();var Qx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),cx1=async({core:$,projectId:K},{id:Y})=>{try{return{content:[{type:"text",text:await $.readJobOutput(K,{id:Y})}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},T46={name:"read_job_output",title:"Read Job Output",description:"Read the output content from a job",schema:Qx1,execute:cx1};j6();var lx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),ix1=async({core:$,projectId:K},{id:Y})=>{try{return{content:[{type:"text",text:await $.readJobPrompt(K,{id:Y})}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},j46={name:"read_job_prompt",title:"Read Job Prompt",description:"Read the prompt content from a job",schema:lx1,execute:ix1};j6();var pr=4,CL=2000,hL=3600000;async function v46($,K){let Y=0,q=K;q=(await $.core.getJob($.projectId,{id:q})).fields?.parentJobId;while(q)Y++,q=(await $.core.getJob($.projectId,{id:q})).fields?.parentJobId;return Y}async function DL($,K,{intervalMs:Y,timeoutMs:q}){let J=Date.now(),z=["completed","error","stopped"];while(!0){let W=await $.core.getJob($.projectId,{id:K});if(z.includes(W.fields?.status))return W;if(Date.now()-J>q)throw Error(`Job ${K} did not complete within timeout (${q/60000} minutes)`);await new Promise((Z)=>setTimeout(Z,Y))}}var dx1=A.object({agent:A.enum(["claude","codex"]).describe("Agent type (claude or codex)"),prompt:A.string().describe("Prompt content"),name:A.string().optional().describe("Job name (auto-generated if omitted)"),model:A.string().optional().describe("Model for Claude agent"),workingDirectory:A.string().optional().describe("Working directory"),objectId:A.string().optional().describe("ID of entity to associate with"),streaming:A.boolean().optional().describe("Enable streaming output"),skipPermissions:A.boolean().optional().describe("Skip permission prompts (default: true). Set to false to enable permission prompts."),parentJobId:A.string().optional().describe("Parent job ID for hierarchy tracking"),blocking:A.boolean().optional().describe("Wait for job completion with 60-minute timeout (default: true). Set to false for asynchronous operation."),dockerImage:A.string().optional().describe("Docker image for containerized execution"),store:A.string().optional().describe('Store code (default: "A")')}),nx1=async({core:$,projectId:K},Y)=>{try{let{blocking:q,parentJobId:J,store:z="A",...W}=Y;if(J){let P=await v46({core:$,projectId:K},J);if(P>=pr)throw Error(`Job depth limit exceeded (max ${pr}, parent depth: ${P})`)}let Z=await $.createJob(K,{...W,store:z,parentJobId:J,blocking:q??!0});if(q===!1)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await DL({core:$,projectId:K},Z.id,{intervalMs:CL,timeoutMs:hL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},V46={name:"create_job",title:"Create Job",description:"Create and execute a job. Set blocking=true to wait for completion and return output directly (60-minute timeout). When parentJobId is provided, depth validation ensures jobs do not exceed depth limit of 4.",schema:dx1,execute:nx1};j6();var ax1=A.object({id:A.string().describe('Job ID to continue (e.g., "Aj1")'),prompt:A.string().describe("Follow-up prompt"),model:A.string().optional().describe("Model (allows changing between turns)"),blocking:A.boolean().optional().describe("Wait for job completion with 60-minute timeout (default: true). Set to false for asynchronous operation.")}),ox1=async({core:$,projectId:K},Y)=>{try{let{id:q,prompt:J,model:z,blocking:W}=Y,Z=await $.continueJob(K,{id:q,prompt:J,model:z});if(W===!1)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await DL({core:$,projectId:K},Z.id,{intervalMs:CL,timeoutMs:hL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(q){return{content:[{type:"text",text:`Error: ${q.message}`}],isError:!0}}},A46={name:"continue_job",title:"Continue Job",description:"Continue a completed or errored job with a follow-up prompt. Set blocking=true to wait for completion and return output directly (60-minute timeout).",schema:ax1,execute:ox1};var M46=[w46,G46,T46,j46,V46,A46];j6();C1();var rx1=A.object({query:A.string().describe("FTS5 search query"),field_name:A.string().optional().describe("Filter by field definition name"),store:A.string().optional().describe('Store code (default: "A")')}),sx1=async({core:$,projectId:K},{query:Y,field_name:q,store:J})=>{try{let z=await $.searchTextFields(K,{query:Y,fieldName:q,store:J||"A"});return{content:[{type:"text",text:JSON.stringify(z,null,2)}]}}catch(z){if(z.message?.includes("fts5: syntax error"))return{content:[{type:"text",text:`Error: ${I.validation('Invalid FTS5 query syntax. Valid syntax includes: simple terms (hello world), phrases ("hello world"), boolean (hello OR world), prefix (hel*), negation (hello NOT world)',"INVALID_FTS5_QUERY").message}`}],isError:!0};return{content:[{type:"text",text:`Error: ${z.message}`}],isError:!0}}},_46={name:"search_text_fields",title:"Search Text Fields",description:"Search text field content using FTS5 full-text search. Returns matching entity IDs, field names, and BM25 relevance ranking. Supports store:X filter to override store parameter.",schema:rx1,execute:sx1};j6();C1();var tx1=A.object({query:A.string().describe("FTS5 search query"),store:A.string().optional().describe('Store code (default: "A")'),limit:A.number().int().positive().optional().describe("Maximum results (default: 20)"),offset:A.number().int().min(0).optional().describe("Pagination offset (default: 0)"),start_marker:A.string().optional().describe('Snippet highlight start marker (default: "<mark>")'),end_marker:A.string().optional().describe('Snippet highlight end marker (default: "</mark>")')}),ex1=async({core:$,projectId:K},{query:Y,store:q,limit:J,offset:z,start_marker:W,end_marker:Z})=>{try{let H=await $.searchContent(K,{query:Y,store:q||"A",limit:J??20,offset:z??0,startMarker:W??"<mark>",endMarker:Z??"</mark>"});return{content:[{type:"text",text:JSON.stringify(H,null,2)}]}}catch(H){if(H.message?.includes("fts5: syntax error"))return{content:[{type:"text",text:`Error: ${I.validation('Invalid FTS5 query syntax. Valid syntax includes: simple terms (hello world), phrases ("hello world"), boolean (hello OR world), prefix (hel*), negation (hello NOT world)',"INVALID_FTS5_QUERY").message}`}],isError:!0};return{content:[{type:"text",text:`Error: ${H.message}`}],isError:!0}}},k46={name:"search",title:"Search Document Content",description:'Search document content using FTS5 full-text search. Returns matching documents with entity IDs, paths, names, BM25 relevance scores, and content snippets. Response includes debug.strategy field indicating execution path: "fts-first" (FTS query with optional filters) or "filters-only" (no FTS, filters applied directly). Query syntax: simple terms (hello), phrases ("hello world"), boolean (hello OR world), prefix (auth*), negation (hello NOT world). Filters: type:document, type:template, type:d/task, store:X (overrides store parameter), fieldName:value (string field equality). For multi-value fields, matches if ANY value equals the filter (contains semantics).',schema:tx1,execute:ex1};var N46=[_46,k46];var $g1=({core:$,projectId:K})=>{let Y=new Fr({name:"caw-server",version:"0.0.1"},{capabilities:{tools:{}}}),q=[...D16,...I16,...U16,...r16,...K46,...X46,...P46,...M46,...N46];for(let J of q)Y.registerTool(J.name,{title:J.title,description:J.description,inputSchema:J.schema},async(z,W)=>{try{let Z=W?.sessionId||W?.requestInfo?.headers?.["mcp-session-id"];return await J.execute({core:$,projectId:K,sessionId:Z},z)}catch(Z){return console.error(`Tool ${J.name} failed:`,Z),{content:[{type:"text",text:`Tool execution failed: ${Z.message}`}],isError:!0}}});return Y},f46=Object.freeze({createMcpServer:$g1});var Yg1=Qr.default("caw:mcp"),E5=Qr.default("caw:mcp:session"),qg1=({core:$})=>{let K=new Map;return Object.freeze({handleRequest:async(W,Z,{projectId:H})=>{let X=W.headers["mcp-session-id"];if(X){let T=K.get(X);if(!T)return E5("Session not found: %s",X),Z.status(404).json({jsonrpc:"2.0",error:{code:-32001,message:"Session not found. Server may have been restarted. If so, ask user to reconnect"},id:null});if(T.projectId!==H)return E5("Session %s belongs to project %s, not %s",X,T.projectId,H),Z.status(404).json({jsonrpc:"2.0",error:{code:-32001,message:"Session not found. Server may have been restarted. If so, ask user to reconnect"},id:null});E5("Routing request to existing session: %s",X),T.lastActivity=Date.now();try{return await T.transport.handleRequest(W,Z,W.body)}catch(j){console.error("MCP transport request failed:",j)}}E5("Creating new MCP session for project: %s",H);let O=null,P=new Co({sessionIdGenerator:()=>{return O=Kg1(),E5("Generated session ID: %s",O),O},onsessioninitialized:(T)=>{E5("Session initialized: %s",T),Z.setHeader("Mcp-Session-Id",T),G.sessionId=T,K.set(T,G),E5("Session stored: %s",T)}});E5("Creating new server for session");let w=f46.createMcpServer({core:$,projectId:H});try{await w.connect(P)}catch(T){return console.error("MCP server connect failed:",T),P.close?.(),w.close?.(),Z.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}let G={sessionId:null,transport:P,server:w,projectId:H,createdAt:Date.now(),lastActivity:Date.now()};Z.on("finish",()=>{if(Z.statusCode!==200&&O&&K.has(O))E5("Request failed (status %d), cleaning up session: %s",Z.statusCode,O),K.delete(O),P.close?.(),w.close?.()});try{await P.handleRequest(W,Z,W.body)}catch(T){console.error("MCP transport request failed:",T)}},getSession:(W)=>{return K.get(W)},closeSession:(W)=>{let Z=K.get(W);if(!Z){E5("Cannot close session - not found: %s",W);return}E5("Closing session: %s",W),Z.transport.close?.(),Z.server.close?.(),K.delete(W)},cleanupIdle:(W)=>{let Z=Date.now(),H=0;for(let[X,O]of K.entries())if(Z-O.lastActivity>W)E5("Cleaning up idle session: %s (idle for %dms)",X,Z-O.lastActivity),O.transport.close?.(),O.server.close?.(),K.delete(X),H++;if(H>0)Yg1("Cleaned up %d idle sessions",H)}})},R46=Object.freeze({create:qg1});var Jg1=86400000,zg1=300000,Wg1=({mcpSessionManager:$})=>{let{jobFns:K,controlFns:Y}=Gh.create({name:"cleanup-idle-mcp-sessions",interval:zg1,execute:async()=>{$.cleanupIdle(Jg1)}});return{job:Object.freeze(K),control:Y}},C46=Object.freeze({create:Wg1});var cL=X$6.default("caw:server"),Qg1=async({port:$,envDir:K,databaseFilepath:Y,assetsDir:q,lazilyCreateDb:J,enableRest:z,enableSse:W,enableStatic:Z,enableCrons:H,enableMcp:X=!0,enableCli:O=!0,staticPath:P,kyselyFactory:w})=>{let G=await Kd8.create({envDir:K,databaseFilepath:Y,assetsDir:q,lazilyCreateDb:J,kyselyFactory:w}),T=Js.default();if(T.use(Js.default.json()),T.get("/health",(k,R)=>{R.json({status:"ok"})}),T.get("/api/info",(k,R)=>{R.json({version:"1.0.83"})}),z)T.use("/api",ea8),zd8.create(T,{core:G}),Hd8.create(T,{core:G}),Xd8.create(T,{core:G}),Pd8.create(T,{core:G}),Gd8.create(T,{core:G}),jd8.create(T,{core:G}),Vd8.create(T,{core:G}),Qa8.create(T,{core:G}),la8.create(T,{core:G}),da8.create(T,{core:G}),aa8.create(T,{core:G});if(W)ra8.create(T,{core:G}),ta8.create(T,{core:G});if(Z&&P)qd8.create(T,{staticPath:P});if(X){cL("Initializing MCP subsystem...");let k=R46.create({core:G});G.registerCronJob(C46,{mcpSessionManager:k}),Ko8.create(T,{core:G,mcpSessionManager:k}),cL("MCP subsystem initialized")}if(O){cL("Initializing CLI Adapter subsystem...");let k=(await Promise.resolve().then(() => (o46(),a46))).default,R=(await Promise.resolve().then(() => (e46(),t46))).default,h=(await Promise.resolve().then(() => (z$6(),J$6))).default,L=(await Promise.resolve().then(() => (H$6(),Z$6))).default,C=h.create({mcpClientFactory:k.create(),aliasGenerator:R.create(),core:G});await C.init(),G.subscribeServerEvents({onProjectCreated:(y)=>C.addProject({objectId:y.id,name:y.name,path:y.path})}),L.create(T,{cliSessionManager:C}),cL("CLI Adapter subsystem initialized")}if(H)G.startAllCrons();let j=null,v=new Set;return Object.freeze({app:T,core:G,start:async()=>{return new Promise((k)=>{j=T.listen($,()=>{let R=j.address().port;k(R)}),j.on("connection",(R)=>{v.add(R),R.on("close",()=>{v.delete(R)})})})},stop:async()=>{if(G.stopAllCrons(),j){for(let k of v)k.destroy();return v.clear(),new Promise((k)=>{j.close(()=>{k()})})}}})},O$6=Object.freeze({create:Qg1});var lg1=3131,ig1=lL.join(lL.dirname(cg1(import.meta.url)),"public_html"),dg1=!0,ng1=!0,ag1=!0,og1=!0,rg1=!1,sg1=!0,tg1=!0,eg1=!0,Jz=($,K)=>$!==void 0?$==="true":K,$y1=()=>{let $=process.env.CAW_ENV_DIR;if(!$)console.error("ERROR: CAW_ENV_DIR environment variable is required"),console.error("Set it to the path of your environment folder, e.g.:"),console.error(' export CAW_ENV_DIR="$HOME/.caw/dev"'),process.exit(1);return{envDir:$,databaseFilepath:lL.join($,"caw.db"),assetsDir:lL.join($,"assets")}},Ky1=async($)=>{let K=await O$6.create($),Y=await K.start();console.log(`Server listening on port ${Y}`),process.on("SIGINT",()=>K.stop("SIGINT")),process.on("SIGTERM",()=>K.stop("SIGTERM"))},Yy1=()=>{let{envDir:$,databaseFilepath:K,assetsDir:Y}=$y1(),q=process.env.CAW_PORT?Number(process.env.CAW_PORT):lg1,J=process.env.STATIC_PATH||ig1,z=Jz(process.env.LAZY_CREATE_DB,dg1),W=Jz(process.env.ENABLE_REST,ng1),Z=Jz(process.env.ENABLE_MCP,ag1),H=Jz(process.env.ENABLE_CLI,og1),X=Jz(process.env.ENABLE_WEBLLM_WSS,rg1),O=Jz(process.env.ENABLE_SSE,sg1),P=Jz(process.env.ENABLE_STATIC,tg1),w=Jz(process.env.ENABLE_CRONS,eg1);return Object.freeze({port:q,staticPath:J,envDir:$,databaseFilepath:K,assetsDir:Y,lazilyCreateDb:z,enableRest:W,enableMcp:Z,enableCli:H,enableWebllmWss:X,enableSse:O,enableStatic:P,enableCrons:w})};await Ky1(Yy1()).catch(($)=>{console.error("ERROR: Failed to start server:",$),process.exit(1)});