0agent 1.0.6 → 1.0.8
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/bin/0agent.js +18 -5
- package/dist/daemon.mjs +612 -40
- package/package.json +1 -1
package/bin/0agent.js
CHANGED
|
@@ -415,6 +415,7 @@ async function streamSession(sessionId) {
|
|
|
415
415
|
|
|
416
416
|
return new Promise((resolve) => {
|
|
417
417
|
const ws = new WS(`ws://localhost:4200/ws`);
|
|
418
|
+
let streaming = false; // true when mid-token-stream
|
|
418
419
|
|
|
419
420
|
ws.on('open', () => {
|
|
420
421
|
ws.send(JSON.stringify({ type: 'subscribe', topics: ['sessions'] }));
|
|
@@ -427,18 +428,30 @@ async function streamSession(sessionId) {
|
|
|
427
428
|
|
|
428
429
|
switch (event.type) {
|
|
429
430
|
case 'session.step':
|
|
430
|
-
|
|
431
|
+
// Newline before step if we were mid-stream
|
|
432
|
+
if (streaming) { process.stdout.write('\n'); streaming = false; }
|
|
433
|
+
console.log(` \x1b[2m›\x1b[0m ${event.step}`);
|
|
434
|
+
break;
|
|
435
|
+
case 'session.token':
|
|
436
|
+
// Token-by-token streaming — print without newline
|
|
437
|
+
if (!streaming) { process.stdout.write('\n '); streaming = true; }
|
|
438
|
+
process.stdout.write(event.token);
|
|
431
439
|
break;
|
|
432
440
|
case 'session.completed': {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
441
|
+
if (streaming) { process.stdout.write('\n'); streaming = false; }
|
|
442
|
+
// Show files written + commands run
|
|
443
|
+
const r = event.result ?? {};
|
|
444
|
+
if (r.files_written?.length) console.log(`\n \x1b[32m✓\x1b[0m Files: ${r.files_written.join(', ')}`);
|
|
445
|
+
if (r.commands_run?.length) console.log(` \x1b[32m✓\x1b[0m Commands run: ${r.commands_run.length}`);
|
|
446
|
+
if (r.tokens_used) console.log(` \x1b[2m${r.tokens_used} tokens · ${r.model}\x1b[0m`);
|
|
447
|
+
console.log('\n \x1b[32m✓ Done\x1b[0m\n');
|
|
436
448
|
ws.close();
|
|
437
449
|
resolve();
|
|
438
450
|
break;
|
|
439
451
|
}
|
|
440
452
|
case 'session.failed':
|
|
441
|
-
|
|
453
|
+
if (streaming) { process.stdout.write('\n'); streaming = false; }
|
|
454
|
+
console.log(`\n \x1b[31m✗ Failed:\x1b[0m ${event.error}\n`);
|
|
442
455
|
ws.close();
|
|
443
456
|
resolve();
|
|
444
457
|
break;
|
package/dist/daemon.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// packages/daemon/src/ZeroAgentDaemon.ts
|
|
2
|
-
import { writeFileSync as
|
|
3
|
-
import { resolve as
|
|
2
|
+
import { writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "node:fs";
|
|
3
|
+
import { resolve as resolve4 } from "node:path";
|
|
4
4
|
import { homedir as homedir3 } from "node:os";
|
|
5
5
|
|
|
6
6
|
// packages/core/src/graph/GraphNode.ts
|
|
@@ -1685,16 +1685,551 @@ var EntityScopedContextLoader = class {
|
|
|
1685
1685
|
}
|
|
1686
1686
|
};
|
|
1687
1687
|
|
|
1688
|
+
// packages/daemon/src/AgentExecutor.ts
|
|
1689
|
+
import { spawn } from "node:child_process";
|
|
1690
|
+
import { writeFileSync, readFileSync as readFileSync2, readdirSync, mkdirSync, existsSync as existsSync2 } from "node:fs";
|
|
1691
|
+
import { resolve as resolve2, dirname, relative } from "node:path";
|
|
1692
|
+
|
|
1693
|
+
// packages/daemon/src/LLMExecutor.ts
|
|
1694
|
+
var AGENT_TOOLS = [
|
|
1695
|
+
{
|
|
1696
|
+
name: "shell_exec",
|
|
1697
|
+
description: "Execute a shell command in the working directory. Use for running servers (with & for background), installing packages, running tests, git operations, etc. Returns stdout + stderr.",
|
|
1698
|
+
input_schema: {
|
|
1699
|
+
type: "object",
|
|
1700
|
+
properties: {
|
|
1701
|
+
command: { type: "string", description: "Shell command to execute" },
|
|
1702
|
+
timeout_ms: { type: "number", description: "Timeout in milliseconds (default 30000)" }
|
|
1703
|
+
},
|
|
1704
|
+
required: ["command"]
|
|
1705
|
+
}
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
name: "write_file",
|
|
1709
|
+
description: "Write content to a file. Creates parent directories if needed. Use for creating HTML, CSS, JS, config files, etc.",
|
|
1710
|
+
input_schema: {
|
|
1711
|
+
type: "object",
|
|
1712
|
+
properties: {
|
|
1713
|
+
path: { type: "string", description: "File path relative to working directory" },
|
|
1714
|
+
content: { type: "string", description: "Full file content to write" }
|
|
1715
|
+
},
|
|
1716
|
+
required: ["path", "content"]
|
|
1717
|
+
}
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
name: "read_file",
|
|
1721
|
+
description: "Read a file's contents.",
|
|
1722
|
+
input_schema: {
|
|
1723
|
+
type: "object",
|
|
1724
|
+
properties: {
|
|
1725
|
+
path: { type: "string", description: "File path relative to working directory" }
|
|
1726
|
+
},
|
|
1727
|
+
required: ["path"]
|
|
1728
|
+
}
|
|
1729
|
+
},
|
|
1730
|
+
{
|
|
1731
|
+
name: "list_dir",
|
|
1732
|
+
description: "List files and directories.",
|
|
1733
|
+
input_schema: {
|
|
1734
|
+
type: "object",
|
|
1735
|
+
properties: {
|
|
1736
|
+
path: { type: "string", description: 'Directory path relative to working directory (default: ".")' }
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
];
|
|
1741
|
+
var LLMExecutor = class {
|
|
1742
|
+
constructor(config) {
|
|
1743
|
+
this.config = config;
|
|
1744
|
+
}
|
|
1745
|
+
get isConfigured() {
|
|
1746
|
+
if (this.config.provider === "ollama") return true;
|
|
1747
|
+
return !!this.config.api_key?.trim();
|
|
1748
|
+
}
|
|
1749
|
+
// ─── Single completion (no tools, no streaming) ──────────────────────────
|
|
1750
|
+
async complete(messages, system) {
|
|
1751
|
+
const res = await this.completeWithTools(messages, [], system, void 0);
|
|
1752
|
+
return { content: res.content, tokens_used: res.tokens_used, model: res.model };
|
|
1753
|
+
}
|
|
1754
|
+
// ─── Tool-calling completion with optional streaming ─────────────────────
|
|
1755
|
+
async completeWithTools(messages, tools, system, onToken) {
|
|
1756
|
+
switch (this.config.provider) {
|
|
1757
|
+
case "anthropic":
|
|
1758
|
+
return this.anthropic(messages, tools, system, onToken);
|
|
1759
|
+
case "openai":
|
|
1760
|
+
return this.openai(messages, tools, system, onToken);
|
|
1761
|
+
case "xai":
|
|
1762
|
+
return this.openai(messages, tools, system, onToken, "https://api.x.ai/v1");
|
|
1763
|
+
case "gemini":
|
|
1764
|
+
return this.openai(messages, tools, system, onToken, "https://generativelanguage.googleapis.com/v1beta/openai");
|
|
1765
|
+
case "ollama":
|
|
1766
|
+
return this.ollama(messages, system, onToken);
|
|
1767
|
+
default:
|
|
1768
|
+
return this.openai(messages, tools, system, onToken);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
// ─── Anthropic ───────────────────────────────────────────────────────────
|
|
1772
|
+
async anthropic(messages, tools, system, onToken) {
|
|
1773
|
+
const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
|
|
1774
|
+
const filtered = messages.filter((m) => m.role !== "system");
|
|
1775
|
+
const anthropicMsgs = filtered.map((m) => {
|
|
1776
|
+
if (m.role === "tool") {
|
|
1777
|
+
return {
|
|
1778
|
+
role: "user",
|
|
1779
|
+
content: [{ type: "tool_result", tool_use_id: m.tool_call_id, content: m.content }]
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
if (m.role === "assistant" && m.tool_calls?.length) {
|
|
1783
|
+
return {
|
|
1784
|
+
role: "assistant",
|
|
1785
|
+
content: [
|
|
1786
|
+
...m.content ? [{ type: "text", text: m.content }] : [],
|
|
1787
|
+
...m.tool_calls.map((tc) => ({
|
|
1788
|
+
type: "tool_use",
|
|
1789
|
+
id: tc.id,
|
|
1790
|
+
name: tc.name,
|
|
1791
|
+
input: tc.input
|
|
1792
|
+
}))
|
|
1793
|
+
]
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
return { role: m.role, content: m.content };
|
|
1797
|
+
});
|
|
1798
|
+
const body = {
|
|
1799
|
+
model: this.config.model,
|
|
1800
|
+
max_tokens: 8192,
|
|
1801
|
+
messages: anthropicMsgs,
|
|
1802
|
+
stream: true
|
|
1803
|
+
};
|
|
1804
|
+
if (sysContent) body.system = sysContent;
|
|
1805
|
+
if (tools.length > 0) {
|
|
1806
|
+
body.tools = tools.map((t) => ({
|
|
1807
|
+
name: t.name,
|
|
1808
|
+
description: t.description,
|
|
1809
|
+
input_schema: t.input_schema
|
|
1810
|
+
}));
|
|
1811
|
+
}
|
|
1812
|
+
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
1813
|
+
method: "POST",
|
|
1814
|
+
headers: {
|
|
1815
|
+
"Content-Type": "application/json",
|
|
1816
|
+
"x-api-key": this.config.api_key,
|
|
1817
|
+
"anthropic-version": "2023-06-01"
|
|
1818
|
+
},
|
|
1819
|
+
body: JSON.stringify(body)
|
|
1820
|
+
});
|
|
1821
|
+
if (!res.ok) {
|
|
1822
|
+
const err = await res.text();
|
|
1823
|
+
throw new Error(`Anthropic ${res.status}: ${err}`);
|
|
1824
|
+
}
|
|
1825
|
+
let textContent = "";
|
|
1826
|
+
let stopReason = "end_turn";
|
|
1827
|
+
let inputTokens = 0;
|
|
1828
|
+
let outputTokens = 0;
|
|
1829
|
+
let modelName = this.config.model;
|
|
1830
|
+
const toolCalls = [];
|
|
1831
|
+
const toolInputBuffers = {};
|
|
1832
|
+
let currentToolId = "";
|
|
1833
|
+
const reader = res.body.getReader();
|
|
1834
|
+
const decoder = new TextDecoder();
|
|
1835
|
+
let buf = "";
|
|
1836
|
+
while (true) {
|
|
1837
|
+
const { done, value } = await reader.read();
|
|
1838
|
+
if (done) break;
|
|
1839
|
+
buf += decoder.decode(value, { stream: true });
|
|
1840
|
+
const lines = buf.split("\n");
|
|
1841
|
+
buf = lines.pop() ?? "";
|
|
1842
|
+
for (const line of lines) {
|
|
1843
|
+
if (!line.startsWith("data: ")) continue;
|
|
1844
|
+
const data = line.slice(6).trim();
|
|
1845
|
+
if (data === "[DONE]" || data === "") continue;
|
|
1846
|
+
let evt;
|
|
1847
|
+
try {
|
|
1848
|
+
evt = JSON.parse(data);
|
|
1849
|
+
} catch {
|
|
1850
|
+
continue;
|
|
1851
|
+
}
|
|
1852
|
+
const type = evt.type;
|
|
1853
|
+
if (type === "message_start") {
|
|
1854
|
+
const usage = evt.message?.usage;
|
|
1855
|
+
inputTokens = usage?.input_tokens ?? 0;
|
|
1856
|
+
modelName = evt.message?.model ?? modelName;
|
|
1857
|
+
} else if (type === "content_block_start") {
|
|
1858
|
+
const block = evt.content_block;
|
|
1859
|
+
if (block?.type === "tool_use") {
|
|
1860
|
+
currentToolId = block.id;
|
|
1861
|
+
toolInputBuffers[currentToolId] = "";
|
|
1862
|
+
toolCalls.push({ id: currentToolId, name: block.name, input: {} });
|
|
1863
|
+
}
|
|
1864
|
+
} else if (type === "content_block_delta") {
|
|
1865
|
+
const delta = evt.delta;
|
|
1866
|
+
if (delta?.type === "text_delta") {
|
|
1867
|
+
const token = delta.text ?? "";
|
|
1868
|
+
textContent += token;
|
|
1869
|
+
if (onToken && token) onToken(token);
|
|
1870
|
+
} else if (delta?.type === "input_json_delta") {
|
|
1871
|
+
toolInputBuffers[currentToolId] = (toolInputBuffers[currentToolId] ?? "") + (delta.partial_json ?? "");
|
|
1872
|
+
}
|
|
1873
|
+
} else if (type === "content_block_stop") {
|
|
1874
|
+
if (currentToolId && toolInputBuffers[currentToolId]) {
|
|
1875
|
+
const tc = toolCalls.find((t) => t.id === currentToolId);
|
|
1876
|
+
if (tc) {
|
|
1877
|
+
try {
|
|
1878
|
+
tc.input = JSON.parse(toolInputBuffers[currentToolId]);
|
|
1879
|
+
} catch {
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
} else if (type === "message_delta") {
|
|
1884
|
+
const usage = evt.usage;
|
|
1885
|
+
outputTokens = usage?.output_tokens ?? 0;
|
|
1886
|
+
const stop = evt.delta?.stop_reason;
|
|
1887
|
+
if (stop === "tool_use") stopReason = "tool_use";
|
|
1888
|
+
else if (stop === "end_turn") stopReason = "end_turn";
|
|
1889
|
+
else if (stop === "max_tokens") stopReason = "max_tokens";
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return {
|
|
1894
|
+
content: textContent,
|
|
1895
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : null,
|
|
1896
|
+
stop_reason: stopReason,
|
|
1897
|
+
tokens_used: inputTokens + outputTokens,
|
|
1898
|
+
model: modelName
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
// ─── OpenAI (also xAI, Gemini) ───────────────────────────────────────────
|
|
1902
|
+
async openai(messages, tools, system, onToken, baseUrl = "https://api.openai.com/v1") {
|
|
1903
|
+
const allMessages = [];
|
|
1904
|
+
const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
|
|
1905
|
+
if (sysContent) allMessages.push({ role: "system", content: sysContent });
|
|
1906
|
+
for (const m of messages.filter((m2) => m2.role !== "system")) {
|
|
1907
|
+
if (m.role === "tool") {
|
|
1908
|
+
allMessages.push({ role: "tool", tool_call_id: m.tool_call_id, content: m.content });
|
|
1909
|
+
} else if (m.role === "assistant" && m.tool_calls?.length) {
|
|
1910
|
+
allMessages.push({
|
|
1911
|
+
role: "assistant",
|
|
1912
|
+
content: m.content || null,
|
|
1913
|
+
tool_calls: m.tool_calls.map((tc) => ({
|
|
1914
|
+
id: tc.id,
|
|
1915
|
+
type: "function",
|
|
1916
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.input) }
|
|
1917
|
+
}))
|
|
1918
|
+
});
|
|
1919
|
+
} else {
|
|
1920
|
+
allMessages.push({ role: m.role, content: m.content });
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const body = {
|
|
1924
|
+
model: this.config.model,
|
|
1925
|
+
messages: allMessages,
|
|
1926
|
+
max_tokens: 8192,
|
|
1927
|
+
stream: true,
|
|
1928
|
+
stream_options: { include_usage: true }
|
|
1929
|
+
};
|
|
1930
|
+
if (tools.length > 0) {
|
|
1931
|
+
body.tools = tools.map((t) => ({
|
|
1932
|
+
type: "function",
|
|
1933
|
+
function: { name: t.name, description: t.description, parameters: t.input_schema }
|
|
1934
|
+
}));
|
|
1935
|
+
}
|
|
1936
|
+
const res = await fetch(`${this.config.base_url ?? baseUrl}/chat/completions`, {
|
|
1937
|
+
method: "POST",
|
|
1938
|
+
headers: {
|
|
1939
|
+
"Content-Type": "application/json",
|
|
1940
|
+
"Authorization": `Bearer ${this.config.api_key}`
|
|
1941
|
+
},
|
|
1942
|
+
body: JSON.stringify(body)
|
|
1943
|
+
});
|
|
1944
|
+
if (!res.ok) {
|
|
1945
|
+
const err = await res.text();
|
|
1946
|
+
throw new Error(`OpenAI ${res.status}: ${err}`);
|
|
1947
|
+
}
|
|
1948
|
+
let textContent = "";
|
|
1949
|
+
let tokensUsed = 0;
|
|
1950
|
+
let modelName = this.config.model;
|
|
1951
|
+
let stopReason = "end_turn";
|
|
1952
|
+
const toolCallMap = {};
|
|
1953
|
+
const reader = res.body.getReader();
|
|
1954
|
+
const decoder = new TextDecoder();
|
|
1955
|
+
let buf = "";
|
|
1956
|
+
while (true) {
|
|
1957
|
+
const { done, value } = await reader.read();
|
|
1958
|
+
if (done) break;
|
|
1959
|
+
buf += decoder.decode(value, { stream: true });
|
|
1960
|
+
const lines = buf.split("\n");
|
|
1961
|
+
buf = lines.pop() ?? "";
|
|
1962
|
+
for (const line of lines) {
|
|
1963
|
+
if (!line.startsWith("data: ")) continue;
|
|
1964
|
+
const data = line.slice(6).trim();
|
|
1965
|
+
if (data === "[DONE]") continue;
|
|
1966
|
+
let evt;
|
|
1967
|
+
try {
|
|
1968
|
+
evt = JSON.parse(data);
|
|
1969
|
+
} catch {
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
modelName = evt.model ?? modelName;
|
|
1973
|
+
const usage = evt.usage;
|
|
1974
|
+
if (usage?.total_tokens) tokensUsed = usage.total_tokens;
|
|
1975
|
+
const choices = evt.choices;
|
|
1976
|
+
if (!choices?.length) continue;
|
|
1977
|
+
const delta = choices[0].delta;
|
|
1978
|
+
if (!delta) continue;
|
|
1979
|
+
const finish = choices[0].finish_reason;
|
|
1980
|
+
if (finish === "tool_calls") stopReason = "tool_use";
|
|
1981
|
+
else if (finish === "stop") stopReason = "end_turn";
|
|
1982
|
+
const token = delta.content;
|
|
1983
|
+
if (token) {
|
|
1984
|
+
textContent += token;
|
|
1985
|
+
if (onToken) onToken(token);
|
|
1986
|
+
}
|
|
1987
|
+
const toolCallDeltas = delta.tool_calls;
|
|
1988
|
+
if (toolCallDeltas) {
|
|
1989
|
+
for (const tc of toolCallDeltas) {
|
|
1990
|
+
const idx = tc.index;
|
|
1991
|
+
if (!toolCallMap[idx]) {
|
|
1992
|
+
toolCallMap[idx] = { id: "", name: "", args: "" };
|
|
1993
|
+
}
|
|
1994
|
+
const fn = tc.function;
|
|
1995
|
+
if (tc.id) toolCallMap[idx].id = tc.id;
|
|
1996
|
+
if (fn?.name) toolCallMap[idx].name = fn.name;
|
|
1997
|
+
if (fn?.arguments) toolCallMap[idx].args += fn.arguments;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
const toolCalls = Object.values(toolCallMap).filter((tc) => tc.id && tc.name).map((tc) => {
|
|
2003
|
+
let input = {};
|
|
2004
|
+
try {
|
|
2005
|
+
input = JSON.parse(tc.args);
|
|
2006
|
+
} catch {
|
|
2007
|
+
}
|
|
2008
|
+
return { id: tc.id, name: tc.name, input };
|
|
2009
|
+
});
|
|
2010
|
+
return {
|
|
2011
|
+
content: textContent,
|
|
2012
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : null,
|
|
2013
|
+
stop_reason: stopReason,
|
|
2014
|
+
tokens_used: tokensUsed,
|
|
2015
|
+
model: modelName
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
// ─── Ollama (no streaming for simplicity) ────────────────────────────────
|
|
2019
|
+
async ollama(messages, system, onToken) {
|
|
2020
|
+
const baseUrl = this.config.base_url ?? "http://localhost:11434";
|
|
2021
|
+
const allMessages = [];
|
|
2022
|
+
const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
|
|
2023
|
+
if (sysContent) allMessages.push({ role: "system", content: sysContent });
|
|
2024
|
+
allMessages.push(...messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content })));
|
|
2025
|
+
const res = await fetch(`${baseUrl}/api/chat`, {
|
|
2026
|
+
method: "POST",
|
|
2027
|
+
headers: { "Content-Type": "application/json" },
|
|
2028
|
+
body: JSON.stringify({ model: this.config.model, messages: allMessages, stream: false })
|
|
2029
|
+
});
|
|
2030
|
+
if (!res.ok) throw new Error(`Ollama error ${res.status}`);
|
|
2031
|
+
const data = await res.json();
|
|
2032
|
+
if (onToken) onToken(data.message.content);
|
|
2033
|
+
return { content: data.message.content, tool_calls: null, stop_reason: "end_turn", tokens_used: data.eval_count ?? 0, model: this.config.model };
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
|
|
2037
|
+
// packages/daemon/src/AgentExecutor.ts
|
|
2038
|
+
var AgentExecutor = class {
|
|
2039
|
+
constructor(llm, config, onStep, onToken) {
|
|
2040
|
+
this.llm = llm;
|
|
2041
|
+
this.config = config;
|
|
2042
|
+
this.onStep = onStep;
|
|
2043
|
+
this.onToken = onToken;
|
|
2044
|
+
this.cwd = config.cwd;
|
|
2045
|
+
this.maxIterations = config.max_iterations ?? 20;
|
|
2046
|
+
this.maxCommandMs = config.max_command_ms ?? 3e4;
|
|
2047
|
+
}
|
|
2048
|
+
cwd;
|
|
2049
|
+
maxIterations;
|
|
2050
|
+
maxCommandMs;
|
|
2051
|
+
async execute(task, systemContext) {
|
|
2052
|
+
const filesWritten = [];
|
|
2053
|
+
const commandsRun = [];
|
|
2054
|
+
let totalTokens = 0;
|
|
2055
|
+
let modelName = "";
|
|
2056
|
+
const systemPrompt = this.buildSystemPrompt(systemContext);
|
|
2057
|
+
const messages = [
|
|
2058
|
+
{ role: "user", content: task }
|
|
2059
|
+
];
|
|
2060
|
+
let finalOutput = "";
|
|
2061
|
+
for (let i = 0; i < this.maxIterations; i++) {
|
|
2062
|
+
this.onStep(i === 0 ? "Thinking\u2026" : "Continuing\u2026");
|
|
2063
|
+
let response;
|
|
2064
|
+
try {
|
|
2065
|
+
response = await this.llm.completeWithTools(
|
|
2066
|
+
messages,
|
|
2067
|
+
AGENT_TOOLS,
|
|
2068
|
+
systemPrompt,
|
|
2069
|
+
// Only stream tokens on the final (non-tool) turn
|
|
2070
|
+
(token) => {
|
|
2071
|
+
this.onToken(token);
|
|
2072
|
+
finalOutput += token;
|
|
2073
|
+
}
|
|
2074
|
+
);
|
|
2075
|
+
} catch (err) {
|
|
2076
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2077
|
+
this.onStep(`LLM error: ${msg}`);
|
|
2078
|
+
finalOutput = `Error: ${msg}`;
|
|
2079
|
+
break;
|
|
2080
|
+
}
|
|
2081
|
+
totalTokens += response.tokens_used;
|
|
2082
|
+
modelName = response.model;
|
|
2083
|
+
if (response.stop_reason === "end_turn" || !response.tool_calls?.length) {
|
|
2084
|
+
if (!finalOutput && response.content) finalOutput = response.content;
|
|
2085
|
+
break;
|
|
2086
|
+
}
|
|
2087
|
+
finalOutput = "";
|
|
2088
|
+
messages.push({
|
|
2089
|
+
role: "assistant",
|
|
2090
|
+
content: response.content,
|
|
2091
|
+
tool_calls: response.tool_calls
|
|
2092
|
+
});
|
|
2093
|
+
for (const tc of response.tool_calls) {
|
|
2094
|
+
this.onStep(`\u25B6 ${tc.name}(${this.summariseInput(tc.name, tc.input)})`);
|
|
2095
|
+
let result;
|
|
2096
|
+
try {
|
|
2097
|
+
result = await this.executeTool(tc.name, tc.input);
|
|
2098
|
+
if (tc.name === "write_file" && tc.input.path) {
|
|
2099
|
+
filesWritten.push(String(tc.input.path));
|
|
2100
|
+
}
|
|
2101
|
+
if (tc.name === "shell_exec" && tc.input.command) {
|
|
2102
|
+
commandsRun.push(String(tc.input.command));
|
|
2103
|
+
}
|
|
2104
|
+
} catch (err) {
|
|
2105
|
+
result = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2106
|
+
}
|
|
2107
|
+
this.onStep(` \u21B3 ${result.slice(0, 120)}${result.length > 120 ? "\u2026" : ""}`);
|
|
2108
|
+
messages.push({
|
|
2109
|
+
role: "tool",
|
|
2110
|
+
content: result,
|
|
2111
|
+
tool_call_id: tc.id
|
|
2112
|
+
});
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return {
|
|
2116
|
+
output: finalOutput || "(no output)",
|
|
2117
|
+
files_written: filesWritten,
|
|
2118
|
+
commands_run: commandsRun,
|
|
2119
|
+
tokens_used: totalTokens,
|
|
2120
|
+
model: modelName,
|
|
2121
|
+
iterations: messages.filter((m) => m.role === "assistant").length
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
// ─── Tool execution ────────────────────────────────────────────────────────
|
|
2125
|
+
async executeTool(name, input) {
|
|
2126
|
+
switch (name) {
|
|
2127
|
+
case "shell_exec":
|
|
2128
|
+
return this.shellExec(
|
|
2129
|
+
String(input.command ?? ""),
|
|
2130
|
+
Number(input.timeout_ms ?? this.maxCommandMs)
|
|
2131
|
+
);
|
|
2132
|
+
case "write_file":
|
|
2133
|
+
return this.writeFile(String(input.path ?? ""), String(input.content ?? ""));
|
|
2134
|
+
case "read_file":
|
|
2135
|
+
return this.readFile(String(input.path ?? ""));
|
|
2136
|
+
case "list_dir":
|
|
2137
|
+
return this.listDir(input.path ? String(input.path) : void 0);
|
|
2138
|
+
default:
|
|
2139
|
+
return `Unknown tool: ${name}`;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
shellExec(command, timeoutMs) {
|
|
2143
|
+
return new Promise((resolve6) => {
|
|
2144
|
+
const chunks = [];
|
|
2145
|
+
const proc = spawn("bash", ["-c", command], {
|
|
2146
|
+
cwd: this.cwd,
|
|
2147
|
+
env: { ...process.env, TERM: "dumb" },
|
|
2148
|
+
timeout: timeoutMs
|
|
2149
|
+
});
|
|
2150
|
+
proc.stdout.on("data", (d) => chunks.push(d.toString()));
|
|
2151
|
+
proc.stderr.on("data", (d) => chunks.push(d.toString()));
|
|
2152
|
+
proc.on("close", (code) => {
|
|
2153
|
+
const output = chunks.join("").trim();
|
|
2154
|
+
resolve6(output || (code === 0 ? "(command completed, no output)" : `exit code ${code}`));
|
|
2155
|
+
});
|
|
2156
|
+
proc.on("error", (err) => {
|
|
2157
|
+
resolve6(`Error: ${err.message}`);
|
|
2158
|
+
});
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
writeFile(filePath, content) {
|
|
2162
|
+
const safe = this.safePath(filePath);
|
|
2163
|
+
if (!safe) return "Error: path outside working directory";
|
|
2164
|
+
mkdirSync(dirname(safe), { recursive: true });
|
|
2165
|
+
writeFileSync(safe, content, "utf8");
|
|
2166
|
+
const rel = relative(this.cwd, safe);
|
|
2167
|
+
return `Written: ${rel} (${content.length} bytes)`;
|
|
2168
|
+
}
|
|
2169
|
+
readFile(filePath) {
|
|
2170
|
+
const safe = this.safePath(filePath);
|
|
2171
|
+
if (!safe) return "Error: path outside working directory";
|
|
2172
|
+
if (!existsSync2(safe)) return `File not found: ${filePath}`;
|
|
2173
|
+
const content = readFileSync2(safe, "utf8");
|
|
2174
|
+
return content.length > 8e3 ? content.slice(0, 8e3) + `
|
|
2175
|
+
\u2026[truncated, ${content.length} total bytes]` : content;
|
|
2176
|
+
}
|
|
2177
|
+
listDir(dirPath) {
|
|
2178
|
+
const safe = this.safePath(dirPath ?? ".");
|
|
2179
|
+
if (!safe) return "Error: path outside working directory";
|
|
2180
|
+
if (!existsSync2(safe)) return `Directory not found: ${dirPath}`;
|
|
2181
|
+
try {
|
|
2182
|
+
const entries = readdirSync(safe, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => `${e.isDirectory() ? "d" : "f"} ${e.name}`).join("\n");
|
|
2183
|
+
return entries || "(empty directory)";
|
|
2184
|
+
} catch (e) {
|
|
2185
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
2189
|
+
safePath(p) {
|
|
2190
|
+
const resolved = resolve2(this.cwd, p);
|
|
2191
|
+
return resolved.startsWith(this.cwd) ? resolved : null;
|
|
2192
|
+
}
|
|
2193
|
+
buildSystemPrompt(extra) {
|
|
2194
|
+
const lines = [
|
|
2195
|
+
`You are 0agent, an AI software engineer. You can execute shell commands and manage files.`,
|
|
2196
|
+
`Working directory: ${this.cwd}`,
|
|
2197
|
+
``,
|
|
2198
|
+
`Instructions:`,
|
|
2199
|
+
`- Use tools to actually accomplish tasks, don't just describe what to do`,
|
|
2200
|
+
`- For web servers: write the files, then start the server with & (background)`,
|
|
2201
|
+
`- For npm/node projects: check package.json first with read_file or list_dir`,
|
|
2202
|
+
`- After write_file, verify with read_file if needed`,
|
|
2203
|
+
`- After shell_exec, check output for errors and retry if needed`,
|
|
2204
|
+
`- Use relative paths from the working directory`,
|
|
2205
|
+
`- Be concise in your final response: state what was done and where to find it`
|
|
2206
|
+
];
|
|
2207
|
+
if (extra) lines.push(``, `Context:`, extra);
|
|
2208
|
+
return lines.join("\n");
|
|
2209
|
+
}
|
|
2210
|
+
summariseInput(toolName, input) {
|
|
2211
|
+
if (toolName === "shell_exec") return `"${String(input.command ?? "").slice(0, 60)}"`;
|
|
2212
|
+
if (toolName === "write_file") return `"${input.path}"`;
|
|
2213
|
+
if (toolName === "read_file") return `"${input.path}"`;
|
|
2214
|
+
if (toolName === "list_dir") return `"${input.path ?? "."}"`;
|
|
2215
|
+
return JSON.stringify(input).slice(0, 60);
|
|
2216
|
+
}
|
|
2217
|
+
};
|
|
2218
|
+
|
|
1688
2219
|
// packages/daemon/src/SessionManager.ts
|
|
1689
2220
|
var SessionManager = class {
|
|
1690
2221
|
sessions = /* @__PURE__ */ new Map();
|
|
1691
2222
|
inferenceEngine;
|
|
1692
2223
|
eventBus;
|
|
1693
2224
|
graph;
|
|
2225
|
+
llm;
|
|
2226
|
+
cwd;
|
|
1694
2227
|
constructor(deps = {}) {
|
|
1695
2228
|
this.inferenceEngine = deps.inferenceEngine;
|
|
1696
2229
|
this.eventBus = deps.eventBus;
|
|
1697
2230
|
this.graph = deps.graph;
|
|
2231
|
+
this.llm = deps.llm;
|
|
2232
|
+
this.cwd = deps.cwd ?? process.cwd();
|
|
1698
2233
|
}
|
|
1699
2234
|
/**
|
|
1700
2235
|
* Create a new session with status 'pending'.
|
|
@@ -1862,14 +2397,36 @@ var SessionManager = class {
|
|
|
1862
2397
|
} else {
|
|
1863
2398
|
this.addStep(session.id, "No inference engine connected \u2014 executing task directly");
|
|
1864
2399
|
}
|
|
1865
|
-
this.
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2400
|
+
if (this.llm?.isConfigured) {
|
|
2401
|
+
const executor = new AgentExecutor(
|
|
2402
|
+
this.llm,
|
|
2403
|
+
{ cwd: this.cwd },
|
|
2404
|
+
// step callback → emit session.step events
|
|
2405
|
+
(step) => this.addStep(session.id, step),
|
|
2406
|
+
// token callback → emit session.token events
|
|
2407
|
+
(token) => this.emit({ type: "session.token", session_id: session.id, token })
|
|
2408
|
+
);
|
|
2409
|
+
const systemContext = enrichedReq.context?.system_context ? String(enrichedReq.context.system_context) : void 0;
|
|
2410
|
+
const agentResult = await executor.execute(enrichedReq.task, systemContext);
|
|
2411
|
+
if (agentResult.files_written.length > 0) {
|
|
2412
|
+
this.addStep(session.id, `Files written: ${agentResult.files_written.join(", ")}`);
|
|
2413
|
+
}
|
|
2414
|
+
if (agentResult.commands_run.length > 0) {
|
|
2415
|
+
this.addStep(session.id, `Commands run: ${agentResult.commands_run.length}`);
|
|
2416
|
+
}
|
|
2417
|
+
this.addStep(session.id, `Done (${agentResult.tokens_used} tokens, ${agentResult.iterations} LLM turns)`);
|
|
2418
|
+
this.completeSession(session.id, {
|
|
2419
|
+
output: agentResult.output,
|
|
2420
|
+
files_written: agentResult.files_written,
|
|
2421
|
+
commands_run: agentResult.commands_run,
|
|
2422
|
+
tokens_used: agentResult.tokens_used,
|
|
2423
|
+
model: agentResult.model
|
|
2424
|
+
});
|
|
2425
|
+
} else {
|
|
2426
|
+
const output = session.plan?.reasoning ?? "No LLM configured \u2014 add api_key to ~/.0agent/config.yaml";
|
|
2427
|
+
this.addStep(session.id, "No LLM API key configured");
|
|
2428
|
+
this.completeSession(session.id, { output });
|
|
2429
|
+
}
|
|
1873
2430
|
} catch (err) {
|
|
1874
2431
|
const message = err instanceof Error ? err.message : String(err);
|
|
1875
2432
|
this.failSession(session.id, message);
|
|
@@ -2112,7 +2669,7 @@ var BackgroundWorkers = class {
|
|
|
2112
2669
|
};
|
|
2113
2670
|
|
|
2114
2671
|
// packages/daemon/src/SkillRegistry.ts
|
|
2115
|
-
import { readFileSync as
|
|
2672
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as existsSync3, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2 } from "node:fs";
|
|
2116
2673
|
import { join } from "node:path";
|
|
2117
2674
|
import { homedir as homedir2 } from "node:os";
|
|
2118
2675
|
import YAML2 from "yaml";
|
|
@@ -2135,11 +2692,11 @@ var SkillRegistry = class {
|
|
|
2135
2692
|
this.loadFromDir(this.customDir, false);
|
|
2136
2693
|
}
|
|
2137
2694
|
loadFromDir(dir, isBuiltin) {
|
|
2138
|
-
if (!
|
|
2139
|
-
const files =
|
|
2695
|
+
if (!existsSync3(dir)) return;
|
|
2696
|
+
const files = readdirSync2(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
2140
2697
|
for (const file of files) {
|
|
2141
2698
|
try {
|
|
2142
|
-
const raw =
|
|
2699
|
+
const raw = readFileSync3(join(dir, file), "utf8");
|
|
2143
2700
|
const skill = YAML2.parse(raw);
|
|
2144
2701
|
if (skill.name) {
|
|
2145
2702
|
this.skills.set(skill.name, skill);
|
|
@@ -2174,9 +2731,9 @@ var SkillRegistry = class {
|
|
|
2174
2731
|
if (this.builtinNames.has(name)) {
|
|
2175
2732
|
throw new Error(`Cannot override built-in skill: ${name}`);
|
|
2176
2733
|
}
|
|
2177
|
-
|
|
2734
|
+
mkdirSync2(this.customDir, { recursive: true });
|
|
2178
2735
|
const filePath = join(this.customDir, `${name}.yaml`);
|
|
2179
|
-
|
|
2736
|
+
writeFileSync2(filePath, yamlContent, "utf8");
|
|
2180
2737
|
const skill = YAML2.parse(yamlContent);
|
|
2181
2738
|
this.skills.set(name, skill);
|
|
2182
2739
|
return skill;
|
|
@@ -2189,7 +2746,7 @@ var SkillRegistry = class {
|
|
|
2189
2746
|
throw new Error(`Cannot delete built-in skill: ${name}`);
|
|
2190
2747
|
}
|
|
2191
2748
|
const filePath = join(this.customDir, `${name}.yaml`);
|
|
2192
|
-
if (
|
|
2749
|
+
if (existsSync3(filePath)) {
|
|
2193
2750
|
unlinkSync(filePath);
|
|
2194
2751
|
}
|
|
2195
2752
|
this.skills.delete(name);
|
|
@@ -2202,8 +2759,8 @@ var SkillRegistry = class {
|
|
|
2202
2759
|
// packages/daemon/src/HTTPServer.ts
|
|
2203
2760
|
import { Hono as Hono8 } from "hono";
|
|
2204
2761
|
import { serve } from "@hono/node-server";
|
|
2205
|
-
import { readFileSync as
|
|
2206
|
-
import { resolve as
|
|
2762
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
2763
|
+
import { resolve as resolve3, dirname as dirname2 } from "node:path";
|
|
2207
2764
|
import { fileURLToPath } from "node:url";
|
|
2208
2765
|
|
|
2209
2766
|
// packages/daemon/src/routes/health.ts
|
|
@@ -2458,15 +3015,15 @@ function skillRoutes(deps) {
|
|
|
2458
3015
|
// packages/daemon/src/HTTPServer.ts
|
|
2459
3016
|
function findGraphHtml() {
|
|
2460
3017
|
const candidates = [
|
|
2461
|
-
|
|
3018
|
+
resolve3(dirname2(fileURLToPath(import.meta.url)), "graph.html"),
|
|
2462
3019
|
// dev (src/)
|
|
2463
|
-
|
|
3020
|
+
resolve3(dirname2(fileURLToPath(import.meta.url)), "..", "graph.html"),
|
|
2464
3021
|
// bundled (dist/../)
|
|
2465
|
-
|
|
3022
|
+
resolve3(dirname2(fileURLToPath(import.meta.url)), "..", "dist", "graph.html")
|
|
2466
3023
|
];
|
|
2467
3024
|
for (const p of candidates) {
|
|
2468
3025
|
try {
|
|
2469
|
-
|
|
3026
|
+
readFileSync4(p);
|
|
2470
3027
|
return p;
|
|
2471
3028
|
} catch {
|
|
2472
3029
|
}
|
|
@@ -2490,7 +3047,7 @@ var HTTPServer = class {
|
|
|
2490
3047
|
this.app.route("/api/skills", skillRoutes({ skillRegistry: deps.skillRegistry }));
|
|
2491
3048
|
const serveGraph = (c) => {
|
|
2492
3049
|
try {
|
|
2493
|
-
const html =
|
|
3050
|
+
const html = readFileSync4(GRAPH_HTML_PATH, "utf8");
|
|
2494
3051
|
return c.html(html);
|
|
2495
3052
|
} catch {
|
|
2496
3053
|
return c.html("<p>Graph UI not found. Run: pnpm build</p>");
|
|
@@ -2500,7 +3057,7 @@ var HTTPServer = class {
|
|
|
2500
3057
|
this.app.get("/graph", serveGraph);
|
|
2501
3058
|
}
|
|
2502
3059
|
start() {
|
|
2503
|
-
return new Promise((
|
|
3060
|
+
return new Promise((resolve6) => {
|
|
2504
3061
|
this.server = serve(
|
|
2505
3062
|
{
|
|
2506
3063
|
fetch: this.app.fetch,
|
|
@@ -2508,20 +3065,20 @@ var HTTPServer = class {
|
|
|
2508
3065
|
hostname: this.deps.host
|
|
2509
3066
|
},
|
|
2510
3067
|
() => {
|
|
2511
|
-
|
|
3068
|
+
resolve6();
|
|
2512
3069
|
}
|
|
2513
3070
|
);
|
|
2514
3071
|
});
|
|
2515
3072
|
}
|
|
2516
3073
|
stop() {
|
|
2517
|
-
return new Promise((
|
|
3074
|
+
return new Promise((resolve6, reject) => {
|
|
2518
3075
|
if (!this.server) {
|
|
2519
|
-
|
|
3076
|
+
resolve6();
|
|
2520
3077
|
return;
|
|
2521
3078
|
}
|
|
2522
3079
|
this.server.close((err) => {
|
|
2523
3080
|
if (err) reject(err);
|
|
2524
|
-
else
|
|
3081
|
+
else resolve6();
|
|
2525
3082
|
});
|
|
2526
3083
|
});
|
|
2527
3084
|
}
|
|
@@ -2545,13 +3102,13 @@ var ZeroAgentDaemon = class {
|
|
|
2545
3102
|
startedAt = 0;
|
|
2546
3103
|
pidFilePath;
|
|
2547
3104
|
constructor() {
|
|
2548
|
-
this.pidFilePath =
|
|
3105
|
+
this.pidFilePath = resolve4(homedir3(), ".0agent", "daemon.pid");
|
|
2549
3106
|
}
|
|
2550
3107
|
async start(opts) {
|
|
2551
3108
|
this.config = await loadConfig(opts?.config_path);
|
|
2552
|
-
const dotDir =
|
|
2553
|
-
if (!
|
|
2554
|
-
|
|
3109
|
+
const dotDir = resolve4(homedir3(), ".0agent");
|
|
3110
|
+
if (!existsSync4(dotDir)) {
|
|
3111
|
+
mkdirSync3(dotDir, { recursive: true });
|
|
2555
3112
|
}
|
|
2556
3113
|
this.adapter = new SQLiteAdapter({ db_path: this.config.graph.db_path });
|
|
2557
3114
|
this.graph = new KnowledgeGraph(this.adapter);
|
|
@@ -2562,10 +3119,25 @@ var ZeroAgentDaemon = class {
|
|
|
2562
3119
|
this.inferenceEngine = new InferenceEngine(this.graph, resolver, policy);
|
|
2563
3120
|
this.skillRegistry = new SkillRegistry();
|
|
2564
3121
|
await this.skillRegistry.loadAll();
|
|
3122
|
+
const defaultLLM = this.config.llm_providers.find((p) => p.is_default) ?? this.config.llm_providers[0];
|
|
3123
|
+
const llmExecutor = defaultLLM ? new LLMExecutor({
|
|
3124
|
+
provider: defaultLLM.provider,
|
|
3125
|
+
model: defaultLLM.model,
|
|
3126
|
+
api_key: defaultLLM.api_key ?? "",
|
|
3127
|
+
base_url: defaultLLM.base_url
|
|
3128
|
+
}) : void 0;
|
|
3129
|
+
if (llmExecutor?.isConfigured) {
|
|
3130
|
+
console.log(`[0agent] LLM: ${defaultLLM?.provider}/${defaultLLM?.model}`);
|
|
3131
|
+
} else {
|
|
3132
|
+
console.warn("[0agent] No LLM API key configured \u2014 tasks will not call the LLM");
|
|
3133
|
+
}
|
|
2565
3134
|
this.eventBus = new WebSocketEventBus();
|
|
2566
3135
|
this.sessionManager = new SessionManager({
|
|
2567
3136
|
inferenceEngine: this.inferenceEngine,
|
|
2568
|
-
eventBus: this.eventBus
|
|
3137
|
+
eventBus: this.eventBus,
|
|
3138
|
+
graph: this.graph,
|
|
3139
|
+
llm: llmExecutor,
|
|
3140
|
+
cwd: process.env["ZEROAGENT_CWD"] ?? process.cwd()
|
|
2569
3141
|
});
|
|
2570
3142
|
this.backgroundWorkers = new BackgroundWorkers({
|
|
2571
3143
|
graph: this.graph,
|
|
@@ -2587,7 +3159,7 @@ var ZeroAgentDaemon = class {
|
|
|
2587
3159
|
getStatus: () => this.getStatus()
|
|
2588
3160
|
});
|
|
2589
3161
|
await this.httpServer.start();
|
|
2590
|
-
|
|
3162
|
+
writeFileSync3(this.pidFilePath, String(process.pid), "utf8");
|
|
2591
3163
|
console.log(
|
|
2592
3164
|
`[0agent] Daemon started on ${this.config.server.host}:${this.config.server.port} (PID: ${process.pid})`
|
|
2593
3165
|
);
|
|
@@ -2621,7 +3193,7 @@ var ZeroAgentDaemon = class {
|
|
|
2621
3193
|
this.graph = null;
|
|
2622
3194
|
}
|
|
2623
3195
|
this.adapter = null;
|
|
2624
|
-
if (
|
|
3196
|
+
if (existsSync4(this.pidFilePath)) {
|
|
2625
3197
|
try {
|
|
2626
3198
|
unlinkSync2(this.pidFilePath);
|
|
2627
3199
|
} catch {
|
|
@@ -2651,11 +3223,11 @@ var ZeroAgentDaemon = class {
|
|
|
2651
3223
|
};
|
|
2652
3224
|
|
|
2653
3225
|
// packages/daemon/src/start.ts
|
|
2654
|
-
import { resolve as
|
|
3226
|
+
import { resolve as resolve5 } from "node:path";
|
|
2655
3227
|
import { homedir as homedir4 } from "node:os";
|
|
2656
|
-
import { existsSync as
|
|
2657
|
-
var CONFIG_PATH = process.env["ZEROAGENT_CONFIG"] ??
|
|
2658
|
-
if (!
|
|
3228
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
3229
|
+
var CONFIG_PATH = process.env["ZEROAGENT_CONFIG"] ?? resolve5(homedir4(), ".0agent", "config.yaml");
|
|
3230
|
+
if (!existsSync5(CONFIG_PATH)) {
|
|
2659
3231
|
console.error(`
|
|
2660
3232
|
0agent is not initialised.
|
|
2661
3233
|
|