amai 0.0.1 → 0.0.3
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/README.md +47 -26
- package/dist/cli.cjs +64 -19
- package/dist/cli.js +64 -19
- package/dist/lib/daemon-entry.cjs +49 -4
- package/dist/lib/daemon-entry.js +49 -4
- package/dist/server.cjs +50 -4
- package/dist/server.d.cts +3 -1
- package/dist/server.d.ts +3 -1
- package/dist/server.js +50 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AMAI CLI
|
|
2
2
|
|
|
3
|
-
A CLI tool that connects to your
|
|
3
|
+
A CLI tool that connects to your backend server to execute tool calls locally on your machine.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
### Option 1: Install
|
|
7
|
+
### Option 1: Install from npm (Recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g amai
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Option 2: Install Globally from Source
|
|
8
14
|
|
|
9
15
|
```bash
|
|
10
16
|
# From the ama-agent package directory
|
|
@@ -22,7 +28,7 @@ bun link
|
|
|
22
28
|
./install-global.sh
|
|
23
29
|
```
|
|
24
30
|
|
|
25
|
-
### Option
|
|
31
|
+
### Option 3: Link Locally to Another Project
|
|
26
32
|
|
|
27
33
|
```bash
|
|
28
34
|
# In your other project directory
|
|
@@ -31,31 +37,33 @@ npm link /path/to/ama/packages/ama-agent
|
|
|
31
37
|
bun link /path/to/ama/packages/ama-agent
|
|
32
38
|
```
|
|
33
39
|
|
|
34
|
-
### Option
|
|
40
|
+
### Option 4: Install from Local Path
|
|
35
41
|
|
|
36
42
|
In your project's `package.json`:
|
|
37
43
|
```json
|
|
38
44
|
{
|
|
39
45
|
"dependencies": {
|
|
40
|
-
"
|
|
46
|
+
"amai": "file:../path/to/ama/packages/ama-agent"
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
49
|
```
|
|
44
50
|
Then run `npm install` or `bun install`.
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
## Usage
|
|
47
53
|
|
|
48
54
|
```bash
|
|
49
|
-
|
|
50
|
-
npm publish
|
|
51
|
-
# Then in your other project: npm install ama-agent
|
|
55
|
+
amai [command] [options]
|
|
52
56
|
```
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
### Commands
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
- `login` - Authorize device
|
|
61
|
+
- `logout` - Log out and remove credentials
|
|
62
|
+
- `start` - Start background daemon (recommended for better performance)
|
|
63
|
+
- `stop` - Stop background daemon
|
|
64
|
+
- `status` - Check daemon status
|
|
65
|
+
- `project add <path>` - Register a project directory
|
|
66
|
+
- `project list` - List registered projects
|
|
59
67
|
|
|
60
68
|
### Options
|
|
61
69
|
|
|
@@ -63,26 +71,37 @@ ama-agent [options]
|
|
|
63
71
|
|
|
64
72
|
### Environment Variables
|
|
65
73
|
|
|
66
|
-
- `SERVER_URL` - WebSocket server URL to connect to (
|
|
74
|
+
- `SERVER_URL` - WebSocket server URL to connect to (optional, has a default)
|
|
67
75
|
- Example: `ws://localhost:3000` or `wss://your-server.com`
|
|
68
76
|
|
|
69
77
|
### Examples
|
|
70
78
|
|
|
71
79
|
```bash
|
|
72
|
-
#
|
|
73
|
-
|
|
80
|
+
# Login first
|
|
81
|
+
amai login
|
|
74
82
|
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
# Start in background mode (recommended)
|
|
84
|
+
amai start
|
|
85
|
+
|
|
86
|
+
# Check daemon status
|
|
87
|
+
amai status
|
|
88
|
+
|
|
89
|
+
# Stop daemon
|
|
90
|
+
amai stop
|
|
91
|
+
|
|
92
|
+
# Register a project
|
|
93
|
+
amai project add /path/to/your/project
|
|
94
|
+
|
|
95
|
+
# List registered projects
|
|
96
|
+
amai project list
|
|
78
97
|
|
|
79
98
|
# Show help
|
|
80
|
-
|
|
99
|
+
amai --help
|
|
81
100
|
```
|
|
82
101
|
|
|
83
102
|
## How It Works
|
|
84
103
|
|
|
85
|
-
1. The CLI connects to a WebSocket server
|
|
104
|
+
1. The CLI connects to a WebSocket server
|
|
86
105
|
2. It listens for tool call messages from the server
|
|
87
106
|
3. When tool calls are received, it executes them locally on your machine
|
|
88
107
|
4. Results are sent back to the server via WebSocket
|
|
@@ -102,13 +121,15 @@ The built files will be in the `dist/` directory.
|
|
|
102
121
|
|
|
103
122
|
## Troubleshooting
|
|
104
123
|
|
|
105
|
-
### "SERVER_URL is required" error
|
|
106
|
-
|
|
107
|
-
Make sure to provide the server URL via the `SERVER_URL` environment variable.
|
|
108
|
-
|
|
109
124
|
### Connection issues
|
|
110
125
|
|
|
111
126
|
- Check that the server is running and accessible
|
|
112
127
|
- Verify the WebSocket URL format (should start with `ws://` or `wss://`)
|
|
113
128
|
- Check firewall/network settings
|
|
114
129
|
- The CLI will automatically attempt to reconnect every 5 seconds if disconnected
|
|
130
|
+
|
|
131
|
+
### Authentication issues
|
|
132
|
+
|
|
133
|
+
- Run `amai login` to authenticate
|
|
134
|
+
- Credentials are stored in `~/.amai/credentials.json`
|
|
135
|
+
- Run `amai logout` to clear credentials and re-authenticate
|
package/dist/cli.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
var pc4 = require('picocolors');
|
|
5
5
|
var WebSocket = require('ws');
|
|
6
|
+
var events = require('events');
|
|
6
7
|
var zod = require('zod');
|
|
7
8
|
var promises = require('fs/promises');
|
|
8
9
|
var path9 = require('path');
|
|
@@ -28,7 +29,7 @@ var readline__default = /*#__PURE__*/_interopDefault(readline);
|
|
|
28
29
|
|
|
29
30
|
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
30
31
|
var CLIENT_ID = "client_01K4Y8A67H544Z6J8A47E5GJ9A";
|
|
31
|
-
var AMA_DIR = path9__default.default.join(os2__default.default.homedir(), ".
|
|
32
|
+
var AMA_DIR = path9__default.default.join(os2__default.default.homedir(), ".amai");
|
|
32
33
|
var CODE_DIR = path9__default.default.join(AMA_DIR, "code");
|
|
33
34
|
var STORAGE_DIR = path9__default.default.join(AMA_DIR, "storage");
|
|
34
35
|
|
|
@@ -1261,10 +1262,50 @@ var startHttpServer = (connection) => {
|
|
|
1261
1262
|
}
|
|
1262
1263
|
const app = new hono.Hono();
|
|
1263
1264
|
app.use(cors.cors());
|
|
1264
|
-
app.post("/daemon/status
|
|
1265
|
+
app.post("/daemon/status", (c) => {
|
|
1265
1266
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1266
1267
|
return c.json({ connected: status === "open" });
|
|
1267
1268
|
});
|
|
1269
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1270
|
+
const encoder = new TextEncoder();
|
|
1271
|
+
const stream = new ReadableStream({
|
|
1272
|
+
start(controller) {
|
|
1273
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
const statusHandler = (data) => {
|
|
1278
|
+
try {
|
|
1279
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1280
|
+
|
|
1281
|
+
`));
|
|
1282
|
+
} catch {
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
statusEmitter.on("status", statusHandler);
|
|
1286
|
+
const heartbeatInterval = setInterval(() => {
|
|
1287
|
+
try {
|
|
1288
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1289
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1290
|
+
|
|
1291
|
+
`));
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
}, 15e3);
|
|
1295
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1296
|
+
statusEmitter.off("status", statusHandler);
|
|
1297
|
+
clearInterval(heartbeatInterval);
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
return new Response(stream, {
|
|
1302
|
+
headers: {
|
|
1303
|
+
"Content-Type": "text/event-stream",
|
|
1304
|
+
"Cache-Control": "no-cache",
|
|
1305
|
+
"Connection": "keep-alive"
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
});
|
|
1268
1309
|
app.get("context", async (c) => {
|
|
1269
1310
|
const context = getContext(process.cwd());
|
|
1270
1311
|
return c.body(JSON.stringify(context));
|
|
@@ -1357,7 +1398,7 @@ var startHttpServer = (connection) => {
|
|
|
1357
1398
|
nodeServer.serve({ fetch: app.fetch, port: 3456 });
|
|
1358
1399
|
};
|
|
1359
1400
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1360
|
-
var CREDENTIALS_DIR = path9__default.default.join(os2__default.default.homedir(), ".
|
|
1401
|
+
var CREDENTIALS_DIR = path9__default.default.join(os2__default.default.homedir(), ".amai");
|
|
1361
1402
|
var CREDENTIALS_PATH = path9__default.default.join(CREDENTIALS_DIR, "credentials.json");
|
|
1362
1403
|
function isAuthenticated() {
|
|
1363
1404
|
try {
|
|
@@ -1508,6 +1549,7 @@ async function login() {
|
|
|
1508
1549
|
}
|
|
1509
1550
|
|
|
1510
1551
|
// src/server.ts
|
|
1552
|
+
var statusEmitter = new events.EventEmitter();
|
|
1511
1553
|
var toolExecutors = {
|
|
1512
1554
|
editFile: editFiles,
|
|
1513
1555
|
deleteFile,
|
|
@@ -1533,6 +1575,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1533
1575
|
});
|
|
1534
1576
|
ws.on("open", () => {
|
|
1535
1577
|
console.log(pc4__default.default.green("Connected to server agent streams"));
|
|
1578
|
+
statusEmitter.emit("status", { connected: true });
|
|
1536
1579
|
});
|
|
1537
1580
|
ws.on("message", async (data) => {
|
|
1538
1581
|
const message = JSON.parse(data.toString());
|
|
@@ -1562,16 +1605,18 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1562
1605
|
});
|
|
1563
1606
|
ws.on("close", () => {
|
|
1564
1607
|
console.log(pc4__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1608
|
+
statusEmitter.emit("status", { connected: false });
|
|
1565
1609
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1566
1610
|
});
|
|
1567
1611
|
ws.on("error", (error) => {
|
|
1568
1612
|
console.error(pc4__default.default.red(`WebSocket error: ${error.message}`));
|
|
1613
|
+
statusEmitter.emit("status", { connected: false });
|
|
1569
1614
|
});
|
|
1570
1615
|
return ws;
|
|
1571
1616
|
}
|
|
1572
1617
|
async function main() {
|
|
1573
1618
|
const serverUrl = DEFAULT_SERVER_URL;
|
|
1574
|
-
console.log(pc4__default.default.green("Starting local
|
|
1619
|
+
console.log(pc4__default.default.green("Starting local amai..."));
|
|
1575
1620
|
console.log(pc4__default.default.gray(`Connecting to server at ${serverUrl}`));
|
|
1576
1621
|
const connection = connectToServer2(serverUrl);
|
|
1577
1622
|
startHttpServer(connection);
|
|
@@ -1762,7 +1807,7 @@ function getDaemonPid() {
|
|
|
1762
1807
|
return null;
|
|
1763
1808
|
}
|
|
1764
1809
|
}
|
|
1765
|
-
var VERSION = "0.0.
|
|
1810
|
+
var VERSION = "0.0.3";
|
|
1766
1811
|
var PROJECT_DIR = process.cwd();
|
|
1767
1812
|
function promptUser(question) {
|
|
1768
1813
|
const rl = readline__default.default.createInterface({
|
|
@@ -1798,9 +1843,9 @@ async function startWithCodeServer() {
|
|
|
1798
1843
|
var args = process.argv.slice(2);
|
|
1799
1844
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
1800
1845
|
console.log(`
|
|
1801
|
-
${pc4__default.default.bold("
|
|
1846
|
+
${pc4__default.default.bold("amai cli")} ${pc4__default.default.gray(VERSION)}
|
|
1802
1847
|
|
|
1803
|
-
Usage:
|
|
1848
|
+
Usage: amai [command] [options]
|
|
1804
1849
|
|
|
1805
1850
|
Commands:
|
|
1806
1851
|
login Authorize device
|
|
@@ -1817,10 +1862,10 @@ Environment Variables:
|
|
|
1817
1862
|
SERVER_URL Server URL to connect to
|
|
1818
1863
|
|
|
1819
1864
|
Example:
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1865
|
+
amai login
|
|
1866
|
+
amai start
|
|
1867
|
+
amai project add /path/to/project
|
|
1868
|
+
amai Start the agent (will prompt for background mode)
|
|
1824
1869
|
`);
|
|
1825
1870
|
process.exit(0);
|
|
1826
1871
|
}
|
|
@@ -1842,8 +1887,8 @@ if (args[0] === "start") {
|
|
|
1842
1887
|
});
|
|
1843
1888
|
} else {
|
|
1844
1889
|
startDaemon();
|
|
1845
|
-
console.log(pc4__default.default.green(pc4__default.default.bold("
|
|
1846
|
-
console.log(pc4__default.default.gray(`Tip: You can check status any time with ${pc4__default.default.bold("
|
|
1890
|
+
console.log(pc4__default.default.green(pc4__default.default.bold("amai started in background mode")));
|
|
1891
|
+
console.log(pc4__default.default.gray(`Tip: You can check status any time with ${pc4__default.default.bold("amai status")}`));
|
|
1847
1892
|
process.exit(0);
|
|
1848
1893
|
}
|
|
1849
1894
|
}
|
|
@@ -1870,7 +1915,7 @@ if (args[0] === "project") {
|
|
|
1870
1915
|
const projectPath = args[2];
|
|
1871
1916
|
if (!projectPath) {
|
|
1872
1917
|
console.error(pc4__default.default.red("Please provide a project path"));
|
|
1873
|
-
console.log("Usage:
|
|
1918
|
+
console.log("Usage: amai project add <path>");
|
|
1874
1919
|
process.exit(1);
|
|
1875
1920
|
}
|
|
1876
1921
|
const resolvedPath = path9__default.default.resolve(projectPath);
|
|
@@ -1899,7 +1944,7 @@ if (args[0] === "project") {
|
|
|
1899
1944
|
process.exit(0);
|
|
1900
1945
|
} else {
|
|
1901
1946
|
console.error(pc4__default.default.red(`Unknown project command: ${args[1]}`));
|
|
1902
|
-
console.log('Use "
|
|
1947
|
+
console.log('Use "amai project add <path>" or "amai project list"');
|
|
1903
1948
|
process.exit(1);
|
|
1904
1949
|
}
|
|
1905
1950
|
}
|
|
@@ -1921,11 +1966,11 @@ if (args[0] === "login" || args[0] === "--login") {
|
|
|
1921
1966
|
}
|
|
1922
1967
|
}
|
|
1923
1968
|
if (isDaemonRunning()) {
|
|
1924
|
-
console.log(pc4__default.default.yellow('Daemon is already running. Use "
|
|
1969
|
+
console.log(pc4__default.default.yellow('Daemon is already running. Use "amai status" to check its status.'));
|
|
1925
1970
|
process.exit(0);
|
|
1926
1971
|
}
|
|
1927
1972
|
console.log("");
|
|
1928
|
-
console.log(pc4__default.default.bold("How would you like to run
|
|
1973
|
+
console.log(pc4__default.default.bold("How would you like to run amai?"));
|
|
1929
1974
|
console.log(pc4__default.default.gray("Background mode is highly recommended for better performance and stability."));
|
|
1930
1975
|
const answer = await promptUser(
|
|
1931
1976
|
pc4__default.default.cyan("Run in background? (Y/n): ")
|
|
@@ -1935,8 +1980,8 @@ if (args[0] === "login" || args[0] === "--login") {
|
|
|
1935
1980
|
console.log(pc4__default.default.green("Starting daemon in background..."));
|
|
1936
1981
|
startDaemon();
|
|
1937
1982
|
console.log(pc4__default.default.green("Daemon started successfully!"));
|
|
1938
|
-
console.log(pc4__default.default.gray('Use "
|
|
1939
|
-
console.log(pc4__default.default.gray('Use "
|
|
1983
|
+
console.log(pc4__default.default.gray('Use "amai status" to check daemon status.'));
|
|
1984
|
+
console.log(pc4__default.default.gray('Use "amai stop" to stop the daemon.'));
|
|
1940
1985
|
process.exit(0);
|
|
1941
1986
|
} else {
|
|
1942
1987
|
console.log(pc4__default.default.yellow("Starting in foreground mode..."));
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import pc4 from 'picocolors';
|
|
3
3
|
import WebSocket from 'ws';
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
6
7
|
import path9, { dirname } from 'path';
|
|
@@ -16,7 +17,7 @@ import readline from 'readline';
|
|
|
16
17
|
|
|
17
18
|
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
18
19
|
var CLIENT_ID = "client_01K4Y8A67H544Z6J8A47E5GJ9A";
|
|
19
|
-
var AMA_DIR = path9.join(os2.homedir(), ".
|
|
20
|
+
var AMA_DIR = path9.join(os2.homedir(), ".amai");
|
|
20
21
|
var CODE_DIR = path9.join(AMA_DIR, "code");
|
|
21
22
|
var STORAGE_DIR = path9.join(AMA_DIR, "storage");
|
|
22
23
|
|
|
@@ -1249,10 +1250,50 @@ var startHttpServer = (connection) => {
|
|
|
1249
1250
|
}
|
|
1250
1251
|
const app = new Hono();
|
|
1251
1252
|
app.use(cors());
|
|
1252
|
-
app.post("/daemon/status
|
|
1253
|
+
app.post("/daemon/status", (c) => {
|
|
1253
1254
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1254
1255
|
return c.json({ connected: status === "open" });
|
|
1255
1256
|
});
|
|
1257
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1258
|
+
const encoder = new TextEncoder();
|
|
1259
|
+
const stream = new ReadableStream({
|
|
1260
|
+
start(controller) {
|
|
1261
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1262
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1263
|
+
|
|
1264
|
+
`));
|
|
1265
|
+
const statusHandler = (data) => {
|
|
1266
|
+
try {
|
|
1267
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1268
|
+
|
|
1269
|
+
`));
|
|
1270
|
+
} catch {
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
statusEmitter.on("status", statusHandler);
|
|
1274
|
+
const heartbeatInterval = setInterval(() => {
|
|
1275
|
+
try {
|
|
1276
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1277
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1278
|
+
|
|
1279
|
+
`));
|
|
1280
|
+
} catch {
|
|
1281
|
+
}
|
|
1282
|
+
}, 15e3);
|
|
1283
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1284
|
+
statusEmitter.off("status", statusHandler);
|
|
1285
|
+
clearInterval(heartbeatInterval);
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
return new Response(stream, {
|
|
1290
|
+
headers: {
|
|
1291
|
+
"Content-Type": "text/event-stream",
|
|
1292
|
+
"Cache-Control": "no-cache",
|
|
1293
|
+
"Connection": "keep-alive"
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
});
|
|
1256
1297
|
app.get("context", async (c) => {
|
|
1257
1298
|
const context = getContext(process.cwd());
|
|
1258
1299
|
return c.body(JSON.stringify(context));
|
|
@@ -1345,7 +1386,7 @@ var startHttpServer = (connection) => {
|
|
|
1345
1386
|
serve({ fetch: app.fetch, port: 3456 });
|
|
1346
1387
|
};
|
|
1347
1388
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1348
|
-
var CREDENTIALS_DIR = path9.join(os2.homedir(), ".
|
|
1389
|
+
var CREDENTIALS_DIR = path9.join(os2.homedir(), ".amai");
|
|
1349
1390
|
var CREDENTIALS_PATH = path9.join(CREDENTIALS_DIR, "credentials.json");
|
|
1350
1391
|
function isAuthenticated() {
|
|
1351
1392
|
try {
|
|
@@ -1496,6 +1537,7 @@ async function login() {
|
|
|
1496
1537
|
}
|
|
1497
1538
|
|
|
1498
1539
|
// src/server.ts
|
|
1540
|
+
var statusEmitter = new EventEmitter();
|
|
1499
1541
|
var toolExecutors = {
|
|
1500
1542
|
editFile: editFiles,
|
|
1501
1543
|
deleteFile,
|
|
@@ -1521,6 +1563,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1521
1563
|
});
|
|
1522
1564
|
ws.on("open", () => {
|
|
1523
1565
|
console.log(pc4.green("Connected to server agent streams"));
|
|
1566
|
+
statusEmitter.emit("status", { connected: true });
|
|
1524
1567
|
});
|
|
1525
1568
|
ws.on("message", async (data) => {
|
|
1526
1569
|
const message = JSON.parse(data.toString());
|
|
@@ -1550,16 +1593,18 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1550
1593
|
});
|
|
1551
1594
|
ws.on("close", () => {
|
|
1552
1595
|
console.log(pc4.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1596
|
+
statusEmitter.emit("status", { connected: false });
|
|
1553
1597
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1554
1598
|
});
|
|
1555
1599
|
ws.on("error", (error) => {
|
|
1556
1600
|
console.error(pc4.red(`WebSocket error: ${error.message}`));
|
|
1601
|
+
statusEmitter.emit("status", { connected: false });
|
|
1557
1602
|
});
|
|
1558
1603
|
return ws;
|
|
1559
1604
|
}
|
|
1560
1605
|
async function main() {
|
|
1561
1606
|
const serverUrl = DEFAULT_SERVER_URL;
|
|
1562
|
-
console.log(pc4.green("Starting local
|
|
1607
|
+
console.log(pc4.green("Starting local amai..."));
|
|
1563
1608
|
console.log(pc4.gray(`Connecting to server at ${serverUrl}`));
|
|
1564
1609
|
const connection = connectToServer2(serverUrl);
|
|
1565
1610
|
startHttpServer(connection);
|
|
@@ -1750,7 +1795,7 @@ function getDaemonPid() {
|
|
|
1750
1795
|
return null;
|
|
1751
1796
|
}
|
|
1752
1797
|
}
|
|
1753
|
-
var VERSION = "0.0.
|
|
1798
|
+
var VERSION = "0.0.3";
|
|
1754
1799
|
var PROJECT_DIR = process.cwd();
|
|
1755
1800
|
function promptUser(question) {
|
|
1756
1801
|
const rl = readline.createInterface({
|
|
@@ -1786,9 +1831,9 @@ async function startWithCodeServer() {
|
|
|
1786
1831
|
var args = process.argv.slice(2);
|
|
1787
1832
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
1788
1833
|
console.log(`
|
|
1789
|
-
${pc4.bold("
|
|
1834
|
+
${pc4.bold("amai cli")} ${pc4.gray(VERSION)}
|
|
1790
1835
|
|
|
1791
|
-
Usage:
|
|
1836
|
+
Usage: amai [command] [options]
|
|
1792
1837
|
|
|
1793
1838
|
Commands:
|
|
1794
1839
|
login Authorize device
|
|
@@ -1805,10 +1850,10 @@ Environment Variables:
|
|
|
1805
1850
|
SERVER_URL Server URL to connect to
|
|
1806
1851
|
|
|
1807
1852
|
Example:
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1853
|
+
amai login
|
|
1854
|
+
amai start
|
|
1855
|
+
amai project add /path/to/project
|
|
1856
|
+
amai Start the agent (will prompt for background mode)
|
|
1812
1857
|
`);
|
|
1813
1858
|
process.exit(0);
|
|
1814
1859
|
}
|
|
@@ -1830,8 +1875,8 @@ if (args[0] === "start") {
|
|
|
1830
1875
|
});
|
|
1831
1876
|
} else {
|
|
1832
1877
|
startDaemon();
|
|
1833
|
-
console.log(pc4.green(pc4.bold("
|
|
1834
|
-
console.log(pc4.gray(`Tip: You can check status any time with ${pc4.bold("
|
|
1878
|
+
console.log(pc4.green(pc4.bold("amai started in background mode")));
|
|
1879
|
+
console.log(pc4.gray(`Tip: You can check status any time with ${pc4.bold("amai status")}`));
|
|
1835
1880
|
process.exit(0);
|
|
1836
1881
|
}
|
|
1837
1882
|
}
|
|
@@ -1858,7 +1903,7 @@ if (args[0] === "project") {
|
|
|
1858
1903
|
const projectPath = args[2];
|
|
1859
1904
|
if (!projectPath) {
|
|
1860
1905
|
console.error(pc4.red("Please provide a project path"));
|
|
1861
|
-
console.log("Usage:
|
|
1906
|
+
console.log("Usage: amai project add <path>");
|
|
1862
1907
|
process.exit(1);
|
|
1863
1908
|
}
|
|
1864
1909
|
const resolvedPath = path9.resolve(projectPath);
|
|
@@ -1887,7 +1932,7 @@ if (args[0] === "project") {
|
|
|
1887
1932
|
process.exit(0);
|
|
1888
1933
|
} else {
|
|
1889
1934
|
console.error(pc4.red(`Unknown project command: ${args[1]}`));
|
|
1890
|
-
console.log('Use "
|
|
1935
|
+
console.log('Use "amai project add <path>" or "amai project list"');
|
|
1891
1936
|
process.exit(1);
|
|
1892
1937
|
}
|
|
1893
1938
|
}
|
|
@@ -1909,11 +1954,11 @@ if (args[0] === "login" || args[0] === "--login") {
|
|
|
1909
1954
|
}
|
|
1910
1955
|
}
|
|
1911
1956
|
if (isDaemonRunning()) {
|
|
1912
|
-
console.log(pc4.yellow('Daemon is already running. Use "
|
|
1957
|
+
console.log(pc4.yellow('Daemon is already running. Use "amai status" to check its status.'));
|
|
1913
1958
|
process.exit(0);
|
|
1914
1959
|
}
|
|
1915
1960
|
console.log("");
|
|
1916
|
-
console.log(pc4.bold("How would you like to run
|
|
1961
|
+
console.log(pc4.bold("How would you like to run amai?"));
|
|
1917
1962
|
console.log(pc4.gray("Background mode is highly recommended for better performance and stability."));
|
|
1918
1963
|
const answer = await promptUser(
|
|
1919
1964
|
pc4.cyan("Run in background? (Y/n): ")
|
|
@@ -1923,8 +1968,8 @@ if (args[0] === "login" || args[0] === "--login") {
|
|
|
1923
1968
|
console.log(pc4.green("Starting daemon in background..."));
|
|
1924
1969
|
startDaemon();
|
|
1925
1970
|
console.log(pc4.green("Daemon started successfully!"));
|
|
1926
|
-
console.log(pc4.gray('Use "
|
|
1927
|
-
console.log(pc4.gray('Use "
|
|
1971
|
+
console.log(pc4.gray('Use "amai status" to check daemon status.'));
|
|
1972
|
+
console.log(pc4.gray('Use "amai stop" to stop the daemon.'));
|
|
1928
1973
|
process.exit(0);
|
|
1929
1974
|
} else {
|
|
1930
1975
|
console.log(pc4.yellow("Starting in foreground mode..."));
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var WebSocket = require('ws');
|
|
5
|
+
var events = require('events');
|
|
5
6
|
var zod = require('zod');
|
|
6
7
|
var promises = require('fs/promises');
|
|
7
8
|
var path9 = require('path');
|
|
@@ -23,7 +24,7 @@ var os2__default = /*#__PURE__*/_interopDefault(os2);
|
|
|
23
24
|
var pc2__default = /*#__PURE__*/_interopDefault(pc2);
|
|
24
25
|
|
|
25
26
|
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
26
|
-
var AMA_DIR = path9__default.default.join(os2__default.default.homedir(), ".
|
|
27
|
+
var AMA_DIR = path9__default.default.join(os2__default.default.homedir(), ".amai");
|
|
27
28
|
var CODE_DIR = path9__default.default.join(AMA_DIR, "code");
|
|
28
29
|
var STORAGE_DIR = path9__default.default.join(AMA_DIR, "storage");
|
|
29
30
|
|
|
@@ -1256,10 +1257,50 @@ var startHttpServer = (connection) => {
|
|
|
1256
1257
|
}
|
|
1257
1258
|
const app = new hono.Hono();
|
|
1258
1259
|
app.use(cors.cors());
|
|
1259
|
-
app.post("/daemon/status
|
|
1260
|
+
app.post("/daemon/status", (c) => {
|
|
1260
1261
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1261
1262
|
return c.json({ connected: status === "open" });
|
|
1262
1263
|
});
|
|
1264
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1265
|
+
const encoder = new TextEncoder();
|
|
1266
|
+
const stream = new ReadableStream({
|
|
1267
|
+
start(controller) {
|
|
1268
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1269
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1270
|
+
|
|
1271
|
+
`));
|
|
1272
|
+
const statusHandler = (data) => {
|
|
1273
|
+
try {
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
statusEmitter.on("status", statusHandler);
|
|
1281
|
+
const heartbeatInterval = setInterval(() => {
|
|
1282
|
+
try {
|
|
1283
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1284
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1285
|
+
|
|
1286
|
+
`));
|
|
1287
|
+
} catch {
|
|
1288
|
+
}
|
|
1289
|
+
}, 15e3);
|
|
1290
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1291
|
+
statusEmitter.off("status", statusHandler);
|
|
1292
|
+
clearInterval(heartbeatInterval);
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
return new Response(stream, {
|
|
1297
|
+
headers: {
|
|
1298
|
+
"Content-Type": "text/event-stream",
|
|
1299
|
+
"Cache-Control": "no-cache",
|
|
1300
|
+
"Connection": "keep-alive"
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1263
1304
|
app.get("context", async (c) => {
|
|
1264
1305
|
const context = getContext(process.cwd());
|
|
1265
1306
|
return c.body(JSON.stringify(context));
|
|
@@ -1351,7 +1392,7 @@ var startHttpServer = (connection) => {
|
|
|
1351
1392
|
});
|
|
1352
1393
|
nodeServer.serve({ fetch: app.fetch, port: 3456 });
|
|
1353
1394
|
};
|
|
1354
|
-
var CREDENTIALS_DIR = path9__default.default.join(os2__default.default.homedir(), ".
|
|
1395
|
+
var CREDENTIALS_DIR = path9__default.default.join(os2__default.default.homedir(), ".amai");
|
|
1355
1396
|
var CREDENTIALS_PATH = path9__default.default.join(CREDENTIALS_DIR, "credentials.json");
|
|
1356
1397
|
function getTokens() {
|
|
1357
1398
|
if (!fs3__default.default.existsSync(CREDENTIALS_PATH)) {
|
|
@@ -1363,6 +1404,7 @@ function getTokens() {
|
|
|
1363
1404
|
}
|
|
1364
1405
|
|
|
1365
1406
|
// src/server.ts
|
|
1407
|
+
var statusEmitter = new events.EventEmitter();
|
|
1366
1408
|
var toolExecutors = {
|
|
1367
1409
|
editFile: editFiles,
|
|
1368
1410
|
deleteFile,
|
|
@@ -1388,6 +1430,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1388
1430
|
});
|
|
1389
1431
|
ws.on("open", () => {
|
|
1390
1432
|
console.log(pc2__default.default.green("Connected to server agent streams"));
|
|
1433
|
+
statusEmitter.emit("status", { connected: true });
|
|
1391
1434
|
});
|
|
1392
1435
|
ws.on("message", async (data) => {
|
|
1393
1436
|
const message = JSON.parse(data.toString());
|
|
@@ -1417,16 +1460,18 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1417
1460
|
});
|
|
1418
1461
|
ws.on("close", () => {
|
|
1419
1462
|
console.log(pc2__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1463
|
+
statusEmitter.emit("status", { connected: false });
|
|
1420
1464
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1421
1465
|
});
|
|
1422
1466
|
ws.on("error", (error) => {
|
|
1423
1467
|
console.error(pc2__default.default.red(`WebSocket error: ${error.message}`));
|
|
1468
|
+
statusEmitter.emit("status", { connected: false });
|
|
1424
1469
|
});
|
|
1425
1470
|
return ws;
|
|
1426
1471
|
}
|
|
1427
1472
|
async function main() {
|
|
1428
1473
|
const serverUrl = DEFAULT_SERVER_URL;
|
|
1429
|
-
console.log(pc2__default.default.green("Starting local
|
|
1474
|
+
console.log(pc2__default.default.green("Starting local amai..."));
|
|
1430
1475
|
console.log(pc2__default.default.gray(`Connecting to server at ${serverUrl}`));
|
|
1431
1476
|
const connection = connectToServer2(serverUrl);
|
|
1432
1477
|
startHttpServer(connection);
|
package/dist/lib/daemon-entry.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
5
6
|
import path9 from 'path';
|
|
@@ -13,7 +14,7 @@ import { serve } from '@hono/node-server';
|
|
|
13
14
|
import { cors } from 'hono/cors';
|
|
14
15
|
|
|
15
16
|
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
16
|
-
var AMA_DIR = path9.join(os2.homedir(), ".
|
|
17
|
+
var AMA_DIR = path9.join(os2.homedir(), ".amai");
|
|
17
18
|
var CODE_DIR = path9.join(AMA_DIR, "code");
|
|
18
19
|
var STORAGE_DIR = path9.join(AMA_DIR, "storage");
|
|
19
20
|
|
|
@@ -1246,10 +1247,50 @@ var startHttpServer = (connection) => {
|
|
|
1246
1247
|
}
|
|
1247
1248
|
const app = new Hono();
|
|
1248
1249
|
app.use(cors());
|
|
1249
|
-
app.post("/daemon/status
|
|
1250
|
+
app.post("/daemon/status", (c) => {
|
|
1250
1251
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1251
1252
|
return c.json({ connected: status === "open" });
|
|
1252
1253
|
});
|
|
1254
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1255
|
+
const encoder = new TextEncoder();
|
|
1256
|
+
const stream = new ReadableStream({
|
|
1257
|
+
start(controller) {
|
|
1258
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1259
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1260
|
+
|
|
1261
|
+
`));
|
|
1262
|
+
const statusHandler = (data) => {
|
|
1263
|
+
try {
|
|
1264
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1265
|
+
|
|
1266
|
+
`));
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
statusEmitter.on("status", statusHandler);
|
|
1271
|
+
const heartbeatInterval = setInterval(() => {
|
|
1272
|
+
try {
|
|
1273
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
}, 15e3);
|
|
1280
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1281
|
+
statusEmitter.off("status", statusHandler);
|
|
1282
|
+
clearInterval(heartbeatInterval);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
return new Response(stream, {
|
|
1287
|
+
headers: {
|
|
1288
|
+
"Content-Type": "text/event-stream",
|
|
1289
|
+
"Cache-Control": "no-cache",
|
|
1290
|
+
"Connection": "keep-alive"
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1253
1294
|
app.get("context", async (c) => {
|
|
1254
1295
|
const context = getContext(process.cwd());
|
|
1255
1296
|
return c.body(JSON.stringify(context));
|
|
@@ -1341,7 +1382,7 @@ var startHttpServer = (connection) => {
|
|
|
1341
1382
|
});
|
|
1342
1383
|
serve({ fetch: app.fetch, port: 3456 });
|
|
1343
1384
|
};
|
|
1344
|
-
var CREDENTIALS_DIR = path9.join(os2.homedir(), ".
|
|
1385
|
+
var CREDENTIALS_DIR = path9.join(os2.homedir(), ".amai");
|
|
1345
1386
|
var CREDENTIALS_PATH = path9.join(CREDENTIALS_DIR, "credentials.json");
|
|
1346
1387
|
function getTokens() {
|
|
1347
1388
|
if (!fs3.existsSync(CREDENTIALS_PATH)) {
|
|
@@ -1353,6 +1394,7 @@ function getTokens() {
|
|
|
1353
1394
|
}
|
|
1354
1395
|
|
|
1355
1396
|
// src/server.ts
|
|
1397
|
+
var statusEmitter = new EventEmitter();
|
|
1356
1398
|
var toolExecutors = {
|
|
1357
1399
|
editFile: editFiles,
|
|
1358
1400
|
deleteFile,
|
|
@@ -1378,6 +1420,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1378
1420
|
});
|
|
1379
1421
|
ws.on("open", () => {
|
|
1380
1422
|
console.log(pc2.green("Connected to server agent streams"));
|
|
1423
|
+
statusEmitter.emit("status", { connected: true });
|
|
1381
1424
|
});
|
|
1382
1425
|
ws.on("message", async (data) => {
|
|
1383
1426
|
const message = JSON.parse(data.toString());
|
|
@@ -1407,16 +1450,18 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1407
1450
|
});
|
|
1408
1451
|
ws.on("close", () => {
|
|
1409
1452
|
console.log(pc2.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1453
|
+
statusEmitter.emit("status", { connected: false });
|
|
1410
1454
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1411
1455
|
});
|
|
1412
1456
|
ws.on("error", (error) => {
|
|
1413
1457
|
console.error(pc2.red(`WebSocket error: ${error.message}`));
|
|
1458
|
+
statusEmitter.emit("status", { connected: false });
|
|
1414
1459
|
});
|
|
1415
1460
|
return ws;
|
|
1416
1461
|
}
|
|
1417
1462
|
async function main() {
|
|
1418
1463
|
const serverUrl = DEFAULT_SERVER_URL;
|
|
1419
|
-
console.log(pc2.green("Starting local
|
|
1464
|
+
console.log(pc2.green("Starting local amai..."));
|
|
1420
1465
|
console.log(pc2.gray(`Connecting to server at ${serverUrl}`));
|
|
1421
1466
|
const connection = connectToServer2(serverUrl);
|
|
1422
1467
|
startHttpServer(connection);
|
package/dist/server.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var WebSocket = require('ws');
|
|
5
|
+
var events = require('events');
|
|
5
6
|
var zod = require('zod');
|
|
6
7
|
var promises = require('fs/promises');
|
|
7
8
|
var path9 = require('path');
|
|
@@ -23,7 +24,7 @@ var os2__default = /*#__PURE__*/_interopDefault(os2);
|
|
|
23
24
|
var pc2__default = /*#__PURE__*/_interopDefault(pc2);
|
|
24
25
|
|
|
25
26
|
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
26
|
-
var AMA_DIR = path9__default.default.join(os2__default.default.homedir(), ".
|
|
27
|
+
var AMA_DIR = path9__default.default.join(os2__default.default.homedir(), ".amai");
|
|
27
28
|
path9__default.default.join(AMA_DIR, "code");
|
|
28
29
|
path9__default.default.join(AMA_DIR, "storage");
|
|
29
30
|
|
|
@@ -1256,10 +1257,50 @@ var startHttpServer = (connection) => {
|
|
|
1256
1257
|
}
|
|
1257
1258
|
const app = new hono.Hono();
|
|
1258
1259
|
app.use(cors.cors());
|
|
1259
|
-
app.post("/daemon/status
|
|
1260
|
+
app.post("/daemon/status", (c) => {
|
|
1260
1261
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1261
1262
|
return c.json({ connected: status === "open" });
|
|
1262
1263
|
});
|
|
1264
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1265
|
+
const encoder = new TextEncoder();
|
|
1266
|
+
const stream = new ReadableStream({
|
|
1267
|
+
start(controller) {
|
|
1268
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1269
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1270
|
+
|
|
1271
|
+
`));
|
|
1272
|
+
const statusHandler = (data) => {
|
|
1273
|
+
try {
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
statusEmitter.on("status", statusHandler);
|
|
1281
|
+
const heartbeatInterval = setInterval(() => {
|
|
1282
|
+
try {
|
|
1283
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1284
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1285
|
+
|
|
1286
|
+
`));
|
|
1287
|
+
} catch {
|
|
1288
|
+
}
|
|
1289
|
+
}, 15e3);
|
|
1290
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1291
|
+
statusEmitter.off("status", statusHandler);
|
|
1292
|
+
clearInterval(heartbeatInterval);
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
return new Response(stream, {
|
|
1297
|
+
headers: {
|
|
1298
|
+
"Content-Type": "text/event-stream",
|
|
1299
|
+
"Cache-Control": "no-cache",
|
|
1300
|
+
"Connection": "keep-alive"
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1263
1304
|
app.get("context", async (c) => {
|
|
1264
1305
|
const context = getContext(process.cwd());
|
|
1265
1306
|
return c.body(JSON.stringify(context));
|
|
@@ -1351,7 +1392,7 @@ var startHttpServer = (connection) => {
|
|
|
1351
1392
|
});
|
|
1352
1393
|
nodeServer.serve({ fetch: app.fetch, port: 3456 });
|
|
1353
1394
|
};
|
|
1354
|
-
var CREDENTIALS_DIR = path9__default.default.join(os2__default.default.homedir(), ".
|
|
1395
|
+
var CREDENTIALS_DIR = path9__default.default.join(os2__default.default.homedir(), ".amai");
|
|
1355
1396
|
var CREDENTIALS_PATH = path9__default.default.join(CREDENTIALS_DIR, "credentials.json");
|
|
1356
1397
|
function getTokens() {
|
|
1357
1398
|
if (!fs3__default.default.existsSync(CREDENTIALS_PATH)) {
|
|
@@ -1363,6 +1404,7 @@ function getTokens() {
|
|
|
1363
1404
|
}
|
|
1364
1405
|
|
|
1365
1406
|
// src/server.ts
|
|
1407
|
+
var statusEmitter = new events.EventEmitter();
|
|
1366
1408
|
var toolExecutors = {
|
|
1367
1409
|
editFile: editFiles,
|
|
1368
1410
|
deleteFile,
|
|
@@ -1388,6 +1430,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1388
1430
|
});
|
|
1389
1431
|
ws.on("open", () => {
|
|
1390
1432
|
console.log(pc2__default.default.green("Connected to server agent streams"));
|
|
1433
|
+
statusEmitter.emit("status", { connected: true });
|
|
1391
1434
|
});
|
|
1392
1435
|
ws.on("message", async (data) => {
|
|
1393
1436
|
const message = JSON.parse(data.toString());
|
|
@@ -1417,16 +1460,18 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1417
1460
|
});
|
|
1418
1461
|
ws.on("close", () => {
|
|
1419
1462
|
console.log(pc2__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1463
|
+
statusEmitter.emit("status", { connected: false });
|
|
1420
1464
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1421
1465
|
});
|
|
1422
1466
|
ws.on("error", (error) => {
|
|
1423
1467
|
console.error(pc2__default.default.red(`WebSocket error: ${error.message}`));
|
|
1468
|
+
statusEmitter.emit("status", { connected: false });
|
|
1424
1469
|
});
|
|
1425
1470
|
return ws;
|
|
1426
1471
|
}
|
|
1427
1472
|
async function main() {
|
|
1428
1473
|
const serverUrl = DEFAULT_SERVER_URL;
|
|
1429
|
-
console.log(pc2__default.default.green("Starting local
|
|
1474
|
+
console.log(pc2__default.default.green("Starting local amai..."));
|
|
1430
1475
|
console.log(pc2__default.default.gray(`Connecting to server at ${serverUrl}`));
|
|
1431
1476
|
const connection = connectToServer2(serverUrl);
|
|
1432
1477
|
startHttpServer(connection);
|
|
@@ -1435,3 +1480,4 @@ async function main() {
|
|
|
1435
1480
|
exports.connectToServer = connectToServer2;
|
|
1436
1481
|
exports.getConnectionStatus = getConnectionStatus;
|
|
1437
1482
|
exports.main = main;
|
|
1483
|
+
exports.statusEmitter = statusEmitter;
|
package/dist/server.d.cts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
2
3
|
|
|
4
|
+
declare const statusEmitter: EventEmitter<[never]>;
|
|
3
5
|
declare function getConnectionStatus(ws: WebSocket): 'connecting' | 'open' | 'closing' | 'closed';
|
|
4
6
|
declare function connectToServer(serverUrl?: string): WebSocket;
|
|
5
7
|
declare function main(): Promise<void>;
|
|
6
8
|
|
|
7
|
-
export { connectToServer, getConnectionStatus, main };
|
|
9
|
+
export { connectToServer, getConnectionStatus, main, statusEmitter };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
2
3
|
|
|
4
|
+
declare const statusEmitter: EventEmitter<[never]>;
|
|
3
5
|
declare function getConnectionStatus(ws: WebSocket): 'connecting' | 'open' | 'closing' | 'closed';
|
|
4
6
|
declare function connectToServer(serverUrl?: string): WebSocket;
|
|
5
7
|
declare function main(): Promise<void>;
|
|
6
8
|
|
|
7
|
-
export { connectToServer, getConnectionStatus, main };
|
|
9
|
+
export { connectToServer, getConnectionStatus, main, statusEmitter };
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
5
6
|
import path9 from 'path';
|
|
@@ -13,7 +14,7 @@ import { serve } from '@hono/node-server';
|
|
|
13
14
|
import { cors } from 'hono/cors';
|
|
14
15
|
|
|
15
16
|
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
16
|
-
var AMA_DIR = path9.join(os2.homedir(), ".
|
|
17
|
+
var AMA_DIR = path9.join(os2.homedir(), ".amai");
|
|
17
18
|
path9.join(AMA_DIR, "code");
|
|
18
19
|
path9.join(AMA_DIR, "storage");
|
|
19
20
|
|
|
@@ -1246,10 +1247,50 @@ var startHttpServer = (connection) => {
|
|
|
1246
1247
|
}
|
|
1247
1248
|
const app = new Hono();
|
|
1248
1249
|
app.use(cors());
|
|
1249
|
-
app.post("/daemon/status
|
|
1250
|
+
app.post("/daemon/status", (c) => {
|
|
1250
1251
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1251
1252
|
return c.json({ connected: status === "open" });
|
|
1252
1253
|
});
|
|
1254
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1255
|
+
const encoder = new TextEncoder();
|
|
1256
|
+
const stream = new ReadableStream({
|
|
1257
|
+
start(controller) {
|
|
1258
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1259
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1260
|
+
|
|
1261
|
+
`));
|
|
1262
|
+
const statusHandler = (data) => {
|
|
1263
|
+
try {
|
|
1264
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1265
|
+
|
|
1266
|
+
`));
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
statusEmitter.on("status", statusHandler);
|
|
1271
|
+
const heartbeatInterval = setInterval(() => {
|
|
1272
|
+
try {
|
|
1273
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
}, 15e3);
|
|
1280
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1281
|
+
statusEmitter.off("status", statusHandler);
|
|
1282
|
+
clearInterval(heartbeatInterval);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
return new Response(stream, {
|
|
1287
|
+
headers: {
|
|
1288
|
+
"Content-Type": "text/event-stream",
|
|
1289
|
+
"Cache-Control": "no-cache",
|
|
1290
|
+
"Connection": "keep-alive"
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1253
1294
|
app.get("context", async (c) => {
|
|
1254
1295
|
const context = getContext(process.cwd());
|
|
1255
1296
|
return c.body(JSON.stringify(context));
|
|
@@ -1341,7 +1382,7 @@ var startHttpServer = (connection) => {
|
|
|
1341
1382
|
});
|
|
1342
1383
|
serve({ fetch: app.fetch, port: 3456 });
|
|
1343
1384
|
};
|
|
1344
|
-
var CREDENTIALS_DIR = path9.join(os2.homedir(), ".
|
|
1385
|
+
var CREDENTIALS_DIR = path9.join(os2.homedir(), ".amai");
|
|
1345
1386
|
var CREDENTIALS_PATH = path9.join(CREDENTIALS_DIR, "credentials.json");
|
|
1346
1387
|
function getTokens() {
|
|
1347
1388
|
if (!fs3.existsSync(CREDENTIALS_PATH)) {
|
|
@@ -1353,6 +1394,7 @@ function getTokens() {
|
|
|
1353
1394
|
}
|
|
1354
1395
|
|
|
1355
1396
|
// src/server.ts
|
|
1397
|
+
var statusEmitter = new EventEmitter();
|
|
1356
1398
|
var toolExecutors = {
|
|
1357
1399
|
editFile: editFiles,
|
|
1358
1400
|
deleteFile,
|
|
@@ -1378,6 +1420,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1378
1420
|
});
|
|
1379
1421
|
ws.on("open", () => {
|
|
1380
1422
|
console.log(pc2.green("Connected to server agent streams"));
|
|
1423
|
+
statusEmitter.emit("status", { connected: true });
|
|
1381
1424
|
});
|
|
1382
1425
|
ws.on("message", async (data) => {
|
|
1383
1426
|
const message = JSON.parse(data.toString());
|
|
@@ -1407,19 +1450,21 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1407
1450
|
});
|
|
1408
1451
|
ws.on("close", () => {
|
|
1409
1452
|
console.log(pc2.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1453
|
+
statusEmitter.emit("status", { connected: false });
|
|
1410
1454
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1411
1455
|
});
|
|
1412
1456
|
ws.on("error", (error) => {
|
|
1413
1457
|
console.error(pc2.red(`WebSocket error: ${error.message}`));
|
|
1458
|
+
statusEmitter.emit("status", { connected: false });
|
|
1414
1459
|
});
|
|
1415
1460
|
return ws;
|
|
1416
1461
|
}
|
|
1417
1462
|
async function main() {
|
|
1418
1463
|
const serverUrl = DEFAULT_SERVER_URL;
|
|
1419
|
-
console.log(pc2.green("Starting local
|
|
1464
|
+
console.log(pc2.green("Starting local amai..."));
|
|
1420
1465
|
console.log(pc2.gray(`Connecting to server at ${serverUrl}`));
|
|
1421
1466
|
const connection = connectToServer2(serverUrl);
|
|
1422
1467
|
startHttpServer(connection);
|
|
1423
1468
|
}
|
|
1424
1469
|
|
|
1425
|
-
export { connectToServer2 as connectToServer, getConnectionStatus, main };
|
|
1470
|
+
export { connectToServer2 as connectToServer, getConnectionStatus, main, statusEmitter };
|
package/package.json
CHANGED