@sudocode-ai/local-server 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +6 -104
- package/dist/cli.js.map +1 -7
- package/dist/execution/engine/engine.js +10 -0
- package/dist/execution/engine/engine.js.map +1 -0
- package/dist/execution/engine/simple-engine.js +611 -0
- package/dist/execution/engine/simple-engine.js.map +1 -0
- package/dist/execution/engine/types.js +10 -0
- package/dist/execution/engine/types.js.map +1 -0
- package/dist/execution/output/ag-ui-adapter.js +438 -0
- package/dist/execution/output/ag-ui-adapter.js.map +1 -0
- package/dist/execution/output/ag-ui-integration.js +96 -0
- package/dist/execution/output/ag-ui-integration.js.map +1 -0
- package/dist/execution/output/claude-code-output-processor.js +769 -0
- package/dist/execution/output/claude-code-output-processor.js.map +1 -0
- package/dist/execution/output/index.js +15 -0
- package/dist/execution/output/index.js.map +1 -0
- package/dist/execution/output/types.js +22 -0
- package/dist/execution/output/types.js.map +1 -0
- package/dist/execution/process/builders/claude.js +59 -0
- package/dist/execution/process/builders/claude.js.map +1 -0
- package/dist/execution/process/index.js +15 -0
- package/dist/execution/process/index.js.map +1 -0
- package/dist/execution/process/manager.js +10 -0
- package/dist/execution/process/manager.js.map +1 -0
- package/dist/execution/process/simple-manager.js +336 -0
- package/dist/execution/process/simple-manager.js.map +1 -0
- package/dist/execution/process/types.js +10 -0
- package/dist/execution/process/types.js.map +1 -0
- package/dist/execution/process/utils.js +97 -0
- package/dist/execution/process/utils.js.map +1 -0
- package/dist/execution/resilience/circuit-breaker.js +291 -0
- package/dist/execution/resilience/circuit-breaker.js.map +1 -0
- package/dist/execution/resilience/executor.js +10 -0
- package/dist/execution/resilience/executor.js.map +1 -0
- package/dist/execution/resilience/index.js +15 -0
- package/dist/execution/resilience/index.js.map +1 -0
- package/dist/execution/resilience/resilient-executor.js +261 -0
- package/dist/execution/resilience/resilient-executor.js.map +1 -0
- package/dist/execution/resilience/retry.js +234 -0
- package/dist/execution/resilience/retry.js.map +1 -0
- package/dist/execution/resilience/types.js +30 -0
- package/dist/execution/resilience/types.js.map +1 -0
- package/dist/execution/transport/event-buffer.js +208 -0
- package/dist/execution/transport/event-buffer.js.map +1 -0
- package/dist/execution/transport/index.js +10 -0
- package/dist/execution/transport/index.js.map +1 -0
- package/dist/execution/transport/sse-transport.js +282 -0
- package/dist/execution/transport/sse-transport.js.map +1 -0
- package/dist/execution/transport/transport-manager.js +231 -0
- package/dist/execution/transport/transport-manager.js.map +1 -0
- package/dist/execution/workflow/index.js +13 -0
- package/dist/execution/workflow/index.js.map +1 -0
- package/dist/execution/workflow/linear-orchestrator.js +683 -0
- package/dist/execution/workflow/linear-orchestrator.js.map +1 -0
- package/dist/execution/workflow/memory-storage.js +68 -0
- package/dist/execution/workflow/memory-storage.js.map +1 -0
- package/dist/execution/workflow/orchestrator.js +9 -0
- package/dist/execution/workflow/orchestrator.js.map +1 -0
- package/dist/execution/workflow/types.js +9 -0
- package/dist/execution/workflow/types.js.map +1 -0
- package/dist/execution/workflow/utils.js +152 -0
- package/dist/execution/workflow/utils.js.map +1 -0
- package/dist/execution/worktree/config.js +280 -0
- package/dist/execution/worktree/config.js.map +1 -0
- package/dist/execution/worktree/git-cli.js +189 -0
- package/dist/execution/worktree/git-cli.js.map +1 -0
- package/dist/execution/worktree/index.js +15 -0
- package/dist/execution/worktree/index.js.map +1 -0
- package/dist/execution/worktree/manager.js +452 -0
- package/dist/execution/worktree/manager.js.map +1 -0
- package/dist/execution/worktree/types.js +42 -0
- package/dist/execution/worktree/types.js.map +1 -0
- package/dist/index.js +356 -104
- package/dist/index.js.map +1 -7
- package/dist/routes/executions-stream.js +55 -0
- package/dist/routes/executions-stream.js.map +1 -0
- package/dist/routes/executions.js +267 -0
- package/dist/routes/executions.js.map +1 -0
- package/dist/routes/feedback.js +329 -0
- package/dist/routes/feedback.js.map +1 -0
- package/dist/routes/issues.js +280 -0
- package/dist/routes/issues.js.map +1 -0
- package/dist/routes/relationships.js +308 -0
- package/dist/routes/relationships.js.map +1 -0
- package/dist/routes/specs.js +270 -0
- package/dist/routes/specs.js.map +1 -0
- package/dist/services/db.js +85 -0
- package/dist/services/db.js.map +1 -0
- package/dist/services/execution-lifecycle.js +286 -0
- package/dist/services/execution-lifecycle.js.map +1 -0
- package/dist/services/execution-service.js +676 -0
- package/dist/services/execution-service.js.map +1 -0
- package/dist/services/executions.js +164 -0
- package/dist/services/executions.js.map +1 -0
- package/dist/services/export.js +106 -0
- package/dist/services/export.js.map +1 -0
- package/dist/services/feedback.js +54 -0
- package/dist/services/feedback.js.map +1 -0
- package/dist/services/issues.js +35 -0
- package/dist/services/issues.js.map +1 -0
- package/dist/services/prompt-template-engine.js +212 -0
- package/dist/services/prompt-template-engine.js.map +1 -0
- package/dist/services/prompt-templates.js +236 -0
- package/dist/services/prompt-templates.js.map +1 -0
- package/dist/services/relationships.js +42 -0
- package/dist/services/relationships.js.map +1 -0
- package/dist/services/specs.js +35 -0
- package/dist/services/specs.js.map +1 -0
- package/dist/services/watcher.js +69 -0
- package/dist/services/watcher.js.map +1 -0
- package/dist/services/websocket.js +389 -0
- package/dist/services/websocket.js.map +1 -0
- package/dist/utils/sudocode-dir.js +9 -0
- package/dist/utils/sudocode-dir.js.map +1 -0
- package/package.json +4 -6
package/dist/cli.js
CHANGED
|
@@ -1,105 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## Related Specifications
|
|
9
|
-
{{#each relatedSpecs}}
|
|
10
|
-
- [[{{id}}]]: {{title}}
|
|
11
|
-
{{/each}}
|
|
12
|
-
{{/if}}
|
|
13
|
-
|
|
14
|
-
{{#if feedback}}
|
|
15
|
-
## Feedback from Previous Attempts
|
|
16
|
-
{{#each feedback}}
|
|
17
|
-
- {{content}} (from {{issueId}})
|
|
18
|
-
{{/each}}
|
|
19
|
-
{{/if}}
|
|
20
|
-
|
|
21
|
-
Please implement a solution for this issue. Make sure to:
|
|
22
|
-
1. Read and understand the issue requirements
|
|
23
|
-
2. Check related specifications for context
|
|
24
|
-
3. Write clean, well-tested code
|
|
25
|
-
4. Update documentation if needed
|
|
26
|
-
`;function ze(o){let t=new U().validate(Ge);if(!t.valid)throw new Error(`Default issue template has invalid syntax: ${t.errors.join(", ")}`);if(o.prepare(`
|
|
27
|
-
SELECT id FROM prompt_templates
|
|
28
|
-
WHERE type = 'issue' AND is_default = 1
|
|
29
|
-
`).get())return;let r=Ht(),n=JSON.stringify(["issueId","title","description","relatedSpecs","feedback"]);o.prepare(`
|
|
30
|
-
INSERT INTO prompt_templates (
|
|
31
|
-
id, name, description, type, template, variables, is_default,
|
|
32
|
-
created_at, updated_at
|
|
33
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
34
|
-
`).run(r,"Default Issue Template","Renders an issue into an executable prompt with related specs and feedback","issue",Ge,n,1,new Date().toISOString(),new Date().toISOString())}function Je(o,e){return o.prepare(`
|
|
35
|
-
SELECT * FROM prompt_templates
|
|
36
|
-
WHERE type = ? AND is_default = 1
|
|
37
|
-
LIMIT 1
|
|
38
|
-
`).get(e)||null}function Ve(o,e){return o.prepare("SELECT * FROM prompt_templates WHERE id = ?").get(e)||null}function Xe(o){let{path:e,readOnly:t=!1}=o,s=Ye.dirname(e);te.existsSync(s)||te.mkdirSync(s,{recursive:!0});let r=new $t(e,{readonly:t,fileMustExist:!1});return t||(r.pragma("journal_mode = WAL"),r.pragma("foreign_keys = ON"),r.pragma("synchronous = NORMAL"),r.pragma("temp_store = MEMORY"),r.exec(Ut),r.exec(Nt),r.exec(qt),r.exec(Bt),r.exec(Lt),r.exec(Gt),ze(r)),r}function zt(o){return o.prepare(`
|
|
39
|
-
SELECT COUNT(*) as count
|
|
40
|
-
FROM sqlite_master
|
|
41
|
-
WHERE type='table'
|
|
42
|
-
AND name IN ('specs', 'issues', 'relationships', 'tags')
|
|
43
|
-
`).get().count===4}function ve(o){let e=o.prepare(`
|
|
44
|
-
SELECT name
|
|
45
|
-
FROM sqlite_master
|
|
46
|
-
WHERE type='table'
|
|
47
|
-
ORDER BY name
|
|
48
|
-
`).all(),t=o.prepare("PRAGMA user_version").get();return{tables:e.map(s=>s.name),version:t.user_version,hasCliTables:zt(o)}}import Ze from"path";import{Mutex as Yt}from"async-mutex";import h from"fs";import v from"path";var _=class extends Error{constructor(t,s,r){super(t);this.code=s;this.cause=r;this.name="WorktreeError"}};import{execSync as Vt}from"child_process";var se=class{execGit(e,t){try{return Vt(e,{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]})}catch(s){let r=s.stderr?.toString()||"",n=s.stdout?.toString()||"",i=r||n||s.message||"Unknown git error";throw new _(`Git command failed: ${e}
|
|
49
|
-
${i}`,"GIT_ERROR",s)}}escapeShellArg(e){return`'${e.replace(/'/g,"'\\''")}'`}async worktreeAdd(e,t,s,r=!1){let n=this.escapeShellArg(t),i=this.escapeShellArg(s),c=`git worktree add ${r?"--force":""} ${n} ${i}`.trim();this.execGit(c,e)}async worktreeRemove(e,t,s=!1){let r=this.escapeShellArg(t),i=`git worktree remove ${s?"--force":""} ${r}`.trim();this.execGit(i,e)}async worktreePrune(e){this.execGit("git worktree prune",e)}async worktreeList(e){let t=this.execGit("git worktree list --porcelain",e);return this.parseWorktreeList(t)}parseWorktreeList(e){let t=[],s=e.split(`
|
|
50
|
-
`).filter(n=>n.trim()),r=null;for(let n of s)if(n.startsWith("worktree "))r&&r.path&&t.push(this.finalizeWorktreeInfo(r)),r={path:n.substring(9).trim(),isMain:!1,isLocked:!1};else if(n.startsWith("HEAD "))r&&(r.commit=n.substring(5).trim());else if(n.startsWith("branch ")){if(r){let i=n.substring(7).trim();r.branch=i.replace("refs/heads/","")}}else n.startsWith("bare")?r&&(r.isMain=!0):n.startsWith("locked ")&&r&&(r.isLocked=!0,r.lockReason=n.substring(7).trim());return r&&r.path&&t.push(this.finalizeWorktreeInfo(r)),t}finalizeWorktreeInfo(e){return{path:e.path||"",branch:e.branch||"(detached)",commit:e.commit||"",isMain:e.isMain||!1,isLocked:e.isLocked||!1,lockReason:e.lockReason}}async createBranch(e,t,s){let r=this.escapeShellArg(t),n=this.escapeShellArg(s),i=`git branch ${r} ${n}`;this.execGit(i,e)}async deleteBranch(e,t,s=!1){let r=this.escapeShellArg(t),i=`git branch ${s?"-D":"-d"} ${r}`;this.execGit(i,e)}async configureSparseCheckout(e,t){this.execGit("git sparse-checkout init --cone",e);let r=`git sparse-checkout set ${t.map(n=>this.escapeShellArg(n)).join(" ")}`;this.execGit(r,e)}async isValidRepo(e){try{return this.execGit("git rev-parse --git-dir",e),!0}catch{return!1}}async listBranches(e){return this.execGit("git branch --list --all --format='%(refname:short)'",e).split(`
|
|
51
|
-
`).map(s=>s.trim()).filter(s=>s.length>0).map(s=>s.replace(/^remotes\/origin\//,""))}async getCurrentCommit(e){return this.execGit("git rev-parse HEAD",e).trim()}};import{initDatabase as Xt}from"@sudocode-ai/cli/dist/db.js";import{importFromJSONL as Qt}from"@sudocode-ai/cli/dist/import.js";import{execSync as Kt}from"child_process";var B=class{locks=new Map;config;git;constructor(e,t){this.config=e,this.git=t||new se}getLock(e){let t=this.locks.get(e);return t||(t=new Yt,this.locks.set(e,t)),t}async createWorktree(e){let{repoPath:t,branchName:s,worktreePath:r,baseBranch:n,createBranch:i,commitSha:a}=e;try{if(i){let u=a||await this.git.getCurrentCommit(t);await this.git.createBranch(t,s,u)}let c=v.dirname(r);if(h.existsSync(c)||h.mkdirSync(c,{recursive:!0}),await this.git.worktreeAdd(t,r,s),this.config.enableSparseCheckout&&this.config.sparseCheckoutPatterns&&await this.git.configureSparseCheckout(r,this.config.sparseCheckoutPatterns),!h.existsSync(r))throw new _(`Worktree creation succeeded but path does not exist: ${r}`,"REPOSITORY_ERROR");await this.setupWorktreeEnvironment(t,r)}catch(c){throw c instanceof _?c:new _(`Failed to create worktree: ${c}`,"REPOSITORY_ERROR",c)}}async setupWorktreeEnvironment(e,t){console.debug("[WorktreeManager] Setting up isolated worktree environment",{repoPath:e,worktreePath:t});let s=v.join(e,".sudocode"),r=v.join(t,".sudocode");console.debug("[WorktreeManager] Directory paths",{mainSudocodeDir:s,worktreeSudocodeDir:r}),h.existsSync(r)?console.debug("[WorktreeManager] .sudocode directory already exists in worktree"):(h.mkdirSync(r,{recursive:!0}),console.debug(`[WorktreeManager] Created .sudocode directory in worktree: ${r}`));let n=["issues.jsonl","specs.jsonl"];for(let l of n){let d=v.join(s,l),p=v.join(r,l);if(console.debug(`[WorktreeManager] Processing ${l}`,{mainFile:d,worktreeFile:p,mainFileExists:h.existsSync(d)}),h.existsSync(d)){let g=h.statSync(d);h.copyFileSync(d,p);let f=h.statSync(p);console.debug(`[WorktreeManager] Synced ${l} from main repo to worktree`,{mainFileSize:g.size,worktreeFileSize:f.size})}else console.debug(`[WorktreeManager] Main file does not exist: ${d}`)}let i=v.join(s,"config.json"),a=v.join(r,"config.json");h.existsSync(i)?(h.copyFileSync(i,a),console.debug("[WorktreeManager] Copied config.json from main repo to worktree")):console.debug("[WorktreeManager] No config.json to copy from main repo");let c=v.join(r,"cache.db"),u=Xt({path:c,verbose:!1});try{if(await Qt(u,{inputDir:r}),console.debug(`[WorktreeManager] Successfully initialized local database in worktree at ${c}`),h.existsSync(c)){let l=h.statSync(c);console.debug("[WorktreeManager] Database file created",{path:c,size:l.size})}else console.error("[WorktreeManager] ERROR: Database file was not created!")}catch(l){throw console.error("[WorktreeManager] Failed to initialize database",l),l}finally{u.close()}console.debug("[WorktreeManager] Worktree environment setup complete")}async ensureWorktreeExists(e,t,s){let n=await this.getLock(s).acquire();try{if(await this.isWorktreeValid(e,s))return;await this.recreateWorktree(e,t,s)}finally{n()}}async cleanupWorktree(e,t){let r=await this.getLock(e).acquire();try{let n=t||await this.inferRepoPath(e);if(!n){h.existsSync(e)&&h.rmSync(e,{recursive:!0,force:!0});return}let i;try{let u=await this.git.worktreeList(n),l=h.realpathSync(e),d=u.find(p=>{try{return h.realpathSync(p.path)===l}catch{return p.path===e}});d&&(i=d.branch)}catch{}try{await this.git.worktreeRemove(n,e,!0)}catch{}let a=v.basename(e),c=v.join(n,".git","worktrees",a);h.existsSync(c)&&h.rmSync(c,{recursive:!0,force:!0}),h.existsSync(e)&&h.rmSync(e,{recursive:!0,force:!0});try{await this.git.worktreePrune(n)}catch{}if(this.config.autoDeleteBranches&&i&&i!=="(detached)")try{await this.git.deleteBranch(n,i,!0)}catch{}}finally{r()}}async isWorktreeValid(e,t){try{if(!h.existsSync(t))return!1;let s=await this.git.worktreeList(e),r=h.realpathSync(t);return s.some(i=>{try{return h.realpathSync(i.path)===r}catch{return i.path===t}})}catch{return!1}}async listWorktrees(e){return await this.git.worktreeList(e)}getConfig(){return{...this.config}}async isValidRepo(e){return this.git.isValidRepo(e)}async listBranches(e){return this.git.listBranches(e)}async recreateWorktree(e,t,s){await this.cleanupWorktree(s,e);let r=v.dirname(s);h.existsSync(r)||h.mkdirSync(r,{recursive:!0});let n,i=1;for(let a=0;a<=i;a++)try{if(await this.git.worktreeAdd(e,s,t),this.config.enableSparseCheckout&&this.config.sparseCheckoutPatterns&&await this.git.configureSparseCheckout(s,this.config.sparseCheckoutPatterns),!h.existsSync(s))throw new _(`Worktree creation succeeded but path does not exist: ${s}`,"REPOSITORY_ERROR");await this.setupWorktreeEnvironment(e,s);return}catch(c){if(n=c,a<i){let u=v.basename(s),l=v.join(e,".git","worktrees",u);h.existsSync(l)&&h.rmSync(l,{recursive:!0,force:!0})}}throw new _(`Failed to recreate worktree after ${i+1} attempts: ${n}`,"REPOSITORY_ERROR",n)}async inferRepoPath(e){try{if(!h.existsSync(e))return;let t=Kt("git rev-parse --git-common-dir",{cwd:e,encoding:"utf8"}).trim(),s=v.resolve(e,t);return v.basename(s)===".git"?v.dirname(s):s}catch{return}}};import Qe from"fs";import Zt from"path";var T={worktreeStoragePath:".sudocode/worktrees",autoCreateBranches:!0,autoDeleteBranches:!1,enableSparseCheckout:!1,sparseCheckoutPatterns:void 0,branchPrefix:"sudocode",cleanupOrphanedWorktreesOnStartup:!0};function es(o){let e=[],t={...T};return o.worktreeStoragePath!==void 0&&(typeof o.worktreeStoragePath=="string"?t.worktreeStoragePath=o.worktreeStoragePath:e.push(`Invalid worktreeStoragePath: must be a string. Using default: ${T.worktreeStoragePath}`)),o.autoCreateBranches!==void 0&&(typeof o.autoCreateBranches=="boolean"?t.autoCreateBranches=o.autoCreateBranches:e.push(`Invalid autoCreateBranches: must be a boolean. Using default: ${T.autoCreateBranches}`)),o.autoDeleteBranches!==void 0&&(typeof o.autoDeleteBranches=="boolean"?t.autoDeleteBranches=o.autoDeleteBranches:e.push(`Invalid autoDeleteBranches: must be a boolean. Using default: ${T.autoDeleteBranches}`)),o.enableSparseCheckout!==void 0&&(typeof o.enableSparseCheckout=="boolean"?t.enableSparseCheckout=o.enableSparseCheckout:e.push(`Invalid enableSparseCheckout: must be a boolean. Using default: ${T.enableSparseCheckout}`)),o.sparseCheckoutPatterns!==void 0&&(Array.isArray(o.sparseCheckoutPatterns)&&o.sparseCheckoutPatterns.every(s=>typeof s=="string")?t.sparseCheckoutPatterns=o.sparseCheckoutPatterns:(e.push("Invalid sparseCheckoutPatterns: must be an array of strings. Ignoring value."),t.sparseCheckoutPatterns=void 0)),o.branchPrefix!==void 0&&(typeof o.branchPrefix=="string"?ts(o.branchPrefix)?t.branchPrefix=o.branchPrefix:e.push(`Invalid branchPrefix: contains invalid git branch name characters. Using default: ${T.branchPrefix}`):e.push(`Invalid branchPrefix: must be a string. Using default: ${T.branchPrefix}`)),o.cleanupOrphanedWorktreesOnStartup!==void 0&&(typeof o.cleanupOrphanedWorktreesOnStartup=="boolean"?t.cleanupOrphanedWorktreesOnStartup=o.cleanupOrphanedWorktreesOnStartup:e.push(`Invalid cleanupOrphanedWorktreesOnStartup: must be a boolean. Using default: ${T.cleanupOrphanedWorktreesOnStartup}`)),{config:t,warnings:e}}function ts(o){let e=[/\.\./,/@\{/,/\\/,/\^/,/~/,/:/,/\?/,/\*/,/\[/,/\s/,/[\x00-\x1f\x7f]/];return o.startsWith("/")||o.endsWith(".lock")?!1:!e.some(t=>t.test(o))}function ss(o=process.cwd()){let e=Zt.join(o,".sudocode","config.json");if(!Qe.existsSync(e))return{config:T,warnings:[`Config file not found at ${e}. Using default configuration.`]};try{let t=Qe.readFileSync(e,"utf-8"),r=JSON.parse(t).worktree||{};return es(r)}catch(t){let s=t instanceof Error?t.message:String(t);return{config:T,warnings:[`Failed to load config from ${e}: ${s}. Using default configuration.`]}}}var be=null,Ke=null;function re(o=process.cwd(),e=!1){if(!e&&be&&Ke===o)return be;let{config:t,warnings:s}=ss(o);return s.length>0&&(console.warn("[Worktree Config] Configuration warnings:"),s.forEach(r=>console.warn(` - ${r}`))),be=t,Ke=o,t}import{randomUUID as rs}from"crypto";function Y(o,e){let t=e.id||rs(),s=new Date().toISOString(),r=null;if(e.issue_id){let a=o.prepare("SELECT uuid FROM issues WHERE id = ?").get(e.issue_id);a&&(r=a.uuid)}o.prepare(`
|
|
52
|
-
INSERT INTO executions (
|
|
53
|
-
id,
|
|
54
|
-
issue_id,
|
|
55
|
-
issue_uuid,
|
|
56
|
-
agent_type,
|
|
57
|
-
mode,
|
|
58
|
-
prompt,
|
|
59
|
-
config,
|
|
60
|
-
status,
|
|
61
|
-
started_at,
|
|
62
|
-
before_commit,
|
|
63
|
-
target_branch,
|
|
64
|
-
branch_name,
|
|
65
|
-
worktree_path,
|
|
66
|
-
created_at,
|
|
67
|
-
updated_at
|
|
68
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
69
|
-
`).run(t,e.issue_id,r,e.agent_type,e.mode||null,e.prompt||null,e.config||null,"running",s,e.before_commit||null,e.target_branch,e.branch_name,e.worktree_path||null,s,s);let i=x(o,t);if(!i)throw new Error(`Failed to create execution with id ${t}`);return i}function x(o,e){return o.prepare(`
|
|
70
|
-
SELECT * FROM executions WHERE id = ?
|
|
71
|
-
`).get(e)||null}function C(o,e,t){let s=x(o,e);if(!s)throw new Error(`Execution not found: ${e}`);let r=[],n=[];if(t.status!==void 0&&(r.push("status = ?"),n.push(t.status)),t.completed_at!==void 0&&(r.push("completed_at = ?"),n.push(t.completed_at)),t.exit_code!==void 0&&(r.push("exit_code = ?"),n.push(t.exit_code)),t.error_message!==void 0&&(r.push("error_message = ?"),n.push(t.error_message)),t.after_commit!==void 0&&(r.push("after_commit = ?"),n.push(t.after_commit)),t.target_branch!==void 0&&(r.push("target_branch = ?"),n.push(t.target_branch)),t.worktree_path!==void 0&&(r.push("worktree_path = ?"),n.push(t.worktree_path)),t.session_id!==void 0&&(r.push("session_id = ?"),n.push(t.session_id)),t.summary!==void 0&&(r.push("summary = ?"),n.push(t.summary)),r.push("updated_at = ?"),n.push(new Date().toISOString()),r.length===1)return s;n.push(e),o.prepare(`
|
|
72
|
-
UPDATE executions
|
|
73
|
-
SET ${r.join(", ")}
|
|
74
|
-
WHERE id = ?
|
|
75
|
-
`).run(...n);let a=x(o,e);if(!a)throw new Error(`Failed to update execution ${e}`);return a}import{randomUUID as ns}from"crypto";var N=class{worktreeManager;db;repoPath;constructor(e,t,s){if(this.db=e,this.repoPath=t,s)this.worktreeManager=s;else{let r=re(t);this.worktreeManager=new B(r)}}async createExecutionWithWorktree(e){let{issueId:t,issueTitle:s,agentType:r,targetBranch:n,repoPath:i}=e,a=this.db.prepare(`SELECT id FROM executions
|
|
76
|
-
WHERE issue_id = ?
|
|
77
|
-
AND status = 'running'
|
|
78
|
-
AND worktree_path IS NOT NULL`).get(t);if(a)throw new Error(`Active execution already exists for issue ${t}: ${a.id}`);if(!await this.worktreeManager.isValidRepo(i))throw new Error(`Not a git repository: ${i}`);if(!(await this.worktreeManager.listBranches(i)).includes(n))throw new Error(`Target branch does not exist: ${n}`);let l=this.worktreeManager.getConfig(),d=ns(),p;if(l.autoCreateBranches){let m=os(s);p=`${l.branchPrefix}/${d.substring(0,8)}/${m}`}else p=n;let g=Ze.join(i,l.worktreeStoragePath,d),f=!1;try{return await this.worktreeManager.createWorktree({repoPath:i,branchName:p,worktreePath:g,baseBranch:n,createBranch:l.autoCreateBranches}),f=!0,{execution:Y(this.db,{id:d,issue_id:t,agent_type:r,mode:e.mode,prompt:e.prompt,config:e.config,target_branch:n,branch_name:p,worktree_path:g}),worktreePath:g,branchName:p}}catch(m){if(f)try{await this.worktreeManager.cleanupWorktree(g,i)}catch(y){console.error("Failed to cleanup worktree after execution creation failure:",y)}throw m}}shouldCleanupExecution(e){let t=x(this.db,e);if(!t)return!1;if(t.config)try{let s=JSON.parse(t.config);if(s.cleanupMode==="manual"||s.cleanupMode==="never")return!1}catch(s){console.error(`Failed to parse execution config for ${e}:`,s)}return!0}async cleanupExecution(e){if(!this.shouldCleanupExecution(e))return;let t=x(this.db,e);if(t&&t.worktree_path)try{await this.worktreeManager.cleanupWorktree(t.worktree_path,this.repoPath)}catch(s){console.error(`Failed to cleanup worktree for execution ${e}:`,s)}}async cleanupOrphanedWorktrees(){let e=this.repoPath,t=this.worktreeManager.getConfig();try{let r=(await this.worktreeManager.listWorktrees(e)).filter(n=>n.path.includes(t.worktreeStoragePath));for(let n of r){let i=n.path,a=Ze.basename(i),c=x(this.db,a);if(c){if(c.status==="completed"||c.status==="failed"||c.status==="stopped"){if(!this.shouldCleanupExecution(a)){console.log(`Skipping cleanup for finished execution ${a} (manual cleanup mode)`);continue}console.log(`Cleaning up worktree for finished execution ${a} (status: ${c.status})`);try{await this.worktreeManager.cleanupWorktree(i,e)}catch(u){console.error(`Failed to cleanup worktree for finished execution ${a}:`,u)}}}else{console.log(`Cleaning up orphaned worktree: ${i} (no execution found)`);try{await this.worktreeManager.cleanupWorktree(i,e)}catch(u){console.error(`Failed to cleanup orphaned worktree ${i}:`,u)}}}}catch(s){console.error(`Failed to cleanup orphaned worktrees in ${e}:`,s)}}};function os(o){return o.toLowerCase().replace(/[\s/]+/g,"-").replace(/[^a-z0-9\-_]/g,"").replace(/-+/g,"-").replace(/^-+|-+$/g,"").substring(0,50)}import{randomUUID as ct}from"crypto";import{spawn as ls}from"child_process";import et from"crypto";var is=128,M,L,as=o=>{!M||M.length<o?(M=Buffer.allocUnsafe(o*is),et.randomFillSync(M),L=0):L+o>M.length&&(et.randomFillSync(M),L=0),L+=o},cs=o=>(as(o|=0),M.subarray(L-o,L)),us=(o,e,t)=>{let s=(2<<31-Math.clz32(o.length-1|1))-1,r=Math.ceil(1.6*s*e/o.length);return(n=e)=>{let i="";for(;;){let a=t(r),c=r;for(;c--;)if(i+=o[a[c]&s]||"",i.length===n)return i}}},tt=(o,e=21)=>us(o,e,cs);function st(o){let e=tt("0123456789abcdefghijklmnopqrstuvwxyz",10);return`${o}-${e()}`}var X=class{constructor(e={}){this._defaultConfig=e}_activeProcesses=new Map;_cleanupTimers=new Map;_metrics={totalSpawned:0,currentlyActive:0,totalCompleted:0,totalFailed:0,averageDuration:0};async acquireProcess(e){let t={...this._defaultConfig,...e},s=this.spawnProcess(t);if(!s.pid)throw s.once("error",()=>{}),new Error("Failed to spawn process: no PID assigned");let r=st("process"),n={id:r,pid:s.pid,status:"busy",spawnedAt:new Date,lastActivity:new Date,exitCode:null,signal:null,process:s,streams:{stdout:s.stdout,stderr:s.stderr,stdin:s.stdin},metrics:{totalDuration:0,tasksCompleted:0,successRate:1}};return this._activeProcesses.set(r,n),this._metrics.totalSpawned++,this._metrics.currentlyActive++,this.setupProcessHandlers(n,t),n}spawnProcess(e){return ls(e.executablePath,e.args,{cwd:e.workDir,stdio:["pipe","pipe","pipe"],env:{...process.env,...e.env}})}setupProcessHandlers(e,t){let{process:s,id:r}=e,n=null;t.timeout&&(n=setTimeout(()=>{e.status==="busy"&&this.terminateProcess(r).catch(()=>{})},t.timeout)),s.once("exit",(i,a)=>{n&&clearTimeout(n),e.exitCode=i,e.signal=a,e.status=i===0?"completed":"crashed";let c=Date.now()-e.spawnedAt.getTime();e.metrics.totalDuration=c,this._metrics.currentlyActive--,i===0?this._metrics.totalCompleted++:this._metrics.totalFailed++;let u=this._metrics.totalCompleted+this._metrics.totalFailed;if(u>0){let d=this._metrics.averageDuration*(u-1);this._metrics.averageDuration=(d+c)/u}e.streams.stdin.destroy(),e.streams.stdout.destroy(),e.streams.stderr.destroy();let l=setTimeout(()=>{this._activeProcesses.delete(r),this._cleanupTimers.delete(r)},5e3);this._cleanupTimers.set(r,l)}),s.once("error",i=>{n&&clearTimeout(n),e.status="crashed",this._metrics.currentlyActive--,this._metrics.totalFailed++}),s.stdout?.on("data",()=>{e.lastActivity=new Date}),s.stderr?.on("data",()=>{e.lastActivity=new Date})}async releaseProcess(e){await this.terminateProcess(e)}async terminateProcess(e,t="SIGTERM"){let s=this._activeProcesses.get(e);if(!s||s.exitCode!==null)return;s.status="terminating",s.process.kill(t);let r=new Promise(i=>{s.exitCode!==null?i():s.process.once("exit",()=>i())}),n=new Promise(i=>{setTimeout(i,2e3)});await Promise.race([r,n]),s.exitCode===null&&(s.process.kill("SIGKILL"),await Promise.race([new Promise(i=>{s.exitCode!==null?i():s.process.once("exit",()=>i())}),new Promise(i=>setTimeout(i,1e3))]))}async sendInput(e,t){let s=this._activeProcesses.get(e);if(!s)throw new Error(`Process ${e} not found`);return new Promise((r,n)=>{s.streams.stdin.write(t,i=>{i?n(i):r()})})}closeInput(e){let t=this._activeProcesses.get(e);if(!t)throw new Error(`Process ${e} not found`);t.streams.stdin.end()}onOutput(e,t){let s=this._activeProcesses.get(e);if(!s)throw new Error(`Process ${e} not found`);s.streams.stdout.on("data",r=>{t(r,"stdout")}),s.streams.stderr.on("data",r=>{t(r,"stderr")})}onError(e,t){let s=this._activeProcesses.get(e);if(!s)throw new Error(`Process ${e} not found`);s.process.on("error",r=>{t(r)})}getProcess(e){return this._activeProcesses.get(e)||null}getActiveProcesses(){return Array.from(this._activeProcesses.values())}getMetrics(){return{...this._metrics}}async shutdown(){let e=Array.from(this._activeProcesses.keys());await Promise.all(e.map(t=>this.terminateProcess(t,"SIGTERM")));for(let[t,s]of this._cleanupTimers.entries())clearTimeout(s),this._cleanupTimers.delete(t)}};function rt(o){let e=[];return o.print&&e.push("--print"),o.outputFormat&&e.push("--output-format",o.outputFormat),(o.verbose||o.print&&o.outputFormat==="stream-json")&&e.push("--verbose"),o.dangerouslySkipPermissions&&e.push("--dangerously-skip-permissions"),o.permissionMode&&e.push("--permission-mode",o.permissionMode),{executablePath:o.claudePath||"claude",args:e,workDir:o.workDir,env:o.env,timeout:o.timeout,idleTimeout:o.idleTimeout,retry:o.retry}}var F=class{constructor(e,t={}){this._processManager=e;this._config=t;this.metrics={maxConcurrent:t.maxConcurrent??3,currentlyRunning:0,availableSlots:t.maxConcurrent??3,queuedTasks:0,completedTasks:0,failedTasks:0,averageDuration:0,successRate:1,throughput:0,totalProcessesSpawned:0,activeProcesses:0}}taskQueue=[];runningTasks=new Map;completedResults=new Map;taskResolvers=new Map;metrics;completeHandlers=[];failedHandlers=[];pollingIntervals=new Map;async submitTask(e){return this.taskQueue.push(e),this.metrics.queuedTasks++,this.processQueue(),e.id}async submitTasks(e){let t=[];for(let s of e){let r=await this.submitTask(s);t.push(r)}return t}processQueue(){let e=0,t=this.taskQueue.length;for(;this.taskQueue.length>0&&this.runningTasks.size<this.metrics.maxConcurrent&&e<t;){let s=this.taskQueue.shift();if(this.metrics.queuedTasks--,e++,this.hasFailedDependency(s)){this.handleTaskFailure(s.id,new Error(`Task ${s.id} failed: one or more dependencies failed`));continue}if(!this.areDependenciesMet(s)){this.taskQueue.push(s),this.metrics.queuedTasks++;continue}this.trackTaskStart(s),this.executeTask(s).catch(r=>{this.handleTaskFailure(s.id,r)})}}areDependenciesMet(e){if(e.dependencies.length===0)return!0;for(let t of e.dependencies){let s=this.completedResults.get(t);if(!s||!s.success)return!1}return!0}hasFailedDependency(e){for(let t of e.dependencies){let s=this.completedResults.get(t);if(s&&!s.success)return!0}return!1}trackTaskStart(e){let t=e.metadata?._retryAttempt||1,s={task:e,process:null,startedAt:new Date,attempt:t};this.runningTasks.set(e.id,s),this.metrics.currentlyRunning=this.runningTasks.size,this.metrics.availableSlots=this.metrics.maxConcurrent-this.runningTasks.size}trackTaskComplete(e){this.runningTasks.delete(e),this.metrics.currentlyRunning=this.runningTasks.size,this.metrics.availableSlots=this.metrics.maxConcurrent-this.runningTasks.size,this.processQueue()}async executeTask(e){let t=new Date,s=null,r="",n="";try{let i=rt({claudePath:this._config.claudePath,workDir:e.workDir,print:!0,outputFormat:"stream-json",dangerouslySkipPermissions:!0,env:e.config.env,timeout:e.config.timeout});s=await this._processManager.acquireProcess(i);let a=this.runningTasks.get(e.id);a&&(a.process=s),this._processManager.onOutput(s.id,(d,p)=>{p==="stdout"?r+=d.toString():n+=d.toString(),this._config.onOutput&&this._config.onOutput(d,p)}),this._processManager.onError(s.id,d=>{n+=`Process error: ${d.message}
|
|
79
|
-
`}),await this._processManager.sendInput(s.id,e.prompt),this._processManager.closeInput(s.id),await this.waitForProcessExit(s,e.config.timeout);let c=new Date,u={taskId:e.id,executionId:s.id,success:s.exitCode===0,exitCode:s.exitCode??-1,output:r,error:n||void 0,startedAt:t,completedAt:c,duration:c.getTime()-t.getTime(),metadata:this.parseMetadata(r)};this.completedResults.set(e.id,u),this.metrics.completedTasks++;let l=this.taskResolvers.get(e.id);l&&(l.forEach(d=>d.resolve(u)),this.taskResolvers.delete(e.id));for(let d of this.completeHandlers)d(u);this.trackTaskComplete(e.id)}catch(i){this.handleTaskFailure(e.id,i)}finally{if(s)try{await this._processManager.releaseProcess(s.id)}catch{}}}async waitForProcessExit(e,t){return new Promise((s,r)=>{if(e.exitCode!==null){s();return}let n=setInterval(()=>{let i=this._processManager.getProcess(e.id);(!i||i.exitCode!==null)&&(clearInterval(n),this.pollingIntervals.delete(e.id),i?i.status==="crashed"?r(new Error(`Process crashed with exit code ${i.exitCode}`)):s():r(new Error("Process not found")))},10);this.pollingIntervals.set(e.id,n),t&&setTimeout(()=>{clearInterval(n),this.pollingIntervals.delete(e.id),r(new Error("Process execution timeout"))},t)})}parseMetadata(e){return{toolsUsed:[],filesChanged:[],tokensUsed:0,cost:0}}handleTaskFailure(e,t){let s=this.runningTasks.get(e);if(s){let a=s.task,c=a.config.maxRetries,u=s.attempt;if(c!==void 0&&u<=c){let l={...a,metadata:{...a.metadata,_retryAttempt:u+1}};this.taskQueue.unshift(l),this.metrics.queuedTasks++,this.trackTaskComplete(e);return}}let r=new Date,n={taskId:e,executionId:`failed-${e}`,success:!1,exitCode:-1,output:"",error:t.message,startedAt:r,completedAt:r,duration:0,metadata:{toolsUsed:[],filesChanged:[],tokensUsed:0,cost:0}};this.completedResults.set(e,n),this.metrics.failedTasks++,this.trackTaskComplete(e);let i=this.taskResolvers.get(e);i&&(i.forEach(a=>a.reject(t)),this.taskResolvers.delete(e));for(let a of this.failedHandlers)a(e,t)}async cancelTask(e){let t=this.taskQueue.findIndex(r=>r.id===e);if(t>=0){this.taskQueue.splice(t,1),this.metrics.queuedTasks--;return}let s=this.runningTasks.get(e);if(s){let r=this.pollingIntervals.get(s.process.id);r&&(clearInterval(r),this.pollingIntervals.delete(s.process.id));try{await this._processManager.terminateProcess(s.process.id)}catch{}this.runningTasks.delete(e),this.metrics.currentlyRunning=this.runningTasks.size,this.metrics.availableSlots=this.metrics.maxConcurrent-this.runningTasks.size,this.processQueue();return}}getTaskStatus(e){let t=this.completedResults.get(e);if(t)return{state:"completed",result:t};let s=this.runningTasks.get(e);if(s)return{state:"running",processId:s.process.id,startedAt:s.startedAt};let r=this.taskQueue.findIndex(n=>n.id===e);return r>=0?{state:"queued",position:r}:null}async waitForTask(e){let t=this.completedResults.get(e);return t||new Promise((s,r)=>{let n=this.taskResolvers.get(e)||[];n.push({resolve:s,reject:r}),this.taskResolvers.set(e,n)})}async waitForTasks(e){return Promise.all(e.map(t=>this.waitForTask(t)))}getMetrics(){return{...this.metrics}}onTaskComplete(e){this.completeHandlers.push(e)}onTaskFailed(e){this.failedHandlers.push(e)}async shutdown(){this.taskQueue=[],this.metrics.queuedTasks=0;let e=Array.from(this.runningTasks.keys());await Promise.all(e.map(t=>this.cancelTask(t)));for(let[t,s]of this.pollingIntervals.entries())clearInterval(s),this.pollingIntervals.delete(t);await this._processManager.shutdown(),this.runningTasks.clear(),this.completedResults.clear(),this.taskResolvers.clear(),this.completeHandlers=[],this.failedHandlers=[],this.metrics.currentlyRunning=0,this.metrics.availableSlots=this.metrics.maxConcurrent}};var ne=class{breakers=new Map;get(e){return this.breakers.get(e)||null}getOrCreate(e,t={failureThreshold:5,successThreshold:2,timeout:6e4}){let s=this.breakers.get(e);return s||(s={name:e,state:"closed",config:t,metrics:{totalRequests:0,failedRequests:0,successfulRequests:0}},this.breakers.set(e,s)),s}canExecute(e){let t=this.breakers.get(e);return t&&t.state==="open"?this.shouldTransitionToHalfOpen(t)?(t.state="half-open",!0):!1:!0}recordSuccess(e){let t=this.breakers.get(e);t&&(t.metrics.totalRequests++,t.metrics.successfulRequests++,t.metrics.lastSuccessTime=new Date,t.state==="half-open"&&this.getConsecutiveSuccesses(t)>=t.config.successThreshold&&(t.state="closed",t.metrics.failedRequests=0))}recordFailure(e,t){let s=this.breakers.get(e);s&&(s.metrics.totalRequests++,s.metrics.failedRequests++,s.metrics.lastFailureTime=new Date,s.state==="closed"?s.metrics.failedRequests>=s.config.failureThreshold&&(s.state="open"):s.state==="half-open"&&(s.state="open"))}reset(e){let t=this.breakers.get(e);t&&(t.state="closed",t.metrics.failedRequests=0,t.metrics.successfulRequests=0)}getAll(){return new Map(this.breakers)}shouldTransitionToHalfOpen(e){return e.metrics.lastFailureTime?Date.now()-e.metrics.lastFailureTime.getTime()>=e.config.timeout:!0}getConsecutiveSuccesses(e){return e.metrics.successfulRequests}};function ke(o,e){let t;switch(e.type){case"exponential":t=e.baseDelayMs*Math.pow(2,o-1);break;case"linear":t=e.baseDelayMs*o;break;case"fixed":t=e.baseDelayMs;break;default:t=e.baseDelayMs}if(t=Math.min(t,e.maxDelayMs),e.jitter){let s=t*.1,r=Math.random()*s*2-s;t+=r,t=Math.max(0,Math.min(t,e.maxDelayMs))}return Math.floor(t)}function ds(o,e){let t=o.message||"";for(let s of e.retryableErrors)if(t.includes(s))return!0;return!1}function ps(o,e){return e.retryableExitCodes.includes(o)}function nt(o,e){if(o.exitCode!==void 0&&o.exitCode!==null&&ps(o.exitCode,e))return!0;if(o.error){let t=new Error(o.error);if(ds(t,e))return!0}return!1}function we(o){return new Promise(e=>setTimeout(e,o))}function Q(o,e,t={}){let s=new Date;return{attemptNumber:o,startedAt:s,completedAt:t.duration!==void 0?s:void 0,duration:t.duration,success:e,error:t.error,exitCode:t.exitCode,willRetry:t.willRetry||!1,nextRetryAt:t.nextRetryAt}}var ot={maxAttempts:3,backoff:{type:"exponential",baseDelayMs:1e3,maxDelayMs:3e4,jitter:!0},retryableErrors:["ECONNREFUSED","ETIMEDOUT","ENOTFOUND","timeout","network","Process execution timeout"],retryableExitCodes:[1,137]};var O=class{_engine;_circuitManager;_defaultPolicy;_metrics;_retryHandlers=[];_circuitOpenHandlers=[];constructor(e,t=ot){this._engine=e,this._circuitManager=new ne,this._defaultPolicy=t,this._metrics={totalRetries:0,successfulRetries:0,failedRetries:0,averageAttemptsToSuccess:0,circuitBreakers:new Map}}async executeTask(e,t){let s=t||this._defaultPolicy,r=e.type,n=[],i=this._circuitManager.getOrCreate(r,{failureThreshold:5,successThreshold:2,timeout:6e4});for(let a=1;a<=s.maxAttempts;a++){if(!this._circuitManager.canExecute(r))return this._circuitOpenHandlers.forEach(l=>{l(r,i)}),{taskId:e.id,executionId:"circuit-breaker-open",success:!1,exitCode:-1,output:"",error:`Circuit breaker is open for ${r}`,startedAt:new Date,completedAt:new Date,duration:0,attempts:[],totalAttempts:0,finalAttempt:Q(0,!1,{error:new Error(`Circuit breaker is open for ${r}`)}),circuitBreakerTriggered:!0};let c=new Date;try{let u=await this._engine.submitTask(e),l=await this._engine.waitForTask(u),d=Date.now()-c.getTime();if(l.success){this._circuitManager.recordSuccess(r);let m=Q(a,!0,{exitCode:l.exitCode,duration:d});return n.push(m),a>1&&(this._metrics.successfulRetries++,this._updateAverageAttempts(a)),this._createResilientResult(l,n)}let g=nt(l,s)&&a<s.maxAttempts,f=Q(a,!1,{error:new Error(l.error||"Task failed"),exitCode:l.exitCode,duration:d,willRetry:g});if(g){let m=ke(a+1,s.backoff);f.nextRetryAt=new Date(Date.now()+m),this._retryHandlers.forEach(y=>{y(e.id,f)}),this._metrics.totalRetries++,n.push(f),await we(m);continue}return n.push(f),this._circuitManager.recordFailure(r,new Error(l.error||"Task failed")),a>1&&(this._metrics.failedRetries+=a-1),this._createResilientResult(l,n)}catch(u){let l=Date.now()-c.getTime(),d=u instanceof Error?u:new Error(String(u)),g=s.retryableErrors.some(m=>d.message.includes(m))&&a<s.maxAttempts,f=Q(a,!1,{error:d,duration:l,willRetry:g});if(g){let m=ke(a+1,s.backoff);f.nextRetryAt=new Date(Date.now()+m),this._retryHandlers.forEach(y=>{y(e.id,f)}),this._metrics.totalRetries++,n.push(f),await we(m);continue}throw n.push(f),this._circuitManager.recordFailure(r,d),a>1&&(this._metrics.failedRetries+=a-1),d}}throw new Error("Unexpected state: exceeded max attempts without return")}async executeTasks(e,t){let s=e.map(r=>this.executeTask(r,t));return Promise.all(s)}getCircuitBreaker(e){return this._circuitManager.get(e)}resetCircuitBreaker(e){this._circuitManager.reset(e)}getRetryMetrics(){return this._metrics.circuitBreakers=this._circuitManager.getAll(),{...this._metrics,circuitBreakers:new Map(this._metrics.circuitBreakers)}}onRetryAttempt(e){this._retryHandlers.push(e)}onCircuitOpen(e){this._circuitOpenHandlers.push(e)}_createResilientResult(e,t){let s=t[t.length-1];return{...e,attempts:t,totalAttempts:t.length,finalAttempt:s,failureReason:e.success?void 0:e.error}}_updateAverageAttempts(e){let t=this._metrics.successfulRetries,r=this._metrics.averageAttemptsToSuccess*(t-1);this._metrics.averageAttemptsToSuccess=(r+e)/t}};function it(o="id"){let e=Date.now(),t=Math.random().toString(36).substring(2,9);return`${o}-${e}-${t}`}function xe(o,e){let t=o,s=/\{\{(\w+(?:\.\w+)*)\}\}/g;return t=t.replace(s,(r,n)=>{let i=Se(e,n);return i==null?r:String(i)}),t}function Se(o,e){if(o==null)return;if(!e.includes("."))return o[e];let t=e.split("."),s=o;for(let r of t){if(s==null)return;s=s[r]}return s}function at(o,e){let t=xe(o,e);return t.includes("{{")&&t.includes("}}")?!1:t==="true"||t==="1"?!0:t==="false"||t==="0"||t===""?!1:!!t}var K=class{_executions=new Map;_cleanedUpExecutions=new Set;_storage;_executor;_agUiAdapter;_lifecycleService;_workflowStartHandlers=[];_workflowCompleteHandlers=[];_workflowFailedHandlers=[];_stepStartHandlers=[];_stepCompleteHandlers=[];_stepFailedHandlers=[];_checkpointHandlers=[];_resumeHandlers=[];_pauseHandlers=[];_cancelHandlers=[];constructor(e,t,s,r){this._executor=e,this._storage=t,this._agUiAdapter=s,this._lifecycleService=r}async startWorkflow(e,t,s){let r={executionId:s.executionId,workflowId:e.id,definition:e,status:"pending",currentStepIndex:0,context:s?.initialContext||e.initialContext||{},stepResults:[],startedAt:new Date};return this._executions.set(r.executionId,r),this._executeWorkflow(e,r,t,s?.checkpointInterval).catch(n=>{r.status="failed",r.completedAt=new Date,r.error=n.message,this._workflowFailedHandlers.forEach(i=>{i(r.executionId,n)}),this._agUiAdapter&&this._agUiAdapter.emitRunError(n.message,n.stack),this._cleanupExecution(r).catch(()=>{})}),r.executionId}async resumeWorkflow(e,t){if(!this._storage)throw new Error("Cannot resume workflow: no storage configured");let s=await this._storage.loadCheckpoint(e);if(!s)throw new Error(`No checkpoint found for execution ${e}`);let r={workflowId:s.workflowId,executionId:s.executionId,definition:s.definition,status:"running",currentStepIndex:s.state.currentStepIndex,context:{...s.state.context},stepResults:[...s.state.stepResults],startedAt:s.state.startedAt,resumedAt:new Date};this._executions.set(e,r),this._resumeHandlers.forEach(i=>{i(e,s)});let n=s.definition.metadata?.workDir||process.cwd();return this._executeWorkflow(s.definition,r,n,t?.checkpointInterval).catch(i=>{r.status="failed",r.completedAt=new Date,r.error=i.message,this._workflowFailedHandlers.forEach(a=>{a(r.executionId,i)}),this._agUiAdapter&&this._agUiAdapter.emitRunError(i.message,i.stack),this._cleanupExecution(r).catch(()=>{})}),e}async pauseWorkflow(e){let t=this._executions.get(e);if(t){if(t.status!=="running")throw new Error(`Cannot pause workflow in ${t.status} state`);t.status="paused",t.pausedAt=new Date,await new Promise(s=>setTimeout(s,250)),this._storage&&await this._saveCheckpoint(t),this._pauseHandlers.forEach(s=>{s(e)})}}async cancelWorkflow(e){let t=this._executions.get(e);t&&(["completed","cancelled"].includes(t.status)||(t.status="cancelled",t.completedAt=new Date,this._storage&&await this._saveCheckpoint(t),this._cancelHandlers.forEach(s=>{s(e)}),await this._cleanupExecution(t)))}getExecution(e){return this._executions.get(e)||null}getStepStatus(e,t){let s=this._executions.get(e);if(!s)return null;let r=s.definition.steps.findIndex(a=>a.id===t);if(r===-1)return null;let n,i=s.stepResults[r];return i!==void 0?n=i.success?"completed":"failed":r===s.currentStepIndex?n="running":n="pending",{stepId:t,status:n,result:i,attempts:1}}async waitForWorkflow(e){let t=this._executions.get(e);if(!t)throw new Error(`Workflow execution ${e} not found`);return["completed","failed","cancelled"].includes(t.status)?t:new Promise((s,r)=>{let n=setInterval(()=>{let i=this._executions.get(e);if(!i){clearInterval(n),r(new Error(`Workflow execution ${e} not found`));return}["completed","failed","cancelled"].includes(i.status)&&(clearInterval(n),s(i))},100);setTimeout(()=>{clearInterval(n),r(new Error(`Timeout waiting for workflow ${e}`))},3e5)})}async listCheckpoints(e){return this._storage?this._storage.listCheckpoints(e):[]}onWorkflowStart(e){this._workflowStartHandlers.push(e)}onWorkflowComplete(e){this._workflowCompleteHandlers.push(e)}onWorkflowFailed(e){this._workflowFailedHandlers.push(e)}onStepStart(e){this._stepStartHandlers.push(e)}onStepComplete(e){this._stepCompleteHandlers.push(e)}onStepFailed(e){this._stepFailedHandlers.push(e)}onCheckpoint(e){this._checkpointHandlers.push(e)}onResume(e){this._resumeHandlers.push(e)}onPause(e){this._pauseHandlers.push(e)}onCancel(e){this._cancelHandlers.push(e)}async _executeWorkflow(e,t,s,r){t.status="running",this._workflowStartHandlers.forEach(i=>{i(t.executionId,e.id)}),this._agUiAdapter&&this._agUiAdapter.emitRunStarted({workflowId:e.id,executionId:t.executionId});for(let i=t.currentStepIndex;i<e.steps.length;i++){let a=e.steps[i];if(["paused","cancelled"].includes(t.status))return;if(t.stepResults[i]&&t.stepResults[i].success){t.currentStepIndex=i+1;continue}if(!this._areDependenciesMet(a,t)){let c=new Error(`Dependencies not met for step ${a.id}`);if(this._stepFailedHandlers.forEach(u=>{u(t.executionId,a.id,c)}),!e.config?.continueOnStepFailure){t.status="failed",t.completedAt=new Date,t.error=c.message,this._workflowFailedHandlers.forEach(u=>{u(t.executionId,c)}),await this._cleanupExecution(t);return}t.currentStepIndex=i+1;continue}if(!this._shouldExecuteStep(a,t.context)){t.currentStepIndex=i+1;continue}this._stepStartHandlers.forEach(c=>{c(t.executionId,a.id,i)}),this._agUiAdapter&&this._agUiAdapter.emitStepStarted(a.id,a.taskType||`Step ${i+1}`);try{let c=await this._executeStep(a,t,s);if(t.stepResults[i]=c,!c.success){let u=new Error(c.error||`Step ${a.id} failed`);if(this._stepFailedHandlers.forEach(l=>{l(t.executionId,a.id,u)}),!e.config?.continueOnStepFailure){t.status="failed",t.completedAt=new Date,t.error=u.message,this._workflowFailedHandlers.forEach(l=>{l(t.executionId,u)}),await this._cleanupExecution(t);return}t.currentStepIndex=i+1;continue}this._applyOutputMapping(a,c,t.context),this._stepCompleteHandlers.forEach(u=>{u(t.executionId,a.id,c)}),this._agUiAdapter&&this._agUiAdapter.emitStepFinished(a.id,"success",c.output),t.currentStepIndex=i+1,r&&this._storage&&(i+1)%r===0&&await this._saveCheckpoint(t)}catch(c){if(this._stepFailedHandlers.forEach(u=>{u(t.executionId,a.id,c)}),this._agUiAdapter&&this._agUiAdapter.emitStepFinished(a.id,"error"),!e.config?.continueOnStepFailure){t.status="failed",t.completedAt=new Date,t.error=c.message,this._workflowFailedHandlers.forEach(u=>{u(t.executionId,c)}),this._agUiAdapter&&this._agUiAdapter.emitRunError(c.message,c.stack),await this._cleanupExecution(t);return}t.currentStepIndex=i+1}}t.status="completed",t.completedAt=new Date;let n={executionId:t.executionId,success:t.stepResults.every(i=>i.success),completedSteps:t.stepResults.filter(i=>i.success).length,failedSteps:t.stepResults.filter(i=>!i.success).length,skippedSteps:0,outputs:t.context,duration:t.completedAt.getTime()-t.startedAt.getTime()};this._workflowCompleteHandlers.forEach(i=>{i(t.executionId,n)}),this._agUiAdapter&&this._agUiAdapter.emitRunFinished(n),await this._cleanupExecution(t)}async _saveCheckpoint(e){if(!this._storage)return;let t={workflowId:e.workflowId,executionId:e.executionId,definition:e.definition,state:{status:e.status,currentStepIndex:e.currentStepIndex,context:e.context,stepResults:e.stepResults,error:e.error,startedAt:e.startedAt,completedAt:e.completedAt},createdAt:new Date};await this._storage.saveCheckpoint(t),this._checkpointHandlers.forEach(s=>{s(t)})}async _cleanupExecution(e){if(!(!this._lifecycleService||!e.executionId)&&!this._cleanedUpExecutions.has(e.executionId)){this._cleanedUpExecutions.add(e.executionId);try{await this._lifecycleService.cleanupExecution(e.executionId)}catch(t){console.error(`Failed to cleanup execution ${e.executionId}:`,t)}}}async _executeStep(e,t,s){let r=xe(e.prompt,t.context),n={id:it("task"),type:e.taskType,prompt:r,workDir:s,priority:0,dependencies:[],createdAt:new Date,config:e.taskConfig||{}};return await this._executor.executeTask(n,e.retryPolicy)}_applyOutputMapping(e,t,s){if(e.outputMapping)for(let[r,n]of Object.entries(e.outputMapping)){let i=Se(t,n);s[r]=i}}_areDependenciesMet(e,t){if(!e.dependencies||e.dependencies.length===0)return!0;for(let s of e.dependencies){let r=t.definition.steps.findIndex(i=>i.id===s);if(r===-1||r>=t.currentStepIndex)return!1;let n=t.stepResults[r];if(!n||!n.success)return!1}return!0}_shouldExecuteStep(e,t){return e.condition?at(e.condition,t):!0}};var oe=class{_metrics;_toolCalls;_fileChanges;_toolCallHandlers=[];_fileChangeHandlers=[];_progressHandlers=[];_errorHandlers=[];_messageHandlers=[];_usageHandlers=[];_lineNumber=0;constructor(){this._metrics={totalMessages:0,toolCalls:[],fileChanges:[],usage:{inputTokens:0,outputTokens:0,cacheTokens:0,totalTokens:0,cost:0,provider:"anthropic",model:"claude"},errors:[],startedAt:new Date,lastUpdate:new Date},this._toolCalls=new Map,this._fileChanges=[]}async processLine(e){this._lineNumber++;let t=e.trim();if(t)try{let s=JSON.parse(t),r=this._detectMessageType(s),n=this._parseMessage(s,r);switch(this._metrics.totalMessages++,this._metrics.lastUpdate=new Date,n.type){case"tool_use":this._handleToolUse(n);break;case"tool_result":this._handleToolResult(n);break;case"text":this._handleText(n);break;case"usage":this._handleUsage(n);break;case"error":this._handleError(n);break;case"unknown":break}this._emitProgress()}catch(s){let r=s instanceof Error?s.message:String(s),n={message:`Failed to parse Claude Code output line ${this._lineNumber}: ${r}`,timestamp:new Date,details:{line:t,error:r,lineNumber:this._lineNumber}};this._metrics.errors.push(n),this._emitError(n)}}getMetrics(){return{...this._metrics,toolCalls:[...this._metrics.toolCalls],fileChanges:[...this._metrics.fileChanges],errors:[...this._metrics.errors]}}getToolCalls(){return Array.from(this._toolCalls.values())}getFileChanges(){return[...this._fileChanges]}onToolCall(e){this._toolCallHandlers.push(e)}onFileChange(e){this._fileChangeHandlers.push(e)}onProgress(e){this._progressHandlers.push(e)}onError(e){this._errorHandlers.push(e)}onMessage(e){this._messageHandlers.push(e)}onUsage(e){this._usageHandlers.push(e)}getToolCallsByName(e){return this.getToolCalls().filter(t=>t.name===e)}getFileChangesByPath(e){return this.getFileChanges().filter(t=>t.path===e)}getFileChangesByOperation(e){return this.getFileChanges().filter(t=>t.operation===e)}getFailedToolCalls(){return this.getToolCalls().filter(e=>e.status==="error")}getSuccessfulToolCalls(){return this.getToolCalls().filter(e=>e.status==="success")}getTotalCost(){return this._metrics.usage.cost||0}getExecutionSummary(){let e=this.getToolCalls(),t=this.getFileChanges(),s={};for(let l of e)s[l.name]=(s[l.name]||0)+1;let r={};for(let l of t)r[l.operation]=(r[l.operation]||0)+1;let n=e.filter(l=>l.status==="success"||l.status==="error"),i=e.filter(l=>l.status==="success"),a=n.length>0?i.length/n.length*100:0,u=(this._metrics.endedAt||new Date).getTime()-this._metrics.startedAt.getTime();return{totalMessages:this._metrics.totalMessages,toolCallsByType:s,fileOperationsByType:r,successRate:a,totalTokens:{input:this._metrics.usage.inputTokens,output:this._metrics.usage.outputTokens,cache:this._metrics.usage.cacheTokens},totalCost:this._metrics.usage.cost||0,duration:u,startTime:this._metrics.startedAt,endTime:this._metrics.endedAt}}_handleToolUse(e){if(e.type!=="tool_use")return;let t={id:e.id,name:e.name,input:e.input,status:"pending",timestamp:e.timestamp};this._toolCalls.set(t.id,t),this._metrics.toolCalls.push(t);for(let s of this._toolCallHandlers)try{s(t)}catch(r){console.error("Tool call handler error:",r)}}_handleToolResult(e){if(e.type!=="tool_result")return;let t=this._toolCalls.get(e.toolUseId);if(!t)return;if(t.status=e.isError?"error":"success",t.result=e.result,t.error=e.isError?String(e.result):void 0,t.completedAt=e.timestamp,["Read","Write","Edit","Glob"].includes(t.name)){let r=this._detectFileChange(t,e);if(r){this._fileChanges.push(r),this._metrics.fileChanges.push(r);for(let n of this._fileChangeHandlers)try{n(r)}catch(i){console.error("File change handler error:",i)}}}}_handleText(e){if(e.type==="text")for(let t of this._messageHandlers)try{t(e)}catch(s){console.error("Message handler error:",s)}}_handleUsage(e){if(e.type!=="usage")return;this._metrics.usage.inputTokens+=e.tokens.input,this._metrics.usage.outputTokens+=e.tokens.output,this._metrics.usage.cacheTokens+=e.tokens.cache,this._metrics.usage.totalTokens=this._metrics.usage.inputTokens+this._metrics.usage.outputTokens;let t=this._metrics.usage.inputTokens/1e6*3,s=this._metrics.usage.outputTokens/1e6*15,r=this._metrics.usage.cacheTokens/1e6*.3;this._metrics.usage.cost=t+s+r;for(let n of this._usageHandlers)try{n(this._metrics.usage)}catch(i){console.error("Usage handler error:",i)}}_handleError(e){if(e.type!=="error")return;let t={message:e.message,timestamp:e.timestamp,details:e.details};this._metrics.errors.find(s=>s.timestamp===e.timestamp)||this._metrics.errors.push(t),this._emitError(t)}_detectFileChange(e,t){let s=e.input.file_path||e.input.path;if(!s||typeof s!="string")return null;let r;switch(e.name){case"Read":case"Glob":r="read";break;case"Write":r="write";break;case"Edit":r="edit";break;default:return null}return{path:s,operation:r,timestamp:t.timestamp,toolCallId:e.id,metadata:{toolName:e.name,success:e.status==="success"}}}_detectMessageType(e){if(e.type==="error")return"error";if(e.type==="result"&&e.usage)return"usage";if(e.message?.content){let t=Array.isArray(e.message.content)?e.message.content[0]:e.message.content;if(t?.type==="tool_use")return"tool_use";if(t?.type==="tool_result")return"tool_result";if(typeof t=="string"||t?.type==="text")return"text"}return"unknown"}_parseMessage(e,t){let s=new Date;switch(t){case"text":return{type:"text",content:this._extractTextContent(e),timestamp:s,metadata:{raw:e,source:"claude-code"}};case"tool_use":{let r=this._extractToolUse(e);return{type:"tool_use",id:r.id,name:r.name,input:r.input,timestamp:s,metadata:{raw:e,source:"claude-code"}}}case"tool_result":{let r=this._extractToolResult(e);return{type:"tool_result",toolUseId:r.toolUseId,result:r.result,isError:r.isError,timestamp:s,metadata:{raw:e,source:"claude-code"}}}case"usage":return{type:"usage",tokens:this._extractUsage(e),timestamp:s,metadata:{raw:e,source:"claude-code"}};case"error":return{type:"error",message:e.error?.message||e.message||"Unknown error",details:e.error||e,timestamp:s,metadata:{raw:e,source:"claude-code"}};case"unknown":default:return{type:"unknown",raw:JSON.stringify(e),timestamp:s,metadata:{raw:e,source:"claude-code"}}}}_extractTextContent(e){return typeof e.message?.content=="string"?e.message.content:Array.isArray(e.message?.content)?e.message.content.filter(s=>s.type==="text"||typeof s=="string").map(s=>typeof s=="string"?s:s.text).join(""):""}_extractToolUse(e){let t=Array.isArray(e.message?.content)?e.message.content.find(s=>s.type==="tool_use"):e.message?.content;return{id:t?.id||"unknown",name:t?.name||"unknown",input:t?.input||{}}}_extractToolResult(e){let t=Array.isArray(e.message?.content)?e.message.content.find(s=>s.type==="tool_result"):e.message?.content;return{toolUseId:t?.tool_use_id||"unknown",result:t?.content||t?.result||null,isError:t?.is_error||!1}}_extractUsage(e){let t=e.usage||{};return{input:t.input_tokens||0,output:t.output_tokens||0,cache:t.cache_creation_input_tokens||t.cache_read_input_tokens||0}}_emitProgress(){let e=this.getMetrics();for(let t of this._progressHandlers)try{t(e)}catch(s){console.error("Progress handler error:",s)}}_emitError(e){for(let t of this._errorHandlers)try{t(e)}catch(s){console.error("Error handler error:",s)}}};import{EventType as E}from"@ag-ui/core";var ie=class{runId;threadId;listeners=new Set;processor=null;currentState={};activeToolCalls=new Map;messageCounter=0;constructor(e,t){this.runId=e,this.threadId=t||e}connectToProcessor(e){this.processor=e,e.onToolCall(this.handleToolCall.bind(this)),e.onFileChange(this.handleFileChange.bind(this)),e.onProgress(this.handleProgress.bind(this)),e.onError(this.handleError.bind(this)),e.onMessage(this.handleMessage.bind(this)),e.onUsage(this.handleUsage.bind(this))}onEvent(e){this.listeners.add(e)}offEvent(e){this.listeners.delete(e)}emitRunStarted(e){let t={type:E.RUN_STARTED,threadId:this.threadId,runId:this.runId,timestamp:Date.now(),...e&&{rawEvent:e}};this.emit(t),this.emitStateSnapshot()}emitRunFinished(e){let t={type:E.RUN_FINISHED,threadId:this.threadId,runId:this.runId,timestamp:Date.now(),...e&&{result:e}};this.emit(t)}emitStateSnapshot(){let e=this.processor?.getMetrics(),t={type:E.STATE_SNAPSHOT,timestamp:Date.now(),snapshot:{...this.currentState,...e&&{totalMessages:e.totalMessages,toolCallCount:e.toolCalls.length,fileChangeCount:e.fileChanges.length,errorCount:e.errors.length,usage:{inputTokens:e.usage.inputTokens,outputTokens:e.usage.outputTokens,totalTokens:e.usage.totalTokens}}}};this.emit(t)}handleToolCall=e=>{let t=e.id,s=Date.now();if(!this.activeToolCalls.has(t)){let r=`msg-${this.messageCounter++}`;this.activeToolCalls.set(t,{startTime:Date.now(),messageId:r});let n={type:E.TOOL_CALL_START,timestamp:s,toolCallId:t,toolCallName:e.name};this.emit(n);let i={type:E.TOOL_CALL_ARGS,timestamp:s,toolCallId:t,delta:JSON.stringify(e.input)};this.emit(i)}if(e.status==="success"||e.status==="error"){let r=this.activeToolCalls.get(t),n=r?Date.now()-r.startTime:void 0,i={type:E.TOOL_CALL_END,timestamp:s,toolCallId:t,...n!==void 0&&{rawEvent:{duration:n}}};if(this.emit(i),r){let a={type:E.TOOL_CALL_RESULT,timestamp:s,messageId:r.messageId,toolCallId:t,content:e.status==="success"?typeof e.result=="string"?e.result:JSON.stringify(e.result):e.error||"Tool call failed"};this.emit(a)}this.activeToolCalls.delete(t),this.emitStateDelta({toolCallCount:this.processor?.getToolCalls().length||0})}};handleFileChange=e=>{let t={type:E.CUSTOM,timestamp:Date.now(),name:"file_change",value:{path:e.path,operation:e.operation,toolCallId:e.toolCallId,changes:e.changes}};this.emit(t),this.emitStateDelta({fileChangeCount:this.processor?.getFileChanges().length||0})};handleProgress=e=>{this.emitStateDelta({totalMessages:e.totalMessages,toolCallCount:e.toolCalls.length,fileChangeCount:e.fileChanges.length,errorCount:e.errors.length,usage:{inputTokens:e.usage.inputTokens,outputTokens:e.usage.outputTokens,totalTokens:e.usage.totalTokens}})};handleError=e=>{let t={type:E.RUN_ERROR,timestamp:e.timestamp.getTime(),message:e.message,...e.details&&{rawEvent:{details:e.details}}};this.emit(t),this.emitStateDelta({errorCount:this.processor?.getMetrics().errors.length||0})};handleMessage=e=>{if(e.type!=="text")return;let t=`msg-${this.messageCounter++}`,s=Date.now(),r={type:E.TEXT_MESSAGE_START,timestamp:s,messageId:t,role:"assistant"};this.emit(r);let n={type:E.TEXT_MESSAGE_CONTENT,timestamp:s,messageId:t,delta:e.content};this.emit(n);let i={type:E.TEXT_MESSAGE_END,timestamp:s,messageId:t};this.emit(i)};handleUsage=e=>{let t=Date.now(),s={type:E.CUSTOM,timestamp:t,name:"USAGE_UPDATE",value:{inputTokens:e.inputTokens,outputTokens:e.outputTokens,cacheTokens:e.cacheTokens,totalTokens:e.totalTokens,cost:e.cost,provider:e.provider,model:e.model}};this.emit(s),this.emitStateDelta({usage:{inputTokens:e.inputTokens,outputTokens:e.outputTokens,totalTokens:e.totalTokens}})};emitStateDelta(e){this.currentState={...this.currentState,...e};let t=Object.entries(e).map(([r,n])=>({op:"replace",path:`/${r}`,value:n})),s={type:E.STATE_DELTA,timestamp:Date.now(),delta:t};this.emit(s)}emit(e){this.listeners.forEach(t=>{try{t(e)}catch(s){console.error("Error in AG-UI event listener:",s)}})}emitStepStarted(e,t){let s={type:E.STEP_STARTED,timestamp:Date.now(),stepName:t,rawEvent:{runId:this.runId,stepId:e}};this.emit(s)}emitStepFinished(e,t,s){let r={type:E.STEP_FINISHED,timestamp:Date.now(),stepName:e,rawEvent:{runId:this.runId,stepId:e,status:t,...s&&{output:s}}};this.emit(r)}emitRunError(e,t,s){let r={type:E.RUN_ERROR,timestamp:Date.now(),message:e,...s&&{code:s},...t&&{rawEvent:{stack:t}}};this.emit(r)}getState(){return{...this.currentState}}getRunId(){return this.runId}};function _e(o,e){let t=new oe,s=new ie(o,e);return s.connectToProcessor(t),{processor:t,adapter:s}}var q=class{db;templateEngine;lifecycleService;repoPath;transportManager;activeOrchestrators=new Map;constructor(e,t,s,r){this.db=e,this.repoPath=t,this.templateEngine=new U,this.lifecycleService=s||new N(e,t),this.transportManager=r}async prepareExecution(e,t){let s=this.db.prepare("SELECT * FROM issues WHERE id = ?").get(e);if(!s)throw new Error(`Issue ${e} not found`);let r=this.db.prepare(`
|
|
80
|
-
SELECT DISTINCT s.id, s.title
|
|
81
|
-
FROM specs s
|
|
82
|
-
JOIN relationships r ON r.to_id = s.id AND r.to_type = 'spec'
|
|
83
|
-
WHERE r.from_id = ? AND r.from_type = 'issue'
|
|
84
|
-
AND r.relationship_type IN ('implements', 'references')
|
|
85
|
-
ORDER BY s.title
|
|
86
|
-
`).all(e),n={issueId:s.id,title:s.title,description:s.content,relatedSpecs:r.length>0?r.map(d=>({id:d.id,title:d.title})):void 0},i;if(t?.templateId){let d=Ve(this.db,t.templateId);if(!d)throw new Error(`Template ${t.templateId} not found`);i=d.template}else{let d=Je(this.db,"issue");if(!d)throw new Error("Default issue template not found");i=d.template}let a=this.templateEngine.render(i,n),c={mode:"worktree",model:"claude-sonnet-4",baseBranch:"main",checkpointInterval:1,continueOnStepFailure:!1,captureFileChanges:!0,captureToolCalls:!0,...t?.config},u=[],l=[];return a.trim()||l.push("Rendered prompt is empty"),{renderedPrompt:a,issue:{id:s.id,title:s.title,content:s.content},relatedSpecs:r,defaultConfig:c,warnings:u,errors:l}}async createExecution(e,t,s){if(!s.trim())throw new Error("Prompt cannot be empty");let r=this.db.prepare("SELECT * FROM issues WHERE id = ?").get(e);if(!r)throw new Error(`Issue ${e} not found`);let n=t.mode||"worktree",i,a;if(n==="worktree"){let f=await this.lifecycleService.createExecutionWithWorktree({issueId:e,issueTitle:r.title,agentType:"claude-code",targetBranch:t.baseBranch||"main",repoPath:this.repoPath,mode:n,prompt:s,config:JSON.stringify(t)});i=f.execution,a=f.worktreePath}else{let f=ct();i=Y(this.db,{id:f,issue_id:e,agent_type:"claude-code",mode:n,prompt:s,config:JSON.stringify(t),target_branch:t.baseBranch||"main",branch_name:t.baseBranch||"main"}),a=this.repoPath}let c={id:`workflow-${i.id}`,steps:[{id:"execute-issue",taskType:"issue",prompt:s,taskConfig:{model:t.model||"claude-sonnet-4",timeout:t.timeout,captureFileChanges:t.captureFileChanges??!0,captureToolCalls:t.captureToolCalls??!0}}],config:{checkpointInterval:t.checkpointInterval??1,continueOnStepFailure:t.continueOnStepFailure??!1,timeout:t.timeout},metadata:{workDir:a,issueId:e,executionId:i.id}},u=new X({executablePath:"claude",args:["--print","--output-format","stream-json","--dangerously-skip-permissions"]}),l=new F(u,{maxConcurrent:1}),d=new O(l),p;if(this.transportManager){let f=_e(i.id);p=f.adapter,this.transportManager.connectAdapter(p,i.id);let m="";l=new F(u,{maxConcurrent:1,onOutput:(y,Ee)=>{if(Ee==="stdout"){m+=y.toString();let P;for(;(P=m.indexOf(`
|
|
87
|
-
`))!==-1;){let D=m.slice(0,P);m=m.slice(P+1),D.trim()&&f.processor.processLine(D).catch(A=>{console.error("[ExecutionService] Error processing output line:",{error:A instanceof Error?A.message:String(A),line:D.slice(0,100)})})}}}}),d=new O(l)}let g=new K(d,void 0,p,this.lifecycleService);return g.onWorkflowStart(()=>{try{C(this.db,i.id,{status:"running"})}catch(f){console.error("[ExecutionService] Failed to update execution status to running",{executionId:i.id,error:f instanceof Error?f.message:String(f)})}}),g.onWorkflowComplete(()=>{console.log("[ExecutionService] Workflow completed successfully",{executionId:i.id});try{C(this.db,i.id,{status:"completed",completed_at:new Date().toISOString()})}catch(f){console.error("[ExecutionService] Failed to update execution status to completed",{executionId:i.id,error:f instanceof Error?f.message:String(f),note:"Execution may have been deleted (e.g., due to CASCADE DELETE from issue deletion)"})}this.activeOrchestrators.delete(i.id)}),g.onWorkflowFailed((f,m)=>{console.error("[ExecutionService] Workflow failed",{executionId:i.id,error:m.message,stack:m.stack});try{C(this.db,i.id,{status:"failed",completed_at:new Date().toISOString(),error_message:m.message})}catch(y){console.error("[ExecutionService] Failed to update execution status to failed",{executionId:i.id,error:y instanceof Error?y.message:String(y),note:"Execution may have been deleted (e.g., due to CASCADE DELETE from issue deletion)"})}this.activeOrchestrators.delete(i.id)}),g.startWorkflow(c,a,{checkpointInterval:t.checkpointInterval,executionId:i.id}),this.activeOrchestrators.set(i.id,g),i}async createFollowUp(e,t){let s=x(this.db,e);if(!s)throw new Error(`Execution ${e} not found`);if(!s.worktree_path)throw new Error(`Cannot create follow-up: execution ${e} has no worktree`);if(this.lifecycleService&&((await import("fs")).existsSync(s.worktree_path)||(console.log(`Recreating worktree for follow-up execution: ${s.worktree_path}`),await this.lifecycleService.worktreeManager.createWorktree({repoPath:this.repoPath,branchName:s.branch_name,worktreePath:s.worktree_path,baseBranch:s.target_branch,createBranch:!1}))),!s.issue_id)throw new Error("Previous execution must have an issue_id for follow-up");let n=`${(await this.prepareExecution(s.issue_id)).renderedPrompt}
|
|
88
|
-
|
|
89
|
-
## Follow-up Feedback
|
|
90
|
-
${t}
|
|
91
|
-
|
|
92
|
-
Please continue working on this issue, taking into account the feedback above.`,i=ct(),a=Y(this.db,{id:i,issue_id:s.issue_id,agent_type:"claude-code",target_branch:s.target_branch,branch_name:s.branch_name,worktree_path:s.worktree_path,config:s.config||void 0}),c={id:`workflow-${a.id}`,steps:[{id:"execute-followup",taskType:"issue",prompt:n,taskConfig:{model:"claude-sonnet-4",captureFileChanges:!0,captureToolCalls:!0}}],config:{checkpointInterval:1,continueOnStepFailure:!1},metadata:{workDir:s.worktree_path,issueId:s.issue_id,executionId:a.id,followUpOf:e}},u=new X({executablePath:"claude",args:["--print","--output-format","stream-json","--dangerously-skip-permissions"]}),l=new F(u,{maxConcurrent:1}),d=new O(l),p;if(this.transportManager){let f=_e(a.id);p=f.adapter,this.transportManager.connectAdapter(p,a.id);let m="";l=new F(u,{maxConcurrent:1,onOutput:(y,Ee)=>{if(Ee==="stdout"){m+=y.toString();let P;for(;(P=m.indexOf(`
|
|
93
|
-
`))!==-1;){let D=m.slice(0,P);m=m.slice(P+1),D.trim()&&f.processor.processLine(D).catch(A=>{console.error("[ExecutionService] Error processing output line:",{error:A instanceof Error?A.message:String(A),line:D.slice(0,100)})})}}}}),d=new O(l)}let g=new K(d,void 0,p,this.lifecycleService);return g.onWorkflowStart(()=>{try{C(this.db,a.id,{status:"running"})}catch(f){console.error("[ExecutionService] Failed to update follow-up execution status to running",{executionId:a.id,error:f instanceof Error?f.message:String(f)})}}),g.onWorkflowComplete(()=>{try{C(this.db,a.id,{status:"completed",completed_at:new Date().toISOString()})}catch(f){console.error("[ExecutionService] Failed to update follow-up execution status to completed",{executionId:a.id,error:f instanceof Error?f.message:String(f)})}this.activeOrchestrators.delete(a.id)}),g.onWorkflowFailed((f,m)=>{try{C(this.db,a.id,{status:"failed",completed_at:new Date().toISOString(),error_message:m.message})}catch(y){console.error("[ExecutionService] Failed to update follow-up execution status to failed",{executionId:a.id,error:y instanceof Error?y.message:String(y)})}this.activeOrchestrators.delete(a.id)}),g.startWorkflow(c,s.worktree_path,{checkpointInterval:1,executionId:a.id}),this.activeOrchestrators.set(a.id,g),a}async cancelExecution(e){let t=x(this.db,e);if(!t)throw new Error(`Execution ${e} not found`);if(t.status!=="running")throw new Error(`Cannot cancel execution in ${t.status} state`);let s=this.activeOrchestrators.get(e);s&&(await s.cancelWorkflow(e),this.activeOrchestrators.delete(e)),C(this.db,e,{status:"stopped",completed_at:new Date().toISOString()})}async cleanupExecution(e){await this.lifecycleService.cleanupExecution(e)}async worktreeExists(e){let t=x(this.db,e);return!t||!t.worktree_path?!1:(await import("fs")).existsSync(t.worktree_path)}async deleteWorktree(e){let t=x(this.db,e);if(!t)throw new Error(`Execution ${e} not found`);if(!t.worktree_path)throw new Error(`Execution ${e} has no worktree to delete`);if(!(await import("fs")).existsSync(t.worktree_path))throw new Error(`Worktree does not exist in filesystem: ${t.worktree_path}`);await this.lifecycleService.worktreeManager.cleanupWorktree(t.worktree_path,this.repoPath)}async shutdown(){let e=[];for(let[t,s]of this.activeOrchestrators.entries())e.push(s.cancelWorkflow(t).catch(r=>{console.error("[ExecutionService] Error canceling execution",{executionId:t,error:r.message})}));await Promise.race([Promise.all(e),new Promise(t=>setTimeout(t,5e3))])}listExecutions(e){return this.db.prepare(`
|
|
94
|
-
SELECT * FROM executions
|
|
95
|
-
WHERE issue_id = ?
|
|
96
|
-
ORDER BY created_at DESC
|
|
97
|
-
`).all(e)}getExecution(e){return x(this.db,e)}};ae();import{Router as Cs}from"express";import{generateIssueId as Is}from"@sudocode-ai/cli/dist/id-generator.js";import{WebSocketServer as Es,WebSocket as Pe}from"ws";import{randomUUID as vs}from"crypto";var ce=!1,De=class{wss=null;clients=new Map;heartbeatInterval=null;HEARTBEAT_INTERVAL=3e4;init(e,t="/ws"){if(this.wss){console.warn("[websocket] WebSocket server already initialized");return}this.wss=new Es({server:e,path:t}),console.log(`[websocket] WebSocket server initialized on path: ${t}`),this.wss.on("connection",this.handleConnection.bind(this)),this.startHeartbeat()}handleConnection(e,t){let s=vs(),r={id:s,ws:e,subscriptions:new Set,isAlive:!0,connectedAt:new Date};this.clients.set(s,r),ce&&console.log(`[websocket] Client connected: ${s} (total: ${this.clients.size})`),e.on("message",n=>this.handleMessage(s,n)),e.on("close",()=>this.handleDisconnection(s)),e.on("error",n=>this.handleError(s,n)),e.on("pong",()=>this.handlePong(s)),this.sendToClient(s,{type:"pong",message:"Connected to sudocode server"})}handleDisconnection(e){let t=this.clients.get(e);t&&(ce&&console.log(`[websocket] Client disconnected: ${e} (subscriptions: ${t.subscriptions.size})`),this.clients.delete(e))}handleError(e,t){console.error(`[websocket] Client error (${e}):`,t.message)}handlePong(e){let t=this.clients.get(e);t&&(t.isAlive=!0)}handleMessage(e,t){if(this.clients.get(e))try{let r=JSON.parse(t.toString());switch(r.type){case"subscribe":this.handleSubscribe(e,r);break;case"unsubscribe":this.handleUnsubscribe(e,r);break;case"ping":this.sendToClient(e,{type:"pong"});break;default:this.sendToClient(e,{type:"error",message:`Unknown message type: ${r.type}`})}}catch(r){console.error(`[websocket] Failed to parse message from ${e}:`,r),this.sendToClient(e,{type:"error",message:"Invalid message format"})}}handleSubscribe(e,t){let s=this.clients.get(e);if(!s)return;let r;if(t.entity_type==="all")r="all";else if(t.entity_type&&t.entity_id)r=`${t.entity_type}:${t.entity_id}`;else if(t.entity_type)r=`${t.entity_type}:*`;else{this.sendToClient(e,{type:"error",message:"Invalid subscription request"});return}s.subscriptions.add(r),ce&&console.log(`[websocket] Client ${e} subscribed to: ${r}`),this.sendToClient(e,{type:"subscribed",subscription:r,message:`Subscribed to ${r}`})}handleUnsubscribe(e,t){let s=this.clients.get(e);if(!s)return;let r;if(t.entity_type==="all")r="all";else if(t.entity_type&&t.entity_id)r=`${t.entity_type}:${t.entity_id}`;else if(t.entity_type)r=`${t.entity_type}:*`;else{this.sendToClient(e,{type:"error",message:"Invalid unsubscription request"});return}s.subscriptions.delete(r),ce&&console.log(`[websocket] Client ${e} unsubscribed from: ${r}`),this.sendToClient(e,{type:"unsubscribed",subscription:r,message:`Unsubscribed from ${r}`})}sendToClient(e,t){let s=this.clients.get(e);if(!(!s||s.ws.readyState!==Pe.OPEN))try{s.ws.send(JSON.stringify(t))}catch(r){console.error(`[websocket] Failed to send message to ${e}:`,r)}}broadcast(e,t,s){let r=`${e}:${t}`,n=`${e}:*`,i=0;this.clients.forEach(a=>{if(a.ws.readyState===Pe.OPEN&&(a.subscriptions.has(r)||a.subscriptions.has(n)||a.subscriptions.has("all")))try{a.ws.send(JSON.stringify(s)),i++}catch(c){console.error(`[websocket] Failed to broadcast to ${a.id}:`,c)}}),i>0&&console.log(`[websocket] Broadcasted ${s.type} for ${r} to ${i} clients`)}broadcastGeneric(e){let t=0;this.clients.forEach(s=>{if(s.ws.readyState===Pe.OPEN&&s.subscriptions.has("all"))try{s.ws.send(JSON.stringify(e)),t++}catch(r){console.error(`[websocket] Failed to broadcast to ${s.id}:`,r)}}),t>0&&console.log(`[websocket] Broadcasted ${e.type} to ${t} clients`)}startHeartbeat(){this.heartbeatInterval||(this.heartbeatInterval=setInterval(()=>{this.clients.forEach((e,t)=>{if(!e.isAlive){console.log(`[websocket] Terminating dead connection: ${t}`),e.ws.terminate(),this.clients.delete(t);return}e.isAlive=!1;try{e.ws.ping()}catch(s){console.error(`[websocket] Failed to ping ${t}:`,s)}})},this.HEARTBEAT_INTERVAL),console.log(`[websocket] Heartbeat started (interval: ${this.HEARTBEAT_INTERVAL}ms)`))}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null,console.log("[websocket] Heartbeat stopped"))}getStats(){return{totalClients:this.clients.size,clients:Array.from(this.clients.values()).map(e=>({id:e.id,subscriptions:Array.from(e.subscriptions),connectedAt:e.connectedAt.toISOString(),isAlive:e.isAlive}))}}async shutdown(){if(console.log("[websocket] Shutting down WebSocket server..."),this.stopHeartbeat(),this.clients.forEach(e=>{try{e.ws.close(1e3,"Server shutting down")}catch(t){console.error(`[websocket] Error closing client ${e.id}:`,t)}}),this.clients.clear(),this.wss)return new Promise(e=>{this.wss.close(()=>{console.log("[websocket] WebSocket server closed"),this.wss=null,e()})})}},W=new De;function lt(o,e){W.init(o,e)}function j(o,e,t){W.broadcast("issue",o,{type:`issue_${e}`,data:t})}function H(o,e,t){W.broadcast("spec",o,{type:`spec_${e}`,data:t})}function ue(o,e){W.broadcastGeneric({type:`feedback_${o}`,data:e})}function Ae(o,e){W.broadcastGeneric({type:`relationship_${o}`,data:e})}function dt(){return W.getStats()}async function Me(){await W.shutdown()}import*as pt from"path";function $(){return process.env.SUDOCODE_DIR||pt.join(process.cwd(),".sudocode")}import{exportToJSONL as _s}from"@sudocode-ai/cli/dist/export.js";import{syncJSONLToMarkdown as gt}from"@sudocode-ai/cli/dist/sync.js";import*as de from"path";var He=null;function Ts(o){return He||(He={db:o,timeoutId:null,pending:!1}),He}async function Rs(o){let e=$();await _s(o,{outputDir:e})}async function J(o,e,t){let s=$();if(t==="issue"){let{getIssueById:r}=await Promise.resolve().then(()=>(ae(),ut)),n=r(o,e);if(n){let i=de.join(s,"issues",`${n.id}.md`);await gt(o,n.id,"issue",i)}}else{let{getSpecById:r}=await Promise.resolve().then(()=>(le(),ft)),n=r(o,e);if(n){let i=n.file_path?de.join(s,n.file_path):de.join(s,"specs",`${n.id}.md`);await gt(o,n.id,"spec",i)}}}function S(o){let e=Ts(o);e.pending=!0,e.timeoutId&&clearTimeout(e.timeoutId),e.timeoutId=setTimeout(async()=>{try{await Rs(o)}catch(t){console.error("Export failed:",t)}finally{e.pending=!1,e.timeoutId=null}},2e3)}function mt(o){let e=Cs();return e.get("/",(t,s)=>{try{let r={};t.query.status&&(r.status=t.query.status),t.query.priority&&(r.priority=parseInt(t.query.priority,10)),t.query.assignee&&(r.assignee=t.query.assignee),r.archived=t.query.archived!==void 0?t.query.archived==="true":!1,t.query.limit&&(r.limit=parseInt(t.query.limit,10)),t.query.offset&&(r.offset=parseInt(t.query.offset,10));let n=Te(o,r);s.json({success:!0,data:n})}catch(r){console.error("Error listing issues:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to list issues"})}}),e.get("/:id",(t,s)=>{try{let{id:r}=t.params,n=G(o,r);if(!n){s.status(404).json({success:!1,data:null,message:`Issue not found: ${r}`});return}s.json({success:!0,data:n})}catch(r){console.error("Error getting issue:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to get issue"})}}),e.post("/",(t,s)=>{try{let{title:r,content:n,status:i,priority:a,assignee:c,parent_id:u}=t.body;if(!r||typeof r!="string"){s.status(400).json({success:!1,data:null,message:"Title is required and must be a string"});return}if(r.length>500){s.status(400).json({success:!1,data:null,message:"Title must be 500 characters or less"});return}let l=$(),d=Is(o,l),p=Re(o,{id:d,title:r,content:n||"",status:i||"open",priority:a!==void 0?a:2,assignee:c||void 0,parent_id:u||void 0});S(o),J(o,p.id,"issue").catch(g=>{console.error(`Failed to sync issue ${p.id} to markdown:`,g)}),j(p.id,"created",p),s.status(201).json({success:!0,data:p})}catch(r){console.error("Error creating issue:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to create issue"})}}),e.put("/:id",(t,s)=>{try{let{id:r}=t.params,{title:n,content:i,status:a,priority:c,assignee:u,parent_id:l,archived:d}=t.body;if(n===void 0&&i===void 0&&a===void 0&&c===void 0&&u===void 0&&l===void 0&&d===void 0){s.status(400).json({success:!1,data:null,message:"At least one field must be provided for update"});return}if(n!==void 0&&typeof n=="string"&&n.length>500){s.status(400).json({success:!1,data:null,message:"Title must be 500 characters or less"});return}let p={};n!==void 0&&(p.title=n),i!==void 0&&(p.content=i),a!==void 0&&(p.status=a),c!==void 0&&(p.priority=c),u!==void 0&&(p.assignee=u),l!==void 0&&(p.parent_id=l),d!==void 0&&(p.archived=d,p.archived_at=d?new Date().toISOString():null);let g=Ce(o,r,p);S(o),J(o,g.id,"issue").catch(f=>{console.error(`Failed to sync issue ${g.id} to markdown:`,f)}),j(g.id,"updated",g),s.json({success:!0,data:g})}catch(r){if(console.error("Error updating issue:",r),r instanceof Error&&r.message.includes("not found")){s.status(404).json({success:!1,data:null,message:r.message});return}s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to update issue"})}}),e.delete("/:id",(t,s)=>{try{let{id:r}=t.params;if(!G(o,r)){s.status(404).json({success:!1,data:null,message:`Issue not found: ${r}`});return}Ie(o,r)?(S(o),j(r,"deleted",{id:r}),s.json({success:!0,data:{id:r,deleted:!0}})):s.status(500).json({success:!1,data:null,message:"Failed to delete issue"})}catch(r){console.error("Error deleting issue:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to delete issue"})}}),e}le();import{Router as Ps}from"express";import{generateSpecId as Ds}from"@sudocode-ai/cli/dist/id-generator.js";import*as ht from"path";function yt(o){let e=Ps();return e.get("/",(t,s)=>{try{let r={};t.query.priority&&(r.priority=parseInt(t.query.priority,10)),r.archived=t.query.archived!==void 0?t.query.archived==="true":!1,t.query.limit&&(r.limit=parseInt(t.query.limit,10)),t.query.offset&&(r.offset=parseInt(t.query.offset,10));let n=Fe(o,r);s.json({success:!0,data:n})}catch(r){console.error("Error listing specs:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to list specs"})}}),e.get("/:id",(t,s)=>{try{let{id:r}=t.params,n=z(o,r);if(!n){s.status(404).json({success:!1,data:null,message:`Spec not found: ${r}`});return}s.json({success:!0,data:n})}catch(r){console.error("Error getting spec:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to get spec"})}}),e.post("/",(t,s)=>{try{let{title:r,content:n,priority:i,parent_id:a}=t.body;if(!r||typeof r!="string"){s.status(400).json({success:!1,data:null,message:"Title is required and must be a string"});return}if(r.length>500){s.status(400).json({success:!1,data:null,message:"Title must be 500 characters or less"});return}let c=$(),u=Ds(o,c),l=ht.join(c,"specs",`${u}.md`),d=Oe(o,{id:u,title:r,file_path:l,content:n||"",priority:i!==void 0?i:2,parent_id:a||void 0});S(o),J(o,d.id,"spec").catch(p=>{console.error(`Failed to sync spec ${d.id} to markdown:`,p)}),H(d.id,"created",d),s.status(201).json({success:!0,data:d})}catch(r){console.error("Error creating spec:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to create spec"})}}),e.put("/:id",(t,s)=>{try{let{id:r}=t.params,{title:n,content:i,priority:a,parent_id:c,archived:u}=t.body;if(n===void 0&&i===void 0&&a===void 0&&c===void 0&&u===void 0){s.status(400).json({success:!1,data:null,message:"At least one field must be provided for update"});return}if(n!==void 0&&typeof n=="string"&&n.length>500){s.status(400).json({success:!1,data:null,message:"Title must be 500 characters or less"});return}let l={};n!==void 0&&(l.title=n),i!==void 0&&(l.content=i),a!==void 0&&(l.priority=a),c!==void 0&&(l.parent_id=c),u!==void 0&&(l.archived=u,l.archived_at=u?new Date().toISOString():null);let d=We(o,r,l);S(o),J(o,d.id,"spec").catch(p=>{console.error(`Failed to sync spec ${d.id} to markdown:`,p)}),H(d.id,"updated",d),s.json({success:!0,data:d})}catch(r){if(console.error("Error updating spec:",r),r instanceof Error&&r.message.includes("not found")){s.status(404).json({success:!1,data:null,message:r.message});return}s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to update spec"})}}),e.delete("/:id",(t,s)=>{try{let{id:r}=t.params;if(!z(o,r)){s.status(404).json({success:!1,data:null,message:`Spec not found: ${r}`});return}je(o,r)?(S(o),H(r,"deleted",{id:r}),s.json({success:!0,data:{id:r,deleted:!0}})):s.status(500).json({success:!1,data:null,message:"Failed to delete spec"})}catch(r){console.error("Error deleting spec:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to delete spec"})}}),e}import{Router as js}from"express";import{addRelationship as As,getRelationship as io,removeRelationship as Ms,getOutgoingRelationships as Fs,getIncomingRelationships as Os,getAllRelationships as Ws}from"@sudocode-ai/cli/dist/operations/relationships.js";function Et(o,e){return As(o,e)}function vt(o,e,t,s,r,n){return Ms(o,e,t,s,r,n)}function bt(o,e,t,s){return Fs(o,e,t,s)}function kt(o,e,t,s){return Os(o,e,t,s)}function wt(o,e,t){return Ws(o,e,t)}function xt(o){let e=js();return e.get("/:entity_type/:entity_id",(t,s)=>{try{let{entity_type:r,entity_id:n}=t.params;if(r!=="spec"&&r!=="issue"){s.status(400).json({success:!1,data:null,message:"Invalid entity_type. Must be 'spec' or 'issue'"});return}let i=wt(o,n,r);s.json({success:!0,data:i})}catch(r){console.error("Error getting relationships:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to get relationships"})}}),e.get("/:entity_type/:entity_id/outgoing",(t,s)=>{try{let{entity_type:r,entity_id:n}=t.params,{relationship_type:i}=t.query;if(r!=="spec"&&r!=="issue"){s.status(400).json({success:!1,data:null,message:"Invalid entity_type. Must be 'spec' or 'issue'"});return}let a=bt(o,n,r,i);s.json({success:!0,data:a})}catch(r){console.error("Error getting outgoing relationships:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to get outgoing relationships"})}}),e.get("/:entity_type/:entity_id/incoming",(t,s)=>{try{let{entity_type:r,entity_id:n}=t.params,{relationship_type:i}=t.query;if(r!=="spec"&&r!=="issue"){s.status(400).json({success:!1,data:null,message:"Invalid entity_type. Must be 'spec' or 'issue'"});return}let a=kt(o,n,r,i);s.json({success:!0,data:a})}catch(r){console.error("Error getting incoming relationships:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to get incoming relationships"})}}),e.post("/",(t,s)=>{try{let{from_id:r,from_type:n,to_id:i,to_type:a,relationship_type:c,metadata:u}=t.body;if(!r||typeof r!="string"){s.status(400).json({success:!1,data:null,message:"from_id is required and must be a string"});return}if(!n||n!=="spec"&&n!=="issue"){s.status(400).json({success:!1,data:null,message:"from_type is required and must be 'spec' or 'issue'"});return}if(!i||typeof i!="string"){s.status(400).json({success:!1,data:null,message:"to_id is required and must be a string"});return}if(!a||a!=="spec"&&a!=="issue"){s.status(400).json({success:!1,data:null,message:"to_type is required and must be 'spec' or 'issue'"});return}if(!c||typeof c!="string"){s.status(400).json({success:!1,data:null,message:"relationship_type is required and must be a string"});return}let l=["blocks","related","discovered-from","implements","references","depends-on"];if(!l.includes(c)){s.status(400).json({success:!1,data:null,message:`Invalid relationship_type. Must be one of: ${l.join(", ")}`});return}let d=Et(o,{from_id:r,from_type:n,to_id:i,to_type:a,relationship_type:c,metadata:u||null});Ae("created",d),S(o),s.status(201).json({success:!0,data:d})}catch(r){if(console.error("Error creating relationship:",r),r instanceof Error){if(r.message.includes("not found")){s.status(404).json({success:!1,data:null,message:r.message});return}if(r.message.includes("already exists")){s.status(409).json({success:!1,data:null,message:r.message});return}}s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to create relationship"})}}),e.delete("/",(t,s)=>{try{let{from_id:r,from_type:n,to_id:i,to_type:a,relationship_type:c}=t.body;if(!r||typeof r!="string"){s.status(400).json({success:!1,data:null,message:"from_id is required and must be a string"});return}if(!n||n!=="spec"&&n!=="issue"){s.status(400).json({success:!1,data:null,message:"from_type is required and must be 'spec' or 'issue'"});return}if(!i||typeof i!="string"){s.status(400).json({success:!1,data:null,message:"to_id is required and must be a string"});return}if(!a||a!=="spec"&&a!=="issue"){s.status(400).json({success:!1,data:null,message:"to_type is required and must be 'spec' or 'issue'"});return}if(!c||typeof c!="string"){s.status(400).json({success:!1,data:null,message:"relationship_type is required and must be a string"});return}vt(o,r,n,i,a,c)?(Ae("deleted",{from_id:r,from_type:n,to_id:i,to_type:a,relationship_type:c}),S(o),s.json({success:!0,data:{from_id:r,from_type:n,to_id:i,to_type:a,relationship_type:c,deleted:!0}})):s.status(404).json({success:!1,data:null,message:"Relationship not found"})}catch(r){console.error("Error deleting relationship:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to delete relationship"})}}),e}import{Router as Ls}from"express";import{createFeedback as Hs,getFeedback as $s,updateFeedback as Us,deleteFeedback as Bs,listFeedback as Ns,getFeedbackForSpec as yo,getFeedbackForIssue as Eo,dismissFeedback as vo}from"@sudocode-ai/cli/dist/operations/feedback.js";function St(o,e){return Hs(o,e)}function $e(o,e){return $s(o,e)}function _t(o,e,t){return Us(o,e,t)}function Tt(o,e){return Bs(o,e)}function Rt(o,e){return Ns(o,e||{})}function Ct(o){let e=Ls();return e.get("/",(t,s)=>{try{let r={};t.query.spec_id&&(r.spec_id=t.query.spec_id),t.query.issue_id&&(r.issue_id=t.query.issue_id),t.query.feedback_type&&(r.feedback_type=t.query.feedback_type),t.query.dismissed!==void 0&&(r.dismissed=t.query.dismissed==="true"),t.query.limit&&(r.limit=parseInt(t.query.limit,10)),t.query.offset&&(r.offset=parseInt(t.query.offset,10));let n=Rt(o,r);s.json({success:!0,data:n})}catch(r){console.error("Error listing feedback:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to list feedback"})}}),e.get("/:id",(t,s)=>{try{let{id:r}=t.params,n=$e(o,r);if(!n){s.status(404).json({success:!1,data:null,message:`Feedback not found: ${r}`});return}s.json({success:!0,data:n})}catch(r){console.error("Error getting feedback:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to get feedback"})}}),e.post("/",(t,s)=>{try{let{issue_id:r,spec_id:n,feedback_type:i,content:a,agent:c,anchor:u,dismissed:l}=t.body;if(!r||typeof r!="string"){s.status(400).json({success:!1,data:null,message:"issue_id is required and must be a string"});return}if(!n||typeof n!="string"){s.status(400).json({success:!1,data:null,message:"spec_id is required and must be a string"});return}if(!i||typeof i!="string"){s.status(400).json({success:!1,data:null,message:"feedback_type is required and must be a string"});return}let d=["comment","suggestion","request"];if(!d.includes(i)){s.status(400).json({success:!1,data:null,message:`Invalid feedback_type. Must be one of: ${d.join(", ")}`});return}if(!a||typeof a!="string"){s.status(400).json({success:!1,data:null,message:"content is required and must be a string"});return}if(u!=null){if(typeof u!="object"){s.status(400).json({success:!1,data:null,message:"anchor must be an object if provided"});return}if(u.anchor_status){let g=["valid","relocated","stale"];if(!g.includes(u.anchor_status)){s.status(400).json({success:!1,data:null,message:`Invalid anchor.anchor_status. Must be one of: ${g.join(", ")}`});return}}}let p=St(o,{issue_id:r,spec_id:n,feedback_type:i,content:a,agent:c||void 0,anchor:u,dismissed:l||!1});ue("created",p),s.status(201).json({success:!0,data:p})}catch(r){if(console.error("Error creating feedback:",r),r instanceof Error){if(r.message.includes("not found")){s.status(404).json({success:!1,data:null,message:r.message});return}if(r.message.includes("Constraint violation")){s.status(409).json({success:!1,data:null,message:r.message});return}}s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to create feedback"})}}),e.put("/:id",(t,s)=>{try{let{id:r}=t.params,{content:n,dismissed:i,anchor:a}=t.body;if(n===void 0&&i===void 0&&a===void 0){s.status(400).json({success:!1,data:null,message:"At least one field must be provided for update"});return}if(a!==void 0){if(typeof a!="object"){s.status(400).json({success:!1,data:null,message:"anchor must be an object"});return}if(!a.anchor_status||typeof a.anchor_status!="string"){s.status(400).json({success:!1,data:null,message:"anchor.anchor_status is required and must be a string"});return}let l=["valid","relocated","stale"];if(!l.includes(a.anchor_status)){s.status(400).json({success:!1,data:null,message:`Invalid anchor.anchor_status. Must be one of: ${l.join(", ")}`});return}}let c={};n!==void 0&&(c.content=n),i!==void 0&&(c.dismissed=i),a!==void 0&&(c.anchor=a);let u=_t(o,r,c);ue("updated",u),s.json({success:!0,data:u})}catch(r){if(console.error("Error updating feedback:",r),r instanceof Error&&r.message.includes("not found")){s.status(404).json({success:!1,data:null,message:r.message});return}s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to update feedback"})}}),e.delete("/:id",(t,s)=>{try{let{id:r}=t.params;if(!$e(o,r)){s.status(404).json({success:!1,data:null,message:`Feedback not found: ${r}`});return}Tt(o,r)?(ue("deleted",{id:r}),s.json({success:!0,data:{id:r,deleted:!0}})):s.status(500).json({success:!1,data:null,message:"Failed to delete feedback"})}catch(r){console.error("Error deleting feedback:",r),s.status(500).json({success:!1,data:null,error_data:r instanceof Error?r.message:String(r),message:"Failed to delete feedback"})}}),e}import{Router as qs}from"express";function It(o,e,t,s){let r=qs(),n=s||new q(o,e,void 0,t);return r.post("/issues/:issueId/executions/prepare",async(i,a)=>{try{let{issueId:c}=i.params,u=i.body||{},l=await n.prepareExecution(c,u);a.json({success:!0,data:l})}catch(c){console.error("[API Route] ERROR: Failed to prepare execution:",c),a.status(500).json({success:!1,data:null,error_data:c instanceof Error?c.message:String(c),message:"Failed to prepare execution"})}}),r.post("/issues/:issueId/executions",async(i,a)=>{try{let{issueId:c}=i.params,{config:u,prompt:l}=i.body;if(!l){a.status(400).json({success:!1,data:null,message:"Prompt is required"});return}let d=await n.createExecution(c,u||{},l);a.status(201).json({success:!0,data:d})}catch(c){console.error("[API Route] ERROR: Failed to create execution:",c);let u=c instanceof Error?c.message:String(c),l=u.includes("not found")?404:500;a.status(l).json({success:!1,data:null,error_data:u,message:"Failed to create execution"})}}),r.get("/executions/:executionId",(i,a)=>{try{let{executionId:c}=i.params,u=n.getExecution(c);if(!u){a.status(404).json({success:!1,data:null,message:`Execution not found: ${c}`});return}a.json({success:!0,data:u})}catch(c){console.error("Error getting execution:",c),a.status(500).json({success:!1,data:null,error_data:c instanceof Error?c.message:String(c),message:"Failed to get execution"})}}),r.get("/issues/:issueId/executions",(i,a)=>{try{let{issueId:c}=i.params,u=n.listExecutions(c);a.json({success:!0,data:u})}catch(c){console.error("Error listing executions:",c),a.status(500).json({success:!1,data:null,error_data:c instanceof Error?c.message:String(c),message:"Failed to list executions"})}}),r.post("/executions/:executionId/follow-up",async(i,a)=>{try{let{executionId:c}=i.params,{feedback:u}=i.body;if(!u){a.status(400).json({success:!1,data:null,message:"Feedback is required"});return}let l=await n.createFollowUp(c,u);a.status(201).json({success:!0,data:l})}catch(c){console.error("Error creating follow-up execution:",c);let u=c instanceof Error?c.message:String(c),l=u.includes("not found")||u.includes("no worktree")?404:500;a.status(l).json({success:!1,data:null,error_data:u,message:"Failed to create follow-up execution"})}}),r.delete("/executions/:executionId",async(i,a)=>{try{let{executionId:c}=i.params;await n.cancelExecution(c),a.json({success:!0,data:{executionId:c},message:"Execution cancelled successfully"})}catch(c){console.error("Error cancelling execution:",c);let u=c instanceof Error?c.message:String(c),l=u.includes("not found")?404:500;a.status(l).json({success:!1,data:null,error_data:u,message:"Failed to cancel execution"})}}),r.get("/executions/:executionId/worktree",async(i,a)=>{try{let{executionId:c}=i.params,u=await n.worktreeExists(c);a.json({success:!0,data:{exists:u}})}catch(c){console.error("Error checking worktree:",c),a.status(500).json({success:!1,data:null,error_data:c instanceof Error?c.message:String(c),message:"Failed to check worktree status"})}}),r.delete("/executions/:executionId/worktree",async(i,a)=>{try{let{executionId:c}=i.params;await n.deleteWorktree(c),a.json({success:!0,data:{executionId:c},message:"Worktree deleted successfully"})}catch(c){console.error("Error deleting worktree:",c);let u=c instanceof Error?c.message:String(c),l=500;u.includes("not found")?l=404:(u.includes("has no worktree")||u.includes("Cannot delete worktree"))&&(l=400),a.status(l).json({success:!1,data:null,error_data:u,message:"Failed to delete worktree"})}}),r}import{Router as Gs}from"express";import{randomUUID as zs}from"crypto";function Pt(o){let e=Gs();return e.get("/:executionId/stream",(t,s)=>{let{executionId:r}=t.params,n=zs(),a=o.getBufferedEvents(r).map(c=>({event:c.event.type,data:c.event}));o.getSseTransport().handleConnection(n,s,r,a)}),e}var pe=class{clients=new Map;heartbeatInterval=null;HEARTBEAT_INTERVAL_MS=3e4;constructor(){this.startHeartbeat()}handleConnection(e,t,s,r){t.setHeader("Content-Type","text/event-stream"),t.setHeader("Cache-Control","no-cache"),t.setHeader("Connection","keep-alive"),t.setHeader("X-Accel-Buffering","no"),t.setHeader("Access-Control-Allow-Origin","*"),t.flushHeaders();let n={clientId:e,response:t,runId:s,connectedAt:new Date,lastActivity:new Date};if(this.clients.set(e,n),this.sendToClient(e,{event:"connected",data:{clientId:e,runId:s,timestamp:Date.now()}}),r&&r.length>0)for(let i of r)this.sendToClient(e,i);t.on("close",()=>{this.removeClient(e)})}sendToClient(e,t){let s=this.clients.get(e);return s?this.writeEvent(s,t):!1}broadcast(e){let t=0;for(let s of this.clients.values())this.writeEvent(s,e)&&t++;return t}broadcastToRun(e,t){let s=0,r=0;for(let n of this.clients.values())n.runId===e&&(r++,this.writeEvent(n,t)&&s++);return s}removeClient(e){let t=this.clients.get(e);if(!t)return!1;try{t.response.writableEnded||t.response.end()}catch{}return this.clients.delete(e),!0}getClientCount(){return this.clients.size}getRunClientCount(e){let t=0;for(let s of this.clients.values())s.runId===e&&t++;return t}getClientIds(){return Array.from(this.clients.keys())}shutdown(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null);for(let e of this.clients.keys())this.removeClient(e)}writeEvent(e,t){try{let{response:s}=e;if(s.writableEnded||!s.writable)return this.removeClient(e.clientId),!1;let r="";t.event&&(r+=`event: ${t.event}
|
|
98
|
-
`),t.id&&(r+=`id: ${t.id}
|
|
99
|
-
`);let i=(typeof t.data=="string"?t.data:JSON.stringify(t.data)).split(`
|
|
100
|
-
`);for(let a of i)r+=`data: ${a}
|
|
101
|
-
`;return r+=`
|
|
102
|
-
`,s.write(r),e.lastActivity=new Date,!0}catch{return this.removeClient(e.clientId),!1}}startHeartbeat(){this.heartbeatInterval=setInterval(()=>{let e={event:"ping",data:{timestamp:Date.now()}};for(let t of this.clients.values())this.writeEvent(t,e)},this.HEARTBEAT_INTERVAL_MS)}};var fe=class{buffers=new Map;MAX_EVENTS_PER_EXECUTION=1e4;RETENTION_MS=1e3*60*60;addEvent(e,t){let s=this.buffers.get(e);s||(s={executionId:e,events:[],nextSequence:0,createdAt:Date.now(),lastUpdatedAt:Date.now()},this.buffers.set(e,s),console.log("[EventBuffer] Created buffer for execution",{executionId:e,timestamp:new Date().toISOString()}));let r={event:t,timestamp:Date.now(),sequenceNumber:s.nextSequence++};if(s.events.push(r),s.lastUpdatedAt=Date.now(),s.events.length>this.MAX_EVENTS_PER_EXECUTION){let n=Math.floor(this.MAX_EVENTS_PER_EXECUTION*.1);s.events.splice(0,n),console.warn("[EventBuffer] Buffer size limit reached, removing oldest events",{executionId:e,removedCount:n,remainingCount:s.events.length})}console.log("[EventBuffer] Event added to buffer",{executionId:e,eventType:t.type,sequenceNumber:r.sequenceNumber,totalEvents:s.events.length})}getEvents(e,t){let s=this.buffers.get(e);return s?t!==void 0?s.events.filter(r=>r.sequenceNumber>=t):[...s.events]:[]}hasBuffer(e){return this.buffers.has(e)}getBufferInfo(e){let t=this.buffers.get(e);return t?{executionId:t.executionId,nextSequence:t.nextSequence,createdAt:t.createdAt,lastUpdatedAt:t.lastUpdatedAt}:null}removeBuffer(e){let t=this.buffers.has(e);return t&&(this.buffers.delete(e),console.log("[EventBuffer] Buffer removed",{executionId:e,timestamp:new Date().toISOString()})),t}clearAll(){let e=this.buffers.size;this.buffers.clear(),console.log("[EventBuffer] All buffers cleared",{count:e})}pruneStale(){let t=Date.now()-this.RETENTION_MS,s=0;for(let[r,n]of this.buffers.entries())n.lastUpdatedAt<t&&(this.buffers.delete(r),s++);return s>0&&console.log("[EventBuffer] Pruned stale buffers",{count:s,remaining:this.buffers.size}),s}getBufferCount(){return this.buffers.size}getTotalEventCount(){let e=0;for(let t of this.buffers.values())e+=t.events.length;return e}getStats(){let e=this.buffers.size,t=this.getTotalEventCount(),s=null,r=null;for(let n of this.buffers.values())(s===null||n.createdAt<s)&&(s=n.createdAt),(r===null||n.createdAt>r)&&(r=n.createdAt);return{bufferCount:e,totalEvents:t,avgEventsPerBuffer:e>0?t/e:0,oldestBuffer:s,newestBuffer:r}}};var ge=class{sseTransport;eventBuffer;adapterListeners=new Map;pruneInterval=null;constructor(){this.sseTransport=new pe,this.eventBuffer=new fe,this.pruneInterval=setInterval(()=>{this.eventBuffer.pruneStale()},15*60*1e3)}connectAdapter(e,t){let s=r=>{t?(this.eventBuffer.addEvent(t,r),this.broadcastToRun(t,r)):this.broadcast(r)};this.adapterListeners.set(e,s),e.onEvent(s)}disconnectAdapter(e){let t=this.adapterListeners.get(e);return t?(e.offEvent(t),this.adapterListeners.delete(e),!0):!1}broadcast(e){return this.sseTransport.broadcast({event:e.type,data:e})}broadcastToRun(e,t){return this.sseTransport.broadcastToRun(e,{event:t.type,data:t})}getSseTransport(){return this.sseTransport}getAdapterCount(){return this.adapterListeners.size}getBufferedEvents(e,t){return this.eventBuffer.getEvents(e,t)}hasBufferedEvents(e){return this.eventBuffer.hasBuffer(e)}getBufferStats(){return this.eventBuffer.getStats()}shutdown(){this.pruneInterval&&(clearInterval(this.pruneInterval),this.pruneInterval=null);for(let e of this.adapterListeners.keys())this.disconnectAdapter(e);this.sseTransport.shutdown(),this.eventBuffer.clearAll()}};ae();le();import{startWatcher as Js}from"@sudocode-ai/cli/dist/watcher.js";function Dt(o){let{db:e,baseDir:t,debounceDelay:s=2e3,syncJSONLToMarkdown:r=!1,onFileChange:n}=o;console.log(`[watcher] Starting file watcher for ${t}`),console.log(`[watcher] Debounce delay: ${s}ms`),r&&console.log("[watcher] Reverse sync (JSONL \u2192 Markdown) enabled");let i=Js({db:e,baseDir:t,debounceDelay:s,syncJSONLToMarkdown:r,onLog:a=>{if(console.log(a),n){let c=a.match(/\[watch\] Synced (spec|issue) ([A-Z]+-\d+) \((created|updated)\)/);if(c){let[,l,d]=c;n({filePath:"",event:"change",entityType:l,entityId:d});return}let u=a.match(/\[watch\] change (issues|specs)\.jsonl/);if(u){let[,l]=u;n({filePath:`${l}.jsonl`,event:"change",entityType:l==="issues"?"issue":"spec",entityId:"*"})}}},onError:a=>{console.error(`[watcher] Error: ${a.message}`)}});return{stop:async()=>{console.log("[watcher] Stopping file watcher..."),await i.stop()},getStats:i.getStats}}var Ks=Xs(import.meta.url),ye=b.dirname(Ks);Ys.config();var w=Be(),Zs=3e3,er=20,Z=process.env.SUDOCODE_DIR||b.join(process.cwd(),".sudocode"),Ue=b.join(Z,"cache.db"),he=b.dirname(Z),k,ee=null,I,V=null;async function tr(){try{console.log(`Initializing database at: ${Ue}`),k=Xe({path:Ue});let o=ve(k);console.log(`Database initialized with ${o.tables.length} tables`),o.hasCliTables||console.warn("Warning: CLI tables not found. Run 'sudocode sync' to initialize the database."),I=new ge,console.log("Transport manager initialized"),V=new q(k,he,void 0,I),console.log("Execution service initialized");let e=re(he);if(e.cleanupOrphanedWorktreesOnStartup)try{console.log("Cleaning up orphaned worktrees...");let t=new B(e);await new N(k,he,t).cleanupOrphanedWorktrees(),console.log("Orphaned worktree cleanup complete")}catch(t){console.error("Failed to cleanup orphaned worktrees:",t)}}catch(o){console.error("Failed to initialize database:",o),process.exit(1)}}await tr();var sr=process.env.WATCH!=="false",rr=process.env.SYNC_JSONL_TO_MARKDOWN==="true";if(sr)try{ee=Dt({db:k,baseDir:Z,debounceDelay:parseInt(process.env.WATCH_DEBOUNCE||"2000",10),syncJSONLToMarkdown:rr,onFileChange:o=>{if(console.log(`[server] File change detected: ${o.entityType||"unknown"} ${o.entityId||""}`),o.entityType==="issue"&&o.entityId)if(o.entityId==="*")j("*","updated",null);else{let e=G(k,o.entityId);e&&j(o.entityId,"updated",e)}else if(o.entityType==="spec"&&o.entityId)if(o.entityId==="*")H("*","updated",null);else{let e=z(k,o.entityId);e&&H(o.entityId,"updated",e)}}}),console.log(`[server] File watcher started on: ${Z}`)}catch(o){console.error("Failed to start file watcher:",o),console.warn("Continuing without file watcher. Set WATCH=false to suppress this warning.")}w.use(Vs());w.use(Be.json());w.use("/api/issues",mt(k));w.use("/api/specs",yt(k));w.use("/api/relationships",xt(k));w.use("/api/feedback",Ct(k));w.use("/api",It(k,he,I,V));w.use("/api/executions",Pt(I));w.get("/health",(o,e)=>{let t=ve(k);e.status(200).json({status:"ok",timestamp:new Date().toISOString(),uptime:process.uptime(),database:{path:Ue,tables:t.tables.length,hasCliTables:t.hasCliTables}})});w.get("/api/version",(o,e)=>{try{let t=b.join(ye,"../.."),s=b.join(t,"cli/package.json"),r=b.join(t,"server/package.json"),n=b.join(t,"frontend/package.json"),i=JSON.parse(me(s,"utf-8")),a=JSON.parse(me(r,"utf-8")),c=JSON.parse(me(n,"utf-8"));e.status(200).json({cli:i.version,server:a.version,frontend:c.version})}catch(t){console.error("Failed to read version information:",t),e.status(500).json({error:"Failed to read version information"})}});w.get("/api/config",(o,e)=>{try{let t=b.join(Z,"config.json"),s=JSON.parse(me(t,"utf-8"));e.status(200).json(s)}catch(t){console.error("Failed to read config:",t),e.status(500).json({error:"Failed to read config"})}});w.get("/ws/stats",(o,e)=>{let t=dt();e.status(200).json(t)});var nr=process.env.NODE_ENV!=="production"&&Qs(b.join(ye,"../../../frontend/dist")),Ne=nr?b.join(ye,"../../../frontend/dist"):b.join(ye,"public");console.log(`[server] Serving static frontend from: ${Ne}`);w.use(Be.static(Ne));w.get("*",(o,e)=>{o.path.startsWith("/api")||o.path.startsWith("/ws")||o.path.startsWith("/health")?e.status(404).json({error:"Not found"}):e.sendFile(b.join(Ne,"index.html"))});var R=Ft.createServer(w);async function or(o,e){let s=!process.env.PORT;for(let r=0;r<e;r++){let n=o+r;try{return await new Promise((i,a)=>{let c=l=>{R.removeListener("error",c),R.removeListener("listening",u),a(l)},u=()=>{R.removeListener("error",c),i()};R.once("error",c),R.once("listening",u),R.listen(n)}),n}catch(i){let a=i;if(a.code==="EADDRINUSE"){if(!s)throw new Error(`Port ${n} is already in use. Please specify a different PORT.`);if(r<e-1){console.log(`Port ${n} is already in use, trying ${n+1}...`);continue}else throw new Error(`Could not find an available port after ${e} attempts (${o}-${n})`)}else throw a}}throw new Error(`Could not start server after ${e} attempts`)}var ir=process.env.PORT?parseInt(process.env.PORT,10):Zs,Ot=await or(ir,er);lt(R,"/ws");var At=`http://localhost:${Ot}`,Mt=`ws://localhost:${Ot}/ws`,ar="\x1B[32m",cr="\x1B[1m",ur="\x1B[0m",Wt=(o,e)=>`\x1B]8;;${o}\x1B\\${e}\x1B]8;;\x1B\\`;console.log(`WebSocket server available at: ${Wt(Mt,Mt)}`);console.log(`${cr}${ar}sudocode local server running on: ${Wt(At,At)}${ur}`);process.on("uncaughtException",o=>{console.error("Uncaught exception:",o),console.error("Stack trace:",o.stack)});process.on("unhandledRejection",(o,e)=>{console.error("Unhandled rejection at:",e),console.error("Reason:",o)});process.on("SIGINT",async()=>{console.log(`
|
|
103
|
-
Shutting down server...`),V&&await V.shutdown(),ee&&await ee.stop(),await Me(),I&&(I.shutdown(),console.log("Transport manager shutdown complete")),k.close(),R.close(()=>{console.log("Server closed"),process.exit(0)}),setTimeout(()=>{console.error("Shutdown timeout - forcing exit"),process.exit(1)},1e4)});process.on("SIGTERM",async()=>{console.log(`
|
|
104
|
-
Shutting down server...`),V&&await V.shutdown(),ee&&await ee.stop(),await Me(),I&&(I.shutdown(),console.log("Transport manager shutdown complete")),k.close(),R.close(()=>{console.log("Server closed"),process.exit(0)})});
|
|
105
|
-
//# sourceMappingURL=cli.js.map
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for sudocode-server
|
|
4
|
+
* Starts the Express server for local development
|
|
5
|
+
*/
|
|
6
|
+
import './index.js';
|
|
7
|
+
//# sourceMappingURL=cli.js.map
|