@whoz-oss/coday-server 0.118.0 → 0.118.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +4 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whoz-oss/coday-server",
3
- "version": "0.118.0",
3
+ "version": "0.118.2",
4
4
  "repository": "https://github.com/whoz-oss/coday",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
package/server.js CHANGED
@@ -307,7 +307,7 @@ ${a}\r
307
307
 
308
308
  More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md#removed-logprovider-and-loglevel-options
309
309
  `)),t}function vK(r){let e=r.logProvider&&r.logProvider();return e&&(r.logger=e),(0,I_t.getLogger)(r)}});var __e=k(bK=>{"use strict";Object.defineProperty(bK,"__esModule",{value:!0});bK.legacyCreateProxyMiddleware=N_t;var $_t=dK(),R_t=Ol(),j_t=x_e(),w_e=R_t.Debug.extend("legacy-create-proxy-middleware");function N_t(r,e){w_e("init");let t=(0,j_t.legacyOptionsAdapter)(r,e),n=(0,$_t.createProxyMiddleware)(t);return w_e("add marker for patching req.url (old behavior)"),n.__LEGACY_HTTP_PROXY_MIDDLEWARE__=!0,n}});var S_e=k(dI=>{"use strict";Object.defineProperty(dI,"__esModule",{value:!0});dI.legacyCreateProxyMiddleware=void 0;var F_t=__e();Object.defineProperty(dI,"legacyCreateProxyMiddleware",{enumerable:!0,get:function(){return F_t.legacyCreateProxyMiddleware}})});var k_e=k(C3=>{"use strict";var M_t=C3&&C3.__createBinding||(Object.create?(function(r,e,t,n){n===void 0&&(n=t);var i=Object.getOwnPropertyDescriptor(e,t);(!i||("get"in i?!e.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return e[t]}}),Object.defineProperty(r,n,i)}):(function(r,e,t,n){n===void 0&&(n=t),r[n]=e[t]})),L_t=C3&&C3.__exportStar||function(r,e){for(var t in r)t!=="default"&&!Object.prototype.hasOwnProperty.call(e,t)&&M_t(e,r,t)};Object.defineProperty(C3,"__esModule",{value:!0});L_t(S_e(),C3)});var xK=k(xu=>{"use strict";var q_t=xu&&xu.__createBinding||(Object.create?(function(r,e,t,n){n===void 0&&(n=t);var i=Object.getOwnPropertyDescriptor(e,t);(!i||("get"in i?!e.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return e[t]}}),Object.defineProperty(r,n,i)}):(function(r,e,t,n){n===void 0&&(n=t),r[n]=e[t]})),hI=xu&&xu.__exportStar||function(r,e){for(var t in r)t!=="default"&&!Object.prototype.hasOwnProperty.call(e,t)&&q_t(e,r,t)};Object.defineProperty(xu,"__esModule",{value:!0});hI(dK(),xu);hI(b_e(),xu);hI(qJ(),xu);hI(k_e(),xu)});var xI=lr(mne(),1);import Rl from"path";import mI from"fs";import*as _ye from"path";var Ne=class{commandWord;description;requiredIntegrations=[];isInternal=!1;constructor(e){this.commandWord=e.commandWord,this.description=e.description,this.requiredIntegrations=e.requiredIntegrations||[],this.isInternal=!!e?.isInternal}getSubCommand(e){return e.slice(this.commandWord.length).trim()}accept(e,t){return!!e&&e.toLowerCase().startsWith(this.commandWord)}};var goe=lr(wo(),1);function kj(r,e=80){if(r.length<=e)return r;try{if(r.trim().startsWith("{")||r.trim().startsWith("[")){let t=JSON.parse(r),n=JSON.stringify(t);return n.length<=e?n:n.substring(0,e)+`...(${n.length} chars)`}}catch{}return r.substring(0,e)+"..."}var ri=class{constructor(e,t){this.type=t;if(e.timestamp)this.timestamp=e.timestamp;else{let n=Math.random().toString(36).substring(2,7);this.timestamp=`${new Date().toISOString()}-${n}`}this.parentKey=e.parentKey,this.threadId=e.threadId,this.length=0}timestamp;parentKey;static type;length;threadId;get date(){let e=this.timestamp.lastIndexOf("-");if(e>0){let t=this.timestamp.substring(e+1);if(t.length===5&&/^[a-z0-9]+$/.test(t))return new Date(this.timestamp.substring(0,e))}return new Date(this.timestamp)}},ww=class extends ri{invite;constructor(e,t){super(e,t),this.invite=e.invite}buildAnswer(e){return new Sr({answer:e,parentKey:this.timestamp,invite:this.invite})}},H0=class r extends ri{static type="heartbeat";constructor(e){super(e,r.type)}},Fi=class r extends ww{defaultValue;static type="invite";constructor(e){super(e,r.type),this.defaultValue=e.defaultValue}},moe="InviteEventDefault",Sr=class r extends ri{answer;invite;name;static type="answer";constructor(e){super(e,r.type),this.answer=e.answer??"No answer",this.invite=e.invite,this.name=e.name}},ju=class r extends ri{speaker;text;static type="text";constructor(e){super(e,r.type),this.speaker=e.speaker,this.text=e.text}},m2=class r extends ri{chunk;static type="text_chunk";constructor(e){super(e,r.type),this.chunk=e.chunk,this.length=this.chunk.length}},W0=class r extends ri{warning;static type="warn";constructor(e){super(e,r.type),this.warning=e.warning}},Ha=class r extends ri{error;static type="error";constructor(e){super(e,r.type),this.error=e.error}},Mi=class r extends ww{options;optionalQuestion;allowFreeText;static type="choice";constructor(e){super(e,r.type),this.options=e.options,this.optionalQuestion=e.optionalQuestion,this.allowFreeText=e.allowFreeText??!1}},hn=class r extends ri{toolRequestId;name;args;static type="tool_request";constructor(e){super(e,r.type),this.toolRequestId=e.toolRequestId??this.timestamp,this.name=e.name,this.args=e.args,this.length=this.args.length+this.name.length+this.toolRequestId.length+20}buildResponse(e){return new Tn({output:e,toolRequestId:this.toolRequestId,parentKey:this.timestamp})}toSingleLineString(e=50){let t=kj(this.args,e);return`\u{1F527} ${this.name}(${t})`}},Tn=class r extends ri{toolRequestId;output;static type="tool_response";constructor(e){if(super(e,r.type),this.toolRequestId=e.toolRequestId||this.timestamp||new Date().toISOString(),this.output=e.output,typeof this.output=="string")this.length=this.output.length+this.toolRequestId.length+20;else if(this.output.type==="text")this.length=this.output.content.length+this.toolRequestId.length+20;else if(this.output.type==="image"){let t=(this.output.width??0)*(this.output.height??0)/750;this.length=(t?t*3.5:this.output.content.length)+this.toolRequestId.length+20}else this.length=this.toolRequestId.length+20}getTextOutput(){return typeof this.output=="string"?this.output:this.output.type==="text"?this.output.content:this.output.type==="image"?`[Image: ${this.output.mimeType}]`:""}toSingleLineString(e=50){let t=this.getTextOutput(),n=kj(t,e),i=typeof this.output!="string"&&this.output.type==="image"?" [image]":"";return`\u2B91 ${n}${i}`}},dp=class r extends ri{static type="thinking";static debounce=5e3;constructor(e){super(e,r.type)}},xa=class r extends ri{summary;static type="summary";constructor(e){super(e,r.type),this.summary=e.summary,this.length=this.summary.length}toSingleLineString(e=80){return`\u{1F4CB} Summary: ${kj(this.summary,e)}`}},jt=class r extends ri{role;name;content;static type="message";constructor(e){super(e,r.type),this.role=e.role,this.name=e.name,this.content=e.content,this.length=this.content.map(t=>{if(t.type==="text")return t.content.length;if(t.type==="image"){let n=(t.width||0)*(t.height||0)/750;return n?n*3.5:t.content.length}return 0}).reduce((t,n)=>t+n,0)}getTextContent(){return this.content.filter(e=>e.type==="text").map(e=>e.content).join(`
310
- `)}},Tc=class r extends ri{threadId;name;summary;users;static type="thread_update";constructor(e){super(e,r.type),this.threadId=e.threadId,this.name=e.name,this.summary=e.summary,this.users=e.users}},Pc=class r extends ri{filename;operation;size;mimeType;static type="file";constructor(e){super(e,r.type),this.filename=e.filename,this.operation=e.operation,this.size=e.size,this.mimeType=e.mimeType}toSingleLineString(){let e=this.operation==="created"?"\u{1F4C4}":this.operation==="updated"?"\u{1F4DD}":"\u{1F5D1}\uFE0F",t=this.size?` (${(this.size/1024).toFixed(1)} KB)`:"";return`${e} ${this.operation}: ${this.filename}${t}`}},Ic=class r extends ri{authUrl;state;integrationName;static type="oauth_request";constructor(e){super(e,r.type),this.authUrl=e.authUrl,this.state=e.state,this.integrationName=e.integrationName}},_o=class r extends ri{subThreadId;agentName;static type="delegation";constructor(e){super(e,r.type),this.subThreadId=e.subThreadId,this.agentName=e.agentName}},_w=class r extends ri{code;state;integrationName;error;errorDescription;static type="oauth_callback";constructor(e){super(e,r.type),this.code=e.code,this.state=e.state,this.integrationName=e.integrationName,this.error=e.error,this.errorDescription=e.errorDescription}},tKe={[jt.type]:jt,[Sr.type]:Sr,[Mi.type]:Mi,[_o.type]:_o,[Ha.type]:Ha,[H0.type]:H0,[Fi.type]:Fi,[hn.type]:hn,[Tn.type]:Tn,[ju.type]:ju,[m2.type]:m2,[dp.type]:dp,[W0.type]:W0,[Tc.type]:Tc,[xa.type]:xa,[Pc.type]:Pc,[Ic.type]:Ic,[_w.type]:_w};function Sw(r){let e=tKe[r.type];return e?new e(r):void 0}var kw=class{constructor(e,t,n,i=!1){this.aiClient=t;this.tools=n;this.internal=i;this.name=e.name,this.description=e.description,this.definition={...e}}name;description;definition;get systemInstructions(){return this.definition.instructions}getAiClient(){return this.aiClient}async run(e,t,n){let i=e.trim(),a=new Sr({answer:i,name:n??t.username});t.addAnswerEvent(a);let o=await this.aiClient.run(this,t),s=new goe.ReplaySubject;return o.subscribe(s),s.asObservable()}};var Ew={name:"Coday",description:"Default fallback agent with neutral character and access to all tools",instructions:`
310
+ `)}},Tc=class r extends ri{threadId;name;summary;users;static type="thread_update";constructor(e){super(e,r.type),this.threadId=e.threadId,this.name=e.name,this.summary=e.summary,this.users=e.users}},Pc=class r extends ri{filename;operation;size;mimeType;static type="file";constructor(e){super(e,r.type),this.filename=e.filename,this.operation=e.operation,this.size=e.size,this.mimeType=e.mimeType}toSingleLineString(){let e=this.operation==="created"?"\u{1F4C4}":this.operation==="updated"?"\u{1F4DD}":"\u{1F5D1}\uFE0F",t=this.size?` (${(this.size/1024).toFixed(1)} KB)`:"";return`${e} ${this.operation}: ${this.filename}${t}`}},Ic=class r extends ri{authUrl;state;integrationName;static type="oauth_request";constructor(e){super(e,r.type),this.authUrl=e.authUrl,this.state=e.state,this.integrationName=e.integrationName}},_o=class r extends ri{subThreadId;agentName;static type="delegation";constructor(e){super(e,r.type),this.subThreadId=e.subThreadId,this.agentName=e.agentName}},_w=class r extends ri{code;state;integrationName;error;errorDescription;static type="oauth_callback";constructor(e){super(e,r.type),this.code=e.code,this.state=e.state,this.integrationName=e.integrationName,this.error=e.error,this.errorDescription=e.errorDescription}},tKe={[jt.type]:jt,[Sr.type]:Sr,[Mi.type]:Mi,[_o.type]:_o,[Ha.type]:Ha,[H0.type]:H0,[Fi.type]:Fi,[hn.type]:hn,[Tn.type]:Tn,[ju.type]:ju,[m2.type]:m2,[dp.type]:dp,[W0.type]:W0,[Tc.type]:Tc,[xa.type]:xa,[Pc.type]:Pc,[Ic.type]:Ic,[_w.type]:_w};function Sw(r){let e=tKe[r.type];return e?new e(r):void 0}var kw=class{constructor(e,t,n,i=!1){this.aiClient=t;this.tools=n;this.internal=i;this.name=e.name,this.description=e.description,this.definition={...e}}name;description;definition;get systemInstructions(){return this.definition.instructions}getAiClient(){return this.aiClient}async run(e,t,n){if(!t)throw new Error(`[Agent:${this.name}] Cannot run: thread context is undefined. Ensure initThread() completed before agent execution.`);let i=e.trim(),a=new Sr({answer:i,name:n??t.username});t.addAnswerEvent(a);let o=await this.aiClient.run(this,t),s=new goe.ReplaySubject;return o.subscribe(s),s.asObservable()}};var Ew={name:"Coday",description:"Default fallback agent with neutral character and access to all tools",instructions:`
311
311
  You are Coday, an AI assistant designed for interactive usage by users through various chat-like interfaces.
312
312
 
313
313
  When other agents are available, you should redirect the user request to the appropriate agent, given the topic.
@@ -877,7 +877,7 @@ Are you sure you want to delete this MCP server configuration?
877
877
 
878
878
  Here are the information collected during previous chats:
879
879
  `),n.join(`
