amai 0.0.5 → 0.0.6
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/cli.cjs +274 -80
- package/dist/cli.js +272 -78
- package/dist/lib/daemon-entry.cjs +213 -19
- package/dist/lib/daemon-entry.js +212 -18
- package/dist/server.cjs +213 -20
- package/dist/server.d.cts +1 -3
- package/dist/server.d.ts +1 -3
- package/dist/server.js +213 -19
- package/package.json +2 -2
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var events = require('events');
|
|
4
|
+
var WebSocket2 = require('ws');
|
|
6
5
|
var zod = require('zod');
|
|
7
6
|
var promises = require('fs/promises');
|
|
8
7
|
var path9 = require('path');
|
|
@@ -18,7 +17,7 @@ var cors = require('hono/cors');
|
|
|
18
17
|
|
|
19
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
19
|
|
|
21
|
-
var
|
|
20
|
+
var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
|
|
22
21
|
var path9__default = /*#__PURE__*/_interopDefault(path9);
|
|
23
22
|
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
24
23
|
var os2__default = /*#__PURE__*/_interopDefault(os2);
|
|
@@ -1421,15 +1420,6 @@ var startHttpServer = (connection) => {
|
|
|
1421
1420
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1422
1421
|
|
|
1423
1422
|
`));
|
|
1424
|
-
const statusHandler = (data) => {
|
|
1425
|
-
try {
|
|
1426
|
-
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1427
|
-
|
|
1428
|
-
`));
|
|
1429
|
-
} catch {
|
|
1430
|
-
}
|
|
1431
|
-
};
|
|
1432
|
-
statusEmitter.on("status", statusHandler);
|
|
1433
1423
|
const heartbeatInterval = setInterval(() => {
|
|
1434
1424
|
try {
|
|
1435
1425
|
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
@@ -1440,7 +1430,6 @@ var startHttpServer = (connection) => {
|
|
|
1440
1430
|
}
|
|
1441
1431
|
}, 15e3);
|
|
1442
1432
|
c.req.raw.signal.addEventListener("abort", () => {
|
|
1443
|
-
statusEmitter.off("status", statusHandler);
|
|
1444
1433
|
clearInterval(heartbeatInterval);
|
|
1445
1434
|
});
|
|
1446
1435
|
}
|
|
@@ -1664,6 +1653,20 @@ function getTokens() {
|
|
|
1664
1653
|
const data = JSON.parse(raw);
|
|
1665
1654
|
return data;
|
|
1666
1655
|
}
|
|
1656
|
+
var getUserId = () => {
|
|
1657
|
+
try {
|
|
1658
|
+
if (!fs3__default.default.existsSync(CREDENTIALS_PATH)) {
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
const raw = fs3__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1662
|
+
const data = JSON.parse(raw);
|
|
1663
|
+
return {
|
|
1664
|
+
userId: data.user.id
|
|
1665
|
+
};
|
|
1666
|
+
} catch {
|
|
1667
|
+
throw new Error("Error while getting userId");
|
|
1668
|
+
}
|
|
1669
|
+
};
|
|
1667
1670
|
var ExplanationSchema = zod.z.object({
|
|
1668
1671
|
explanation: zod.z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1669
1672
|
});
|
|
@@ -1752,8 +1755,201 @@ var runTerminalCommand = async (input, projectCwd) => {
|
|
|
1752
1755
|
}
|
|
1753
1756
|
};
|
|
1754
1757
|
|
|
1758
|
+
// src/lib/rpc-handlers.ts
|
|
1759
|
+
var rpcHandlers = {
|
|
1760
|
+
"daemon:get_workspace_folders": async () => {
|
|
1761
|
+
const projects = projectRegistry.list();
|
|
1762
|
+
return {
|
|
1763
|
+
folders: projects.map((p) => ({
|
|
1764
|
+
id: p.id,
|
|
1765
|
+
cwd: p.cwd,
|
|
1766
|
+
name: p.name,
|
|
1767
|
+
active: p.active
|
|
1768
|
+
}))
|
|
1769
|
+
};
|
|
1770
|
+
},
|
|
1771
|
+
"daemon:get_environment": async ({ gitRepositoryUrl }) => {
|
|
1772
|
+
const projects = projectRegistry.list();
|
|
1773
|
+
if (projects.length === 0) {
|
|
1774
|
+
const error = {
|
|
1775
|
+
_tag: "ProjectUnlinkedError",
|
|
1776
|
+
message: `Getting a local project by git repository URL "${gitRepositoryUrl}" returned an unlinked project. Please link it by running \`amai link <project name> <path to project directory>\``
|
|
1777
|
+
};
|
|
1778
|
+
throw error;
|
|
1779
|
+
}
|
|
1780
|
+
return {
|
|
1781
|
+
project: projects[0],
|
|
1782
|
+
env: {
|
|
1783
|
+
platform: process.platform,
|
|
1784
|
+
arch: process.arch,
|
|
1785
|
+
nodeVersion: process.version
|
|
1786
|
+
}
|
|
1787
|
+
};
|
|
1788
|
+
},
|
|
1789
|
+
"daemon:get_context": async ({ cwd }) => {
|
|
1790
|
+
try {
|
|
1791
|
+
const files = getContext(cwd);
|
|
1792
|
+
return { files, cwd };
|
|
1793
|
+
} catch (error) {
|
|
1794
|
+
const rpcError = {
|
|
1795
|
+
_tag: "ContextError",
|
|
1796
|
+
message: error.message || "Failed to get context"
|
|
1797
|
+
};
|
|
1798
|
+
throw rpcError;
|
|
1799
|
+
}
|
|
1800
|
+
},
|
|
1801
|
+
"daemon:get_ide_projects": async () => {
|
|
1802
|
+
const projects = await scanIdeProjects();
|
|
1803
|
+
return { projects };
|
|
1804
|
+
},
|
|
1805
|
+
"daemon:register_project": async ({ projectId, cwd, name }) => {
|
|
1806
|
+
if (!projectId || !cwd) {
|
|
1807
|
+
const error = {
|
|
1808
|
+
_tag: "ValidationError",
|
|
1809
|
+
message: "projectId and cwd are required"
|
|
1810
|
+
};
|
|
1811
|
+
throw error;
|
|
1812
|
+
}
|
|
1813
|
+
projectRegistry.register(projectId, cwd, name);
|
|
1814
|
+
return { success: true, projectId, cwd };
|
|
1815
|
+
},
|
|
1816
|
+
"daemon:unregister_project": async ({ projectId }) => {
|
|
1817
|
+
if (!projectId) {
|
|
1818
|
+
const error = {
|
|
1819
|
+
_tag: "ValidationError",
|
|
1820
|
+
message: "projectId is required"
|
|
1821
|
+
};
|
|
1822
|
+
throw error;
|
|
1823
|
+
}
|
|
1824
|
+
projectRegistry.unregister(projectId);
|
|
1825
|
+
return { success: true, projectId };
|
|
1826
|
+
},
|
|
1827
|
+
"daemon:get_project": async ({ projectId }) => {
|
|
1828
|
+
const project = projectRegistry.getProject(projectId);
|
|
1829
|
+
if (!project) {
|
|
1830
|
+
const error = {
|
|
1831
|
+
_tag: "ProjectNotFoundError",
|
|
1832
|
+
message: `Project not found: ${projectId}`
|
|
1833
|
+
};
|
|
1834
|
+
throw error;
|
|
1835
|
+
}
|
|
1836
|
+
return { project };
|
|
1837
|
+
},
|
|
1838
|
+
"daemon:list_projects": async () => {
|
|
1839
|
+
const projects = projectRegistry.list();
|
|
1840
|
+
return { projects };
|
|
1841
|
+
},
|
|
1842
|
+
"daemon:status": async () => {
|
|
1843
|
+
return {
|
|
1844
|
+
connected: true,
|
|
1845
|
+
timestamp: Date.now(),
|
|
1846
|
+
platform: process.platform,
|
|
1847
|
+
arch: process.arch
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
var reconnectTimeout = null;
|
|
1852
|
+
var connectToUserStreams = async (serverUrl) => {
|
|
1853
|
+
const userId = getUserId();
|
|
1854
|
+
if (!userId?.userId) {
|
|
1855
|
+
throw new Error("User ID not found");
|
|
1856
|
+
}
|
|
1857
|
+
const params = new URLSearchParams({
|
|
1858
|
+
userId: userId.userId,
|
|
1859
|
+
type: "cli"
|
|
1860
|
+
});
|
|
1861
|
+
const tokens = getTokens();
|
|
1862
|
+
if (!tokens) {
|
|
1863
|
+
throw new Error("No tokens found");
|
|
1864
|
+
}
|
|
1865
|
+
const wsUrl = `${serverUrl}/api/v1/user-streams?${params.toString()}`;
|
|
1866
|
+
const ws = new WebSocket2__default.default(wsUrl, {
|
|
1867
|
+
headers: {
|
|
1868
|
+
Authorization: `Bearer ${tokens.access_token}`
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
ws.on("open", () => {
|
|
1872
|
+
console.log(pc2__default.default.green("CLI connected to user-streams"));
|
|
1873
|
+
if (reconnectTimeout) {
|
|
1874
|
+
clearTimeout(reconnectTimeout);
|
|
1875
|
+
reconnectTimeout = null;
|
|
1876
|
+
}
|
|
1877
|
+
});
|
|
1878
|
+
ws.on("message", async (event) => {
|
|
1879
|
+
try {
|
|
1880
|
+
const message = JSON.parse(event.toString());
|
|
1881
|
+
if (message._tag === "rpc_call") {
|
|
1882
|
+
const { requestId, method, input } = message;
|
|
1883
|
+
console.log(pc2__default.default.gray(`RPC call: ${method}`));
|
|
1884
|
+
const handler = rpcHandlers[method];
|
|
1885
|
+
if (!handler) {
|
|
1886
|
+
ws.send(JSON.stringify({
|
|
1887
|
+
_tag: "rpc_result",
|
|
1888
|
+
requestId,
|
|
1889
|
+
data: {
|
|
1890
|
+
_tag: "UnknownMethodError",
|
|
1891
|
+
message: `Unknown RPC method: ${method}`
|
|
1892
|
+
}
|
|
1893
|
+
}));
|
|
1894
|
+
console.log(pc2__default.default.yellow(`Unknown RPC method: ${method}`));
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
try {
|
|
1898
|
+
const result = await handler(input || {});
|
|
1899
|
+
ws.send(JSON.stringify({
|
|
1900
|
+
_tag: "rpc_result",
|
|
1901
|
+
requestId,
|
|
1902
|
+
data: result
|
|
1903
|
+
}));
|
|
1904
|
+
console.log(pc2__default.default.green(`RPC completed: ${method}`));
|
|
1905
|
+
} catch (error) {
|
|
1906
|
+
const rpcError = error._tag ? error : {
|
|
1907
|
+
_tag: "RpcError",
|
|
1908
|
+
message: error.message || String(error)
|
|
1909
|
+
};
|
|
1910
|
+
ws.send(JSON.stringify({
|
|
1911
|
+
_tag: "rpc_result",
|
|
1912
|
+
requestId,
|
|
1913
|
+
data: rpcError
|
|
1914
|
+
}));
|
|
1915
|
+
console.log(pc2__default.default.red(`RPC failed: ${method} - ${rpcError.message}`));
|
|
1916
|
+
}
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
if (message.type === "presence_request") {
|
|
1920
|
+
if (message.status === "connected") {
|
|
1921
|
+
ws.send(JSON.stringify({
|
|
1922
|
+
type: "presence_request",
|
|
1923
|
+
status: "connected"
|
|
1924
|
+
}));
|
|
1925
|
+
}
|
|
1926
|
+
if (message.status === "disconnected") {
|
|
1927
|
+
ws.send(JSON.stringify({
|
|
1928
|
+
type: "presence_request",
|
|
1929
|
+
status: "disconnected"
|
|
1930
|
+
}));
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
} catch (parseError) {
|
|
1934
|
+
console.error(pc2__default.default.red(`Failed to parse message: ${parseError}`));
|
|
1935
|
+
}
|
|
1936
|
+
});
|
|
1937
|
+
ws.on("close", (code, reason) => {
|
|
1938
|
+
console.log(pc2__default.default.yellow(`CLI disconnected from user-streams (code: ${code})`));
|
|
1939
|
+
console.log(pc2__default.default.gray("Reconnecting in 5 seconds..."));
|
|
1940
|
+
reconnectTimeout = setTimeout(() => {
|
|
1941
|
+
connectToUserStreams(serverUrl).catch((err) => {
|
|
1942
|
+
console.error(pc2__default.default.red(`Reconnection failed: ${err.message}`));
|
|
1943
|
+
});
|
|
1944
|
+
}, 5e3);
|
|
1945
|
+
});
|
|
1946
|
+
ws.on("error", (error) => {
|
|
1947
|
+
console.error(pc2__default.default.red(`User streams WebSocket error: ${error.message}`));
|
|
1948
|
+
});
|
|
1949
|
+
return ws;
|
|
1950
|
+
};
|
|
1951
|
+
|
|
1755
1952
|
// src/server.ts
|
|
1756
|
-
var statusEmitter = new events.EventEmitter();
|
|
1757
1953
|
var toolExecutors = {
|
|
1758
1954
|
editFile: editFiles,
|
|
1759
1955
|
deleteFile,
|
|
@@ -1765,7 +1961,7 @@ var toolExecutors = {
|
|
|
1765
1961
|
runTerminalCommand
|
|
1766
1962
|
};
|
|
1767
1963
|
function getConnectionStatus(ws) {
|
|
1768
|
-
return ws.readyState ===
|
|
1964
|
+
return ws.readyState === WebSocket2__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket2__default.default.OPEN ? "open" : ws.readyState === WebSocket2__default.default.CLOSING ? "closing" : "closed";
|
|
1769
1965
|
}
|
|
1770
1966
|
function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
1771
1967
|
const tokens = getTokens();
|
|
@@ -1773,14 +1969,13 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1773
1969
|
throw new Error("No tokens found");
|
|
1774
1970
|
}
|
|
1775
1971
|
const wsUrl = `${serverUrl}/agent-streams`;
|
|
1776
|
-
const ws = new
|
|
1972
|
+
const ws = new WebSocket2__default.default(wsUrl, {
|
|
1777
1973
|
headers: {
|
|
1778
1974
|
"Authorization": `Bearer ${tokens.access_token}`
|
|
1779
1975
|
}
|
|
1780
1976
|
});
|
|
1781
1977
|
ws.on("open", () => {
|
|
1782
1978
|
console.log(pc2__default.default.green("Connected to server agent streams"));
|
|
1783
|
-
statusEmitter.emit("status", { connected: true });
|
|
1784
1979
|
});
|
|
1785
1980
|
ws.on("message", async (data) => {
|
|
1786
1981
|
const message = JSON.parse(data.toString());
|
|
@@ -1810,12 +2005,10 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1810
2005
|
});
|
|
1811
2006
|
ws.on("close", () => {
|
|
1812
2007
|
console.log(pc2__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1813
|
-
statusEmitter.emit("status", { connected: false });
|
|
1814
2008
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1815
2009
|
});
|
|
1816
2010
|
ws.on("error", (error) => {
|
|
1817
2011
|
console.error(pc2__default.default.red(`WebSocket error: ${error.message}`));
|
|
1818
|
-
statusEmitter.emit("status", { connected: false });
|
|
1819
2012
|
});
|
|
1820
2013
|
return ws;
|
|
1821
2014
|
}
|
|
@@ -1824,6 +2017,7 @@ async function main() {
|
|
|
1824
2017
|
console.log(pc2__default.default.green("Starting local amai..."));
|
|
1825
2018
|
console.log(pc2__default.default.gray(`Connecting to server at ${serverUrl}`));
|
|
1826
2019
|
const connection = connectToServer2(serverUrl);
|
|
2020
|
+
await connectToUserStreams(serverUrl);
|
|
1827
2021
|
startHttpServer(connection);
|
|
1828
2022
|
}
|
|
1829
2023
|
var execAsync2 = util.promisify(child_process.exec);
|
package/dist/lib/daemon-entry.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import { EventEmitter } from 'events';
|
|
2
|
+
import WebSocket2 from 'ws';
|
|
4
3
|
import { z } from 'zod';
|
|
5
4
|
import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
6
5
|
import path9 from 'path';
|
|
@@ -1411,15 +1410,6 @@ var startHttpServer = (connection) => {
|
|
|
1411
1410
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1412
1411
|
|
|
1413
1412
|
`));
|
|
1414
|
-
const statusHandler = (data) => {
|
|
1415
|
-
try {
|
|
1416
|
-
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1417
|
-
|
|
1418
|
-
`));
|
|
1419
|
-
} catch {
|
|
1420
|
-
}
|
|
1421
|
-
};
|
|
1422
|
-
statusEmitter.on("status", statusHandler);
|
|
1423
1413
|
const heartbeatInterval = setInterval(() => {
|
|
1424
1414
|
try {
|
|
1425
1415
|
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
@@ -1430,7 +1420,6 @@ var startHttpServer = (connection) => {
|
|
|
1430
1420
|
}
|
|
1431
1421
|
}, 15e3);
|
|
1432
1422
|
c.req.raw.signal.addEventListener("abort", () => {
|
|
1433
|
-
statusEmitter.off("status", statusHandler);
|
|
1434
1423
|
clearInterval(heartbeatInterval);
|
|
1435
1424
|
});
|
|
1436
1425
|
}
|
|
@@ -1654,6 +1643,20 @@ function getTokens() {
|
|
|
1654
1643
|
const data = JSON.parse(raw);
|
|
1655
1644
|
return data;
|
|
1656
1645
|
}
|
|
1646
|
+
var getUserId = () => {
|
|
1647
|
+
try {
|
|
1648
|
+
if (!fs3.existsSync(CREDENTIALS_PATH)) {
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
const raw = fs3.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1652
|
+
const data = JSON.parse(raw);
|
|
1653
|
+
return {
|
|
1654
|
+
userId: data.user.id
|
|
1655
|
+
};
|
|
1656
|
+
} catch {
|
|
1657
|
+
throw new Error("Error while getting userId");
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1657
1660
|
var ExplanationSchema = z.object({
|
|
1658
1661
|
explanation: z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1659
1662
|
});
|
|
@@ -1742,8 +1745,201 @@ var runTerminalCommand = async (input, projectCwd) => {
|
|
|
1742
1745
|
}
|
|
1743
1746
|
};
|
|
1744
1747
|
|
|
1748
|
+
// src/lib/rpc-handlers.ts
|
|
1749
|
+
var rpcHandlers = {
|
|
1750
|
+
"daemon:get_workspace_folders": async () => {
|
|
1751
|
+
const projects = projectRegistry.list();
|
|
1752
|
+
return {
|
|
1753
|
+
folders: projects.map((p) => ({
|
|
1754
|
+
id: p.id,
|
|
1755
|
+
cwd: p.cwd,
|
|
1756
|
+
name: p.name,
|
|
1757
|
+
active: p.active
|
|
1758
|
+
}))
|
|
1759
|
+
};
|
|
1760
|
+
},
|
|
1761
|
+
"daemon:get_environment": async ({ gitRepositoryUrl }) => {
|
|
1762
|
+
const projects = projectRegistry.list();
|
|
1763
|
+
if (projects.length === 0) {
|
|
1764
|
+
const error = {
|
|
1765
|
+
_tag: "ProjectUnlinkedError",
|
|
1766
|
+
message: `Getting a local project by git repository URL "${gitRepositoryUrl}" returned an unlinked project. Please link it by running \`amai link <project name> <path to project directory>\``
|
|
1767
|
+
};
|
|
1768
|
+
throw error;
|
|
1769
|
+
}
|
|
1770
|
+
return {
|
|
1771
|
+
project: projects[0],
|
|
1772
|
+
env: {
|
|
1773
|
+
platform: process.platform,
|
|
1774
|
+
arch: process.arch,
|
|
1775
|
+
nodeVersion: process.version
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
},
|
|
1779
|
+
"daemon:get_context": async ({ cwd }) => {
|
|
1780
|
+
try {
|
|
1781
|
+
const files = getContext(cwd);
|
|
1782
|
+
return { files, cwd };
|
|
1783
|
+
} catch (error) {
|
|
1784
|
+
const rpcError = {
|
|
1785
|
+
_tag: "ContextError",
|
|
1786
|
+
message: error.message || "Failed to get context"
|
|
1787
|
+
};
|
|
1788
|
+
throw rpcError;
|
|
1789
|
+
}
|
|
1790
|
+
},
|
|
1791
|
+
"daemon:get_ide_projects": async () => {
|
|
1792
|
+
const projects = await scanIdeProjects();
|
|
1793
|
+
return { projects };
|
|
1794
|
+
},
|
|
1795
|
+
"daemon:register_project": async ({ projectId, cwd, name }) => {
|
|
1796
|
+
if (!projectId || !cwd) {
|
|
1797
|
+
const error = {
|
|
1798
|
+
_tag: "ValidationError",
|
|
1799
|
+
message: "projectId and cwd are required"
|
|
1800
|
+
};
|
|
1801
|
+
throw error;
|
|
1802
|
+
}
|
|
1803
|
+
projectRegistry.register(projectId, cwd, name);
|
|
1804
|
+
return { success: true, projectId, cwd };
|
|
1805
|
+
},
|
|
1806
|
+
"daemon:unregister_project": async ({ projectId }) => {
|
|
1807
|
+
if (!projectId) {
|
|
1808
|
+
const error = {
|
|
1809
|
+
_tag: "ValidationError",
|
|
1810
|
+
message: "projectId is required"
|
|
1811
|
+
};
|
|
1812
|
+
throw error;
|
|
1813
|
+
}
|
|
1814
|
+
projectRegistry.unregister(projectId);
|
|
1815
|
+
return { success: true, projectId };
|
|
1816
|
+
},
|
|
1817
|
+
"daemon:get_project": async ({ projectId }) => {
|
|
1818
|
+
const project = projectRegistry.getProject(projectId);
|
|
1819
|
+
if (!project) {
|
|
1820
|
+
const error = {
|
|
1821
|
+
_tag: "ProjectNotFoundError",
|
|
1822
|
+
message: `Project not found: ${projectId}`
|
|
1823
|
+
};
|
|
1824
|
+
throw error;
|
|
1825
|
+
}
|
|
1826
|
+
return { project };
|
|
1827
|
+
},
|
|
1828
|
+
"daemon:list_projects": async () => {
|
|
1829
|
+
const projects = projectRegistry.list();
|
|
1830
|
+
return { projects };
|
|
1831
|
+
},
|
|
1832
|
+
"daemon:status": async () => {
|
|
1833
|
+
return {
|
|
1834
|
+
connected: true,
|
|
1835
|
+
timestamp: Date.now(),
|
|
1836
|
+
platform: process.platform,
|
|
1837
|
+
arch: process.arch
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
var reconnectTimeout = null;
|
|
1842
|
+
var connectToUserStreams = async (serverUrl) => {
|
|
1843
|
+
const userId = getUserId();
|
|
1844
|
+
if (!userId?.userId) {
|
|
1845
|
+
throw new Error("User ID not found");
|
|
1846
|
+
}
|
|
1847
|
+
const params = new URLSearchParams({
|
|
1848
|
+
userId: userId.userId,
|
|
1849
|
+
type: "cli"
|
|
1850
|
+
});
|
|
1851
|
+
const tokens = getTokens();
|
|
1852
|
+
if (!tokens) {
|
|
1853
|
+
throw new Error("No tokens found");
|
|
1854
|
+
}
|
|
1855
|
+
const wsUrl = `${serverUrl}/api/v1/user-streams?${params.toString()}`;
|
|
1856
|
+
const ws = new WebSocket2(wsUrl, {
|
|
1857
|
+
headers: {
|
|
1858
|
+
Authorization: `Bearer ${tokens.access_token}`
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
ws.on("open", () => {
|
|
1862
|
+
console.log(pc2.green("CLI connected to user-streams"));
|
|
1863
|
+
if (reconnectTimeout) {
|
|
1864
|
+
clearTimeout(reconnectTimeout);
|
|
1865
|
+
reconnectTimeout = null;
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
ws.on("message", async (event) => {
|
|
1869
|
+
try {
|
|
1870
|
+
const message = JSON.parse(event.toString());
|
|
1871
|
+
if (message._tag === "rpc_call") {
|
|
1872
|
+
const { requestId, method, input } = message;
|
|
1873
|
+
console.log(pc2.gray(`RPC call: ${method}`));
|
|
1874
|
+
const handler = rpcHandlers[method];
|
|
1875
|
+
if (!handler) {
|
|
1876
|
+
ws.send(JSON.stringify({
|
|
1877
|
+
_tag: "rpc_result",
|
|
1878
|
+
requestId,
|
|
1879
|
+
data: {
|
|
1880
|
+
_tag: "UnknownMethodError",
|
|
1881
|
+
message: `Unknown RPC method: ${method}`
|
|
1882
|
+
}
|
|
1883
|
+
}));
|
|
1884
|
+
console.log(pc2.yellow(`Unknown RPC method: ${method}`));
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
try {
|
|
1888
|
+
const result = await handler(input || {});
|
|
1889
|
+
ws.send(JSON.stringify({
|
|
1890
|
+
_tag: "rpc_result",
|
|
1891
|
+
requestId,
|
|
1892
|
+
data: result
|
|
1893
|
+
}));
|
|
1894
|
+
console.log(pc2.green(`RPC completed: ${method}`));
|
|
1895
|
+
} catch (error) {
|
|
1896
|
+
const rpcError = error._tag ? error : {
|
|
1897
|
+
_tag: "RpcError",
|
|
1898
|
+
message: error.message || String(error)
|
|
1899
|
+
};
|
|
1900
|
+
ws.send(JSON.stringify({
|
|
1901
|
+
_tag: "rpc_result",
|
|
1902
|
+
requestId,
|
|
1903
|
+
data: rpcError
|
|
1904
|
+
}));
|
|
1905
|
+
console.log(pc2.red(`RPC failed: ${method} - ${rpcError.message}`));
|
|
1906
|
+
}
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
if (message.type === "presence_request") {
|
|
1910
|
+
if (message.status === "connected") {
|
|
1911
|
+
ws.send(JSON.stringify({
|
|
1912
|
+
type: "presence_request",
|
|
1913
|
+
status: "connected"
|
|
1914
|
+
}));
|
|
1915
|
+
}
|
|
1916
|
+
if (message.status === "disconnected") {
|
|
1917
|
+
ws.send(JSON.stringify({
|
|
1918
|
+
type: "presence_request",
|
|
1919
|
+
status: "disconnected"
|
|
1920
|
+
}));
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
} catch (parseError) {
|
|
1924
|
+
console.error(pc2.red(`Failed to parse message: ${parseError}`));
|
|
1925
|
+
}
|
|
1926
|
+
});
|
|
1927
|
+
ws.on("close", (code, reason) => {
|
|
1928
|
+
console.log(pc2.yellow(`CLI disconnected from user-streams (code: ${code})`));
|
|
1929
|
+
console.log(pc2.gray("Reconnecting in 5 seconds..."));
|
|
1930
|
+
reconnectTimeout = setTimeout(() => {
|
|
1931
|
+
connectToUserStreams(serverUrl).catch((err) => {
|
|
1932
|
+
console.error(pc2.red(`Reconnection failed: ${err.message}`));
|
|
1933
|
+
});
|
|
1934
|
+
}, 5e3);
|
|
1935
|
+
});
|
|
1936
|
+
ws.on("error", (error) => {
|
|
1937
|
+
console.error(pc2.red(`User streams WebSocket error: ${error.message}`));
|
|
1938
|
+
});
|
|
1939
|
+
return ws;
|
|
1940
|
+
};
|
|
1941
|
+
|
|
1745
1942
|
// src/server.ts
|
|
1746
|
-
var statusEmitter = new EventEmitter();
|
|
1747
1943
|
var toolExecutors = {
|
|
1748
1944
|
editFile: editFiles,
|
|
1749
1945
|
deleteFile,
|
|
@@ -1755,7 +1951,7 @@ var toolExecutors = {
|
|
|
1755
1951
|
runTerminalCommand
|
|
1756
1952
|
};
|
|
1757
1953
|
function getConnectionStatus(ws) {
|
|
1758
|
-
return ws.readyState ===
|
|
1954
|
+
return ws.readyState === WebSocket2.CONNECTING ? "connecting" : ws.readyState === WebSocket2.OPEN ? "open" : ws.readyState === WebSocket2.CLOSING ? "closing" : "closed";
|
|
1759
1955
|
}
|
|
1760
1956
|
function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
1761
1957
|
const tokens = getTokens();
|
|
@@ -1763,14 +1959,13 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1763
1959
|
throw new Error("No tokens found");
|
|
1764
1960
|
}
|
|
1765
1961
|
const wsUrl = `${serverUrl}/agent-streams`;
|
|
1766
|
-
const ws = new
|
|
1962
|
+
const ws = new WebSocket2(wsUrl, {
|
|
1767
1963
|
headers: {
|
|
1768
1964
|
"Authorization": `Bearer ${tokens.access_token}`
|
|
1769
1965
|
}
|
|
1770
1966
|
});
|
|
1771
1967
|
ws.on("open", () => {
|
|
1772
1968
|
console.log(pc2.green("Connected to server agent streams"));
|
|
1773
|
-
statusEmitter.emit("status", { connected: true });
|
|
1774
1969
|
});
|
|
1775
1970
|
ws.on("message", async (data) => {
|
|
1776
1971
|
const message = JSON.parse(data.toString());
|
|
@@ -1800,12 +1995,10 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1800
1995
|
});
|
|
1801
1996
|
ws.on("close", () => {
|
|
1802
1997
|
console.log(pc2.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1803
|
-
statusEmitter.emit("status", { connected: false });
|
|
1804
1998
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1805
1999
|
});
|
|
1806
2000
|
ws.on("error", (error) => {
|
|
1807
2001
|
console.error(pc2.red(`WebSocket error: ${error.message}`));
|
|
1808
|
-
statusEmitter.emit("status", { connected: false });
|
|
1809
2002
|
});
|
|
1810
2003
|
return ws;
|
|
1811
2004
|
}
|
|
@@ -1814,6 +2007,7 @@ async function main() {
|
|
|
1814
2007
|
console.log(pc2.green("Starting local amai..."));
|
|
1815
2008
|
console.log(pc2.gray(`Connecting to server at ${serverUrl}`));
|
|
1816
2009
|
const connection = connectToServer2(serverUrl);
|
|
2010
|
+
await connectToUserStreams(serverUrl);
|
|
1817
2011
|
startHttpServer(connection);
|
|
1818
2012
|
}
|
|
1819
2013
|
var execAsync2 = promisify(exec);
|