@whoz-oss/coday-server 0.121.0 → 0.121.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/package.json +1 -1
- package/server.js +2 -2
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -960,7 +960,7 @@ The following shows current values at different levels:
|
|
|
960
960
|
Note: Your global bio will also be included:
|
|
961
961
|
"${u}"`)}let c=await this.interactor.promptText(s,o||"");if(c!==null){a?this.services.user.setBio(c):this.services.user.setProjectBio(n,c);let u=a?"USER-level":`PROJECT-level for "${n}"`;c.trim()?this.interactor.displayText(`\u2705 ${u} bio updated successfully`):this.interactor.displayText(`\u2705 ${u} bio cleared`)}return i}};var dE=class extends Ne{constructor(t){super({commandWord:"cost",description:"Configure the cost limit threshold for AI operations"});this.interactor=t}async handle(t,n){let i=n.aiThread?.usage.priceThreshold??E5.priceThreshold,a=await this.interactor.promptText(`Cost limit in dollars (${i.toFixed(2)}):`,i.toString()),o=parseFloat(a.trim());return isNaN(o)||o<=0?(this.interactor.error("Invalid cost limit. Please enter a positive number or 0."),n):(n.aiThread&&(n.aiThread.usage.priceThreshold=o),this.interactor.displayText(`\u2705 Cost limit updated to $${o.toFixed(2)}`),n)}};var hE=class extends Yn{constructor(t,n){super({commandWord:"config",description:"handles config related commands"},t);this.services=n;this.selectProjectHandler=new aE(this.interactor,this.services),this.handlers=[new lE(this.interactor,this.services),new Fk(this.interactor,this.services),new fE(this.interactor,this.services),new zk(this.interactor,this.services),new pE(this.interactor,this.services),new dE(this.interactor)]}selectProjectHandler};var yde={anthropic:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY",google:"GEMINI_API_KEY"},Gm=class{constructor(e,t,n,i){this.interactor=e;this.userService=t;this.projectStateService=n;this.logger=i}aiClients=[];aiProviderConfigs;init(e){if(this.aiProviderConfigs)return;let t=e.project.ai||[],n=this.projectStateService.selectedProject?.config?.ai||[],i=this.userService.config.ai||[],a=this.detectProvidersFromEnvironment(),o=new Set([...t.map(l=>l.name.toLowerCase()),...n.map(l=>l.name.toLowerCase()),...i.map(l=>l.name.toLowerCase())]);this.aiProviderConfigs=[];for(let l of[...a,...t,...n,...i]){let f=this.aiProviderConfigs.findIndex(p=>p.name===l.name);if(f===-1)this.aiProviderConfigs.push(l);else{let p=this.aiProviderConfigs[f],d=p.models?[...p.models]:[];(l.models??[]).forEach(h=>{let g=d.findIndex(v=>v.alias===h.alias||v.name===h.name);if(g===-1)d.push(h);else{let v=d[g];d[g]={...v,...h,price:{...v.price,...h.price}}}}),this.aiProviderConfigs[f]={...p,...l,models:d}}}this.aiClients=this.aiProviderConfigs.map(l=>this.createClient(l)).filter(l=>!!l);let s=a.filter(l=>!o.has(l.name.toLowerCase())).map(l=>l.name.toLowerCase()),c=new Set(s),u=`AI providers (models listed as: name (alias)):
|
|
962
962
|
`+this.aiProviderConfigs.map(l=>{let f=this.aiClients.find(v=>v.name.toLowerCase()===l.name.toLowerCase()),p=!!f,d=p?"\u2705":"\u274C",h=c.has(l.name.toLowerCase())?" (auto-detected)":"",g=` - ${d} ${l.name}${h}`;if(p&&Array.isArray(f.models)&&f.models.length>0){let v=f.models.map(y=>y.alias&&y.alias!==y.name?`${y.name} (${y.alias})`:y.name).join(", ");g+=`, models: ${v}`}return g}).join(`
|
|
963
|
-
`);this.interactor.displayText(u)}getClient(e,t){let n=this.aiClients.filter(i=>{let a=!e||i.name.toLowerCase()===e.toLowerCase(),o=!t||i.supportsModel(t.toLowerCase());return a&&o});return n.length?n[0]:void 0}getAllModels(){let e=[];for(let t of this.aiClients)if(t.models)for(let n of t.models)e.push({name:n.name,providerName:t.name});return e}cleanup(){this.aiClients.forEach(e=>e.kill()),this.aiClients=[]}kill(){this.cleanup(),this.aiProviderConfigs=void 0}getApiKey(e){let t=yde[e.name];return(t?process.env[t]??process.env[`${e.name.toUpperCase()}_API_KEY`]:void 0)??e.apiKey}detectProvidersFromEnvironment(){let e=[];for(let[t,n]of Object.entries(yde)){let i=process.env[n];if(i){let a={name:t,apiKey:i};e.push(a),this.interactor.debug(`\u{1F50D} Auto-detected ${t} provider from ${n} environment variable`)}}return e}createClient(e){let t=this.getApiKey(e);if(!t){this.interactor.displayText(`\u2139\uFE0F no api key for AI provider '${e.name}'`);return}let n={...e,apiKey:t};switch(e.name.toLowerCase()){case"anthropic":return new i_(this.interactor,n,this.logger);case"google":return new Sk(this.interactor,n,this.logger);default:return new Rm(this.interactor,n,this.logger)}}};function bde(r){return({query:t,agentName:n})=>{try{let i=`@${n} ${t}`;return r.addCommands(i),`Query successfully redirected to agent '${n}'. The agent will process the query with full context after this run completes.`}catch(i){return`Error during redirection: ${i.message}`}}}var Ig=class extends Zt{constructor(t,n,i,a){super(t,i,a);this.agentSummaries=n}static TYPE="AI";async buildTools(t,n){let i=[];if(!t.oneshot){let a=async({message:s,options:c})=>`User answered: ${c?.length?await this.interactor.chooseOption(c,s,void 0,!0):await this.interactor.promptText(s)}`,o={type:"function",function:{name:`${this.name}__queryUser`,description:`Allows to ask the user a question.
|
|
963
|
+
`);this.interactor.displayText(u)}getClient(e,t){let n=this.aiClients.filter(i=>{let a=!e||i.name.toLowerCase()===e.toLowerCase(),o=!t||i.supportsModel(t.toLowerCase());return a&&o});return n.length?n[0]:void 0}getAllModels(){let e=[];for(let t of this.aiClients)if(t.models)for(let n of t.models)e.push({name:n.name,providerName:t.name});return e}cleanup(){this.aiClients.forEach(e=>e.kill()),this.aiClients=[]}kill(){this.cleanup(),this.aiProviderConfigs=void 0}getApiKey(e){let t=yde[e.name];return(t?process.env[t]??process.env[`${e.name.toUpperCase()}_API_KEY`]:void 0)??e.apiKey}detectProvidersFromEnvironment(){let e=[];for(let[t,n]of Object.entries(yde)){let i=process.env[n];if(i){let a={name:t,apiKey:i};e.push(a),this.interactor.debug(`\u{1F50D} Auto-detected ${t} provider from ${n} environment variable`)}}return e}createClient(e){let t=this.getApiKey(e);if(!t){this.interactor.displayText(`\u2139\uFE0F no api key for AI provider '${e.name}'`);return}let n={...e,apiKey:t};switch(e.name.toLowerCase()){case"anthropic":return new i_(this.interactor,n,this.logger);case"google":return new Sk(this.interactor,n,this.logger);default:return new Rm(this.interactor,n,this.logger)}}};function bde(r,e){return({query:t,agentName:n})=>{try{if(e&&n.toLowerCase()===e.toLowerCase())return`Redirection to self ('${n}') is not allowed. Please answer the query directly instead of redirecting to yourself.`;let i=`@${n} ${t}`;return r.addCommands(i),`Query successfully redirected to agent '${n}'. The agent will process the query with full context after this run completes.`}catch(i){return`Error during redirection: ${i.message}`}}}var Ig=class extends Zt{constructor(t,n,i,a){super(t,i,a);this.agentSummaries=n}static TYPE="AI";async buildTools(t,n){let i=[];if(!t.oneshot){let a=async({message:s,options:c})=>`User answered: ${c?.length?await this.interactor.chooseOption(c,s,void 0,!0):await this.interactor.promptText(s)}`,o={type:"function",function:{name:`${this.name}__queryUser`,description:`Allows to ask the user a question.
|
|
964
964
|
IMPORTANT: Use this tool only when necessary, as it is intrusive for the user.
|
|
965
965
|
|
|
966
966
|
If no options are provided, the user can answer with free text.
|
|
@@ -1206,7 +1206,7 @@ Actions:
|
|
|
1206
1206
|
- stop <session>: kill a session and its processes
|
|
1207
1207
|
|
|
1208
1208
|
Use clear, stable session names matching the application role, eg: "backend", "frontend", "agentos", "worker", "db".
|
|
1209
|
-
New sessions always start from the project root directory.`,parameters:{type:"object",properties:{action:{type:"string",enum:["list","status","start","logs","send","stop"],description:"The tmux action to perform."},session:{type:"string",description:"Session name (required for all actions except list)."},command:{type:"string",description:"Command to run (required for start and send actions)."}}},parse:JSON.parse,function:async({action:i,session:a,command:o})=>{try{if(a!==void 0){let s=s5t(a);if(s)return s}switch(i){case"list":try{let{stdout:s}=await Ud("tmux",["list-sessions"]);return s.trim()}catch{return"No tmux sessions running"}case"status":{if(!a)return"Error: session name is required for status";try{return await Ud("tmux",["has-session","-t",a]),"running"}catch{return"stopped"}}case"start":{if(!a)return"Error: session name is required for start";if(!o)return"Error: command is required for start";try{await Ud("tmux",["new-session","-d","-s",a,"-x","220","-y","50","-c",n])}catch{}return await Ud("tmux",["send-keys","-t",a,o,"Enter"]),`Session '${a}' started`}case"logs":{if(!a)return"Error: session name is required for logs";try{let{stdout:s}=await Ud("tmux",["capture-pane","-t",a,"-p","-S","-200"]);return s.trim()}catch{return`Session '${a}' not found`}}case"send":{if(!a)return"Error: session name is required for send";if(!o)return"Error: command is required for send";try{return await Ud("tmux",["send-keys","-t",a,o,"Enter"]),`Command sent to session '${a}'`}catch{return`Session '${a}' not found`}}case"stop":{if(!a)return"Error: session name is required for stop";try{return await Ud("tmux",["kill-session","-t",a]),`Session '${a}' killed`}catch{return`Session '${a}' not found`}}default:return`Unknown action: ${i}`}}catch(s){let c=`tmux error: ${s instanceof Error?s.message:String(s)}`;return this.interactor.error(c),c}}}}]}async kill(){}};var c5t=["push
|
|
1209
|
+
New sessions always start from the project root directory.`,parameters:{type:"object",properties:{action:{type:"string",enum:["list","status","start","logs","send","stop"],description:"The tmux action to perform."},session:{type:"string",description:"Session name (required for all actions except list)."},command:{type:"string",description:"Command to run (required for start and send actions)."}}},parse:JSON.parse,function:async({action:i,session:a,command:o})=>{try{if(a!==void 0){let s=s5t(a);if(s)return s}switch(i){case"list":try{let{stdout:s}=await Ud("tmux",["list-sessions"]);return s.trim()}catch{return"No tmux sessions running"}case"status":{if(!a)return"Error: session name is required for status";try{return await Ud("tmux",["has-session","-t",a]),"running"}catch{return"stopped"}}case"start":{if(!a)return"Error: session name is required for start";if(!o)return"Error: command is required for start";try{await Ud("tmux",["new-session","-d","-s",a,"-x","220","-y","50","-c",n])}catch{}return await Ud("tmux",["send-keys","-t",a,o,"Enter"]),`Session '${a}' started`}case"logs":{if(!a)return"Error: session name is required for logs";try{let{stdout:s}=await Ud("tmux",["capture-pane","-t",a,"-p","-S","-200"]);return s.trim()}catch{return`Session '${a}' not found`}}case"send":{if(!a)return"Error: session name is required for send";if(!o)return"Error: command is required for send";try{return await Ud("tmux",["send-keys","-t",a,o,"Enter"]),`Command sent to session '${a}'`}catch{return`Session '${a}' not found`}}case"stop":{if(!a)return"Error: session name is required for stop";try{return await Ud("tmux",["kill-session","-t",a]),`Session '${a}' killed`}catch{return`Session '${a}' not found`}}default:return`Unknown action: ${i}`}}catch(s){let c=`tmux error: ${s instanceof Error?s.message:String(s)}`;return this.interactor.error(c),c}}}}]}async kill(){}};var c5t=["push -f","push --force","push --tags","reset","&&","clean"],u5t=["list"],t6e=async({params:r,root:e,interactor:t})=>{let n=r.trimStart();if(n.startsWith("worktree ")){let o=n.slice(9).trimStart().split(/\s+/)[0]??"";if(!u5t.includes(o))return`Error: 'git worktree ${o}' is not allowed via the generic git tool. Use the dedicated worktree tools (list_worktrees, create_worktree, remove_worktree) instead.`}let i=`git ${r}`,a=c5t.some(o=>r.includes(o));return await Io({command:i,root:e,interactor:t,requireConfirmation:a})};var I9=class extends Zt{constructor(t,n,i,a){super(t,i,a);this.integrationService=n}static TYPE="GIT";async buildTools(t,n){let i=[];if(!this.integrationService.hasIntegration("GIT"))return i;let a=async({params:s})=>await t6e({params:s,root:t.project.root,interactor:this.interactor}),o={type:"function",function:{name:`${this.name}__git`,description:'Run git command and parameters. Note: worktree subcommands are blocked except "list" \u2014 use the dedicated worktree tools instead.',parameters:{type:"object",properties:{params:{type:"string",description:"Additional command (like add, log, diff, status) and parameters for git, will be composed as `git [params]`."}}},parse:JSON.parse,function:a}};return i.push(o),i}};import*as su from"node:path";import*as nc from"node:fs/promises";function r6e(r){return r.replace(/[^a-zA-Z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")}async function l5t(r){let e=su.join(r,".git");try{return(await nc.lstat(e)).isFile()}catch{return!1}}function n6e(r,e){return`${r}__${e}`}function f5t(r,e){let t=`${r}__`;return e.startsWith(t)?e.slice(t.length):null}var D9=class extends Zt{constructor(t,n,i,a,o){super(t,i,a);this.integrationService=n;this.projectService=o}static TYPE="GIT_WORKTREE";async buildTools(t,n){let i=[];if(!this.integrationService.hasIntegration("GIT_WORKTREE"))return i;if(!this.projectService)return this.interactor.debug("[WORKTREE] ProjectService not available, worktree tools disabled"),i;let a=this.projectService,o=t.project.root,s=t.project.name,c=su.dirname(o),u=await l5t(o),l={type:"function",function:{name:`${this.name}__list_worktrees`,description:"Lists all git worktrees for the current repository, with their Coday project registration status.",parameters:{type:"object",properties:{}},parse:JSON.parse,function:async()=>{let p=await Io({command:"git worktree list --porcelain",root:o,interactor:this.interactor}),d=[];for(let h of p.split(/\n\n+/).filter(Boolean)){let g=h.trim().split(`
|
|
1210
1210
|
`),v=g.find(S=>S.startsWith("worktree ")),y=g.find(S=>S.startsWith("branch "));if(!v)continue;let x=v.replace("worktree ","").trim(),_=y?y.replace("branch refs/heads/","").trim():"(detached)";d.push({path:x,branch:_})}let m=d[0]?.path;return JSON.stringify(d.map(h=>{let g=h.path===m,v=su.basename(h.path),y=g?s:v;return{branch:h.branch,path:h.path,projectName:y,isMain:g}}),null,2)}}};if(i.push(l),!u){let p={type:"function",function:{name:`${this.name}__create_worktree`,description:"Creates a git worktree on the given branch (creates the branch if it does not exist) and registers it as a Coday sub-project. Returns projectName, worktreePath and branch.",parameters:{type:"object",properties:{branch:{type:"string",description:"Branch name to create or checkout (e.g., feat/my-feature)"}},required:["branch"]},parse:JSON.parse,function:async({branch:d})=>{if(!/^[a-zA-Z0-9._/-]+$/.test(d))return`Error: branch name contains invalid characters: ${d}`;let m=r6e(d),h=su.join(c,`${s}__${m}`),g=n6e(s,m);try{return await nc.access(h),`Error: worktree path already exists: ${h}`}catch{}let v=await Io({command:`git worktree add "${h}" -b "${d}"`,root:o,interactor:this.interactor});if(v.startsWith("Command failed:")){try{await nc.rmdir(h)}catch{}if(v=await Io({command:`git worktree add "${h}" "${d}"`,root:o,interactor:this.interactor}),v.startsWith("Command failed:"))return`Failed to create worktree:
|
|
1211
1211
|
${v}`}await a.registerWorktreeProject(g,h,s),this.interactor.debug(`[WORKTREE] Registered project '${g}' at ${h}`);let y=this.config?.postCreateScript;if(typeof y=="string"&&y.trim()){let x=su.resolve(o,y),_,S;try{await nc.access(x,nc.constants.X_OK);let C=await Io({command:`"${x}"`,root:h,interactor:this.interactor});C.startsWith("Command failed:")?(_=`postCreateScript failed: ${C}`,this.interactor.warn(`[WORKTREE] ${_}`)):(S=C,this.interactor.displayText(`[WORKTREE] postCreateScript completed:
|
|
1212
1212
|
${C}`))}catch{_=`postCreateScript not found or not executable: ${x}`,this.interactor.warn(`[WORKTREE] ${_}`)}return JSON.stringify({projectName:g,worktreePath:h,branch:d,..._?{warning:_}:{},...S?{postCreateScriptOutput:S}:{}})}return JSON.stringify({projectName:g,worktreePath:h,branch:d})}}};i.push(p)}let f={type:"function",function:{name:`${this.name}__remove_worktree`,description:"Removes a git worktree by branch name and cleans up the associated Coday project entry. Use force=true to remove even with uncommitted changes. Optionally provide projectName (from list_worktrees) to locate the actual directory when the path does not match the branch-derived name (e.g. legacy worktrees).",parameters:{type:"object",properties:{branch:{type:"string",description:"Branch whose worktree to remove"},projectName:{type:"string",description:"Optional. The Coday project name for the worktree (as shown by list_worktrees). When provided, the actual directory is resolved from this name instead of being derived from the branch name. Useful for legacy worktrees whose directory name does not match the current naming convention."},force:{type:"boolean",description:"Force removal even if the worktree has uncommitted changes"}},required:["branch"]},parse:JSON.parse,function:async({branch:p,projectName:d,force:m})=>{if(!/^[a-zA-Z0-9._/-]+$/.test(p))return`Error: branch name contains invalid characters: ${p}`;let h=r6e(p),g,v;if(d){let _=f5t(s,d);if(_===null)return`Error: projectName "${d}" does not start with the expected prefix "${s}__". Use list_worktrees to get the correct projectName.`;v=_,g=d}else v=h,g=n6e(s,h);let y=su.join(c,`${s}__${v}`);if(y===o)return"Error: cannot remove the main worktree";let x=!1;try{x=(await nc.stat(y)).isDirectory()}catch{}if(x){let _=await Io({command:`git worktree remove "${y}"${m?" --force":""}`,root:o,interactor:this.interactor});if(_.startsWith("Command failed:"))return`Failed to remove worktree:
|