@oxgeneral/orch 0.2.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/LICENSE +21 -0
- package/dist/App-CPQPQTZU.js +4751 -0
- package/dist/agent-J62U7ABO.js +157 -0
- package/dist/chunk-2B32FPEB.js +11 -0
- package/dist/chunk-2B32FPEB.js.map +1 -0
- package/dist/chunk-2VSAM7RH.js +166 -0
- package/dist/chunk-33QNTNR6.js +46 -0
- package/dist/chunk-45K2XID7.js +29 -0
- package/dist/chunk-6GFVB6EK.js +101 -0
- package/dist/chunk-6HENRUYZ.js +2 -0
- package/dist/chunk-6HENRUYZ.js.map +1 -0
- package/dist/chunk-AELEEEV3.js +92 -0
- package/dist/chunk-AELEEEV3.js.map +1 -0
- package/dist/chunk-CHIP7O6V.js +83 -0
- package/dist/chunk-CIIE6LNG.js +217 -0
- package/dist/chunk-E3TCKHU6.js +13 -0
- package/dist/chunk-E3TCKHU6.js.map +1 -0
- package/dist/chunk-ED47GL3F.js +29 -0
- package/dist/chunk-HNKJ4IF7.js +177 -0
- package/dist/chunk-HXYAZGLP.js +15 -0
- package/dist/chunk-IRN2U2NE.js +79 -0
- package/dist/chunk-IZYSGYXG.js +2 -0
- package/dist/chunk-IZYSGYXG.js.map +1 -0
- package/dist/chunk-O5AO5QIR.js +76 -0
- package/dist/chunk-P6ATSXGL.js +107 -0
- package/dist/chunk-PBFE5V3G.js +2 -0
- package/dist/chunk-PBFE5V3G.js.map +1 -0
- package/dist/chunk-PNE6LQRF.js +5 -0
- package/dist/chunk-POUC4CPC.js +2 -0
- package/dist/chunk-POUC4CPC.js.map +1 -0
- package/dist/chunk-TX7WOFCW.js +59 -0
- package/dist/chunk-VTA74YWX.js +291 -0
- package/dist/chunk-XI4TU6VU.js +50 -0
- package/dist/chunk-ZU6AY2VU.js +2 -0
- package/dist/chunk-ZU6AY2VU.js.map +1 -0
- package/dist/claude-GH6P2DC5.js +4 -0
- package/dist/claude-S47YTIHU.js +2 -0
- package/dist/claude-S47YTIHU.js.map +1 -0
- package/dist/cli.js +205 -0
- package/dist/codex-2CH57B7G.js +2 -0
- package/dist/codex-2CH57B7G.js.map +1 -0
- package/dist/codex-U7LTJTX6.js +115 -0
- package/dist/config-VN4MYHSY.js +75 -0
- package/dist/container-74P43KDY.js +1532 -0
- package/dist/context-EPHCF34F.js +83 -0
- package/dist/cursor-3DI5GKRF.js +92 -0
- package/dist/cursor-QFUNKPCQ.js +2 -0
- package/dist/cursor-QFUNKPCQ.js.map +1 -0
- package/dist/doctor-BK46WCQ5.js +67 -0
- package/dist/doctor-service-A34DHPKI.js +2 -0
- package/dist/doctor-service-NTWBWOM2.js +2 -0
- package/dist/doctor-service-NTWBWOM2.js.map +1 -0
- package/dist/goal-KGAIM3ZK.js +110 -0
- package/dist/index.d.ts +1356 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/init-QBWCEDCI.js +152 -0
- package/dist/logs-PYEKMQE2.js +207 -0
- package/dist/msg-BBIPCGDO.js +95 -0
- package/dist/orchestrator-TAFBYQQ5.js +2 -0
- package/dist/orchestrator-TAFBYQQ5.js.map +1 -0
- package/dist/orchestrator-VGYKSOZJ.js +1292 -0
- package/dist/output-5VQVCJ2K.js +2 -0
- package/dist/process-manager-HUVNAPQV.js +2 -0
- package/dist/process-manager-TLZOTO4Y.js +2 -0
- package/dist/process-manager-TLZOTO4Y.js.map +1 -0
- package/dist/registry-PQWRVNF2.js +2 -0
- package/dist/registry-UQAHK77P.js +2 -0
- package/dist/registry-UQAHK77P.js.map +1 -0
- package/dist/run-4GSZFGQZ.js +95 -0
- package/dist/shell-5ZNXFGXV.js +3 -0
- package/dist/shell-OGTSH4RJ.js +3 -0
- package/dist/shell-OGTSH4RJ.js.map +1 -0
- package/dist/status-KIISF542.js +56 -0
- package/dist/task-NUCRHYW7.js +209 -0
- package/dist/team-IBUP5XV4.js +97 -0
- package/dist/template-engine-322SCRR6.js +2 -0
- package/dist/template-engine-322SCRR6.js.map +1 -0
- package/dist/template-engine-3CDRZNMJ.js +3 -0
- package/dist/tui-WWZA73IO.js +225 -0
- package/dist/update-RJ4IYACQ.js +64 -0
- package/dist/update-check-4RV7Z6WT.js +2 -0
- package/dist/workspace-manager-47KI7B27.js +179 -0
- package/dist/workspace-manager-7M46ESUL.js +2 -0
- package/dist/workspace-manager-7M46ESUL.js.map +1 -0
- package/package.json +79 -0
- package/readme.md +270 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// src/domain/errors.ts
|
|
3
|
+
var OrchestryError = class extends Error {
|
|
4
|
+
constructor(message, exitCode, hint) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.exitCode = exitCode;
|
|
7
|
+
this.hint = hint;
|
|
8
|
+
this.name = "OrchestryError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var NotInitializedError = class extends OrchestryError {
|
|
12
|
+
constructor() {
|
|
13
|
+
super("Not initialized", 3, "Run: orch init");
|
|
14
|
+
this.name = "NotInitializedError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var InvalidArgumentsError = class extends OrchestryError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message, 2);
|
|
20
|
+
this.name = "InvalidArgumentsError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var LockConflictError = class extends OrchestryError {
|
|
24
|
+
constructor(pid) {
|
|
25
|
+
super(`Orchestrator already running (PID: ${pid})`, 4, "Use: orch status");
|
|
26
|
+
this.name = "LockConflictError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var NoAgentsError = class extends OrchestryError {
|
|
30
|
+
constructor() {
|
|
31
|
+
super("No agents configured", 1, "Run: orch agent add <name> --adapter claude");
|
|
32
|
+
this.name = "NoAgentsError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var TaskNotFoundError = class extends OrchestryError {
|
|
36
|
+
constructor(taskId) {
|
|
37
|
+
super(`Task not found: ${taskId}`, 1);
|
|
38
|
+
this.name = "TaskNotFoundError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var AgentNotFoundError = class extends OrchestryError {
|
|
42
|
+
constructor(agentId) {
|
|
43
|
+
super(`Agent not found: ${agentId}`, 1);
|
|
44
|
+
this.name = "AgentNotFoundError";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var TaskAlreadyRunningError = class extends OrchestryError {
|
|
48
|
+
constructor(taskId, runId, agentName) {
|
|
49
|
+
super(
|
|
50
|
+
`Task ${taskId} is already running (run: ${runId}, agent: ${agentName})`,
|
|
51
|
+
1,
|
|
52
|
+
`Use: orch logs --task ${taskId} --follow`
|
|
53
|
+
);
|
|
54
|
+
this.name = "TaskAlreadyRunningError";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var InvalidTransitionError = class extends OrchestryError {
|
|
58
|
+
constructor(taskId, from, to) {
|
|
59
|
+
super(`Invalid transition for ${taskId}: ${from} \u2192 ${to}`, 1);
|
|
60
|
+
this.name = "InvalidTransitionError";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var GoalNotFoundError = class extends OrchestryError {
|
|
64
|
+
constructor(goalId) {
|
|
65
|
+
super(`Goal not found: ${goalId}`, 1);
|
|
66
|
+
this.name = "GoalNotFoundError";
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var TeamNotFoundError = class extends OrchestryError {
|
|
70
|
+
constructor(teamId) {
|
|
71
|
+
super(`Team not found: ${teamId}`, 1);
|
|
72
|
+
this.name = "TeamNotFoundError";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export { AgentNotFoundError, GoalNotFoundError, InvalidArgumentsError, InvalidTransitionError, LockConflictError, NoAgentsError, NotInitializedError, OrchestryError, TaskAlreadyRunningError, TaskNotFoundError, TeamNotFoundError };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdtemp, writeFile, readFile, unlink, rm } from 'fs/promises';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
|
|
7
|
+
async function openInEditor(initialContent, opts = {}) {
|
|
8
|
+
const { extension = ".yml", prefix = "orch-" } = opts;
|
|
9
|
+
const editor = process.env["EDITOR"] || process.env["VISUAL"] || "vi";
|
|
10
|
+
const dir = await mkdtemp(join(tmpdir(), prefix));
|
|
11
|
+
const filePath = join(dir, `edit${extension}`);
|
|
12
|
+
await writeFile(filePath, initialContent, "utf8");
|
|
13
|
+
try {
|
|
14
|
+
const parts = editor.split(/\s+/);
|
|
15
|
+
const child = spawn(parts[0], [...parts.slice(1), filePath], { stdio: "inherit" });
|
|
16
|
+
await new Promise((resolve, reject) => {
|
|
17
|
+
child.on("close", (code) => {
|
|
18
|
+
if (code === 0) resolve();
|
|
19
|
+
else reject(new Error(`Editor exited with code ${code}`));
|
|
20
|
+
});
|
|
21
|
+
child.on("error", reject);
|
|
22
|
+
});
|
|
23
|
+
return await readFile(filePath, "utf8");
|
|
24
|
+
} finally {
|
|
25
|
+
await unlink(filePath).catch(() => {
|
|
26
|
+
});
|
|
27
|
+
await rm(dir, { recursive: true }).catch(() => {
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function toEditorContent(fields) {
|
|
32
|
+
const lines = [
|
|
33
|
+
"---",
|
|
34
|
+
`title: ${fields.title}`,
|
|
35
|
+
`priority: ${fields.priority}`,
|
|
36
|
+
"---",
|
|
37
|
+
"",
|
|
38
|
+
fields.description ?? ""
|
|
39
|
+
];
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
function fromEditorContent(content) {
|
|
43
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
44
|
+
if (!match) {
|
|
45
|
+
return { description: content.trim() || void 0 };
|
|
46
|
+
}
|
|
47
|
+
const frontmatter = match[1] ?? "";
|
|
48
|
+
const body = match[2] ?? "";
|
|
49
|
+
const result = {};
|
|
50
|
+
for (const line of frontmatter.split("\n")) {
|
|
51
|
+
const kv = line.match(/^([\w]+):\s*(.*)$/);
|
|
52
|
+
if (!kv) continue;
|
|
53
|
+
const key = kv[1];
|
|
54
|
+
const value = kv[2] ?? "";
|
|
55
|
+
if (key === "title" && value.trim()) {
|
|
56
|
+
result.title = value.trim();
|
|
57
|
+
} else if (key === "priority") {
|
|
58
|
+
const n = parseInt(value.trim(), 10);
|
|
59
|
+
if (n >= 1 && n <= 4) result.priority = n;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const desc = body.trim();
|
|
63
|
+
if (desc) result.description = desc;
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function agentToEditorContent(fields) {
|
|
67
|
+
const lines = [
|
|
68
|
+
"# Edit agent configuration.",
|
|
69
|
+
"# Lines starting with # are ignored.",
|
|
70
|
+
"# Role description goes below the second --- separator.",
|
|
71
|
+
"---",
|
|
72
|
+
`name: ${fields.name}`,
|
|
73
|
+
`model: ${fields.model ?? ""}`,
|
|
74
|
+
"---",
|
|
75
|
+
"",
|
|
76
|
+
fields.role ?? ""
|
|
77
|
+
];
|
|
78
|
+
return lines.join("\n");
|
|
79
|
+
}
|
|
80
|
+
function agentFromEditorContent(content) {
|
|
81
|
+
const lines = content.split("\n");
|
|
82
|
+
const firstFence = lines.findIndex((l) => l.trimEnd() === "---");
|
|
83
|
+
const stripped = (firstFence >= 0 ? [...lines.slice(0, firstFence).filter((l) => !l.startsWith("#")), ...lines.slice(firstFence)] : lines.filter((l) => !l.startsWith("#"))).join("\n");
|
|
84
|
+
const match = stripped.trimStart().match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
85
|
+
if (!match) {
|
|
86
|
+
const role = stripped.trim();
|
|
87
|
+
return { role: role || void 0 };
|
|
88
|
+
}
|
|
89
|
+
const frontmatter = match[1] ?? "";
|
|
90
|
+
const body = match[2] ?? "";
|
|
91
|
+
const result = {};
|
|
92
|
+
for (const line of frontmatter.split("\n")) {
|
|
93
|
+
const kv = line.match(/^([\w]+):\s*(.*)$/);
|
|
94
|
+
if (!kv) continue;
|
|
95
|
+
const key = kv[1];
|
|
96
|
+
const value = kv[2] ?? "";
|
|
97
|
+
if (key === "name") {
|
|
98
|
+
result.name = value.trim();
|
|
99
|
+
} else if (key === "model") {
|
|
100
|
+
result.model = value.trim();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
result.role = body.trim();
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { agentFromEditorContent, agentToEditorContent, fromEditorContent, openInEditor, toEditorContent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/domain/run.ts"],"names":["createTokenUsage","input","output"],"mappings":"AAqCO,SAASA,CAAAA,CAAiBC,CAAAA,CAAeC,CAAAA,CAA4B,CAC1E,OAAO,CAAE,KAAA,CAAAD,CAAAA,CAAO,MAAA,CAAAC,CAAAA,CAAQ,KAAA,CAAOD,CAAAA,CAAQC,CAAO,CAChD","file":"chunk-PBFE5V3G.js","sourcesContent":["/**\n * Run domain model.\n *\n * A Run represents a single execution attempt of a Task by an Agent.\n * Events are stored in separate .jsonl files (append-only), not in memory.\n */\n\nexport type RunStatus =\n | 'preparing'\n | 'running'\n | 'succeeded'\n | 'failed'\n | 'timed_out'\n | 'cancelled';\n\nexport interface Run {\n id: string;\n task_id: string;\n agent_id: string;\n attempt: number;\n status: RunStatus;\n started_at: string;\n finished_at?: string;\n workspace_path: string;\n prompt: string;\n pid?: number;\n error?: string;\n tokens?: TokenUsage;\n}\n\nexport interface TokenUsage {\n input: number;\n output: number;\n total: number;\n}\n\n/** Create TokenUsage with total always computed as input + output. */\nexport function createTokenUsage(input: number, output: number): TokenUsage {\n return { input, output, total: input + output };\n}\n\nexport interface RunEvent {\n timestamp: string;\n type: RunEventType;\n data: unknown;\n}\n\nexport type RunEventType =\n | 'agent_output'\n | 'file_changed'\n | 'command_run'\n | 'tool_call'\n | 'error'\n | 'done';\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {spawn}from'child_process';var c=class{isAlive(e){try{return process.kill(e,0),!0}catch(n){return n.code==="EPERM"}}kill(e,n="SIGTERM"){try{process.kill(-e,n);}catch{try{process.kill(e,n);}catch{}}}async killWithGrace(e,n=1e4){if(!this.isAlive(e))return;this.kill(e,"SIGTERM");let r=Date.now()+n;for(;Date.now()<r;){if(!this.isAlive(e))return;await new Promise(t=>setTimeout(t,200));}this.kill(e,"SIGKILL");}spawn(e,n,r){let t=spawn(e,n,{stdio:["ignore","pipe","pipe"],detached:true,...r});if(!t.pid)throw new Error(`Failed to spawn process: ${e}`);return t.unref(),{process:t,pid:t.pid}}},u=16384;function p(i){return i.length>u?i.slice(0,u):i}async function*h(i){let e=[],n=0;for await(let r of i){let t=Buffer.isBuffer(r)?r:Buffer.from(r,"utf-8");if(t.length===0)continue;e.push(t),n+=t.length;let o=e.length===1?e[0]:Buffer.concat(e,n);e.length=0,n=0;let s=0,l;for(;(l=o.indexOf(10,s))!==-1;)l>s&&(yield p(o.toString("utf-8",s,l))),s=l+1;if(s<o.length){let a=o.subarray(s);e.push(a),n=a.length;}}if(n>0){let r=e.length===1?e[0]:Buffer.concat(e,n);yield p(r.toString("utf-8"));}}export{c as a,h as b};//# sourceMappingURL=chunk-POUC4CPC.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-POUC4CPC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infrastructure/process/process-manager.ts"],"names":["ProcessManager","pid","err","signal","graceMs","deadline","r","command","args","options","proc","spawn","MAX_LINE_LEN","capLine","s","readLines","stream","chunks","totalLen","chunk","buf","buffer","offset","newlineIdx","remainder","final"],"mappings":"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","file":"chunk-POUC4CPC.js","sourcesContent":["/**\n * Process management utilities.\n *\n * Handles spawning subprocesses, PID checks, graceful kill.\n */\n\nimport { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\nimport type { Readable } from 'node:stream';\n\nexport interface SpawnResult {\n process: ChildProcess;\n pid: number;\n}\n\nexport interface IProcessManager {\n isAlive(pid: number): boolean;\n kill(pid: number, signal?: NodeJS.Signals): void;\n killWithGrace(pid: number, graceMs?: number): Promise<void>;\n spawn(command: string, args: string[], options?: SpawnOptions): SpawnResult;\n}\n\nexport class ProcessManager implements IProcessManager {\n isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM means process exists but we lack permission to signal it\n if ((err as NodeJS.ErrnoException).code === 'EPERM') return true;\n return false;\n }\n }\n\n kill(pid: number, signal: NodeJS.Signals = 'SIGTERM'): void {\n // Kill entire process group (-pid) to clean up child processes (vitest, playwright, etc.)\n try {\n process.kill(-pid, signal);\n } catch {\n // Group kill failed — fall back to direct PID kill\n try {\n process.kill(pid, signal);\n } catch {\n // Process already dead\n }\n }\n }\n\n async killWithGrace(pid: number, graceMs: number = 10_000): Promise<void> {\n if (!this.isAlive(pid)) return;\n\n this.kill(pid, 'SIGTERM');\n\n const deadline = Date.now() + graceMs;\n\n while (Date.now() < deadline) {\n if (!this.isAlive(pid)) return;\n await new Promise((r) => setTimeout(r, 200));\n }\n\n // Force kill if still alive\n this.kill(pid, 'SIGKILL');\n }\n\n spawn(command: string, args: string[], options?: SpawnOptions): SpawnResult {\n const proc = spawn(command, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: true, // Create new process group so killWithGrace(-pid) kills all children\n ...options,\n });\n\n if (!proc.pid) {\n throw new Error(`Failed to spawn process: ${command}`);\n }\n\n // Allow parent to exit without waiting for this child.\n // Pipes (stdout/stderr) still hold refs while being read — that's intentional.\n proc.unref();\n\n return { process: proc, pid: proc.pid };\n }\n}\n\n/**\n * Max stdout line length before truncation (16 KB).\n * First layer of a three-layer cap: readLines (16 KB) → serializeEventData (8 KB) → bus emit (4 KB).\n * Truncated lines produce invalid JSON → adapters' catch block handles gracefully.\n */\nconst MAX_LINE_LEN = 16384;\n\n/** Cap a string to MAX_LINE_LEN. */\nfunction capLine(s: string): string {\n return s.length > MAX_LINE_LEN ? s.slice(0, MAX_LINE_LEN) : s;\n}\n\n/**\n * Read lines from a readable stream as an async generator.\n *\n * Uses `for await` on the raw Readable (proper backpressure) instead of\n * readline.createInterface, which buffers all 'line' events in an unbounded\n * queue even when the consumer is paused — causing OOM under high throughput.\n *\n * Uses Buffer.concat + offset tracking to avoid O(n²) string copies.\n */\nexport async function* readLines(stream: Readable): AsyncGenerator<string> {\n const chunks: Buffer[] = [];\n let totalLen = 0;\n\n for await (const chunk of stream) {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string, 'utf-8');\n if (buf.length === 0) continue;\n chunks.push(buf);\n totalLen += buf.length;\n\n // Concat once per chunk arrival, then scan for newlines with offset tracking\n const buffer = chunks.length === 1 ? chunks[0]! : Buffer.concat(chunks, totalLen);\n chunks.length = 0;\n totalLen = 0;\n\n let offset = 0;\n let newlineIdx: number;\n while ((newlineIdx = buffer.indexOf(0x0a, offset)) !== -1) {\n if (newlineIdx > offset) {\n yield capLine(buffer.toString('utf-8', offset, newlineIdx));\n }\n offset = newlineIdx + 1;\n }\n\n // Keep unconsumed remainder for next chunk\n if (offset < buffer.length) {\n const remainder = buffer.subarray(offset);\n chunks.push(remainder);\n totalLen = remainder.length;\n }\n }\n\n // Flush remaining data (last line without trailing newline)\n if (totalLen > 0) {\n const final = chunks.length === 1 ? chunks[0]! : Buffer.concat(chunks, totalLen);\n yield capLine(final.toString('utf-8'));\n }\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readLines } from './chunk-CHIP7O6V.js';
|
|
3
|
+
|
|
4
|
+
// src/domain/run.ts
|
|
5
|
+
function createTokenUsage(input, output) {
|
|
6
|
+
return { input, output, total: input + output };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// src/infrastructure/adapters/utils.ts
|
|
10
|
+
function extractTokens(parsed, opts) {
|
|
11
|
+
let usage = parsed.usage;
|
|
12
|
+
if (!usage && opts?.statsFallback) {
|
|
13
|
+
const stats = parsed.stats;
|
|
14
|
+
usage = stats?.usage;
|
|
15
|
+
}
|
|
16
|
+
if (usage && typeof usage.input_tokens === "number") {
|
|
17
|
+
const input = usage.input_tokens;
|
|
18
|
+
const output = typeof usage.output_tokens === "number" ? usage.output_tokens : 0;
|
|
19
|
+
return createTokenUsage(input, output);
|
|
20
|
+
}
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
function createStreamingEvents(proc, parseEvent, adapterName, signal) {
|
|
24
|
+
async function* generate() {
|
|
25
|
+
let gotDoneEvent = false;
|
|
26
|
+
let exitCode = null;
|
|
27
|
+
let exitError = null;
|
|
28
|
+
const exitPromise = new Promise((resolve) => {
|
|
29
|
+
proc.on("close", (code) => {
|
|
30
|
+
exitCode = code;
|
|
31
|
+
resolve();
|
|
32
|
+
});
|
|
33
|
+
proc.on("error", (err) => {
|
|
34
|
+
exitError = err;
|
|
35
|
+
resolve();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
if (proc.stdout) {
|
|
39
|
+
for await (const line of readLines(proc.stdout)) {
|
|
40
|
+
if (signal?.aborted) break;
|
|
41
|
+
const event = parseEvent(line);
|
|
42
|
+
if (event) {
|
|
43
|
+
if (event.type === "done") gotDoneEvent = true;
|
|
44
|
+
yield event;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
await exitPromise;
|
|
49
|
+
if (exitError && !signal?.aborted && !gotDoneEvent) {
|
|
50
|
+
throw exitError;
|
|
51
|
+
}
|
|
52
|
+
if (exitCode !== 0 && exitCode !== null && !signal?.aborted && !gotDoneEvent) {
|
|
53
|
+
throw new Error(`${adapterName} process exited with code ${exitCode}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return generate();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { createStreamingEvents, extractTokens };
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { NotInitializedError } from './chunk-O5AO5QIR.js';
|
|
3
|
+
import { randomBytes } from 'crypto';
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path2 from 'path';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import { accessSync } from 'fs';
|
|
8
|
+
|
|
9
|
+
async function atomicWrite(filePath, content) {
|
|
10
|
+
const dir = path2.dirname(filePath);
|
|
11
|
+
await ensureDir(dir);
|
|
12
|
+
const tmpPath = path2.join(dir, `.${path2.basename(filePath)}.${randomBytes(4).toString("hex")}.tmp`);
|
|
13
|
+
try {
|
|
14
|
+
await fs.writeFile(tmpPath, content, "utf-8");
|
|
15
|
+
await fs.rename(tmpPath, filePath);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
await fs.unlink(tmpPath).catch(() => {
|
|
18
|
+
});
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function readYaml(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
25
|
+
return yaml.load(content);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
if (isENOENT(err)) return null;
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function writeYaml(filePath, data) {
|
|
32
|
+
const content = yaml.dump(data, {
|
|
33
|
+
indent: 2,
|
|
34
|
+
lineWidth: 120,
|
|
35
|
+
noRefs: true,
|
|
36
|
+
sortKeys: false
|
|
37
|
+
});
|
|
38
|
+
await atomicWrite(filePath, content);
|
|
39
|
+
}
|
|
40
|
+
async function readJson(filePath) {
|
|
41
|
+
try {
|
|
42
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
43
|
+
return JSON.parse(content);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
if (isENOENT(err)) return null;
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function writeJson(filePath, data) {
|
|
50
|
+
const content = JSON.stringify(data, null, 2) + "\n";
|
|
51
|
+
await atomicWrite(filePath, content);
|
|
52
|
+
}
|
|
53
|
+
var PIPE_BUF = 4096;
|
|
54
|
+
async function appendJsonl(filePath, record) {
|
|
55
|
+
const dir = path2.dirname(filePath);
|
|
56
|
+
await ensureDir(dir);
|
|
57
|
+
let line = JSON.stringify(record) + "\n";
|
|
58
|
+
const byteLen = Buffer.byteLength(line, "utf-8");
|
|
59
|
+
if (byteLen > PIPE_BUF && record !== null && typeof record === "object") {
|
|
60
|
+
const obj = record;
|
|
61
|
+
if (typeof obj.data === "string" && obj.data.length > 0) {
|
|
62
|
+
const shell = JSON.stringify({ ...obj, data: "" }) + "\n";
|
|
63
|
+
const overhead = Buffer.byteLength(shell, "utf-8");
|
|
64
|
+
const budget = PIPE_BUF - overhead - 3;
|
|
65
|
+
if (budget > 0) {
|
|
66
|
+
const truncated = obj.data.slice(0, budget);
|
|
67
|
+
line = JSON.stringify({ ...obj, data: truncated + "\u2026" }) + "\n";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const fd = await fs.open(filePath, "a");
|
|
72
|
+
try {
|
|
73
|
+
await fd.write(line, null, "utf-8");
|
|
74
|
+
} finally {
|
|
75
|
+
await fd.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
var MAX_JSONL_READ_SIZE = 50 * 1024 * 1024;
|
|
79
|
+
async function readJsonl(filePath) {
|
|
80
|
+
try {
|
|
81
|
+
const stat = await fs.stat(filePath);
|
|
82
|
+
if (stat.size > MAX_JSONL_READ_SIZE) {
|
|
83
|
+
process.stderr.write(
|
|
84
|
+
`[readJsonl] file too large (${(stat.size / 1024 / 1024).toFixed(1)} MB), reading tail only: ${filePath}
|
|
85
|
+
`
|
|
86
|
+
);
|
|
87
|
+
return readJsonlTail(filePath, 200);
|
|
88
|
+
}
|
|
89
|
+
return readAndParseJsonl(filePath);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (isENOENT(err)) return [];
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function readJsonlTail(filePath, count) {
|
|
96
|
+
try {
|
|
97
|
+
const stat = await fs.stat(filePath);
|
|
98
|
+
if (stat.size < 32768) {
|
|
99
|
+
return (await readAndParseJsonl(filePath)).slice(-count);
|
|
100
|
+
}
|
|
101
|
+
const fd = await fs.open(filePath, "r");
|
|
102
|
+
try {
|
|
103
|
+
const chunkSize = Math.min(stat.size, stat.size > 1048576 ? 131072 : 65536);
|
|
104
|
+
let position = Math.max(0, stat.size - chunkSize);
|
|
105
|
+
let earliestReadPosition = position;
|
|
106
|
+
let tail = "";
|
|
107
|
+
for (let attempt = 0; attempt < 4 && position >= 0; attempt++) {
|
|
108
|
+
earliestReadPosition = position;
|
|
109
|
+
const readSize = Math.min(chunkSize, stat.size - position);
|
|
110
|
+
const buf = Buffer.alloc(readSize);
|
|
111
|
+
await fd.read(buf, 0, readSize, position);
|
|
112
|
+
tail = buf.toString("utf-8") + tail;
|
|
113
|
+
const lines2 = tail.split("\n").filter((l) => l.trim().length > 0);
|
|
114
|
+
if (lines2.length >= count + 1) {
|
|
115
|
+
return parseJsonlLines(lines2.slice(-count));
|
|
116
|
+
}
|
|
117
|
+
if (position === 0) break;
|
|
118
|
+
position = Math.max(0, position - chunkSize);
|
|
119
|
+
}
|
|
120
|
+
const lines = tail.split("\n").filter((l) => l.trim().length > 0);
|
|
121
|
+
const safeLines = earliestReadPosition > 0 ? lines.slice(1) : lines;
|
|
122
|
+
return parseJsonlLines(safeLines.slice(-count));
|
|
123
|
+
} finally {
|
|
124
|
+
await fd.close();
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (isENOENT(err)) return [];
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function readAndParseJsonl(filePath) {
|
|
132
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
133
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
134
|
+
return parseJsonlLines(lines);
|
|
135
|
+
}
|
|
136
|
+
function parseJsonlLines(lines) {
|
|
137
|
+
const results = [];
|
|
138
|
+
for (const raw of lines) {
|
|
139
|
+
const line = raw.trim();
|
|
140
|
+
if (!line) continue;
|
|
141
|
+
try {
|
|
142
|
+
results.push(JSON.parse(line));
|
|
143
|
+
} catch {
|
|
144
|
+
process.stderr.write(`[readJsonl] skipping corrupt line: ${line.slice(0, 200)}
|
|
145
|
+
`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
150
|
+
async function ensureDir(dirPath) {
|
|
151
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
152
|
+
}
|
|
153
|
+
async function pathExists(filePath) {
|
|
154
|
+
try {
|
|
155
|
+
await fs.access(filePath);
|
|
156
|
+
return true;
|
|
157
|
+
} catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function listFiles(dirPath, ext) {
|
|
162
|
+
try {
|
|
163
|
+
const entries = await fs.readdir(dirPath);
|
|
164
|
+
if (ext) {
|
|
165
|
+
return entries.filter((e) => e.endsWith(ext));
|
|
166
|
+
}
|
|
167
|
+
return entries;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (isENOENT(err)) return [];
|
|
170
|
+
throw err;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function isENOENT(err) {
|
|
174
|
+
return err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
175
|
+
}
|
|
176
|
+
var ORCHESTRY_DIR = ".orchestry";
|
|
177
|
+
var ID_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
178
|
+
var Paths = class {
|
|
179
|
+
constructor(projectRoot) {
|
|
180
|
+
this.projectRoot = projectRoot;
|
|
181
|
+
}
|
|
182
|
+
/** Root .orchestry/ directory */
|
|
183
|
+
get root() {
|
|
184
|
+
return path2.join(this.projectRoot, ORCHESTRY_DIR);
|
|
185
|
+
}
|
|
186
|
+
get configPath() {
|
|
187
|
+
return path2.join(this.root, "config.yml");
|
|
188
|
+
}
|
|
189
|
+
get statePath() {
|
|
190
|
+
return path2.join(this.root, "state.json");
|
|
191
|
+
}
|
|
192
|
+
get lockPath() {
|
|
193
|
+
return path2.join(this.root, "orchestry.lock");
|
|
194
|
+
}
|
|
195
|
+
get tasksDir() {
|
|
196
|
+
return path2.join(this.root, "tasks");
|
|
197
|
+
}
|
|
198
|
+
get agentsDir() {
|
|
199
|
+
return path2.join(this.root, "agents");
|
|
200
|
+
}
|
|
201
|
+
get runsDir() {
|
|
202
|
+
return path2.join(this.root, "runs");
|
|
203
|
+
}
|
|
204
|
+
get templatesDir() {
|
|
205
|
+
return path2.join(this.root, "templates");
|
|
206
|
+
}
|
|
207
|
+
get logsDir() {
|
|
208
|
+
return path2.join(this.root, "logs");
|
|
209
|
+
}
|
|
210
|
+
get contextDir() {
|
|
211
|
+
return path2.join(this.root, "context");
|
|
212
|
+
}
|
|
213
|
+
contextPath(key) {
|
|
214
|
+
return path2.join(this.contextDir, `${sanitizeId(key)}.json`);
|
|
215
|
+
}
|
|
216
|
+
get messagesDir() {
|
|
217
|
+
return path2.join(this.root, "messages");
|
|
218
|
+
}
|
|
219
|
+
messagePath(id) {
|
|
220
|
+
return path2.join(this.messagesDir, `${sanitizeId(id)}.json`);
|
|
221
|
+
}
|
|
222
|
+
get goalsDir() {
|
|
223
|
+
return path2.join(this.root, "goals");
|
|
224
|
+
}
|
|
225
|
+
goalPath(id) {
|
|
226
|
+
return path2.join(this.goalsDir, `${sanitizeId(id)}.yml`);
|
|
227
|
+
}
|
|
228
|
+
get teamsDir() {
|
|
229
|
+
return path2.join(this.root, "teams");
|
|
230
|
+
}
|
|
231
|
+
teamPath(id) {
|
|
232
|
+
return path2.join(this.teamsDir, `${sanitizeId(id)}.yml`);
|
|
233
|
+
}
|
|
234
|
+
get gitignorePath() {
|
|
235
|
+
return path2.join(this.root, ".gitignore");
|
|
236
|
+
}
|
|
237
|
+
get workspaceExcludePath() {
|
|
238
|
+
return path2.join(this.root, "workspace-exclude");
|
|
239
|
+
}
|
|
240
|
+
taskPath(id) {
|
|
241
|
+
return path2.join(this.tasksDir, `${sanitizeId(id)}.yml`);
|
|
242
|
+
}
|
|
243
|
+
agentPath(id) {
|
|
244
|
+
return path2.join(this.agentsDir, `${sanitizeId(id)}.yml`);
|
|
245
|
+
}
|
|
246
|
+
runPath(id) {
|
|
247
|
+
return path2.join(this.runsDir, `${sanitizeId(id)}.json`);
|
|
248
|
+
}
|
|
249
|
+
runEventsPath(id) {
|
|
250
|
+
return path2.join(this.runsDir, `${sanitizeId(id)}.jsonl`);
|
|
251
|
+
}
|
|
252
|
+
defaultTemplatePath() {
|
|
253
|
+
return path2.join(this.templatesDir, "default.md");
|
|
254
|
+
}
|
|
255
|
+
async isInitialized() {
|
|
256
|
+
return pathExists(this.root);
|
|
257
|
+
}
|
|
258
|
+
async requireInit() {
|
|
259
|
+
if (!await this.isInitialized()) {
|
|
260
|
+
throw new NotInitializedError();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
function sanitizeId(id) {
|
|
265
|
+
if (!ID_PATTERN.test(id)) {
|
|
266
|
+
throw new Error(`Invalid identifier: "${id}"`);
|
|
267
|
+
}
|
|
268
|
+
return id;
|
|
269
|
+
}
|
|
270
|
+
function validateWorkspacePath(workspacePath, projectRoot) {
|
|
271
|
+
const resolved = path2.resolve(workspacePath);
|
|
272
|
+
const root = path2.resolve(projectRoot);
|
|
273
|
+
if (!resolved.startsWith(root + path2.sep) && resolved !== root) {
|
|
274
|
+
throw new Error(`Workspace path "${workspacePath}" is outside project root`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
278
|
+
let dir = path2.resolve(startDir);
|
|
279
|
+
const root = path2.parse(dir).root;
|
|
280
|
+
while (dir !== root) {
|
|
281
|
+
try {
|
|
282
|
+
accessSync(path2.join(dir, ".orchestry"));
|
|
283
|
+
return dir;
|
|
284
|
+
} catch {
|
|
285
|
+
}
|
|
286
|
+
dir = path2.dirname(dir);
|
|
287
|
+
}
|
|
288
|
+
return startDir;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export { Paths, appendJsonl, atomicWrite, ensureDir, findProjectRoot, listFiles, pathExists, readJson, readJsonl, readJsonlTail, readYaml, sanitizeId, validateWorkspacePath, writeJson, writeYaml };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from 'child_process';
|
|
3
|
+
import { promisify } from 'util';
|
|
4
|
+
|
|
5
|
+
var execFileAsync = promisify(execFile);
|
|
6
|
+
var DoctorService = class {
|
|
7
|
+
constructor(adapterRegistry, processManager) {
|
|
8
|
+
this.adapterRegistry = adapterRegistry;
|
|
9
|
+
this.processManager = processManager;
|
|
10
|
+
}
|
|
11
|
+
async runAll() {
|
|
12
|
+
const checks = [];
|
|
13
|
+
const adapters = this.adapterRegistry.list();
|
|
14
|
+
let adaptersReady = 0;
|
|
15
|
+
for (const adapter of adapters) {
|
|
16
|
+
const result = await adapter.test();
|
|
17
|
+
if (result.ok) {
|
|
18
|
+
adaptersReady++;
|
|
19
|
+
checks.push({
|
|
20
|
+
name: adapter.kind,
|
|
21
|
+
status: "ok",
|
|
22
|
+
detail: result.version
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
checks.push({
|
|
26
|
+
name: adapter.kind,
|
|
27
|
+
status: "fail",
|
|
28
|
+
detail: result.error
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
checks.push(await this.checkCommand("git", ["--version"], "git"));
|
|
33
|
+
checks.push(await this.checkCommand("node", ["--version"], "node"));
|
|
34
|
+
return {
|
|
35
|
+
checks,
|
|
36
|
+
adaptersReady,
|
|
37
|
+
adaptersTotal: adapters.length
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async checkCommand(command, args, name) {
|
|
41
|
+
try {
|
|
42
|
+
const { stdout } = await execFileAsync(command, args);
|
|
43
|
+
return { name, status: "ok", detail: stdout.trim() };
|
|
44
|
+
} catch {
|
|
45
|
+
return { name, status: "fail", detail: `${command}: command not found` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { DoctorService };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class extends Error{constructor(r,e,h){super(r);this.exitCode=e;this.hint=h;this.name="OrchestryError";}},o=class extends t{constructor(){super("Not initialized",3,"Run: orch init"),this.name="NotInitializedError";}},a=class extends t{constructor(s){super(s,2),this.name="InvalidArgumentsError";}},c=class extends t{constructor(s){super(`Orchestrator already running (PID: ${s})`,4,"Use: orch status"),this.name="LockConflictError";}};var i=class extends t{constructor(){super("No agents configured",1,"Run: orch agent add <name> --adapter claude"),this.name="NoAgentsError";}},u=class extends t{constructor(s){super(`Task not found: ${s}`,1),this.name="TaskNotFoundError";}},d=class extends t{constructor(s){super(`Agent not found: ${s}`,1),this.name="AgentNotFoundError";}},p=class extends t{constructor(s,r,e){super(`Task ${s} is already running (run: ${r}, agent: ${e})`,1,`Use: orch logs --task ${s} --follow`),this.name="TaskAlreadyRunningError";}},l=class extends t{constructor(s,r,e){super(`Invalid transition for ${s}: ${r} \u2192 ${e}`,1),this.name="InvalidTransitionError";}},x=class extends t{constructor(s){super(`Goal not found: ${s}`,1),this.name="GoalNotFoundError";}},g=class extends t{constructor(s){super(`Team not found: ${s}`,1),this.name="TeamNotFoundError";}};export{t as a,o as b,a as c,c as d,i as e,u as f,d as g,p as h,l as i,x as j,g as k};//# sourceMappingURL=chunk-ZU6AY2VU.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-ZU6AY2VU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/domain/errors.ts"],"names":["OrchestryError","message","exitCode","hint","NotInitializedError","InvalidArgumentsError","LockConflictError","pid","NoAgentsError","TaskNotFoundError","taskId","AgentNotFoundError","agentId","TaskAlreadyRunningError","runId","agentName","InvalidTransitionError","from","to","GoalNotFoundError","goalId","TeamNotFoundError","teamId"],"mappings":"AAeO,IAAMA,CAAAA,CAAN,cAA6B,KAAM,CACxC,YACEC,CAAAA,CACgBC,CAAAA,CACAC,CAAAA,CAChB,CACA,KAAA,CAAMF,CAAO,EAHG,IAAA,CAAA,QAAA,CAAAC,CAAAA,CACA,UAAAC,CAAAA,CAGhB,IAAA,CAAK,KAAO,iBACd,CACF,CAAA,CAEaC,CAAAA,CAAN,cAAkCJ,CAAe,CACtD,WAAA,EAAc,CACZ,MAAM,iBAAA,CAAmB,CAAA,CAAG,gBAAgB,CAAA,CAC5C,IAAA,CAAK,IAAA,CAAO,sBACd,CACF,CAAA,CAEaK,EAAN,cAAoCL,CAAe,CACxD,WAAA,CAAYC,CAAAA,CAAiB,CAC3B,MAAMA,CAAAA,CAAS,CAAC,CAAA,CAChB,IAAA,CAAK,IAAA,CAAO,wBACd,CACF,CAAA,CAEaK,CAAAA,CAAN,cAAgCN,CAAe,CACpD,YAAYO,CAAAA,CAAa,CACvB,KAAA,CAAM,CAAA,mCAAA,EAAsCA,CAAG,CAAA,CAAA,CAAA,CAAK,EAAG,kBAAkB,CAAA,CACzE,IAAA,CAAK,IAAA,CAAO,oBACd,CACF,EASO,IAAMC,CAAAA,CAAN,cAA4BR,CAAe,CAChD,WAAA,EAAc,CACZ,KAAA,CAAM,sBAAA,CAAwB,EAAG,6CAA6C,CAAA,CAC9E,KAAK,IAAA,CAAO,gBACd,CACF,CAAA,CAEaS,CAAAA,CAAN,cAAgCT,CAAe,CACpD,WAAA,CAAYU,CAAAA,CAAgB,CAC1B,KAAA,CAAM,CAAA,gBAAA,EAAmBA,CAAM,CAAA,CAAA,CAAI,CAAC,CAAA,CACpC,IAAA,CAAK,IAAA,CAAO,oBACd,CACF,CAAA,CAEaC,CAAAA,CAAN,cAAiCX,CAAe,CACrD,YAAYY,CAAAA,CAAiB,CAC3B,KAAA,CAAM,CAAA,iBAAA,EAAoBA,CAAO,CAAA,CAAA,CAAI,CAAC,CAAA,CACtC,IAAA,CAAK,KAAO,qBACd,CACF,EAEaC,CAAAA,CAAN,cAAsCb,CAAe,CAC1D,WAAA,CAAYU,CAAAA,CAAgBI,EAAeC,CAAAA,CAAmB,CAC5D,MACE,CAAA,KAAA,EAAQL,CAAM,6BAA6BI,CAAK,CAAA,SAAA,EAAYC,CAAS,CAAA,CAAA,CAAA,CACrE,CAAA,CACA,CAAA,sBAAA,EAAyBL,CAAM,CAAA,SAAA,CACjC,CAAA,CACA,IAAA,CAAK,IAAA,CAAO,0BACd,CACF,EAEaM,CAAAA,CAAN,cAAqChB,CAAe,CACzD,WAAA,CAAYU,CAAAA,CAAgBO,EAAcC,CAAAA,CAAY,CACpD,MAAM,CAAA,uBAAA,EAA0BR,CAAM,KAAKO,CAAI,CAAA,QAAA,EAAMC,CAAE,CAAA,CAAA,CAAI,CAAC,CAAA,CAC5D,KAAK,IAAA,CAAO,yBACd,CACF,CAAA,CAEaC,CAAAA,CAAN,cAAgCnB,CAAe,CACpD,WAAA,CAAYoB,CAAAA,CAAgB,CAC1B,KAAA,CAAM,CAAA,gBAAA,EAAmBA,CAAM,CAAA,CAAA,CAAI,CAAC,EACpC,IAAA,CAAK,IAAA,CAAO,oBACd,CACF,CAAA,CAEaC,CAAAA,CAAN,cAAgCrB,CAAe,CACpD,YAAYsB,CAAAA,CAAgB,CAC1B,KAAA,CAAM,CAAA,gBAAA,EAAmBA,CAAM,CAAA,CAAA,CAAI,CAAC,CAAA,CACpC,IAAA,CAAK,IAAA,CAAO,oBACd,CACF","file":"chunk-ZU6AY2VU.js","sourcesContent":["/**\n * Typed error hierarchy for the orchestrator.\n *\n * Every error carries an exit code (matching CLI_UI_DESIGN.md §11)\n * and an optional hint for the user.\n *\n * Exit codes:\n * 0 - Success\n * 1 - General error\n * 2 - Invalid arguments\n * 3 - Not initialized (.orchestry/ not found)\n * 4 - Lock conflict (orchestrator already running)\n * 5 - Agent error (adapter test failed)\n */\n\nexport class OrchestryError extends Error {\n constructor(\n message: string,\n public readonly exitCode: number,\n public readonly hint?: string,\n ) {\n super(message);\n this.name = 'OrchestryError';\n }\n}\n\nexport class NotInitializedError extends OrchestryError {\n constructor() {\n super('Not initialized', 3, 'Run: orch init');\n this.name = 'NotInitializedError';\n }\n}\n\nexport class InvalidArgumentsError extends OrchestryError {\n constructor(message: string) {\n super(message, 2);\n this.name = 'InvalidArgumentsError';\n }\n}\n\nexport class LockConflictError extends OrchestryError {\n constructor(pid: number) {\n super(`Orchestrator already running (PID: ${pid})`, 4, 'Use: orch status');\n this.name = 'LockConflictError';\n }\n}\n\nexport class AgentAdapterError extends OrchestryError {\n constructor(adapter: string, detail: string) {\n super(`Agent adapter \"${adapter}\" not available`, 5, detail);\n this.name = 'AgentAdapterError';\n }\n}\n\nexport class NoAgentsError extends OrchestryError {\n constructor() {\n super('No agents configured', 1, 'Run: orch agent add <name> --adapter claude');\n this.name = 'NoAgentsError';\n }\n}\n\nexport class TaskNotFoundError extends OrchestryError {\n constructor(taskId: string) {\n super(`Task not found: ${taskId}`, 1);\n this.name = 'TaskNotFoundError';\n }\n}\n\nexport class AgentNotFoundError extends OrchestryError {\n constructor(agentId: string) {\n super(`Agent not found: ${agentId}`, 1);\n this.name = 'AgentNotFoundError';\n }\n}\n\nexport class TaskAlreadyRunningError extends OrchestryError {\n constructor(taskId: string, runId: string, agentName: string) {\n super(\n `Task ${taskId} is already running (run: ${runId}, agent: ${agentName})`,\n 1,\n `Use: orch logs --task ${taskId} --follow`,\n );\n this.name = 'TaskAlreadyRunningError';\n }\n}\n\nexport class InvalidTransitionError extends OrchestryError {\n constructor(taskId: string, from: string, to: string) {\n super(`Invalid transition for ${taskId}: ${from} → ${to}`, 1);\n this.name = 'InvalidTransitionError';\n }\n}\n\nexport class GoalNotFoundError extends OrchestryError {\n constructor(goalId: string) {\n super(`Goal not found: ${goalId}`, 1);\n this.name = 'GoalNotFoundError';\n }\n}\n\nexport class TeamNotFoundError extends OrchestryError {\n constructor(teamId: string) {\n super(`Team not found: ${teamId}`, 1);\n this.name = 'TeamNotFoundError';\n }\n}\n\nexport class MessageNotFoundError extends OrchestryError {\n constructor(messageId: string) {\n super(`Message not found: ${messageId}`, 1);\n this.name = 'MessageNotFoundError';\n }\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {b,a as a$1}from'./chunk-IZYSGYXG.js';import'./chunk-PBFE5V3G.js';import'./chunk-POUC4CPC.js';import {execFile}from'child_process';import {promisify}from'util';var l=promisify(execFile),a=class{constructor(e){this.processManager=e;}kind="claude";async test(){try{let{stdout:e}=await l("claude",["--version"]);return {ok:!0,version:e.trim()}}catch{return {ok:false,error:"Claude Code CLI not found. Install: npm i -g @anthropic-ai/claude-code"}}}execute(e){let t=["--print","--output-format","stream-json","--max-turns",String(e.config.max_turns??50),"--verbose","--dangerously-skip-permissions"];e.config.model&&t.push("--model",e.config.model),e.config.system_prompt&&t.push("--system-prompt",e.config.system_prompt),t.push(e.prompt);let{process:s,pid:c}=this.processManager.spawn("claude",t,{cwd:e.workspace,env:{...process.env,...e.env},signal:e.signal}),u=b(s,d,"Claude",e.signal);return {pid:c,events:u}}async stop(e){await this.processManager.killWithGrace(e);}};function d(r){if(!r.trim())return null;try{let e=JSON.parse(r),t=new Date().toISOString();switch(e.type){case "assistant":return {type:"output",timestamp:t,data:e.message??e};case "tool_use":return {type:"tool_call",timestamp:t,data:e};case "tool_result":return {type:"output",timestamp:t,data:e};case "error":return {type:"error",timestamp:t,data:e.error??e};case "result":{let s=a$1(e,{statsFallback:!0});return {type:"done",timestamp:t,data:e,tokens:s}}default:return {type:"output",timestamp:t,data:e}}}catch{return {type:"output",timestamp:new Date().toISOString(),data:r}}}export{a as ClaudeAdapter};//# sourceMappingURL=claude-S47YTIHU.js.map
|
|
2
|
+
//# sourceMappingURL=claude-S47YTIHU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infrastructure/adapters/claude.ts"],"names":["execFileAsync","promisify","execFile","ClaudeAdapter","processManager","stdout","params","args","proc","pid","events","createStreamingEvents","parseClaudeEvent","line","parsed","timestamp","tokens","extractTokens"],"mappings":"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","file":"claude-S47YTIHU.js","sourcesContent":["/**\n * Claude Code adapter.\n *\n * Spawns `claude --print --output-format stream-json` in headless mode.\n * Parses JSON-lines from stdout into AgentEvent stream.\n */\n\nimport type { IAgentAdapter, AdapterTestResult, ExecuteParams, AgentEvent, ExecuteHandle } from './interface.js';\nimport type { IProcessManager } from '../process/process-manager.js';\nimport { extractTokens, createStreamingEvents } from './utils.js';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport class ClaudeAdapter implements IAgentAdapter {\n readonly kind = 'claude';\n\n constructor(private readonly processManager: IProcessManager) {}\n\n async test(): Promise<AdapterTestResult> {\n try {\n const { stdout } = await execFileAsync('claude', ['--version']);\n return { ok: true, version: stdout.trim() };\n } catch {\n return {\n ok: false,\n error: 'Claude Code CLI not found. Install: npm i -g @anthropic-ai/claude-code',\n };\n }\n }\n\n execute(params: ExecuteParams): ExecuteHandle {\n const args = [\n '--print',\n '--output-format', 'stream-json',\n '--max-turns', String(params.config.max_turns ?? 50),\n '--verbose',\n '--dangerously-skip-permissions', // Agents run autonomously; stdin is 'ignore' so prompts would hang\n ];\n\n if (params.config.model) {\n args.push('--model', params.config.model);\n }\n\n if (params.config.system_prompt) {\n args.push('--system-prompt', params.config.system_prompt);\n }\n\n args.push(params.prompt);\n\n const { process: proc, pid } = this.processManager.spawn('claude', args, {\n cwd: params.workspace,\n env: { ...process.env, ...params.env },\n signal: params.signal,\n });\n\n const events = createStreamingEvents(proc, parseClaudeEvent, 'Claude', params.signal);\n\n return { pid, events };\n }\n\n async stop(pid: number): Promise<void> {\n await this.processManager.killWithGrace(pid);\n }\n}\n\nfunction parseClaudeEvent(line: string): AgentEvent | null {\n if (!line.trim()) return null;\n\n try {\n const parsed: Record<string, unknown> = JSON.parse(line);\n const timestamp = new Date().toISOString();\n\n switch (parsed.type) {\n case 'assistant':\n return { type: 'output', timestamp, data: (parsed.message as unknown) ?? parsed };\n case 'tool_use':\n return { type: 'tool_call', timestamp, data: parsed };\n case 'tool_result':\n return { type: 'output', timestamp, data: parsed };\n case 'error':\n return { type: 'error', timestamp, data: (parsed.error as unknown) ?? parsed };\n case 'result': {\n const tokens = extractTokens(parsed, { statsFallback: true });\n return { type: 'done', timestamp, data: parsed, tokens };\n }\n default:\n return { type: 'output', timestamp, data: parsed };\n }\n } catch {\n return { type: 'output', timestamp: new Date().toISOString(), data: line };\n }\n}\n"]}
|