@oxgeneral/orch 0.3.2 → 0.3.4
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-RKAPZNZO.js +6682 -0
- package/dist/agent-KBTLGGCT.js +183 -0
- package/dist/agent-shop-YN2BSLHM.js +2 -0
- package/dist/chunk-2C2TFQ7K.js +136 -0
- package/dist/chunk-45K2XID7.js +29 -0
- package/dist/{shell-IH2MMTVP.js → chunk-52BFUGDD.js} +8 -6
- package/dist/chunk-7X2GI5OV.js +181 -0
- package/dist/{chunk-HSBYJ5C5.js → chunk-A36WAF2S.js} +89 -2
- package/dist/chunk-CHIP7O6V.js +83 -0
- package/dist/{claude-RIB3RQS5.js → chunk-D6RFF3KN.js} +12 -9
- package/dist/{chunk-BCPUTULS.js → chunk-DAVHOWGD.js} +188 -16
- package/dist/chunk-FRTKB575.js +87 -0
- package/dist/chunk-HXYAZGLP.js +15 -0
- package/dist/chunk-I3SMISEF.js +29 -0
- package/dist/chunk-K6DMQERQ.js +89 -0
- package/dist/chunk-LV6GDBBI.js +297 -0
- package/dist/chunk-NLQAJ7TW.js +147 -0
- package/dist/chunk-NLQAJ7TW.js.map +1 -0
- package/dist/chunk-P6ATSXGL.js +107 -0
- package/dist/chunk-PNE6LQRF.js +5 -0
- package/dist/{chunk-MGFMVPRD.js → chunk-S3QYSBW4.js} +11 -4
- package/dist/chunk-S3QYSBW4.js.map +1 -0
- package/dist/chunk-U2VDNUZL.js +52 -0
- package/dist/{chunk-QEEM67OA.js → chunk-UIJYU3J7.js} +3 -3
- package/dist/{chunk-QEEM67OA.js.map → chunk-UIJYU3J7.js.map} +1 -1
- package/dist/{chunk-2UC4SVJB.js → chunk-VMDQVRBR.js} +22 -8
- package/dist/chunk-VMDQVRBR.js.map +1 -0
- package/dist/chunk-W6RSVMXR.js +66 -0
- package/dist/claude-INM52PTH.js +88 -0
- package/dist/claude-INM52PTH.js.map +1 -0
- package/dist/claude-NHUNA5RZ.js +5 -0
- package/dist/cli.js +199 -1
- package/dist/clipboard-service-RTDUUQQU.js +200 -0
- package/dist/{codex-VBUSA2GJ.js → codex-DIXT44JR.js} +17 -11
- package/dist/codex-QGH2GRV6.js +125 -0
- package/dist/codex-QGH2GRV6.js.map +1 -0
- package/dist/config-OTAVSMOD.js +75 -0
- package/dist/container-LJU4QNDH.js +1594 -0
- package/dist/context-OL4BVUV5.js +83 -0
- package/dist/{cursor-4QIOTDBW.js → cursor-C3TR2IJC.js} +11 -8
- package/dist/cursor-KQJTQ73D.js +99 -0
- package/dist/cursor-KQJTQ73D.js.map +1 -0
- package/dist/doctor-V2FPS236.js +67 -0
- package/dist/doctor-service-TPOMFAIG.js +2 -0
- package/dist/goal-FMYYN2FR.js +138 -0
- package/dist/index.d.ts +64 -41
- package/dist/index.js +23 -14
- package/dist/index.js.map +1 -1
- package/dist/init-U7MCIOB2.js +165 -0
- package/dist/logs-PHPYWQ6I.js +207 -0
- package/dist/msg-FUWWLEKM.js +95 -0
- package/dist/orchestrator-ADO66XZ3.js +5 -0
- package/dist/{orchestrator-FGGXK3N3.js.map → orchestrator-ADO66XZ3.js.map} +1 -1
- package/dist/orchestrator-E3FQ4SOE.js +1424 -0
- package/dist/process-manager-HUVNAPQV.js +2 -0
- package/dist/registry-PQWRVNF2.js +2 -0
- package/dist/run-N72G5V2H.js +95 -0
- package/dist/shell-3S4VLYEG.js +4 -0
- package/dist/shell-JXOPKDXH.js +221 -0
- package/dist/shell-JXOPKDXH.js.map +1 -0
- package/dist/shop-picker-2HY67UWP.js +79 -0
- package/dist/status-RZWN2C6C.js +56 -0
- package/dist/task-2TJW6Z7O.js +221 -0
- package/dist/team-PFLP4PPL.js +97 -0
- package/dist/template-engine-4IZKRRHG.js +3 -0
- package/dist/tui-IM3YUUVD.js +245 -0
- package/dist/update-YLP7FPNY.js +64 -0
- package/dist/update-check-4YKLGBFB.js +2 -0
- package/dist/{workspace-manager-T6AXG7XL.js → workspace-manager-EVD67GCG.js} +4 -4
- package/dist/{workspace-manager-T6AXG7XL.js.map → workspace-manager-EVD67GCG.js.map} +1 -1
- package/dist/workspace-manager-JM6U7JOH.js +215 -0
- package/package.json +1 -1
- package/readme.md +9 -2
- package/scripts/postinstall.js +44 -2
- package/dist/App-YJM5QGP7.js +0 -19
- package/dist/agent-S4DKSX63.js +0 -9
- package/dist/agent-shop-D2RS4BZK.js +0 -2
- package/dist/chunk-2UC4SVJB.js.map +0 -1
- package/dist/chunk-5AJ4LYO5.js +0 -8
- package/dist/chunk-6MJ7V6VY.js +0 -2
- package/dist/chunk-CDFA4IIQ.js +0 -2
- package/dist/chunk-CHRW4CLD.js +0 -2
- package/dist/chunk-GZ2Q56YZ.js +0 -2
- package/dist/chunk-HMMPM7MF.js +0 -3
- package/dist/chunk-HXOMNULD.js +0 -2
- package/dist/chunk-IQXRQBUK.js +0 -83
- package/dist/chunk-IQXRQBUK.js.map +0 -1
- package/dist/chunk-L26TK7Y5.js +0 -2
- package/dist/chunk-L3FYR45M.js +0 -2
- package/dist/chunk-LXNRCJ22.js +0 -2
- package/dist/chunk-MGFMVPRD.js.map +0 -1
- package/dist/chunk-MNXU3KCD.js +0 -2
- package/dist/chunk-PJ5DKXGR.js +0 -2
- package/dist/chunk-UMZEA3JT.js +0 -5
- package/dist/chunk-UW6GUUE6.js +0 -3
- package/dist/chunk-ZA5Z33GO.js +0 -11
- package/dist/claude-E36EGXUV.js +0 -2
- package/dist/claude-RIB3RQS5.js.map +0 -1
- package/dist/clipboard-service-PDTSZIR5.js +0 -25
- package/dist/codex-OTZKVESD.js +0 -2
- package/dist/codex-VBUSA2GJ.js.map +0 -1
- package/dist/config-CCSS2P7R.js +0 -2
- package/dist/container-OIXLFSX2.js +0 -6
- package/dist/context-GSMQHQES.js +0 -7
- package/dist/cursor-3DJA6LWS.js +0 -2
- package/dist/cursor-4QIOTDBW.js.map +0 -1
- package/dist/doctor-KBK5JZBZ.js +0 -2
- package/dist/doctor-service-PB7YBH3F.js +0 -2
- package/dist/goal-RFKFPR7M.js +0 -8
- package/dist/init-WRDFAFS2.js +0 -53
- package/dist/logs-5QHJWMEG.js +0 -12
- package/dist/msg-4SCLBO4K.js +0 -9
- package/dist/orchestrator-FGGXK3N3.js +0 -5
- package/dist/orchestrator-R7IWZUT6.js +0 -13
- package/dist/process-manager-33H27MQF.js +0 -2
- package/dist/registry-BO2PPRNG.js +0 -2
- package/dist/run-HSHRELOP.js +0 -3
- package/dist/shell-EOJBDWTH.js +0 -2
- package/dist/shell-IH2MMTVP.js.map +0 -1
- package/dist/shop-picker-LE3SKFOX.js +0 -5
- package/dist/status-DLBNWSWM.js +0 -2
- package/dist/task-J6ZN7ALI.js +0 -20
- package/dist/team-MSIBKOQC.js +0 -4
- package/dist/template-engine-ONIDVD4F.js +0 -2
- package/dist/tui-LW7WIDE3.js +0 -2
- package/dist/update-PC2ENCKU.js +0 -2
- package/dist/update-check-HGMBDYHL.js +0 -2
- package/dist/workspace-manager-KOOYTO7E.js +0 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { printError, amber, printSuccess, dim, getIcon } from './chunk-7X2GI5OV.js';
|
|
3
|
+
|
|
4
|
+
// src/cli/commands/run.ts
|
|
5
|
+
function registerRunCommand(program, container) {
|
|
6
|
+
program.command("run [task-id]").description("Run tasks").option("--all", "Run all todo tasks").option("--watch", "Watch mode: continuous orchestration").action(async (taskId, opts) => {
|
|
7
|
+
await container.paths.requireInit();
|
|
8
|
+
if (opts.watch) {
|
|
9
|
+
await runWatch(container);
|
|
10
|
+
} else if (opts.all) {
|
|
11
|
+
await runAll(container);
|
|
12
|
+
} else if (taskId) {
|
|
13
|
+
await runSingle(container, taskId);
|
|
14
|
+
} else {
|
|
15
|
+
printError("Specify a task ID, --all, or --watch");
|
|
16
|
+
process.exit(2);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async function runSingle(container, taskId) {
|
|
21
|
+
const task = await container.taskService.get(taskId);
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(` ${amber("orch")} \xB7 running ${taskId} "${task.title}"`);
|
|
24
|
+
const unsub = container.eventBus.onAny((event) => {
|
|
25
|
+
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
26
|
+
switch (event.type) {
|
|
27
|
+
case "agent:output":
|
|
28
|
+
console.log(` ${dim(time)} ${getIcon("agentAction")} ${typeof event.data === "string" ? event.data.slice(0, 80) : ""}`);
|
|
29
|
+
break;
|
|
30
|
+
case "agent:file_changed":
|
|
31
|
+
console.log(` ${dim(time)} ${getIcon("agentAction")} Modified ${event.path}`);
|
|
32
|
+
break;
|
|
33
|
+
case "agent:error":
|
|
34
|
+
console.log(` ${dim(time)} ${getIcon("failed")} ${event.error}`);
|
|
35
|
+
break;
|
|
36
|
+
case "agent:completed":
|
|
37
|
+
if (event.success) {
|
|
38
|
+
printSuccess("Done");
|
|
39
|
+
} else {
|
|
40
|
+
printError("Failed");
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
try {
|
|
46
|
+
await container.orchestrator.runTask(taskId);
|
|
47
|
+
} finally {
|
|
48
|
+
unsub();
|
|
49
|
+
}
|
|
50
|
+
console.log();
|
|
51
|
+
}
|
|
52
|
+
async function runAll(container) {
|
|
53
|
+
console.log();
|
|
54
|
+
console.log(` ${amber("orch")} \xB7 running all todo tasks`);
|
|
55
|
+
console.log();
|
|
56
|
+
await container.orchestrator.runAll();
|
|
57
|
+
}
|
|
58
|
+
async function runWatch(container) {
|
|
59
|
+
console.log(`${amber("orch")} \xB7 watching \xB7 poll interval ${container.config.scheduling.poll_interval_ms / 1e3}s`);
|
|
60
|
+
console.log("\u2501".repeat(43));
|
|
61
|
+
console.log();
|
|
62
|
+
container.eventBus.onAny((event) => {
|
|
63
|
+
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit" });
|
|
64
|
+
switch (event.type) {
|
|
65
|
+
case "agent:output": {
|
|
66
|
+
const data = typeof event.data === "string" ? event.data.slice(0, 60) : "";
|
|
67
|
+
console.log(`${dim(time)} ${getIcon("agentAction")} ${data}`);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "agent:completed":
|
|
71
|
+
if (event.success) {
|
|
72
|
+
console.log(`${dim(time)} ${getIcon("done")} DONE ${event.runId}`);
|
|
73
|
+
} else {
|
|
74
|
+
console.log(`${dim(time)} ${getIcon("failed")} FAIL ${event.runId}`);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case "run:retry":
|
|
78
|
+
console.log(`${dim(time)} ${getIcon("retrying")} RETRY attempt ${event.attempt} \xB7 next in ${Math.round(event.delay_ms / 1e3)}s`);
|
|
79
|
+
break;
|
|
80
|
+
case "orchestrator:tick":
|
|
81
|
+
process.stdout.write(`\r${amber("orch")} \xB7 watching \xB7 ${event.running} running \xB7 ${event.queued} queued `);
|
|
82
|
+
break;
|
|
83
|
+
case "orchestrator:stall_detected":
|
|
84
|
+
console.log(`${dim(time)} ${getIcon("warning")} STALL ${event.runId}`);
|
|
85
|
+
break;
|
|
86
|
+
case "orchestrator:shutdown":
|
|
87
|
+
console.log(`
|
|
88
|
+
${dim("Shutting down...")}`);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
await container.orchestrator.startWatch();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { registerRunCommand };
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { classifyAdapterError } from './chunk-NLQAJ7TW.js';
|
|
2
|
+
import { readLines } from './chunk-O2MSGW3V.js';
|
|
3
|
+
import { execFile } from 'child_process';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
|
|
6
|
+
// src/infrastructure/adapters/event-buffer.ts
|
|
7
|
+
var DEFAULT_CAPACITY = 1024;
|
|
8
|
+
function deferred() {
|
|
9
|
+
let resolve;
|
|
10
|
+
const promise = new Promise((r) => {
|
|
11
|
+
resolve = r;
|
|
12
|
+
});
|
|
13
|
+
return { promise, resolve };
|
|
14
|
+
}
|
|
15
|
+
var EventBuffer = class {
|
|
16
|
+
buf;
|
|
17
|
+
head = 0;
|
|
18
|
+
// read index
|
|
19
|
+
tail = 0;
|
|
20
|
+
// write index
|
|
21
|
+
count = 0;
|
|
22
|
+
capacity;
|
|
23
|
+
// Consumer notification: resolved when new data is available
|
|
24
|
+
dataReady = null;
|
|
25
|
+
// Producer notification: resolved when space is available
|
|
26
|
+
spaceReady = null;
|
|
27
|
+
closed = false;
|
|
28
|
+
constructor(capacity = DEFAULT_CAPACITY) {
|
|
29
|
+
this.capacity = capacity;
|
|
30
|
+
this.buf = new Array(capacity);
|
|
31
|
+
}
|
|
32
|
+
/** Number of buffered events. */
|
|
33
|
+
get size() {
|
|
34
|
+
return this.count;
|
|
35
|
+
}
|
|
36
|
+
get isFull() {
|
|
37
|
+
return this.count >= this.capacity;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Push an event into the buffer.
|
|
41
|
+
* If the buffer is full, waits until space is available (backpressure).
|
|
42
|
+
*/
|
|
43
|
+
async push(event) {
|
|
44
|
+
while (this.isFull && !this.closed) {
|
|
45
|
+
if (!this.spaceReady) {
|
|
46
|
+
this.spaceReady = deferred();
|
|
47
|
+
}
|
|
48
|
+
await this.spaceReady.promise;
|
|
49
|
+
}
|
|
50
|
+
if (this.closed) return;
|
|
51
|
+
this.buf[this.tail] = event;
|
|
52
|
+
this.tail = (this.tail + 1) % this.capacity;
|
|
53
|
+
this.count++;
|
|
54
|
+
if (this.dataReady) {
|
|
55
|
+
const dr = this.dataReady;
|
|
56
|
+
this.dataReady = null;
|
|
57
|
+
dr.resolve();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Dequeue the next event. O(1).
|
|
62
|
+
* Returns undefined only when buffer is empty AND closed.
|
|
63
|
+
*/
|
|
64
|
+
async take() {
|
|
65
|
+
while (this.count === 0) {
|
|
66
|
+
if (this.closed) return void 0;
|
|
67
|
+
if (!this.dataReady) {
|
|
68
|
+
this.dataReady = deferred();
|
|
69
|
+
}
|
|
70
|
+
await this.dataReady.promise;
|
|
71
|
+
}
|
|
72
|
+
const event = this.buf[this.head];
|
|
73
|
+
this.buf[this.head] = void 0;
|
|
74
|
+
this.head = (this.head + 1) % this.capacity;
|
|
75
|
+
this.count--;
|
|
76
|
+
if (this.spaceReady) {
|
|
77
|
+
const sr = this.spaceReady;
|
|
78
|
+
this.spaceReady = null;
|
|
79
|
+
sr.resolve();
|
|
80
|
+
}
|
|
81
|
+
return event;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Signal that no more events will be pushed.
|
|
85
|
+
* Wakes up any waiting consumer/producer.
|
|
86
|
+
*/
|
|
87
|
+
close() {
|
|
88
|
+
this.closed = true;
|
|
89
|
+
if (this.dataReady) {
|
|
90
|
+
const dr = this.dataReady;
|
|
91
|
+
this.dataReady = null;
|
|
92
|
+
dr.resolve();
|
|
93
|
+
}
|
|
94
|
+
if (this.spaceReady) {
|
|
95
|
+
const sr = this.spaceReady;
|
|
96
|
+
this.spaceReady = null;
|
|
97
|
+
sr.resolve();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
get isClosed() {
|
|
101
|
+
return this.closed;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Async iterator that drains the buffer until closed and empty.
|
|
105
|
+
*/
|
|
106
|
+
async *[Symbol.asyncIterator]() {
|
|
107
|
+
while (true) {
|
|
108
|
+
const event = await this.take();
|
|
109
|
+
if (event === void 0) return;
|
|
110
|
+
yield event;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
var execFileAsync = promisify(execFile);
|
|
115
|
+
var ShellAdapter = class {
|
|
116
|
+
constructor(processManager) {
|
|
117
|
+
this.processManager = processManager;
|
|
118
|
+
}
|
|
119
|
+
kind = "shell";
|
|
120
|
+
async test() {
|
|
121
|
+
try {
|
|
122
|
+
const { stdout } = await execFileAsync("bash", ["--version"]);
|
|
123
|
+
const version = stdout.split("\n")[0]?.trim() ?? "unknown";
|
|
124
|
+
return { ok: true, version };
|
|
125
|
+
} catch {
|
|
126
|
+
return { ok: false, error: "bash not found", errorKind: classifyAdapterError("bash not found") };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
execute(params) {
|
|
130
|
+
const command = params.config.command;
|
|
131
|
+
if (!command) {
|
|
132
|
+
async function* errorGen() {
|
|
133
|
+
yield {
|
|
134
|
+
type: "error",
|
|
135
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
136
|
+
data: "Shell adapter requires a command in agent config",
|
|
137
|
+
errorKind: "spawn_failed" /* SPAWN_FAILED */
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return { pid: 0, events: errorGen() };
|
|
141
|
+
}
|
|
142
|
+
const { process: proc, pid } = this.processManager.spawn("bash", ["-lc", command], {
|
|
143
|
+
cwd: params.workspace,
|
|
144
|
+
env: {
|
|
145
|
+
...process.env,
|
|
146
|
+
...params.env,
|
|
147
|
+
ORCHESTRY_TASK_PROMPT: params.prompt
|
|
148
|
+
},
|
|
149
|
+
signal: params.signal
|
|
150
|
+
});
|
|
151
|
+
const signal = params.signal;
|
|
152
|
+
const processManager = this.processManager;
|
|
153
|
+
async function* generateEvents() {
|
|
154
|
+
const buffer = new EventBuffer();
|
|
155
|
+
const onAbort = () => {
|
|
156
|
+
processManager.killWithGrace(pid, 5e3).catch(() => {
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
if (signal) {
|
|
160
|
+
if (signal.aborted) {
|
|
161
|
+
onAbort();
|
|
162
|
+
} else {
|
|
163
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const stdoutPromise = (async () => {
|
|
167
|
+
if (!proc.stdout) return;
|
|
168
|
+
for await (const line of readLines(proc.stdout)) {
|
|
169
|
+
if (signal?.aborted) break;
|
|
170
|
+
await buffer.push({
|
|
171
|
+
type: "output",
|
|
172
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
173
|
+
data: line
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
})();
|
|
177
|
+
const stderrPromise = (async () => {
|
|
178
|
+
if (!proc.stderr) return;
|
|
179
|
+
for await (const line of readLines(proc.stderr)) {
|
|
180
|
+
if (signal?.aborted) break;
|
|
181
|
+
await buffer.push({
|
|
182
|
+
type: "error",
|
|
183
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
184
|
+
data: line,
|
|
185
|
+
errorKind: classifyAdapterError(line)
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
})();
|
|
189
|
+
void Promise.all([stdoutPromise, stderrPromise]).then(
|
|
190
|
+
() => buffer.close(),
|
|
191
|
+
() => buffer.close()
|
|
192
|
+
);
|
|
193
|
+
yield* buffer;
|
|
194
|
+
if (signal && !signal.aborted) {
|
|
195
|
+
signal.removeEventListener("abort", onAbort);
|
|
196
|
+
}
|
|
197
|
+
await new Promise((resolve, reject) => {
|
|
198
|
+
if (proc.exitCode !== null || proc.killed) {
|
|
199
|
+
resolve();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
proc.on("close", (code) => {
|
|
203
|
+
if (code === 0 || signal?.aborted) {
|
|
204
|
+
resolve();
|
|
205
|
+
} else {
|
|
206
|
+
reject(new Error(`Shell command exited with code ${code}`));
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
proc.on("error", reject);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return { pid, events: generateEvents() };
|
|
213
|
+
}
|
|
214
|
+
async stop(pid) {
|
|
215
|
+
await this.processManager.killWithGrace(pid);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export { ShellAdapter };
|
|
220
|
+
//# sourceMappingURL=shell-JXOPKDXH.js.map
|
|
221
|
+
//# sourceMappingURL=shell-JXOPKDXH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infrastructure/adapters/event-buffer.ts","../src/infrastructure/adapters/shell.ts"],"names":[],"mappings":"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","file":"shell-JXOPKDXH.js","sourcesContent":["/**\n * Lock-free ring buffer for AgentEvent streaming.\n *\n * Replaces Array.shift() O(n) polling with O(1) dequeue\n * and event-driven notification instead of 50ms busy-wait.\n * Includes backpressure: when buffer is full, push() returns\n * a promise that resolves when space is available.\n */\n\nimport type { AgentEvent } from './interface.js';\n\nconst DEFAULT_CAPACITY = 1024;\n\ninterface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n}\n\nfunction deferred<T>(): Deferred<T> {\n let resolve!: (value: T) => void;\n const promise = new Promise<T>((r) => { resolve = r; });\n return { promise, resolve };\n}\n\nexport class EventBuffer {\n private buf: (AgentEvent | undefined)[];\n private head = 0; // read index\n private tail = 0; // write index\n private count = 0;\n private readonly capacity: number;\n\n // Consumer notification: resolved when new data is available\n private dataReady: Deferred<void> | null = null;\n // Producer notification: resolved when space is available\n private spaceReady: Deferred<void> | null = null;\n\n private closed = false;\n\n constructor(capacity = DEFAULT_CAPACITY) {\n this.capacity = capacity;\n this.buf = new Array(capacity);\n }\n\n /** Number of buffered events. */\n get size(): number {\n return this.count;\n }\n\n get isFull(): boolean {\n return this.count >= this.capacity;\n }\n\n /**\n * Push an event into the buffer.\n * If the buffer is full, waits until space is available (backpressure).\n */\n async push(event: AgentEvent): Promise<void> {\n while (this.isFull && !this.closed) {\n if (!this.spaceReady) {\n this.spaceReady = deferred<void>();\n }\n await this.spaceReady.promise;\n }\n if (this.closed) return;\n\n this.buf[this.tail] = event;\n this.tail = (this.tail + 1) % this.capacity;\n this.count++;\n\n // Wake up consumer if waiting\n if (this.dataReady) {\n const dr = this.dataReady;\n this.dataReady = null;\n dr.resolve();\n }\n }\n\n /**\n * Dequeue the next event. O(1).\n * Returns undefined only when buffer is empty AND closed.\n */\n async take(): Promise<AgentEvent | undefined> {\n while (this.count === 0) {\n if (this.closed) return undefined;\n if (!this.dataReady) {\n this.dataReady = deferred<void>();\n }\n await this.dataReady.promise;\n }\n\n const event = this.buf[this.head];\n this.buf[this.head] = undefined; // allow GC\n this.head = (this.head + 1) % this.capacity;\n this.count--;\n\n // Wake up producer if waiting for space\n if (this.spaceReady) {\n const sr = this.spaceReady;\n this.spaceReady = null;\n sr.resolve();\n }\n\n return event;\n }\n\n /**\n * Signal that no more events will be pushed.\n * Wakes up any waiting consumer/producer.\n */\n close(): void {\n this.closed = true;\n if (this.dataReady) {\n const dr = this.dataReady;\n this.dataReady = null;\n dr.resolve();\n }\n if (this.spaceReady) {\n const sr = this.spaceReady;\n this.spaceReady = null;\n sr.resolve();\n }\n }\n\n get isClosed(): boolean {\n return this.closed;\n }\n\n /**\n * Async iterator that drains the buffer until closed and empty.\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<AgentEvent> {\n while (true) {\n const event = await this.take();\n if (event === undefined) return;\n yield event;\n }\n }\n}\n","/**\n * Shell adapter.\n *\n * Spawns an arbitrary command via `bash -lc`.\n * Task prompt is passed via ORCHESTRY_TASK_PROMPT env variable.\n * Consumes stdout and stderr concurrently to avoid deadlocks.\n */\n\nimport type { IAgentAdapter, AdapterTestResult, ExecuteParams, AgentEvent, ExecuteHandle } from './interface.js';\nimport type { IProcessManager } from '../process/process-manager.js';\nimport { readLines } from '../process/process-manager.js';\nimport { EventBuffer } from './event-buffer.js';\nimport { classifyAdapterError, AdapterErrorKind } from '../../domain/errors.js';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport class ShellAdapter implements IAgentAdapter {\n readonly kind = 'shell';\n\n constructor(private readonly processManager: IProcessManager) {}\n\n async test(): Promise<AdapterTestResult> {\n try {\n const { stdout } = await execFileAsync('bash', ['--version']);\n const version = stdout.split('\\n')[0]?.trim() ?? 'unknown';\n return { ok: true, version };\n } catch {\n return { ok: false, error: 'bash not found', errorKind: classifyAdapterError('bash not found') };\n }\n }\n\n execute(params: ExecuteParams): ExecuteHandle {\n const command = params.config.command;\n if (!command) {\n // Return a handle that immediately yields an error\n async function* errorGen(): AsyncGenerator<AgentEvent> {\n yield {\n type: 'error',\n timestamp: new Date().toISOString(),\n data: 'Shell adapter requires a command in agent config',\n errorKind: AdapterErrorKind.SPAWN_FAILED,\n };\n }\n return { pid: 0, events: errorGen() };\n }\n\n const { process: proc, pid } = this.processManager.spawn('bash', ['-lc', command], {\n cwd: params.workspace,\n env: {\n ...process.env,\n ...params.env,\n ORCHESTRY_TASK_PROMPT: params.prompt,\n },\n signal: params.signal,\n });\n\n const signal = params.signal;\n const processManager = this.processManager;\n\n async function* generateEvents(): AsyncGenerator<AgentEvent> {\n // Ring buffer with backpressure replaces Array.shift() polling\n const buffer = new EventBuffer();\n\n // Ensure process is reaped on abort — SIGTERM + grace period + SIGKILL\n const onAbort = () => {\n processManager.killWithGrace(pid, 5_000).catch(() => {});\n };\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n }\n\n const stdoutPromise = (async () => {\n if (!proc.stdout) return;\n for await (const line of readLines(proc.stdout)) {\n if (signal?.aborted) break;\n await buffer.push({\n type: 'output',\n timestamp: new Date().toISOString(),\n data: line,\n });\n }\n })();\n\n const stderrPromise = (async () => {\n if (!proc.stderr) return;\n for await (const line of readLines(proc.stderr)) {\n if (signal?.aborted) break;\n await buffer.push({\n type: 'error',\n timestamp: new Date().toISOString(),\n data: line,\n errorKind: classifyAdapterError(line),\n });\n }\n })();\n\n // Close the buffer once both streams are drained (or on error)\n void Promise.all([stdoutPromise, stderrPromise]).then(\n () => buffer.close(),\n () => buffer.close(),\n );\n\n // Yield events as they arrive — no polling, no busy-wait\n yield* buffer;\n\n // Clean up abort listener\n if (signal && !signal.aborted) {\n signal.removeEventListener('abort', onAbort);\n }\n\n // Wait for process to exit\n await new Promise<void>((resolve, reject) => {\n // If process already exited, resolve immediately\n if (proc.exitCode !== null || proc.killed) {\n resolve();\n return;\n }\n proc.on('close', (code) => {\n if (code === 0 || signal?.aborted) {\n resolve();\n } else {\n reject(new Error(`Shell command exited with code ${code}`));\n }\n });\n proc.on('error', reject);\n });\n }\n\n return { pid, events: generateEvents() };\n }\n\n async stop(pid: number): Promise<void> {\n await this.processManager.killWithGrace(pid);\n }\n}\n"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
async function pickFromShop(templates) {
|
|
6
|
+
if (!process.stdin.isTTY) return null;
|
|
7
|
+
let selected = 0;
|
|
8
|
+
function getPageSize() {
|
|
9
|
+
const rows = process.stdout.rows ?? 24;
|
|
10
|
+
return Math.max(1, Math.min(templates.length, rows - 4));
|
|
11
|
+
}
|
|
12
|
+
function render() {
|
|
13
|
+
const pageSize = getPageSize();
|
|
14
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
15
|
+
console.log(chalk.bold.yellow("\n AGENT SHOP") + chalk.gray(" \u2014 arrow keys to navigate, enter to select, q to cancel\n"));
|
|
16
|
+
const start = Math.max(0, Math.min(selected - Math.floor(pageSize / 2), templates.length - pageSize));
|
|
17
|
+
const end = Math.min(start + pageSize, templates.length);
|
|
18
|
+
for (let i = start; i < end; i++) {
|
|
19
|
+
const t = templates[i];
|
|
20
|
+
const isSelected = i === selected;
|
|
21
|
+
const cursor = isSelected ? chalk.yellow(" \u25B8 ") : " ";
|
|
22
|
+
const name = isSelected ? chalk.bold.white(t.name) : chalk.gray(t.name);
|
|
23
|
+
const desc = chalk.gray(` \u2014 ${t.description}`);
|
|
24
|
+
const model = chalk.gray.dim(` [${t.model.replace("claude-", "")}]`);
|
|
25
|
+
console.log(`${cursor}${name}${desc}${model}`);
|
|
26
|
+
}
|
|
27
|
+
if (templates.length > pageSize) {
|
|
28
|
+
console.log(chalk.gray(`
|
|
29
|
+
${start + 1}-${end} of ${templates.length}`));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
34
|
+
process.stdin.setRawMode(true);
|
|
35
|
+
readline.emitKeypressEvents(process.stdin);
|
|
36
|
+
function cleanup() {
|
|
37
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
38
|
+
try {
|
|
39
|
+
process.stdin.setRawMode(false);
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
rl.close();
|
|
43
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
44
|
+
}
|
|
45
|
+
const onKeypress = (_ch, key) => {
|
|
46
|
+
if (key.name === "up" || key.ctrl && key.name === "p") {
|
|
47
|
+
selected = (selected - 1 + templates.length) % templates.length;
|
|
48
|
+
render();
|
|
49
|
+
} else if (key.name === "down" || key.ctrl && key.name === "n") {
|
|
50
|
+
selected = (selected + 1) % templates.length;
|
|
51
|
+
render();
|
|
52
|
+
} else if (key.name === "return") {
|
|
53
|
+
cleanup();
|
|
54
|
+
resolve(templates[selected]);
|
|
55
|
+
} else if (key.name === "q" || key.name === "escape" || key.ctrl && key.name === "c") {
|
|
56
|
+
cleanup();
|
|
57
|
+
resolve(null);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
rl.on("error", () => {
|
|
61
|
+
cleanup();
|
|
62
|
+
resolve(null);
|
|
63
|
+
});
|
|
64
|
+
rl.on("close", () => {
|
|
65
|
+
cleanup();
|
|
66
|
+
resolve(null);
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
render();
|
|
70
|
+
} catch {
|
|
71
|
+
cleanup();
|
|
72
|
+
resolve(null);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
process.stdin.on("keypress", onKeypress);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { pickFromShop };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { formatDurationSince, amber, dim, statusIcon, agentName, priorityLabel, formatTokens } from './chunk-7X2GI5OV.js';
|
|
3
|
+
|
|
4
|
+
// src/cli/commands/status.ts
|
|
5
|
+
function registerStatusCommand(program, container) {
|
|
6
|
+
program.command("status").description("Show orchestrator status").action(async () => {
|
|
7
|
+
await container.paths.requireInit();
|
|
8
|
+
const tasks = await container.taskService.list();
|
|
9
|
+
const agents = await container.agentService.list();
|
|
10
|
+
const state = await container.stateStore.read();
|
|
11
|
+
if (container.context.json) {
|
|
12
|
+
console.log(JSON.stringify({ tasks, agents, state }, null, 2));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const runningCount = Object.keys(state.running).length;
|
|
16
|
+
const mode = state.pid ? "watching" : "idle";
|
|
17
|
+
const uptime = state.started_at ? formatDurationSince(state.started_at) : "";
|
|
18
|
+
console.log();
|
|
19
|
+
console.log(`${amber("orch")} \xB7 ${container.config.project.name} \xB7 ${mode}`);
|
|
20
|
+
console.log();
|
|
21
|
+
const counts = {};
|
|
22
|
+
for (const t of tasks) {
|
|
23
|
+
counts[t.status] = (counts[t.status] ?? 0) + 1;
|
|
24
|
+
}
|
|
25
|
+
if (runningCount > 0) {
|
|
26
|
+
console.log(` ${"RUNNING".padEnd(12)}${runningCount}${"".padEnd(20)}AGENTS ${agents.length}`);
|
|
27
|
+
}
|
|
28
|
+
for (const [status, count] of Object.entries(counts)) {
|
|
29
|
+
if (status !== "in_progress") {
|
|
30
|
+
console.log(` ${dim(status.padEnd(12))}${count}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const runningTasks = tasks.filter((t) => t.status === "in_progress");
|
|
34
|
+
if (runningTasks.length > 0) {
|
|
35
|
+
console.log();
|
|
36
|
+
for (const t of runningTasks) {
|
|
37
|
+
const time = formatDurationSince(t.updated_at);
|
|
38
|
+
console.log(
|
|
39
|
+
` ${statusIcon("in_progress")} ${t.assignee ? agentName(t.assignee) : ""} ${t.title.slice(0, 35).padEnd(37)}${time} ${priorityLabel(t.priority)}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const totalTokens = state.stats.total_tokens.total;
|
|
44
|
+
const footer = [
|
|
45
|
+
uptime ? `up ${uptime}` : null,
|
|
46
|
+
totalTokens > 0 ? `${formatTokens(totalTokens)} tokens` : null
|
|
47
|
+
].filter(Boolean).join(" \xB7 ");
|
|
48
|
+
if (footer) {
|
|
49
|
+
console.log();
|
|
50
|
+
console.log(` ${dim(footer)}`);
|
|
51
|
+
}
|
|
52
|
+
console.log();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { registerStatusCommand };
|