880
- `)):""}loadMemoriesFrom(e){this.memories=[],this.userMemoriesPath=this.readMemories(this.userMemoriesPath)?this.userMemoriesPath:void 0;let t=e?.configPath?Jk.join(e.configPath,EL):void 0;this.projectMemoriesPath=this.readMemories(t)?t:void 0,e&&this.checkInit()}readMemories(e){if(!e)return!1;try{if(!jot(e)){let n=Ype.stringify({memories:[]});Not(e,n)}let t=oa(e);return t?(this.memories.push(...t.memories),!0):!1}catch{return!1}}checkInit(){if(!this.userMemoriesPath||!this.projectMemoriesPath)throw new Error("user or project path not set for memory service")}saveMemories(){this.checkInit();let e=this.memories.filter(n=>n.level==="USER"),t=this.memories.filter(n=>n.level==="PROJECT");Un(this.userMemoriesPath,{memories:e}),Un(this.projectMemoriesPath,{memories:t})}};import*as ede from"crypto";import*as No from"node:fs/promises";import*as jo from"path";import{exec as Qpe}from"child_process";var zm=class r{constructor(e,t,n){this.repository=e;this.defaultProject=t;this.isForcedMode=n}maskingService=new af;listProjects(){let e=this.repository.listProjects();return this.isForcedMode&&this.defaultProject?e.filter(t=>t===this.defaultProject).map(t=>{let n=this.repository.getConfig(t);return{name:t,volatile:n?.volatile}}):e.map(t=>{let n=this.repository.getConfig(t);return{name:t,volatile:n?.volatile}})}getDefaultProject(){return this.defaultProject||null}getForcedMode(){return this.isForcedMode}getProject(e){this.checkAgainstForced(e);let t=this.repository.getConfig(e);if(t)return{name:e,config:t};if(!this.isForcedMode&&this.defaultProject){let n=process.cwd(),i=r.generateProjectId(n),a=jo.basename(n);if(e===a||e===i||e===this.defaultProject){console.log(`[PROJECT_SERVICE] Creating volatile project for '${e}' at ${n}`);let o=this.getOrCreateVolatileProject(n);if(t=this.repository.getConfig(o),t)return{name:o,config:t}}}return null}exists(e){return this.checkAgainstForced(e),this.repository.exists(e)}createProject(e,t){if(this.checkAgainstForced(e),!e||!t)throw new Error("Project name and path are required");if(!this.repository.createProject(e,t))throw new Error(`Project '${e}' already exists`)}updateProjectConfig(e,t){if(this.checkAgainstForced(e),!this.repository.exists(e))throw new Error(`Project '${e}' does not exist`);this.repository.saveConfig(e,t)}async deleteProject(e,t){if(this.checkAgainstForced(e),e.includes("__"))t?.removeGitWorktree&&await this.removeGitWorktree(e),await this.unregisterWorktreeProject(e);else if(!this.repository.deleteProject(e))throw new Error(`Project '${e}' does not exist`)}async removeGitWorktree(e){let t=e.lastIndexOf("__");if(t===-1)return;let n=e.substring(0,t),i=this.repository.getConfig(n);if(!i?.path){console.warn(`[PROJECT_SERVICE] Cannot remove git worktree: parent project '${n}' config not found`);return}let a=i.path,o=jo.dirname(a),s=jo.join(o,e);try{await No.access(s)}catch{console.warn(`[PROJECT_SERVICE] Worktree path does not exist, skipping git removal: ${s}`);return}Qpe(`git worktree remove "${s}" --force`,{cwd:a},c=>{if(c){console.error(`[PROJECT_SERVICE] Failed to remove git worktree: ${c.message}`);return}console.log(`[PROJECT_SERVICE] Removed git worktree: ${s}`),Qpe("git worktree prune",{cwd:a},u=>{u&&console.warn(`[PROJECT_SERVICE] git worktree prune failed: ${u.message}`)})})}getProjectConfigForClient(e){this.checkAgainstForced(e);let t=this.repository.getConfig(e);return t?this.maskingService.maskConfig(t):null}updateProjectConfigFromClient(e,t){this.checkAgainstForced(e);let n=this.repository.getConfig(e);if(!n)throw new Error(`Project '${e}' does not exist`);let i=this.maskingService.unmaskConfig(t,n);this.repository.saveConfig(e,i)}checkAgainstForced(e){if(this.isForcedMode&&this.defaultProject&&this.defaultProject!==e)throw Error(`Project selection outside of ${this.defaultProject} not allowed`)}getOrCreateVolatileProject(e){let t=r.generateProjectId(e);if(this.repository.exists(t))return t;this.repository.createProject(t,e);let n=this.repository.getConfig(t);return n&&(n.volatile=!0,n.createdAt=Date.now(),this.repository.saveConfig(t,n)),t}async registerWorktreeProject(e,t,n){let i=this.repository.getConfig(n),a=i?{...i,path:t,volatile:void 0,createdAt:void 0}:{version:1,path:t,integration:{},storage:{type:"file"},agents:[]};this.repository.createProject(e,t),this.repository.saveConfig(e,a);let o=this.repository.getProjectInfo(n);if(o){let s=this.repository.getProjectInfo(e);if(s)for(let c of["agents","prompts","schedulers","memories"]){let u=jo.join(o.configPath,c),l=jo.join(s.configPath,c);try{await No.access(u),await No.symlink(u,l)}catch{}}}}async unregisterWorktreeProject(e){let t=this.repository.getProjectInfo(e);if(!t)return;let n=e.lastIndexOf("__");if(n!==-1){let i=e.substring(0,n),a=this.repository.getProjectInfo(i);a&&await this.migrateThreadsToParent(t.configPath,a.configPath)}this.repository.deleteProject(e)}async migrateThreadsToParent(e,t){let n=jo.join(e,"threads"),i=jo.join(t,"threads");try{await No.access(n)}catch{return}await No.mkdir(i,{recursive:!0});let a=await No.readdir(n);for(let o of a){let s=jo.join(n,o),c=jo.join(i,o);try{await No.access(c)}catch{await No.rename(s,c)}}}static generateProjectId(e){let t=jo.basename(e),n=ede.createHash("sha256").update(e).digest("hex").substring(0,8);return`${t}_${n}`}};var nde=lr(wo(),1);import*as Bm from"node:path";import*as rde from"node:os";import{mkdirSync as Fot}from"fs";var Mot="projects",tde="project.yaml",Hm=class{constructor(e,t,n){this.interactor=e;this.projectService=t;let i=Bm.join(rde.userInfo().homedir,".coday");this.projectsConfigPath=Bm.join(n??i,Mot),Fot(this.projectsConfigPath,{recursive:!0}),this.projects=this.projectService.listProjects().map(a=>a.name)}projectsConfigPath;maskingService=new af;projects;selectedProjectBehaviorSubject=new nde.BehaviorSubject(null);selectedProject$=this.selectedProjectBehaviorSubject.asObservable();selectProject(e){let t=Bm.join(this.projectsConfigPath,e),n=Bm.join(t,tde),i=this.projectService.getProject(e)?.config;if(!i?.path){console.log(`[PROJECT_STATE_SERVICE] ERROR: No project path in config for '${e}'`),this.interactor.error("Invalid selection, project path needed \u{1F622}.");return}let o={name:e,config:i,configPath:t};console.log(`[PROJECT_STATE_SERVICE] Selected '${e}' \u2192 ${i.path}`),this.updateSelectedProject(o),this.interactor.displayText(`Project local configuration used: ${n}`)}updateSelectedProject(e){this.selectedProjectBehaviorSubject.next(e)}get selectedProject(){return this.selectedProjectBehaviorSubject.value}save(e){let t=this.selectedProjectBehaviorSubject.value;if(!t){this.interactor.error("No current project selected, save not possible");return}let n={...t.config,...e};Un(Bm.join(t.configPath,tde),n),t.config=n,this.updateSelectedProject(t)}getConfigForClient(){let e=this.selectedProjectBehaviorSubject.value;return e?this.maskingService.maskConfig(e.config):null}updateConfigFromClient(e){let t=this.selectedProjectBehaviorSubject.value;if(!t){this.interactor.error("No current project selected, update not possible");return}let n=this.maskingService.unmaskConfig(e,t.config);this.save(n)}};import*as al from"node:path";import*as ode from"node:os";import{randomUUID as qot}from"node:crypto";import{existsSync as sf,mkdirSync as Uot,readdirSync as ade,unlinkSync as zot}from"fs";import*as CL from"node:path";import*as ide from"node:os";function Lot(r){return r.replace(/[^a-zA-Z0-9]/g,"_")}function Wm(r,e){try{let t=CL.join(ide.userInfo().homedir,".coday"),n=Lot(r),i=CL.join(e??t,"users",n,"user.yaml"),a=oa(i);return a?a.groups?.includes("CODAY_ADMIN")??!1:!1}catch{return!1}}var Kk=class{codayConfigDir;projectService;nativeHandlerStubs=[];constructor(e,t){let n=al.join(ode.userInfo().homedir,".coday");this.codayConfigDir=e??n,this.projectService=t}getProjectPath(e){if(this.projectService)return this.projectService.getProject(e)?.config.path}async getOrCreatePromptsDir(e,t){let n;if(t==="local")n=al.join(this.codayConfigDir,"projects",e,"prompts");else{let i=this.getProjectPath(e);if(!i)throw new Error("Project path not configured, cannot access project prompts");let a=await C2({text:"coday.yaml",root:i});if(a.length===0)throw new Error(`coday.yaml not found in project path: ${i}`);let o=al.dirname(a[0]);n=al.join(i,o,"prompts")}return sf(n)||(Uot(n,{recursive:!0}),console.log(`[PROMPT] Created prompts directory: ${n}`)),n}async getOrCreatePromptFilePath(e,t,n){let i=await this.getOrCreatePromptsDir(e,n);return al.join(i,`${t}.yml`)}async findPromptSource(e,t){let n=await this.getOrCreatePromptFilePath(e,t,"local");if(sf(n))return"local";if(this.projectService)try{let i=await this.getOrCreatePromptFilePath(e,t,"project");if(sf(i))return"project"}catch{}return null}async findProjectForPrompt(e){let t=al.join(this.codayConfigDir,"projects");if(!sf(t))return null;let n=ade(t,{withFileTypes:!0}).filter(i=>i.isDirectory()).map(i=>i.name);for(let i of n){let a=al.join(this.codayConfigDir,"projects",i,"prompts",`${e}.yml`);if(sf(a))return i;if(this.projectService)try{let o=await this.getOrCreatePromptFilePath(i,e,"project");if(sf(o))return i}catch{}}return null}validatePlaceholders(e){let t=/\{\{(\w+)\}\}/g,n=[],i=/^[A-Za-z][A-Za-z0-9-]*$/;if(e.forEach(a=>{let o;for(;(o=t.exec(a))!==null;){let s=o[1];s&&s!=="PARAMETERS"&&!i.test(s)&&n.push(s)}}),n.length>0)throw new Error(`Invalid parameter keys: ${n.join(", ")}. Keys must start with a letter and contain only letters, digits, and hyphens.`)}async create(e,t,n="local"){try{this.validatePlaceholders(t.commands);let i=qot(),a={...t,id:i,source:n,createdAt:new Date().toISOString(),parameterFormat:this.getParameterFormat(t.commands)},o=await this.getOrCreatePromptFilePath(e,i,n);if(sf(o))throw new Error(`Prompt with ID ${i} already exists`);return Un(o,a),console.log(`[PROMPT] Created prompt ${i} in ${n} for project ${e}`),a}catch(i){throw new Error(`Failed to create prompt: ${i instanceof Error?i.message:"Unknown error"}`)}}async get(e,t){try{let n=await this.findPromptSource(e,t);if(!n)return null;let i=await this.getOrCreatePromptFilePath(e,t,n),a=oa(i);return a?(a.source||(a.source=n),a):null}catch(n){return console.error(`Failed to get prompt ${t}:`,n),null}}async getById(e){try{let t=await this.findProjectForPrompt(e);if(!t)return null;let n=await this.get(t,e);return n?{prompt:n,projectName:t}:null}catch(t){return console.error(`Failed to get prompt ${e}:`,t),null}}async update(e,t,n,i){try{let a=await this.get(e,t);if(!a)return null;if(n.webhookEnabled!==void 0&&n.webhookEnabled!==a.webhookEnabled&&!Wm(i,this.codayConfigDir))throw new Error("Only CODAY_ADMIN can enable/disable webhook for prompts");let{id:o,createdAt:s,source:c,...u}=n;n.commands&&this.validatePlaceholders(n.commands);let l={...a,...u,updatedAt:new Date().toISOString()};n.commands&&(l.parameterFormat=this.getParameterFormat(n.commands));let f=await this.getOrCreatePromptFilePath(e,t,a.source);return Un(f,l),console.log(`[PROMPT] Updated prompt ${t} (${a.source}) by user ${i}`),l}catch(a){throw console.error(`Failed to update prompt ${t}:`,a),a}}async delete(e,t){try{let n=await this.findPromptSource(e,t);if(!n)return!1;let i=await this.getOrCreatePromptFilePath(e,t,n);return zot(i),console.log(`[PROMPT] Deleted prompt ${t} from ${n}`),!0}catch(n){return console.error(`Failed to delete prompt ${t}:`,n),!1}}getParameterFormat(e){if(e.some(a=>/\{\{PARAMETERS\}\}/.test(a)))return"";let n=/\{\{(\w+)\}\}/g,i=new Set;return e.forEach(a=>{let o;for(;(o=n.exec(a))!==null;){let s=o[1];s&&s!=="PARAMETERS"&&i.add(s)}}),i.size===0?"":Array.from(i).map(a=>`${a}=""`).join(" ")}registerNativeHandler(e){this.nativeHandlerStubs.find(t=>t.id===e.id)||this.nativeHandlerStubs.push(e)}async list(e,t=[]){try{let n=[],i=["local"];this.projectService&&i.push("project");for(let a of i)try{let o=await this.getOrCreatePromptsDir(e,a);if(!sf(o))continue;let c=ade(o).filter(u=>u.endsWith(".yml"));for(let u of c){let l=u.replace(".yml",""),f=await this.get(e,l);f&&n.push({id:f.id,name:f.name,description:f.description,webhookEnabled:f.webhookEnabled,createdBy:f.createdBy,createdAt:f.createdAt,updatedAt:f.updatedAt,source:f.source||a,parameterFormat:f.parameterFormat})}}catch(o){console.log(`[PROMPT] Could not access ${a} prompts for ${e}:`,o)}return n.push(...V0),n.push(...this.nativeHandlerStubs),n.push(...t),n.sort((a,o)=>new Date(o.createdAt).getTime()-new Date(a.createdAt).getTime())}catch(n){return console.error(`Failed to list prompts for project ${e}:`,n),[]}}async enableWebhook(e,t,n){return this.update(e,t,{webhookEnabled:!0},n)}async disableWebhook(e,t,n){return this.update(e,t,{webhookEnabled:!1},n)}};var sde=lr(wo(),1),Yk=class{constructor(e){this.promptService=e}threadCodayManager;threadService;codayOptions;logger;initialize(e,t,n,i){this.threadCodayManager=e,this.threadService=t,this.codayOptions=n,this.logger=i}processCommands(e,t){let n;if(typeof t=="string"){let a=e.some(s=>/\{\{PARAMETERS\}\}/.test(s));if(e.some(s=>/\{\{(?!PARAMETERS\}\})\w+\}\}/.test(s)))throw new Error("Prompt contains structured placeholders ({{key}}). Use an object parameter instead of a string.");a?n=e.map(s=>s.replace(/\{\{PARAMETERS\}\}/g,t)):n=e.map((s,c)=>c===0?`${s} ${t}`.trim():s)}else typeof t=="object"&&t!==null?n=e.map(a=>{let o=a;return Object.entries(t).forEach(([s,c])=>{let u=`{{${s}}}`;o=o.replaceAll(u,String(c))}),o}):n=[...e];let i=new Set;if(n.forEach(a=>{let o=a.match(/\{\{(\w+)\}\}/g);o&&o.forEach(s=>i.add(s))}),i.size>0){let a=Array.from(i).map(o=>o.replace(/[{}]/g,"")).join(", ");throw new Error(`Missing required parameters: ${a}`)}return n}async executePrompt(e,t,n,i,a){if(!this.threadCodayManager||!this.threadService||!this.codayOptions||!this.logger)throw new Error("PromptExecutionService not initialized. Call initialize() first.");let{title:o,awaitFinalAnswer:s=!1,projectName:c}=a||{},u,l;if(c){if(l=c,u=await this.promptService.get(l,e),!u)throw new Error(`Prompt ${e} not found in project ${l}`)}else{let y=await this.promptService.getById(e);if(!y)throw new Error(`Prompt not found: ${e}`);u=y.prompt,l=y.projectName}if(i==="webhook"&&!u.webhookEnabled)throw new Error(`Prompt ${e} is not enabled for webhook execution`);if(!u.commands||u.commands.length===0)throw new Error("Prompt has no commands configured");let f=this.processCommands(u.commands,t);if(!n)throw new Error("Username is required");let d=(await this.threadService.createThread(l,n,o)).id;console.log(`[PROMPT_EXEC] Created new thread: ${d} for ${i} execution`);let m={...this.codayOptions,oneshot:!0,project:l,thread:d,prompts:f};console.log(`[PROMPT_EXEC] Creating instance for ${i} execution with ${f.length} prompts:`,f);let h=this.threadCodayManager.createWithoutConnection(d,l,n,m);h.prepareCoday();let g=h.coday.interactor,v={project:l,title:o??"Untitled",username:n,clientId:d,promptCount:f.length,awaitFinalAnswer:!!s,promptName:u.name,promptId:u.id,executionMode:i};if(i==="webhook"?this.logger.logWebhook(v):this.logger.logWebhook({...v,webhookName:u.name,webhookUuid:u.id}),s){let y=[],x=g.events.pipe((0,sde.filter)(_=>(console.log(`[PROMPT_EXEC] Received event type: ${_.type}, role: ${_ instanceof jt?_.role:"N/A"}`),_ instanceof jt&&_.role==="assistant"&&!!_.name))).subscribe(_=>{y.push(_)});try{await h.coday.run(),x.unsubscribe();let _=y[y.length-1];return await this.threadCodayManager.cleanup(d),{threadId:d,lastEvent:_}}catch(_){x.unsubscribe();let S=_ instanceof Error?_.message:"Unknown error";throw this.logger.logWebhookError({error:`Prompt execution failed: ${S}`,username:n,project:l,clientId:d}),console.error("[PROMPT_EXEC] Error waiting for prompt completion:",_),await this.threadCodayManager.cleanup(d),_}}else return h.coday.run().catch(y=>{console.error("[PROMPT_EXEC] Error during prompt Coday run:",y)}),setTimeout(()=>{this.threadCodayManager.cleanup(d).catch(y=>{console.error("[PROMPT_EXEC] Error cleaning up prompt thread after timeout:",y)})},300*1e3),{threadId:d}}};var Tg=lr(zc(),1);import*as ui from"fs";import*as Vm from"path";import{randomUUID as Bot}from"node:crypto";var Xk=class{constructor(e,t,n){this.logger=e;this.promptService=t;this.codayConfigDir=n}schedulers=new Map;checkInterval;CHECK_INTERVAL_MS=3e4;promptExecutionService;initializeExecution(e){this.promptExecutionService=e}async initialize(){console.log("[SCHEDULER] Initializing SchedulerService..."),await this.loadAllSchedulers(),this.startScheduler(),console.log(`[SCHEDULER] SchedulerService initialized with ${this.schedulers.size} schedulers`)}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=void 0,console.log("[SCHEDULER] SchedulerService stopped"))}async loadAllSchedulers(){this.schedulers.clear();let e=Vm.join(this.codayConfigDir,"projects");if(!ui.existsSync(e))return;let t=ui.readdirSync(e,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>n.name);for(let n of t)try{let i=await this.loadProjectSchedulers(n);for(let a of i)this.schedulers.set(a.id,a)}catch(i){console.error(`[SCHEDULER] Failed to load schedulers for project ${n}:`,i)}}async loadProjectSchedulers(e){let t=this.getSchedulersDir(e);if(!ui.existsSync(t))return[];let n=ui.readdirSync(t),i=[];for(let a of n)if(a.endsWith(".yml"))try{let o=Vm.join(t,a),s=ui.readFileSync(o,"utf-8"),c=Tg.parse(s),u=this.calculateNextRunSkippingMissed(c),l=u.occurrenceCount!==c.occurrenceCount;c.nextRun=u.nextRun,c.occurrenceCount=u.occurrenceCount,l&&await this.saveScheduler(c,e),i.push(c)}catch(o){console.error(`[SCHEDULER] Failed to load scheduler from ${a}:`,o)}return i}startScheduler(){this.checkInterval=setInterval(()=>{this.checkAndExecuteSchedulers()},this.CHECK_INTERVAL_MS),this.checkAndExecuteSchedulers()}async checkAndExecuteSchedulers(){for(let e of this.schedulers.values())e.enabled&&Bue(e.schedule,e.nextRun??null,e.occurrenceCount??0)&&this.executeScheduler(e).catch(t=>{console.error(`[SCHEDULER] Failed to execute scheduler ${e.id}:`,t)})}async executeSchedulerInternal(e,t,n){if(!this.promptExecutionService)throw new Error("PromptExecutionService not initialized");console.log(`[SCHEDULER] Executing scheduler "${e.name}" (${e.id}) [${n}]`);let i=e.parameters;e.parameters&&typeof e.parameters=="object"&&Object.keys(e.parameters).length===1&&"PARAMETERS"in e.parameters&&(i=String(e.parameters.PARAMETERS));let a=await this.promptExecutionService.executePrompt(e.promptId,i,e.createdBy,"scheduled",{title:n==="scheduled"?`Scheduled: ${e.name}`:`Manual: ${e.name}`,awaitFinalAnswer:!1,projectName:t});return console.log(`[SCHEDULER] Scheduler "${e.name}" executed successfully. Thread: ${a.threadId}`),a.threadId}async executeScheduler(e){let t=new Date().toISOString();console.log(`[SCHEDULER] Executing scheduler "${e.name}" (${e.id})`);let n=this.findProjectForScheduler(e.id);if(!n){console.error(`[SCHEDULER] Cannot find project for scheduler ${e.id}, skipping execution`);return}let i,a,o;e.lastRun=t,e.occurrenceCount=(e.occurrenceCount??0)+1,e.nextRun=Oh(e.schedule,new Date,e.occurrenceCount);try{await this.saveScheduler(e,n),console.log(`[SCHEDULER] Next execution for "${e.name}": ${e.nextRun}`),this.schedulers.set(e.id,e),a=await this.executeSchedulerInternal(e,n,"scheduled"),i=!0}catch(s){i=!1,o=s instanceof Error?s.message:String(s),console.error(`[SCHEDULER] Scheduler "${e.name}" failed:`,s)}this.logger.logTriggerExecution({triggerId:e.id,triggerName:e.name,webhookUuid:e.promptId,projectName:n,success:i,threadId:a,error:o})}validateSchedule(e){return zue(e)}calculateNextRunSkippingMissed(e){let t=new Date,n=e.occurrenceCount??0,i=e.nextRun;if(!i)return{nextRun:Oh(e.schedule,t,n),occurrenceCount:n};if(new Date(i)>=t)return{nextRun:i,occurrenceCount:n};let a=1e3,o=0,s=0;for(;o<a;){if(n++,s++,i=Oh(e.schedule,t,n),!i)return s>0&&console.log(`[SCHEDULER] Scheduler "${e.name}" (${e.id}) expired after skipping ${s} missed occurrence(s)`),{nextRun:null,occurrenceCount:n};if(new Date(i)>=t)return s>0&&console.log(`[SCHEDULER] Scheduler "${e.name}" (${e.id}) skipped ${s} missed occurrence(s), next run: ${i}`),{nextRun:i,occurrenceCount:n};o++}return console.warn(`[SCHEDULER] Could not find future nextRun for scheduler ${e.id} after ${a} iterations`),{nextRun:null,occurrenceCount:n}}getSchedulersDir(e){return Vm.join(this.codayConfigDir,"projects",e,"schedulers")}getSchedulerFilePath(e,t){return Vm.join(this.getSchedulersDir(e),`${t}.yml`)}findProjectForScheduler(e){let t=Vm.join(this.codayConfigDir,"projects");if(!ui.existsSync(t))return null;let n=ui.readdirSync(t,{withFileTypes:!0}).filter(i=>i.isDirectory()).map(i=>i.name);for(let i of n){let a=this.getSchedulerFilePath(i,e);if(ui.existsSync(a))return i}return null}canAccessScheduler(e,t){return e.createdBy===t?!0:Wm(t,this.codayConfigDir)}async listSchedulers(e,t){return(await this.loadProjectSchedulers(e)).filter(i=>this.canAccessScheduler(i,t)).map(i=>({id:i.id,name:i.name,enabled:i.enabled,promptId:i.promptId,schedule:i.schedule,parameters:i.parameters,lastRun:i.lastRun,nextRun:i.nextRun,createdBy:i.createdBy}))}async getScheduler(e,t,n){let i=this.getSchedulerFilePath(e,t);if(!ui.existsSync(i))return null;try{let a=ui.readFileSync(i,"utf-8"),o=Tg.parse(a),s=this.calculateNextRunSkippingMissed(o);return o.nextRun=s.nextRun,o.occurrenceCount=s.occurrenceCount,n&&!this.canAccessScheduler(o,n)?(console.log(`[SCHEDULER] Access denied for user ${n} to scheduler ${t}`),null):o}catch(a){return console.error(`[SCHEDULER] Failed to load scheduler ${t}:`,a),null}}async createScheduler(e,t,n){let i=this.validateSchedule(t.schedule);if(!i.valid)throw new Error(`Invalid schedule: ${i.error}`);if(!await this.promptService.getById(t.promptId))throw new Error(`Prompt not found: ${t.promptId}`);let o={id:Bot(),name:t.name,enabled:t.enabled??!0,promptId:t.promptId,schedule:t.schedule,parameters:t.parameters,createdBy:n,createdAt:new Date().toISOString(),nextRun:Oh(t.schedule,new Date,0),occurrenceCount:0};return await this.saveScheduler(o,e),this.schedulers.set(o.id,o),console.log(`[SCHEDULER] Created scheduler "${o.name}" (${o.id}) for project ${e}`),o}async updateScheduler(e,t,n,i){let a=await this.getScheduler(e,t,i);if(!a)throw new Error(`Scheduler not found or access denied: ${t}`);if(console.log(`[SCHEDULER] Updating scheduler ${t} by user ${i}`),n.name!==void 0&&(a.name=n.name),n.enabled!==void 0&&(a.enabled=n.enabled),n.parameters!==void 0&&(a.parameters=n.parameters),n.promptId!==void 0){if(!await this.promptService.getById(n.promptId))throw new Error(`Prompt not found: ${n.promptId}`);a.promptId=n.promptId}if(n.schedule!==void 0){let o=this.validateSchedule(n.schedule);if(!o.valid)throw new Error(`Invalid schedule: ${o.error}`);a.schedule=n.schedule,a.occurrenceCount=0,a.nextRun=Oh(n.schedule,new Date,0)}return await this.saveScheduler(a,e),this.schedulers.set(a.id,a),console.log(`[SCHEDULER] Updated scheduler "${a.name}" (${a.id})`),a}async deleteScheduler(e,t,n){if(!await this.getScheduler(e,t,n))return!1;let a=this.getSchedulerFilePath(e,t);return ui.unlinkSync(a),this.schedulers.delete(t),console.log(`[SCHEDULER] Deleted scheduler ${t} by user ${n}`),!0}async enableScheduler(e,t,n){return this.updateScheduler(e,t,{enabled:!0},n)}async disableScheduler(e,t,n){return this.updateScheduler(e,t,{enabled:!1},n)}async runSchedulerNow(e,t,n){let i=await this.getScheduler(e,t,n);if(!i)throw new Error(`Scheduler not found or access denied: ${t}`);let a=await this.executeSchedulerInternal(i,e,"manual");return i.lastRun=new Date().toISOString(),await this.saveScheduler(i,e),this.schedulers.set(i.id,i),this.logger.logTriggerExecution({triggerId:i.id,triggerName:i.name,webhookUuid:i.promptId,projectName:e,success:!0,threadId:a}),a}async saveScheduler(e,t){if(!t&&(t=this.findProjectForScheduler(e.id),!t))throw new Error(`Cannot find project for scheduler ${e.id}`);let n=this.getSchedulersDir(t);ui.mkdirSync(n,{recursive:!0});let i=this.getSchedulerFilePath(t,e.id),a=Tg.stringify(e);ui.writeFileSync(i,a,"utf-8")}};import*as fs from"node:path";import*as ude from"node:os";import{existsSync as cde,lstatSync as Hot,mkdirSync as AL,readdirSync as Wot,renameSync as Vot}from"fs";var Qk=class r{projectsConfigPath;static PROJECT_FILENAME="project.yaml";constructor(e){let t=fs.join(ude.userInfo().homedir,".coday");this.projectsConfigPath=fs.join(e??t,"projects"),AL(this.projectsConfigPath,{recursive:!0})}listProjects(){return Wot(this.projectsConfigPath).filter(t=>!t.startsWith(".")&&Hot(fs.join(this.projectsConfigPath,t)).isDirectory())}getProjectInfo(e){let t=fs.join(this.projectsConfigPath,e),n=fs.join(t,r.PROJECT_FILENAME);return cde(n)?{name:e,configPath:t}:null}exists(e){return this.getProjectInfo(e)!==null}getConfig(e){let t=this.getProjectInfo(e);if(!t)return console.log(`[PROJECT_REPO] No project info found for: '${e}'`),null;let n=fs.join(t.configPath,r.PROJECT_FILENAME),i=oa(n);if(!i)return console.log(`[PROJECT_REPO] Failed to load config for '${e}' from ${n}`),null;let a=Dh(i,Hue);return a!==i&&(console.log(`[PROJECT_REPO] Config migrated for '${e}', saving...`),Un(n,a)),a}saveConfig(e,t){let n=this.getProjectInfo(e);if(!n)throw new Error(`Project '${e}' does not exist`);let i=fs.join(n.configPath,r.PROJECT_FILENAME);Un(i,t)}createProject(e,t){let n=fs.join(this.projectsConfigPath,e),i=fs.join(n,r.PROJECT_FILENAME);return cde(i)?!1:(console.log(`[PROJECT_REPO] Creating project '${e}' with path: ${t}`),AL(n,{recursive:!0}),Un(i,{version:1,path:t,integration:{},storage:{type:"file"},agents:[]}),console.log(`[PROJECT_REPO] Project '${e}' created successfully`),!0)}deleteProject(e){let t=this.getProjectInfo(e);if(!t)return!1;let n=fs.join(this.projectsConfigPath,".deleted");AL(n,{recursive:!0});let i=new Date().toISOString().replace(/:/g,"-"),a=fs.join(n,`${e}__${i}`);return Vot(t.configPath,a),!0}};var TL=lr(zc(),1);import{promises as ol}from"fs";import ld from"path";var lde=async r=>{try{let e=await ol.readFile(r,"utf-8");return TL.default.parse(e)}catch{return null}},eE=class{constructor(e){this.projectsDir=e}getThreadsDir(e){return ld.join(this.projectsDir,e,"threads")}async ensureThreadsDir(e){let t=this.getThreadsDir(e);try{await ol.mkdir(t,{recursive:!0})}catch(n){throw new Nu(`Failed to initialize threads directory for project ${e}`,n)}}getThreadFileName(e){return`${e.id}.yml`}async findThreadFile(e,t){try{let n=this.getThreadsDir(e),i=`${t}.yml`;try{return await ol.access(ld.join(n,i)),i}catch{}return(await ol.readdir(n)).find(s=>s.endsWith(`-${t}.yml`))||null}catch(n){throw new Nu(`Error finding thread ${t} in project ${e}`,n)}}async getById(e,t){try{let n=await this.findThreadFile(e,t);if(!n)return null;let i=this.getThreadsDir(e),a=ld.join(i,n),o=await lde(a);if(!o)return null;let s=Dh(o,rF);return s.projectId||(s.projectId=e),s!==o&&Un(a,s),new Fu(s)}catch(n){throw new Nu(`Failed to read thread ${t} from project ${e}`,n)}}async save(e,t){await this.ensureThreadsDir(e);try{if(t.id||(t.id=crypto.randomUUID()),t.projectId||(t.projectId=e),t.projectId!==e)throw new Error(`Thread projectId mismatch: expected ${e}, got ${t.projectId}`);let n=this.getThreadsDir(e),i=this.getThreadFileName(t),a=ld.join(n,i),o=await this.findThreadFile(e,t.id);if(o&&o!==i){let u=ld.join(n,o);try{await ol.unlink(u),console.log(`[THREAD-REPO] Migrated/renamed thread file: ${o} \u2192 ${i}`)}catch(l){console.warn(`[THREAD-REPO] Could not delete old thread file: ${o}`,l)}}let s={...t.serialize(),version:rF.length+1},c=TL.default.stringify(s);return await ol.writeFile(a,c,"utf-8"),t}catch(n){throw new Nu(`Failed to save thread ${t.id} to project ${e}`,n)}}async listByProject(e){try{let t=this.getThreadsDir(e);try{await ol.access(t)}catch{return[]}let n=await ol.readdir(t);return(await Promise.all(n.filter(a=>a.endsWith(".yml")).map(async a=>{let o=await lde(ld.join(t,a));if(!o)return null;let s=o.projectId||e;return{id:o.id,username:o.username,projectId:s,name:o.name??"...",summary:o.summary??"",createdDate:o.createdDate??"",modifiedDate:o.modifiedDate??"",price:o.price??0,starring:o.starring??[],users:Array.isArray(o.users)?o.users.map(c=>typeof c=="string"?{userId:c}:c):o.username?[{userId:o.username}]:[],parentThreadId:o.parentThreadId,parentEventId:o.parentEventId,delegatedAgentName:o.delegatedAgentName,delegatedTask:o.delegatedTask}}))).filter(a=>!!a).sort((a,o)=>a.modifiedDate>o.modifiedDate?-1:1)}catch(t){throw new Nu(`Failed to list threads for project ${e}`,t)}}async delete(e,t){try{let n=await this.findThreadFile(e,t);if(!n)return!1;let i=this.getThreadsDir(e);return await ol.unlink(ld.join(i,n)),!0}catch(n){throw new Nu(`Failed to delete thread ${t} from project ${e}`,n)}}};var tE=class{constructor(e,t,n){this.projectRepository=e;this.projectsDir=t;this.threadFileService=n}repositoryCache=new Map;threadListCache=new Map;CACHE_TTL_MS=14400*1e3;loadingPromises=new Map;getThreadRepository(e){let t=this.repositoryCache.get(e);if(t)return t;if(!this.projectRepository.getProjectInfo(e))throw new Error(`Project '${e}' not found`);let i=new eE(this.projectsDir);return this.repositoryCache.set(e,i),i}clearCache(e){e?this.repositoryCache.delete(e):this.repositoryCache.clear()}async listThreads(e,t){let n=this.threadListCache.get(e);if(n&&Date.now()-n.timestamp<this.CACHE_TTL_MS)return n.data.filter(o=>on(o,t)).sort((o,s)=>o.modifiedDate>s.modifiedDate?-1:1);let i=this.loadingPromises.get(e);if(i)return await i,this.listThreads(e,t);let a=this.loadThreadListFromDisk(e);this.loadingPromises.set(e,a);try{return(await a).filter(s=>on(s,t)).sort((s,c)=>s.modifiedDate>c.modifiedDate?-1:1)}catch(o){throw this.threadListCache.delete(e),o}finally{this.loadingPromises.delete(e)}}async loadThreadListFromDisk(e){let n=await this.getThreadRepository(e).listByProject(e);return this.threadListCache.set(e,{data:n,timestamp:Date.now()}),n}toThreadSummary(e){return{id:e.id,username:e.username,projectId:e.projectId,name:e.name,summary:e.summary,createdDate:e.createdDate,modifiedDate:e.modifiedDate,price:e.price,starring:e.starring,users:e.users,parentThreadId:e.parentThreadId,parentEventId:e.parentEventId,delegatedAgentName:e.delegatedAgentName,delegatedTask:e.delegatedTask}}updateThreadInCache(e,t){let n=this.threadListCache.get(e);if(!n)return;let i=n.data.findIndex(a=>a.id===t.id);i!==-1?n.data[i]=t:n.data.push(t)}removeThreadFromCache(e,t){let n=this.threadListCache.get(e);n&&(n.data=n.data.filter(i=>i.id!==t))}async getThread(e,t){return await this.getThreadRepository(e).getById(e,t)}async createThread(e,t,n){let i=this.getThreadRepository(e),a=new Fu({id:crypto.randomUUID(),username:t,projectId:e,name:n||"",price:0}),o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async updateThread(e,t,n){let i=this.getThreadRepository(e),a=await i.getById(e,t);if(!a){let c=this.threadListCache.get(e)?.data.find(u=>u.id===t);if(c)a=new Fu({id:c.id,username:c.username,projectId:c.projectId,name:c.name,summary:c.summary,price:c.price,starring:c.starring,users:c.users,parentThreadId:c.parentThreadId,parentEventId:c.parentEventId,delegatedAgentName:c.delegatedAgentName,delegatedTask:c.delegatedTask});else throw new Error(`Thread '${t}' not found in project '${e}'`)}n.name!==void 0&&(a.name=n.name),n.summary!==void 0&&(a.summary=n.summary),n.users!==void 0&&(a.users=n.users);let o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async starThread(e,t,n){let i=this.getThreadRepository(e),a=await i.getById(e,t);if(!a)throw new Error(`Thread '${t}' not found in project '${e}'`);a.starring.includes(n)||a.starring.push(n);let o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async unstarThread(e,t,n){let i=this.getThreadRepository(e),a=await i.getById(e,t);if(!a)throw new Error(`Thread '${t}' not found in project '${e}'`);a.starring=a.starring.filter(s=>s!==n);let o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async deleteThread(e,t){let i=await this.getThreadRepository(e).delete(e,t);return i&&(await this.threadFileService.deleteThreadFiles(e,t),this.removeThreadFromCache(e,t)),i}async saveThread(e,t){let i=await this.getThreadRepository(e).save(e,t);return this.updateThreadInCache(e,this.toThreadSummary(i)),i}async listAllThreads(e){let t=this.threadListCache.get(e);if(t&&Date.now()-t.timestamp<this.CACHE_TTL_MS)return t.data.sort((a,o)=>a.modifiedDate>o.modifiedDate?-1:1);let n=this.loadingPromises.get(e);if(n)return await n,this.listAllThreads(e);let i=this.loadThreadListFromDisk(e);this.loadingPromises.set(e,i);try{return(await i).sort((o,s)=>o.modifiedDate>s.modifiedDate?-1:1)}catch(a){throw this.threadListCache.delete(e),a}finally{this.loadingPromises.delete(e)}}async exists(e,t){return await this.getThread(e,t)!==null}};var mde=lr(zc(),1);import*as Ca from"fs/promises";import*as Pg from"path";var fde=5,pde=24,dde=100,hde={SHORT_THREADS:7,LONG_THREADS:30},rE=class{constructor(e,t){this.projectsConfigPath=e;this.logger=t}cleanupTimer=null;initialTimer=null;isRunning=!1;async start(){if(this.isRunning){this.log("Thread cleanup service already running");return}this.isRunning=!0,this.log("Starting thread cleanup service with user message-based retention"),this.initialTimer=setTimeout(async()=>{await this.performCleanup(),this.cleanupTimer=setInterval(async()=>{await this.performCleanup()},pde*60*60*1e3),this.log(`Thread cleanup scheduled every ${pde} hours`)},fde*60*1e3),this.log(`Initial cleanup scheduled in ${fde} minutes`)}async stop(){this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.cleanupTimer&&(clearInterval(this.cleanupTimer),this.cleanupTimer=null),this.isRunning=!1,this.log("Thread cleanup service stopped (all timers cleared)")}async performCleanup(){let e=Date.now(),t=0,n=0,i=0;try{this.log("Starting thread cleanup...");let a=await Ca.readdir(this.projectsConfigPath);this.log(`Found ${a.length} projects to scan`);for(let s of a)try{let c=Pg.join(this.projectsConfigPath,s);if(!(await Ca.lstat(c)).isDirectory())continue;let l=Pg.join(c,"threads");try{await Ca.access(l)}catch{continue}let{scanned:f,deleted:p,errors:d}=await this.cleanupProjectThreads(s,l);t+=f,n+=p,i+=d}catch(c){this.logError(`Error processing project ${s}: ${c}`),i++}let o=Date.now()-e;this.log(`Cleanup completed: ${n}/${t} threads deleted across all projects in ${o}ms (${i} errors)`)}catch(a){let o=Date.now()-e;this.logError(`Cleanup failed after ${o}ms: ${a}`)}}async cleanupProjectThreads(e,t){let n=0,i=0,a=0;try{let s=(await Ca.readdir(t)).filter(c=>c.endsWith(".yml"));if(n=s.length,s.length===0)return{scanned:n,deleted:i,errors:a};this.log(`Scanning ${s.length} threads in project ${e}`);for(let c=0;c<s.length;c+=dde){let u=s.slice(c,c+dde),l=await this.processBatch(u,t,e);i+=l.deleted,a+=l.errors}i>0&&this.log(`Project ${e}: deleted ${i}/${n} expired threads`)}catch(o){this.logError(`Error scanning project ${e}: ${o}`),a++}return{scanned:n,deleted:i,errors:a}}async processBatch(e,t,n){let i=0,a=0;return await Promise.all(e.map(async o=>{let s=Pg.join(t,o),c;try{let u=await Ca.readFile(s,"utf-8");c=mde.parse(u)}catch(u){await Ca.unlink(s),this.logError(`Error processing file ${o} in project ${n}: ${u}`)}if(!(!c||!c.modifiedDate)&&!(c.starring&&Array.isArray(c.starring)&&c.starring.length>0)&&this.shouldDeleteThread(c)){let u=this.countUserMessages(c.messages||[]),l=this.getDaysSinceModified(c.modifiedDate),f=c.id||o.replace(".yml","");await Ca.unlink(s),await this.deleteThreadFiles(t,f),i++,this.logger.logThreadCleanup(n,o),console.log(`ThreadCleanup: Deleted thread ${f} and its files directory (${u} user messages, ${l} days old)`)}})),{deleted:i,errors:a}}shouldDeleteThread(e){if(!e||!e.modifiedDate)return!1;let n=this.countUserMessages(e.messages||[])<=3?hde.SHORT_THREADS:hde.LONG_THREADS;return this.getDaysSinceModified(e.modifiedDate)>n}countUserMessages(e){return e.filter(t=>t&&t.type==="message"&&t.role==="user").length}getDaysSinceModified(e){let t=new Date(e),i=new Date().getTime()-t.getTime();return Math.floor(i/(1e3*60*60*24))}async deleteThreadFiles(e,t){try{let n=Pg.join(e,`${t}-files`);try{await Ca.access(n),await Ca.rm(n,{recursive:!0,force:!0}),console.log(`ThreadCleanup: Deleted files directory ${n}`)}catch(i){if(i.code!=="ENOENT")throw i}}catch(n){console.error(`ThreadCleanup: Error deleting files for ${t}:`,n)}}log(e){let t=new Date().toISOString();console.log(`[${t}] ThreadCleanup: ${e}`)}logError(e){let t=new Date().toISOString();console.error(`[${t}] ThreadCleanup ERROR: ${e}`)}async forceCleanup(){this.log("Manual cleanup triggered"),await this.performCleanup()}};import*as Hi from"path";import*as fd from"fs";import*as Aa from"fs/promises";var nE=class{constructor(e){this.projectsDir=e}getThreadFilesDir(e,t){return Hi.join(this.projectsDir,e,"threads",`${t}-files`)}async ensureThreadFilesDir(e,t){let n=this.getThreadFilesDir(e,t);fd.existsSync(n)||await Aa.mkdir(n,{recursive:!0})}async listFiles(e,t){await this.ensureThreadFilesDir(e,t);let n=this.getThreadFilesDir(e,t),i=await Aa.readdir(n);return await Promise.all(i.map(async o=>{let s=Hi.join(n,o),c=await Aa.stat(s);return{filename:o,size:c.size,lastModified:c.mtime.toISOString()}}))}async saveFile(e,t,n,i){let a=this.getThreadFilesDir(e,t);await Aa.mkdir(a,{recursive:!0});let o=Hi.join(a,n);await Aa.writeFile(o,i)}async getFile(e,t,n){await this.ensureThreadFilesDir(e,t);let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);if(!o.startsWith(s))throw new Error("Access denied: invalid file path");if(!fd.existsSync(a))throw new Error(`File '${n}' not found`);return await Aa.readFile(a)}async getFilePath(e,t,n){await this.ensureThreadFilesDir(e,t);let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);if(!o.startsWith(s))throw new Error("Access denied: invalid file path");if(!fd.existsSync(a))throw new Error(`File '${n}' not found`);return a}fileExists(e,t,n){let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);return o.startsWith(s)?fd.existsSync(a):!1}async deleteFile(e,t,n){await this.ensureThreadFilesDir(e,t);let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);if(!o.startsWith(s))throw new Error("Access denied: invalid file path");if(!fd.existsSync(a))throw new Error(`File '${n}' not found`);await Aa.unlink(a)}async deleteThreadFiles(e,t){try{let n=this.getThreadFilesDir(e,t);fd.existsSync(n)&&(await Aa.rm(n,{recursive:!0,force:!0}),console.log(`Deleted thread files directory: ${n}`))}catch(n){console.error(`Error deleting thread files for ${t}:`,n)}}};var gde=lr(wo(),1);var iE=class{constructor(e,t,n,i){this.threadRepository=t;this.projectId=n;this.interactor=i;this.username=e.username}activeThread$=new gde.BehaviorSubject(null);isKilled=!1;activeThread=this.activeThread$.asObservable();username;async kill(){this.isKilled=!0,this.activeThread$.complete()}async create(e){let t=new Fu({id:"",username:this.username,name:e??"",price:0});return this.activeThread$.next(t),t}async select(e){let t=await this.threadRepository.getById(this.projectId,e);if(!t)throw new Error(`Thread ${e} not found`);return this.activeThread$.next(t),t}async save(e){let t=this.activeThread$.value;if(!t){console.error(`No thread existing when save attempt with name '${e}'`);return}e&&(t.name=e);let n=await this.threadRepository.save(this.projectId,t);this.activeThread$.next(n)}async autoSave(e){if(this.isKilled){console.log("Autosave skipped: service has been killed");return}let t=this.activeThread$.value;if(!t||t.messagesLength==0){console.log(`Autosave of an empty or falsy thread aborted, threadId: ${t?.id}, user: ${this.username}`);return}try{e&&(t.name=e),await this.threadRepository.save(this.projectId,t),this.interactor&&this.interactor.sendEvent(new Tc({threadId:t.id,name:t.name||void 0}))}catch(n){console.log("Autosave failed (service may have been killed):",n instanceof Error?n.message:n)}}async truncateAtUserMessage(e){let t=this.activeThread$.value;if(!t)return console.error("No active thread available for truncation"),!1;let n=t.truncateAtUserMessage(e);return n||this.interactor?.warn("Failed to truncate thread."),n}getCurrentThread(){return this.activeThread$.value}};async function vde(r,e){let t=e.project.selectedProject,n=e.user.getUserData(t.name),i=await Gk(t.config.path,r,n),a=new Z0({...i,root:t.config.path,name:t.name},e.user.username);return console.log(`[BUILD_CONTEXT] Context for '${a.project.name}' \u2192 root: ${a.project.root}, desc: ${a.project.description?.length||0} chars`),a}var aE=class extends Ne{constructor(t,n){super({commandWord:"select-project",description:"Select an existing project"});this.interactor=t;this.services=n}async handle(t,n){try{let i=this.getSubCommand(t);return await this.selectProject(i)??n}catch(i){return this.interactor.error(`Invalid project selection because: ${i.toString()}`),n}}async selectProject(t){return this.services.project.selectProject(t),this.services.project.selectedProject?vde(this.interactor,this.services):null}};var oE=class extends Ne{constructor(t,n,i){super({commandWord:"add",description:"Add a new integration configuration. User level is default, use --project/-p for project level."});this.interactor=t;this.service=n;this.editHandler=i}async handle(t,n){let a=!!Pt(this.getSubCommand(t),[{key:"project",alias:"p"}]).project,o=a?"project":"user",s=this.service.getMergedIntegrations(),c=Object.keys(s),u=hp.filter(h=>!c.includes(h));if(u.length===0)return this.interactor.displayText(`All available integrations (${hp.join(", ")}) are already configured.`),n;let l=await this.interactor.chooseOption(u.sort(),"Select integration to add:",`Available integrations to configure:
880
+ `)):""}loadMemoriesFrom(e){this.memories=[],this.userMemoriesPath=this.readMemories(this.userMemoriesPath)?this.userMemoriesPath:void 0;let t=e?.configPath?Jk.join(e.configPath,EL):void 0;this.projectMemoriesPath=this.readMemories(t)?t:void 0,e&&this.checkInit()}readMemories(e){if(!e)return!1;try{if(!jot(e)){let n=Ype.stringify({memories:[]});Not(e,n)}let t=oa(e);return t?(this.memories.push(...t.memories),!0):!1}catch{return!1}}checkInit(){if(!this.userMemoriesPath||!this.projectMemoriesPath)throw new Error("user or project path not set for memory service")}saveMemories(){this.checkInit();let e=this.memories.filter(n=>n.level==="USER"),t=this.memories.filter(n=>n.level==="PROJECT");Un(this.userMemoriesPath,{memories:e}),Un(this.projectMemoriesPath,{memories:t})}};import*as ede from"crypto";import*as No from"node:fs/promises";import*as jo from"path";import{exec as Qpe}from"child_process";var zm=class r{constructor(e,t,n){this.repository=e;this.defaultProject=t;this.isForcedMode=n}maskingService=new af;listProjects(){let e=this.repository.listProjects();return this.isForcedMode&&this.defaultProject?e.filter(t=>t===this.defaultProject).map(t=>{let n=this.repository.getConfig(t);return{name:t,volatile:n?.volatile}}):e.map(t=>{let n=this.repository.getConfig(t);return{name:t,volatile:n?.volatile}})}getDefaultProject(){return this.defaultProject||null}getForcedMode(){return this.isForcedMode}getProject(e){this.checkAgainstForced(e);let t=this.repository.getConfig(e);if(t)return{name:e,config:t};if(!this.isForcedMode&&this.defaultProject){let n=process.cwd(),i=r.generateProjectId(n),a=jo.basename(n);if(e===a||e===i||e===this.defaultProject){console.log(`[PROJECT_SERVICE] Creating volatile project for '${e}' at ${n}`);let o=this.getOrCreateVolatileProject(n);if(t=this.repository.getConfig(o),t)return{name:o,config:t}}}return null}exists(e){return this.checkAgainstForced(e),this.repository.exists(e)}createProject(e,t){if(this.checkAgainstForced(e),!e||!t)throw new Error("Project name and path are required");if(!this.repository.createProject(e,t))throw new Error(`Project '${e}' already exists`)}updateProjectConfig(e,t){if(this.checkAgainstForced(e),!this.repository.exists(e))throw new Error(`Project '${e}' does not exist`);this.repository.saveConfig(e,t)}async deleteProject(e,t){if(this.checkAgainstForced(e),e.includes("__"))t?.removeGitWorktree&&await this.removeGitWorktree(e),await this.unregisterWorktreeProject(e);else if(!this.repository.deleteProject(e))throw new Error(`Project '${e}' does not exist`)}async removeGitWorktree(e){let t=e.lastIndexOf("__");if(t===-1)return;let n=e.substring(0,t),i=this.repository.getConfig(n);if(!i?.path){console.warn(`[PROJECT_SERVICE] Cannot remove git worktree: parent project '${n}' config not found`);return}let a=i.path,o=jo.dirname(a),s=jo.join(o,e);try{await No.access(s)}catch{console.warn(`[PROJECT_SERVICE] Worktree path does not exist, skipping git removal: ${s}`);return}Qpe(`git worktree remove "${s}" --force`,{cwd:a},c=>{if(c){console.error(`[PROJECT_SERVICE] Failed to remove git worktree: ${c.message}`);return}console.log(`[PROJECT_SERVICE] Removed git worktree: ${s}`),Qpe("git worktree prune",{cwd:a},u=>{u&&console.warn(`[PROJECT_SERVICE] git worktree prune failed: ${u.message}`)})})}getProjectConfigForClient(e){this.checkAgainstForced(e);let t=this.repository.getConfig(e);return t?this.maskingService.maskConfig(t):null}updateProjectConfigFromClient(e,t){this.checkAgainstForced(e);let n=this.repository.getConfig(e);if(!n)throw new Error(`Project '${e}' does not exist`);let i=this.maskingService.unmaskConfig(t,n);this.repository.saveConfig(e,i)}checkAgainstForced(e){if(this.isForcedMode&&this.defaultProject&&this.defaultProject!==e)throw Error(`Project selection outside of ${this.defaultProject} not allowed`)}getOrCreateVolatileProject(e){let t=r.generateProjectId(e);if(this.repository.exists(t))return t;this.repository.createProject(t,e);let n=this.repository.getConfig(t);return n&&(n.volatile=!0,n.createdAt=Date.now(),this.repository.saveConfig(t,n)),t}async registerWorktreeProject(e,t,n){let i=this.repository.getConfig(n),a=i?{...i,path:t,volatile:void 0,createdAt:void 0}:{version:1,path:t,integration:{},storage:{type:"file"},agents:[]};this.repository.createProject(e,t),this.repository.saveConfig(e,a);let o=this.repository.getProjectInfo(n);if(o){let s=this.repository.getProjectInfo(e);if(s)for(let c of["agents","prompts","schedulers","memories"]){let u=jo.join(o.configPath,c),l=jo.join(s.configPath,c);try{await No.access(u),await No.symlink(u,l)}catch{}}}}async unregisterWorktreeProject(e){let t=this.repository.getProjectInfo(e);if(!t)return;let n=e.lastIndexOf("__");if(n!==-1){let i=e.substring(0,n),a=this.repository.getProjectInfo(i);a&&await this.migrateThreadsToParent(t.configPath,a.configPath)}this.repository.deleteProject(e)}async migrateThreadsToParent(e,t){let n=jo.join(e,"threads"),i=jo.join(t,"threads");try{await No.access(n)}catch{return}await No.mkdir(i,{recursive:!0});let a=await No.readdir(n);for(let o of a){let s=jo.join(n,o),c=jo.join(i,o);try{await No.access(c)}catch{await No.rename(s,c)}}}static generateProjectId(e){let t=jo.basename(e),n=ede.createHash("sha256").update(e).digest("hex").substring(0,8);return`${t}_${n}`}};var nde=lr(wo(),1);import*as Bm from"node:path";import*as rde from"node:os";import{mkdirSync as Fot}from"fs";var Mot="projects",tde="project.yaml",Hm=class{constructor(e,t,n){this.interactor=e;this.projectService=t;let i=Bm.join(rde.userInfo().homedir,".coday");this.projectsConfigPath=Bm.join(n??i,Mot),Fot(this.projectsConfigPath,{recursive:!0}),this.projects=this.projectService.listProjects().map(a=>a.name)}projectsConfigPath;maskingService=new af;projects;selectedProjectBehaviorSubject=new nde.BehaviorSubject(null);selectedProject$=this.selectedProjectBehaviorSubject.asObservable();selectProject(e){let t=Bm.join(this.projectsConfigPath,e),n=Bm.join(t,tde),i=this.projectService.getProject(e)?.config;if(!i?.path){console.log(`[PROJECT_STATE_SERVICE] ERROR: No project path in config for '${e}'`),this.interactor.error("Invalid selection, project path needed \u{1F622}.");return}let o={name:e,config:i,configPath:t};console.log(`[PROJECT_STATE_SERVICE] Selected '${e}' \u2192 ${i.path}`),this.updateSelectedProject(o),this.interactor.displayText(`Project local configuration used: ${n}`)}updateSelectedProject(e){this.selectedProjectBehaviorSubject.next(e)}get selectedProject(){return this.selectedProjectBehaviorSubject.value}save(e){let t=this.selectedProjectBehaviorSubject.value;if(!t){this.interactor.error("No current project selected, save not possible");return}let n={...t.config,...e};Un(Bm.join(t.configPath,tde),n),t.config=n,this.updateSelectedProject(t)}getConfigForClient(){let e=this.selectedProjectBehaviorSubject.value;return e?this.maskingService.maskConfig(e.config):null}updateConfigFromClient(e){let t=this.selectedProjectBehaviorSubject.value;if(!t){this.interactor.error("No current project selected, update not possible");return}let n=this.maskingService.unmaskConfig(e,t.config);this.save(n)}};import*as al from"node:path";import*as ode from"node:os";import{randomUUID as qot}from"node:crypto";import{existsSync as sf,mkdirSync as Uot,readdirSync as ade,unlinkSync as zot}from"fs";import*as CL from"node:path";import*as ide from"node:os";function Lot(r){return r.replace(/[^a-zA-Z0-9]/g,"_")}function Wm(r,e){try{let t=CL.join(ide.userInfo().homedir,".coday"),n=Lot(r),i=CL.join(e??t,"users",n,"user.yaml"),a=oa(i);return a?a.groups?.includes("CODAY_ADMIN")??!1:!1}catch{return!1}}var Kk=class{codayConfigDir;projectService;nativeHandlerStubs=[];constructor(e,t){let n=al.join(ode.userInfo().homedir,".coday");this.codayConfigDir=e??n,this.projectService=t}getProjectPath(e){if(this.projectService)return this.projectService.getProject(e)?.config.path}async getOrCreatePromptsDir(e,t){let n;if(t==="local")n=al.join(this.codayConfigDir,"projects",e,"prompts");else{let i=this.getProjectPath(e);if(!i)throw new Error("Project path not configured, cannot access project prompts");let a=await C2({text:"coday.yaml",root:i});if(a.length===0)throw new Error(`coday.yaml not found in project path: ${i}`);let o=al.dirname(a[0]);n=al.join(i,o,"prompts")}return sf(n)||(Uot(n,{recursive:!0}),console.log(`[PROMPT] Created prompts directory: ${n}`)),n}async getOrCreatePromptFilePath(e,t,n){let i=await this.getOrCreatePromptsDir(e,n);return al.join(i,`${t}.yml`)}async findPromptSource(e,t){let n=await this.getOrCreatePromptFilePath(e,t,"local");if(sf(n))return"local";if(this.projectService)try{let i=await this.getOrCreatePromptFilePath(e,t,"project");if(sf(i))return"project"}catch{}return null}async findProjectForPrompt(e){let t=al.join(this.codayConfigDir,"projects");if(!sf(t))return null;let n=ade(t,{withFileTypes:!0}).filter(i=>i.isDirectory()).map(i=>i.name);for(let i of n){let a=al.join(this.codayConfigDir,"projects",i,"prompts",`${e}.yml`);if(sf(a))return i;if(this.projectService)try{let o=await this.getOrCreatePromptFilePath(i,e,"project");if(sf(o))return i}catch{}}return null}validatePlaceholders(e){let t=/\{\{(\w+)\}\}/g,n=[],i=/^[A-Za-z][A-Za-z0-9-]*$/;if(e.forEach(a=>{let o;for(;(o=t.exec(a))!==null;){let s=o[1];s&&s!=="PARAMETERS"&&!i.test(s)&&n.push(s)}}),n.length>0)throw new Error(`Invalid parameter keys: ${n.join(", ")}. Keys must start with a letter and contain only letters, digits, and hyphens.`)}async create(e,t,n="local"){try{this.validatePlaceholders(t.commands);let i=qot(),a={...t,id:i,source:n,createdAt:new Date().toISOString(),parameterFormat:this.getParameterFormat(t.commands)},o=await this.getOrCreatePromptFilePath(e,i,n);if(sf(o))throw new Error(`Prompt with ID ${i} already exists`);return Un(o,a),console.log(`[PROMPT] Created prompt ${i} in ${n} for project ${e}`),a}catch(i){throw new Error(`Failed to create prompt: ${i instanceof Error?i.message:"Unknown error"}`)}}async get(e,t){try{let n=await this.findPromptSource(e,t);if(!n)return null;let i=await this.getOrCreatePromptFilePath(e,t,n),a=oa(i);return a?(a.source||(a.source=n),a):null}catch(n){return console.error(`Failed to get prompt ${t}:`,n),null}}async getById(e){try{let t=await this.findProjectForPrompt(e);if(!t)return null;let n=await this.get(t,e);return n?{prompt:n,projectName:t}:null}catch(t){return console.error(`Failed to get prompt ${e}:`,t),null}}async update(e,t,n,i){try{let a=await this.get(e,t);if(!a)return null;if(n.webhookEnabled!==void 0&&n.webhookEnabled!==a.webhookEnabled&&!Wm(i,this.codayConfigDir))throw new Error("Only CODAY_ADMIN can enable/disable webhook for prompts");let{id:o,createdAt:s,source:c,...u}=n;n.commands&&this.validatePlaceholders(n.commands);let l={...a,...u,updatedAt:new Date().toISOString()};n.commands&&(l.parameterFormat=this.getParameterFormat(n.commands));let f=await this.getOrCreatePromptFilePath(e,t,a.source);return Un(f,l),console.log(`[PROMPT] Updated prompt ${t} (${a.source}) by user ${i}`),l}catch(a){throw console.error(`Failed to update prompt ${t}:`,a),a}}async delete(e,t){try{let n=await this.findPromptSource(e,t);if(!n)return!1;let i=await this.getOrCreatePromptFilePath(e,t,n);return zot(i),console.log(`[PROMPT] Deleted prompt ${t} from ${n}`),!0}catch(n){return console.error(`Failed to delete prompt ${t}:`,n),!1}}getParameterFormat(e){if(e.some(a=>/\{\{PARAMETERS\}\}/.test(a)))return"";let n=/\{\{(\w+)\}\}/g,i=new Set;return e.forEach(a=>{let o;for(;(o=n.exec(a))!==null;){let s=o[1];s&&s!=="PARAMETERS"&&i.add(s)}}),i.size===0?"":Array.from(i).map(a=>`${a}=""`).join(" ")}registerNativeHandler(e){this.nativeHandlerStubs.find(t=>t.id===e.id)||this.nativeHandlerStubs.push(e)}async list(e,t=[]){try{let n=[],i=["local"];this.projectService&&i.push("project");for(let a of i)try{let o=await this.getOrCreatePromptsDir(e,a);if(!sf(o))continue;let c=ade(o).filter(u=>u.endsWith(".yml"));for(let u of c){let l=u.replace(".yml",""),f=await this.get(e,l);f&&n.push({id:f.id,name:f.name,description:f.description,webhookEnabled:f.webhookEnabled,createdBy:f.createdBy,createdAt:f.createdAt,updatedAt:f.updatedAt,source:f.source||a,parameterFormat:f.parameterFormat})}}catch(o){console.log(`[PROMPT] Could not access ${a} prompts for ${e}:`,o)}return n.push(...V0),n.push(...this.nativeHandlerStubs),n.push(...t),n.sort((a,o)=>new Date(o.createdAt).getTime()-new Date(a.createdAt).getTime())}catch(n){return console.error(`Failed to list prompts for project ${e}:`,n),[]}}async enableWebhook(e,t,n){return this.update(e,t,{webhookEnabled:!0},n)}async disableWebhook(e,t,n){return this.update(e,t,{webhookEnabled:!1},n)}};var sde=lr(wo(),1),Yk=class{constructor(e){this.promptService=e}threadCodayManager;threadService;codayOptions;logger;initialize(e,t,n,i){this.threadCodayManager=e,this.threadService=t,this.codayOptions=n,this.logger=i}processCommands(e,t){let n;if(typeof t=="string"){let a=e.some(s=>/\{\{PARAMETERS\}\}/.test(s));if(e.some(s=>/\{\{(?!PARAMETERS\}\})\w+\}\}/.test(s)))throw new Error("Prompt contains structured placeholders ({{key}}). Use an object parameter instead of a string.");a?n=e.map(s=>s.replace(/\{\{PARAMETERS\}\}/g,t)):n=e.map((s,c)=>c===0?`${s} ${t}`.trim():s)}else typeof t=="object"&&t!==null?n=e.map(a=>{let o=a;return Object.entries(t).forEach(([s,c])=>{let u=`{{${s}}}`;o=o.replaceAll(u,String(c))}),o}):n=[...e];let i=new Set;if(n.forEach(a=>{let o=a.match(/\{\{(\w+)\}\}/g);o&&o.forEach(s=>i.add(s))}),i.size>0){let a=Array.from(i).map(o=>o.replace(/[{}]/g,"")).join(", ");throw new Error(`Missing required parameters: ${a}`)}return n}async executePrompt(e,t,n,i,a){if(!this.threadCodayManager||!this.threadService||!this.codayOptions||!this.logger)throw new Error("PromptExecutionService not initialized. Call initialize() first.");let{title:o,awaitFinalAnswer:s=!1,projectName:c}=a||{},u,l;if(c){if(l=c,u=await this.promptService.get(l,e),!u)throw new Error(`Prompt ${e} not found in project ${l}`)}else{let y=await this.promptService.getById(e);if(!y)throw new Error(`Prompt not found: ${e}`);u=y.prompt,l=y.projectName}if(i==="webhook"&&!u.webhookEnabled)throw new Error(`Prompt ${e} is not enabled for webhook execution`);if(!u.commands||u.commands.length===0)throw new Error("Prompt has no commands configured");let f=this.processCommands(u.commands,t);if(!n)throw new Error("Username is required");let d=(await this.threadService.createThread(l,n,o)).id;console.log(`[PROMPT_EXEC] Created new thread: ${d} for ${i} execution`);let m={...this.codayOptions,oneshot:!0,project:l,thread:d,prompts:f};console.log(`[PROMPT_EXEC] Creating instance for ${i} execution with ${f.length} prompts:`,f);let h=this.threadCodayManager.createWithoutConnection(d,l,n,m);h.prepareCoday();let g=h.coday.interactor,v={project:l,title:o??"Untitled",username:n,clientId:d,promptCount:f.length,awaitFinalAnswer:!!s,promptName:u.name,promptId:u.id,executionMode:i};if(i==="webhook"?this.logger.logWebhook(v):this.logger.logWebhook({...v,webhookName:u.name,webhookUuid:u.id}),s){let y=[],x=g.events.pipe((0,sde.filter)(_=>(console.log(`[PROMPT_EXEC] Received event type: ${_.type}, role: ${_ instanceof jt?_.role:"N/A"}`),_ instanceof jt&&_.role==="assistant"&&!!_.name))).subscribe(_=>{y.push(_)});try{await h.coday.run(),x.unsubscribe();let _=y[y.length-1];return await this.threadCodayManager.cleanup(d),{threadId:d,lastEvent:_}}catch(_){x.unsubscribe();let S=_ instanceof Error?_.message:"Unknown error";throw this.logger.logWebhookError({error:`Prompt execution failed: ${S}`,username:n,project:l,clientId:d}),console.error("[PROMPT_EXEC] Error waiting for prompt completion:",_),await this.threadCodayManager.cleanup(d),_}}else return h.coday.run().catch(y=>{console.error("[PROMPT_EXEC] Error during prompt Coday run:",y)}).finally(()=>{this.threadCodayManager.cleanup(d).catch(y=>{console.error("[PROMPT_EXEC] Error cleaning up prompt thread after run:",y)})}),{threadId:d}}};var Tg=lr(zc(),1);import*as ui from"fs";import*as Vm from"path";import{randomUUID as Bot}from"node:crypto";var Xk=class{constructor(e,t,n){this.logger=e;this.promptService=t;this.codayConfigDir=n}schedulers=new Map;checkInterval;CHECK_INTERVAL_MS=3e4;promptExecutionService;initializeExecution(e){this.promptExecutionService=e}async initialize(){console.log("[SCHEDULER] Initializing SchedulerService..."),await this.loadAllSchedulers(),this.startScheduler(),console.log(`[SCHEDULER] SchedulerService initialized with ${this.schedulers.size} schedulers`)}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=void 0,console.log("[SCHEDULER] SchedulerService stopped"))}async loadAllSchedulers(){this.schedulers.clear();let e=Vm.join(this.codayConfigDir,"projects");if(!ui.existsSync(e))return;let t=ui.readdirSync(e,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>n.name);for(let n of t)try{let i=await this.loadProjectSchedulers(n);for(let a of i)this.schedulers.set(a.id,a)}catch(i){console.error(`[SCHEDULER] Failed to load schedulers for project ${n}:`,i)}}async loadProjectSchedulers(e){let t=this.getSchedulersDir(e);if(!ui.existsSync(t))return[];let n=ui.readdirSync(t),i=[];for(let a of n)if(a.endsWith(".yml"))try{let o=Vm.join(t,a),s=ui.readFileSync(o,"utf-8"),c=Tg.parse(s),u=this.calculateNextRunSkippingMissed(c),l=u.occurrenceCount!==c.occurrenceCount;c.nextRun=u.nextRun,c.occurrenceCount=u.occurrenceCount,l&&await this.saveScheduler(c,e),i.push(c)}catch(o){console.error(`[SCHEDULER] Failed to load scheduler from ${a}:`,o)}return i}startScheduler(){this.checkInterval=setInterval(()=>{this.checkAndExecuteSchedulers()},this.CHECK_INTERVAL_MS),this.checkAndExecuteSchedulers()}async checkAndExecuteSchedulers(){for(let e of this.schedulers.values())e.enabled&&Bue(e.schedule,e.nextRun??null,e.occurrenceCount??0)&&this.executeScheduler(e).catch(t=>{console.error(`[SCHEDULER] Failed to execute scheduler ${e.id}:`,t)})}async executeSchedulerInternal(e,t,n){if(!this.promptExecutionService)throw new Error("PromptExecutionService not initialized");console.log(`[SCHEDULER] Executing scheduler "${e.name}" (${e.id}) [${n}]`);let i=e.parameters;e.parameters&&typeof e.parameters=="object"&&Object.keys(e.parameters).length===1&&"PARAMETERS"in e.parameters&&(i=String(e.parameters.PARAMETERS));let a=await this.promptExecutionService.executePrompt(e.promptId,i,e.createdBy,"scheduled",{title:n==="scheduled"?`Scheduled: ${e.name}`:`Manual: ${e.name}`,awaitFinalAnswer:!1,projectName:t});return console.log(`[SCHEDULER] Scheduler "${e.name}" executed successfully. Thread: ${a.threadId}`),a.threadId}async executeScheduler(e){let t=new Date().toISOString();console.log(`[SCHEDULER] Executing scheduler "${e.name}" (${e.id})`);let n=this.findProjectForScheduler(e.id);if(!n){console.error(`[SCHEDULER] Cannot find project for scheduler ${e.id}, skipping execution`);return}let i,a,o;e.lastRun=t,e.occurrenceCount=(e.occurrenceCount??0)+1,e.nextRun=Oh(e.schedule,new Date,e.occurrenceCount);try{await this.saveScheduler(e,n),console.log(`[SCHEDULER] Next execution for "${e.name}": ${e.nextRun}`),this.schedulers.set(e.id,e),a=await this.executeSchedulerInternal(e,n,"scheduled"),i=!0}catch(s){i=!1,o=s instanceof Error?s.message:String(s),console.error(`[SCHEDULER] Scheduler "${e.name}" failed:`,s)}this.logger.logTriggerExecution({triggerId:e.id,triggerName:e.name,webhookUuid:e.promptId,projectName:n,success:i,threadId:a,error:o})}validateSchedule(e){return zue(e)}calculateNextRunSkippingMissed(e){let t=new Date,n=e.occurrenceCount??0,i=e.nextRun;if(!i)return{nextRun:Oh(e.schedule,t,n),occurrenceCount:n};if(new Date(i)>=t)return{nextRun:i,occurrenceCount:n};let a=1e3,o=0,s=0;for(;o<a;){if(n++,s++,i=Oh(e.schedule,t,n),!i)return s>0&&console.log(`[SCHEDULER] Scheduler "${e.name}" (${e.id}) expired after skipping ${s} missed occurrence(s)`),{nextRun:null,occurrenceCount:n};if(new Date(i)>=t)return s>0&&console.log(`[SCHEDULER] Scheduler "${e.name}" (${e.id}) skipped ${s} missed occurrence(s), next run: ${i}`),{nextRun:i,occurrenceCount:n};o++}return console.warn(`[SCHEDULER] Could not find future nextRun for scheduler ${e.id} after ${a} iterations`),{nextRun:null,occurrenceCount:n}}getSchedulersDir(e){return Vm.join(this.codayConfigDir,"projects",e,"schedulers")}getSchedulerFilePath(e,t){return Vm.join(this.getSchedulersDir(e),`${t}.yml`)}findProjectForScheduler(e){let t=Vm.join(this.codayConfigDir,"projects");if(!ui.existsSync(t))return null;let n=ui.readdirSync(t,{withFileTypes:!0}).filter(i=>i.isDirectory()).map(i=>i.name);for(let i of n){let a=this.getSchedulerFilePath(i,e);if(ui.existsSync(a))return i}return null}canAccessScheduler(e,t){return e.createdBy===t?!0:Wm(t,this.codayConfigDir)}async listSchedulers(e,t){return(await this.loadProjectSchedulers(e)).filter(i=>this.canAccessScheduler(i,t)).map(i=>({id:i.id,name:i.name,enabled:i.enabled,promptId:i.promptId,schedule:i.schedule,parameters:i.parameters,lastRun:i.lastRun,nextRun:i.nextRun,createdBy:i.createdBy}))}async getScheduler(e,t,n){let i=this.getSchedulerFilePath(e,t);if(!ui.existsSync(i))return null;try{let a=ui.readFileSync(i,"utf-8"),o=Tg.parse(a),s=this.calculateNextRunSkippingMissed(o);return o.nextRun=s.nextRun,o.occurrenceCount=s.occurrenceCount,n&&!this.canAccessScheduler(o,n)?(console.log(`[SCHEDULER] Access denied for user ${n} to scheduler ${t}`),null):o}catch(a){return console.error(`[SCHEDULER] Failed to load scheduler ${t}:`,a),null}}async createScheduler(e,t,n){let i=this.validateSchedule(t.schedule);if(!i.valid)throw new Error(`Invalid schedule: ${i.error}`);if(!await this.promptService.getById(t.promptId))throw new Error(`Prompt not found: ${t.promptId}`);let o={id:Bot(),name:t.name,enabled:t.enabled??!0,promptId:t.promptId,schedule:t.schedule,parameters:t.parameters,createdBy:n,createdAt:new Date().toISOString(),nextRun:Oh(t.schedule,new Date,0),occurrenceCount:0};return await this.saveScheduler(o,e),this.schedulers.set(o.id,o),console.log(`[SCHEDULER] Created scheduler "${o.name}" (${o.id}) for project ${e}`),o}async updateScheduler(e,t,n,i){let a=await this.getScheduler(e,t,i);if(!a)throw new Error(`Scheduler not found or access denied: ${t}`);if(console.log(`[SCHEDULER] Updating scheduler ${t} by user ${i}`),n.name!==void 0&&(a.name=n.name),n.enabled!==void 0&&(a.enabled=n.enabled),n.parameters!==void 0&&(a.parameters=n.parameters),n.promptId!==void 0){if(!await this.promptService.getById(n.promptId))throw new Error(`Prompt not found: ${n.promptId}`);a.promptId=n.promptId}if(n.schedule!==void 0){let o=this.validateSchedule(n.schedule);if(!o.valid)throw new Error(`Invalid schedule: ${o.error}`);a.schedule=n.schedule,a.occurrenceCount=0,a.nextRun=Oh(n.schedule,new Date,0)}return await this.saveScheduler(a,e),this.schedulers.set(a.id,a),console.log(`[SCHEDULER] Updated scheduler "${a.name}" (${a.id})`),a}async deleteScheduler(e,t,n){if(!await this.getScheduler(e,t,n))return!1;let a=this.getSchedulerFilePath(e,t);return ui.unlinkSync(a),this.schedulers.delete(t),console.log(`[SCHEDULER] Deleted scheduler ${t} by user ${n}`),!0}async enableScheduler(e,t,n){return this.updateScheduler(e,t,{enabled:!0},n)}async disableScheduler(e,t,n){return this.updateScheduler(e,t,{enabled:!1},n)}async runSchedulerNow(e,t,n){let i=await this.getScheduler(e,t,n);if(!i)throw new Error(`Scheduler not found or access denied: ${t}`);let a=await this.executeSchedulerInternal(i,e,"manual");return i.lastRun=new Date().toISOString(),await this.saveScheduler(i,e),this.schedulers.set(i.id,i),this.logger.logTriggerExecution({triggerId:i.id,triggerName:i.name,webhookUuid:i.promptId,projectName:e,success:!0,threadId:a}),a}async saveScheduler(e,t){if(!t&&(t=this.findProjectForScheduler(e.id),!t))throw new Error(`Cannot find project for scheduler ${e.id}`);let n=this.getSchedulersDir(t);ui.mkdirSync(n,{recursive:!0});let i=this.getSchedulerFilePath(t,e.id),a=Tg.stringify(e);ui.writeFileSync(i,a,"utf-8")}};import*as fs from"node:path";import*as ude from"node:os";import{existsSync as cde,lstatSync as Hot,mkdirSync as AL,readdirSync as Wot,renameSync as Vot}from"fs";var Qk=class r{projectsConfigPath;static PROJECT_FILENAME="project.yaml";constructor(e){let t=fs.join(ude.userInfo().homedir,".coday");this.projectsConfigPath=fs.join(e??t,"projects"),AL(this.projectsConfigPath,{recursive:!0})}listProjects(){return Wot(this.projectsConfigPath).filter(t=>!t.startsWith(".")&&Hot(fs.join(this.projectsConfigPath,t)).isDirectory())}getProjectInfo(e){let t=fs.join(this.projectsConfigPath,e),n=fs.join(t,r.PROJECT_FILENAME);return cde(n)?{name:e,configPath:t}:null}exists(e){return this.getProjectInfo(e)!==null}getConfig(e){let t=this.getProjectInfo(e);if(!t)return console.log(`[PROJECT_REPO] No project info found for: '${e}'`),null;let n=fs.join(t.configPath,r.PROJECT_FILENAME),i=oa(n);if(!i)return console.log(`[PROJECT_REPO] Failed to load config for '${e}' from ${n}`),null;let a=Dh(i,Hue);return a!==i&&(console.log(`[PROJECT_REPO] Config migrated for '${e}', saving...`),Un(n,a)),a}saveConfig(e,t){let n=this.getProjectInfo(e);if(!n)throw new Error(`Project '${e}' does not exist`);let i=fs.join(n.configPath,r.PROJECT_FILENAME);Un(i,t)}createProject(e,t){let n=fs.join(this.projectsConfigPath,e),i=fs.join(n,r.PROJECT_FILENAME);return cde(i)?!1:(console.log(`[PROJECT_REPO] Creating project '${e}' with path: ${t}`),AL(n,{recursive:!0}),Un(i,{version:1,path:t,integration:{},storage:{type:"file"},agents:[]}),console.log(`[PROJECT_REPO] Project '${e}' created successfully`),!0)}deleteProject(e){let t=this.getProjectInfo(e);if(!t)return!1;let n=fs.join(this.projectsConfigPath,".deleted");AL(n,{recursive:!0});let i=new Date().toISOString().replace(/:/g,"-"),a=fs.join(n,`${e}__${i}`);return Vot(t.configPath,a),!0}};var TL=lr(zc(),1);import{promises as ol}from"fs";import ld from"path";var lde=async r=>{try{let e=await ol.readFile(r,"utf-8");return TL.default.parse(e)}catch{return null}},eE=class{constructor(e){this.projectsDir=e}getThreadsDir(e){return ld.join(this.projectsDir,e,"threads")}async ensureThreadsDir(e){let t=this.getThreadsDir(e);try{await ol.mkdir(t,{recursive:!0})}catch(n){throw new Nu(`Failed to initialize threads directory for project ${e}`,n)}}getThreadFileName(e){return`${e.id}.yml`}async findThreadFile(e,t){try{let n=this.getThreadsDir(e),i=`${t}.yml`;try{return await ol.access(ld.join(n,i)),i}catch{}return(await ol.readdir(n)).find(s=>s.endsWith(`-${t}.yml`))||null}catch(n){throw new Nu(`Error finding thread ${t} in project ${e}`,n)}}async getById(e,t){try{let n=await this.findThreadFile(e,t);if(!n)return null;let i=this.getThreadsDir(e),a=ld.join(i,n),o=await lde(a);if(!o)return null;let s=Dh(o,rF);return s.projectId||(s.projectId=e),s!==o&&Un(a,s),new Fu(s)}catch(n){throw new Nu(`Failed to read thread ${t} from project ${e}`,n)}}async save(e,t){await this.ensureThreadsDir(e);try{if(t.id||(t.id=crypto.randomUUID()),t.projectId||(t.projectId=e),t.projectId!==e)throw new Error(`Thread projectId mismatch: expected ${e}, got ${t.projectId}`);let n=this.getThreadsDir(e),i=this.getThreadFileName(t),a=ld.join(n,i),o=await this.findThreadFile(e,t.id);if(o&&o!==i){let u=ld.join(n,o);try{await ol.unlink(u),console.log(`[THREAD-REPO] Migrated/renamed thread file: ${o} \u2192 ${i}`)}catch(l){console.warn(`[THREAD-REPO] Could not delete old thread file: ${o}`,l)}}let s={...t.serialize(),version:rF.length+1},c=TL.default.stringify(s);return await ol.writeFile(a,c,"utf-8"),t}catch(n){throw new Nu(`Failed to save thread ${t.id} to project ${e}`,n)}}async listByProject(e){try{let t=this.getThreadsDir(e);try{await ol.access(t)}catch{return[]}let n=await ol.readdir(t);return(await Promise.all(n.filter(a=>a.endsWith(".yml")).map(async a=>{let o=await lde(ld.join(t,a));if(!o)return null;let s=o.projectId||e;return{id:o.id,username:o.username,projectId:s,name:o.name??"...",summary:o.summary??"",createdDate:o.createdDate??"",modifiedDate:o.modifiedDate??"",price:o.price??0,starring:o.starring??[],users:Array.isArray(o.users)?o.users.map(c=>typeof c=="string"?{userId:c}:c):o.username?[{userId:o.username}]:[],parentThreadId:o.parentThreadId,parentEventId:o.parentEventId,delegatedAgentName:o.delegatedAgentName,delegatedTask:o.delegatedTask}}))).filter(a=>!!a).sort((a,o)=>a.modifiedDate>o.modifiedDate?-1:1)}catch(t){throw new Nu(`Failed to list threads for project ${e}`,t)}}async delete(e,t){try{let n=await this.findThreadFile(e,t);if(!n)return!1;let i=this.getThreadsDir(e);return await ol.unlink(ld.join(i,n)),!0}catch(n){throw new Nu(`Failed to delete thread ${t} from project ${e}`,n)}}};var tE=class{constructor(e,t,n){this.projectRepository=e;this.projectsDir=t;this.threadFileService=n}repositoryCache=new Map;threadListCache=new Map;CACHE_TTL_MS=14400*1e3;loadingPromises=new Map;getThreadRepository(e){let t=this.repositoryCache.get(e);if(t)return t;if(!this.projectRepository.getProjectInfo(e))throw new Error(`Project '${e}' not found`);let i=new eE(this.projectsDir);return this.repositoryCache.set(e,i),i}clearCache(e){e?this.repositoryCache.delete(e):this.repositoryCache.clear()}async listThreads(e,t){let n=this.threadListCache.get(e);if(n&&Date.now()-n.timestamp<this.CACHE_TTL_MS)return n.data.filter(o=>on(o,t)).sort((o,s)=>o.modifiedDate>s.modifiedDate?-1:1);let i=this.loadingPromises.get(e);if(i)return await i,this.listThreads(e,t);let a=this.loadThreadListFromDisk(e);this.loadingPromises.set(e,a);try{return(await a).filter(s=>on(s,t)).sort((s,c)=>s.modifiedDate>c.modifiedDate?-1:1)}catch(o){throw this.threadListCache.delete(e),o}finally{this.loadingPromises.delete(e)}}async loadThreadListFromDisk(e){let n=await this.getThreadRepository(e).listByProject(e);return this.threadListCache.set(e,{data:n,timestamp:Date.now()}),n}toThreadSummary(e){return{id:e.id,username:e.username,projectId:e.projectId,name:e.name,summary:e.summary,createdDate:e.createdDate,modifiedDate:e.modifiedDate,price:e.price,starring:e.starring,users:e.users,parentThreadId:e.parentThreadId,parentEventId:e.parentEventId,delegatedAgentName:e.delegatedAgentName,delegatedTask:e.delegatedTask}}updateThreadInCache(e,t){let n=this.threadListCache.get(e);if(!n)return;let i=n.data.findIndex(a=>a.id===t.id);i!==-1?n.data[i]=t:n.data.push(t)}removeThreadFromCache(e,t){let n=this.threadListCache.get(e);n&&(n.data=n.data.filter(i=>i.id!==t))}async getThread(e,t){return await this.getThreadRepository(e).getById(e,t)}async createThread(e,t,n){let i=this.getThreadRepository(e),a=new Fu({id:crypto.randomUUID(),username:t,projectId:e,name:n||"",price:0}),o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async updateThread(e,t,n){let i=this.getThreadRepository(e),a=await i.getById(e,t);if(!a){let c=this.threadListCache.get(e)?.data.find(u=>u.id===t);if(c)a=new Fu({id:c.id,username:c.username,projectId:c.projectId,name:c.name,summary:c.summary,price:c.price,starring:c.starring,users:c.users,parentThreadId:c.parentThreadId,parentEventId:c.parentEventId,delegatedAgentName:c.delegatedAgentName,delegatedTask:c.delegatedTask});else throw new Error(`Thread '${t}' not found in project '${e}'`)}n.name!==void 0&&(a.name=n.name),n.summary!==void 0&&(a.summary=n.summary),n.users!==void 0&&(a.users=n.users);let o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async starThread(e,t,n){let i=this.getThreadRepository(e),a=await i.getById(e,t);if(!a)throw new Error(`Thread '${t}' not found in project '${e}'`);a.starring.includes(n)||a.starring.push(n);let o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async unstarThread(e,t,n){let i=this.getThreadRepository(e),a=await i.getById(e,t);if(!a)throw new Error(`Thread '${t}' not found in project '${e}'`);a.starring=a.starring.filter(s=>s!==n);let o=await i.save(e,a);return this.updateThreadInCache(e,this.toThreadSummary(o)),o}async deleteThread(e,t){let i=await this.getThreadRepository(e).delete(e,t);return i&&(await this.threadFileService.deleteThreadFiles(e,t),this.removeThreadFromCache(e,t)),i}async saveThread(e,t){let i=await this.getThreadRepository(e).save(e,t);return this.updateThreadInCache(e,this.toThreadSummary(i)),i}async listAllThreads(e){let t=this.threadListCache.get(e);if(t&&Date.now()-t.timestamp<this.CACHE_TTL_MS)return t.data.sort((a,o)=>a.modifiedDate>o.modifiedDate?-1:1);let n=this.loadingPromises.get(e);if(n)return await n,this.listAllThreads(e);let i=this.loadThreadListFromDisk(e);this.loadingPromises.set(e,i);try{return(await i).sort((o,s)=>o.modifiedDate>s.modifiedDate?-1:1)}catch(a){throw this.threadListCache.delete(e),a}finally{this.loadingPromises.delete(e)}}async exists(e,t){return await this.getThread(e,t)!==null}};var mde=lr(zc(),1);import*as Ca from"fs/promises";import*as Pg from"path";var fde=5,pde=24,dde=100,hde={SHORT_THREADS:7,LONG_THREADS:30},rE=class{constructor(e,t){this.projectsConfigPath=e;this.logger=t}cleanupTimer=null;initialTimer=null;isRunning=!1;async start(){if(this.isRunning){this.log("Thread cleanup service already running");return}this.isRunning=!0,this.log("Starting thread cleanup service with user message-based retention"),this.initialTimer=setTimeout(async()=>{await this.performCleanup(),this.cleanupTimer=setInterval(async()=>{await this.performCleanup()},pde*60*60*1e3),this.log(`Thread cleanup scheduled every ${pde} hours`)},fde*60*1e3),this.log(`Initial cleanup scheduled in ${fde} minutes`)}async stop(){this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.cleanupTimer&&(clearInterval(this.cleanupTimer),this.cleanupTimer=null),this.isRunning=!1,this.log("Thread cleanup service stopped (all timers cleared)")}async performCleanup(){let e=Date.now(),t=0,n=0,i=0;try{this.log("Starting thread cleanup...");let a=await Ca.readdir(this.projectsConfigPath);this.log(`Found ${a.length} projects to scan`);for(let s of a)try{let c=Pg.join(this.projectsConfigPath,s);if(!(await Ca.lstat(c)).isDirectory())continue;let l=Pg.join(c,"threads");try{await Ca.access(l)}catch{continue}let{scanned:f,deleted:p,errors:d}=await this.cleanupProjectThreads(s,l);t+=f,n+=p,i+=d}catch(c){this.logError(`Error processing project ${s}: ${c}`),i++}let o=Date.now()-e;this.log(`Cleanup completed: ${n}/${t} threads deleted across all projects in ${o}ms (${i} errors)`)}catch(a){let o=Date.now()-e;this.logError(`Cleanup failed after ${o}ms: ${a}`)}}async cleanupProjectThreads(e,t){let n=0,i=0,a=0;try{let s=(await Ca.readdir(t)).filter(c=>c.endsWith(".yml"));if(n=s.length,s.length===0)return{scanned:n,deleted:i,errors:a};this.log(`Scanning ${s.length} threads in project ${e}`);for(let c=0;c<s.length;c+=dde){let u=s.slice(c,c+dde),l=await this.processBatch(u,t,e);i+=l.deleted,a+=l.errors}i>0&&this.log(`Project ${e}: deleted ${i}/${n} expired threads`)}catch(o){this.logError(`Error scanning project ${e}: ${o}`),a++}return{scanned:n,deleted:i,errors:a}}async processBatch(e,t,n){let i=0,a=0;return await Promise.all(e.map(async o=>{let s=Pg.join(t,o),c;try{let u=await Ca.readFile(s,"utf-8");c=mde.parse(u)}catch(u){await Ca.unlink(s),this.logError(`Error processing file ${o} in project ${n}: ${u}`)}if(!(!c||!c.modifiedDate)&&!(c.starring&&Array.isArray(c.starring)&&c.starring.length>0)&&this.shouldDeleteThread(c)){let u=this.countUserMessages(c.messages||[]),l=this.getDaysSinceModified(c.modifiedDate),f=c.id||o.replace(".yml","");await Ca.unlink(s),await this.deleteThreadFiles(t,f),i++,this.logger.logThreadCleanup(n,o),console.log(`ThreadCleanup: Deleted thread ${f} and its files directory (${u} user messages, ${l} days old)`)}})),{deleted:i,errors:a}}shouldDeleteThread(e){if(!e||!e.modifiedDate)return!1;let n=this.countUserMessages(e.messages||[])<=3?hde.SHORT_THREADS:hde.LONG_THREADS;return this.getDaysSinceModified(e.modifiedDate)>n}countUserMessages(e){return e.filter(t=>t&&t.type==="message"&&t.role==="user").length}getDaysSinceModified(e){let t=new Date(e),i=new Date().getTime()-t.getTime();return Math.floor(i/(1e3*60*60*24))}async deleteThreadFiles(e,t){try{let n=Pg.join(e,`${t}-files`);try{await Ca.access(n),await Ca.rm(n,{recursive:!0,force:!0}),console.log(`ThreadCleanup: Deleted files directory ${n}`)}catch(i){if(i.code!=="ENOENT")throw i}}catch(n){console.error(`ThreadCleanup: Error deleting files for ${t}:`,n)}}log(e){let t=new Date().toISOString();console.log(`[${t}] ThreadCleanup: ${e}`)}logError(e){let t=new Date().toISOString();console.error(`[${t}] ThreadCleanup ERROR: ${e}`)}async forceCleanup(){this.log("Manual cleanup triggered"),await this.performCleanup()}};import*as Hi from"path";import*as fd from"fs";import*as Aa from"fs/promises";var nE=class{constructor(e){this.projectsDir=e}getThreadFilesDir(e,t){return Hi.join(this.projectsDir,e,"threads",`${t}-files`)}async ensureThreadFilesDir(e,t){let n=this.getThreadFilesDir(e,t);fd.existsSync(n)||await Aa.mkdir(n,{recursive:!0})}async listFiles(e,t){await this.ensureThreadFilesDir(e,t);let n=this.getThreadFilesDir(e,t),i=await Aa.readdir(n);return await Promise.all(i.map(async o=>{let s=Hi.join(n,o),c=await Aa.stat(s);return{filename:o,size:c.size,lastModified:c.mtime.toISOString()}}))}async saveFile(e,t,n,i){let a=this.getThreadFilesDir(e,t);await Aa.mkdir(a,{recursive:!0});let o=Hi.join(a,n);await Aa.writeFile(o,i)}async getFile(e,t,n){await this.ensureThreadFilesDir(e,t);let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);if(!o.startsWith(s))throw new Error("Access denied: invalid file path");if(!fd.existsSync(a))throw new Error(`File '${n}' not found`);return await Aa.readFile(a)}async getFilePath(e,t,n){await this.ensureThreadFilesDir(e,t);let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);if(!o.startsWith(s))throw new Error("Access denied: invalid file path");if(!fd.existsSync(a))throw new Error(`File '${n}' not found`);return a}fileExists(e,t,n){let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);return o.startsWith(s)?fd.existsSync(a):!1}async deleteFile(e,t,n){await this.ensureThreadFilesDir(e,t);let i=this.getThreadFilesDir(e,t),a=Hi.join(i,n),o=Hi.resolve(a),s=Hi.resolve(i);if(!o.startsWith(s))throw new Error("Access denied: invalid file path");if(!fd.existsSync(a))throw new Error(`File '${n}' not found`);await Aa.unlink(a)}async deleteThreadFiles(e,t){try{let n=this.getThreadFilesDir(e,t);fd.existsSync(n)&&(await Aa.rm(n,{recursive:!0,force:!0}),console.log(`Deleted thread files directory: ${n}`))}catch(n){console.error(`Error deleting thread files for ${t}:`,n)}}};var gde=lr(wo(),1);var iE=class{constructor(e,t,n,i){this.threadRepository=t;this.projectId=n;this.interactor=i;this.username=e.username}activeThread$=new gde.BehaviorSubject(null);isKilled=!1;activeThread=this.activeThread$.asObservable();username;async kill(){this.isKilled=!0,this.activeThread$.complete()}async create(e){let t=new Fu({id:"",username:this.username,name:e??"",price:0});return this.activeThread$.next(t),t}async select(e){let t=await this.threadRepository.getById(this.projectId,e);if(!t)throw new Error(`Thread ${e} not found`);return this.activeThread$.next(t),t}async save(e){let t=this.activeThread$.value;if(!t){console.error(`No thread existing when save attempt with name '${e}'`);return}e&&(t.name=e);let n=await this.threadRepository.save(this.projectId,t);this.activeThread$.next(n)}async autoSave(e){if(this.isKilled){console.log("Autosave skipped: service has been killed");return}let t=this.activeThread$.value;if(!t||t.messagesLength==0){console.log(`Autosave of an empty or falsy thread aborted, threadId: ${t?.id}, user: ${this.username}`);return}try{e&&(t.name=e),await this.threadRepository.save(this.projectId,t),this.interactor&&this.interactor.sendEvent(new Tc({threadId:t.id,name:t.name||void 0}))}catch(n){console.log("Autosave failed (service may have been killed):",n instanceof Error?n.message:n)}}async truncateAtUserMessage(e){let t=this.activeThread$.value;if(!t)return console.error("No active thread available for truncation"),!1;let n=t.truncateAtUserMessage(e);return n||this.interactor?.warn("Failed to truncate thread."),n}getCurrentThread(){return this.activeThread$.value}};async function vde(r,e){let t=e.project.selectedProject,n=e.user.getUserData(t.name),i=await Gk(t.config.path,r,n),a=new Z0({...i,root:t.config.path,name:t.name},e.user.username);return console.log(`[BUILD_CONTEXT] Context for '${a.project.name}' \u2192 root: ${a.project.root}, desc: ${a.project.description?.length||0} chars`),a}var aE=class extends Ne{constructor(t,n){super({commandWord:"select-project",description:"Select an existing project"});this.interactor=t;this.services=n}async handle(t,n){try{let i=this.getSubCommand(t);return await this.selectProject(i)??n}catch(i){return this.interactor.error(`Invalid project selection because: ${i.toString()}`),n}}async selectProject(t){return this.services.project.selectProject(t),this.services.project.selectedProject?vde(this.interactor,this.services):null}};var oE=class extends Ne{constructor(t,n,i){super({commandWord:"add",description:"Add a new integration configuration. User level is default, use --project/-p for project level."});this.interactor=t;this.service=n;this.editHandler=i}async handle(t,n){let a=!!Pt(this.getSubCommand(t),[{key:"project",alias:"p"}]).project,o=a?"project":"user",s=this.service.getMergedIntegrations(),c=Object.keys(s),u=hp.filter(h=>!c.includes(h));if(u.length===0)return this.interactor.displayText(`All available integrations (${hp.join(", ")}) are already configured.`),n;let l=await this.interactor.chooseOption(u.sort(),"Select integration to add:",`Available integrations to configure:
881
881
 
