@kelceyp/caw-server 1.0.71 → 1.0.72
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 +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -751,4 +751,4 @@ ID Remapping (${W} IDs):
|
|
|
751
751
|
`}return{content:[{type:"text",text:z}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},i16={name:"import_documents",title:"Import Documents",description:"Import documents and templates from .caw-exports/. Store must be empty.",schema:Wx1,execute:Zx1};var d16=[Q16,c16,l16,i16];v6();var Hx1=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)")}),Xx1=async({core:$,projectId:K},{name:q,store:Y,state:J})=>{try{let z=await $.createStory(K,{name:q,store:Y||"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}}},n16={name:"create_story",title:"Create Story",description:"Create a new story",schema:Hx1,execute:Xx1};v6();var Ox1=A.object({store:A.string().optional().describe('Store code (default: "A")')}),Px1=async({core:$,projectId:K},{store:q})=>{try{let Y=await $.listStories(K,{store:q||"A"});return{content:[{type:"text",text:JSON.stringify(Y,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},a16={name:"list_stories",title:"List Stories",description:"List all stories",schema:Ox1,execute:Px1};v6();var wx1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),Gx1=async({core:$,projectId:K},{id:q})=>{try{let Y=await $.getStory(K,{id:q});return{content:[{type:"text",text:JSON.stringify(Y,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},o16={name:"get_story",title:"Get Story",description:"Get story details",schema:wx1,execute:Gx1};v6();var Tx1=A.object({id:A.string().describe('Story ID (e.g., "As1")'),state:A.string().describe("New state value")}),jx1=async({core:$,projectId:K},{id:q,state:Y})=>{try{let J=await $.updateStoryState(K,{id:q,state:Y});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}}},r16={name:"update_story_state",title:"Update Story State",description:"Update story state",schema:Tx1,execute:jx1};v6();var vx1=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.")}),Vx1=async({core:$,projectId:K},{id:q,confirm:Y})=>{try{if(!Y){let z=await $.getStory(K,{id:q});return{content:[{type:"text",text:`This will delete story: ${z.id} (${z.name})
|
|
752
752
|
State: ${z.fields.state}
|
|
753
753
|
|
|
754
|
-
To confirm, call delete_story with confirm=true`}]}}let J=await $.deleteStory(K,{id:q});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}}},s16={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:vx1,execute:Vx1};v6();var Ax1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),Mx1=async({core:$,projectId:K},{id:q})=>{try{let Y=await $.incrementStoryJobCount(K,{id:q});if(!Y)return{content:[{type:"text",text:`Story ${q} not found`}],isError:!0};return{content:[{type:"text",text:`Incremented activeJobCount for story ${Y.id} to: ${Y.fields.activeJobCount}`}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},t16={name:"increment_story_job_count",title:"Increment Story Job Count",description:"Increment a story's activeJobCount field (for manual tracking of agent activity)",schema:Ax1,execute:Mx1};v6();var _x1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),kx1=async({core:$,projectId:K},{id:q})=>{try{let Y=await $.decrementStoryJobCount(K,{id:q});if(!Y)return{content:[{type:"text",text:`Story ${q} not found`}],isError:!0};return{content:[{type:"text",text:`Decremented activeJobCount for story ${Y.id} to: ${Y.fields.activeJobCount}`}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},e16={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:_x1,execute:kx1};var $46=[n16,a16,o16,r16,s16,t16,e16];v6();var Nx1=A.object({id:A.string().describe('Asset ID (e.g., "Aa1")')}),fx1=async({core:$,projectId:K},{id:q})=>{try{let{buffer:Y,mimeType:J,sizeBytes:z,hash:W,originalFilename:Z}=await $.readAssetBinary(K,{id:q});return{content:[{type:"image",data:Y.toString("base64"),mimeType:J},{type:"text",text:JSON.stringify({id:q,originalFilename:Z,sizeBytes:z,hash:W},null,2)}]}}catch(Y){return{content:[{type:"text",text:Y.code==="ASSET_NOT_FOUND"||Y.code==="BLOB_NOT_FOUND"?Y.message:`Error: ${Y.message}`}],isError:!0}}},K46={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:Nx1,execute:fx1};var q46=[K46];v6();var Rx1=A.object({store:A.string().optional().describe('Store code (default: "A")'),status:A.string().optional().describe("Filter by status (pending, running, completed, error, stopped)")}),Cx1=async({core:$,projectId:K},{store:q,status:Y})=>{try{let J=await $.listJobs(K,{store:q||"A",status:Y});return{content:[{type:"text",text:JSON.stringify(J,null,2)}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},Y46={name:"list_jobs",title:"List Jobs",description:"List all jobs",schema:Rx1,execute:Cx1};v6();var hx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),Dx1=async({core:$,projectId:K},{id:q})=>{try{let Y=await $.getJob(K,{id:q});return{content:[{type:"text",text:JSON.stringify(Y,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},J46={name:"read_job",title:"Read Job",description:"Get job details",schema:hx1,execute:Dx1};v6();var Ex1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),Lx1=async({core:$,projectId:K},{id:q})=>{try{return{content:[{type:"text",text:await $.readJobOutput(K,{id:q})}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},z46={name:"read_job_output",title:"Read Job Output",description:"Read the output content from a job",schema:Ex1,execute:Lx1};v6();var Sx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),Ix1=async({core:$,projectId:K},{id:q})=>{try{return{content:[{type:"text",text:await $.readJobPrompt(K,{id:q})}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},W46={name:"read_job_prompt",title:"Read Job Prompt",description:"Read the prompt content from a job",schema:Sx1,execute:Ix1};v6();var gr=4,_L=2000,kL=1800000;async function Z46($,K){let q=0,Y=K;Y=(await $.core.getJob($.projectId,{id:Y})).fields?.parentJobId;while(Y)q++,Y=(await $.core.getJob($.projectId,{id:Y})).fields?.parentJobId;return q}async function NL($,K,{intervalMs:q,timeoutMs:Y}){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>Y)throw Error(`Job ${K} did not complete within timeout (${Y/60000} minutes)`);await new Promise((Z)=>setTimeout(Z,q))}}var xx1=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"),parentJobId:A.string().optional().describe("Parent job ID for hierarchy tracking"),blocking:A.boolean().optional().describe("Wait for job completion (default: false). When true, polls until complete and returns output. 30-minute timeout applies."),store:A.string().optional().describe('Store code (default: "A")')}),gx1=async({core:$,projectId:K},q)=>{try{let{blocking:Y,parentJobId:J,store:z="A",...W}=q;if(J){let P=await Z46({core:$,projectId:K},J);if(P>=gr)throw Error(`Job depth limit exceeded (max ${gr}, parent depth: ${P})`)}let Z=await $.createJob(K,{...W,store:z,parentJobId:J,blocking:Y||!1});if(!Y)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await NL({core:$,projectId:K},Z.id,{intervalMs:_L,timeoutMs:kL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},H46={name:"create_job",title:"Create Job",description:"Create and execute a job. Set blocking=true to wait for completion and return output directly (30-minute timeout). When parentJobId is provided, depth validation ensures jobs do not exceed depth limit of 4.",schema:xx1,execute:gx1};v6();var yx1=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 (default: false). When true, polls until complete and returns output. 30-minute timeout applies.")}),bx1=async({core:$,projectId:K},q)=>{try{let{id:Y,prompt:J,model:z,blocking:W}=q,Z=await $.continueJob(K,{id:Y,prompt:J,model:z});if(!W)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await NL({core:$,projectId:K},Z.id,{intervalMs:_L,timeoutMs:kL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},X46={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 (30-minute timeout).",schema:yx1,execute:bx1};var O46=[Y46,J46,z46,W46,H46,X46];v6();C1();var ux1=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")')}),mx1=async({core:$,projectId:K},{query:q,field_name:Y,store:J})=>{try{let z=await $.searchTextFields(K,{query:q,fieldName:Y,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}}},P46={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:ux1,execute:mx1};v6();C1();var Bx1=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>")')}),Ux1=async({core:$,projectId:K},{query:q,store:Y,limit:J,offset:z,start_marker:W,end_marker:Z})=>{try{let H=await $.searchContent(K,{query:q,store:Y||"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}}},w46={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:Bx1,execute:Ux1};var G46=[P46,w46];var Fx1=({core:$,projectId:K})=>{let q=new xr({name:"caw-server",version:"0.0.1"},{capabilities:{tools:{}}}),Y=[...A16,...N16,...S16,...p16,...d16,...$46,...q46,...O46,...G46];for(let J of Y)q.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 q},T46=Object.freeze({createMcpServer:Fx1});var Qx1=yr.default("caw:mcp"),f5=yr.default("caw:mcp:session"),cx1=({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 f5("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 f5("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});f5("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)}}f5("Creating new MCP session for project: %s",H);let O=null,P=new Vo({sessionIdGenerator:()=>{return O=px1(),f5("Generated session ID: %s",O),O},onsessioninitialized:(T)=>{f5("Session initialized: %s",T),Z.setHeader("Mcp-Session-Id",T),G.sessionId=T,K.set(T,G),f5("Session stored: %s",T)}});f5("Creating new server for session");let w=T46.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))f5("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){f5("Cannot close session - not found: %s",W);return}f5("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)f5("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)Qx1("Cleaned up %d idle sessions",H)}})},j46=Object.freeze({create:cx1});var lx1=86400000,ix1=300000,dx1=({mcpSessionManager:$})=>{let{jobFns:K,controlFns:q}=Zh.create({name:"cleanup-idle-mcp-sessions",interval:ix1,execute:async()=>{$.cleanupIdle(lx1)}});return{job:Object.freeze(K),control:q}},v46=Object.freeze({create:dx1});var BL=$$6.default("caw:server"),Eg1=async({port:$,envDir:K,databaseFilepath:q,assetsDir:Y,lazilyCreateDb:J,enableRest:z,enableSse:W,enableStatic:Z,enableCrons:H,enableMcp:X=!0,enableCli:O=!0,staticPath:P,kyselyFactory:w})=>{let G=await di8.create({envDir:K,databaseFilepath:q,assetsDir:Y,lazilyCreateDb:J,kyselyFactory:w}),T=rr.default();if(T.use(rr.default.json()),T.get("/health",(k,R)=>{R.json({status:"ok"})}),T.get("/api/info",(k,R)=>{R.json({version:"1.0.71"})}),z)T.use("/api",la8),ri8.create(T,{core:G}),ei8.create(T,{core:G}),$d8.create(T,{core:G}),qd8.create(T,{core:G}),Jd8.create(T,{core:G}),Wd8.create(T,{core:G}),Hd8.create(T,{core:G}),ga8.create(T,{core:G}),ba8.create(T,{core:G}),ma8.create(T,{core:G}),Ua8.create(T,{core:G});if(W)pa8.create(T,{core:G}),ca8.create(T,{core:G});if(Z&&P)ai8.create(T,{staticPath:P});if(X){BL("Initializing MCP subsystem...");let k=j46.create({core:G});G.registerCronJob(v46,{mcpSessionManager:k}),da8.create(T,{core:G,mcpSessionManager:k}),BL("MCP subsystem initialized")}if(O){BL("Initializing CLI Adapter subsystem...");let k=(await Promise.resolve().then(() => (F46(),U46))).default,R=(await Promise.resolve().then(() => (l46(),c46))).default,h=(await Promise.resolve().then(() => (r46(),o46))).default,E=(await Promise.resolve().then(() => (e46(),t46))).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})}),E.create(T,{cliSessionManager:C}),BL("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()})})}}})},K$6=Object.freeze({create:Eg1});var Sg1=3131,Ig1=UL.join(UL.dirname(Lg1(import.meta.url)),"public_html"),xg1=!0,gg1=!0,yg1=!0,bg1=!0,ug1=!1,mg1=!0,Bg1=!0,Ug1=!0,eJ=($,K)=>$!==void 0?$==="true":K,Fg1=()=>{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:UL.join($,"caw.db"),assetsDir:UL.join($,"assets")}},pg1=async($)=>{let K=await K$6.create($),q=await K.start();console.log(`Server listening on port ${q}`),process.on("SIGINT",()=>K.stop("SIGINT")),process.on("SIGTERM",()=>K.stop("SIGTERM"))},Qg1=()=>{let{envDir:$,databaseFilepath:K,assetsDir:q}=Fg1(),Y=process.env.CAW_PORT?Number(process.env.CAW_PORT):Sg1,J=process.env.STATIC_PATH||Ig1,z=eJ(process.env.LAZY_CREATE_DB,xg1),W=eJ(process.env.ENABLE_REST,gg1),Z=eJ(process.env.ENABLE_MCP,yg1),H=eJ(process.env.ENABLE_CLI,bg1),X=eJ(process.env.ENABLE_WEBLLM_WSS,ug1),O=eJ(process.env.ENABLE_SSE,mg1),P=eJ(process.env.ENABLE_STATIC,Bg1),w=eJ(process.env.ENABLE_CRONS,Ug1);return Object.freeze({port:Y,staticPath:J,envDir:$,databaseFilepath:K,assetsDir:q,lazilyCreateDb:z,enableRest:W,enableMcp:Z,enableCli:H,enableWebllmWss:X,enableSse:O,enableStatic:P,enableCrons:w})};await pg1(Qg1()).catch(($)=>{console.error("ERROR: Failed to start server:",$),process.exit(1)});
|
|
754
|
+
To confirm, call delete_story with confirm=true`}]}}let J=await $.deleteStory(K,{id:q});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}}},s16={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:vx1,execute:Vx1};v6();var Ax1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),Mx1=async({core:$,projectId:K},{id:q})=>{try{let Y=await $.incrementStoryJobCount(K,{id:q});if(!Y)return{content:[{type:"text",text:`Story ${q} not found`}],isError:!0};return{content:[{type:"text",text:`Incremented activeJobCount for story ${Y.id} to: ${Y.fields.activeJobCount}`}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},t16={name:"increment_story_job_count",title:"Increment Story Job Count",description:"Increment a story's activeJobCount field (for manual tracking of agent activity)",schema:Ax1,execute:Mx1};v6();var _x1=A.object({id:A.string().describe('Story ID (e.g., "As1")')}),kx1=async({core:$,projectId:K},{id:q})=>{try{let Y=await $.decrementStoryJobCount(K,{id:q});if(!Y)return{content:[{type:"text",text:`Story ${q} not found`}],isError:!0};return{content:[{type:"text",text:`Decremented activeJobCount for story ${Y.id} to: ${Y.fields.activeJobCount}`}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},e16={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:_x1,execute:kx1};var $46=[n16,a16,o16,r16,s16,t16,e16];v6();var Nx1=A.object({id:A.string().describe('Asset ID (e.g., "Aa1")')}),fx1=async({core:$,projectId:K},{id:q})=>{try{let{buffer:Y,mimeType:J,sizeBytes:z,hash:W,originalFilename:Z}=await $.readAssetBinary(K,{id:q});return{content:[{type:"image",data:Y.toString("base64"),mimeType:J},{type:"text",text:JSON.stringify({id:q,originalFilename:Z,sizeBytes:z,hash:W},null,2)}]}}catch(Y){return{content:[{type:"text",text:Y.code==="ASSET_NOT_FOUND"||Y.code==="BLOB_NOT_FOUND"?Y.message:`Error: ${Y.message}`}],isError:!0}}},K46={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:Nx1,execute:fx1};var q46=[K46];v6();var Rx1=A.object({store:A.string().optional().describe('Store code (default: "A")'),status:A.string().optional().describe("Filter by status (pending, running, completed, error, stopped)")}),Cx1=async({core:$,projectId:K},{store:q,status:Y})=>{try{let J=await $.listJobs(K,{store:q||"A",status:Y});return{content:[{type:"text",text:JSON.stringify(J,null,2)}]}}catch(J){return{content:[{type:"text",text:`Error: ${J.message}`}],isError:!0}}},Y46={name:"list_jobs",title:"List Jobs",description:"List all jobs",schema:Rx1,execute:Cx1};v6();var hx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),Dx1=async({core:$,projectId:K},{id:q})=>{try{let Y=await $.getJob(K,{id:q});return{content:[{type:"text",text:JSON.stringify(Y,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},J46={name:"read_job",title:"Read Job",description:"Get job details",schema:hx1,execute:Dx1};v6();var Ex1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),Lx1=async({core:$,projectId:K},{id:q})=>{try{return{content:[{type:"text",text:await $.readJobOutput(K,{id:q})}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},z46={name:"read_job_output",title:"Read Job Output",description:"Read the output content from a job",schema:Ex1,execute:Lx1};v6();var Sx1=A.object({id:A.string().describe('Job ID (e.g., "Aj1")')}),Ix1=async({core:$,projectId:K},{id:q})=>{try{return{content:[{type:"text",text:await $.readJobPrompt(K,{id:q})}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},W46={name:"read_job_prompt",title:"Read Job Prompt",description:"Read the prompt content from a job",schema:Sx1,execute:Ix1};v6();var gr=4,_L=2000,kL=1800000;async function Z46($,K){let q=0,Y=K;Y=(await $.core.getJob($.projectId,{id:Y})).fields?.parentJobId;while(Y)q++,Y=(await $.core.getJob($.projectId,{id:Y})).fields?.parentJobId;return q}async function NL($,K,{intervalMs:q,timeoutMs:Y}){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>Y)throw Error(`Job ${K} did not complete within timeout (${Y/60000} minutes)`);await new Promise((Z)=>setTimeout(Z,q))}}var xx1=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"),parentJobId:A.string().optional().describe("Parent job ID for hierarchy tracking"),blocking:A.boolean().optional().describe("Wait for job completion (default: false). When true, polls until complete and returns output. 30-minute timeout applies."),dockerImage:A.string().optional().describe("Docker image for containerized execution"),store:A.string().optional().describe('Store code (default: "A")')}),gx1=async({core:$,projectId:K},q)=>{try{let{blocking:Y,parentJobId:J,store:z="A",...W}=q;if(J){let P=await Z46({core:$,projectId:K},J);if(P>=gr)throw Error(`Job depth limit exceeded (max ${gr}, parent depth: ${P})`)}let Z=await $.createJob(K,{...W,store:z,parentJobId:J,blocking:Y||!1});if(!Y)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await NL({core:$,projectId:K},Z.id,{intervalMs:_L,timeoutMs:kL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},H46={name:"create_job",title:"Create Job",description:"Create and execute a job. Set blocking=true to wait for completion and return output directly (30-minute timeout). When parentJobId is provided, depth validation ensures jobs do not exceed depth limit of 4.",schema:xx1,execute:gx1};v6();var yx1=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 (default: false). When true, polls until complete and returns output. 30-minute timeout applies.")}),bx1=async({core:$,projectId:K},q)=>{try{let{id:Y,prompt:J,model:z,blocking:W}=q,Z=await $.continueJob(K,{id:Y,prompt:J,model:z});if(!W)return{content:[{type:"text",text:JSON.stringify(Z,null,2)}]};let H=await NL({core:$,projectId:K},Z.id,{intervalMs:_L,timeoutMs:kL}),X=await $.readJobOutput(K,{id:Z.id}),O={...H,output:X};return{content:[{type:"text",text:JSON.stringify(O,null,2)}]}}catch(Y){return{content:[{type:"text",text:`Error: ${Y.message}`}],isError:!0}}},X46={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 (30-minute timeout).",schema:yx1,execute:bx1};var O46=[Y46,J46,z46,W46,H46,X46];v6();C1();var ux1=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")')}),mx1=async({core:$,projectId:K},{query:q,field_name:Y,store:J})=>{try{let z=await $.searchTextFields(K,{query:q,fieldName:Y,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}}},P46={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:ux1,execute:mx1};v6();C1();var Bx1=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>")')}),Ux1=async({core:$,projectId:K},{query:q,store:Y,limit:J,offset:z,start_marker:W,end_marker:Z})=>{try{let H=await $.searchContent(K,{query:q,store:Y||"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}}},w46={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:Bx1,execute:Ux1};var G46=[P46,w46];var Fx1=({core:$,projectId:K})=>{let q=new xr({name:"caw-server",version:"0.0.1"},{capabilities:{tools:{}}}),Y=[...A16,...N16,...S16,...p16,...d16,...$46,...q46,...O46,...G46];for(let J of Y)q.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 q},T46=Object.freeze({createMcpServer:Fx1});var Qx1=yr.default("caw:mcp"),f5=yr.default("caw:mcp:session"),cx1=({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 f5("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 f5("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});f5("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)}}f5("Creating new MCP session for project: %s",H);let O=null,P=new Vo({sessionIdGenerator:()=>{return O=px1(),f5("Generated session ID: %s",O),O},onsessioninitialized:(T)=>{f5("Session initialized: %s",T),Z.setHeader("Mcp-Session-Id",T),G.sessionId=T,K.set(T,G),f5("Session stored: %s",T)}});f5("Creating new server for session");let w=T46.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))f5("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){f5("Cannot close session - not found: %s",W);return}f5("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)f5("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)Qx1("Cleaned up %d idle sessions",H)}})},j46=Object.freeze({create:cx1});var lx1=86400000,ix1=300000,dx1=({mcpSessionManager:$})=>{let{jobFns:K,controlFns:q}=Zh.create({name:"cleanup-idle-mcp-sessions",interval:ix1,execute:async()=>{$.cleanupIdle(lx1)}});return{job:Object.freeze(K),control:q}},v46=Object.freeze({create:dx1});var BL=$$6.default("caw:server"),Eg1=async({port:$,envDir:K,databaseFilepath:q,assetsDir:Y,lazilyCreateDb:J,enableRest:z,enableSse:W,enableStatic:Z,enableCrons:H,enableMcp:X=!0,enableCli:O=!0,staticPath:P,kyselyFactory:w})=>{let G=await di8.create({envDir:K,databaseFilepath:q,assetsDir:Y,lazilyCreateDb:J,kyselyFactory:w}),T=rr.default();if(T.use(rr.default.json()),T.get("/health",(k,R)=>{R.json({status:"ok"})}),T.get("/api/info",(k,R)=>{R.json({version:"1.0.72"})}),z)T.use("/api",la8),ri8.create(T,{core:G}),ei8.create(T,{core:G}),$d8.create(T,{core:G}),qd8.create(T,{core:G}),Jd8.create(T,{core:G}),Wd8.create(T,{core:G}),Hd8.create(T,{core:G}),ga8.create(T,{core:G}),ba8.create(T,{core:G}),ma8.create(T,{core:G}),Ua8.create(T,{core:G});if(W)pa8.create(T,{core:G}),ca8.create(T,{core:G});if(Z&&P)ai8.create(T,{staticPath:P});if(X){BL("Initializing MCP subsystem...");let k=j46.create({core:G});G.registerCronJob(v46,{mcpSessionManager:k}),da8.create(T,{core:G,mcpSessionManager:k}),BL("MCP subsystem initialized")}if(O){BL("Initializing CLI Adapter subsystem...");let k=(await Promise.resolve().then(() => (F46(),U46))).default,R=(await Promise.resolve().then(() => (l46(),c46))).default,h=(await Promise.resolve().then(() => (r46(),o46))).default,E=(await Promise.resolve().then(() => (e46(),t46))).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})}),E.create(T,{cliSessionManager:C}),BL("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()})})}}})},K$6=Object.freeze({create:Eg1});var Sg1=3131,Ig1=UL.join(UL.dirname(Lg1(import.meta.url)),"public_html"),xg1=!0,gg1=!0,yg1=!0,bg1=!0,ug1=!1,mg1=!0,Bg1=!0,Ug1=!0,eJ=($,K)=>$!==void 0?$==="true":K,Fg1=()=>{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:UL.join($,"caw.db"),assetsDir:UL.join($,"assets")}},pg1=async($)=>{let K=await K$6.create($),q=await K.start();console.log(`Server listening on port ${q}`),process.on("SIGINT",()=>K.stop("SIGINT")),process.on("SIGTERM",()=>K.stop("SIGTERM"))},Qg1=()=>{let{envDir:$,databaseFilepath:K,assetsDir:q}=Fg1(),Y=process.env.CAW_PORT?Number(process.env.CAW_PORT):Sg1,J=process.env.STATIC_PATH||Ig1,z=eJ(process.env.LAZY_CREATE_DB,xg1),W=eJ(process.env.ENABLE_REST,gg1),Z=eJ(process.env.ENABLE_MCP,yg1),H=eJ(process.env.ENABLE_CLI,bg1),X=eJ(process.env.ENABLE_WEBLLM_WSS,ug1),O=eJ(process.env.ENABLE_SSE,mg1),P=eJ(process.env.ENABLE_STATIC,Bg1),w=eJ(process.env.ENABLE_CRONS,Ug1);return Object.freeze({port:Y,staticPath:J,envDir:$,databaseFilepath:K,assetsDir:q,lazilyCreateDb:z,enableRest:W,enableMcp:Z,enableCli:H,enableWebllmWss:X,enableSse:O,enableStatic:P,enableCrons:w})};await pg1(Qg1()).catch(($)=>{console.error("ERROR: Failed to start server:",$),process.exit(1)});
|