@oxgeneral/orch 0.2.4 → 0.3.0
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-TW35IULR.js +18 -0
- package/dist/agent-FRQKL7YI.js +9 -0
- package/dist/{orchestrator-OMU46RCE.js → chunk-2UC4SVJB.js} +190 -53
- package/dist/chunk-2UC4SVJB.js.map +1 -0
- package/dist/chunk-5AJ4LYO5.js +8 -0
- package/dist/{chunk-45K2XID7.js → chunk-6DWHQPTE.js} +2 -1
- package/dist/chunk-6DWHQPTE.js.map +1 -0
- package/dist/{chunk-POUC4CPC.js → chunk-6MJ7V6VY.js} +2 -2
- package/dist/{chunk-HNKJ4IF7.js → chunk-B4JQM4NU.js} +34 -10
- package/dist/chunk-B4JQM4NU.js.map +1 -0
- package/dist/{chunk-6HENRUYZ.js → chunk-CDFA4IIQ.js} +2 -2
- package/dist/chunk-CHRW4CLD.js +2 -0
- package/dist/{chunk-VAAOW526.js → chunk-GZ2Q56YZ.js} +2 -2
- package/dist/{doctor-service-QEJCE5FK.js → chunk-HMMPM7MF.js} +2 -2
- package/dist/{chunk-AELEEEV3.js → chunk-HSBYJ5C5.js} +27 -7
- package/dist/chunk-HXOMNULD.js +2 -0
- package/dist/{chunk-3TGCIXJA.js → chunk-IQXRQBUK.js} +2 -1
- package/dist/chunk-IQXRQBUK.js.map +1 -0
- package/dist/chunk-L26TK7Y5.js +2 -0
- package/dist/chunk-L3FYR45M.js +2 -0
- package/dist/chunk-LXNRCJ22.js +2 -0
- package/dist/{chunk-TX7WOFCW.js → chunk-MGFMVPRD.js} +4 -7
- package/dist/chunk-MGFMVPRD.js.map +1 -0
- package/dist/chunk-MNXU3KCD.js +2 -0
- package/dist/{chunk-CHIP7O6V.js → chunk-O2MSGW3V.js} +3 -1
- package/dist/chunk-O2MSGW3V.js.map +1 -0
- package/dist/chunk-PJ5DKXGR.js +2 -0
- package/dist/{chunk-2KSBOAW3.js → chunk-QEEM67OA.js} +11 -17
- package/dist/chunk-QEEM67OA.js.map +1 -0
- package/dist/chunk-UMZEA3JT.js +5 -0
- package/dist/{shell-OGTSH4RJ.js → chunk-UW6GUUE6.js} +3 -3
- package/dist/chunk-XDVMX2FO.js +8 -0
- package/dist/chunk-XDVMX2FO.js.map +1 -0
- package/dist/chunk-ZA5Z33GO.js +11 -0
- package/dist/claude-E36EGXUV.js +2 -0
- package/dist/{chunk-IRN2U2NE.js → claude-RIB3RQS5.js} +5 -2
- package/dist/claude-RIB3RQS5.js.map +1 -0
- package/dist/cli.js +1 -199
- package/dist/clipboard-service-PDTSZIR5.js +25 -0
- package/dist/codex-OTZKVESD.js +2 -0
- package/dist/{codex-U7LTJTX6.js → codex-VBUSA2GJ.js} +5 -3
- package/dist/codex-VBUSA2GJ.js.map +1 -0
- package/dist/config-CCSS2P7R.js +2 -0
- package/dist/container-OIXLFSX2.js +6 -0
- package/dist/context-GSMQHQES.js +7 -0
- package/dist/cursor-3DJA6LWS.js +2 -0
- package/dist/{cursor-3DI5GKRF.js → cursor-4QIOTDBW.js} +5 -3
- package/dist/cursor-4QIOTDBW.js.map +1 -0
- package/dist/doctor-KBK5JZBZ.js +2 -0
- package/dist/{chunk-K6DMQERQ.js → doctor-service-F2SXDWHS.js} +3 -1
- package/dist/doctor-service-F2SXDWHS.js.map +1 -0
- package/dist/doctor-service-PB7YBH3F.js +2 -0
- package/dist/goal-RFKFPR7M.js +8 -0
- package/dist/index.d.ts +105 -43
- package/dist/index.js +1817 -5
- package/dist/index.js.map +1 -1
- package/dist/init-WRDFAFS2.js +53 -0
- package/dist/logs-5QHJWMEG.js +12 -0
- package/dist/msg-4SCLBO4K.js +9 -0
- package/dist/orchestrator-FGGXK3N3.js +5 -0
- package/dist/{orchestrator-L6QX2LJ7.js.map → orchestrator-FGGXK3N3.js.map} +1 -1
- package/dist/orchestrator-R7IWZUT6.js +13 -0
- package/dist/process-manager-33H27MQF.js +2 -0
- package/dist/process-manager-A36Y7LHP.js +3 -0
- package/dist/{process-manager-TLZOTO4Y.js.map → process-manager-A36Y7LHP.js.map} +1 -1
- package/dist/registry-BO2PPRNG.js +2 -0
- package/dist/registry-JXXRLJ5J.js +3 -0
- package/dist/{registry-UQAHK77P.js.map → registry-JXXRLJ5J.js.map} +1 -1
- package/dist/run-HSHRELOP.js +3 -0
- package/dist/shell-EOJBDWTH.js +2 -0
- package/dist/{chunk-CIIE6LNG.js → shell-IH2MMTVP.js} +3 -2
- package/dist/shell-IH2MMTVP.js.map +1 -0
- package/dist/status-DLBNWSWM.js +2 -0
- package/dist/task-J6ZN7ALI.js +20 -0
- package/dist/team-MSIBKOQC.js +4 -0
- package/dist/template-engine-MFL5B677.js +3 -0
- package/dist/{template-engine-322SCRR6.js.map → template-engine-MFL5B677.js.map} +1 -1
- package/dist/template-engine-ONIDVD4F.js +2 -0
- package/dist/tui-G4XUFAIP.js +2 -0
- package/dist/update-PC2ENCKU.js +2 -0
- package/dist/update-check-HGMBDYHL.js +2 -0
- package/dist/workspace-manager-KOOYTO7E.js +3 -0
- package/dist/{workspace-manager-G5EQRS72.js → workspace-manager-T6AXG7XL.js} +4 -3
- package/dist/workspace-manager-T6AXG7XL.js.map +1 -0
- package/package.json +2 -1
- package/readme.md +5 -4
- package/scripts/benchmark.ts +304 -0
- package/dist/App-KHUT3IV7.js +0 -4962
- package/dist/agent-V5M2C3OC.js +0 -157
- package/dist/chunk-33QNTNR6.js +0 -46
- package/dist/chunk-6HENRUYZ.js.map +0 -1
- package/dist/chunk-AELEEEV3.js.map +0 -1
- package/dist/chunk-ED47GL3F.js +0 -29
- package/dist/chunk-FRTKB575.js +0 -87
- package/dist/chunk-HXYAZGLP.js +0 -15
- package/dist/chunk-I5WEMARW.js +0 -166
- package/dist/chunk-IZYSGYXG.js +0 -2
- package/dist/chunk-IZYSGYXG.js.map +0 -1
- package/dist/chunk-P6ATSXGL.js +0 -107
- package/dist/chunk-PBFE5V3G.js +0 -2
- package/dist/chunk-PBFE5V3G.js.map +0 -1
- package/dist/chunk-PNE6LQRF.js +0 -5
- package/dist/chunk-POUC4CPC.js.map +0 -1
- package/dist/chunk-QTDKQYZI.js +0 -11
- package/dist/chunk-QTDKQYZI.js.map +0 -1
- package/dist/chunk-VAAOW526.js.map +0 -1
- package/dist/chunk-ZTQ3KWXR.js +0 -13
- package/dist/chunk-ZTQ3KWXR.js.map +0 -1
- package/dist/claude-GH6P2DC5.js +0 -4
- package/dist/claude-S47YTIHU.js +0 -2
- package/dist/claude-S47YTIHU.js.map +0 -1
- package/dist/codex-2CH57B7G.js +0 -2
- package/dist/codex-2CH57B7G.js.map +0 -1
- package/dist/config-LJFM55LN.js +0 -75
- package/dist/container-KPH4HVAJ.js +0 -1532
- package/dist/context-EPSDCJTU.js +0 -83
- package/dist/cursor-QFUNKPCQ.js +0 -2
- package/dist/cursor-QFUNKPCQ.js.map +0 -1
- package/dist/doctor-GHRV5I2S.js +0 -67
- package/dist/doctor-service-QEJCE5FK.js.map +0 -1
- package/dist/doctor-service-TPOMFAIG.js +0 -2
- package/dist/goal-I56QP7HS.js +0 -110
- package/dist/init-EQTGQ4G2.js +0 -165
- package/dist/logs-AK255DEJ.js +0 -207
- package/dist/msg-SQWQLJP6.js +0 -95
- package/dist/orchestrator-L6QX2LJ7.js +0 -2
- package/dist/process-manager-HUVNAPQV.js +0 -2
- package/dist/process-manager-TLZOTO4Y.js +0 -2
- package/dist/registry-PQWRVNF2.js +0 -2
- package/dist/registry-UQAHK77P.js +0 -2
- package/dist/run-PSZURVVL.js +0 -95
- package/dist/shell-5ZNXFGXV.js +0 -3
- package/dist/shell-OGTSH4RJ.js.map +0 -1
- package/dist/status-DTF7D3DV.js +0 -56
- package/dist/task-35SDKXFC.js +0 -209
- package/dist/team-AISPLEJB.js +0 -97
- package/dist/template-engine-322SCRR6.js +0 -2
- package/dist/template-engine-3CDRZNMJ.js +0 -3
- package/dist/tui-AR6PVMBQ.js +0 -230
- package/dist/update-DCCWVISK.js +0 -64
- package/dist/update-check-4YKLGBFB.js +0 -2
- package/dist/workspace-manager-AS4TFA7R.js +0 -3
- package/dist/workspace-manager-AS4TFA7R.js.map +0 -1
package/dist/tui-AR6PVMBQ.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// src/cli/commands/tui.ts
|
|
3
|
-
function registerTuiCommand(program, container) {
|
|
4
|
-
program.command("tui").description("Launch interactive TUI dashboard").action(async () => {
|
|
5
|
-
await container.paths.requireInit();
|
|
6
|
-
const tasks = await container.taskService.list();
|
|
7
|
-
const agents = await container.agentService.list();
|
|
8
|
-
const state = await container.stateStore.read();
|
|
9
|
-
const { render } = await import('ink');
|
|
10
|
-
const { createElement } = await import('react');
|
|
11
|
-
const { App } = await import('./App-KHUT3IV7.js');
|
|
12
|
-
const onRunTask = async (taskId) => {
|
|
13
|
-
await container.orchestrator.runTask(taskId);
|
|
14
|
-
};
|
|
15
|
-
const onCreateTask = async (title, opts) => {
|
|
16
|
-
return container.taskService.create({
|
|
17
|
-
title,
|
|
18
|
-
priority: opts?.priority,
|
|
19
|
-
description: opts?.description
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
const onCancelTask = async (taskId) => {
|
|
23
|
-
await container.orchestrator.cancelTask(taskId);
|
|
24
|
-
};
|
|
25
|
-
const onRetryTask = async (taskId) => {
|
|
26
|
-
await container.taskService.retry(taskId);
|
|
27
|
-
};
|
|
28
|
-
const onAssignTask = async (taskId, agentId) => {
|
|
29
|
-
await container.taskService.assign(taskId, agentId);
|
|
30
|
-
};
|
|
31
|
-
const onRunAll = async () => {
|
|
32
|
-
await container.orchestrator.runAll();
|
|
33
|
-
};
|
|
34
|
-
const onDisableAgent = async (agentId) => {
|
|
35
|
-
await container.agentService.disable(agentId);
|
|
36
|
-
};
|
|
37
|
-
const onEnableAgent = async (agentId) => {
|
|
38
|
-
await container.agentService.enable(agentId);
|
|
39
|
-
};
|
|
40
|
-
const onSubscribeEvents = (handler) => {
|
|
41
|
-
return container.eventBus.onAny(handler);
|
|
42
|
-
};
|
|
43
|
-
const onRefreshTasks = async () => {
|
|
44
|
-
return container.taskService.list();
|
|
45
|
-
};
|
|
46
|
-
const onRefreshAgents = async () => {
|
|
47
|
-
return container.agentService.list();
|
|
48
|
-
};
|
|
49
|
-
const onRefreshState = async () => {
|
|
50
|
-
return container.stateStore.read();
|
|
51
|
-
};
|
|
52
|
-
const onAddAgent = async (name, adapter, opts) => {
|
|
53
|
-
return container.agentService.create({
|
|
54
|
-
name,
|
|
55
|
-
adapter: adapter ?? "claude",
|
|
56
|
-
model: opts?.model || void 0,
|
|
57
|
-
role: opts?.role || void 0,
|
|
58
|
-
approval_policy: opts?.approval_policy || void 0
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
const onDeleteAgent = async (agentId) => {
|
|
62
|
-
await container.agentService.remove(agentId);
|
|
63
|
-
};
|
|
64
|
-
const onDeleteTask = async (taskId) => {
|
|
65
|
-
await container.taskService.delete(taskId);
|
|
66
|
-
};
|
|
67
|
-
const onApproveTask = async (taskId) => {
|
|
68
|
-
await container.taskService.updateStatus(taskId, "done");
|
|
69
|
-
};
|
|
70
|
-
const onRejectTask = async (taskId, feedback) => {
|
|
71
|
-
await container.taskService.reject(taskId, feedback);
|
|
72
|
-
};
|
|
73
|
-
const onUpdateTask = async (taskId, fields) => {
|
|
74
|
-
return container.taskService.update(taskId, fields);
|
|
75
|
-
};
|
|
76
|
-
const onUpdateAgent = async (agentId, fields) => {
|
|
77
|
-
return container.agentService.update(agentId, {
|
|
78
|
-
...fields,
|
|
79
|
-
approval_policy: fields.approval_policy
|
|
80
|
-
});
|
|
81
|
-
};
|
|
82
|
-
const onForceStopAgent = async (agentId) => {
|
|
83
|
-
await container.orchestrator.forceStopAgent(agentId);
|
|
84
|
-
};
|
|
85
|
-
const onToggleAutonomous = async (agentId, enabled) => {
|
|
86
|
-
return container.agentService.setAutonomous(agentId, enabled);
|
|
87
|
-
};
|
|
88
|
-
const onLoadHistory = async (onBatch) => {
|
|
89
|
-
const allRuns = await container.runService.listAll();
|
|
90
|
-
const FIRST_BATCH = 3;
|
|
91
|
-
const TOTAL_RUNS = 10;
|
|
92
|
-
const firstRuns = allRuns.slice(0, FIRST_BATCH);
|
|
93
|
-
const restRuns = allRuns.slice(FIRST_BATCH, TOTAL_RUNS);
|
|
94
|
-
const loadRunEvents = async (run) => {
|
|
95
|
-
const events = await container.runService.readEventsTail(run.id, 30);
|
|
96
|
-
return events.map((evt) => ({
|
|
97
|
-
timestamp: evt.timestamp,
|
|
98
|
-
agentId: run.agent_id,
|
|
99
|
-
taskId: run.task_id,
|
|
100
|
-
type: evt.type,
|
|
101
|
-
data: evt.data
|
|
102
|
-
}));
|
|
103
|
-
};
|
|
104
|
-
if (firstRuns.length > 0) {
|
|
105
|
-
const firstEntries = (await Promise.all(firstRuns.map(loadRunEvents))).flat();
|
|
106
|
-
firstEntries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
107
|
-
onBatch(firstEntries.slice(-200));
|
|
108
|
-
}
|
|
109
|
-
if (restRuns.length > 0) {
|
|
110
|
-
const restEntries = (await Promise.all(restRuns.map(loadRunEvents))).flat();
|
|
111
|
-
restEntries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
112
|
-
onBatch(restEntries.slice(-200));
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const onCreateTeam = async (input) => {
|
|
116
|
-
return container.teamService.create(input);
|
|
117
|
-
};
|
|
118
|
-
const onListTeams = async () => {
|
|
119
|
-
return container.teamService.list();
|
|
120
|
-
};
|
|
121
|
-
const onJoinTeam = async (teamId, agentId) => {
|
|
122
|
-
return container.teamService.join(teamId, agentId);
|
|
123
|
-
};
|
|
124
|
-
const onLeaveTeam = async (teamId, agentId) => {
|
|
125
|
-
return container.teamService.leave(teamId, agentId);
|
|
126
|
-
};
|
|
127
|
-
const onDisbandTeam = async (teamId) => {
|
|
128
|
-
await container.teamService.disband(teamId);
|
|
129
|
-
};
|
|
130
|
-
const onSetTeamLead = async (teamId, agentId) => {
|
|
131
|
-
return container.teamService.setLead(teamId, agentId);
|
|
132
|
-
};
|
|
133
|
-
const onRefreshGoals = async () => {
|
|
134
|
-
return container.goalService.list();
|
|
135
|
-
};
|
|
136
|
-
const onCreateGoal = async (input) => {
|
|
137
|
-
return container.goalService.create(input);
|
|
138
|
-
};
|
|
139
|
-
const onUpdateGoal = async (id, fields) => {
|
|
140
|
-
return container.goalService.update(id, fields);
|
|
141
|
-
};
|
|
142
|
-
const onUpdateGoalStatus = async (id, status) => {
|
|
143
|
-
return container.goalService.updateStatus(id, status);
|
|
144
|
-
};
|
|
145
|
-
const onDeleteGoal = async (id) => {
|
|
146
|
-
await container.goalService.delete(id);
|
|
147
|
-
};
|
|
148
|
-
const onStartWatch = async () => {
|
|
149
|
-
await container.orchestrator.startWatch();
|
|
150
|
-
};
|
|
151
|
-
const onStopWatch = async () => {
|
|
152
|
-
await container.orchestrator.stop();
|
|
153
|
-
};
|
|
154
|
-
const currentVersion = program.version() ?? "0.0.0";
|
|
155
|
-
const updateCheckPromise = import('./update-check-4YKLGBFB.js').then((m) => m.checkForUpdateSWR(currentVersion)).catch(() => null);
|
|
156
|
-
let watchStarted = false;
|
|
157
|
-
let watchError;
|
|
158
|
-
try {
|
|
159
|
-
await container.orchestrator.startWatch();
|
|
160
|
-
watchStarted = true;
|
|
161
|
-
} catch (err) {
|
|
162
|
-
watchError = err instanceof Error ? err.message : String(err);
|
|
163
|
-
}
|
|
164
|
-
const updateInfo = await updateCheckPromise;
|
|
165
|
-
const { waitUntilExit } = render(
|
|
166
|
-
createElement(App, {
|
|
167
|
-
projectName: container.config.project.name,
|
|
168
|
-
tasks,
|
|
169
|
-
agents,
|
|
170
|
-
state,
|
|
171
|
-
onRunTask,
|
|
172
|
-
onCreateTask,
|
|
173
|
-
onCancelTask,
|
|
174
|
-
onRetryTask,
|
|
175
|
-
onAssignTask,
|
|
176
|
-
onRunAll,
|
|
177
|
-
onDisableAgent,
|
|
178
|
-
onEnableAgent,
|
|
179
|
-
onSubscribeEvents,
|
|
180
|
-
onRefreshTasks,
|
|
181
|
-
onRefreshAgents,
|
|
182
|
-
onRefreshState,
|
|
183
|
-
onLoadHistory,
|
|
184
|
-
onAddAgent,
|
|
185
|
-
onDeleteAgent,
|
|
186
|
-
onApproveTask,
|
|
187
|
-
onRejectTask,
|
|
188
|
-
onDeleteTask,
|
|
189
|
-
onUpdateTask,
|
|
190
|
-
onUpdateAgent,
|
|
191
|
-
onForceStopAgent,
|
|
192
|
-
onToggleAutonomous,
|
|
193
|
-
onRefreshGoals,
|
|
194
|
-
onCreateGoal,
|
|
195
|
-
onUpdateGoal,
|
|
196
|
-
onUpdateGoalStatus,
|
|
197
|
-
onDeleteGoal,
|
|
198
|
-
onCreateTeam,
|
|
199
|
-
onListTeams,
|
|
200
|
-
onJoinTeam,
|
|
201
|
-
onLeaveTeam,
|
|
202
|
-
onDisbandTeam,
|
|
203
|
-
onSetTeamLead,
|
|
204
|
-
onStartWatch,
|
|
205
|
-
onStopWatch,
|
|
206
|
-
initialWatchActive: watchStarted,
|
|
207
|
-
watchError,
|
|
208
|
-
version: currentVersion,
|
|
209
|
-
latestVersion: updateInfo?.updateAvailable ? updateInfo.latest : void 0,
|
|
210
|
-
initialActivityFilter: container.globalConfig.tui.activity_filter,
|
|
211
|
-
onSaveActivityFilter: async (preset) => {
|
|
212
|
-
await container.globalConfigStore.set("activity_filter", preset);
|
|
213
|
-
},
|
|
214
|
-
initialMaxConcurrent: container.config.scheduling.max_concurrent_agents,
|
|
215
|
-
onSaveMaxConcurrent: async (value) => {
|
|
216
|
-
await container.configStore.set("scheduling.max_concurrent_agents", value);
|
|
217
|
-
container.config.scheduling.max_concurrent_agents = value;
|
|
218
|
-
}
|
|
219
|
-
}),
|
|
220
|
-
{ kittyKeyboard: { mode: "auto", flags: ["disambiguateEscapeCodes"] } }
|
|
221
|
-
);
|
|
222
|
-
await waitUntilExit();
|
|
223
|
-
if (watchStarted) {
|
|
224
|
-
await container.orchestrator.stop().catch(() => {
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export { registerTuiCommand };
|
package/dist/update-DCCWVISK.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { checkForUpdateNow } from './chunk-FRTKB575.js';
|
|
3
|
-
import { amber, dim } from './chunk-I5WEMARW.js';
|
|
4
|
-
import { execFile } from 'child_process';
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
|
|
7
|
-
var PACKAGE_NAME = "@oxgeneral/orch";
|
|
8
|
-
function runInstall() {
|
|
9
|
-
return new Promise((resolve) => {
|
|
10
|
-
const child = execFile(
|
|
11
|
-
"npm",
|
|
12
|
-
["install", "-g", `${PACKAGE_NAME}@latest`],
|
|
13
|
-
{ timeout: 6e4 },
|
|
14
|
-
(err, stdout, stderr) => {
|
|
15
|
-
const output = (stdout ?? "") + (stderr ?? "");
|
|
16
|
-
resolve({ code: err ? 1 : 0, output });
|
|
17
|
-
}
|
|
18
|
-
);
|
|
19
|
-
child.stdout?.pipe(process.stdout);
|
|
20
|
-
child.stderr?.pipe(process.stderr);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
function registerUpdateCommand(program) {
|
|
24
|
-
program.command("update").description("Check for updates and install the latest version").option("--check", "Only check, do not install").action(async (opts) => {
|
|
25
|
-
console.log();
|
|
26
|
-
console.log(` ${amber("orch update")} \xB7 checking for updates\u2026`);
|
|
27
|
-
console.log();
|
|
28
|
-
const currentVersion = program.version() ?? "0.0.0";
|
|
29
|
-
const info = await checkForUpdateNow(currentVersion);
|
|
30
|
-
if (!info) {
|
|
31
|
-
console.log(` ${chalk.ansi256(167)("\u2715")} Could not reach npm registry`);
|
|
32
|
-
console.log(` Check your network connection and try again.`);
|
|
33
|
-
console.log();
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
if (!info.updateAvailable) {
|
|
37
|
-
console.log(` ${chalk.ansi256(72)("\u2713")} Already up to date ${dim(`(${info.current})`)}`);
|
|
38
|
-
console.log();
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
console.log(` ${chalk.ansi256(214)("\u25CF")} Update available: ${dim(info.current)} \u2192 ${chalk.ansi256(72)(info.latest)}`);
|
|
42
|
-
console.log();
|
|
43
|
-
if (opts.check) {
|
|
44
|
-
console.log(` Run ${dim("orch update")} to install.`);
|
|
45
|
-
console.log();
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
console.log(` Installing ${PACKAGE_NAME}@${info.latest}\u2026`);
|
|
49
|
-
console.log();
|
|
50
|
-
const result = await runInstall();
|
|
51
|
-
if (result.code !== 0) {
|
|
52
|
-
console.log();
|
|
53
|
-
console.log(` ${chalk.ansi256(167)("\u2715")} Update failed. Try manually:`);
|
|
54
|
-
console.log(` npm install -g ${PACKAGE_NAME}@latest`);
|
|
55
|
-
console.log();
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
console.log();
|
|
59
|
-
console.log(` ${chalk.ansi256(72)("\u2713")} Updated to ${info.latest}`);
|
|
60
|
-
console.log();
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export { registerUpdateCommand };
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import {l as l$1,m,h as h$1}from'./chunk-QTDKQYZI.js';import {l}from'./chunk-VAAOW526.js';import n from'path';import h from'fs/promises';var g=class{constructor(r,e){this.projectRoot=r;this.processManager=e;}async mergeBack(r){return new Promise(e=>{let{process:o}=this.processManager.spawn("git",["merge","--no-ff",r,"-m",`Merge ${r}`],{cwd:this.projectRoot}),t="",a=2e3,c=i=>{t.length<a&&(t+=i.toString());};o.stdout?.on("data",c),o.stderr?.on("data",c),o.on("close",i=>{if(i===0){e({success:true});return}let s=t.slice(0,1e3);if(!(s.includes("CONFLICT")||s.includes("Merge conflict"))){e({success:false,conflictInfo:s});return}try{let{process:f}=this.processManager.spawn("git",["merge","--abort"],{cwd:this.projectRoot});f.on("close",()=>{e({success:!1,conflictInfo:s});}),f.on("error",()=>{e({success:!1,conflictInfo:s});});}catch{e({success:false,conflictInfo:s});}}),o.on("error",i=>{e({success:false,conflictInfo:i.message});});})}};var y=class{constructor(r,e,o){this.projectRoot=r;this.orchestryDir=e;this.processManager=o;this.mergeStrategy=new g(r,o);}mergeStrategy;gitRepoChecked=false;isGitRepo=false;async prepare(r,e,o){let t=this.resolveMode(r,e,o);switch(t!=="shared"&&await this.requireGitRepo(t),t){case "shared":return {path:this.projectRoot};case "worktree":return this.prepareWorktree(r);case "isolated":return {path:await this.prepareIsolated(r)};default:return {path:this.projectRoot}}}async requireGitRepo(r){if(!this.gitRepoChecked){try{let{process:e}=this.processManager.spawn("git",["rev-parse","--is-inside-work-tree"],{cwd:this.projectRoot}),o=await new Promise(t=>{e.on("close",t),e.on("error",()=>t(1));});this.isGitRepo=o===0;}catch{this.isGitRepo=false;}this.isGitRepo&&(this.gitRepoChecked=true);}if(!this.isGitRepo)throw new l(`workspace_mode "${r}" requires a git repository`,`Run: git init && git add -A && git commit -m "Initial commit"
|
|
2
|
-
Or set workspace_mode: shared in .orchestry/config.yml`)}async mergeBack(r){return this.mergeStrategy.mergeBack(r)}async cleanup(r){let e=n.join(this.orchestryDir,"workspaces",l$1(r));try{let{process:o}=this.processManager.spawn("git",["worktree","remove","--force",e],{cwd:this.projectRoot});await new Promise(t=>{o.on("close",()=>t()),o.on("error",()=>t());});}catch{}try{await h.rm(e,{recursive:!0,force:!0});}catch{}}validate(r,e){m(r,e);}resolveMode(r,e,o){return r.workspace_mode??e.config.workspace_mode??o.defaults.agent.workspace_mode??"worktree"}async prepareWorktree(r){let e=n.join(this.orchestryDir,"workspaces",l$1(r.id));await h$1(n.dirname(e));let o=k(r.title)||l$1(r.id),t=`orchestry/${l$1(r.id)}/${o}`,{process:a}=this.processManager.spawn("git",["worktree","add",e,"-b",t],{cwd:this.projectRoot});await new Promise((i,s)=>{a.on("close",p=>{p===0?i():s(new Error(`git worktree add failed with code ${p}`));}),a.on("error",s);});let c=n.join(e,".orchestry");return await h.rm(c,{recursive:true,force:true}).catch(()=>{}),{path:e,branch:t}}async prepareIsolated(r){let e=n.join(this.orchestryDir,"workspaces",l$1(r.id));await h$1(n.dirname(e));try{let{process:t}=this.processManager.spawn("git",["clone","--local","--no-hardlinks",".",e],{cwd:this.projectRoot});await new Promise((a,c)=>{t.on("close",i=>{i===0?a():c(new Error("git clone failed"));}),t.on("error",c);});}catch{let a=["-a",`--exclude-from=${n.join(this.orchestryDir,"workspace-exclude")}`,"./",`${e}/`],{process:c}=this.processManager.spawn("rsync",a,{cwd:this.projectRoot});await new Promise((i,s)=>{c.on("close",p=>{p===0?i():s(new Error(`rsync failed with code ${p}`));}),c.on("error",s);});}let o=n.join(e,".orchestry");return await h.rm(o,{recursive:true,force:true}).catch(()=>{}),e}};function k(l){return l.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,40)}export{y as WorkspaceManager};//# sourceMappingURL=workspace-manager-AS4TFA7R.js.map
|
|
3
|
-
//# sourceMappingURL=workspace-manager-AS4TFA7R.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/infrastructure/workspace/merge-strategy.ts","../src/infrastructure/workspace/workspace-manager.ts"],"names":["MergeStrategy","projectRoot","processManager","branch","resolve","proc","output","maxOutputLen","appendOutput","chunk","code","trimmedOutput","abortProc","err","WorkspaceManager","orchestryDir","task","agent","config","mode","WorkspaceError","taskId","workspacePath","path","sanitizeId","fs","validateWorkspacePath","ensureDir","titleSlug","sanitizeTitle","branchName","reject","worktreeOrchestry","args","clonedOrchestry","title"],"mappings":"yIAYO,IAAMA,EAAN,KAAoB,CACzB,YACmBC,CAAAA,CACAC,CAAAA,CACjB,CAFiB,IAAA,CAAA,WAAA,CAAAD,CAAAA,CACA,oBAAAC,EAChB,CAMH,MAAM,SAAA,CAAUC,CAAAA,CAAsC,CACpD,OAAO,IAAI,OAAA,CAASC,CAAAA,EAAY,CAC9B,GAAM,CAAE,QAASC,CAAK,CAAA,CAAI,KAAK,cAAA,CAAe,KAAA,CAC5C,MACA,CAAC,OAAA,CAAS,UAAWF,CAAAA,CAAQ,IAAA,CAAM,SAASA,CAAM,CAAA,CAAE,CAAA,CACpD,CAAE,IAAK,IAAA,CAAK,WAAY,CAC1B,CAAA,CAEIG,CAAAA,CAAS,GACPC,CAAAA,CAAe,GAAA,CACfC,EAAgBC,CAAAA,EAAkB,CAClCH,EAAO,MAAA,CAASC,CAAAA,GAAcD,GAAUG,CAAAA,CAAM,QAAA,IACpD,CAAA,CACAJ,CAAAA,CAAK,QAAQ,EAAA,CAAG,MAAA,CAAQG,CAAY,CAAA,CACpCH,CAAAA,CAAK,QAAQ,EAAA,CAAG,MAAA,CAAQG,CAAY,CAAA,CAEpCH,CAAAA,CAAK,GAAG,OAAA,CAAUK,CAAAA,EAAS,CACzB,GAAIA,CAAAA,GAAS,EAAG,CACdN,CAAAA,CAAQ,CAAE,OAAA,CAAS,IAAK,CAAC,CAAA,CACzB,MACF,CAEA,IAAMO,EAAgBL,CAAAA,CAAO,KAAA,CAAM,EAAG,GAAI,CAAA,CAG1C,GAAI,EAFeK,CAAAA,CAAc,SAAS,UAAU,CAAA,EAAKA,EAAc,QAAA,CAAS,gBAAgB,GAE/E,CAEfP,CAAAA,CAAQ,CAAE,OAAA,CAAS,KAAA,CAAO,aAAcO,CAAc,CAAC,EACvD,MACF,CAGA,GAAI,CACF,GAAM,CAAE,OAAA,CAASC,CAAU,EAAI,IAAA,CAAK,cAAA,CAAe,MACjD,KAAA,CACA,CAAC,QAAS,SAAS,CAAA,CACnB,CAAE,GAAA,CAAK,KAAK,WAAY,CAC1B,EACAA,CAAAA,CAAU,EAAA,CAAG,QAAS,IAAM,CAC1BR,EAAQ,CAAE,OAAA,CAAS,GAAO,YAAA,CAAcO,CAAc,CAAC,EACzD,CAAC,EACDC,CAAAA,CAAU,EAAA,CAAG,QAAS,IAAM,CAC1BR,EAAQ,CAAE,OAAA,CAAS,GAAO,YAAA,CAAcO,CAAc,CAAC,EACzD,CAAC,EACH,CAAA,KAAQ,CACNP,EAAQ,CAAE,OAAA,CAAS,MAAO,YAAA,CAAcO,CAAc,CAAC,EACzD,CACF,CAAC,CAAA,CAEDN,EAAK,EAAA,CAAG,OAAA,CAAUQ,GAAQ,CACxBT,CAAAA,CAAQ,CAAE,OAAA,CAAS,KAAA,CAAO,aAAcS,CAAAA,CAAI,OAAQ,CAAC,EACvD,CAAC,EACH,CAAC,CACH,CACF,CAAA,CCzDO,IAAMC,CAAAA,CAAN,KAAoD,CAKzD,WAAA,CACmBb,CAAAA,CACAc,EACAb,CAAAA,CACjB,CAHiB,iBAAAD,CAAAA,CACA,IAAA,CAAA,YAAA,CAAAc,EACA,IAAA,CAAA,cAAA,CAAAb,CAAAA,CAEjB,KAAK,aAAA,CAAgB,IAAIF,EAAcC,CAAAA,CAAaC,CAAc,EACpE,CAViB,aAAA,CACT,cAAA,CAAiB,KAAA,CACjB,UAAY,KAAA,CAUpB,MAAM,QAAQc,CAAAA,CAAYC,CAAAA,CAAcC,EAAoD,CAC1F,IAAMC,EAAO,IAAA,CAAK,WAAA,CAAYH,EAAMC,CAAAA,CAAOC,CAAM,EAMjD,OAJIC,CAAAA,GAAS,UACX,MAAM,IAAA,CAAK,eAAeA,CAAI,CAAA,CAGxBA,GACN,KAAK,SACH,OAAO,CAAE,KAAM,IAAA,CAAK,WAAY,EAElC,KAAK,UAAA,CACH,OAAO,IAAA,CAAK,eAAA,CAAgBH,CAAI,CAAA,CAElC,KAAK,WACH,OAAO,CAAE,IAAA,CAAM,MAAM,KAAK,eAAA,CAAgBA,CAAI,CAAE,CAAA,CAElD,QACE,OAAO,CAAE,IAAA,CAAM,KAAK,WAAY,CACpC,CACF,CAEA,MAAc,eAAeG,CAAAA,CAAoC,CAC/D,GAAI,CAAC,IAAA,CAAK,eAAgB,CACxB,GAAI,CACF,GAAM,CAAE,QAASd,CAAK,CAAA,CAAI,KAAK,cAAA,CAAe,KAAA,CAC5C,MACA,CAAC,WAAA,CAAa,uBAAuB,CAAA,CACrC,CAAE,IAAK,IAAA,CAAK,WAAY,CAC1B,CAAA,CACMK,CAAAA,CAAO,MAAM,IAAI,QAAwBN,CAAAA,EAAY,CACzDC,EAAK,EAAA,CAAG,OAAA,CAASD,CAAO,CAAA,CACxBC,CAAAA,CAAK,GAAG,OAAA,CAAS,IAAMD,EAAQ,CAAC,CAAC,EACnC,CAAC,CAAA,CACD,KAAK,SAAA,CAAYM,CAAAA,GAAS,EAC5B,CAAA,KAAQ,CACN,KAAK,SAAA,CAAY,MACnB,CAEI,IAAA,CAAK,SAAA,GAAW,KAAK,cAAA,CAAiB,IAAA,EAC5C,CAEA,GAAI,CAAC,KAAK,SAAA,CACR,MAAM,IAAIU,CAAAA,CACR,CAAA,gBAAA,EAAmBD,CAAI,CAAA,2BAAA,CAAA,CACvB,CAAA;AAAA,+DAAA,CACF,CAEJ,CAEA,MAAM,SAAA,CAAUhB,EAAsC,CACpD,OAAO,IAAA,CAAK,aAAA,CAAc,SAAA,CAAUA,CAAM,CAC5C,CAEA,MAAM,OAAA,CAAQkB,CAAAA,CAA+B,CAC3C,IAAMC,EAAgBC,CAAAA,CAAK,IAAA,CAAK,IAAA,CAAK,YAAA,CAAc,YAAA,CAAcC,GAAAA,CAAWH,CAAM,CAAC,CAAA,CAGnF,GAAI,CACF,GAAM,CAAE,OAAA,CAAShB,CAAK,CAAA,CAAI,IAAA,CAAK,cAAA,CAAe,KAAA,CAC5C,MACA,CAAC,UAAA,CAAY,QAAA,CAAU,SAAA,CAAWiB,CAAa,CAAA,CAC/C,CAAE,GAAA,CAAK,IAAA,CAAK,WAAY,CAC1B,CAAA,CACA,MAAM,IAAI,OAAA,CAAelB,CAAAA,EAAY,CACnCC,CAAAA,CAAK,EAAA,CAAG,QAAS,IAAMD,CAAAA,EAAS,CAAA,CAChCC,CAAAA,CAAK,EAAA,CAAG,QAAS,IAAMD,CAAAA,EAAS,EAClC,CAAC,EACH,MAAQ,CAER,CAGA,GAAI,CACF,MAAMqB,CAAAA,CAAG,GAAGH,CAAAA,CAAe,CAAE,SAAA,CAAW,CAAA,CAAA,CAAM,KAAA,CAAO,CAAA,CAAK,CAAC,EAC7D,CAAA,KAAQ,CAER,CACF,CAEA,QAAA,CAASA,EAAuBrB,CAAAA,CAA2B,CACzDyB,CAAAA,CAAsBJ,CAAAA,CAAerB,CAAW,EAClD,CAEQ,WAAA,CAAYe,CAAAA,CAAYC,CAAAA,CAAcC,CAAAA,CAA2C,CACvF,OACEF,EAAK,cAAA,EACLC,CAAAA,CAAM,OAAO,cAAA,EACbC,CAAAA,CAAO,SAAS,KAAA,CAAM,cAAA,EACtB,UAEJ,CAEA,MAAc,eAAA,CAAgBF,EAAoC,CAChE,IAAMM,CAAAA,CAAgBC,CAAAA,CAAK,IAAA,CACzB,IAAA,CAAK,aACL,YAAA,CACAC,GAAAA,CAAWR,CAAAA,CAAK,EAAE,CACpB,CAAA,CACA,MAAMW,GAAAA,CAAUJ,CAAAA,CAAK,OAAA,CAAQD,CAAa,CAAC,CAAA,CAE3C,IAAMM,CAAAA,CAAYC,CAAAA,CAAcb,CAAAA,CAAK,KAAK,CAAA,EAAKQ,GAAAA,CAAWR,EAAK,EAAE,CAAA,CAC3Dc,CAAAA,CAAa,CAAA,UAAA,EAAaN,GAAAA,CAAWR,CAAAA,CAAK,EAAE,CAAC,CAAA,CAAA,EAAIY,CAAS,CAAA,CAAA,CAE1D,CAAE,OAAA,CAASvB,CAAK,CAAA,CAAI,IAAA,CAAK,eAAe,KAAA,CAC5C,KAAA,CACA,CAAC,UAAA,CAAY,KAAA,CAAOiB,CAAAA,CAAe,IAAA,CAAMQ,CAAU,CAAA,CACnD,CAAE,GAAA,CAAK,IAAA,CAAK,WAAY,CAC1B,CAAA,CAEA,MAAM,IAAI,OAAA,CAAc,CAAC1B,CAAAA,CAAS2B,CAAAA,GAAW,CAC3C1B,CAAAA,CAAK,GAAG,OAAA,CAAUK,CAAAA,EAAS,CACrBA,CAAAA,GAAS,CAAA,CAAGN,CAAAA,GACX2B,CAAAA,CAAO,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqCrB,CAAI,CAAA,CAAE,CAAC,EACpE,CAAC,CAAA,CACDL,CAAAA,CAAK,EAAA,CAAG,OAAA,CAAS0B,CAAM,EACzB,CAAC,CAAA,CAGD,IAAMC,CAAAA,CAAoBT,CAAAA,CAAK,KAAKD,CAAAA,CAAe,YAAY,EAC/D,OAAA,MAAMG,CAAAA,CAAG,GAAGO,CAAAA,CAAmB,CAAE,SAAA,CAAW,IAAA,CAAM,KAAA,CAAO,IAAK,CAAC,CAAA,CAAE,KAAA,CAAM,IAAM,CAAC,CAAC,CAAA,CAExE,CAAE,IAAA,CAAMV,CAAAA,CAAe,MAAA,CAAQQ,CAAW,CACnD,CAEA,MAAc,eAAA,CAAgBd,CAAAA,CAA6B,CACzD,IAAMM,CAAAA,CAAgBC,CAAAA,CAAK,KACzB,IAAA,CAAK,YAAA,CACL,YAAA,CACAC,GAAAA,CAAWR,CAAAA,CAAK,EAAE,CACpB,CAAA,CACA,MAAMW,GAAAA,CAAUJ,CAAAA,CAAK,OAAA,CAAQD,CAAa,CAAC,CAAA,CAG3C,GAAI,CACF,GAAM,CAAE,OAAA,CAASjB,CAAK,CAAA,CAAI,IAAA,CAAK,eAAe,KAAA,CAC5C,KAAA,CACA,CAAC,OAAA,CAAS,SAAA,CAAW,gBAAA,CAAkB,GAAA,CAAKiB,CAAa,CAAA,CACzD,CAAE,GAAA,CAAK,IAAA,CAAK,WAAY,CAC1B,CAAA,CAEA,MAAM,IAAI,OAAA,CAAc,CAAClB,CAAAA,CAAS2B,CAAAA,GAAW,CAC3C1B,CAAAA,CAAK,GAAG,OAAA,CAAUK,CAAAA,EAAS,CACrBA,CAAAA,GAAS,CAAA,CAAGN,CAAAA,GACX2B,CAAAA,CAAO,IAAI,KAAA,CAAM,kBAAkB,CAAC,EAC3C,CAAC,CAAA,CACD1B,CAAAA,CAAK,EAAA,CAAG,OAAA,CAAS0B,CAAM,EACzB,CAAC,EACH,CAAA,KAAQ,CAGN,IAAME,CAAAA,CAAO,CAAC,KAAM,CAAA,eAAA,EADAV,CAAAA,CAAK,KAAK,IAAA,CAAK,YAAA,CAAc,mBAAmB,CACnB,CAAA,CAAA,CAAI,IAAA,CAAM,CAAA,EAAGD,CAAa,CAAA,CAAA,CAAG,EAExE,CAAE,OAAA,CAASjB,CAAK,CAAA,CAAI,IAAA,CAAK,cAAA,CAAe,MAAM,OAAA,CAAS4B,CAAAA,CAAM,CACjE,GAAA,CAAK,IAAA,CAAK,WACZ,CAAC,CAAA,CAED,MAAM,IAAI,OAAA,CAAc,CAAC7B,CAAAA,CAAS2B,IAAW,CAC3C1B,CAAAA,CAAK,EAAA,CAAG,OAAA,CAAUK,CAAAA,EAAS,CACrBA,IAAS,CAAA,CAAGN,CAAAA,EAAQ,CACnB2B,CAAAA,CAAO,IAAI,KAAA,CAAM,0BAA0BrB,CAAI,CAAA,CAAE,CAAC,EACzD,CAAC,CAAA,CACDL,EAAK,EAAA,CAAG,OAAA,CAAS0B,CAAM,EACzB,CAAC,EACH,CAGA,IAAMG,CAAAA,CAAkBX,CAAAA,CAAK,IAAA,CAAKD,CAAAA,CAAe,YAAY,CAAA,CAC7D,OAAA,MAAMG,CAAAA,CAAG,EAAA,CAAGS,CAAAA,CAAiB,CAAE,UAAW,IAAA,CAAM,KAAA,CAAO,IAAK,CAAC,CAAA,CAAE,KAAA,CAAM,IAAM,CAAC,CAAC,CAAA,CAEtEZ,CACT,CACF,EAEA,SAASO,CAAAA,CAAcM,CAAAA,CAAuB,CAC5C,OAAOA,CAAAA,CACJ,WAAA,GACA,OAAA,CAAQ,aAAA,CAAe,GAAG,CAAA,CAC1B,OAAA,CAAQ,QAAA,CAAU,EAAE,CAAA,CACpB,KAAA,CAAM,CAAA,CAAG,EAAE,CAChB","file":"workspace-manager-AS4TFA7R.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 try {\n const { process: proc } = this.processManager.spawn(\n 'git',\n ['rev-parse', '--is-inside-work-tree'],\n { cwd: this.projectRoot },\n );\n const code = await new Promise<number | null>((resolve) => {\n proc.on('close', resolve);\n proc.on('error', () => resolve(1));\n });\n this.isGitRepo = code === 0;\n } catch {\n this.isGitRepo = false;\n }\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): 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 try {\n const { process: proc } = this.processManager.spawn(\n 'git',\n ['worktree', 'remove', '--force', workspacePath],\n { cwd: this.projectRoot },\n );\n await new Promise<void>((resolve) => {\n proc.on('close', () => resolve());\n proc.on('error', () => resolve());\n });\n } catch {\n // Not a worktree or git not available — fall through to rm\n }\n\n // Remove directory regardless (handles isolated mode and worktree cleanup failures)\n try {\n await fs.rm(workspacePath, { recursive: true, force: true });\n } catch {\n // Workspace may not exist\n }\n }\n\n validate(workspacePath: string, projectRoot: string): void {\n validateWorkspacePath(workspacePath, projectRoot);\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 const { process: proc } = this.processManager.spawn(\n 'git',\n ['worktree', 'add', workspacePath, '-b', branchName],\n { cwd: this.projectRoot },\n );\n\n await new Promise<void>((resolve, reject) => {\n proc.on('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`git worktree add failed with code ${code}`));\n });\n proc.on('error', reject);\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 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 { process: proc } = this.processManager.spawn(\n 'git',\n ['clone', '--local', '--no-hardlinks', '.', workspacePath],\n { cwd: this.projectRoot },\n );\n\n await new Promise<void>((resolve, reject) => {\n proc.on('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error('git clone failed'));\n });\n proc.on('error', reject);\n });\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 { process: proc } = this.processManager.spawn('rsync', args, {\n cwd: this.projectRoot,\n });\n\n await new Promise<void>((resolve, reject) => {\n proc.on('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`rsync failed with code ${code}`));\n });\n proc.on('error', reject);\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"]}
|