882
882
  ${u.map(h=>`- **${h}**`).join(`
883
883
  `)}
@@ -1299,7 +1299,7 @@ ${n}
1299
1299
  b) For each query key get the allowed operators.
1300
1300
  c) Use the query key and only the allowed operators to create the jql.
1301
1301
  d) Explicitly calling out the JQL URL
1302
- `,parameters:{type:"object",properties:{jql:{type:"string",description:"JQL query for counting issues"}}},parse:JSON.parse,function:async({jql:g})=>{let v=await this.jiraService.ensureInitialized();return v.isNewlyInitialized?{fieldMappingInfo:v.fieldMappingInfo,message:v.message}:(v.fieldMapping&&wZ(g,v.fieldMapping),Lve(g,a,s,o,this.interactor))}}},m={type:"function",function:{name:`${this.name}__postIssue`,description:"Create a new Jira issue/ticket without asking for more information from the user, directly call the function to create the ticket",parameters:{type:"object",properties:{request:{type:"object",description:"Request object containing all issue fields",properties:{projectKey:{type:"string",description:"Project key where the issue will be created"},summary:{type:"string",description:"Summary/title of the issue"},description:{type:"string",description:"Detailed description of the issue"},issuetype:{type:"string",description:"Type of issue"},assignee:{type:"string",description:"User ID of the assignee"},reporter:{type:"string",description:"User ID of the reporter"},priority:{type:"string",description:'Priority of the issue (e.g., "High", "Medium", "Low")'},labels:{type:"array",items:{type:"string"},description:"Labels to attach to the issue"},components:{type:"array",items:{type:"string"},description:"Components to associate with the issue"},fixVersions:{type:"array",items:{type:"string"},description:"Fix versions to associate with the issue"},duedate:{type:"string",description:"Due date in YYYY-MM-DD format"},parent:{type:"object",properties:{key:{type:"string",description:"The issue key of the parent"}},description:"Parent issue for this issue (directly establishes parent-child relationship)"},linkedIssues:{type:"array",items:{type:"object",properties:{key:{type:"string",description:"The key of the issue to link"},linkType:{type:"string",description:'The type of link to create (default: "is part of" for epics, "relates to" for other issue types)'},isEpicLink:{type:"boolean",description:"Whether to use Epic Link field if available (default: true for epics)"}}},description:"Issues to link to this issue after creation (especially useful for epics)"}}}}},parse:JSON.parse,function:async({request:g})=>{try{if(g.error&&g.partialRequest){let v=g.partialRequest,y=g.error||"Previous attempt failed";return this.interactor.displayText(`Retrying Jira issue creation with saved information. Previous error: ${y}`),await EZ(v,a,s,o,this.interactor)}return await EZ(g,a,s,o,this.interactor)}catch(v){throw this.interactor.error(`Error in createJiraIssue function: ${v}`),v}}}},h={type:"function",function:{name:`${this.name}__linkIssues`,description:"Link two Jira issues with a specified relationship type",parameters:{type:"object",properties:{inwardIssueKey:{type:"string",description:"The issue key that is the source of the link (inward issue)"},outwardIssueKey:{type:"string",description:"The issue key that is the target of the link (outward issue)"},linkType:{type:"string",description:'The type of link to create between issues (e.g., "relates to", "blocks", "is blocked by")'},comment:{type:"string",description:"Optional comment to add when creating the link"},isEpicLink:{type:"boolean",description:"Set to true to create an Epic-Issue relationship. This will attempt to use the Epic Link field if available, falling back to standard issue linking if not."}}},parse:JSON.parse,function:({inwardIssueKey:g,outwardIssueKey:v,linkType:y,comment:x,isEpicLink:_})=>_Z({inwardIssueKey:g,outwardIssueKey:v,linkType:y,comment:x,isEpicLink:_},a,s,o,this.interactor)}};return i.push(u),i.push(l),i.push(f),i.push(p),i.push(d),i.push(m),i.push(h),i}};KT();var g7=new Map,Wve=!1,v7=class extends Zt{constructor(t,n,i,a){super(t,i,a);this.integrationService=n}static TYPE="SLACK";async buildTools(){let t=[];if(!this.integrationService.hasIntegration(this.name))return console.log("failed to have integration slack"),t;let n=this.integrationService.getApiKey(this.name);if(!n)return console.log("no bot token"),t;let i=async(p,d={},m="GET",h)=>{let g=new URL(`https://slack.com/api/${p}`);m==="GET"&&Object.entries(d).forEach(([_,S])=>{g.searchParams.append(_,S)});let v={method:m,headers:{Authorization:`Bearer ${n}`,"Content-Type":m==="POST"?"application/json; charset=utf-8":"application/x-www-form-urlencoded"}};m==="POST"&&h&&(v.body=JSON.stringify(h));let x=await(await fetch(g.toString(),v)).json();if(!x.ok)throw new Error(`Slack API error: ${x.error||"Unknown error"}`);return x},a=async()=>{if(!Wve)try{let p;do{let d={limit:"200"};p&&(d.cursor=p);let m=await i("users.list",d);m.members?.forEach(h=>{h.deleted||g7.set(h.id,h)}),p=m.response_metadata?.next_cursor}while(p);Wve=!0}catch(p){this.interactor.debug(`Failed to populate user cache: ${p}`)}},o=async p=>(await a(),p.replace(/<@([A-Z0-9]+)>/g,(d,m)=>{let h=g7.get(m);return h?`@${h.real_name||h.name}`:d})),s={type:"function",function:{name:`${this.name}__list_channels`,description:"List Slack channels where the bot is a member. Only returns channels the bot has been invited to and can actually read/write. Returns channel ID, name, topic, and member count.",parameters:{type:"object",properties:{types:{type:"string",description:'Comma-separated list of channel types. Options: public_channel, private_channel, mpim (group DM), im (direct message). Default: "public_channel,private_channel"'},limit:{type:"number",description:"Maximum number of channels to return (default: 100, max: 1000)"}}},parse:JSON.parse,function:async p=>{try{let d={types:p.types||"public_channel,private_channel",limit:String(p.limit||100),exclude_archived:"true"},h=(await i("users.conversations",d)).channels||[];if(h.length===0)return"No channels found. The bot may need to be invited to channels first.";let g=h.map(v=>({id:v.id,name:v.name,private:v.is_private,topic:v.topic?.value||"",purpose:v.purpose?.value||"",members:v.num_members}));return JSON.stringify(g,null,2)}catch(d){return`Error listing channels: ${d instanceof Error?d.message:String(d)}`}}}},c={type:"function",function:{name:`${this.name}__read_channel`,description:"Read messages from a Slack channel. Returns messages with timestamps, user mentions resolved to names, and thread indicators. Use slack_list_channels first to get channel IDs. The channel parameter is required.",parameters:{type:"object",properties:{channel:{type:"string",description:"Channel ID (e.g., C1234567890). Get this from slack_list_channels. REQUIRED."},limit:{type:"number",description:"Number of messages to return (default: 20, max: 100)"},oldest:{type:"string",description:'Only messages after this Unix timestamp (e.g., "1234567890.123456")'},latest:{type:"string",description:"Only messages before this Unix timestamp"}}},parse:JSON.parse,function:async p=>{try{if(!p.channel)return"Error: channel parameter is required. Use slack_list_channels to get available channel IDs.";let d={channel:p.channel,limit:String(p.limit||20)};p.oldest&&(d.oldest=p.oldest),p.latest&&(d.latest=p.latest);let h=(await i("conversations.history",d)).messages||[];if(h.length===0)return"No messages found in this channel.";let g=await Promise.all(h.map(async v=>{let y=v.user?g7.get(v.user):void 0,x=await o(v.text);return{timestamp:v.ts,user:y?.real_name||y?.name||v.user||"Unknown",text:x,hasThread:!!v.thread_ts&&v.reply_count&&v.reply_count>0,replyCount:v.reply_count||0}}));return g.reverse(),JSON.stringify(g,null,2)}catch(d){return`Error reading channel: ${d instanceof Error?d.message:String(d)}`}}}},u={type:"function",function:{name:`${this.name}__read_thread`,description:"Read replies in a Slack thread. Use the timestamp from slack_read_channel to identify the thread. Returns all messages in the thread including the parent message. Both channel and thread_ts parameters are required.",parameters:{type:"object",properties:{channel:{type:"string",description:"Channel ID where the thread is located. REQUIRED."},thread_ts:{type:"string",description:"Timestamp of the parent message (thread_ts from slack_read_channel). REQUIRED."},limit:{type:"number",description:"Maximum number of replies to return (default: 50, max: 100)"}}},parse:JSON.parse,function:async p=>{try{if(!p.channel||!p.thread_ts)return"Error: both channel and thread_ts parameters are required.";let d={channel:p.channel,ts:p.thread_ts,limit:String(p.limit||50)},h=(await i("conversations.replies",d)).messages||[];if(h.length===0)return"No messages found in this thread.";let g=await Promise.all(h.map(async v=>{let y=v.user?g7.get(v.user):void 0,x=await o(v.text);return{timestamp:v.ts,user:y?.real_name||y?.name||v.user||"Unknown",text:x,isParent:v.ts===p.thread_ts}}));return JSON.stringify(g,null,2)}catch(d){return`Error reading thread: ${d instanceof Error?d.message:String(d)}`}}}},l={type:"function",function:{name:`${this.name}__post_message`,description:'Post a message to a Slack channel or DM. Can also reply to a thread by providing thread_ts. The bot must be a member of the channel to post. Both channel and text parameters are required.\n\n## Message Posting Rules\n- When interacting with a user in Slack, prefer a direct message with the user unless asked otherwise or called from a channel.\n- To mention a user, use `<@firstname.lastname>` or `<@id_of_user>`.\n- Always keep it short, under 10 lines of text.\n\n## Slack Message Formatting Constraints\n\n**What works:**\n- `*text*` for bold (single asterisk)\n- `_text_` for italic\n- `` `code` `` for inline code\n- ` ``` code block ``` ` for code blocks\n- Bullet lists with `-`\n- Numbered lists with `1. 2. 3.`\n- Direct URLs (automatically clickable): https://example.com\n- Emojis\n\n**Link formatting:**\n- To display text with a link: `<https://example.com|Link text>` (pipe separator)\n- Example: `<https://github.com/user/repo|View repository>` displays as "View repository" (clickable)\n- Avoid posting raw URLs when you can use descriptive link text\n\n**Best practices for Slack:**\n- Use single asterisks `*text*` for emphasis/titles\n- Prefer descriptive link text over raw URLs for better readability\n- For multiple links, use link text format to keep messages clean',parameters:{type:"object",properties:{channel:{type:"string",description:"Channel ID to post to. For DMs, use the user ID or DM channel ID. REQUIRED."},text:{type:"string",description:"Message text to post. Supports Slack markdown (bold, italic, links, etc.). REQUIRED."},thread_ts:{type:"string",description:"Optional: timestamp of parent message to reply in thread"}}},parse:JSON.parse,function:async p=>{try{if(!p.channel||!p.text)return"Error: both channel and text parameters are required.";let d={channel:p.channel,text:p.text};p.thread_ts&&(d.thread_ts=p.thread_ts);let m=await i("chat.postMessage",{},"POST",d);return JSON.stringify({success:!0,channel:m.channel,timestamp:m.ts,message:"Message posted successfully"})}catch(d){return`Error posting message: ${d instanceof Error?d.message:String(d)}`}}}},f={type:"function",function:{name:`${this.name}__list_users`,description:"List Slack workspace users. Useful for finding user IDs to send DMs or understanding who is mentioned in messages.",parameters:{type:"object",properties:{limit:{type:"number",description:"Maximum number of users to return (default: 100)"}}},parse:JSON.parse,function:async p=>{try{await a();let d=Array.from(g7.values()).filter(m=>!m.is_bot).slice(0,p.limit||100).map(m=>({id:m.id,name:m.name,realName:m.real_name}));return JSON.stringify(d,null,2)}catch(d){return`Error listing users: ${d instanceof Error?d.message:String(d)}`}}}};return t.push(s,c,u,l,f),t}};var IZ;(typeof navigator>"u"||!navigator.userAgent?.startsWith?.("Mozilla/5.0 "))&&(IZ="oauth4webapi/v3.8.3");function qZ(r,e){if(r==null)return!1;try{return r instanceof e||Object.getPrototypeOf(r)[Symbol.toStringTag]===e.prototype[Symbol.toStringTag]}catch{return!1}}var du="ERR_INVALID_ARG_VALUE",Pl="ERR_INVALID_ARG_TYPE";function pi(r,e,t){let n=new TypeError(r,{cause:t});return Object.assign(n,{code:e}),n}var Gve=Symbol(),yvt=Symbol(),bvt=Symbol(),Zve=Symbol(),J4r=Symbol(),DZ=Symbol(),K4r=Symbol(),xvt=new TextEncoder,wvt=new TextDecoder;function OZ(r){return typeof r=="string"?xvt.encode(r):wvt.decode(r)}var $Z;Uint8Array.prototype.toBase64?$Z=r=>(r instanceof ArrayBuffer&&(r=new Uint8Array(r)),r.toBase64({alphabet:"base64url",omitPadding:!0})):$Z=e=>{e instanceof ArrayBuffer&&(e=new Uint8Array(e));let t=[];for(let n=0;n<e.byteLength;n+=32768)t.push(String.fromCharCode.apply(null,e.subarray(n,n+32768)));return btoa(t.join("")).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")};var RZ;Uint8Array.fromBase64?RZ=r=>{try{return Uint8Array.fromBase64(r,{alphabet:"base64url"})}catch(e){throw pi("The input to be decoded is not correctly encoded.",du,e)}}:RZ=r=>{try{let e=atob(r.replace(/-/g,"+").replace(/_/g,"/").replace(/\s/g,"")),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);return t}catch(e){throw pi("The input to be decoded is not correctly encoded.",du,e)}};function XT(r){return typeof r=="string"?RZ(r):$Z(r)}var S4=class extends Error{code;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Yvt,Error.captureStackTrace?.(this,this.constructor)}},jZ=class extends Error{code;constructor(e,t){super(e,t),this.name=this.constructor.name,t?.code&&(this.code=t?.code),Error.captureStackTrace?.(this,this.constructor)}};function Nt(r,e,t){return new jZ(r,{code:e,cause:t})}function QT(r){return!(r===null||typeof r!="object"||Array.isArray(r))}function Jve(r){qZ(r,Headers)&&(r=Object.fromEntries(r.entries()));let e=new Headers(r??{});if(IZ&&!e.has("user-agent")&&e.set("user-agent",IZ),e.has("authorization"))throw pi('"options.headers" must not include the "authorization" header name',du);return e}function Kve(r,e){if(e!==void 0){if(typeof e=="function"&&(e=e(r.href)),!(e instanceof AbortSignal))throw pi('"options.signal" must return or be an instance of AbortSignal',Pl);return e}}function y7(r,e,t,n,i){try{if(typeof r!="number"||!Number.isFinite(r))throw pi(`${t} must be a number`,Pl,i);if(r>0)return;if(e){if(r!==0)throw pi(`${t} must be a non-negative number`,du,i);return}throw pi(`${t} must be a positive number`,du,i)}catch(a){throw n?Nt(a.message,n,i):a}}function Na(r,e,t,n){try{if(typeof r!="string")throw pi(`${e} must be a string`,Pl,n);if(r.length===0)throw pi(`${e} must not be empty`,du,n)}catch(i){throw t?Nt(i.message,t,n):i}}function Yve(r){Svt(r,"application/json")}function _vt(r,...e){let t='"response" content-type must be ';if(e.length>2){let n=e.pop();t+=`${e.join(", ")}, or ${n}`}else e.length===2?t+=`${e[0]} or ${e[1]}`:t+=e[0];return Nt(t,Qvt,r)}function Svt(r,e){if(Mvt(r)!==e)throw _vt(r,e)}function Xve(){return XT(crypto.getRandomValues(new Uint8Array(32)))}function rP(){return Xve()}function nP(){return Xve()}async function iP(r){return Na(r,"codeVerifier"),XT(await crypto.subtle.digest("SHA-256",OZ(r)))}function UZ(r){let e=r?.[yvt];return typeof e=="number"&&Number.isFinite(e)?e:0}function zZ(r){let e=r?.[bvt];return typeof e=="number"&&Number.isFinite(e)&&Math.sign(e)!==-1?e:30}function BZ(){return Math.floor(Date.now()/1e3)}function aP(r){if(typeof r!="object"||r===null)throw pi('"as" must be an object',Pl);Na(r.issuer,'"as.issuer"')}function oP(r){if(typeof r!="object"||r===null)throw pi('"client" must be an object',Pl);Na(r.client_id,'"client.client_id"')}function sP(r){return Na(r,'"clientSecret"'),(e,t,n,i)=>{n.set("client_id",t.client_id),n.set("client_secret",r)}}var kvt=URL.parse?(r,e)=>URL.parse(r,e):(r,e)=>{try{return new URL(r,e)}catch{return null}};function Qve(r,e){if(e&&r.protocol!=="https:")throw Nt("only requests to HTTPS are allowed",tyt,r);if(r.protocol!=="https:"&&r.protocol!=="http:")throw Nt("only HTTP and HTTPS requests are allowed",ryt,r)}function Vve(r,e,t,n){let i;if(typeof r!="string"||!(i=kvt(r)))throw Nt(`authorization server metadata does not contain a valid ${t?`"as.mtls_endpoint_aliases.${e}"`:`"as.${e}"`}`,r===void 0?nyt:iyt,{attribute:t?`mtls_endpoint_aliases.${e}`:e});return Qve(i,n),i}function Evt(r,e,t,n){return t&&r.mtls_endpoint_aliases&&e in r.mtls_endpoint_aliases?Vve(r.mtls_endpoint_aliases[e],e,t,n):Vve(r[e],e,t,n)}var NZ=class extends Error{cause;code;error;status;error_description;response;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Kvt,this.cause=t.cause,this.error=t.cause.error,this.status=t.response.status,this.error_description=t.cause.error_description,Object.defineProperty(this,"response",{enumerable:!1,value:t.response}),Error.captureStackTrace?.(this,this.constructor)}},FZ=class extends Error{cause;code;error;error_description;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Xvt,this.cause=t.cause,this.error=t.cause.get("error"),this.error_description=t.cause.get("error_description")??void 0,Error.captureStackTrace?.(this,this.constructor)}},MZ=class extends Error{cause;code;response;status;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Jvt,this.cause=t.cause,this.status=t.response.status,this.response=t.response,Object.defineProperty(this,"response",{enumerable:!1}),Error.captureStackTrace?.(this,this.constructor)}},eP="[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+",Cvt="[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}",Avt='"((?:[^"\\\\]|\\\\[\\s\\S])*)"',Tvt="("+eP+")\\s*=\\s*"+Avt,Pvt="("+eP+")\\s*=\\s*("+eP+")",Ivt=new RegExp("^[,\\s]*("+eP+")"),Dvt=new RegExp("^[,\\s]*"+Tvt+"[,\\s]*(.*)"),Ovt=new RegExp("^[,\\s]*"+Pvt+"[,\\s]*(.*)"),$vt=new RegExp("^("+Cvt+")(?:$|[,\\s])(.*)");function Rvt(r){if(!qZ(r,Response))throw pi('"response" must be an instance of Response',Pl);let e=r.headers.get("www-authenticate");if(e===null)return;let t=[],n=e;for(;n;){let i=n.match(Ivt),a=i?.[1].toLowerCase();if(!a)return;let o=n.substring(i[0].length);if(o&&!o.match(/^[\s,]/))return;let s=o.match(/^\s+(.*)$/),c=!!s;n=s?s[1]:void 0;let u={},l;if(c)for(;n;){let p,d;if(i=n.match(Dvt)){if([,p,d,n]=i,d.includes("\\"))try{d=JSON.parse(`"${d}"`)}catch{}u[p.toLowerCase()]=d;continue}if(i=n.match(Ovt)){[,p,d,n]=i,u[p.toLowerCase()]=d;continue}if(i=n.match($vt)){if(Object.keys(u).length)break;[,l,n]=i;break}return}else n=o||void 0;let f={scheme:a,parameters:u};l&&(f.token68=l),t.push(f)}if(t.length)return t}async function jvt(r){if(r.status>399&&r.status<500){oye(r),Yve(r);try{let e=await r.clone().json();if(QT(e)&&typeof e.error=="string"&&e.error.length)return e}catch{}}}async function Nvt(r,e,t){if(r.status!==e){aye(r);let n;throw(n=await jvt(r))?(await r.body?.cancel(),new NZ("server responded with an error in the response body",{cause:n,response:r})):Nt(`"response" is not a conform ${t} response (unexpected HTTP status code)`,eyt,r)}}function eye(r){if(!WZ.has(r))throw pi('"options.DPoP" is not a valid DPoPHandle',du)}async function Fvt(r,e,t,n,i,a){if(Na(r,'"accessToken"'),!(t instanceof URL))throw pi('"url" must be an instance of URL',Pl);Qve(t,a?.[Gve]!==!0),n=Jve(n),a?.DPoP&&(eye(a.DPoP),await a.DPoP.addProof(t,n,e.toUpperCase(),r)),n.set("authorization",`${n.has("dpop")?"DPoP":"Bearer"} ${r}`);let o=await(a?.[Zve]||fetch)(t.href,{body:i,headers:Object.fromEntries(n.entries()),method:e,redirect:"manual",signal:Kve(t,a?.signal)});return a?.DPoP?.cacheNonce(o,t),o}async function tye(r,e,t,n,i,a){let o=await Fvt(r,e,t,n,i,a);return aye(o),o}var Y4r=Symbol();function Mvt(r){return r.headers.get("content-type")?.split(";")[0]}async function Lvt(r,e,t,n,i,a,o){return await t(r,e,i,a),a.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"),(o?.[Zve]||fetch)(n.href,{body:i,headers:Object.fromEntries(a.entries()),method:"POST",redirect:"manual",signal:Kve(n,o?.signal)})}async function rye(r,e,t,n,i,a){let o=Evt(r,"token_endpoint",e.use_mtls_endpoint_aliases,a?.[Gve]!==!0);i.set("grant_type",n);let s=Jve(a?.headers);s.set("accept","application/json"),a?.DPoP!==void 0&&(eye(a.DPoP),await a.DPoP.addProof(o,s,"POST"));let c=await Lvt(r,e,t,o,i,s,a);return a?.DPoP?.cacheNonce(c,o),c}async function cP(r,e,t,n,i){aP(r),oP(e),Na(n,'"refreshToken"');let a=new URLSearchParams(i?.additionalParameters);return a.set("refresh_token",n),rye(r,e,t,"refresh_token",a,i)}var nye=new WeakMap,qvt=new WeakMap;function iye(r){if(!r.id_token)return;let e=nye.get(r);if(!e)throw pi('"ref" was already garbage collected or did not resolve from the proper sources',du);return e}async function HZ(r,e,t,n,i,a){if(aP(r),oP(e),!qZ(t,Response))throw pi('"response" must be an instance of Response',Pl);await Nvt(t,200,"Token Endpoint"),oye(t);let o=await uyt(t);if(Na(o.access_token,'"response" body "access_token" property',Gr,{body:o}),Na(o.token_type,'"response" body "token_type" property',Gr,{body:o}),o.token_type=o.token_type.toLowerCase(),o.expires_in!==void 0){let s=typeof o.expires_in!="number"?parseFloat(o.expires_in):o.expires_in;y7(s,!0,'"response" body "expires_in" property',Gr,{body:o}),o.expires_in=s}if(o.refresh_token!==void 0&&Na(o.refresh_token,'"response" body "refresh_token" property',Gr,{body:o}),o.scope!==void 0&&typeof o.scope!="string")throw Nt('"response" body "scope" property must be a string',Gr,{body:o});if(o.id_token!==void 0){Na(o.id_token,'"response" body "id_token" property',Gr,{body:o});let s=["aud","exp","iat","iss","sub"];e.require_auth_time===!0&&s.push("auth_time"),e.default_max_age!==void 0&&(y7(e.default_max_age,!0,'"client.default_max_age"'),s.push("auth_time")),n?.length&&s.push(...n);let{claims:c,jwt:u}=await ayt(o.id_token,oyt.bind(void 0,e.id_token_signed_response_alg,r.id_token_signing_alg_values_supported,"RS256"),UZ(e),zZ(e),i).then(Vvt.bind(void 0,s)).then(zvt.bind(void 0,r)).then(Uvt.bind(void 0,e.client_id));if(Array.isArray(c.aud)&&c.aud.length!==1){if(c.azp===void 0)throw Nt('ID Token "aud" (audience) claim includes additional untrusted audiences',Rf,{claims:c,claim:"aud"});if(c.azp!==e.client_id)throw Nt('unexpected ID Token "azp" (authorized party) claim value',Rf,{expected:e.client_id,claims:c,claim:"azp"})}c.auth_time!==void 0&&y7(c.auth_time,!0,'ID Token "auth_time" (authentication time)',Gr,{claims:c}),qvt.set(t,u),nye.set(o,c)}if(a?.[o.token_type]!==void 0)a[o.token_type](t,o);else if(o.token_type!=="dpop"&&o.token_type!=="bearer")throw new S4("unsupported `token_type` value",{cause:{body:o}});return o}function aye(r){let e;if(e=Rvt(r))throw new MZ("server responded with a challenge in the WWW-Authenticate HTTP Header",{cause:e,response:r})}async function uP(r,e,t,n){return HZ(r,e,t,void 0,n?.[DZ],n?.recognizedTokenTypes)}function Uvt(r,e){if(Array.isArray(e.claims.aud)){if(!e.claims.aud.includes(r))throw Nt('unexpected JWT "aud" (audience) claim value',Rf,{expected:r,claims:e.claims,claim:"aud"})}else if(e.claims.aud!==r)throw Nt('unexpected JWT "aud" (audience) claim value',Rf,{expected:r,claims:e.claims,claim:"aud"});return e}function zvt(r,e){let t=r[lyt]?.(e)??r.issuer;if(e.claims.iss!==t)throw Nt('unexpected JWT "iss" (issuer) claim value',Rf,{expected:t,claims:e.claims,claim:"iss"});return e}var WZ=new WeakSet;function Bvt(r){return WZ.add(r),r}var Hvt=Symbol();async function lP(r,e,t,n,i,a,o){if(aP(r),oP(e),!WZ.has(n))throw pi('"callbackParameters" must be an instance of URLSearchParams obtained from "validateAuthResponse()", or "validateJwtAuthResponse()',du);Na(i,'"redirectUri"');let s=i3(n,"code");if(!s)throw Nt('no authorization code in "callbackParameters"',Gr);let c=new URLSearchParams(o?.additionalParameters);return c.set("redirect_uri",i),c.set("code",s),a!==Hvt&&(Na(a,'"codeVerifier"'),c.set("code_verifier",a)),rye(r,e,t,"authorization_code",c,o)}var Wvt={aud:"audience",c_hash:"code hash",client_id:"client id",exp:"expiration time",iat:"issued at",iss:"issuer",jti:"jwt id",nonce:"nonce",s_hash:"state hash",sub:"subject",ath:"access token hash",htm:"http method",htu:"http uri",cnf:"confirmation",auth_time:"authentication time"};function Vvt(r,e){for(let t of r)if(e.claims[t]===void 0)throw Nt(`JWT "${t}" (${Wvt[t]}) claim missing`,Gr,{claims:e.claims});return e}var TZ=Symbol(),PZ=Symbol();async function fP(r,e,t,n){return typeof n?.expectedNonce=="string"||typeof n?.maxAge=="number"||n?.requireIdToken?Gvt(r,e,t,n.expectedNonce,n.maxAge,n[DZ],n.recognizedTokenTypes):Zvt(r,e,t,n?.[DZ],n?.recognizedTokenTypes)}async function Gvt(r,e,t,n,i,a,o){let s=[];switch(n){case void 0:n=TZ;break;case TZ:break;default:Na(n,'"expectedNonce" argument'),s.push("nonce")}switch(i??=e.default_max_age,i){case void 0:i=PZ;break;case PZ:break;default:y7(i,!0,'"maxAge" argument'),s.push("auth_time")}let c=await HZ(r,e,t,s,a,o);Na(c.id_token,'"response" body "id_token" property',Gr,{body:c});let u=iye(c);if(i!==PZ){let l=BZ()+UZ(e),f=zZ(e);if(u.auth_time+i<l-f)throw Nt("too much time has elapsed since the last End-User authentication",tP,{claims:u,now:l,tolerance:f,claim:"auth_time"})}if(n===TZ){if(u.nonce!==void 0)throw Nt('unexpected ID Token "nonce" claim value',Rf,{expected:void 0,claims:u,claim:"nonce"})}else if(u.nonce!==n)throw Nt('unexpected ID Token "nonce" claim value',Rf,{expected:n,claims:u,claim:"nonce"});return c}async function Zvt(r,e,t,n,i){let a=await HZ(r,e,t,void 0,n,i),o=iye(a);if(o){if(e.default_max_age!==void 0){y7(e.default_max_age,!0,'"client.default_max_age"');let s=BZ()+UZ(e),c=zZ(e);if(o.auth_time+e.default_max_age<s-c)throw Nt("too much time has elapsed since the last End-User authentication",tP,{claims:o,now:s,tolerance:c,claim:"auth_time"})}if(o.nonce!==void 0)throw Nt('unexpected ID Token "nonce" claim value',Rf,{expected:void 0,claims:o,claim:"nonce"})}return a}var Jvt="OAUTH_WWW_AUTHENTICATE_CHALLENGE",Kvt="OAUTH_RESPONSE_BODY_ERROR",Yvt="OAUTH_UNSUPPORTED_OPERATION",Xvt="OAUTH_AUTHORIZATION_RESPONSE_ERROR";var LZ="OAUTH_PARSE_ERROR",Gr="OAUTH_INVALID_RESPONSE";var Qvt="OAUTH_RESPONSE_IS_NOT_JSON",eyt="OAUTH_RESPONSE_IS_NOT_CONFORM",tyt="OAUTH_HTTP_REQUEST_FORBIDDEN",ryt="OAUTH_REQUEST_PROTOCOL_FORBIDDEN",tP="OAUTH_JWT_TIMESTAMP_CHECK_FAILED",Rf="OAUTH_JWT_CLAIM_COMPARISON_FAILED";var nyt="OAUTH_MISSING_SERVER_METADATA",iyt="OAUTH_INVALID_SERVER_METADATA";function oye(r){if(r.bodyUsed)throw pi('"response" body has been used already',du)}async function ayt(r,e,t,n,i){let{0:a,1:o,length:s}=r.split(".");if(s===5)if(i!==void 0)r=await i(r),{0:a,1:o,length:s}=r.split(".");else throw new S4("JWE decryption is not configured",{cause:r});if(s!==3)throw Nt("Invalid JWT",Gr,r);let c;try{c=JSON.parse(OZ(XT(a)))}catch(f){throw Nt("failed to parse JWT Header body as base64url encoded JSON",LZ,f)}if(!QT(c))throw Nt("JWT Header must be a top level object",Gr,r);if(e(c),c.crit!==void 0)throw new S4('no JWT "crit" header parameter extensions are supported',{cause:{header:c}});let u;try{u=JSON.parse(OZ(XT(o)))}catch(f){throw Nt("failed to parse JWT Payload body as base64url encoded JSON",LZ,f)}if(!QT(u))throw Nt("JWT Payload must be a top level object",Gr,r);let l=BZ()+t;if(u.exp!==void 0){if(typeof u.exp!="number")throw Nt('unexpected JWT "exp" (expiration time) claim type',Gr,{claims:u});if(u.exp<=l-n)throw Nt('unexpected JWT "exp" (expiration time) claim value, expiration is past current timestamp',tP,{claims:u,now:l,tolerance:n,claim:"exp"})}if(u.iat!==void 0&&typeof u.iat!="number")throw Nt('unexpected JWT "iat" (issued at) claim type',Gr,{claims:u});if(u.iss!==void 0&&typeof u.iss!="string")throw Nt('unexpected JWT "iss" (issuer) claim type',Gr,{claims:u});if(u.nbf!==void 0){if(typeof u.nbf!="number")throw Nt('unexpected JWT "nbf" (not before) claim type',Gr,{claims:u});if(u.nbf>l+n)throw Nt('unexpected JWT "nbf" (not before) claim value',tP,{claims:u,now:l,tolerance:n,claim:"nbf"})}if(u.aud!==void 0&&typeof u.aud!="string"&&!Array.isArray(u.aud))throw Nt('unexpected JWT "aud" (audience) claim type',Gr,{claims:u});return{header:c,claims:u,jwt:r}}function oyt(r,e,t,n){if(r!==void 0){if(typeof r=="string"?n.alg!==r:!r.includes(n.alg))throw Nt('unexpected JWT "alg" header parameter',Gr,{header:n,expected:r,reason:"client configuration"});return}if(Array.isArray(e)){if(!e.includes(n.alg))throw Nt('unexpected JWT "alg" header parameter',Gr,{header:n,expected:e,reason:"authorization server metadata"});return}if(t!==void 0){if(typeof t=="string"?n.alg!==t:typeof t=="function"?!t(n.alg):!t.includes(n.alg))throw Nt('unexpected JWT "alg" header parameter',Gr,{header:n,expected:t,reason:"default value"});return}throw Nt('missing client or server configuration to verify used JWT "alg" header parameter',void 0,{client:r,issuer:e,fallback:t})}function i3(r,e){let{0:t,length:n}=r.getAll(e);if(n>1)throw Nt(`"${e}" parameter must be provided only once`,Gr);return t}var syt=Symbol(),cyt=Symbol();function pP(r,e,t,n){if(aP(r),oP(e),t instanceof URL&&(t=t.searchParams),!(t instanceof URLSearchParams))throw pi('"parameters" must be an instance of URLSearchParams, or URL',Pl);if(i3(t,"response"))throw Nt('"parameters" contains a JARM response, use validateJwtAuthResponse() instead of validateAuthResponse()',Gr,{parameters:t});let i=i3(t,"iss"),a=i3(t,"state");if(!i&&r.authorization_response_iss_parameter_supported)throw Nt('response parameter "iss" (issuer) missing',Gr,{parameters:t});if(i&&i!==r.issuer)throw Nt('unexpected "iss" (issuer) response parameter value',Gr,{expected:r.issuer,parameters:t});switch(n){case void 0:case cyt:if(a!==void 0)throw Nt('unexpected "state" response parameter encountered',Gr,{expected:void 0,parameters:t});break;case syt:break;default:if(Na(n,'"expectedState" argument'),a!==n)throw Nt(a===void 0?'response parameter "state" missing':'unexpected "state" response parameter value',Gr,{expected:n,parameters:t})}if(i3(t,"error"))throw new FZ("authorization response from the server is an error",{cause:t});let s=i3(t,"id_token"),c=i3(t,"token");if(s!==void 0||c!==void 0)throw new S4("implicit and hybrid flows are not supported");return Bvt(new URLSearchParams(t))}async function uyt(r,e=Yve){let t;try{t=await r.json()}catch(n){throw e(r),Nt('failed to parse "response" body as JSON',LZ,n)}if(!QT(t))throw Nt('"response" body must be a top level object',Gr,{body:t});return t}var X4r=Symbol(),lyt=Symbol();var dP=class{constructor(e,t,n,i,a,o,s="BASECAMP"){this.redirectUri=n;this.interactor=i;this.userService=a;this.integrationName=s;this.resolvedProjectName=a.resolveProjectName(o),this.as={issuer:"https://launchpad.37signals.com",authorization_endpoint:"https://launchpad.37signals.com/authorization/new",token_endpoint:"https://launchpad.37signals.com/authorization/token"},this.client={client_id:e},this.clientAuth=sP(t)}as;client;clientAuth;tokenData=null;accounts=[];selectedAccountHref=null;pendingState=null;pendingCodeVerifier=null;pendingResolve=null;pendingReject=null;resolvedProjectName;isAuthenticated(){return this.tokenData||this.loadTokensFromStorage(),this.tokenData?this.tokenData.expiresAt>Date.now()+300*1e3:!1}async getAccessToken(){if(!this.tokenData)throw new Error("Not authenticated. Call authenticate() first.");return!this.isAuthenticated()&&this.tokenData.refreshToken&&await this.refreshToken(),this.tokenData.accessToken}getApiBaseUrl(){if(!this.selectedAccountHref)throw new Error("No account selected");return this.selectedAccountHref}async authenticate(){if(this.isAuthenticated())return this.tokenData;let e=nP(),t=rP(),n=await iP(t);this.pendingState=e,this.pendingCodeVerifier=t;let i=new URL(this.as.authorization_endpoint);return i.searchParams.set("client_id",this.client.client_id),i.searchParams.set("redirect_uri",this.redirectUri),i.searchParams.set("response_type","code"),i.searchParams.set("state",e),i.searchParams.set("code_challenge",n),i.searchParams.set("code_challenge_method","S256"),i.searchParams.set("type","web_server"),this.interactor.sendEvent(new Ic({authUrl:i.toString(),state:e,integrationName:"BASECAMP"})),new Promise((a,o)=>{this.pendingResolve=a,this.pendingReject=o})}async handleCallback(e){if(e.integrationName==="BASECAMP"){if(e.state!==this.pendingState){this.interactor.error("Invalid OAuth state");return}if(e.error){let t=e.error==="access_denied"?"OAuth authentication denied by user":e.error==="user_cancelled"?"OAuth authentication cancelled by user":`OAuth error: ${e.error}${e.errorDescription?" - "+e.errorDescription:""}`;this.interactor.warn(t),this.pendingReject&&(this.pendingReject(new Error(t)),this.pendingReject=null),this.pendingResolve=null,this.pendingState=null,this.pendingCodeVerifier=null;return}if(!e.code||!this.pendingCodeVerifier||!this.pendingState){this.interactor.error("No pending OAuth flow or missing code");return}try{let t=new URL(this.redirectUri);t.searchParams.set("code",e.code),t.searchParams.set("state",e.state);let n=pP(this.as,this.client,t,this.pendingState),i=await lP(this.as,this.client,this.clientAuth,n,this.redirectUri,this.pendingCodeVerifier,{additionalParameters:new URLSearchParams({type:"web_server"})});this.interactor.debug(`Token exchange response status: ${i.status}`);let a=await i.text();i=new Response(a,{status:i.status,statusText:i.statusText,headers:i.headers});let o;try{o=await fP(this.as,this.client,i),this.interactor.debug("oauth4webapi validation succeeded")}catch(s){if(this.interactor.debug(`Using manual parsing for Basecamp response: ${s.message}`),!i.ok)throw new Error(`Token exchange failed: ${i.status}`);o=JSON.parse(a),this.interactor.debug("Token response parsed successfully")}this.tokenData={accessToken:o.access_token,refreshToken:o.refresh_token,expiresAt:Date.now()+(o.expires_in??1209600)*1e3},await this.fetchAccounts(),this.saveTokensToStorage(),this.pendingResolve&&(this.pendingResolve(this.tokenData),this.pendingResolve=null),this.pendingState=null,this.pendingCodeVerifier=null}catch(t){this.interactor.error(`OAuth token exchange failed: ${t.message}`),this.pendingState=null,this.pendingCodeVerifier=null}}}async fetchAccounts(){if(this.tokenData)try{let e=await tye(this.tokenData.accessToken,"GET",new URL("https://launchpad.37signals.com/authorization.json"),void 0,void 0);if(!e.ok)return;let t=await e.json();if(this.accounts=t.accounts.filter(n=>n.product==="bc3").map(n=>({id:n.id,name:n.name,href:n.href})),this.accounts.length>0){let n=this.accounts[0];n&&(this.selectedAccountHref=n.href,this.interactor.displayText(`Connected to Basecamp account: ${n.name}`))}}catch(e){this.interactor.warn(`Failed to fetch Basecamp accounts: ${e.message}`)}}async refreshToken(){if(!this.tokenData?.refreshToken)throw new Error("No refresh token available");try{let e=await cP(this.as,this.client,this.clientAuth,this.tokenData.refreshToken,{additionalParameters:new URLSearchParams({type:"refresh"})}),t=await uP(this.as,this.client,e);this.tokenData={accessToken:t.access_token,refreshToken:t.refresh_token??this.tokenData.refreshToken,expiresAt:Date.now()+(t.expires_in??1209600)*1e3},this.saveTokensToStorage()}catch(e){throw this.tokenData=null,this.clearTokensFromStorage(),new Error(`Token refresh failed: ${e.message}`)}}loadTokensFromStorage(){let t=this.userService.config.projects?.[this.resolvedProjectName]?.integration?.[this.integrationName]?.oauth2,n=t?.tokens,i=t?.account_href,a=t?.account_name;if(n){this.tokenData={accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at},i&&(this.selectedAccountHref=i),this.interactor.debug(`Loaded OAuth tokens from storage for ${this.integrationName}`);let o=new Date(this.tokenData.expiresAt),s=new Date,c=this.tokenData.expiresAt-s.getTime(),u=Math.floor(c/(1e3*60*60*24)),l=Math.floor(c%(1e3*60*60*24)/(1e3*60*60));this.interactor.debug(`Token expires at: ${o.toLocaleString()} (in ${u}d ${l}h)`),a&&this.interactor.displayText(`Using stored Basecamp account: ${a}`)}}saveTokensToStorage(){if(!this.tokenData)return;let e=this.userService.config;e.projects||(e.projects={}),e.projects[this.resolvedProjectName]||(e.projects[this.resolvedProjectName]={integration:{}}),e.projects[this.resolvedProjectName].integration||(e.projects[this.resolvedProjectName].integration={}),e.projects[this.resolvedProjectName].integration[this.integrationName]||(e.projects[this.resolvedProjectName].integration[this.integrationName]={}),e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2||(e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2={});let t=e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2;t.tokens={access_token:this.tokenData.accessToken,refresh_token:this.tokenData.refreshToken,expires_at:this.tokenData.expiresAt},this.selectedAccountHref&&(t.account_href=this.selectedAccountHref);let n=this.accounts.find(u=>u.href===this.selectedAccountHref);n&&(t.account_name=n.name),this.userService.save();let i=new Date(this.tokenData.expiresAt),a=new Date,o=this.tokenData.expiresAt-a.getTime(),s=Math.floor(o/(1e3*60*60*24)),c=Math.floor(o%(1e3*60*60*24)/(1e3*60*60));this.interactor.debug(`Saved OAuth tokens to storage for ${this.integrationName}. Expires at: ${i.toLocaleString()} (in ${s}d ${c}h)`)}clearTokensFromStorage(){let t=this.userService.config.projects?.[this.resolvedProjectName]?.integration?.[this.integrationName]?.oauth2;t&&(delete t.tokens,delete t.account_href,delete t.account_name,this.userService.save(),this.interactor.debug(`Cleared OAuth tokens from storage for ${this.integrationName}`))}};async function cye(r,e){try{r.isAuthenticated()||await r.authenticate();let t=await r.getAccessToken(),n=r.getApiBaseUrl(),i=e?`${n}/projects.json?page=${e}`:`${n}/projects.json`,a=await fetch(i,{headers:{Authorization:`Bearer ${t}`,"User-Agent":"Coday (https://github.com/whoz-oss/coday)"}});if(!a.ok)return`Error fetching projects: ${a.status} ${a.statusText}`;let o=await a.json(),s=a.headers.get("X-Total-Count"),c=a.headers.get("Link"),u=null;if(c){let p=c.match(/page=(\d+)>; rel="next"/);p&&p[1]&&(u=parseInt(p[1],10))}if(o.length===0)return"No projects found in this Basecamp account.";let l=o.map(p=>{let d=(p.dock||[]).map(m=>` - ${m.title} (ID: ${m.id}, type: ${m.name})${m.enabled?"":" [disabled]"}`).join(`
1302
+ `,parameters:{type:"object",properties:{jql:{type:"string",description:"JQL query for counting issues"}}},parse:JSON.parse,function:async({jql:g})=>{let v=await this.jiraService.ensureInitialized();return v.isNewlyInitialized?{fieldMappingInfo:v.fieldMappingInfo,message:v.message}:(v.fieldMapping&&wZ(g,v.fieldMapping),Lve(g,a,s,o,this.interactor))}}},m={type:"function",function:{name:`${this.name}__postIssue`,description:"Create a new Jira issue/ticket without asking for more information from the user, directly call the function to create the ticket",parameters:{type:"object",properties:{request:{type:"object",description:"Request object containing all issue fields",properties:{projectKey:{type:"string",description:"Project key where the issue will be created"},summary:{type:"string",description:"Summary/title of the issue"},description:{type:"string",description:"Detailed description of the issue"},issuetype:{type:"string",description:"Type of issue"},assignee:{type:"string",description:"User ID of the assignee"},reporter:{type:"string",description:"User ID of the reporter"},priority:{type:"string",description:'Priority of the issue (e.g., "High", "Medium", "Low")'},labels:{type:"array",items:{type:"string"},description:"Labels to attach to the issue"},components:{type:"array",items:{type:"string"},description:"Components to associate with the issue"},fixVersions:{type:"array",items:{type:"string"},description:"Fix versions to associate with the issue"},duedate:{type:"string",description:"Due date in YYYY-MM-DD format"},parent:{type:"object",properties:{key:{type:"string",description:"The issue key of the parent"}},description:"Parent issue for this issue (directly establishes parent-child relationship)"},linkedIssues:{type:"array",items:{type:"object",properties:{key:{type:"string",description:"The key of the issue to link"},linkType:{type:"string",description:'The type of link to create (default: "is part of" for epics, "relates to" for other issue types)'},isEpicLink:{type:"boolean",description:"Whether to use Epic Link field if available (default: true for epics)"}}},description:"Issues to link to this issue after creation (especially useful for epics)"}}}}},parse:JSON.parse,function:async({request:g})=>{try{if(g.error&&g.partialRequest){let v=g.partialRequest,y=g.error||"Previous attempt failed";return this.interactor.displayText(`Retrying Jira issue creation with saved information. Previous error: ${y}`),await EZ(v,a,s,o,this.interactor)}return await EZ(g,a,s,o,this.interactor)}catch(v){throw this.interactor.error(`Error in createJiraIssue function: ${v}`),v}}}},h={type:"function",function:{name:`${this.name}__linkIssues`,description:"Link two Jira issues with a specified relationship type",parameters:{type:"object",properties:{inwardIssueKey:{type:"string",description:"The issue key that is the source of the link (inward issue)"},outwardIssueKey:{type:"string",description:"The issue key that is the target of the link (outward issue)"},linkType:{type:"string",description:'The type of link to create between issues (e.g., "relates to", "blocks", "is blocked by")'},comment:{type:"string",description:"Optional comment to add when creating the link"},isEpicLink:{type:"boolean",description:"Set to true to create an Epic-Issue relationship. This will attempt to use the Epic Link field if available, falling back to standard issue linking if not."}}},parse:JSON.parse,function:({inwardIssueKey:g,outwardIssueKey:v,linkType:y,comment:x,isEpicLink:_})=>_Z({inwardIssueKey:g,outwardIssueKey:v,linkType:y,comment:x,isEpicLink:_},a,s,o,this.interactor)}};return i.push(u),i.push(l),i.push(f),i.push(p),i.push(d),i.push(m),i.push(h),i}};KT();var g7=new Map,Wve=!1,v7=class extends Zt{constructor(t,n,i,a){super(t,i,a);this.integrationService=n}static TYPE="SLACK";async buildTools(){let t=[];if(!this.integrationService.hasIntegration(this.name))return console.log("failed to have integration slack"),t;let n=this.integrationService.getApiKey(this.name);if(!n)return console.log("no bot token"),t;let i=async(p,d={},m="GET",h)=>{let g=new URL(`https://slack.com/api/${p}`);m==="GET"&&Object.entries(d).forEach(([_,S])=>{g.searchParams.append(_,S)});let v={method:m,headers:{Authorization:`Bearer ${n}`,"Content-Type":m==="POST"?"application/json; charset=utf-8":"application/x-www-form-urlencoded"}};m==="POST"&&h&&(v.body=JSON.stringify(h));let x=await(await fetch(g.toString(),v)).json();if(!x.ok)throw new Error(`Slack API error: ${x.error||"Unknown error"}`);return x},a=async()=>{if(!Wve)try{let p;do{let d={limit:"200"};p&&(d.cursor=p);let m=await i("users.list",d);m.members?.forEach(h=>{h.deleted||g7.set(h.id,h)}),p=m.response_metadata?.next_cursor}while(p);Wve=!0}catch(p){this.interactor.debug(`Failed to populate user cache: ${p}`)}},o=async p=>(await a(),p.replace(/<@([A-Z0-9]+)>/g,(d,m)=>{let h=g7.get(m);return h?`@${h.real_name||h.name}`:d})),s={type:"function",function:{name:`${this.name}__list_channels`,description:"List Slack channels where the bot is a member. Only returns channels the bot has been invited to and can actually read/write. Returns channel ID, name, topic, and member count.",parameters:{type:"object",properties:{types:{type:"string",description:'Comma-separated list of channel types. Options: public_channel, private_channel, mpim (group DM), im (direct message). Default: "public_channel,private_channel"'},limit:{type:"number",description:"Maximum number of channels to return (default: 100, max: 1000)"}}},parse:JSON.parse,function:async p=>{try{let d={types:p.types||"public_channel,private_channel",limit:String(p.limit||100),exclude_archived:"true"},h=(await i("users.conversations",d)).channels||[];if(h.length===0)return"No channels found. The bot may need to be invited to channels first.";let g=h.map(v=>({id:v.id,name:v.name,private:v.is_private,topic:v.topic?.value||"",purpose:v.purpose?.value||"",members:v.num_members}));return JSON.stringify(g,null,2)}catch(d){return`Error listing channels: ${d instanceof Error?d.message:String(d)}`}}}},c={type:"function",function:{name:`${this.name}__read_channel`,description:"Read messages from a Slack channel. Returns messages with timestamps, user mentions resolved to names, and thread indicators. Use slack_list_channels first to get channel IDs. The channel parameter is required.",parameters:{type:"object",properties:{channel:{type:"string",description:"Channel ID (e.g., C1234567890). Get this from slack_list_channels. REQUIRED."},limit:{type:"number",description:"Number of messages to return (default: 20, max: 100)"},oldest:{type:"string",description:'Only messages after this Unix timestamp (e.g., "1234567890.123456")'},latest:{type:"string",description:"Only messages before this Unix timestamp"}}},parse:JSON.parse,function:async p=>{try{if(!p.channel)return"Error: channel parameter is required. Use slack_list_channels to get available channel IDs.";let d={channel:p.channel,limit:String(p.limit||20)};p.oldest&&(d.oldest=p.oldest),p.latest&&(d.latest=p.latest);let h=(await i("conversations.history",d)).messages||[];if(h.length===0)return"No messages found in this channel.";let g=await Promise.all(h.map(async v=>{let y=v.user?g7.get(v.user):void 0,x=await o(v.text);return{timestamp:v.ts,user:y?.real_name||y?.name||v.user||"Unknown",text:x,hasThread:!!v.thread_ts&&v.reply_count&&v.reply_count>0,replyCount:v.reply_count||0}}));return g.reverse(),JSON.stringify(g,null,2)}catch(d){return`Error reading channel: ${d instanceof Error?d.message:String(d)}`}}}},u={type:"function",function:{name:`${this.name}__read_thread`,description:"Read replies in a Slack thread. Use the timestamp from slack_read_channel to identify the thread. Returns all messages in the thread including the parent message. Both channel and thread_ts parameters are required.",parameters:{type:"object",properties:{channel:{type:"string",description:"Channel ID where the thread is located. REQUIRED."},thread_ts:{type:"string",description:"Timestamp of the parent message (thread_ts from slack_read_channel). REQUIRED."},limit:{type:"number",description:"Maximum number of replies to return (default: 50, max: 100)"}}},parse:JSON.parse,function:async p=>{try{if(!p.channel||!p.thread_ts)return"Error: both channel and thread_ts parameters are required.";let d={channel:p.channel,ts:p.thread_ts,limit:String(p.limit||50)},h=(await i("conversations.replies",d)).messages||[];if(h.length===0)return"No messages found in this thread.";let g=await Promise.all(h.map(async v=>{let y=v.user?g7.get(v.user):void 0,x=await o(v.text);return{timestamp:v.ts,user:y?.real_name||y?.name||v.user||"Unknown",text:x,isParent:v.ts===p.thread_ts}}));return JSON.stringify(g,null,2)}catch(d){return`Error reading thread: ${d instanceof Error?d.message:String(d)}`}}}},l={type:"function",function:{name:`${this.name}__post_message`,description:'Post a message to a Slack channel or DM. Can also reply to a thread by providing thread_ts. The bot must be a member of the channel to post. Both channel and text parameters are required.\n\n## Message Posting Rules\n- When interacting with a user in Slack, prefer a direct message with the user unless asked otherwise or called from a channel.\n- To mention a user, use `<@firstname.lastname>` or `<@id_of_user>`.\n- Always keep it short, under 10 lines of text.\n\n## Slack Message Formatting Constraints\n\n**What works:**\n- `*text*` for bold (single asterisk)\n- `_text_` for italic\n- `` `code` `` for inline code\n- ` ``` code block ``` ` for code blocks\n- Bullet lists with `-`\n- Numbered lists with `1. 2. 3.`\n- Direct URLs (automatically clickable): https://example.com\n- Emojis\n\n**Link formatting:**\n- To display text with a link: `<https://example.com|Link text>` (pipe separator)\n- Example: `<https://github.com/user/repo|View repository>` displays as "View repository" (clickable)\n- Avoid posting raw URLs when you can use descriptive link text\n\n**Best practices for Slack:**\n- Use single asterisks `*text*` for emphasis/titles\n- Prefer descriptive link text over raw URLs for better readability\n- For multiple links, use link text format to keep messages clean',parameters:{type:"object",properties:{channel:{type:"string",description:"Channel ID to post to. For DMs, use the user ID or DM channel ID. REQUIRED."},text:{type:"string",description:"Message text to post. Supports Slack markdown (bold, italic, links, etc.). REQUIRED."},thread_ts:{type:"string",description:"Optional: timestamp of parent message to reply in thread"}}},parse:JSON.parse,function:async p=>{try{if(!p.channel||!p.text)return"Error: both channel and text parameters are required.";let d={channel:p.channel,text:p.text};p.thread_ts&&(d.thread_ts=p.thread_ts);let m=await i("chat.postMessage",{},"POST",d);return JSON.stringify({success:!0,channel:m.channel,timestamp:m.ts,message:"Message posted successfully"})}catch(d){return`Error posting message: ${d instanceof Error?d.message:String(d)}`}}}},f={type:"function",function:{name:`${this.name}__list_users`,description:"List Slack workspace users. Useful for finding user IDs to send DMs or understanding who is mentioned in messages.",parameters:{type:"object",properties:{limit:{type:"number",description:"Maximum number of users to return (default: 100)"}}},parse:JSON.parse,function:async p=>{try{await a();let d=Array.from(g7.values()).filter(m=>!m.is_bot).slice(0,p.limit||100).map(m=>({id:m.id,name:m.name,realName:m.real_name}));return JSON.stringify(d,null,2)}catch(d){return`Error listing users: ${d instanceof Error?d.message:String(d)}`}}}};return t.push(s,c,u,l,f),t}};var IZ;(typeof navigator>"u"||!navigator.userAgent?.startsWith?.("Mozilla/5.0 "))&&(IZ="oauth4webapi/v3.8.3");function qZ(r,e){if(r==null)return!1;try{return r instanceof e||Object.getPrototypeOf(r)[Symbol.toStringTag]===e.prototype[Symbol.toStringTag]}catch{return!1}}var du="ERR_INVALID_ARG_VALUE",Pl="ERR_INVALID_ARG_TYPE";function pi(r,e,t){let n=new TypeError(r,{cause:t});return Object.assign(n,{code:e}),n}var Gve=Symbol(),yvt=Symbol(),bvt=Symbol(),Zve=Symbol(),J4r=Symbol(),DZ=Symbol(),K4r=Symbol(),xvt=new TextEncoder,wvt=new TextDecoder;function OZ(r){return typeof r=="string"?xvt.encode(r):wvt.decode(r)}var $Z;Uint8Array.prototype.toBase64?$Z=r=>(r instanceof ArrayBuffer&&(r=new Uint8Array(r)),r.toBase64({alphabet:"base64url",omitPadding:!0})):$Z=e=>{e instanceof ArrayBuffer&&(e=new Uint8Array(e));let t=[];for(let n=0;n<e.byteLength;n+=32768)t.push(String.fromCharCode.apply(null,e.subarray(n,n+32768)));return btoa(t.join("")).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")};var RZ;Uint8Array.fromBase64?RZ=r=>{try{return Uint8Array.fromBase64(r,{alphabet:"base64url"})}catch(e){throw pi("The input to be decoded is not correctly encoded.",du,e)}}:RZ=r=>{try{let e=atob(r.replace(/-/g,"+").replace(/_/g,"/").replace(/\s/g,"")),t=new Uint8Array(e.length);for(let n=0;n<e.length;n++)t[n]=e.charCodeAt(n);return t}catch(e){throw pi("The input to be decoded is not correctly encoded.",du,e)}};function XT(r){return typeof r=="string"?RZ(r):$Z(r)}var S4=class extends Error{code;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Yvt,Error.captureStackTrace?.(this,this.constructor)}},jZ=class extends Error{code;constructor(e,t){super(e,t),this.name=this.constructor.name,t?.code&&(this.code=t?.code),Error.captureStackTrace?.(this,this.constructor)}};function Nt(r,e,t){return new jZ(r,{code:e,cause:t})}function QT(r){return!(r===null||typeof r!="object"||Array.isArray(r))}function Jve(r){qZ(r,Headers)&&(r=Object.fromEntries(r.entries()));let e=new Headers(r??{});if(IZ&&!e.has("user-agent")&&e.set("user-agent",IZ),e.has("authorization"))throw pi('"options.headers" must not include the "authorization" header name',du);return e}function Kve(r,e){if(e!==void 0){if(typeof e=="function"&&(e=e(r.href)),!(e instanceof AbortSignal))throw pi('"options.signal" must return or be an instance of AbortSignal',Pl);return e}}function y7(r,e,t,n,i){try{if(typeof r!="number"||!Number.isFinite(r))throw pi(`${t} must be a number`,Pl,i);if(r>0)return;if(e){if(r!==0)throw pi(`${t} must be a non-negative number`,du,i);return}throw pi(`${t} must be a positive number`,du,i)}catch(a){throw n?Nt(a.message,n,i):a}}function Na(r,e,t,n){try{if(typeof r!="string")throw pi(`${e} must be a string`,Pl,n);if(r.length===0)throw pi(`${e} must not be empty`,du,n)}catch(i){throw t?Nt(i.message,t,n):i}}function Yve(r){Svt(r,"application/json")}function _vt(r,...e){let t='"response" content-type must be ';if(e.length>2){let n=e.pop();t+=`${e.join(", ")}, or ${n}`}else e.length===2?t+=`${e[0]} or ${e[1]}`:t+=e[0];return Nt(t,Qvt,r)}function Svt(r,e){if(Mvt(r)!==e)throw _vt(r,e)}function Xve(){return XT(crypto.getRandomValues(new Uint8Array(32)))}function rP(){return Xve()}function nP(){return Xve()}async function iP(r){return Na(r,"codeVerifier"),XT(await crypto.subtle.digest("SHA-256",OZ(r)))}function UZ(r){let e=r?.[yvt];return typeof e=="number"&&Number.isFinite(e)?e:0}function zZ(r){let e=r?.[bvt];return typeof e=="number"&&Number.isFinite(e)&&Math.sign(e)!==-1?e:30}function BZ(){return Math.floor(Date.now()/1e3)}function aP(r){if(typeof r!="object"||r===null)throw pi('"as" must be an object',Pl);Na(r.issuer,'"as.issuer"')}function oP(r){if(typeof r!="object"||r===null)throw pi('"client" must be an object',Pl);Na(r.client_id,'"client.client_id"')}function sP(r){return Na(r,'"clientSecret"'),(e,t,n,i)=>{n.set("client_id",t.client_id),n.set("client_secret",r)}}var kvt=URL.parse?(r,e)=>URL.parse(r,e):(r,e)=>{try{return new URL(r,e)}catch{return null}};function Qve(r,e){if(e&&r.protocol!=="https:")throw Nt("only requests to HTTPS are allowed",tyt,r);if(r.protocol!=="https:"&&r.protocol!=="http:")throw Nt("only HTTP and HTTPS requests are allowed",ryt,r)}function Vve(r,e,t,n){let i;if(typeof r!="string"||!(i=kvt(r)))throw Nt(`authorization server metadata does not contain a valid ${t?`"as.mtls_endpoint_aliases.${e}"`:`"as.${e}"`}`,r===void 0?nyt:iyt,{attribute:t?`mtls_endpoint_aliases.${e}`:e});return Qve(i,n),i}function Evt(r,e,t,n){return t&&r.mtls_endpoint_aliases&&e in r.mtls_endpoint_aliases?Vve(r.mtls_endpoint_aliases[e],e,t,n):Vve(r[e],e,t,n)}var NZ=class extends Error{cause;code;error;status;error_description;response;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Kvt,this.cause=t.cause,this.error=t.cause.error,this.status=t.response.status,this.error_description=t.cause.error_description,Object.defineProperty(this,"response",{enumerable:!1,value:t.response}),Error.captureStackTrace?.(this,this.constructor)}},FZ=class extends Error{cause;code;error;error_description;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Xvt,this.cause=t.cause,this.error=t.cause.get("error"),this.error_description=t.cause.get("error_description")??void 0,Error.captureStackTrace?.(this,this.constructor)}},MZ=class extends Error{cause;code;response;status;constructor(e,t){super(e,t),this.name=this.constructor.name,this.code=Jvt,this.cause=t.cause,this.status=t.response.status,this.response=t.response,Object.defineProperty(this,"response",{enumerable:!1}),Error.captureStackTrace?.(this,this.constructor)}},eP="[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+",Cvt="[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}",Avt='"((?:[^"\\\\]|\\\\[\\s\\S])*)"',Tvt="("+eP+")\\s*=\\s*"+Avt,Pvt="("+eP+")\\s*=\\s*("+eP+")",Ivt=new RegExp("^[,\\s]*("+eP+")"),Dvt=new RegExp("^[,\\s]*"+Tvt+"[,\\s]*(.*)"),Ovt=new RegExp("^[,\\s]*"+Pvt+"[,\\s]*(.*)"),$vt=new RegExp("^("+Cvt+")(?:$|[,\\s])(.*)");function Rvt(r){if(!qZ(r,Response))throw pi('"response" must be an instance of Response',Pl);let e=r.headers.get("www-authenticate");if(e===null)return;let t=[],n=e;for(;n;){let i=n.match(Ivt),a=i?.[1].toLowerCase();if(!a)return;let o=n.substring(i[0].length);if(o&&!o.match(/^[\s,]/))return;let s=o.match(/^\s+(.*)$/),c=!!s;n=s?s[1]:void 0;let u={},l;if(c)for(;n;){let p,d;if(i=n.match(Dvt)){if([,p,d,n]=i,d.includes("\\"))try{d=JSON.parse(`"${d}"`)}catch{}u[p.toLowerCase()]=d;continue}if(i=n.match(Ovt)){[,p,d,n]=i,u[p.toLowerCase()]=d;continue}if(i=n.match($vt)){if(Object.keys(u).length)break;[,l,n]=i;break}return}else n=o||void 0;let f={scheme:a,parameters:u};l&&(f.token68=l),t.push(f)}if(t.length)return t}async function jvt(r){if(r.status>399&&r.status<500){oye(r),Yve(r);try{let e=await r.clone().json();if(QT(e)&&typeof e.error=="string"&&e.error.length)return e}catch{}}}async function Nvt(r,e,t){if(r.status!==e){aye(r);let n;throw(n=await jvt(r))?(await r.body?.cancel(),new NZ("server responded with an error in the response body",{cause:n,response:r})):Nt(`"response" is not a conform ${t} response (unexpected HTTP status code)`,eyt,r)}}function eye(r){if(!WZ.has(r))throw pi('"options.DPoP" is not a valid DPoPHandle',du)}async function Fvt(r,e,t,n,i,a){if(Na(r,'"accessToken"'),!(t instanceof URL))throw pi('"url" must be an instance of URL',Pl);Qve(t,a?.[Gve]!==!0),n=Jve(n),a?.DPoP&&(eye(a.DPoP),await a.DPoP.addProof(t,n,e.toUpperCase(),r)),n.set("authorization",`${n.has("dpop")?"DPoP":"Bearer"} ${r}`);let o=await(a?.[Zve]||fetch)(t.href,{body:i,headers:Object.fromEntries(n.entries()),method:e,redirect:"manual",signal:Kve(t,a?.signal)});return a?.DPoP?.cacheNonce(o,t),o}async function tye(r,e,t,n,i,a){let o=await Fvt(r,e,t,n,i,a);return aye(o),o}var Y4r=Symbol();function Mvt(r){return r.headers.get("content-type")?.split(";")[0]}async function Lvt(r,e,t,n,i,a,o){return await t(r,e,i,a),a.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"),(o?.[Zve]||fetch)(n.href,{body:i,headers:Object.fromEntries(a.entries()),method:"POST",redirect:"manual",signal:Kve(n,o?.signal)})}async function rye(r,e,t,n,i,a){let o=Evt(r,"token_endpoint",e.use_mtls_endpoint_aliases,a?.[Gve]!==!0);i.set("grant_type",n);let s=Jve(a?.headers);s.set("accept","application/json"),a?.DPoP!==void 0&&(eye(a.DPoP),await a.DPoP.addProof(o,s,"POST"));let c=await Lvt(r,e,t,o,i,s,a);return a?.DPoP?.cacheNonce(c,o),c}async function cP(r,e,t,n,i){aP(r),oP(e),Na(n,'"refreshToken"');let a=new URLSearchParams(i?.additionalParameters);return a.set("refresh_token",n),rye(r,e,t,"refresh_token",a,i)}var nye=new WeakMap,qvt=new WeakMap;function iye(r){if(!r.id_token)return;let e=nye.get(r);if(!e)throw pi('"ref" was already garbage collected or did not resolve from the proper sources',du);return e}async function HZ(r,e,t,n,i,a){if(aP(r),oP(e),!qZ(t,Response))throw pi('"response" must be an instance of Response',Pl);await Nvt(t,200,"Token Endpoint"),oye(t);let o=await uyt(t);if(Na(o.access_token,'"response" body "access_token" property',Gr,{body:o}),Na(o.token_type,'"response" body "token_type" property',Gr,{body:o}),o.token_type=o.token_type.toLowerCase(),o.expires_in!==void 0){let s=typeof o.expires_in!="number"?parseFloat(o.expires_in):o.expires_in;y7(s,!0,'"response" body "expires_in" property',Gr,{body:o}),o.expires_in=s}if(o.refresh_token!==void 0&&Na(o.refresh_token,'"response" body "refresh_token" property',Gr,{body:o}),o.scope!==void 0&&typeof o.scope!="string")throw Nt('"response" body "scope" property must be a string',Gr,{body:o});if(o.id_token!==void 0){Na(o.id_token,'"response" body "id_token" property',Gr,{body:o});let s=["aud","exp","iat","iss","sub"];e.require_auth_time===!0&&s.push("auth_time"),e.default_max_age!==void 0&&(y7(e.default_max_age,!0,'"client.default_max_age"'),s.push("auth_time")),n?.length&&s.push(...n);let{claims:c,jwt:u}=await ayt(o.id_token,oyt.bind(void 0,e.id_token_signed_response_alg,r.id_token_signing_alg_values_supported,"RS256"),UZ(e),zZ(e),i).then(Vvt.bind(void 0,s)).then(zvt.bind(void 0,r)).then(Uvt.bind(void 0,e.client_id));if(Array.isArray(c.aud)&&c.aud.length!==1){if(c.azp===void 0)throw Nt('ID Token "aud" (audience) claim includes additional untrusted audiences',Rf,{claims:c,claim:"aud"});if(c.azp!==e.client_id)throw Nt('unexpected ID Token "azp" (authorized party) claim value',Rf,{expected:e.client_id,claims:c,claim:"azp"})}c.auth_time!==void 0&&y7(c.auth_time,!0,'ID Token "auth_time" (authentication time)',Gr,{claims:c}),qvt.set(t,u),nye.set(o,c)}if(a?.[o.token_type]!==void 0)a[o.token_type](t,o);else if(o.token_type!=="dpop"&&o.token_type!=="bearer")throw new S4("unsupported `token_type` value",{cause:{body:o}});return o}function aye(r){let e;if(e=Rvt(r))throw new MZ("server responded with a challenge in the WWW-Authenticate HTTP Header",{cause:e,response:r})}async function uP(r,e,t,n){return HZ(r,e,t,void 0,n?.[DZ],n?.recognizedTokenTypes)}function Uvt(r,e){if(Array.isArray(e.claims.aud)){if(!e.claims.aud.includes(r))throw Nt('unexpected JWT "aud" (audience) claim value',Rf,{expected:r,claims:e.claims,claim:"aud"})}else if(e.claims.aud!==r)throw Nt('unexpected JWT "aud" (audience) claim value',Rf,{expected:r,claims:e.claims,claim:"aud"});return e}function zvt(r,e){let t=r[lyt]?.(e)??r.issuer;if(e.claims.iss!==t)throw Nt('unexpected JWT "iss" (issuer) claim value',Rf,{expected:t,claims:e.claims,claim:"iss"});return e}var WZ=new WeakSet;function Bvt(r){return WZ.add(r),r}var Hvt=Symbol();async function lP(r,e,t,n,i,a,o){if(aP(r),oP(e),!WZ.has(n))throw pi('"callbackParameters" must be an instance of URLSearchParams obtained from "validateAuthResponse()", or "validateJwtAuthResponse()',du);Na(i,'"redirectUri"');let s=i3(n,"code");if(!s)throw Nt('no authorization code in "callbackParameters"',Gr);let c=new URLSearchParams(o?.additionalParameters);return c.set("redirect_uri",i),c.set("code",s),a!==Hvt&&(Na(a,'"codeVerifier"'),c.set("code_verifier",a)),rye(r,e,t,"authorization_code",c,o)}var Wvt={aud:"audience",c_hash:"code hash",client_id:"client id",exp:"expiration time",iat:"issued at",iss:"issuer",jti:"jwt id",nonce:"nonce",s_hash:"state hash",sub:"subject",ath:"access token hash",htm:"http method",htu:"http uri",cnf:"confirmation",auth_time:"authentication time"};function Vvt(r,e){for(let t of r)if(e.claims[t]===void 0)throw Nt(`JWT "${t}" (${Wvt[t]}) claim missing`,Gr,{claims:e.claims});return e}var TZ=Symbol(),PZ=Symbol();async function fP(r,e,t,n){return typeof n?.expectedNonce=="string"||typeof n?.maxAge=="number"||n?.requireIdToken?Gvt(r,e,t,n.expectedNonce,n.maxAge,n[DZ],n.recognizedTokenTypes):Zvt(r,e,t,n?.[DZ],n?.recognizedTokenTypes)}async function Gvt(r,e,t,n,i,a,o){let s=[];switch(n){case void 0:n=TZ;break;case TZ:break;default:Na(n,'"expectedNonce" argument'),s.push("nonce")}switch(i??=e.default_max_age,i){case void 0:i=PZ;break;case PZ:break;default:y7(i,!0,'"maxAge" argument'),s.push("auth_time")}let c=await HZ(r,e,t,s,a,o);Na(c.id_token,'"response" body "id_token" property',Gr,{body:c});let u=iye(c);if(i!==PZ){let l=BZ()+UZ(e),f=zZ(e);if(u.auth_time+i<l-f)throw Nt("too much time has elapsed since the last End-User authentication",tP,{claims:u,now:l,tolerance:f,claim:"auth_time"})}if(n===TZ){if(u.nonce!==void 0)throw Nt('unexpected ID Token "nonce" claim value',Rf,{expected:void 0,claims:u,claim:"nonce"})}else if(u.nonce!==n)throw Nt('unexpected ID Token "nonce" claim value',Rf,{expected:n,claims:u,claim:"nonce"});return c}async function Zvt(r,e,t,n,i){let a=await HZ(r,e,t,void 0,n,i),o=iye(a);if(o){if(e.default_max_age!==void 0){y7(e.default_max_age,!0,'"client.default_max_age"');let s=BZ()+UZ(e),c=zZ(e);if(o.auth_time+e.default_max_age<s-c)throw Nt("too much time has elapsed since the last End-User authentication",tP,{claims:o,now:s,tolerance:c,claim:"auth_time"})}if(o.nonce!==void 0)throw Nt('unexpected ID Token "nonce" claim value',Rf,{expected:void 0,claims:o,claim:"nonce"})}return a}var Jvt="OAUTH_WWW_AUTHENTICATE_CHALLENGE",Kvt="OAUTH_RESPONSE_BODY_ERROR",Yvt="OAUTH_UNSUPPORTED_OPERATION",Xvt="OAUTH_AUTHORIZATION_RESPONSE_ERROR";var LZ="OAUTH_PARSE_ERROR",Gr="OAUTH_INVALID_RESPONSE";var Qvt="OAUTH_RESPONSE_IS_NOT_JSON",eyt="OAUTH_RESPONSE_IS_NOT_CONFORM",tyt="OAUTH_HTTP_REQUEST_FORBIDDEN",ryt="OAUTH_REQUEST_PROTOCOL_FORBIDDEN",tP="OAUTH_JWT_TIMESTAMP_CHECK_FAILED",Rf="OAUTH_JWT_CLAIM_COMPARISON_FAILED";var nyt="OAUTH_MISSING_SERVER_METADATA",iyt="OAUTH_INVALID_SERVER_METADATA";function oye(r){if(r.bodyUsed)throw pi('"response" body has been used already',du)}async function ayt(r,e,t,n,i){let{0:a,1:o,length:s}=r.split(".");if(s===5)if(i!==void 0)r=await i(r),{0:a,1:o,length:s}=r.split(".");else throw new S4("JWE decryption is not configured",{cause:r});if(s!==3)throw Nt("Invalid JWT",Gr,r);let c;try{c=JSON.parse(OZ(XT(a)))}catch(f){throw Nt("failed to parse JWT Header body as base64url encoded JSON",LZ,f)}if(!QT(c))throw Nt("JWT Header must be a top level object",Gr,r);if(e(c),c.crit!==void 0)throw new S4('no JWT "crit" header parameter extensions are supported',{cause:{header:c}});let u;try{u=JSON.parse(OZ(XT(o)))}catch(f){throw Nt("failed to parse JWT Payload body as base64url encoded JSON",LZ,f)}if(!QT(u))throw Nt("JWT Payload must be a top level object",Gr,r);let l=BZ()+t;if(u.exp!==void 0){if(typeof u.exp!="number")throw Nt('unexpected JWT "exp" (expiration time) claim type',Gr,{claims:u});if(u.exp<=l-n)throw Nt('unexpected JWT "exp" (expiration time) claim value, expiration is past current timestamp',tP,{claims:u,now:l,tolerance:n,claim:"exp"})}if(u.iat!==void 0&&typeof u.iat!="number")throw Nt('unexpected JWT "iat" (issued at) claim type',Gr,{claims:u});if(u.iss!==void 0&&typeof u.iss!="string")throw Nt('unexpected JWT "iss" (issuer) claim type',Gr,{claims:u});if(u.nbf!==void 0){if(typeof u.nbf!="number")throw Nt('unexpected JWT "nbf" (not before) claim type',Gr,{claims:u});if(u.nbf>l+n)throw Nt('unexpected JWT "nbf" (not before) claim value',tP,{claims:u,now:l,tolerance:n,claim:"nbf"})}if(u.aud!==void 0&&typeof u.aud!="string"&&!Array.isArray(u.aud))throw Nt('unexpected JWT "aud" (audience) claim type',Gr,{claims:u});return{header:c,claims:u,jwt:r}}function oyt(r,e,t,n){if(r!==void 0){if(typeof r=="string"?n.alg!==r:!r.includes(n.alg))throw Nt('unexpected JWT "alg" header parameter',Gr,{header:n,expected:r,reason:"client configuration"});return}if(Array.isArray(e)){if(!e.includes(n.alg))throw Nt('unexpected JWT "alg" header parameter',Gr,{header:n,expected:e,reason:"authorization server metadata"});return}if(t!==void 0){if(typeof t=="string"?n.alg!==t:typeof t=="function"?!t(n.alg):!t.includes(n.alg))throw Nt('unexpected JWT "alg" header parameter',Gr,{header:n,expected:t,reason:"default value"});return}throw Nt('missing client or server configuration to verify used JWT "alg" header parameter',void 0,{client:r,issuer:e,fallback:t})}function i3(r,e){let{0:t,length:n}=r.getAll(e);if(n>1)throw Nt(`"${e}" parameter must be provided only once`,Gr);return t}var syt=Symbol(),cyt=Symbol();function pP(r,e,t,n){if(aP(r),oP(e),t instanceof URL&&(t=t.searchParams),!(t instanceof URLSearchParams))throw pi('"parameters" must be an instance of URLSearchParams, or URL',Pl);if(i3(t,"response"))throw Nt('"parameters" contains a JARM response, use validateJwtAuthResponse() instead of validateAuthResponse()',Gr,{parameters:t});let i=i3(t,"iss"),a=i3(t,"state");if(!i&&r.authorization_response_iss_parameter_supported)throw Nt('response parameter "iss" (issuer) missing',Gr,{parameters:t});if(i&&i!==r.issuer)throw Nt('unexpected "iss" (issuer) response parameter value',Gr,{expected:r.issuer,parameters:t});switch(n){case void 0:case cyt:if(a!==void 0)throw Nt('unexpected "state" response parameter encountered',Gr,{expected:void 0,parameters:t});break;case syt:break;default:if(Na(n,'"expectedState" argument'),a!==n)throw Nt(a===void 0?'response parameter "state" missing':'unexpected "state" response parameter value',Gr,{expected:n,parameters:t})}if(i3(t,"error"))throw new FZ("authorization response from the server is an error",{cause:t});let s=i3(t,"id_token"),c=i3(t,"token");if(s!==void 0||c!==void 0)throw new S4("implicit and hybrid flows are not supported");return Bvt(new URLSearchParams(t))}async function uyt(r,e=Yve){let t;try{t=await r.json()}catch(n){throw e(r),Nt('failed to parse "response" body as JSON',LZ,n)}if(!QT(t))throw Nt('"response" body must be a top level object',Gr,{body:t});return t}var X4r=Symbol(),lyt=Symbol();var dP=class{constructor(e,t,n,i,a,o,s="BASECAMP"){this.redirectUri=n;this.interactor=i;this.userService=a;this.integrationName=s;this.resolvedProjectName=a.resolveProjectName(o),this.as={issuer:"https://launchpad.37signals.com",authorization_endpoint:"https://launchpad.37signals.com/authorization/new",token_endpoint:"https://launchpad.37signals.com/authorization/token"},this.client={client_id:e},this.clientAuth=sP(t)}as;client;clientAuth;tokenData=null;accounts=[];selectedAccountHref=null;pendingState=null;pendingCodeVerifier=null;pendingResolve=null;pendingReject=null;resolvedProjectName;isAuthenticated(){return this.tokenData||this.loadTokensFromStorage(),this.tokenData?this.tokenData.expiresAt>Date.now()+300*1e3:!1}async getAccessToken(){if(!this.tokenData)throw new Error("Not authenticated. Call authenticate() first.");return!this.isAuthenticated()&&this.tokenData.refreshToken&&await this.refreshToken(),this.tokenData.accessToken}getApiBaseUrl(){if(!this.selectedAccountHref)throw new Error("No account selected");return this.selectedAccountHref}async authenticate(){if(this.isAuthenticated())return this.tokenData;if(this.tokenData?.refreshToken)try{return this.interactor.debug("Access token expired, attempting silent refresh..."),await this.refreshToken(),this.interactor.debug("Token refreshed successfully"),this.tokenData}catch(a){this.interactor.debug(`Silent refresh failed (${a.message}), starting full re-authentication`)}let e=nP(),t=rP(),n=await iP(t);this.pendingState=e,this.pendingCodeVerifier=t;let i=new URL(this.as.authorization_endpoint);return i.searchParams.set("client_id",this.client.client_id),i.searchParams.set("redirect_uri",this.redirectUri),i.searchParams.set("response_type","code"),i.searchParams.set("state",e),i.searchParams.set("code_challenge",n),i.searchParams.set("code_challenge_method","S256"),i.searchParams.set("type","web_server"),this.interactor.sendEvent(new Ic({authUrl:i.toString(),state:e,integrationName:"BASECAMP"})),new Promise((a,o)=>{this.pendingResolve=a,this.pendingReject=o})}async handleCallback(e){if(e.integrationName==="BASECAMP"){if(e.state!==this.pendingState){this.interactor.error("Invalid OAuth state");return}if(e.error){let t=e.error==="access_denied"?"OAuth authentication denied by user":e.error==="user_cancelled"?"OAuth authentication cancelled by user":`OAuth error: ${e.error}${e.errorDescription?" - "+e.errorDescription:""}`;this.interactor.warn(t),this.pendingReject&&(this.pendingReject(new Error(t)),this.pendingReject=null),this.pendingResolve=null,this.pendingState=null,this.pendingCodeVerifier=null;return}if(!e.code||!this.pendingCodeVerifier||!this.pendingState){this.interactor.error("No pending OAuth flow or missing code");return}try{let t=new URL(this.redirectUri);t.searchParams.set("code",e.code),t.searchParams.set("state",e.state);let n=pP(this.as,this.client,t,this.pendingState),i=await lP(this.as,this.client,this.clientAuth,n,this.redirectUri,this.pendingCodeVerifier,{additionalParameters:new URLSearchParams({type:"web_server"})});this.interactor.debug(`Token exchange response status: ${i.status}`);let a=await i.text();i=new Response(a,{status:i.status,statusText:i.statusText,headers:i.headers});let o;try{o=await fP(this.as,this.client,i),this.interactor.debug("oauth4webapi validation succeeded")}catch(s){if(this.interactor.debug(`Using manual parsing for Basecamp response: ${s.message}`),!i.ok)throw new Error(`Token exchange failed: ${i.status}`);o=JSON.parse(a),this.interactor.debug("Token response parsed successfully")}this.tokenData={accessToken:o.access_token,refreshToken:o.refresh_token,expiresAt:Date.now()+(o.expires_in??1209600)*1e3},await this.fetchAccounts(),this.saveTokensToStorage(),this.pendingResolve&&(this.pendingResolve(this.tokenData),this.pendingResolve=null),this.pendingState=null,this.pendingCodeVerifier=null}catch(t){this.interactor.error(`OAuth token exchange failed: ${t.message}`),this.pendingState=null,this.pendingCodeVerifier=null}}}async fetchAccounts(){if(this.tokenData)try{let e=await tye(this.tokenData.accessToken,"GET",new URL("https://launchpad.37signals.com/authorization.json"),void 0,void 0);if(!e.ok)return;let t=await e.json();if(this.accounts=t.accounts.filter(n=>n.product==="bc3").map(n=>({id:n.id,name:n.name,href:n.href})),this.accounts.length>0){let n=this.accounts[0];n&&(this.selectedAccountHref=n.href,this.interactor.displayText(`Connected to Basecamp account: ${n.name}`))}}catch(e){this.interactor.warn(`Failed to fetch Basecamp accounts: ${e.message}`)}}async refreshToken(){if(!this.tokenData?.refreshToken)throw new Error("No refresh token available");try{let e=await cP(this.as,this.client,this.clientAuth,this.tokenData.refreshToken,{additionalParameters:new URLSearchParams({type:"refresh"})}),t=await uP(this.as,this.client,e);this.tokenData={accessToken:t.access_token,refreshToken:t.refresh_token??this.tokenData.refreshToken,expiresAt:Date.now()+(t.expires_in??1209600)*1e3},this.saveTokensToStorage()}catch(e){throw this.tokenData=null,this.clearTokensFromStorage(),new Error(`Token refresh failed: ${e.message}`)}}loadTokensFromStorage(){let t=this.userService.config.projects?.[this.resolvedProjectName]?.integration?.[this.integrationName]?.oauth2,n=t?.tokens,i=t?.account_href,a=t?.account_name;if(n){this.tokenData={accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at},i&&(this.selectedAccountHref=i),this.interactor.debug(`Loaded OAuth tokens from storage for ${this.integrationName}`);let o=new Date(this.tokenData.expiresAt),s=new Date,c=this.tokenData.expiresAt-s.getTime(),u=Math.floor(c/(1e3*60*60*24)),l=Math.floor(c%(1e3*60*60*24)/(1e3*60*60));this.interactor.debug(`Token expires at: ${o.toLocaleString()} (in ${u}d ${l}h)`),a&&this.interactor.displayText(`Using stored Basecamp account: ${a}`)}}saveTokensToStorage(){if(!this.tokenData)return;let e=this.userService.config;e.projects||(e.projects={}),e.projects[this.resolvedProjectName]||(e.projects[this.resolvedProjectName]={integration:{}}),e.projects[this.resolvedProjectName].integration||(e.projects[this.resolvedProjectName].integration={}),e.projects[this.resolvedProjectName].integration[this.integrationName]||(e.projects[this.resolvedProjectName].integration[this.integrationName]={}),e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2||(e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2={});let t=e.projects[this.resolvedProjectName].integration[this.integrationName].oauth2;t.tokens={access_token:this.tokenData.accessToken,refresh_token:this.tokenData.refreshToken,expires_at:this.tokenData.expiresAt},this.selectedAccountHref&&(t.account_href=this.selectedAccountHref);let n=this.accounts.find(u=>u.href===this.selectedAccountHref);n&&(t.account_name=n.name),this.userService.save();let i=new Date(this.tokenData.expiresAt),a=new Date,o=this.tokenData.expiresAt-a.getTime(),s=Math.floor(o/(1e3*60*60*24)),c=Math.floor(o%(1e3*60*60*24)/(1e3*60*60));this.interactor.debug(`Saved OAuth tokens to storage for ${this.integrationName}. Expires at: ${i.toLocaleString()} (in ${s}d ${c}h)`)}clearTokensFromStorage(){let t=this.userService.config.projects?.[this.resolvedProjectName]?.integration?.[this.integrationName]?.oauth2;t&&(delete t.tokens,delete t.account_href,delete t.account_name,this.userService.save(),this.interactor.debug(`Cleared OAuth tokens from storage for ${this.integrationName}`))}};async function cye(r,e){try{r.isAuthenticated()||await r.authenticate();let t=await r.getAccessToken(),n=r.getApiBaseUrl(),i=e?`${n}/projects.json?page=${e}`:`${n}/projects.json`,a=await fetch(i,{headers:{Authorization:`Bearer ${t}`,"User-Agent":"Coday (https://github.com/whoz-oss/coday)"}});if(!a.ok)return`Error fetching projects: ${a.status} ${a.statusText}`;let o=await a.json(),s=a.headers.get("X-Total-Count"),c=a.headers.get("Link"),u=null;if(c){let p=c.match(/page=(\d+)>; rel="next"/);p&&p[1]&&(u=parseInt(p[1],10))}if(o.length===0)return"No projects found in this Basecamp account.";let l=o.map(p=>{let d=(p.dock||[]).map(m=>` - ${m.title} (ID: ${m.id}, type: ${m.name})${m.enabled?"":" [disabled]"}`).join(`
1303
1303
  `);return`- ${p.name} (ID: ${p.id})${p.description?`: ${p.description}`:""}${d?`
1304
1304
  Tools:
1305
1305
  ${d}`:""}`}).join(`
