@oxgeneral/orch 1.0.9 → 1.0.11
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/App-AMHLKLQC.js +22 -0
- package/dist/chunk-EGOR4O46.js +5 -0
- package/dist/{chunk-4MMHVHA6.js → chunk-LY3X2UHQ.js} +7 -3
- package/dist/{chunk-5YSW77VI.js → chunk-OLKQBQ4I.js} +20 -7
- package/dist/chunk-OLKQBQ4I.js.map +1 -0
- package/dist/{chunk-VG4465AG.js → chunk-XUZZJCKG.js} +6 -2
- package/dist/chunk-XUZZJCKG.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{container-LV3WOPMS.js → container-NS3YS47A.js} +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -6
- package/dist/{init-D4356W7G.js → init-6OVZBN6B.js} +1 -1
- package/dist/orchestrator-47LRXS6X.js +6 -0
- package/dist/{orchestrator-WLWIAFXH.js.map → orchestrator-47LRXS6X.js.map} +1 -1
- package/dist/orchestrator-R23BOZDZ.js +17 -0
- package/dist/run-PX7O3ILN.js +3 -0
- package/dist/serve-JOIPYKKG.js +3 -0
- package/dist/{structured-logger-EXMGTUDB.js → structured-logger-RJ6VF35J.js} +2 -2
- package/dist/{task-6Z5P7ODZ.js → task-HEPJYY7D.js} +1 -1
- package/dist/{template-engine-42PKL5KD.js → template-engine-VLIOJ3WX.js} +1 -1
- package/dist/template-engine-XOH3FZPU.js +3 -0
- package/dist/{template-engine-5ZKVJMYA.js.map → template-engine-XOH3FZPU.js.map} +1 -1
- package/dist/tui-H3L67RHY.js +2 -0
- package/dist/{update-XGJZFV4H.js → update-W54LAATS.js} +1 -1
- package/dist/update-check-R5ABF6HE.js +2 -0
- package/dist/workspace-manager-DG4IFFG3.js +4 -0
- package/dist/{workspace-manager-RH24FSNT.js → workspace-manager-KUU7UMMC.js} +84 -74
- package/dist/workspace-manager-KUU7UMMC.js.map +1 -0
- package/package.json +1 -1
- package/dist/App-Q6LOPAZT.js +0 -22
- package/dist/chunk-5YSW77VI.js.map +0 -1
- package/dist/chunk-VG4465AG.js.map +0 -1
- package/dist/chunk-XLBV2PFL.js +0 -5
- package/dist/orchestrator-PSXVHP2L.js +0 -17
- package/dist/orchestrator-WLWIAFXH.js +0 -6
- package/dist/run-NE5E4JPW.js +0 -3
- package/dist/serve-4RT4HERL.js +0 -3
- package/dist/template-engine-5ZKVJMYA.js +0 -3
- package/dist/tui-GH3Z5CO4.js +0 -2
- package/dist/update-check-CZJC7VW6.js +0 -2
- package/dist/workspace-manager-RH24FSNT.js.map +0 -1
- package/dist/workspace-manager-VJ4FN5PJ.js +0 -3
package/dist/index.d.ts
CHANGED
|
@@ -1002,6 +1002,8 @@ interface IWorkspaceManager {
|
|
|
1002
1002
|
mergeBack(branch: string): Promise<MergeResult>;
|
|
1003
1003
|
cleanup(taskId: string, branch?: string): Promise<void>;
|
|
1004
1004
|
validate(workspacePath: string, projectRoot: string): void;
|
|
1005
|
+
/** Get files changed on a worktree branch relative to its merge-base. */
|
|
1006
|
+
getChangedFiles(branch: string): Promise<string[]>;
|
|
1005
1007
|
}
|
|
1006
1008
|
|
|
1007
1009
|
interface ITemplateEngine {
|
|
@@ -1134,6 +1136,10 @@ declare class Orchestrator {
|
|
|
1134
1136
|
private stoppedResolvers;
|
|
1135
1137
|
/** When true, `tick()` skips `seedAutonomousTasks()`. Set via `startWatch()` options. */
|
|
1136
1138
|
private skipAutonomousSeeding;
|
|
1139
|
+
/** Cooldown: track last auto-seed time per agent to prevent re-seed spam. */
|
|
1140
|
+
private readonly lastAutoSeedAt;
|
|
1141
|
+
/** Minimum interval between auto-seed tasks for the same agent (30 seconds). */
|
|
1142
|
+
private static readonly AUTO_SEED_COOLDOWN_MS;
|
|
1137
1143
|
/** Promise-chain mutex to serialize critical state mutations. */
|
|
1138
1144
|
private stateMutex;
|
|
1139
1145
|
constructor(deps: OrchestratorDeps);
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Paths } from './chunk-J7ITYXE6.js';
|
|
2
|
-
import { canTransition, isTerminal } from './chunk-
|
|
3
|
-
export { Orchestrator, canTransition, isBlocked, isDispatchable, isTerminal, resolveFailureStatus } from './chunk-
|
|
4
|
-
import { AUTONOMOUS_LABEL } from './chunk-
|
|
2
|
+
import { canTransition, isTerminal } from './chunk-OLKQBQ4I.js';
|
|
3
|
+
export { Orchestrator, canTransition, isBlocked, isDispatchable, isTerminal, resolveFailureStatus } from './chunk-OLKQBQ4I.js';
|
|
4
|
+
import { AUTONOMOUS_LABEL } from './chunk-XUZZJCKG.js';
|
|
5
5
|
export { AdapterRegistry } from './chunk-6DWHQPTE.js';
|
|
6
6
|
export { SkillLoader } from './chunk-U2JVMD2G.js';
|
|
7
7
|
import { ensureDir, readYaml, writeYaml, readJson, writeJson, listFiles, appendJsonl, readJsonl, readJsonlTail, closeAppendHandle, pathExists } from './chunk-W3J7CURM.js';
|
|
@@ -1949,10 +1949,10 @@ async function buildFullContainer(context) {
|
|
|
1949
1949
|
import('./cursor-TKV5FFCN.js'),
|
|
1950
1950
|
import('./shell-PMLIRG3N.js'),
|
|
1951
1951
|
import('./opencode-YWT3M4NX.js'),
|
|
1952
|
-
import('./workspace-manager-
|
|
1953
|
-
import('./template-engine-
|
|
1952
|
+
import('./workspace-manager-KUU7UMMC.js'),
|
|
1953
|
+
import('./template-engine-XOH3FZPU.js'),
|
|
1954
1954
|
import('./skill-loader-RHCFIK74.js'),
|
|
1955
|
-
import('./orchestrator-
|
|
1955
|
+
import('./orchestrator-47LRXS6X.js'),
|
|
1956
1956
|
import('./doctor-service-F2SXDWHS.js')
|
|
1957
1957
|
]);
|
|
1958
1958
|
const processManager = new ProcessManager();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {f}from'./chunk-
|
|
2
|
+
import {f}from'./chunk-LY3X2UHQ.js';import {a}from'./chunk-N4OXN2HW.js';import'./chunk-CHRW4CLD.js';import {b as b$1}from'./chunk-3AXNSYCM.js';import {k,j,c,a as a$1}from'./chunk-ZMLF5HI5.js';import {k as k$1,j as j$1,q}from'./chunk-64WUDYEM.js';import'./chunk-IKNBPOQL.js';import v from'path';import k$2 from'fs/promises';import {execFile}from'child_process';import {promisify}from'util';var x=`Agent architect \u2014 designs and creates AI agents for the orchestrator via \`orch agent add\`.
|
|
3
3
|
|
|
4
4
|
## CREATION PROCESS
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"orchestrator-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"orchestrator-47LRXS6X.js"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {d as d$2,e as e$2,c as c$1}from'./chunk-LY3X2UHQ.js';import {e,c,b,d as d$1,g,f}from'./chunk-KR7VDF23.js';import {a}from'./chunk-CHRW4CLD.js';import {a as a$1}from'./chunk-P4JTJBWO.js';import {d,l,h,e as e$1,n}from'./chunk-IKNBPOQL.js';import {dirname}from'path';import A from'fs/promises';import {execFile}from'child_process';function et(d,t){if(!d?.length||!t?.length)return false;for(let e of d)for(let s of t)if(ht(e,s))return true;return false}function j(d){let t=d.split("*")[0],e=!t.endsWith("/"),s=e?dirname(t):"";return {raw:d,base:t,isFile:e,dir:s}}var x=class{entries;constructor(t){this.entries=[];for(let e of t)if(e?.length)for(let s of e)this.entries.push(j(s));}overlapsAny(t){if(!t?.length||this.entries.length===0)return false;for(let e of t){let s=j(e);for(let a of this.entries)if(ut(s,a))return true}return false}add(t){if(t?.length)for(let e of t)this.entries.push(j(e));}get size(){return this.entries.length}};function ut(d,t){return d.raw===t.raw||d.base.startsWith(t.base)||t.base.startsWith(d.base)?true:d.isFile&&t.isFile?d.dir===t.dir&&d.dir!==".":false}function ht(d,t){if(d===t)return true;let e=d.split("*")[0],s=t.split("*")[0];if(e.startsWith(s)||s.startsWith(e))return true;if(!e.endsWith("/")&&!s.endsWith("/")){let a=dirname(e),r=dirname(s);return a===r&&a!=="."}return false}var st=Promise.resolve();async function G(d){let t,e=new Promise(a=>{t=a;}),s=st;st=e,await s;try{return await ft(d)}finally{t();}}var pt=6e4;async function ft(d){let t=await it(d);if(t!==null){if(mt(t)&&!await gt(d))return {acquired:false,pid:t};await A.unlink(d).catch(()=>{});}try{let e=await A.open(d,"wx");return await e.writeFile(String(process.pid),"utf-8"),await e.close(),{acquired:!0,pid:process.pid}}catch(e){if(e.code==="EEXIST")return {acquired:false,pid:await it(d)??void 0};throw e}}async function F(d){await A.unlink(d).catch(()=>{});}async function at(d){let t=Date.now()/1e3;await A.utimes(d,t,t).catch(()=>{});}async function it(d){try{let t=await A.readFile(d,"utf-8"),e=parseInt(t.trim(),10);return isNaN(e)?null:e}catch{return null}}async function gt(d){try{let t=await A.stat(d);return Date.now()-t.mtimeMs>pt}catch{return true}}function mt(d){try{return process.kill(d,0),!0}catch(t){return t.code==="EPERM"}}var C=class{constructor(t){this.inner=t;}cache=new Map;async list(t){let e=t?`${t.status??""}:${t.goalId??""}`:"__all__";if(this.cache.has(e))return this.cache.get(e);let s=await this.inner.list(t);return this.cache.set(e,s),s}async get(t){return this.inner.get(t)}async save(t){await this.inner.save(t),this.cache.clear();}async delete(t){await this.inner.delete(t),this.cache.clear();}invalidate(){this.cache.clear();}},B=class{constructor(t){this.inner=t;}listCache=null;nameCache=new Map;async list(){if(this.listCache)return this.listCache;let t=await this.inner.list();return this.listCache=t,t}async get(t){return this.inner.get(t)}async getByName(t){if(this.nameCache.has(t))return this.nameCache.get(t)??null;let e=await this.inner.getByName(t);return this.nameCache.set(t,e),e}async save(t){await this.inner.save(t),this.listCache=null,this.nameCache.clear();}async delete(t){await this.inner.delete(t),this.listCache=null,this.nameCache.clear();}invalidate(){this.listCache=null,this.nameCache.clear();}},M=class{constructor(t){this.inner=t;}cache=new Map;async list(t){let e=t?.status??"__all__";if(this.cache.has(e))return this.cache.get(e);let s=await this.inner.list(t);return this.cache.set(e,s),s}async get(t){return this.inner.get(t)}async save(t){await this.inner.save(t),this.cache.clear();}async delete(t){await this.inner.delete(t),this.cache.clear();}invalidate(){this.cache.clear();}};var St={test_pass:{cmd:"npm",args:["test"]},typecheck:{cmd:"npx",args:["tsc","--noEmit"]},lint:{cmd:"npm",args:["run","lint"]}},E=class{cwd;timeoutMs;constructor(t){this.cwd=t.cwd,this.timeoutMs=t.timeout_ms??12e4;}async runAll(t){let e=[];for(let s of t){let a=await this.runCriterion(s);e.push(a);}return e}static allPassed(t){return t.length>0&&t.every(e=>e.passed)}static formatReport(t){return t.map(s=>{let a=s.passed?"\u2713":"\u2717",r=s.output.slice(0,500);return `${a} ${s.criterion}: ${s.passed?"PASSED":"FAILED"}
|
|
3
|
+
${r}`}).join(`
|
|
4
|
+
|
|
5
|
+
`)}runCriterion(t){let{cmd:e,args:s}=St[t];return new Promise(a=>{execFile(e,s,{cwd:this.cwd,timeout:this.timeoutMs,maxBuffer:1024*1024},(r,l,n)=>{let o=(l+`
|
|
6
|
+
`+n).trim();a({criterion:t,passed:!r,output:o.slice(0,2e3)});});})}};var wt=8192,yt=4096,rt=class d$3{constructor(t){this.deps=t;this.cachedTaskStore=new C(t.taskStore),this.cachedAgentStore=new B(t.agentStore),this.cachedGoalStore=t.goalStore?new M(t.goalStore):null;}intervalId=null;shuttingDown=false;state=null;abortControllers=new Map;cachedTaskStore;cachedAgentStore;cachedGoalStore;saveStateTimer=null;saveStateDirty=false;lockAcquired=false;consecutiveTickFailures=0;maxConsecutiveTickFailures=5;maxRetryQueueSize=100;signalHandlers=[];immediateDispatchTimer=null;taskCreatedUnsub=null;tickInProgress=false;stoppedResolvers=[];skipAutonomousSeeding=false;lastAutoSeedAt=new Map;static AUTO_SEED_COOLDOWN_MS=3e4;stateMutex=Promise.resolve();get isOwner(){return this.lockAcquired}withStateLock(t){let e,s=new Promise(r=>{e=r;}),a=this.stateMutex;return this.stateMutex=s,a.then(async()=>{try{return await t()}finally{e();}})}async runTask(t){if(this.lockAcquired){await this.freshDispatch(()=>this.dispatchTask(t));return}await this.withTemporaryLock(()=>this.freshDispatch(()=>this.dispatchTask(t)));}async runAll(){if(this.lockAcquired){await this.freshDispatch(()=>this.dispatchAll());return}await this.withTemporaryLock(()=>this.freshDispatch(()=>this.dispatchAll()));}async freshDispatch(t){await this.withStateLock(async()=>{this.cachedTaskStore.invalidate(),this.cachedAgentStore.invalidate(),await this.loadState(),await t(),await this.saveState();});}async withTemporaryLock(t){let e=await G(this.deps.lockPath);if(!e.acquired)throw new d(e.pid);this.lockAcquired=true;try{await t();}finally{this.lockAcquired=false,await F(this.deps.lockPath);}}async startWatch(t){this.skipAutonomousSeeding=t?.skipAutonomousSeeding??false;let e=await G(this.deps.lockPath);if(!e.acquired)throw new d(e.pid);this.lockAcquired=true,await this.loadState(),await this.cleanupStaleRunningEntries(),this.state.pid=process.pid,this.state.started_at=new Date().toISOString(),await this.saveState(),this.registerSignalHandlers(),this.taskCreatedUnsub=this.deps.eventBus.on("task:created",()=>{this.scheduleImmediateDispatch();}),await this.tick(),this.intervalId=setInterval(()=>this.tick().then(()=>{this.consecutiveTickFailures=0;},s=>{this.consecutiveTickFailures++;let a=s instanceof Error?s.message:String(s);this.deps.eventBus.emit({type:"orchestrator:error",error:a,context:"tick",fatal:this.consecutiveTickFailures>=this.maxConsecutiveTickFailures}),this.consecutiveTickFailures>=this.maxConsecutiveTickFailures&&(this.deps.eventBus.emit({type:"orchestrator:shutdown",reason:`${this.consecutiveTickFailures} consecutive tick failures`}),this.stop().catch(r=>{this.deps.eventBus.emit({type:"orchestrator:error",error:r instanceof Error?r.message:String(r),context:"stop after consecutive tick failures",fatal:false});}));}),this.deps.config.scheduling.poll_interval_ms);}waitForStop(){return this.shuttingDown?Promise.resolve():new Promise(t=>{this.stoppedResolvers.push(t);})}registerSignalHandlers(){let t=e=>{this.deps.eventBus.emit({type:"orchestrator:shutdown",reason:`Received ${e}`}),this.stop().catch(s=>{this.deps.eventBus.emit({type:"orchestrator:error",error:s instanceof Error?s.message:String(s),context:`stop after ${e} signal`,fatal:false});});};for(let e of ["SIGINT","SIGTERM"]){let s=()=>t(e);this.signalHandlers.push([e,s]),process.on(e,s);}}removeSignalHandlers(){for(let[t,e]of this.signalHandlers)process.removeListener(t,e);this.signalHandlers=[];}async stop(){if(!this.shuttingDown){this.shuttingDown=true,this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.taskCreatedUnsub&&(this.taskCreatedUnsub(),this.taskCreatedUnsub=null),this.immediateDispatchTimer&&(clearTimeout(this.immediateDispatchTimer),this.immediateDispatchTimer=null),await this.flushStateLazy(),await this.withStateLock(async()=>{if(this.state){for(let[t,e$1]of Object.entries(this.state.running)){this.abortControllers.get(t)?.abort(),this.abortControllers.delete(t),await this.deps.processManager.killWithGrace(e$1.pid),await this.deps.runService.finish(e$1.run_id,"cancelled");let s=await this.deps.taskStore.get(t);s&&await this.deps.taskService.updateStatus(t,e(s)),await this.deps.agentService.setStatus(e$1.agent_id,"idle");}this.state.running={},this.state.claimed=new Set,this.state.pid=void 0,this.state.started_at=void 0,await this.saveState();}}),this.lockAcquired&&(await F(this.deps.lockPath),this.lockAcquired=false),this.removeSignalHandlers();for(let t of this.stoppedResolvers)t();this.stoppedResolvers=[];}}async cancelTask(t){if(!this.lockAcquired)return this.withTemporaryLock(()=>this.cancelTask(t));await this.withStateLock(async()=>{await this.loadState();let e=this.state,s=e.running[t];s&&(this.abortControllers.get(t)?.abort(),this.abortControllers.delete(t),await this.deps.processManager.killWithGrace(s.pid,3e3).catch(a=>{this.deps.eventBus.emit({type:"orchestrator:error",error:a instanceof Error?a.message:String(a),context:`cancelTask kill process ${s.pid} for task ${t}`,fatal:false});}),await this.deps.runService.finish(s.run_id,"cancelled").catch(a=>{this.deps.eventBus.emit({type:"orchestrator:error",error:a instanceof Error?a.message:String(a),context:`cancelTask finish run ${s.run_id}`,fatal:false});}),await this.deps.agentService.setStatus(s.agent_id,"idle").catch(a=>{this.deps.eventBus.emit({type:"orchestrator:error",error:a instanceof Error?a.message:String(a),context:`cancelTask setStatus idle for agent ${s.agent_id}`,fatal:false});}),delete e.running[t],await this.saveState()),e.retry_queue=e.retry_queue.filter(a=>a.task_id!==t);try{await this.deps.taskService.cancel(t);}catch{try{await this.deps.taskService.updateStatus(t,"cancelled");}catch{}}await this.saveState();});}async forceStopAgent(t){if(!this.lockAcquired)return this.withTemporaryLock(()=>this.forceStopAgent(t));await this.withStateLock(async()=>{await this.loadState();let e=this.state;for(let[s,a]of Object.entries(e.running))if(a.agent_id===t){this.abortControllers.get(s)?.abort(),this.abortControllers.delete(s),await this.deps.processManager.killWithGrace(a.pid,3e3),await this.deps.runService.finish(a.run_id,"cancelled");try{await this.deps.taskService.updateStatus(s,"failed");}catch{}delete e.running[s];}await this.deps.agentService.setStatus(t,"idle"),await this.saveState();});}async tick(){if(!this.shuttingDown){this.tickInProgress=true;try{await this.withStateLock(async()=>{if(this.shuttingDown)return;this.cachedTaskStore.invalidate(),this.cachedAgentStore.invalidate(),this.cachedGoalStore?.invalidate(),await this.loadState(),await this.reconcile(),this.skipAutonomousSeeding||await this.seedAutonomousTasks(),await this.dispatchAll();let t=await this.cachedTaskStore.list(),e=Object.keys(this.state.running).length,s=t.filter(a=>c(a.status)).length;this.deps.eventBus.emit({type:"orchestrator:tick",running:e,queued:s});}),await at(this.deps.lockPath);}finally{this.tickInProgress=false;}}}scheduleImmediateDispatch(t=0){this.shuttingDown||this.immediateDispatchTimer||(this.immediateDispatchTimer=setTimeout(()=>{if(this.immediateDispatchTimer=null,!this.shuttingDown){if(this.tickInProgress){t<10&&this.scheduleImmediateDispatch(t+1);return}this.immediateDispatch().catch(e=>{this.deps.eventBus.emit({type:"orchestrator:error",error:e instanceof Error?e.message:String(e),context:"immediate dispatch on task:created",fatal:false});});}},500));}async immediateDispatch(){this.shuttingDown||await this.freshDispatch(()=>this.shuttingDown?Promise.resolve():this.dispatchAll());}async reconcile(){let t=this.state,e=Date.now(),s=Object.entries(t.running),a=await Promise.all(s.map(([i])=>this.deps.taskStore.get(i)));for(let i=0;i<s.length;i++){let[h,c]=s[i],m=a[i];if(!m||b(m.status)){this.abortControllers.delete(h),delete t.running[h],await this.deps.agentService.setStatus(c.agent_id,"idle").catch(p=>{this.deps.eventBus.emit({type:"orchestrator:error",error:p instanceof Error?p.message:String(p),context:`reconcile setStatus idle for stale agent ${c.agent_id} (task ${h})`,fatal:false});});continue}if(!this.deps.processManager.isAlive(c.pid)){try{await this._handleRunFailure(h,c,"Process crashed unexpectedly");}catch{delete t.running[h],await this.deps.agentService.setStatus(c.agent_id,"idle").catch(p=>{this.deps.eventBus.emit({type:"orchestrator:error",error:p instanceof Error?p.message:String(p),context:`reconcile crash fallback setStatus idle for agent ${c.agent_id} (task ${h})`,fatal:false});});}continue}let y=new Date(c.last_event_at).getTime(),v=this.deps.config.defaults.agent.stall_timeout_ms;if(e-y>v){this.deps.eventBus.emit({type:"orchestrator:stall_detected",runId:c.run_id}),this.abortControllers.get(h)?.abort(),await this.deps.processManager.killWithGrace(c.pid,5e3);try{await this._handleRunFailure(h,c,"Agent stalled (no events)");}catch{delete t.running[h],await this.deps.agentService.setStatus(c.agent_id,"idle").catch(p=>{this.deps.eventBus.emit({type:"orchestrator:error",error:p instanceof Error?p.message:String(p),context:`reconcile stall fallback setStatus idle for agent ${c.agent_id} (task ${h})`,fatal:false});});}}}let r=new Set(Object.values(t.running).map(i=>i.agent_id)),[l,n]=await Promise.all([this.cachedAgentStore.list(),this.cachedTaskStore.list()]),o=l.filter(i=>i.status==="running"&&!r.has(i.id));o.length>0&&await Promise.all(o.map(i=>this.deps.agentService.setStatus(i.id,"idle")));let g=n.filter(i=>i.status==="in_progress"&&!t.running[i.id]);g.length>0&&await Promise.all(g.map(async i=>{try{await this.deps.taskService.updateStatus(i.id,"failed");}catch{i.status="failed",i.updated_at=new Date().toISOString(),await this.deps.taskStore.save(i).catch(h=>{this.deps.eventBus.emit({type:"orchestrator:error",error:h instanceof Error?h.message:String(h),context:`force-write orphaned task ${i.id}`,fatal:false});});}this.deps.eventBus.emit({type:"task:orphaned",taskId:i.id});}));let u=[];t.retry_queue=t.retry_queue.filter(i=>e>=new Date(i.due_at).getTime()?(u.push(i.task_id),false):true);for(let i of u)await this.dispatchTask(i);await this.saveState();}async seedAutonomousTasks(){let e=(await this.cachedAgentStore.list()).filter(n=>n.autonomous&&n.status==="idle");if(e.length===0)return;let s=await this.cachedTaskStore.list(),a$1=this.cachedGoalStore?await this.cachedGoalStore.list({status:"active"}):[],r=false,l=new Set;for(let n of e){if(s.some(m=>m.assignee===n.id&&!b(m.status)))continue;let g=this.lastAutoSeedAt.get(n.id)??0;if(Date.now()-g<d$3.AUTO_SEED_COOLDOWN_MS)continue;let u=a$1.find(m=>m.assignee===n.id&&!l.has(m.id))??a$1.find(m=>!m.assignee&&!l.has(m.id));u&&l.add(u.id);let i=n.role??"general assistant",h=u?`[auto] ${n.name}: ${u.title.slice(0,60)}`:`[auto] ${n.name}: ${i.slice(0,60)}`,c=u?`## GOAL (highest priority)
|
|
7
|
+
|
|
8
|
+
${u.description||u.title}
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
Agent role: ${i}`:`Autonomous work cycle. Agent role: ${i}`;try{await this.deps.taskService.create({title:h,description:c,assignee:n.id,labels:[a],priority:3,goalId:u?.id}),this.lastAutoSeedAt.set(n.id,Date.now()),r=!0;}catch(m){this.deps.eventBus.emit({type:"orchestrator:error",error:m instanceof Error?m.message:String(m),context:`autonomous task for agent ${n.id}`,fatal:false});}}r&&this.cachedTaskStore.invalidate();}async dispatchAll(){let t=this.state,e$1=this.deps.config.scheduling.max_concurrent_agents,s=Object.keys(t.running).length,a=e$1-s;if(a<=0)return;let r=await this.cachedTaskStore.list(),l$1=new Map(r.map(i=>[i.id,i])),n=r.filter(i=>c(i.status)&&!d$1(i,l$1)&&!t.running[i.id]&&!t.claimed.has(i.id)).sort((i,h)=>{let c=(i.priority??3)-(h.priority??3);if(c!==0)return c;let m=(i.goalId?0:1)-(h.goalId?0:1);if(m!==0)return m;let y=h.updated_at??"",v=i.updated_at??"";return y<v?-1:y>v?1:0}).slice(0,a),o=new Set,g$1=r.filter(i=>i.status==="in_progress"&&i.scope?.length),u=new x(g$1.map(i=>i.scope));for(let i of n)if(i.scope?.length)if(u.overlapsAny(i.scope)){let h=g$1.find(c=>et(i.scope,c.scope));this.deps.eventBus.emit({type:"task:scope_overlap",taskId:i.id,overlappingTaskId:h?.id??i.id,patterns:i.scope}),o.add(i.id);}else u.add(i.scope);for(let i of n)if(!o.has(i.id))try{await this.dispatchTask(i.id);}catch(h){if(h instanceof l)try{let c=await this.deps.taskStore.get(i.id);if(c&&!b(c.status)){let m={...c,attempts:(c.attempts??0)+1,updated_at:new Date().toISOString()},y=e(m),v={...m,status:y};if(await this.deps.taskStore.save(v),y==="failed"){let p=r.map(f=>f.id===v.id?v:f);this.cachedTaskStore.invalidate(),await this.cascadeFailDependents(v.id,p,`dependency ${v.id} failed: ${h.message}`);}else {let p=g(v.attempts-1,this.deps.config.scheduling.retry_base_delay_ms,this.deps.config.scheduling.retry_max_delay_ms);this.enqueueRetry(t,v.id,v.attempts,p,h.message),await this.saveState();}}}catch{}this.deps.eventBus.emit({type:"orchestrator:error",error:h instanceof Error?h.message:String(h),context:`dispatch task ${i.id}`,fatal:false});}}enqueueRetry(t,e,s,a,r){t.retry_queue.some(l=>l.task_id===e)||(t.retry_queue.length>=this.maxRetryQueueSize&&t.retry_queue.shift(),t.retry_queue.push({task_id:e,attempt:s,due_at:new Date(Date.now()+a).toISOString(),error:r}));}async cascadeFailDependents(t,e,s){let a=new Map;for(let g of e)for(let u of g.depends_on){let i=a.get(u);i||(i=[],a.set(u,i)),i.push(g);}let r=[t],l=0,n=new Set,o=false;for(;l<r.length;){let g=r[l++];if(n.has(g))continue;n.add(g);let u=a.get(g);if(!u)continue;let i=[];for(let c of u)b(c.status)||n.has(c.id)||(i.push({task:c,previousStatus:c.status}),r.push(c.id));if(i.length===0)continue;let h=new Date().toISOString();await Promise.all(i.map(({task:c})=>this.deps.taskStore.save({...c,status:"failed",updated_at:h})));for(let{task:c,previousStatus:m}of i)this.deps.eventBus.emit({type:"task:status_changed",taskId:c.id,from:m,to:"failed"}),this.deps.eventBus.emit({type:"task:cascade_failed",taskId:c.id,failedDependencyId:t,reason:s});o=true;}o&&this.cachedTaskStore.invalidate();}async dispatchTask(t){let e=this.state;if(e.running[t]){let a=e.running[t];throw new h(t,a.run_id,a.agent_id)}let s=await this.deps.taskService.get(t);e.claimed.add(t),await this.saveState();try{let a=await this.cachedAgentStore.list(),r=await this.deps.agentService.findBestAgent(s);if(!r){if(a.length===0)throw new e$1;this.unclaim(t),await this.saveState();return}let{path:l,branch:n}=await this.deps.workspaceManager.prepare(s,r,this.deps.config),o=this.deps.config.prompt?.system_template??d$2,g=this.deps.config.prompt?.user_template??e$2,u=this.deps.config.prompt?.template,i=s.attempts+1,h;if(i>1){let S=await this.deps.runService.getLastFailedRunContext(s.id);S&&(h={previous_error:S.error,previous_output:S.output});}let c=s.goalId,[m,y,v]=await Promise.all([this.deps.contextStore?.getAll(),this.deps.messageService?this.deps.messageService.drainMailbox(r.id,s.id):[],c&&this.cachedGoalStore?this.cachedGoalStore.get(c).catch(()=>null):null]),p;if(v){let ct=(await this.cachedTaskStore.list()).filter(D=>D.goalId===c),lt=await this.deps.contextStore?.get(`${c}-progress`),dt=ct.map(D=>`[${D.status}] ${D.title}`);p={id:v.id,title:v.title,description:v.description,status:v.status,task_names:dt,progress:lt?.value};}let f=c$1(s,r,i,l,this.deps.config,{allAgents:a,retryContext:h,sharedContext:m,feedback:s.feedback,messages:y.length?y:void 0,goal:p}),w,k;if(u?w=await this.deps.templateEngine.render(u,f):(k=await this.deps.templateEngine.render(o,f),w=await this.deps.templateEngine.render(g,f)),this.deps.skillLoader&&r.config.skills?.length){let S=await this.deps.skillLoader.loadSkills(r.config.skills);S&&(k!==void 0?k=k+`
|
|
12
|
+
|
|
13
|
+
`+S:w=w+`
|
|
14
|
+
|
|
15
|
+
`+S);}let R=await this.deps.runService.create({taskId:s.id,agentId:r.id,attempt:i,prompt:w,workspacePath:l});if((s.status==="failed"||s.status==="cancelled")&&(await this.deps.taskService.retry(t),s.status="todo",s.attempts=0),await this.deps.taskService.updateStatus(t,"in_progress"),await this.deps.taskService.assign(t,r.id),await this.deps.taskService.incrementAttempts(t),n){let S=await this.deps.taskStore.get(t);S&&(S.proof={...S.proof??{files_changed:[]},branch:n},S.workspace=l,await this.deps.taskStore.save(S));}await this.deps.agentService.setStatus(r.id,"running");let _=await this.deps.agentService.get(r.id);_.current_task=t,_.last_error=void 0,await this.deps.agentStore.save(_);let ot=this.deps.adapterRegistry.require(r.adapter),I=new AbortController;this.abortControllers.set(t,I);let N=ot.execute({prompt:w,systemPrompt:k,workspace:l,env:{...r.config.env,ORCH_AGENT_ID:r.id,ORCH_AGENT_NAME:r.name,ORCH_TASK_ID:s.id},config:_.config,signal:I.signal}),U=N.pid,W=new Date().toISOString();await this.deps.runService.start(R.id,U),this.unclaim(t),e.running[t]={run_id:R.id,agent_id:r.id,task_id:t,pid:U,started_at:W,last_event_at:W},await this.saveState(),this.collectEvents(N.events,R.id,t,r.id).catch(S=>{this.deps.eventBus.emit({type:"orchestrator:error",error:S instanceof Error?S.message:String(S),context:`adapter execution for ${t}`,fatal:!1});});}catch(a){throw this.abortControllers.delete(t),this.unclaim(t),await this.saveState(),a}}async collectEvents(t,e,s,a){let r,l,n,o,g=new Set;try{for await(let i of t){if(this.shuttingDown)break;if(i.type==="done"){if(i.tokens){let{input:f,output:w,reasoning:k,cache_read:R,cache_write:_}=i.tokens;r=a$1(f,w,{reasoning:k,cache_read:R,cache_write:_});}let p=i.data;p&&typeof p.result=="string"&&(l=p.result);}if(i.type==="output"){let p=i.data;if(p){let f=typeof p.text=="string"?p.text:typeof p.message=="string"?p.message:void 0;f?.trim()&&(n=f);}}if(i.type==="file_change"){let p=i.data;if(p&&Array.isArray(p.paths))for(let f of p.paths)typeof f=="string"&&g.add(f);else {let f=p&&typeof p.path=="string"?p.path:typeof i.data=="string"?i.data:String(i.data);g.add(f);}}let h=kt(i.timestamp)?i.timestamp:new Date().toISOString(),c=i.type==="file_change"?(()=>{let p=i.data;return p&&typeof p.path=="string"?p.path:typeof i.data=="string"?i.data:String(i.data)})():null,m=nt(i.data,wt);i.data=void 0;let y={timestamp:h,type:i.type==="output"?"agent_output":i.type==="file_change"?"file_changed":i.type==="command"?"command_run":i.type==="tool_call"?"tool_call":i.type==="error"?"error":"done",data:m};await this.deps.runService.appendEvent(e,y),this.state?.running[s]&&(this.state.running[s].last_event_at=h,this.saveStateLazy());let v=nt(m,yt);i.type==="output"||i.type==="tool_call"?this.deps.eventBus.emit({type:"agent:output",runId:e,agentId:a,data:v}):i.type==="file_change"?this.deps.eventBus.emit({type:"agent:file_changed",runId:e,agentId:a,path:c}):i.type==="error"&&(i.errorKind&&(o=i.errorKind),this.deps.eventBus.emit({type:"agent:error",runId:e,agentId:a,error:v,...i.errorKind?{errorKind:i.errorKind}:{}}));}let u=l??n;await this.handleRunSuccess(s,e,a,r,u,[...g]);}catch(u){let i=u instanceof Error?u.message:String(u),h=o??(u instanceof Error?u.errorKind:void 0),c=this.state?.running[s];c&&await this.handleRunFailure(s,c,i,h);}finally{this.deps.runStore.closeRunEvents(e);}}async handleRunSuccess(t,e,s,a,r,l){return this.withStateLock(()=>this._handleRunSuccess(t,e,s,a,r,l))}async _handleRunSuccess(t,e,s,a$1,r,l){await this.flushStateLazy(),this.abortControllers.delete(t);let n=this.state;if(!n.running[t])return;let o=await this.deps.taskStore.get(t);if(!o)return;let g=l;(!g||g.length===0)&&o.proof?.branch&&(g=await this.deps.workspaceManager.getChangedFiles(o.proof.branch)),o.proof={...o.proof,agent_summary:r?.slice(0,2e3)??o.proof?.agent_summary,files_changed:g?.length?g:o.proof?.files_changed??[]},delete o.feedback,await this.deps.taskStore.save(o);let u=await this.deps.agentStore.get(s),h=o.labels?.includes(a)||u?.config.approval_policy==="auto",c=f(o,true,h);await this.deps.runService.finish(e,"succeeded",a$1);let m=n.running[t],y=m?Date.now()-new Date(m.started_at).getTime():0;m&&(n.stats.total_runtime_ms+=y),delete n.running[t];let v={tasks_completed:(u?.stats.tasks_completed??0)+1,total_runs:(u?.stats.total_runs??0)+1,total_runtime_ms:(u?.stats.total_runtime_ms??0)+y};if(a$1&&(v.tokens_used=(u?.stats.tokens_used??0)+a$1.total),await this.deps.agentService.updateStats(s,v).catch(f=>{this.deps.eventBus.emit({type:"orchestrator:error",error:f instanceof Error?f.message:String(f),context:`agent stats update for ${s}`,fatal:false});}),n.stats.total_tasks_completed++,n.stats.total_runs++,a$1&&(n.stats.total_tokens.input+=a$1.input,n.stats.total_tokens.output+=a$1.output,n.stats.total_tokens.reasoning+=a$1.reasoning,n.stats.total_tokens.cache_read+=a$1.cache_read,n.stats.total_tokens.cache_write+=a$1.cache_write,n.stats.total_tokens.total=n.stats.total_tokens.input+n.stats.total_tokens.output+n.stats.total_tokens.reasoning),o.proof?.branch)try{let f=await this.deps.workspaceManager.mergeBack(o.proof.branch);if(f.success)this.deps.eventBus.emit({type:"workspace:merge_succeeded",taskId:t,branch:o.proof.branch}),await this.deps.workspaceManager.cleanup(t,o.proof.branch).catch(w=>{this.deps.eventBus.emit({type:"orchestrator:error",error:w instanceof Error?w.message:String(w),context:`workspace cleanup for ${t}`,fatal:!1});});else {this.deps.eventBus.emit({type:"workspace:merge_conflict",taskId:t,branch:o.proof.branch,conflictInfo:f.conflictInfo}),await this.forceTaskToReview(o,s,`MERGE CONFLICT: ${f.conflictInfo}`);return}}catch(f){let w=f instanceof Error?f.message:String(f);await this.forceTaskToReview(o,s,`MERGE ERROR: ${w}`);return}try{await this.deps.taskService.updateStatus(t,c);}catch(f){let w=f instanceof Error?f.message:String(f);this.deps.eventBus.emit({type:"orchestrator:error",error:w,context:`state machine validation failed for task ${t} -> ${c}, force-writing`,fatal:false}),o.status=c,o.updated_at=new Date().toISOString(),await this.deps.taskStore.save(o).catch(k=>{this.deps.eventBus.emit({type:"orchestrator:error",error:k instanceof Error?k.message:String(k),context:`force-write task ${t} to store failed`,fatal:false});});}await this.deps.agentService.setStatus(s,"idle").catch(f=>{this.deps.eventBus.emit({type:"orchestrator:error",error:f instanceof Error?f.message:String(f),context:`_handleRunSuccess setStatus idle for agent ${s}`,fatal:false});});let p=await this.deps.agentStore.get(s);p&&(p.current_task=void 0,await this.deps.agentStore.save(p)),c==="review"&&o.review_criteria?.length?await this.runAutoReview(t,o.review_criteria,o.workspace??this.deps.projectRoot,h):c==="review"&&h&&await this.deps.taskService.updateStatus(t,"done"),await this.saveState(),this.scheduleImmediateDispatch();}async handleRunFailure(t,e,s,a){return this.withStateLock(()=>this._handleRunFailure(t,e,s,a))}async _handleRunFailure(t,e$1,s,a){await this.flushStateLazy(),this.abortControllers.delete(t);let r=this.state,l=await this.deps.taskStore.get(t);if(!l)return;await this.deps.runService.finish(e$1.run_id,"failed",void 0,s),await this.deps.agentService.setStatus(e$1.agent_id,"idle");let n$1=await this.deps.agentStore.get(e$1.agent_id);n$1&&(n$1.current_task=void 0,n$1.last_error={message:s.slice(0,500),kind:a??n(s),timestamp:new Date().toISOString()},await this.deps.agentStore.save(n$1));let o=Date.now()-new Date(e$1.started_at).getTime();await this.deps.agentService.updateStats(e$1.agent_id,{tasks_failed:(n$1?.stats.tasks_failed??0)+1,total_runs:(n$1?.stats.total_runs??0)+1,total_runtime_ms:(n$1?.stats.total_runtime_ms??0)+o});let g$1=e(l);if(await this.deps.taskService.updateStatus(t,g$1),g$1==="retrying"){let u=g(l.attempts-1,this.deps.config.scheduling.retry_base_delay_ms,this.deps.config.scheduling.retry_max_delay_ms);this.enqueueRetry(r,t,l.attempts+1,u,s),this.deps.eventBus.emit({type:"run:retry",runId:e$1.run_id,attempt:l.attempts+1,delay_ms:u});}else {r.stats.total_tasks_failed++,this.cachedTaskStore.invalidate();let u=await this.cachedTaskStore.list();await this.cascadeFailDependents(t,u,`dependency ${t} failed: ${s}`);}r.stats.total_runtime_ms+=o,l.proof?.branch&&await this.deps.workspaceManager.cleanup(t,l.proof.branch).catch(u=>{this.deps.eventBus.emit({type:"orchestrator:error",error:u instanceof Error?u.message:String(u),context:`workspace cleanup for ${t}`,fatal:false});}),delete r.running[t],r.stats.total_runs++,await this.saveState(),this.scheduleImmediateDispatch();}async runAutoReview(t,e,s,a=false){let l=await new E({cwd:s}).runAll(e),n=E.allPassed(l),o=await this.deps.taskStore.get(t);if(o&&(o.review_results=l,o.proof={...o.proof,test_results:E.formatReport(l),files_changed:o.proof?.files_changed??[]},await this.deps.taskStore.save(o),this.deps.eventBus.emit({type:"task:auto_reviewed",taskId:t,passed:n,results:l}),n||a)){n||this.deps.eventBus.emit({type:"orchestrator:error",error:`Review criteria failed for task ${t} but autoApprove is set \u2014 force-approving`,context:"auto-review-with-auto-approve",fatal:false});try{await this.deps.taskService.updateStatus(t,"done");}catch(g){let u=g instanceof Error?g.message:String(g);this.deps.eventBus.emit({type:"orchestrator:error",error:u,context:`auto-review transition failed for task ${t} -> done, force-writing`,fatal:false}),o.status="done",o.updated_at=new Date().toISOString(),await this.deps.taskStore.save(o).catch(i=>{this.deps.eventBus.emit({type:"orchestrator:error",error:i instanceof Error?i.message:String(i),context:`force-write task ${t} to store failed (auto-review)`,fatal:false});});}}}async forceTaskToReview(t,e,s){t.proof={...t.proof,agent_summary:`${s}
|
|
16
|
+
|
|
17
|
+
${t.proof?.agent_summary??""}`.slice(0,2e3),files_changed:t.proof?.files_changed??[]},t.status="review",t.updated_at=new Date().toISOString(),await this.deps.taskStore.save(t),await this.deps.agentService.setStatus(e,"idle").catch(r=>{this.deps.eventBus.emit({type:"orchestrator:error",error:r instanceof Error?r.message:String(r),context:`forceTaskToReview setStatus idle for agent ${e}`,fatal:false});});let a=await this.deps.agentStore.get(e);a&&(a.current_task=void 0,await this.deps.agentStore.save(a)),await this.saveState();}unclaim(t){this.state.claimed.delete(t);}requireOwnership(){if(!this.lockAcquired)throw new d(0)}async loadState(){this.state=await this.deps.stateStore.read();}async cleanupStaleRunningEntries(){let t=this.state,e=Object.entries(t.running).filter(([,a])=>!this.deps.processManager.isAlive(a.pid)),s=new Set;if(e.length>0){for(let[a]of e)delete t.running[a],s.add(a);await Promise.all(e.map(async([a,r])=>{await this.deps.agentService.setStatus(r.agent_id,"idle").catch(l=>{this.deps.eventBus.emit({type:"orchestrator:error",error:l instanceof Error?l.message:String(l),context:`startup cleanup: setStatus idle for agent ${r.agent_id}`,fatal:false});}),await this.forceTaskCancelled(a),await this.deps.runService.finish(r.run_id,"cancelled",void 0,"Orchestrator restarted").catch(l=>{this.deps.eventBus.emit({type:"orchestrator:error",error:l instanceof Error?l.message:String(l),context:`startup cleanup: finish run ${r.run_id}`,fatal:false});});}));}if(t.claimed=new Set,s.size>0){let r=(await this.cachedTaskStore.list()).filter(n=>n.status==="in_progress"&&!t.running[n.id]);r.length>0&&await Promise.all(r.map(n=>this.forceTaskCancelled(n.id)));let l=new Set([...s,...r.map(n=>n.id)]);t.retry_queue=t.retry_queue.filter(n=>!l.has(n.task_id)),await this.saveState();}}async forceTaskCancelled(t){try{await this.deps.taskService.updateStatus(t,"cancelled");}catch{let e=await this.deps.taskStore.get(t);e&&!b(e.status)&&(e.status="cancelled",e.updated_at=new Date().toISOString(),await this.deps.taskStore.save(e).catch(s=>{this.deps.eventBus.emit({type:"orchestrator:error",error:s instanceof Error?s.message:String(s),context:`startup cleanup: force-cancel task ${t}`,fatal:false});}));}}async saveState(){this.state&&await this.deps.stateStore.write(this.state);}saveStateLazy(){this.saveStateDirty=true,!this.saveStateTimer&&(this.saveStateTimer=setTimeout(()=>{this.saveStateTimer=null,this.saveStateDirty&&(this.saveStateDirty=false,this.saveState().catch(t=>{this.deps.eventBus.emit({type:"orchestrator:error",error:t instanceof Error?t.message:String(t),context:"debounced state save",fatal:false});}));},500));}async flushStateLazy(){this.saveStateTimer&&(clearTimeout(this.saveStateTimer),this.saveStateTimer=null),this.saveStateDirty&&(this.saveStateDirty=false,await this.saveState());}};function kt(d){if(typeof d!="string")return false;let t=new Date(d);return !isNaN(t.getTime())&&t.toISOString()===d}function nt(d,t){let e=typeof d=="string"?d:JSON.stringify(d);return e.length>t?e.slice(0,t)+"\u2026":e}export{rt as Orchestrator};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {i,p as p$1,j,q,c}from'./chunk-64WUDYEM.js';function p(e,n){e.command("run [task-id]").description("Run tasks").option("--all","Run all todo tasks").option("--watch","Watch mode: continuous orchestration").option("--verbose","Include agent output in watch mode").action(async(o,t)=>{t.watch?await m(n,t.verbose??false):t.all?await d(n):o?await u(n,o):(i("Specify a task ID, --all, or --watch"),process.exit(2));});}async function u(e,n){let o=await e.taskService.get(n);console.log(),console.log(` ${p$1("orch")} \xB7 running ${n} "${o.title}"`);let t=e.eventBus.onAny(s=>{let c$1=new Date().toLocaleTimeString("en-US",{hour12:false,hour:"2-digit",minute:"2-digit",second:"2-digit"});switch(s.type){case "agent:output":console.log(` ${q(c$1)} ${c("agentAction")} ${typeof s.data=="string"?s.data.slice(0,80):""}`);break;case "agent:file_changed":console.log(` ${q(c$1)} ${c("agentAction")} Modified ${s.path}`);break;case "agent:error":console.log(` ${q(c$1)} ${c("failed")} ${s.error}`);break;case "agent:completed":s.success?j("Done"):i("Failed");break}});try{await e.orchestrator.runTask(n);}finally{t();}console.log();}async function d(e){console.log(),console.log(` ${p$1("orch")} \xB7 running all todo tasks`),console.log(),await e.orchestrator.runAll();}async function m(e,n){console.log(`${p$1("orch")} \xB7 watching \xB7 poll interval ${e.config.scheduling.poll_interval_ms/1e3}s`),console.log("\u2501".repeat(43)),console.log(),e.eventBus.onAny(o=>{let t=new Date().toLocaleTimeString("en-US",{hour12:false,hour:"2-digit",minute:"2-digit"});switch(o.type){case "agent:output":{if(!n)break;let s=typeof o.data=="string"?o.data.slice(0,60):"";console.log(`${q(t)} ${c("agentAction")} ${s}`);break}case "agent:completed":o.success?console.log(`${q(t)} ${c("done")} DONE ${o.runId}`):console.log(`${q(t)} ${c("failed")} FAIL ${o.runId}`);break;case "run:retry":console.log(`${q(t)} ${c("retrying")} RETRY attempt ${o.attempt} \xB7 next in ${Math.round(o.delay_ms/1e3)}s`);break;case "orchestrator:tick":process.stdout.write(`\r${p$1("orch")} \xB7 watching \xB7 ${o.running} running \xB7 ${o.queued} queued `);break;case "orchestrator:stall_detected":console.log(`${q(t)} ${c("warning")} STALL ${o.runId}`);break;case "orchestrator:shutdown":console.log(`
|
|
3
|
+
${q("Shutting down...")}`);break}}),await e.orchestrator.startWatch(),await e.orchestrator.waitForStop();}export{p as registerRunCommand};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {i}from'./chunk-64WUDYEM.js';import g from'fs';var p=6;function w(o,n){let t=o.version()??"0.0.0";o.command("serve").description("Headless daemon mode \u2014 structured logs to stdout").option("--once","Process todo tasks and exit when all are terminal").option("--tick-interval <ms>","Override polling interval (ms)").option("--log-file <path>","Also write logs to file (append mode)").option("--log-format <format>","Log format: json or text (default: json)","json").option("--verbose","Include high-frequency agent:output events").action(async s=>{await u(n,t,s);});}var f=new Set(["json","text"]);async function u(o,n,t){if(t.logFormat&&!f.has(t.logFormat)){i(`Unknown --log-format "${t.logFormat}". Valid: json, text`),process.exitCode=2;return}let s=t.logFormat==="text"?"text":"json",l=[process.stdout],i$1;if(t.logFile&&(i$1=g.createWriteStream(t.logFile,{flags:"a"}),i$1.on("error",e=>{process.stderr.write(`Log file error: ${e.message}
|
|
3
|
+
`);}),l.push(i$1)),t.tickInterval){let e=parseInt(t.tickInterval,10);!isNaN(e)&&e>0&&(o.config.scheduling.poll_interval_ms=e);}let{StructuredLogger:m}=await import('./structured-logger-RJ6VF35J.js'),a=new m({format:s,verbose:t.verbose??false,streams:l,idleLogInterval:p}),d=a.subscribe(o.eventBus);a.log("info","serve:started",{mode:t.once?"once":"watch",pid:process.pid,poll_interval_ms:o.config.scheduling.poll_interval_ms}),import('./update-check-R5ABF6HE.js').then(e=>e.checkForUpdateSWR(n)).catch(()=>null).then(e=>{e?.updateAvailable&&(a.log("warn","update:available",{current:e.current,latest:e.latest,hint:"Run: npm install -g @oxgeneral/orch"}),import('./update-check-R5ABF6HE.js').then(r=>r.backgroundInstall(e.latest)).then(r=>{r&&a.log("info","update:installed",{version:e.latest,hint:"Restart to apply"});}).catch(()=>{}));});try{if(t.once){let{runOnce:e}=await import('./once-runner-AMKCFW22.js'),r=await e(o.orchestrator,o.taskStore,o.eventBus);a.log("info","serve:finished",{result:r,exit_code:r==="has_failed"?1:0}),process.exitCode=r==="has_failed"?1:0;}else await o.orchestrator.startWatch(),await o.orchestrator.waitForStop();}finally{d(),await a.flush();}}export{w as registerServeCommand};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var o=class{tickCounter=0;opts;constructor(r){this.opts=r;}subscribe(r){return r.onAny(t=>{let e=this.transform(t);e&&this.write(e);})}log(r,t,e){this.write({ts:new Date().toISOString(),level:r,event:t,...e});}async flush(){let r=this.opts.streams.filter(t=>t!==process.stdout&&t!==process.stderr).map(t=>new Promise(e=>{t.end(()=>{e();});}));await Promise.all(r);}transform(r){let t=new Date().toISOString();switch(r.type){case "orchestrator:tick":{this.tickCounter++;let e=r.running===0&&r.queued===0;if(!this.opts.verbose&&e&&this.tickCounter%this.opts.idleLogInterval!==0)return null;let a=+(process.memoryUsage().heapUsed/1048576).toFixed(1);return {ts:t,level:"info",event:r.type,running:r.running,queued:r.queued,heap_mb:a}}case "orchestrator:shutdown":return {ts:t,level:"info",event:r.type,reason:r.reason};case "orchestrator:error":return {ts:t,level:r.fatal?"error":"warn",event:r.type,error:r.error,context:r.context,fatal:r.fatal};case "orchestrator:stall_detected":return {ts:t,level:"warn",event:r.type,runId:r.runId};case "agent:started":return {ts:t,level:"info",event:r.type,agentId:r.agentId,taskId:r.taskId,runId:r.runId};case "agent:completed":return {ts:t,level:r.success?"info":"warn",event:r.type,runId:r.runId,agentId:r.agentId,success:r.success};case "agent:error":return {ts:t,level:"error",event:r.type,runId:r.runId,agentId:r.agentId,error:r.error,errorKind:r.errorKind};case "agent:output":return this.opts.verbose?{ts:t,level:"debug",event:r.type,runId:r.runId,agentId:r.agentId,data:r.data.slice(0,200)}:null;case "agent:file_changed":return {ts:t,level:"info",event:r.type,runId:r.runId,agentId:r.agentId,path:r.path};case "run:retry":return {ts:t,level:"warn",event:r.type,runId:r.runId,attempt:r.attempt,delay_ms:r.delay_ms};case "task:created":return {ts:t,level:"info",event:r.type,taskId:r.task.id,title:r.task.title};case "task:status_changed":return {ts:t,level:"info",event:r.type,taskId:r.taskId,from:r.from,to:r.to};case "task:auto_reviewed":return {ts:t,level:"info",event:r.type,taskId:r.taskId,passed:r.passed};case "workspace:merge_succeeded":return {ts:t,level:"info",event:r.type,taskId:r.taskId,branch:r.branch};case "workspace:merge_conflict":return {ts:t,level:"warn",event:r.type,taskId:r.taskId,branch:r.branch,conflictInfo:r.conflictInfo};case "task:orphaned":return {ts:t,level:"warn",event:r.type,taskId:r.taskId};case "task:scope_overlap":return {ts:t,level:"warn",event:r.type,taskId:r.taskId,overlappingTaskId:r.overlappingTaskId,patterns:r.patterns};default:return null}}write(r){let t=this.opts.format==="json"?JSON.stringify(r)+`
|
|
3
|
-
`:this.formatText(r);for(let e of this.opts.streams)e.write(t);}formatText(r){let t=r.ts.slice(11,23),e=r.level.toUpperCase().padEnd(5),{ts:a,level:p,event:n,...i}=r,
|
|
2
|
+
var o=class{tickCounter=0;opts;constructor(r){this.opts=r;}subscribe(r){return r.onAny(t=>{let e=this.transform(t);e&&this.write(e);})}log(r,t,e){this.write({ts:new Date().toISOString(),level:r,event:t,...e});}async flush(){let r=this.opts.streams.filter(t=>t!==process.stdout&&t!==process.stderr).map(t=>new Promise(e=>{t.end(()=>{e();});}));await Promise.all(r);}transform(r){let t=new Date().toISOString();switch(r.type){case "orchestrator:tick":{this.tickCounter++;let e=r.running===0&&r.queued===0;if(!this.opts.verbose&&e&&this.tickCounter%this.opts.idleLogInterval!==0)return null;let a=+(process.memoryUsage().heapUsed/1048576).toFixed(1);return {ts:t,level:"info",event:r.type,running:r.running,queued:r.queued,heap_mb:a}}case "orchestrator:shutdown":return {ts:t,level:"info",event:r.type,reason:r.reason};case "orchestrator:error":return {ts:t,level:r.fatal?"error":"warn",event:r.type,error:r.error,context:r.context,fatal:r.fatal};case "orchestrator:stall_detected":return {ts:t,level:"warn",event:r.type,runId:r.runId};case "agent:started":return {ts:t,level:"info",event:r.type,agentId:r.agentId,taskId:r.taskId,runId:r.runId};case "agent:completed":return {ts:t,level:r.success?"info":"warn",event:r.type,runId:r.runId,agentId:r.agentId,success:r.success};case "agent:error":return {ts:t,level:"error",event:r.type,runId:r.runId,agentId:r.agentId,error:r.error,errorKind:r.errorKind};case "agent:output":return this.opts.verbose?{ts:t,level:"debug",event:r.type,runId:r.runId,agentId:r.agentId,data:r.data.slice(0,200)}:null;case "agent:file_changed":return {ts:t,level:"info",event:r.type,runId:r.runId,agentId:r.agentId,path:r.path};case "run:retry":return {ts:t,level:"warn",event:r.type,runId:r.runId,attempt:r.attempt,delay_ms:r.delay_ms};case "task:created":return {ts:t,level:"info",event:r.type,taskId:r.task.id,title:r.task.title};case "task:status_changed":return {ts:t,level:"info",event:r.type,taskId:r.taskId,from:r.from,to:r.to};case "task:auto_reviewed":return {ts:t,level:"info",event:r.type,taskId:r.taskId,passed:r.passed};case "workspace:merge_succeeded":return {ts:t,level:"info",event:r.type,taskId:r.taskId,branch:r.branch};case "workspace:merge_conflict":return {ts:t,level:"warn",event:r.type,taskId:r.taskId,branch:r.branch,conflictInfo:r.conflictInfo};case "task:orphaned":return {ts:t,level:"warn",event:r.type,taskId:r.taskId};case "task:scope_overlap":return {ts:t,level:"warn",event:r.type,taskId:r.taskId,overlappingTaskId:r.overlappingTaskId,patterns:r.patterns};case "task:cascade_failed":return {ts:t,level:"warn",event:r.type,taskId:r.taskId,failedDependencyId:r.failedDependencyId,reason:r.reason};default:return null}}write(r){let t=this.opts.format==="json"?JSON.stringify(r)+`
|
|
3
|
+
`:this.formatText(r);for(let e of this.opts.streams)e.write(t);}formatText(r){let t=r.ts.slice(11,23),e=r.level.toUpperCase().padEnd(5),{ts:a,level:p,event:n,...i}=r,d=Object.entries(i).map(([l,s])=>`${l}=${typeof s=="string"?s:JSON.stringify(s)}`).join(" ");return `${t} ${e} ${n} ${d}
|
|
4
4
|
`}};export{o as StructuredLogger};
|
|
@@ -17,4 +17,4 @@ import {j,q,g,d,e,o,l,n,m}from'./chunk-64WUDYEM.js';function x(_,a){let c=_.comm
|
|
|
17
17
|
Review Results
|
|
18
18
|
${"\u2500".repeat(42)}`);for(let o of t.review_results){let n=o.passed?"\u2713":"\u2717";if(console.log(` ${n} ${o.criterion}: ${o.passed?"passed":"failed"}`),o.output)for(let l of o.output.split(`
|
|
19
19
|
`))console.log(` ${l}`);}}console.log();}),c.command("edit <id>").description("Open task in $EDITOR to modify title, priority and description").action(async e=>{let t=await a.taskService.get(e),{openInEditor:i,toEditorContent:o,fromEditorContent:n}=await import('./editor-7IFRWVTL.js'),l=t.attachments?.length?`
|
|
20
|
-
# Attachments: ${t.attachments.join(", ")}`:"",d=o({title:t.title,priority:t.priority,description:t.description})+l,s=await i(d),r=n(s),g={};if(r.title&&r.title!==t.title&&(g.title=r.title),r.priority&&r.priority!==t.priority&&(g.priority=r.priority),r.description!==void 0&&r.description!==t.description&&(g.description=r.description??""),Object.keys(g).length===0){console.log(" No changes.");return}let w=await a.taskService.update(e,g);j(`Updated ${w.id} "${w.title}"`);}),c.command("assign <task-id> <agent-id>").description("Assign task to agent").action(async(e,t)=>{let i=await a.taskService.assign(e,t);j(`Assigned ${i.id} \u2192 ${t}`);}),c.command("cancel <id>").description("Cancel a task").action(async e=>{if((await a.taskService.get(e)).status==="in_progress"){let{buildFullContainer:i}=await import('./container-
|
|
20
|
+
# Attachments: ${t.attachments.join(", ")}`:"",d=o({title:t.title,priority:t.priority,description:t.description})+l,s=await i(d),r=n(s),g={};if(r.title&&r.title!==t.title&&(g.title=r.title),r.priority&&r.priority!==t.priority&&(g.priority=r.priority),r.description!==void 0&&r.description!==t.description&&(g.description=r.description??""),Object.keys(g).length===0){console.log(" No changes.");return}let w=await a.taskService.update(e,g);j(`Updated ${w.id} "${w.title}"`);}),c.command("assign <task-id> <agent-id>").description("Assign task to agent").action(async(e,t)=>{let i=await a.taskService.assign(e,t);j(`Assigned ${i.id} \u2192 ${t}`);}),c.command("cancel <id>").description("Cancel a task").action(async e=>{if((await a.taskService.get(e)).status==="in_progress"){let{buildFullContainer:i}=await import('./container-NS3YS47A.js');await(await i(a.context)).orchestrator.cancelTask(e);}else await a.taskService.cancel(e);j(`Cancelled ${e}`);}),c.command("approve <id>").description("Approve a task in review").action(async e=>{await a.taskService.updateStatus(e,"done"),j(`Approved ${e}`);}),c.command("reject <id>").description("Reject a task and send it back for rework").option("-r, --reason <reason>","Feedback for the agent explaining what to fix").action(async(e,t)=>{await a.taskService.reject(e,t.reason),j(`Rejected ${e} \u2192 todo${t.reason?` (reason: ${t.reason})`:""}`);}),c.command("retry <id>").description("Retry a failed task").action(async e=>{await a.taskService.retry(e),j(`Reset ${e} to todo`);});}export{x as registerTaskCommand};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export{f as DEFAULT_PROMPT_TEMPLATE,d as DEFAULT_SYSTEM_TEMPLATE,e as DEFAULT_USER_TEMPLATE,a as LiquidTemplateEngine,c as buildPromptContext,b as filterRelevantContext}from'./chunk-
|
|
2
|
+
export{f as DEFAULT_PROMPT_TEMPLATE,d as DEFAULT_SYSTEM_TEMPLATE,e as DEFAULT_USER_TEMPLATE,a as LiquidTemplateEngine,c as buildPromptContext,b as filterRelevantContext}from'./chunk-LY3X2UHQ.js';import'./chunk-CHRW4CLD.js';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { DEFAULT_PROMPT_TEMPLATE, DEFAULT_SYSTEM_TEMPLATE, DEFAULT_USER_TEMPLATE, LiquidTemplateEngine, buildPromptContext, filterRelevantContext } from './chunk-XUZZJCKG.js';
|
|
2
|
+
//# sourceMappingURL=template-engine-XOH3FZPU.js.map
|
|
3
|
+
//# sourceMappingURL=template-engine-XOH3FZPU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"template-engine-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"template-engine-XOH3FZPU.js"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
function rt(l,e){l.command("tui").description("Launch interactive TUI dashboard").action(async()=>{let y=await e.taskService.list(),v=await e.agentService.list(),S=await e.stateStore.read(),{render:w}=await import('ink'),{createElement:f}=await import('react'),{App:h}=await import('./App-AMHLKLQC.js'),k=async t=>{await e.orchestrator.runTask(t);},T=async(t,s)=>e.taskService.create({title:t,priority:s?.priority,description:s?.description,attachments:s?.attachments}),A=async t=>{await e.orchestrator.cancelTask(t);},b=async t=>{await e.taskService.retry(t);},C=async(t,s)=>{await e.taskService.assign(t,s);},R=async()=>{await e.orchestrator.runAll();},_=async t=>{await e.agentService.disable(t);},E=async t=>{await e.agentService.enable(t);},j=t=>e.eventBus.onAny(t),D=async()=>e.taskService.list(),U=async()=>e.agentService.list(),P=async()=>e.stateStore.read(),G=async(t,s,n)=>e.agentService.create({name:t,adapter:s??"claude",model:n?.model||void 0,role:n?.role||void 0,approval_policy:n?.approval_policy||void 0,skills:n?.skills||void 0}),x=async t=>{await e.agentService.remove(t);},I=async t=>{await e.taskService.delete(t);},L=async t=>{await e.taskService.updateStatus(t,"done");},F=async(t,s)=>{await e.taskService.reject(t,s);},H=async(t,s)=>e.taskService.update(t,s),W=async(t,s)=>e.agentService.update(t,{...s,approval_policy:s.approval_policy}),N=async t=>{await e.orchestrator.forceStopAgent(t);},B=async(t,s)=>e.agentService.setAutonomous(t,s),O=async t=>{let s=await e.runService.listAll();s.sort((a,r)=>new Date(r.started_at).getTime()-new Date(a.started_at).getTime());let n=s.filter(a=>a.status==="succeeded"||a.status==="failed"),d=3,it=10,m=n.slice(0,d),p=n.slice(d,it),u=async a=>(await e.runService.readEventsTail(a.id,30)).map(i=>({timestamp:i.timestamp,agentId:a.agent_id,taskId:a.task_id,type:i.type,data:i.data}));if(m.length>0){let a=(await Promise.all(m.map(u))).flat();a.sort((r,i)=>new Date(r.timestamp).getTime()-new Date(i.timestamp).getTime()),t(a.slice(-200));}if(p.length>0){let a=(await Promise.all(p.map(u))).flat();a.sort((r,i)=>new Date(r.timestamp).getTime()-new Date(i.timestamp).getTime()),t(a.slice(-200));}},M=async t=>e.teamService.create(t),V=async()=>e.teamService.list(),J=async(t,s)=>e.teamService.join(t,s),K=async(t,s)=>e.teamService.leave(t,s),q=async t=>{await e.teamService.disband(t);},z=async(t,s)=>e.teamService.setLead(t,s),Q=async()=>e.goalService.list(),X=async t=>e.goalService.create(t),Y=async(t,s)=>e.goalService.update(t,s),Z=async(t,s)=>e.goalService.updateStatus(t,s),$=async t=>{await e.goalService.delete(t);},tt=async t=>e.goalService.getProgressReport(t),et=async()=>{await e.orchestrator.startWatch();},st=async()=>{await e.orchestrator.stop();},o=l.version()??"0.0.0",at=import('./update-check-R5ABF6HE.js').then(t=>t.checkForUpdateSWR(o)).catch(()=>null),c=false,g;try{await e.orchestrator.startWatch(),c=!0;}catch(t){g=t instanceof Error?t.message:String(t);}let{waitUntilExit:nt}=w(f(h,{projectName:e.config.project.name,tasks:y,agents:v,state:S,onRunTask:k,onCreateTask:T,onCancelTask:A,onRetryTask:b,onAssignTask:C,onRunAll:R,onDisableAgent:_,onEnableAgent:E,onSubscribeEvents:j,onRefreshTasks:D,onRefreshAgents:U,onRefreshState:P,onLoadHistory:O,onAddAgent:G,onDeleteAgent:x,onApproveTask:L,onRejectTask:F,onDeleteTask:I,onUpdateTask:H,onUpdateAgent:W,onForceStopAgent:N,onToggleAutonomous:B,onRefreshGoals:Q,onCreateGoal:X,onUpdateGoal:Y,onUpdateGoalStatus:Z,onDeleteGoal:$,onGetGoalProgress:tt,onCreateTeam:M,onListTeams:V,onJoinTeam:J,onLeaveTeam:K,onDisbandTeam:q,onSetTeamLead:z,onStartWatch:et,onStopWatch:st,initialWatchActive:c,watchError:g,version:o,latestVersion:void 0,onCheckUpdate:async()=>{let t=await at;if(t?.updateAvailable)return t.latest;let n=await(await import('./update-check-R5ABF6HE.js')).checkForUpdateNow(o);return n?.updateAvailable?n.latest:void 0},onBackgroundInstall:async t=>(await import('./update-check-R5ABF6HE.js')).backgroundInstall(t),initialActivityFilter:e.globalConfig.tui.activity_filter,onSaveActivityFilter:async t=>{await e.globalConfigStore.set("activity_filter",t);},initialNotifications:e.globalConfig.tui.notifications,onSaveNotifications:async t=>{await e.globalConfigStore.set("notifications",t);},initialMaxConcurrent:e.config.scheduling.max_concurrent_agents,onSaveMaxConcurrent:async t=>{await e.configStore.set("scheduling.max_concurrent_agents",t),e.config.scheduling.max_concurrent_agents=t;},onCompleteOnboarding:async()=>{let t=await e.stateStore.read();t.onboardingCompleted=true,await e.stateStore.write(t);}}),{kittyKeyboard:{mode:"auto",flags:["disambiguateEscapeCodes"]}});await nt(),c&&await e.orchestrator.stop().catch(()=>{}),e.eventBus.clear();});}export{rt as registerTuiCommand};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {a}from'./chunk-
|
|
2
|
+
import {a}from'./chunk-EGOR4O46.js';import {p,q}from'./chunk-64WUDYEM.js';import {execFile}from'child_process';import e from'chalk';var c="@oxgeneral/orch";function u(){return new Promise(n=>{let t=execFile("npm",["install","-g",`${c}@latest`],{timeout:6e4},(s,o,a)=>{let d=(o??"")+(a??"");n({code:s?1:0,output:d});});t.stdout?.pipe(process.stdout),t.stderr?.pipe(process.stderr);})}function h(n){n.command("update").description("Check for updates and install the latest version").option("--check","Only check, do not install").action(async t=>{console.log(),console.log(` ${p("orch update")} \xB7 checking for updates\u2026`),console.log();let s=n.version()??"0.0.0",o=await a(s);if(o||(console.log(` ${e.ansi256(167)("\u2715")} Could not reach npm registry`),console.log(" Check your network connection and try again."),console.log(),process.exit(1)),!o.updateAvailable){console.log(` ${e.ansi256(72)("\u2713")} Already up to date ${q(`(${o.current})`)}`),console.log();return}if(console.log(` ${e.ansi256(214)("\u25CF")} Update available: ${q(o.current)} \u2192 ${e.ansi256(72)(o.latest)}`),console.log(),t.check){console.log(` Run ${q("orch update")} to install.`),console.log();return}console.log(` Installing ${c}@${o.latest}\u2026`),console.log(),(await u()).code!==0&&(console.log(),console.log(` ${e.ansi256(167)("\u2715")} Update failed. Try manually:`),console.log(` npm install -g ${c}@latest`),console.log(),process.exit(1)),console.log(),console.log(` ${e.ansi256(72)("\u2713")} Updated to ${o.latest}`),console.log();});}export{h as registerUpdateCommand};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {c,d as d$1}from'./chunk-3AXNSYCM.js';import {j}from'./chunk-ZMLF5HI5.js';import {l}from'./chunk-IKNBPOQL.js';import n from'path';import u from'fs/promises';var d=class{constructor(t,e){this.projectRoot=t;this.processManager=e;}async mergeBack(t){return new Promise(e=>{let{process:r}=this.processManager.spawn("git",["merge","--no-ff",t,"-m",`Merge ${t}`],{cwd:this.projectRoot}),s="",o=2e3,i=a=>{s.length<o&&(s+=a.toString());};r.stdout?.on("data",i),r.stderr?.on("data",i),r.on("close",a=>{if(a===0){e({success:true});return}let c=s.slice(0,1e3);if(!(c.includes("CONFLICT")||c.includes("Merge conflict"))){e({success:false,conflictInfo:c});return}try{let{process:l}=this.processManager.spawn("git",["merge","--abort"],{cwd:this.projectRoot});l.on("close",()=>{e({success:!1,conflictInfo:c});}),l.on("error",()=>{e({success:!1,conflictInfo:c});});}catch{e({success:false,conflictInfo:c});}}),r.on("error",a=>{e({success:false,conflictInfo:a.message});});})}};var w=class{constructor(t,e,r){this.projectRoot=t;this.orchestryDir=e;this.processManager=r;this.mergeStrategy=new d(t,r);}mergeStrategy;gitRepoChecked=false;isGitRepo=false;async prepare(t,e,r){let s=this.resolveMode(t,e,r);switch(s!=="shared"&&await this.requireGitRepo(s),s){case "shared":return {path:this.projectRoot};case "worktree":return this.prepareWorktree(t);case "isolated":return {path:await this.prepareIsolated(t)};default:return {path:this.projectRoot}}}async requireGitRepo(t){if(!this.gitRepoChecked){let e=await this.spawnAndWait("git",["rev-parse","--is-inside-work-tree"]);this.isGitRepo=e===0,this.isGitRepo&&(this.gitRepoChecked=true);}if(!this.isGitRepo)throw new l(`workspace_mode "${t}" requires a git repository`,`Run: git init && git add -A && git commit -m "Initial commit"
|
|
3
|
+
Or set workspace_mode: shared in .orchestry/config.yml`)}async mergeBack(t){return this.mergeStrategy.mergeBack(t)}async cleanup(t,e){let r=n.join(this.orchestryDir,"workspaces",c(t));await this.spawnAndWait("git",["worktree","remove","--force",r]);let s=e?this.spawnAndWait("git",["branch","-D",e]).then(()=>{}):Promise.resolve(),o=u.rm(r,{recursive:true,force:true}).catch(()=>{});await Promise.all([s,o]);}validate(t,e){d$1(t,e);}async getChangedFiles(t){try{let{stdout:e}=await this.spawnAndCapture("git",["merge-base","HEAD",t]),r=e.trim();if(!r)return [];let{stdout:s,code:o}=await this.spawnAndCapture("git",["diff","--name-only",`${r}...${t}`]);return o!==0||!s.trim()?[]:s.trim().split(`
|
|
4
|
+
`).filter(Boolean)}catch{return []}}resolveMode(t,e,r){return t.workspace_mode??e.config.workspace_mode??r.defaults.agent.workspace_mode??"worktree"}async prepareWorktree(t){let e=n.join(this.orchestryDir,"workspaces",c(t.id));await j(n.dirname(e));let r=y(t.title)||c(t.id),s=`orchestry/${c(t.id)}/${r}`;try{return await u.access(e),{path:e,branch:s}}catch{}if(await this.spawnAndWait("git",["worktree","add",e,"-b",s])!==0){await this.spawnAndWait("git",["worktree","prune"]);let a=await this.spawnAndWait("git",["worktree","add",e,s]);if(a!==0)throw new l(`git worktree add failed with code ${a}`,"Run: git worktree prune && git branch | grep orchestry | xargs -r git branch -D")}let i=n.join(e,".orchestry");return await u.rm(i,{recursive:true,force:true}).catch(()=>{}),{path:e,branch:s}}async spawnAndWait(t,e){try{let{process:r}=this.processManager.spawn(t,e,{cwd:this.projectRoot});return new Promise(s=>{r.on("close",o=>s(o??1)),r.on("error",()=>s(1));})}catch{return 1}}async spawnAndCapture(t,e){try{let{process:r}=this.processManager.spawn(t,e,{cwd:this.projectRoot}),s="";r.stdout?.on("data",i=>{s+=i.toString();});let o=await new Promise(i=>{r.on("close",a=>i(a??1)),r.on("error",()=>i(1));});return {stdout:s,code:o}}catch{return {stdout:"",code:1}}}async prepareIsolated(t){let e=n.join(this.orchestryDir,"workspaces",c(t.id));await j(n.dirname(e));try{if(await this.spawnAndWait("git",["clone","--local","--no-hardlinks",this.projectRoot,e])!==0)throw new Error("git clone failed")}catch{let o=["-a",`--exclude-from=${n.join(this.orchestryDir,"workspace-exclude")}`,"./",`${e}/`],i=await this.spawnAndWait("rsync",o);if(i!==0)throw new Error(`rsync failed with code ${i}`)}let r=n.join(e,".orchestry");return await u.rm(r,{recursive:true,force:true}).catch(()=>{}),e}};function y(g){return g.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,40)}export{w as WorkspaceManager};
|
|
@@ -91,20 +91,8 @@ var WorkspaceManager = class {
|
|
|
91
91
|
}
|
|
92
92
|
async requireGitRepo(mode) {
|
|
93
93
|
if (!this.gitRepoChecked) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"git",
|
|
97
|
-
["rev-parse", "--is-inside-work-tree"],
|
|
98
|
-
{ cwd: this.projectRoot }
|
|
99
|
-
);
|
|
100
|
-
const code = await new Promise((resolve) => {
|
|
101
|
-
proc.on("close", resolve);
|
|
102
|
-
proc.on("error", () => resolve(1));
|
|
103
|
-
});
|
|
104
|
-
this.isGitRepo = code === 0;
|
|
105
|
-
} catch {
|
|
106
|
-
this.isGitRepo = false;
|
|
107
|
-
}
|
|
94
|
+
const code = await this.spawnAndWait("git", ["rev-parse", "--is-inside-work-tree"]);
|
|
95
|
+
this.isGitRepo = code === 0;
|
|
108
96
|
if (this.isGitRepo) this.gitRepoChecked = true;
|
|
109
97
|
}
|
|
110
98
|
if (!this.isGitRepo) {
|
|
@@ -119,32 +107,9 @@ var WorkspaceManager = class {
|
|
|
119
107
|
}
|
|
120
108
|
async cleanup(taskId, branch) {
|
|
121
109
|
const workspacePath = path.join(this.orchestryDir, "workspaces", sanitizeId(taskId));
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
["worktree", "remove", "--force", workspacePath],
|
|
126
|
-
{ cwd: this.projectRoot }
|
|
127
|
-
);
|
|
128
|
-
await new Promise((resolve) => {
|
|
129
|
-
proc.on("close", () => resolve());
|
|
130
|
-
proc.on("error", () => resolve());
|
|
131
|
-
});
|
|
132
|
-
} catch {
|
|
133
|
-
}
|
|
134
|
-
const branchDeletion = branch ? (async () => {
|
|
135
|
-
try {
|
|
136
|
-
const { process: proc } = this.processManager.spawn(
|
|
137
|
-
"git",
|
|
138
|
-
["branch", "-D", branch],
|
|
139
|
-
{ cwd: this.projectRoot }
|
|
140
|
-
);
|
|
141
|
-
await new Promise((resolve) => {
|
|
142
|
-
proc.on("close", () => resolve());
|
|
143
|
-
proc.on("error", () => resolve());
|
|
144
|
-
});
|
|
145
|
-
} catch {
|
|
146
|
-
}
|
|
147
|
-
})() : Promise.resolve();
|
|
110
|
+
await this.spawnAndWait("git", ["worktree", "remove", "--force", workspacePath]);
|
|
111
|
+
const branchDeletion = branch ? this.spawnAndWait("git", ["branch", "-D", branch]).then(() => {
|
|
112
|
+
}) : Promise.resolve();
|
|
148
113
|
const dirRemoval = fs.rm(workspacePath, { recursive: true, force: true }).catch(() => {
|
|
149
114
|
});
|
|
150
115
|
await Promise.all([branchDeletion, dirRemoval]);
|
|
@@ -152,6 +117,28 @@ var WorkspaceManager = class {
|
|
|
152
117
|
validate(workspacePath, projectRoot) {
|
|
153
118
|
validateWorkspacePath(workspacePath, projectRoot);
|
|
154
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Get files changed on a worktree branch relative to its merge-base.
|
|
122
|
+
* Uses `git merge-base` to find the fork point dynamically (no hardcoded branch name).
|
|
123
|
+
*/
|
|
124
|
+
async getChangedFiles(branch) {
|
|
125
|
+
try {
|
|
126
|
+
const { stdout: baseStdout } = await this.spawnAndCapture(
|
|
127
|
+
"git",
|
|
128
|
+
["merge-base", "HEAD", branch]
|
|
129
|
+
);
|
|
130
|
+
const mergeBase = baseStdout.trim();
|
|
131
|
+
if (!mergeBase) return [];
|
|
132
|
+
const { stdout: diffStdout, code } = await this.spawnAndCapture(
|
|
133
|
+
"git",
|
|
134
|
+
["diff", "--name-only", `${mergeBase}...${branch}`]
|
|
135
|
+
);
|
|
136
|
+
if (code !== 0 || !diffStdout.trim()) return [];
|
|
137
|
+
return diffStdout.trim().split("\n").filter(Boolean);
|
|
138
|
+
} catch {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
155
142
|
resolveMode(task, agent, config) {
|
|
156
143
|
return task.workspace_mode ?? agent.config.workspace_mode ?? config.defaults.agent.workspace_mode ?? "worktree";
|
|
157
144
|
}
|
|
@@ -164,26 +151,62 @@ var WorkspaceManager = class {
|
|
|
164
151
|
await ensureDir(path.dirname(workspacePath));
|
|
165
152
|
const titleSlug = sanitizeTitle(task.title) || sanitizeId(task.id);
|
|
166
153
|
const branchName = `orchestry/${sanitizeId(task.id)}/${titleSlug}`;
|
|
167
|
-
|
|
154
|
+
try {
|
|
155
|
+
await fs.access(workspacePath);
|
|
156
|
+
return { path: workspacePath, branch: branchName };
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
const createResult = await this.spawnAndWait(
|
|
168
160
|
"git",
|
|
169
|
-
["worktree", "add", workspacePath, "-b", branchName]
|
|
170
|
-
{ cwd: this.projectRoot }
|
|
161
|
+
["worktree", "add", workspacePath, "-b", branchName]
|
|
171
162
|
);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
163
|
+
if (createResult !== 0) {
|
|
164
|
+
await this.spawnAndWait("git", ["worktree", "prune"]);
|
|
165
|
+
const reuseResult = await this.spawnAndWait(
|
|
166
|
+
"git",
|
|
167
|
+
["worktree", "add", workspacePath, branchName]
|
|
168
|
+
);
|
|
169
|
+
if (reuseResult !== 0) {
|
|
170
|
+
throw new WorkspaceError(
|
|
171
|
+
`git worktree add failed with code ${reuseResult}`,
|
|
177
172
|
"Run: git worktree prune && git branch | grep orchestry | xargs -r git branch -D"
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
});
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
182
176
|
const worktreeOrchestry = path.join(workspacePath, ".orchestry");
|
|
183
177
|
await fs.rm(worktreeOrchestry, { recursive: true, force: true }).catch(() => {
|
|
184
178
|
});
|
|
185
179
|
return { path: workspacePath, branch: branchName };
|
|
186
180
|
}
|
|
181
|
+
/** Spawn a command and return exit code (non-throwing). */
|
|
182
|
+
async spawnAndWait(cmd, args) {
|
|
183
|
+
try {
|
|
184
|
+
const { process: proc } = this.processManager.spawn(cmd, args, { cwd: this.projectRoot });
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
proc.on("close", (code) => resolve(code ?? 1));
|
|
187
|
+
proc.on("error", () => resolve(1));
|
|
188
|
+
});
|
|
189
|
+
} catch {
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/** Spawn a command and capture stdout + exit code. */
|
|
194
|
+
async spawnAndCapture(cmd, args) {
|
|
195
|
+
try {
|
|
196
|
+
const { process: proc } = this.processManager.spawn(cmd, args, { cwd: this.projectRoot });
|
|
197
|
+
let stdout = "";
|
|
198
|
+
proc.stdout?.on("data", (chunk) => {
|
|
199
|
+
stdout += chunk.toString();
|
|
200
|
+
});
|
|
201
|
+
const code = await new Promise((resolve) => {
|
|
202
|
+
proc.on("close", (c) => resolve(c ?? 1));
|
|
203
|
+
proc.on("error", () => resolve(1));
|
|
204
|
+
});
|
|
205
|
+
return { stdout, code };
|
|
206
|
+
} catch {
|
|
207
|
+
return { stdout: "", code: 1 };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
187
210
|
async prepareIsolated(task) {
|
|
188
211
|
const workspacePath = path.join(
|
|
189
212
|
this.orchestryDir,
|
|
@@ -192,31 +215,18 @@ var WorkspaceManager = class {
|
|
|
192
215
|
);
|
|
193
216
|
await ensureDir(path.dirname(workspacePath));
|
|
194
217
|
try {
|
|
195
|
-
const
|
|
218
|
+
const cloneResult = await this.spawnAndWait(
|
|
196
219
|
"git",
|
|
197
|
-
["clone", "--local", "--no-hardlinks",
|
|
198
|
-
{ cwd: this.projectRoot }
|
|
220
|
+
["clone", "--local", "--no-hardlinks", this.projectRoot, workspacePath]
|
|
199
221
|
);
|
|
200
|
-
|
|
201
|
-
proc.on("close", (code) => {
|
|
202
|
-
if (code === 0) resolve();
|
|
203
|
-
else reject(new Error("git clone failed"));
|
|
204
|
-
});
|
|
205
|
-
proc.on("error", reject);
|
|
206
|
-
});
|
|
222
|
+
if (cloneResult !== 0) throw new Error("git clone failed");
|
|
207
223
|
} catch {
|
|
208
224
|
const excludeFile = path.join(this.orchestryDir, "workspace-exclude");
|
|
209
225
|
const args = ["-a", `--exclude-from=${excludeFile}`, "./", `${workspacePath}/`];
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
proc.on("close", (code) => {
|
|
215
|
-
if (code === 0) resolve();
|
|
216
|
-
else reject(new Error(`rsync failed with code ${code}`));
|
|
217
|
-
});
|
|
218
|
-
proc.on("error", reject);
|
|
219
|
-
});
|
|
226
|
+
const rsyncResult = await this.spawnAndWait("rsync", args);
|
|
227
|
+
if (rsyncResult !== 0) {
|
|
228
|
+
throw new Error(`rsync failed with code ${rsyncResult}`);
|
|
229
|
+
}
|
|
220
230
|
}
|
|
221
231
|
const clonedOrchestry = path.join(workspacePath, ".orchestry");
|
|
222
232
|
await fs.rm(clonedOrchestry, { recursive: true, force: true }).catch(() => {
|
|
@@ -229,5 +239,5 @@ function sanitizeTitle(title) {
|
|
|
229
239
|
}
|
|
230
240
|
|
|
231
241
|
export { WorkspaceManager };
|
|
232
|
-
//# sourceMappingURL=workspace-manager-
|
|
233
|
-
//# sourceMappingURL=workspace-manager-
|
|
242
|
+
//# sourceMappingURL=workspace-manager-KUU7UMMC.js.map
|
|
243
|
+
//# sourceMappingURL=workspace-manager-KUU7UMMC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infrastructure/workspace/merge-strategy.ts","../src/infrastructure/workspace/workspace-manager.ts"],"names":[],"mappings":"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","file":"workspace-manager-KUU7UMMC.js","sourcesContent":["/**\n * Git merge strategy for worktree branches.\n *\n * Encapsulates `git merge --no-ff` execution and conflict handling.\n */\n\nimport type { IProcessManager } from '../process/process-manager.js';\n\nexport type MergeResult =\n | { success: true }\n | { success: false; conflictInfo: string };\n\nexport class MergeStrategy {\n constructor(\n private readonly projectRoot: string,\n private readonly processManager: IProcessManager,\n ) {}\n\n /**\n * Merge a branch into the current branch with --no-ff.\n * On conflict, aborts the merge and returns conflict info.\n */\n async mergeBack(branch: string): Promise<MergeResult> {\n return new Promise((resolve) => {\n const { process: proc } = this.processManager.spawn(\n 'git',\n ['merge', '--no-ff', branch, '-m', `Merge ${branch}`],\n { cwd: this.projectRoot },\n );\n\n let output = '';\n const maxOutputLen = 2000;\n const appendOutput = (chunk: Buffer) => {\n if (output.length < maxOutputLen) output += chunk.toString();\n };\n proc.stdout?.on('data', appendOutput);\n proc.stderr?.on('data', appendOutput);\n\n proc.on('close', (code) => {\n if (code === 0) {\n resolve({ success: true });\n return;\n }\n\n const trimmedOutput = output.slice(0, 1000);\n const isConflict = trimmedOutput.includes('CONFLICT') || trimmedOutput.includes('Merge conflict');\n\n if (!isConflict) {\n // Non-conflict failure (branch not found, hook failure, etc.) — no merge to abort\n resolve({ success: false, conflictInfo: trimmedOutput });\n return;\n }\n\n // Abort the failed merge to restore clean state\n try {\n const { process: abortProc } = this.processManager.spawn(\n 'git',\n ['merge', '--abort'],\n { cwd: this.projectRoot },\n );\n abortProc.on('close', () => {\n resolve({ success: false, conflictInfo: trimmedOutput });\n });\n abortProc.on('error', () => {\n resolve({ success: false, conflictInfo: trimmedOutput });\n });\n } catch {\n resolve({ success: false, conflictInfo: trimmedOutput });\n }\n });\n\n proc.on('error', (err) => {\n resolve({ success: false, conflictInfo: err.message });\n });\n });\n }\n}\n","/**\n * Workspace manager implementation.\n *\n * Resolves workspace path based on mode priority chain:\n * task.workspace_mode → agent.config.workspace_mode → defaults.agent.workspace_mode → 'worktree'\n */\n\nimport path from 'node:path';\nimport fs from 'node:fs/promises';\nimport type { Agent } from '../../domain/agent.js';\nimport type { OrchestratorConfig } from '../../domain/config.js';\nimport type { Task, WorkspaceMode } from '../../domain/task.js';\nimport type { IProcessManager } from '../process/process-manager.js';\nimport { validateWorkspacePath, sanitizeId } from '../storage/paths.js';\nimport { ensureDir } from '../storage/fs-utils.js';\nimport type { IWorkspaceManager, PrepareResult } from './interface.js';\nimport { MergeStrategy, type MergeResult } from './merge-strategy.js';\nimport { WorkspaceError } from '../../domain/errors.js';\n\nexport class WorkspaceManager implements IWorkspaceManager {\n private readonly mergeStrategy: MergeStrategy;\n private gitRepoChecked = false;\n private isGitRepo = false;\n\n constructor(\n private readonly projectRoot: string,\n private readonly orchestryDir: string,\n private readonly processManager: IProcessManager,\n ) {\n this.mergeStrategy = new MergeStrategy(projectRoot, processManager);\n }\n\n async prepare(task: Task, agent: Agent, config: OrchestratorConfig): Promise<PrepareResult> {\n const mode = this.resolveMode(task, agent, config);\n\n if (mode !== 'shared') {\n await this.requireGitRepo(mode);\n }\n\n switch (mode) {\n case 'shared':\n return { path: this.projectRoot };\n\n case 'worktree':\n return this.prepareWorktree(task);\n\n case 'isolated':\n return { path: await this.prepareIsolated(task) };\n\n default:\n return { path: this.projectRoot };\n }\n }\n\n private async requireGitRepo(mode: WorkspaceMode): Promise<void> {\n if (!this.gitRepoChecked) {\n const code = await this.spawnAndWait('git', ['rev-parse', '--is-inside-work-tree']);\n this.isGitRepo = code === 0;\n // Only cache positive result — negative may change if user runs git init\n if (this.isGitRepo) this.gitRepoChecked = true;\n }\n\n if (!this.isGitRepo) {\n throw new WorkspaceError(\n `workspace_mode \"${mode}\" requires a git repository`,\n 'Run: git init && git add -A && git commit -m \"Initial commit\"\\n Or set workspace_mode: shared in .orchestry/config.yml',\n );\n }\n }\n\n async mergeBack(branch: string): Promise<MergeResult> {\n return this.mergeStrategy.mergeBack(branch);\n }\n\n async cleanup(taskId: string, branch?: string): Promise<void> {\n const workspacePath = path.join(this.orchestryDir, 'workspaces', sanitizeId(taskId));\n\n // Try git worktree remove first (cleans up .git/worktrees/ metadata)\n await this.spawnAndWait('git', ['worktree', 'remove', '--force', workspacePath]);\n\n // Delete branch + remove directory concurrently\n const branchDeletion = branch\n ? this.spawnAndWait('git', ['branch', '-D', branch]).then(() => {})\n : Promise.resolve();\n\n const dirRemoval = fs.rm(workspacePath, { recursive: true, force: true }).catch(() => {});\n\n await Promise.all([branchDeletion, dirRemoval]);\n }\n\n validate(workspacePath: string, projectRoot: string): void {\n validateWorkspacePath(workspacePath, projectRoot);\n }\n\n /**\n * Get files changed on a worktree branch relative to its merge-base.\n * Uses `git merge-base` to find the fork point dynamically (no hardcoded branch name).\n */\n async getChangedFiles(branch: string): Promise<string[]> {\n try {\n const { stdout: baseStdout } = await this.spawnAndCapture(\n 'git', ['merge-base', 'HEAD', branch],\n );\n const mergeBase = baseStdout.trim();\n if (!mergeBase) return [];\n\n const { stdout: diffStdout, code } = await this.spawnAndCapture(\n 'git', ['diff', '--name-only', `${mergeBase}...${branch}`],\n );\n if (code !== 0 || !diffStdout.trim()) return [];\n return diffStdout.trim().split('\\n').filter(Boolean);\n } catch {\n return [];\n }\n }\n\n private resolveMode(task: Task, agent: Agent, config: OrchestratorConfig): WorkspaceMode {\n return (\n task.workspace_mode ??\n agent.config.workspace_mode ??\n config.defaults.agent.workspace_mode ??\n 'worktree'\n );\n }\n\n private async prepareWorktree(task: Task): Promise<PrepareResult> {\n const workspacePath = path.join(\n this.orchestryDir,\n 'workspaces',\n sanitizeId(task.id),\n );\n await ensureDir(path.dirname(workspacePath));\n\n const titleSlug = sanitizeTitle(task.title) || sanitizeId(task.id);\n const branchName = `orchestry/${sanitizeId(task.id)}/${titleSlug}`;\n\n // Idempotent: if worktree directory already exists (retry after failure), reuse it\n try {\n await fs.access(workspacePath);\n return { path: workspacePath, branch: branchName };\n } catch {\n // Directory doesn't exist — create fresh\n }\n\n // Try creating worktree: first with new branch (-b), fallback to existing branch\n const createResult = await this.spawnAndWait(\n 'git', ['worktree', 'add', workspacePath, '-b', branchName],\n );\n if (createResult !== 0) {\n // Branch may already exist from a previous failed run — prune stale metadata and retry\n await this.spawnAndWait('git', ['worktree', 'prune']);\n const reuseResult = await this.spawnAndWait(\n 'git', ['worktree', 'add', workspacePath, branchName],\n );\n if (reuseResult !== 0) {\n throw new WorkspaceError(\n `git worktree add failed with code ${reuseResult}`,\n 'Run: git worktree prune && git branch | grep orchestry | xargs -r git branch -D',\n );\n }\n }\n\n // Remove .orchestry/ from worktree to prevent recursive state/workspaces\n const worktreeOrchestry = path.join(workspacePath, '.orchestry');\n await fs.rm(worktreeOrchestry, { recursive: true, force: true }).catch(() => {});\n\n return { path: workspacePath, branch: branchName };\n }\n\n /** Spawn a command and return exit code (non-throwing). */\n private async spawnAndWait(cmd: string, args: string[]): Promise<number> {\n try {\n const { process: proc } = this.processManager.spawn(cmd, args, { cwd: this.projectRoot });\n return new Promise<number>((resolve) => {\n proc.on('close', (code) => resolve(code ?? 1));\n proc.on('error', () => resolve(1));\n });\n } catch {\n return 1;\n }\n }\n\n /** Spawn a command and capture stdout + exit code. */\n private async spawnAndCapture(cmd: string, args: string[]): Promise<{ stdout: string; code: number }> {\n try {\n const { process: proc } = this.processManager.spawn(cmd, args, { cwd: this.projectRoot });\n let stdout = '';\n proc.stdout?.on('data', (chunk: Buffer) => { stdout += chunk.toString(); });\n const code = await new Promise<number>((resolve) => {\n proc.on('close', (c) => resolve(c ?? 1));\n proc.on('error', () => resolve(1));\n });\n return { stdout, code };\n } catch {\n return { stdout: '', code: 1 };\n }\n }\n\n private async prepareIsolated(task: Task): Promise<string> {\n const workspacePath = path.join(\n this.orchestryDir,\n 'workspaces',\n sanitizeId(task.id),\n );\n await ensureDir(path.dirname(workspacePath));\n\n // Try git clone first, fall back to rsync\n try {\n const cloneResult = await this.spawnAndWait(\n 'git', ['clone', '--local', '--no-hardlinks', this.projectRoot, workspacePath],\n );\n if (cloneResult !== 0) throw new Error('git clone failed');\n } catch {\n // Fallback: rsync\n const excludeFile = path.join(this.orchestryDir, 'workspace-exclude');\n const args = ['-a', `--exclude-from=${excludeFile}`, './', `${workspacePath}/`];\n\n const rsyncResult = await this.spawnAndWait('rsync', args);\n if (rsyncResult !== 0) {\n throw new Error(`rsync failed with code ${rsyncResult}`);\n }\n }\n\n // Remove .orchestry/ to prevent recursive workspaces (covers both clone and rsync)\n const clonedOrchestry = path.join(workspacePath, '.orchestry');\n await fs.rm(clonedOrchestry, { recursive: true, force: true }).catch(() => {});\n\n return workspacePath;\n }\n}\n\nfunction sanitizeTitle(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 40);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxgeneral/orch",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "AI agent runtime — orchestrate Claude, Cursor, Codex & OpenCode as one team. Multi-agent task automation with state machine, auto-retry, inter-agent messaging, goals and teams. Zero-config CLI + programmatic API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|