@@ -1415,7 +1415,7 @@ ${this.services.memory.getFormattedMemories("PROJECT",n.name)}
1415
1415
 
1416
1416
  ${l}
1417
1417
 
1418
- `;let p=n.integrations?new Map(Object.entries(n.integrations).map(([x,_])=>{let S=_&&_.length>0?_:[];return[x,S]})):void 0;this.interactor.debug(`Agent '${n.name}' integrations: ${p?Array.from(p.keys()).join(", "):"ALL (undefined)"}`);let d=performance.now(),m=await this.toolbox.getTools({context:t,integrations:p,agentName:n.name}),h=performance.now()-d;this.interactor.debug(` \u{1F4E6} Tools available for '${n.name}': ${m.map(x=>x.function.name).join(", ")}`);let g=new Tw([...m]),v=new kw(n,o,g),y=performance.now()-i;return this.interactor.debug(`\u2728 Agent '${n.name}' created: ${y.toFixed(2)}ms (client: ${s.toFixed(2)}ms, docs: ${f.toFixed(2)}ms, tools: ${h.toFixed(2)}ms)`),v}catch(a){let o=`Failed to create agent ${n.name}`;console.error(`${o}:`,a),this.interactor.error(o);return}}};var pyt=100,vP=class{constructor(e,t,n){this.interactor=e;this.options=t;this.services=n;this.interactor.debugLevelEnabled=t.debug,this.interactor.debug("Coday started with debug"),this.configHandler=new hE(e,this.services),this.maxIterations=pyt,this.aiThreadService=new iE(n.user,n.thread.getThreadRepository(t.project),t.project,e),this.aiThreadService.activeThread.subscribe(i=>{!this.context||!i||(this.context.aiThread=i,this.replayThread(i))}),this.aiClientProvider=new Gm(this.interactor,this.services.user,this.services.project,this.services.logger),this.interactor.events.pipe((0,ZZ.filter)(i=>i instanceof Fi||i instanceof Mi||i instanceof Sr),(0,ZZ.filter)(()=>!this.isReplaying)).subscribe(i=>{i instanceof Fi||i instanceof Mi?this.pendingQuestionEvent=i:i instanceof Sr&&this.pendingQuestionEvent&&i.parentKey===this.pendingQuestionEvent.timestamp&&(this.pendingQuestionEvent=null)})}context=null;configHandler;handlerLooper;aiHandler;maxIterations;initialPrompts=[];aiThreadService;killed=!1;isReplaying=!1;pendingQuestionEvent=null;messageQueue=[];aiClientProvider;addUserMessage(e,t){if(this.pendingQuestionEvent){let n=this.pendingQuestionEvent.buildAnswer(t);n.name=e,this.interactor.sendEvent(n)}else{let n=new jt({role:"user",content:[{type:"text",content:t}],name:e});this.interactor.sendEvent(n),this.messageQueue.push({username:e,message:t})}}replay(){let e=this.aiThreadService.getCurrentThread();if(!e){console.log("No active thread to replay");return}this.replayThread(e),!this.options.oneshot&&e.runStatus!=="RUNNING"&&this.interactor.replayLastInvite()}upload(e){let t=this.aiThreadService.getCurrentThread();if(!t){this.interactor.error("No active thread available for upload");return}let n=this.services.user.username;e.forEach(a=>{t.addUserMessage(n,a)});let i=new jt({role:"user",content:e,name:n});this.interactor.sendEvent(i)}async run(){this.initialPrompts=this.options.prompts?[...this.options.prompts]:[],this.interactor.debug(`[CODAY] Starting run with ${this.initialPrompts.length} initial prompts`);try{do{if(this.killed)return;if(await this.initContext(),await this.initThread(),!this.context){this.interactor.error("Could not initialize context \u{1F62D}");break}let e=await this.initCommand();if(!e&&this.options.oneshot)break;let t=this.context?.aiThread;t&&(t.runStatus="RUNNING"),this.context?.addCommands(e),this.context=await this.handlerLooper?.handle(this.context)??null}while(!this.context?.oneshot)}finally{this.stop()}await this.cleanup()}stop(){let e=this.context?.aiThread;e&&(e.runStatus="STOPPED"),this.handlerLooper?.stop(),this.aiThreadService.autoSave()}async cleanup(){try{this.services.agent&&await this.services.agent.kill(),this.aiThreadService.kill(),this.aiClientProvider.cleanup(),this.context=null,this.handlerLooper=void 0,this.aiHandler=void 0,this.messageQueue=[],this.pendingQuestionEvent=null}catch(e){console.error("Error during agent cleanup:",e)}}async kill(){this.killed=!0,this.stop();try{await this.cleanup()}catch(e){console.error("Error during kill cleanup:",e)}this.handlerLooper?.kill(),this.interactor.kill()}async replayThread(e){this.isReplaying=!0;try{let t=(await e.getMessages(void 0,void 0)).messages;if(!t?.length)return;let n=[...t].sort((i,a)=>new Date(i.timestamp).getTime()-new Date(a.timestamp).getTime());for(let i of n)(i instanceof jt||i instanceof hn||i instanceof Tn||i instanceof Fi||i instanceof Mi||i instanceof Sr)&&this.interactor.sendEvent(i);this.interactor.displayText(`Selected thread '${e.name}'`)}finally{this.isReplaying=!1}}async initContext(){if(this.context){this.interactor.debug("[CODAY] Context already initialized, skipping");return}if(this.interactor.debug(`[CODAY] Initializing context for project: ${this.options.project}`),this.context=await this.configHandler.selectProjectHandler.selectProject(this.options.project),!this.context){console.log("[CODAY] ERROR: Failed to create context from selectProject");return}console.log(`[CODAY] Context initialized: '${this.context.project.name}' at ${this.context.project.root}`),this.context&&(this.context.oneshot=this.options.oneshot,this.context.fileReadOnly=this.options.fileReadOnly,this.options.thread&&this.options.project&&(this.context.threadFilesRoot=_ye.join(this.options.configDir,"projects",this.options.project,"threads",`${this.options.thread}-files`),this.interactor.debug(`[CODAY] Thread files root: ${this.context.threadFilesRoot}`)),this.services.aiConfig=new Wk(this.services.user,this.services.project),this.services.aiConfig.initialize(this.context),this.services.mcp.initialize(this.context),this.services.agent=new k4(this.interactor,this.aiClientProvider,this.services,this.context.project.root,this.options.agentFolders),this.aiHandler=new OE(this.interactor,this.services.agent,this.aiThreadService),this.handlerLooper=new DE(this.interactor,this.aiHandler,this.configHandler,this.services),this.aiClientProvider.init(this.context),await this.handlerLooper.init(this.context.project),console.log("[CODAY] Initializing services..."),await this.services.agent.initialize(this.context),console.log("[CODAY] Services initialized"))}async initThread(){if(!this.context?.aiThread){if(!this.options.thread)throw Error("No thread given, cannot start Coday instance");await this.aiThreadService.select(this.options.thread)}}async initCommand(){let e;if(this.interactor.debug(`[CODAY] initCommand: ${this.initialPrompts.length} prompts available, oneshot=${this.options.oneshot}`),this.initialPrompts.length)e=this.initialPrompts.shift(),this.interactor.debug(`[CODAY] Using initial prompt: ${e}`),this.initialPrompts.length&&(this.interactor.debug(`[CODAY] Adding ${this.initialPrompts.length} remaining prompts to queue`),this.context?.addCommands(...this.initialPrompts),this.initialPrompts=[]);else if(this.messageQueue.length>0){let t=this.messageQueue.shift();this.interactor.debug(`[CODAY] Consuming queued message from ${t.username}`),e=t.message,this.context&&(this.context.username=t.username)}else this.options.oneshot?this.interactor.debug("[CODAY] No initial prompts and oneshot mode, exiting"):(this.interactor.debug("[CODAY] No initial prompts, waiting for user input"),e=await this.interactor.promptText(moe),this.context&&this.interactor.lastAnswerName&&(this.context.username=this.interactor.lastAnswerName));return e}};function fe(r,...e){console.log(`${new Date().toISOString()} ${r}`,...e)}var dyt=4,yP=class{constructor(e,t){this.aiClient=e;this.threadService=t}process(e,t){this.run(e,t).catch(n=>fe("POST_PROCESSOR",`Unhandled error for thread ${e.id}:`,n))}async run(e,t){let n=e.getUserMessageCount();if(n<dyt){fe("POST_PROCESSOR",`Skipping thread ${e.id}: only ${n} user message(s)`);return}fe("POST_PROCESSOR",`Processing thread ${e.id} (${n} user messages)`);let{messages:i}=await e.getMessages(void 0,void 0),a=i.filter(f=>f instanceof jt).slice(-40).map(f=>{let p=f.role==="user"?"User":f.name||"Assistant",d=f.content.filter(m=>m.type==="text").map(m=>m.content).join(" ").slice(0,500);return`${p}: ${d}`});if(!a.length){fe("POST_PROCESSOR",`Skipping thread ${e.id}: no text messages`);return}let s=`You are given a conversation transcript. Reply ONLY with a valid JSON object (no markdown, no explanation) with two fields:
1418
+ `;let p=n.integrations?new Map(Object.entries(n.integrations).map(([x,_])=>{let S=_&&_.length>0?_:[];return[x,S]})):void 0;this.interactor.debug(`Agent '${n.name}' integrations: ${p?Array.from(p.keys()).join(", "):"ALL (undefined)"}`);let d=performance.now(),m=await this.toolbox.getTools({context:t,integrations:p,agentName:n.name}),h=performance.now()-d;this.interactor.debug(` \u{1F4E6} Tools available for '${n.name}': ${m.map(x=>x.function.name).join(", ")}`);let g=new Tw([...m]),v=new kw(n,o,g),y=performance.now()-i;return this.interactor.debug(`\u2728 Agent '${n.name}' created: ${y.toFixed(2)}ms (client: ${s.toFixed(2)}ms, docs: ${f.toFixed(2)}ms, tools: ${h.toFixed(2)}ms)`),v}catch(a){let o=`Failed to create agent ${n.name}`;console.error(`${o}:`,a),this.interactor.error(o);return}}};var pyt=100,vP=class{constructor(e,t,n){this.interactor=e;this.options=t;this.services=n;this.interactor.debugLevelEnabled=t.debug,this.interactor.debug("Coday started with debug"),this.configHandler=new hE(e,this.services),this.maxIterations=pyt,this.aiThreadService=new iE(n.user,n.thread.getThreadRepository(t.project),t.project,e),this.aiThreadService.activeThread.subscribe(i=>{!this.context||!i||(this.context.aiThread=i,this.replayThread(i))}),this.aiClientProvider=new Gm(this.interactor,this.services.user,this.services.project,this.services.logger),this.interactor.events.pipe((0,ZZ.filter)(i=>i instanceof Fi||i instanceof Mi||i instanceof Sr),(0,ZZ.filter)(()=>!this.isReplaying)).subscribe(i=>{i instanceof Fi||i instanceof Mi?this.pendingQuestionEvent=i:i instanceof Sr&&this.pendingQuestionEvent&&i.parentKey===this.pendingQuestionEvent.timestamp&&(this.pendingQuestionEvent=null)})}context=null;configHandler;handlerLooper;aiHandler;maxIterations;initialPrompts=[];aiThreadService;killed=!1;isReplaying=!1;pendingQuestionEvent=null;messageQueue=[];aiClientProvider;addUserMessage(e,t){if(this.pendingQuestionEvent){let n=this.pendingQuestionEvent.buildAnswer(t);n.name=e,this.interactor.sendEvent(n)}else{let n=new jt({role:"user",content:[{type:"text",content:t}],name:e});this.interactor.sendEvent(n),this.messageQueue.push({username:e,message:t})}}replay(){let e=this.aiThreadService.getCurrentThread();if(!e){console.log("No active thread to replay");return}this.replayThread(e),!this.options.oneshot&&e.runStatus!=="RUNNING"&&this.interactor.replayLastInvite()}upload(e){let t=this.aiThreadService.getCurrentThread();if(!t){this.interactor.error("No active thread available for upload");return}let n=this.services.user.username;e.forEach(a=>{t.addUserMessage(n,a)});let i=new jt({role:"user",content:e,name:n});this.interactor.sendEvent(i)}async run(){this.initialPrompts=this.options.prompts?[...this.options.prompts]:[],this.interactor.debug(`[CODAY] Starting run with ${this.initialPrompts.length} initial prompts`);try{do{if(this.killed)return;if(await this.initContext(),await this.initThread(),!this.context){this.interactor.error("Could not initialize context \u{1F62D}");break}let e=await this.initCommand();if(!e&&this.options.oneshot)break;let t=this.context?.aiThread;t&&(t.runStatus="RUNNING"),this.context?.addCommands(e),this.context=await this.handlerLooper?.handle(this.context)??null}while(!this.context?.oneshot)}finally{this.stop()}await this.cleanup()}stop(){let e=this.context?.aiThread;e&&(e.runStatus="STOPPED"),this.handlerLooper?.stop(),this.aiThreadService.autoSave()}async cleanup(){try{this.services.agent&&await this.services.agent.kill(),this.aiThreadService.kill(),this.aiClientProvider.cleanup(),this.context=null,this.handlerLooper=void 0,this.aiHandler=void 0,this.messageQueue=[],this.pendingQuestionEvent=null}catch(e){console.error("Error during agent cleanup:",e)}}async kill(){this.killed=!0,this.stop();try{await this.cleanup()}catch(e){console.error("Error during kill cleanup:",e)}this.handlerLooper?.kill(),this.interactor.kill()}async replayThread(e){this.isReplaying=!0;try{let t=(await e.getMessages(void 0,void 0)).messages;if(!t?.length)return;let n=[...t].sort((i,a)=>new Date(i.timestamp).getTime()-new Date(a.timestamp).getTime());for(let i of n)(i instanceof jt||i instanceof hn||i instanceof Tn||i instanceof Fi||i instanceof Mi||i instanceof Sr)&&this.interactor.sendEvent(i);this.interactor.displayText(`Selected thread '${e.name}'`)}finally{this.isReplaying=!1}}async initContext(){if(this.context){this.interactor.debug("[CODAY] Context already initialized, skipping");return}if(this.interactor.debug(`[CODAY] Initializing context for project: ${this.options.project}`),this.context=await this.configHandler.selectProjectHandler.selectProject(this.options.project),!this.context){console.log("[CODAY] ERROR: Failed to create context from selectProject");return}console.log(`[CODAY] Context initialized: '${this.context.project.name}' at ${this.context.project.root}`),this.context&&(this.context.oneshot=this.options.oneshot,this.context.fileReadOnly=this.options.fileReadOnly,this.options.thread&&this.options.project&&(this.context.threadFilesRoot=_ye.join(this.options.configDir,"projects",this.options.project,"threads",`${this.options.thread}-files`),this.interactor.debug(`[CODAY] Thread files root: ${this.context.threadFilesRoot}`)),this.services.aiConfig=new Wk(this.services.user,this.services.project),this.services.aiConfig.initialize(this.context),this.services.mcp.initialize(this.context),this.services.agent=new k4(this.interactor,this.aiClientProvider,this.services,this.context.project.root,this.options.agentFolders),this.aiHandler=new OE(this.interactor,this.services.agent,this.aiThreadService),this.handlerLooper=new DE(this.interactor,this.aiHandler,this.configHandler,this.services),this.aiClientProvider.init(this.context),await this.handlerLooper.init(this.context.project),console.log("[CODAY] Initializing services..."),await this.services.agent.initialize(this.context),console.log("[CODAY] Services initialized"))}async initThread(){if(this.context?.aiThread)return;if(!this.options.thread)throw Error("No thread given, cannot start Coday instance");let e=await this.aiThreadService.select(this.options.thread);this.context&&(this.context.aiThread=e)}async initCommand(){let e;if(this.interactor.debug(`[CODAY] initCommand: ${this.initialPrompts.length} prompts available, oneshot=${this.options.oneshot}`),this.initialPrompts.length)e=this.initialPrompts.shift(),this.interactor.debug(`[CODAY] Using initial prompt: ${e}`),this.initialPrompts.length&&(this.interactor.debug(`[CODAY] Adding ${this.initialPrompts.length} remaining prompts to queue`),this.context?.addCommands(...this.initialPrompts),this.initialPrompts=[]);else if(this.messageQueue.length>0){let t=this.messageQueue.shift();this.interactor.debug(`[CODAY] Consuming queued message from ${t.username}`),e=t.message,this.context&&(this.context.username=t.username)}else this.options.oneshot?this.interactor.debug("[CODAY] No initial prompts and oneshot mode, exiting"):(this.interactor.debug("[CODAY] No initial prompts, waiting for user input"),e=await this.interactor.promptText(moe),this.context&&this.interactor.lastAnswerName&&(this.context.username=this.interactor.lastAnswerName));return e}};function fe(r,...e){console.log(`${new Date().toISOString()} ${r}`,...e)}var dyt=4,yP=class{constructor(e,t){this.aiClient=e;this.threadService=t}process(e,t){this.run(e,t).catch(n=>fe("POST_PROCESSOR",`Unhandled error for thread ${e.id}:`,n))}async run(e,t){let n=e.getUserMessageCount();if(n<dyt){fe("POST_PROCESSOR",`Skipping thread ${e.id}: only ${n} user message(s)`);return}fe("POST_PROCESSOR",`Processing thread ${e.id} (${n} user messages)`);let{messages:i}=await e.getMessages(void 0,void 0),a=i.filter(f=>f instanceof jt).slice(-40).map(f=>{let p=f.role==="user"?"User":f.name||"Assistant",d=f.content.filter(m=>m.type==="text").map(m=>m.content).join(" ").slice(0,500);return`${p}: ${d}`});if(!a.length){fe("POST_PROCESSOR",`Skipping thread ${e.id}: no text messages`);return}let s=`You are given a conversation transcript. Reply ONLY with a valid JSON object (no markdown, no explanation) with two fields:
1419
1419
  - "name": a short title for the conversation (max 60 chars, no quotes)
1420
1420
  - "summary": 2-4 sentences summarising the main topics and outcomes
1421
1421