amai 0.0.5 → 0.0.7

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.
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import WebSocket from 'ws';
3
- import { EventEmitter } from 'events';
4
3
  import { z } from 'zod';
5
4
  import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
6
- import path9 from 'path';
7
- import fs3, { readdirSync } from 'fs';
8
- import os2 from 'os';
5
+ import path11 from 'path';
6
+ import fs4, { readdirSync } from 'fs';
7
+ import os3 from 'os';
9
8
  import { randomUUID, createHash } from 'crypto';
10
9
  import { exec, spawn } from 'child_process';
11
10
  import { promisify } from 'util';
@@ -15,12 +14,12 @@ import { serve } from '@hono/node-server';
15
14
  import { cors } from 'hono/cors';
16
15
 
17
16
  var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
18
- var AMA_DIR = path9.join(os2.homedir(), ".amai");
19
- var CODE_DIR = path9.join(AMA_DIR, "code");
20
- var STORAGE_DIR = path9.join(AMA_DIR, "storage");
17
+ var AMA_DIR = path11.join(os3.homedir(), ".amai");
18
+ var CODE_DIR = path11.join(AMA_DIR, "code");
19
+ var STORAGE_DIR = path11.join(AMA_DIR, "storage");
21
20
 
22
21
  // src/lib/project-registry.ts
23
- var REGISTRY_FILE = path9.join(AMA_DIR, "projects.json");
22
+ var REGISTRY_FILE = path11.join(AMA_DIR, "projects.json");
24
23
  var ProjectRegistry = class {
25
24
  projects = /* @__PURE__ */ new Map();
26
25
  constructor() {
@@ -28,14 +27,14 @@ var ProjectRegistry = class {
28
27
  }
29
28
  load() {
30
29
  try {
31
- if (fs3.existsSync(REGISTRY_FILE)) {
32
- const data = fs3.readFileSync(REGISTRY_FILE, "utf8");
30
+ if (fs4.existsSync(REGISTRY_FILE)) {
31
+ const data = fs4.readFileSync(REGISTRY_FILE, "utf8");
33
32
  const parsed = JSON.parse(data);
34
33
  if (!Array.isArray(parsed)) {
35
34
  console.error("Invalid project registry format: expected array, got", typeof parsed);
36
35
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
37
- fs3.copyFileSync(REGISTRY_FILE, backupFile);
38
- fs3.unlinkSync(REGISTRY_FILE);
36
+ fs4.copyFileSync(REGISTRY_FILE, backupFile);
37
+ fs4.unlinkSync(REGISTRY_FILE);
39
38
  return;
40
39
  }
41
40
  const projects = parsed;
@@ -48,11 +47,11 @@ var ProjectRegistry = class {
48
47
  }
49
48
  } catch (error) {
50
49
  console.error("Failed to load project registry:", error);
51
- if (fs3.existsSync(REGISTRY_FILE)) {
50
+ if (fs4.existsSync(REGISTRY_FILE)) {
52
51
  try {
53
52
  const backupFile = REGISTRY_FILE + ".backup." + Date.now();
54
- fs3.copyFileSync(REGISTRY_FILE, backupFile);
55
- fs3.unlinkSync(REGISTRY_FILE);
53
+ fs4.copyFileSync(REGISTRY_FILE, backupFile);
54
+ fs4.unlinkSync(REGISTRY_FILE);
56
55
  console.log("Corrupted registry file backed up and removed. Starting fresh.");
57
56
  } catch (backupError) {
58
57
  }
@@ -61,21 +60,21 @@ var ProjectRegistry = class {
61
60
  }
62
61
  save() {
63
62
  try {
64
- if (!fs3.existsSync(AMA_DIR)) {
65
- fs3.mkdirSync(AMA_DIR, { recursive: true });
63
+ if (!fs4.existsSync(AMA_DIR)) {
64
+ fs4.mkdirSync(AMA_DIR, { recursive: true });
66
65
  }
67
66
  const projects = Array.from(this.projects.values());
68
- fs3.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
67
+ fs4.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
69
68
  } catch (error) {
70
69
  console.error("Failed to save project registry:", error);
71
70
  }
72
71
  }
73
72
  register(projectId, cwd, name) {
74
- const normalizedCwd = path9.normalize(path9.resolve(cwd));
73
+ const normalizedCwd = path11.normalize(path11.resolve(cwd));
75
74
  this.projects.set(projectId, {
76
75
  id: projectId,
77
76
  cwd: normalizedCwd,
78
- name: name || path9.basename(normalizedCwd),
77
+ name: name || path11.basename(normalizedCwd),
79
78
  active: true
80
79
  });
81
80
  this.save();
@@ -105,9 +104,9 @@ var ProjectRegistry = class {
105
104
  var projectRegistry = new ProjectRegistry();
106
105
  function isPathWithinProject(filePath, projectCwd) {
107
106
  try {
108
- const resolved = path9.resolve(projectCwd, filePath);
109
- const normalized = path9.normalize(resolved);
110
- const normalizedCwd = path9.normalize(projectCwd);
107
+ const resolved = path11.resolve(projectCwd, filePath);
108
+ const normalized = path11.normalize(resolved);
109
+ const normalizedCwd = path11.normalize(projectCwd);
111
110
  return normalized.startsWith(normalizedCwd);
112
111
  } catch {
113
112
  return false;
@@ -123,7 +122,7 @@ function validatePath(filePath, projectCwd) {
123
122
  };
124
123
  }
125
124
  try {
126
- const resolvedPath = path9.resolve(projectCwd, filePath);
125
+ const resolvedPath = path11.resolve(projectCwd, filePath);
127
126
  if (!isPathWithinProject(filePath, projectCwd)) {
128
127
  return {
129
128
  valid: false,
@@ -142,7 +141,7 @@ function validatePath(filePath, projectCwd) {
142
141
  }
143
142
  }
144
143
  function resolveProjectPath(filePath, projectCwd) {
145
- return path9.resolve(projectCwd, filePath);
144
+ return path11.resolve(projectCwd, filePath);
146
145
  }
147
146
 
148
147
  // src/tools/read-file.ts
@@ -264,7 +263,7 @@ var read_file = async function(input, projectCwd) {
264
263
  };
265
264
  }
266
265
  } else {
267
- const absolute_file_path = path9.resolve(relative_file_path);
266
+ const absolute_file_path = path11.resolve(relative_file_path);
268
267
  try {
269
268
  const fileStats = await stat(absolute_file_path);
270
269
  if (!fileStats.isFile()) {
@@ -858,20 +857,20 @@ var editFiles = async function(input, projectCwd) {
858
857
  }
859
858
  const basePath = projectCwd || process.cwd();
860
859
  const filePath = resolveProjectPath(target_file, basePath);
861
- const dirPath = path9.dirname(filePath);
860
+ const dirPath = path11.dirname(filePath);
862
861
  await mkdir(dirPath, { recursive: true });
863
862
  let isNewFile = providedNewFile;
864
863
  let existingContent = "";
865
864
  if (isNewFile === void 0) {
866
865
  try {
867
- existingContent = await fs3.promises.readFile(filePath, "utf-8");
866
+ existingContent = await fs4.promises.readFile(filePath, "utf-8");
868
867
  isNewFile = false;
869
868
  } catch (error) {
870
869
  isNewFile = true;
871
870
  }
872
871
  } else if (!isNewFile) {
873
872
  try {
874
- existingContent = await fs3.promises.readFile(filePath, "utf-8");
873
+ existingContent = await fs4.promises.readFile(filePath, "utf-8");
875
874
  } catch (error) {
876
875
  isNewFile = true;
877
876
  }
@@ -884,7 +883,7 @@ var editFiles = async function(input, projectCwd) {
884
883
  content
885
884
  );
886
885
  try {
887
- await fs3.promises.writeFile(filePath, content);
886
+ await fs4.promises.writeFile(filePath, content);
888
887
  } catch (writeError) {
889
888
  checkpointStore.removeCheckpoint(checkpointId);
890
889
  throw writeError;
@@ -1008,7 +1007,7 @@ var grepTool = async function(input, projectCwd) {
1008
1007
  try {
1009
1008
  const { includePattern, excludePattern: excludePattern2, caseSensitive } = options || {};
1010
1009
  const searchDir = projectCwd || process.cwd();
1011
- if (projectCwd && !path9.isAbsolute(projectCwd)) {
1010
+ if (projectCwd && !path11.isAbsolute(projectCwd)) {
1012
1011
  return {
1013
1012
  success: false,
1014
1013
  message: "Invalid project directory",
@@ -1200,8 +1199,8 @@ var list = async function(input, projectCwd) {
1200
1199
  const walk = async (currentDir, depth) => {
1201
1200
  const entries = await readdir(currentDir, { withFileTypes: true });
1202
1201
  for (const entry of entries) {
1203
- const entryAbsolutePath = path9.join(currentDir, entry.name);
1204
- const entryRelativePath = path9.relative(absolutePath, entryAbsolutePath) || ".";
1202
+ const entryAbsolutePath = path11.join(currentDir, entry.name);
1203
+ const entryRelativePath = path11.relative(absolutePath, entryAbsolutePath) || ".";
1205
1204
  if (entry.isDirectory()) {
1206
1205
  const isExcluded = entry.name.match(excludePattern);
1207
1206
  if (includeDirectoriesNormalized && matchPattern(entry.name) && !isExcluded) {
@@ -1251,213 +1250,9 @@ var list = async function(input, projectCwd) {
1251
1250
  };
1252
1251
  }
1253
1252
  };
1254
- var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local"];
1255
- var getContext = (dir, base = dir, allFiles = []) => {
1256
- const filePath = readdirSync(dir, { withFileTypes: true });
1257
- for (const file of filePath) {
1258
- if (ignoreFiles.includes(file.name)) continue;
1259
- const fullPath = path9.join(dir, file.name);
1260
- if (file.isDirectory()) {
1261
- getContext(fullPath, base, allFiles);
1262
- } else {
1263
- allFiles.push(path9.relative(base, fullPath));
1264
- }
1265
- }
1266
- return allFiles;
1267
- };
1268
- var HOME = os2.homedir();
1269
- var IDE_PROJECTS_PATHS = {
1270
- vscode: path9.join(HOME, ".vscode", "projects"),
1271
- cursor: path9.join(HOME, ".cursor", "projects"),
1272
- claude: path9.join(HOME, ".claude", "projects")
1273
- };
1274
- function getWorkspaceStoragePath(ide) {
1275
- const platform = os2.platform();
1276
- const appName = "Cursor" ;
1277
- if (platform === "darwin") {
1278
- return path9.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
1279
- } else if (platform === "win32") {
1280
- return path9.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
1281
- } else {
1282
- return path9.join(HOME, ".config", appName, "User", "workspaceStorage");
1283
- }
1284
- }
1285
- function scanWorkspaceStorage(ide) {
1286
- const projects = [];
1287
- const storagePath = getWorkspaceStoragePath();
1288
- if (!fs3.existsSync(storagePath)) {
1289
- return projects;
1290
- }
1291
- try {
1292
- const workspaces = fs3.readdirSync(storagePath);
1293
- for (const workspace of workspaces) {
1294
- const workspaceJsonPath = path9.join(storagePath, workspace, "workspace.json");
1295
- if (fs3.existsSync(workspaceJsonPath)) {
1296
- try {
1297
- const content = fs3.readFileSync(workspaceJsonPath, "utf-8");
1298
- const data = JSON.parse(content);
1299
- if (data.folder && typeof data.folder === "string") {
1300
- let projectPath = data.folder;
1301
- if (projectPath.startsWith("file://")) {
1302
- projectPath = projectPath.replace("file://", "");
1303
- projectPath = decodeURIComponent(projectPath);
1304
- }
1305
- if (fs3.existsSync(projectPath) && fs3.statSync(projectPath).isDirectory()) {
1306
- projects.push({
1307
- name: path9.basename(projectPath),
1308
- path: projectPath,
1309
- type: ide
1310
- });
1311
- }
1312
- }
1313
- } catch (err) {
1314
- console.debug(`Error reading workspace.json at ${workspaceJsonPath}: ${err}`);
1315
- }
1316
- }
1317
- }
1318
- } catch (err) {
1319
- console.debug(`Error scanning workspaceStorage for ${ide}: ${err}`);
1320
- }
1321
- return projects;
1322
- }
1323
- var scanIdeProjects = async () => {
1324
- try {
1325
- const allProjects = [];
1326
- const seenPaths = /* @__PURE__ */ new Set();
1327
- const addProject = (projectPath, ide) => {
1328
- try {
1329
- const resolvedPath = fs3.realpathSync(projectPath);
1330
- if (fs3.existsSync(resolvedPath) && fs3.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1331
- const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
1332
- try {
1333
- return fs3.realpathSync(ideDir) === resolvedPath;
1334
- } catch {
1335
- return false;
1336
- }
1337
- });
1338
- if (!isIdeProjectsDir) {
1339
- seenPaths.add(resolvedPath);
1340
- allProjects.push({
1341
- name: path9.basename(resolvedPath),
1342
- path: resolvedPath,
1343
- type: ide
1344
- });
1345
- }
1346
- }
1347
- } catch {
1348
- }
1349
- };
1350
- const cursorProjects = scanWorkspaceStorage("cursor");
1351
- for (const project of cursorProjects) {
1352
- addProject(project.path, "cursor");
1353
- }
1354
- for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
1355
- if (ide === "cursor") continue;
1356
- if (fs3.existsSync(dirPath)) {
1357
- const projects = fs3.readdirSync(dirPath);
1358
- projects.forEach((project) => {
1359
- const projectPath = path9.join(dirPath, project);
1360
- try {
1361
- const stats = fs3.lstatSync(projectPath);
1362
- let actualPath = null;
1363
- if (stats.isSymbolicLink()) {
1364
- actualPath = fs3.realpathSync(projectPath);
1365
- } else if (stats.isFile()) {
1366
- try {
1367
- let content = fs3.readFileSync(projectPath, "utf-8").trim();
1368
- if (content.startsWith("~/") || content === "~") {
1369
- content = content.replace(/^~/, HOME);
1370
- }
1371
- const resolvedContent = path9.isAbsolute(content) ? content : path9.resolve(path9.dirname(projectPath), content);
1372
- if (fs3.existsSync(resolvedContent) && fs3.statSync(resolvedContent).isDirectory()) {
1373
- actualPath = fs3.realpathSync(resolvedContent);
1374
- }
1375
- } catch {
1376
- return;
1377
- }
1378
- } else if (stats.isDirectory()) {
1379
- actualPath = fs3.realpathSync(projectPath);
1380
- }
1381
- if (actualPath) {
1382
- addProject(actualPath, ide);
1383
- }
1384
- } catch {
1385
- }
1386
- });
1387
- }
1388
- }
1389
- return allProjects;
1390
- } catch (error) {
1391
- console.debug(`Error scanning IDE projects: ${error}`);
1392
- return [];
1393
- }
1394
- };
1395
- var wsConnection = null;
1396
- var startHttpServer = (connection) => {
1397
- if (connection) {
1398
- wsConnection = connection;
1399
- }
1253
+ var startHttpServer = () => {
1400
1254
  const app = new Hono();
1401
1255
  app.use(cors());
1402
- app.post("/daemon/status", (c) => {
1403
- const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
1404
- return c.json({ connected: status === "open" });
1405
- });
1406
- app.get("/daemon/status/stream", (c) => {
1407
- const encoder = new TextEncoder();
1408
- const stream = new ReadableStream({
1409
- start(controller) {
1410
- const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
1411
- controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
1412
-
1413
- `));
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
- const heartbeatInterval = setInterval(() => {
1424
- try {
1425
- const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
1426
- controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
1427
-
1428
- `));
1429
- } catch {
1430
- }
1431
- }, 15e3);
1432
- c.req.raw.signal.addEventListener("abort", () => {
1433
- statusEmitter.off("status", statusHandler);
1434
- clearInterval(heartbeatInterval);
1435
- });
1436
- }
1437
- });
1438
- return new Response(stream, {
1439
- headers: {
1440
- "Content-Type": "text/event-stream",
1441
- "Cache-Control": "no-cache",
1442
- "Connection": "keep-alive"
1443
- }
1444
- });
1445
- });
1446
- app.get("context", async (c) => {
1447
- const context = getContext(process.cwd());
1448
- return c.body(JSON.stringify(context));
1449
- });
1450
- app.get("/ide-projects", async (c) => {
1451
- try {
1452
- const projects = await scanIdeProjects();
1453
- if (!projects) {
1454
- return c.json({ error: "No projects found" }, 500);
1455
- }
1456
- return c.json({ projects });
1457
- } catch (error) {
1458
- return c.json({ error: "Failed to scan IDE projects" }, 500);
1459
- }
1460
- });
1461
1256
  app.post("/projects/register", async (c) => {
1462
1257
  try {
1463
1258
  const { projectId, cwd, name } = await c.req.json();
@@ -1486,14 +1281,14 @@ var startHttpServer = (connection) => {
1486
1281
  }
1487
1282
  let resolved;
1488
1283
  if (projectCwd) {
1489
- resolved = path9.isAbsolute(filePath) ? filePath : path9.resolve(projectCwd, filePath);
1490
- const normalizedResolved = path9.normalize(resolved);
1491
- const normalizedCwd = path9.normalize(projectCwd);
1284
+ resolved = path11.isAbsolute(filePath) ? filePath : path11.resolve(projectCwd, filePath);
1285
+ const normalizedResolved = path11.normalize(resolved);
1286
+ const normalizedCwd = path11.normalize(projectCwd);
1492
1287
  if (!normalizedResolved.startsWith(normalizedCwd)) {
1493
1288
  return c.json({ error: "Path is outside project directory" }, 403);
1494
1289
  }
1495
1290
  } else {
1496
- resolved = path9.isAbsolute(filePath) ? filePath : path9.join(process.cwd(), filePath);
1291
+ resolved = path11.isAbsolute(filePath) ? filePath : path11.join(process.cwd(), filePath);
1497
1292
  }
1498
1293
  let currentContent;
1499
1294
  try {
@@ -1580,9 +1375,9 @@ var startHttpServer = (connection) => {
1580
1375
  }
1581
1376
  let resolved;
1582
1377
  if (projectCwd) {
1583
- resolved = path9.isAbsolute(filePath || checkpoint.filePath) ? filePath || checkpoint.filePath : path9.resolve(projectCwd, filePath || checkpoint.filePath);
1584
- const normalizedResolved = path9.normalize(resolved);
1585
- const normalizedCwd = path9.normalize(projectCwd);
1378
+ resolved = path11.isAbsolute(filePath || checkpoint.filePath) ? filePath || checkpoint.filePath : path11.resolve(projectCwd, filePath || checkpoint.filePath);
1379
+ const normalizedResolved = path11.normalize(resolved);
1380
+ const normalizedCwd = path11.normalize(projectCwd);
1586
1381
  if (!normalizedResolved.startsWith(normalizedCwd)) {
1587
1382
  return c.json({ error: "Path is outside project directory" }, 403);
1588
1383
  }
@@ -1644,16 +1439,30 @@ var startHttpServer = (connection) => {
1644
1439
  });
1645
1440
  serve({ fetch: app.fetch, port: 3456 });
1646
1441
  };
1647
- var CREDENTIALS_DIR = path9.join(os2.homedir(), ".amai");
1648
- var CREDENTIALS_PATH = path9.join(CREDENTIALS_DIR, "credentials.json");
1442
+ var CREDENTIALS_DIR = path11.join(os3.homedir(), ".amai");
1443
+ var CREDENTIALS_PATH = path11.join(CREDENTIALS_DIR, "credentials.json");
1649
1444
  function getTokens() {
1650
- if (!fs3.existsSync(CREDENTIALS_PATH)) {
1445
+ if (!fs4.existsSync(CREDENTIALS_PATH)) {
1651
1446
  return null;
1652
1447
  }
1653
- const raw = fs3.readFileSync(CREDENTIALS_PATH, "utf8");
1448
+ const raw = fs4.readFileSync(CREDENTIALS_PATH, "utf8");
1654
1449
  const data = JSON.parse(raw);
1655
1450
  return data;
1656
1451
  }
1452
+ var getUserId = () => {
1453
+ try {
1454
+ if (!fs4.existsSync(CREDENTIALS_PATH)) {
1455
+ return;
1456
+ }
1457
+ const raw = fs4.readFileSync(CREDENTIALS_PATH, "utf8");
1458
+ const data = JSON.parse(raw);
1459
+ return {
1460
+ userId: data.user.id
1461
+ };
1462
+ } catch {
1463
+ throw new Error("Error while getting userId");
1464
+ }
1465
+ };
1657
1466
  var ExplanationSchema = z.object({
1658
1467
  explanation: z.string().describe("One sentence explanation as to why this tool is being used")
1659
1468
  });
@@ -1741,9 +1550,343 @@ var runTerminalCommand = async (input, projectCwd) => {
1741
1550
  };
1742
1551
  }
1743
1552
  };
1553
+ var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local"];
1554
+ var getContext = (dir, base = dir, allFiles = []) => {
1555
+ const filePath = readdirSync(dir, { withFileTypes: true });
1556
+ for (const file of filePath) {
1557
+ if (ignoreFiles.includes(file.name)) continue;
1558
+ const fullPath = path11.join(dir, file.name);
1559
+ if (file.isDirectory()) {
1560
+ getContext(fullPath, base, allFiles);
1561
+ } else {
1562
+ allFiles.push(path11.relative(base, fullPath));
1563
+ }
1564
+ }
1565
+ return allFiles;
1566
+ };
1567
+ var HOME = os3.homedir();
1568
+ var IDE_PROJECTS_PATHS = {
1569
+ vscode: path11.join(HOME, ".vscode", "projects"),
1570
+ cursor: path11.join(HOME, ".cursor", "projects"),
1571
+ claude: path11.join(HOME, ".claude", "projects")
1572
+ };
1573
+ function getWorkspaceStoragePath(ide) {
1574
+ const platform = os3.platform();
1575
+ const appName = "Cursor" ;
1576
+ if (platform === "darwin") {
1577
+ return path11.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
1578
+ } else if (platform === "win32") {
1579
+ return path11.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
1580
+ } else {
1581
+ return path11.join(HOME, ".config", appName, "User", "workspaceStorage");
1582
+ }
1583
+ }
1584
+ function scanWorkspaceStorage(ide) {
1585
+ const projects = [];
1586
+ const storagePath = getWorkspaceStoragePath();
1587
+ if (!fs4.existsSync(storagePath)) {
1588
+ return projects;
1589
+ }
1590
+ try {
1591
+ const workspaces = fs4.readdirSync(storagePath);
1592
+ for (const workspace of workspaces) {
1593
+ const workspaceJsonPath = path11.join(storagePath, workspace, "workspace.json");
1594
+ if (fs4.existsSync(workspaceJsonPath)) {
1595
+ try {
1596
+ const content = fs4.readFileSync(workspaceJsonPath, "utf-8");
1597
+ const data = JSON.parse(content);
1598
+ if (data.folder && typeof data.folder === "string") {
1599
+ let projectPath = data.folder;
1600
+ if (projectPath.startsWith("file://")) {
1601
+ projectPath = projectPath.replace("file://", "");
1602
+ projectPath = decodeURIComponent(projectPath);
1603
+ }
1604
+ if (fs4.existsSync(projectPath) && fs4.statSync(projectPath).isDirectory()) {
1605
+ projects.push({
1606
+ name: path11.basename(projectPath),
1607
+ path: projectPath,
1608
+ type: ide
1609
+ });
1610
+ }
1611
+ }
1612
+ } catch (err) {
1613
+ console.debug(`Error reading workspace.json at ${workspaceJsonPath}: ${err}`);
1614
+ }
1615
+ }
1616
+ }
1617
+ } catch (err) {
1618
+ console.debug(`Error scanning workspaceStorage for ${ide}: ${err}`);
1619
+ }
1620
+ return projects;
1621
+ }
1622
+ var scanIdeProjects = async () => {
1623
+ try {
1624
+ const allProjects = [];
1625
+ const seenPaths = /* @__PURE__ */ new Set();
1626
+ const addProject = (projectPath, ide) => {
1627
+ try {
1628
+ const resolvedPath = fs4.realpathSync(projectPath);
1629
+ if (fs4.existsSync(resolvedPath) && fs4.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
1630
+ const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
1631
+ try {
1632
+ return fs4.realpathSync(ideDir) === resolvedPath;
1633
+ } catch {
1634
+ return false;
1635
+ }
1636
+ });
1637
+ if (!isIdeProjectsDir) {
1638
+ seenPaths.add(resolvedPath);
1639
+ allProjects.push({
1640
+ name: path11.basename(resolvedPath),
1641
+ path: resolvedPath,
1642
+ type: ide
1643
+ });
1644
+ }
1645
+ }
1646
+ } catch {
1647
+ }
1648
+ };
1649
+ const cursorProjects = scanWorkspaceStorage("cursor");
1650
+ for (const project of cursorProjects) {
1651
+ addProject(project.path, "cursor");
1652
+ }
1653
+ for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
1654
+ if (ide === "cursor") continue;
1655
+ if (fs4.existsSync(dirPath)) {
1656
+ const projects = fs4.readdirSync(dirPath);
1657
+ projects.forEach((project) => {
1658
+ const projectPath = path11.join(dirPath, project);
1659
+ try {
1660
+ const stats = fs4.lstatSync(projectPath);
1661
+ let actualPath = null;
1662
+ if (stats.isSymbolicLink()) {
1663
+ actualPath = fs4.realpathSync(projectPath);
1664
+ } else if (stats.isFile()) {
1665
+ try {
1666
+ let content = fs4.readFileSync(projectPath, "utf-8").trim();
1667
+ if (content.startsWith("~/") || content === "~") {
1668
+ content = content.replace(/^~/, HOME);
1669
+ }
1670
+ const resolvedContent = path11.isAbsolute(content) ? content : path11.resolve(path11.dirname(projectPath), content);
1671
+ if (fs4.existsSync(resolvedContent) && fs4.statSync(resolvedContent).isDirectory()) {
1672
+ actualPath = fs4.realpathSync(resolvedContent);
1673
+ }
1674
+ } catch {
1675
+ return;
1676
+ }
1677
+ } else if (stats.isDirectory()) {
1678
+ actualPath = fs4.realpathSync(projectPath);
1679
+ }
1680
+ if (actualPath) {
1681
+ addProject(actualPath, ide);
1682
+ }
1683
+ } catch {
1684
+ }
1685
+ });
1686
+ }
1687
+ }
1688
+ return allProjects;
1689
+ } catch (error) {
1690
+ console.debug(`Error scanning IDE projects: ${error}`);
1691
+ return [];
1692
+ }
1693
+ };
1694
+
1695
+ // src/lib/rpc-handlers.ts
1696
+ var rpcHandlers = {
1697
+ "daemon:get_workspace_folders": async () => {
1698
+ const projects = projectRegistry.list();
1699
+ return {
1700
+ folders: projects.map((p) => ({
1701
+ id: p.id,
1702
+ cwd: p.cwd,
1703
+ name: p.name,
1704
+ active: p.active
1705
+ }))
1706
+ };
1707
+ },
1708
+ "daemon:get_environment": async ({ gitRepositoryUrl }) => {
1709
+ const projects = projectRegistry.list();
1710
+ if (projects.length === 0) {
1711
+ const error = {
1712
+ _tag: "ProjectUnlinkedError",
1713
+ 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>\``
1714
+ };
1715
+ throw error;
1716
+ }
1717
+ return {
1718
+ project: projects[0],
1719
+ env: {
1720
+ platform: process.platform,
1721
+ arch: process.arch,
1722
+ nodeVersion: process.version
1723
+ }
1724
+ };
1725
+ },
1726
+ "daemon:get_context": async ({ cwd }) => {
1727
+ try {
1728
+ const files = getContext(cwd);
1729
+ return { files, cwd };
1730
+ } catch (error) {
1731
+ const rpcError = {
1732
+ _tag: "ContextError",
1733
+ message: error.message || "Failed to get context"
1734
+ };
1735
+ throw rpcError;
1736
+ }
1737
+ },
1738
+ "daemon:get_ide_projects": async () => {
1739
+ const projects = await scanIdeProjects();
1740
+ return { projects };
1741
+ },
1742
+ "daemon:register_project": async ({ projectId, cwd, name }) => {
1743
+ if (!projectId || !cwd) {
1744
+ const error = {
1745
+ _tag: "ValidationError",
1746
+ message: "projectId and cwd are required"
1747
+ };
1748
+ throw error;
1749
+ }
1750
+ projectRegistry.register(projectId, cwd, name);
1751
+ return { success: true, projectId, cwd };
1752
+ },
1753
+ "daemon:unregister_project": async ({ projectId }) => {
1754
+ if (!projectId) {
1755
+ const error = {
1756
+ _tag: "ValidationError",
1757
+ message: "projectId is required"
1758
+ };
1759
+ throw error;
1760
+ }
1761
+ projectRegistry.unregister(projectId);
1762
+ return { success: true, projectId };
1763
+ },
1764
+ "daemon:get_project": async ({ projectId }) => {
1765
+ const project = projectRegistry.getProject(projectId);
1766
+ if (!project) {
1767
+ const error = {
1768
+ _tag: "ProjectNotFoundError",
1769
+ message: `Project not found: ${projectId}`
1770
+ };
1771
+ throw error;
1772
+ }
1773
+ return { project };
1774
+ },
1775
+ "daemon:list_projects": async () => {
1776
+ const projects = projectRegistry.list();
1777
+ return { projects };
1778
+ },
1779
+ "daemon:status": async () => {
1780
+ return {
1781
+ connected: true,
1782
+ timestamp: Date.now(),
1783
+ platform: process.platform,
1784
+ arch: process.arch
1785
+ };
1786
+ }
1787
+ };
1788
+ var reconnectTimeout = null;
1789
+ var connectToUserStreams = async (serverUrl) => {
1790
+ const userId = getUserId();
1791
+ if (!userId?.userId) {
1792
+ throw new Error("User ID not found");
1793
+ }
1794
+ const params = new URLSearchParams({
1795
+ userId: userId.userId,
1796
+ type: "cli"
1797
+ });
1798
+ const tokens = getTokens();
1799
+ if (!tokens) {
1800
+ throw new Error("No tokens found");
1801
+ }
1802
+ const wsUrl = `${serverUrl}/api/v1/user-streams?${params.toString()}`;
1803
+ const ws = new WebSocket(wsUrl, {
1804
+ headers: {
1805
+ Authorization: `Bearer ${tokens.access_token}`
1806
+ }
1807
+ });
1808
+ ws.on("open", () => {
1809
+ console.log(pc2.green("CLI connected to user-streams"));
1810
+ if (reconnectTimeout) {
1811
+ clearTimeout(reconnectTimeout);
1812
+ reconnectTimeout = null;
1813
+ }
1814
+ });
1815
+ ws.on("message", async (event) => {
1816
+ try {
1817
+ const message = JSON.parse(event.toString());
1818
+ if (message._tag === "rpc_call") {
1819
+ const { requestId, method, input } = message;
1820
+ console.log(pc2.gray(`RPC call: ${method}`));
1821
+ const handler = rpcHandlers[method];
1822
+ if (!handler) {
1823
+ ws.send(JSON.stringify({
1824
+ _tag: "rpc_result",
1825
+ requestId,
1826
+ data: {
1827
+ _tag: "UnknownMethodError",
1828
+ message: `Unknown RPC method: ${method}`
1829
+ }
1830
+ }));
1831
+ console.log(pc2.yellow(`Unknown RPC method: ${method}`));
1832
+ return;
1833
+ }
1834
+ try {
1835
+ const result = await handler(input || {});
1836
+ ws.send(JSON.stringify({
1837
+ _tag: "rpc_result",
1838
+ requestId,
1839
+ data: result
1840
+ }));
1841
+ console.log(pc2.green(`RPC completed: ${method}`));
1842
+ } catch (error) {
1843
+ const rpcError = error._tag ? error : {
1844
+ _tag: "RpcError",
1845
+ message: error.message || String(error)
1846
+ };
1847
+ ws.send(JSON.stringify({
1848
+ _tag: "rpc_result",
1849
+ requestId,
1850
+ data: rpcError
1851
+ }));
1852
+ console.log(pc2.red(`RPC failed: ${method} - ${rpcError.message}`));
1853
+ }
1854
+ return;
1855
+ }
1856
+ if (message.type === "presence_request") {
1857
+ if (message.status === "connected") {
1858
+ ws.send(JSON.stringify({
1859
+ type: "presence_request",
1860
+ status: "connected"
1861
+ }));
1862
+ }
1863
+ if (message.status === "disconnected") {
1864
+ ws.send(JSON.stringify({
1865
+ type: "presence_request",
1866
+ status: "disconnected"
1867
+ }));
1868
+ }
1869
+ }
1870
+ } catch (parseError) {
1871
+ console.error(pc2.red(`Failed to parse message: ${parseError}`));
1872
+ }
1873
+ });
1874
+ ws.on("close", (code, reason) => {
1875
+ console.log(pc2.yellow(`CLI disconnected from user-streams (code: ${code})`));
1876
+ console.log(pc2.gray("Reconnecting in 5 seconds..."));
1877
+ reconnectTimeout = setTimeout(() => {
1878
+ connectToUserStreams(serverUrl).catch((err) => {
1879
+ console.error(pc2.red(`Reconnection failed: ${err.message}`));
1880
+ });
1881
+ }, 5e3);
1882
+ });
1883
+ ws.on("error", (error) => {
1884
+ console.error(pc2.red(`User streams WebSocket error: ${error.message}`));
1885
+ });
1886
+ return ws;
1887
+ };
1744
1888
 
1745
1889
  // src/server.ts
1746
- var statusEmitter = new EventEmitter();
1747
1890
  var toolExecutors = {
1748
1891
  editFile: editFiles,
1749
1892
  deleteFile,
@@ -1754,10 +1897,7 @@ var toolExecutors = {
1754
1897
  stringReplace: apply_patch,
1755
1898
  runTerminalCommand
1756
1899
  };
1757
- function getConnectionStatus(ws) {
1758
- return ws.readyState === WebSocket.CONNECTING ? "connecting" : ws.readyState === WebSocket.OPEN ? "open" : ws.readyState === WebSocket.CLOSING ? "closing" : "closed";
1759
- }
1760
- function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
1900
+ function connectToServer(serverUrl = DEFAULT_SERVER_URL) {
1761
1901
  const tokens = getTokens();
1762
1902
  if (!tokens) {
1763
1903
  throw new Error("No tokens found");
@@ -1770,7 +1910,6 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
1770
1910
  });
1771
1911
  ws.on("open", () => {
1772
1912
  console.log(pc2.green("Connected to server agent streams"));
1773
- statusEmitter.emit("status", { connected: true });
1774
1913
  });
1775
1914
  ws.on("message", async (data) => {
1776
1915
  const message = JSON.parse(data.toString());
@@ -1800,12 +1939,10 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
1800
1939
  });
1801
1940
  ws.on("close", () => {
1802
1941
  console.log(pc2.red("Disconnected from server. Reconnecting in 5s..."));
1803
- statusEmitter.emit("status", { connected: false });
1804
- setTimeout(() => connectToServer2(serverUrl), 5e3);
1942
+ setTimeout(() => connectToServer(serverUrl), 5e3);
1805
1943
  });
1806
1944
  ws.on("error", (error) => {
1807
1945
  console.error(pc2.red(`WebSocket error: ${error.message}`));
1808
- statusEmitter.emit("status", { connected: false });
1809
1946
  });
1810
1947
  return ws;
1811
1948
  }
@@ -1813,8 +1950,9 @@ async function main() {
1813
1950
  const serverUrl = DEFAULT_SERVER_URL;
1814
1951
  console.log(pc2.green("Starting local amai..."));
1815
1952
  console.log(pc2.gray(`Connecting to server at ${serverUrl}`));
1816
- const connection = connectToServer2(serverUrl);
1817
- startHttpServer(connection);
1953
+ connectToServer(serverUrl);
1954
+ await connectToUserStreams(serverUrl);
1955
+ startHttpServer();
1818
1956
  }
1819
1957
  var execAsync2 = promisify(exec);
1820
1958
  var CODE_SERVER_VERSION = "4.96.4";
@@ -1843,27 +1981,27 @@ function getDownloadUrl() {
1843
1981
  }
1844
1982
  function getCodeServerDir() {
1845
1983
  const { platform, arch } = getPlatformInfo();
1846
- return path9.join(CODE_DIR, `code-server-${CODE_SERVER_VERSION}-${platform}-${arch}`);
1984
+ return path11.join(CODE_DIR, `code-server-${CODE_SERVER_VERSION}-${platform}-${arch}`);
1847
1985
  }
1848
1986
  function getCodeServerBin() {
1849
- return path9.join(getCodeServerDir(), "bin", "code-server");
1987
+ return path11.join(getCodeServerDir(), "bin", "code-server");
1850
1988
  }
1851
1989
  function isCodeServerInstalled() {
1852
1990
  const binPath = getCodeServerBin();
1853
- return fs3.existsSync(binPath);
1991
+ return fs4.existsSync(binPath);
1854
1992
  }
1855
1993
  async function installCodeServer() {
1856
1994
  const { ext } = getPlatformInfo();
1857
1995
  const downloadUrl = getDownloadUrl();
1858
- const tarballPath = path9.join(AMA_DIR, `code-server.${ext}`);
1859
- if (!fs3.existsSync(AMA_DIR)) {
1860
- fs3.mkdirSync(AMA_DIR, { recursive: true });
1996
+ const tarballPath = path11.join(AMA_DIR, `code-server.${ext}`);
1997
+ if (!fs4.existsSync(AMA_DIR)) {
1998
+ fs4.mkdirSync(AMA_DIR, { recursive: true });
1861
1999
  }
1862
- if (!fs3.existsSync(CODE_DIR)) {
1863
- fs3.mkdirSync(CODE_DIR, { recursive: true });
2000
+ if (!fs4.existsSync(CODE_DIR)) {
2001
+ fs4.mkdirSync(CODE_DIR, { recursive: true });
1864
2002
  }
1865
- if (!fs3.existsSync(STORAGE_DIR)) {
1866
- fs3.mkdirSync(STORAGE_DIR, { recursive: true });
2003
+ if (!fs4.existsSync(STORAGE_DIR)) {
2004
+ fs4.mkdirSync(STORAGE_DIR, { recursive: true });
1867
2005
  }
1868
2006
  console.log(pc2.cyan(`Downloading code-server v${CODE_SERVER_VERSION}...`));
1869
2007
  console.log(pc2.gray(downloadUrl));
@@ -1872,13 +2010,13 @@ async function installCodeServer() {
1872
2010
  throw new Error(`Failed to download code-server: ${response.statusText}`);
1873
2011
  }
1874
2012
  const buffer = await response.arrayBuffer();
1875
- await fs3.promises.writeFile(tarballPath, Buffer.from(buffer));
2013
+ await fs4.promises.writeFile(tarballPath, Buffer.from(buffer));
1876
2014
  console.log(pc2.cyan("Extracting code-server..."));
1877
2015
  await execAsync2(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
1878
- await fs3.promises.unlink(tarballPath);
2016
+ await fs4.promises.unlink(tarballPath);
1879
2017
  const binPath = getCodeServerBin();
1880
- if (fs3.existsSync(binPath)) {
1881
- await fs3.promises.chmod(binPath, 493);
2018
+ if (fs4.existsSync(binPath)) {
2019
+ await fs4.promises.chmod(binPath, 493);
1882
2020
  }
1883
2021
  console.log(pc2.green("\u2713 code-server installed successfully"));
1884
2022
  }
@@ -1903,19 +2041,19 @@ async function killExistingCodeServer() {
1903
2041
  async function startCodeServer(cwd) {
1904
2042
  const binPath = getCodeServerBin();
1905
2043
  const workDir = cwd || process.cwd();
1906
- if (!fs3.existsSync(binPath)) {
2044
+ if (!fs4.existsSync(binPath)) {
1907
2045
  throw new Error("code-server is not installed. Run installCodeServer() first.");
1908
2046
  }
1909
2047
  await killExistingCodeServer();
1910
- const workspaceStoragePath = path9.join(STORAGE_DIR, "User", "workspaceStorage");
1911
- path9.join(STORAGE_DIR, "User", "globalStorage");
2048
+ const workspaceStoragePath = path11.join(STORAGE_DIR, "User", "workspaceStorage");
2049
+ path11.join(STORAGE_DIR, "User", "globalStorage");
1912
2050
  try {
1913
- if (fs3.existsSync(workspaceStoragePath)) {
1914
- await fs3.promises.rm(workspaceStoragePath, { recursive: true, force: true });
2051
+ if (fs4.existsSync(workspaceStoragePath)) {
2052
+ await fs4.promises.rm(workspaceStoragePath, { recursive: true, force: true });
1915
2053
  }
1916
- const stateDbPath = path9.join(STORAGE_DIR, "User", "globalStorage", "state.vscdb");
1917
- if (fs3.existsSync(stateDbPath)) {
1918
- await fs3.promises.unlink(stateDbPath);
2054
+ const stateDbPath = path11.join(STORAGE_DIR, "User", "globalStorage", "state.vscdb");
2055
+ if (fs4.existsSync(stateDbPath)) {
2056
+ await fs4.promises.unlink(stateDbPath);
1919
2057
  }
1920
2058
  } catch {
1921
2059
  }
@@ -1955,7 +2093,7 @@ if (process.env.AMA_DAEMON === "1") {
1955
2093
  }
1956
2094
  if (isCodeServerInstalled()) {
1957
2095
  try {
1958
- const projectDir = process.cwd() || os2.homedir();
2096
+ const projectDir = process.cwd() || os3.homedir();
1959
2097
  await startCodeServer(projectDir);
1960
2098
  } catch (error) {
1961
2099
  console.error(pc2.red(`Failed to start code-server: ${error.message}`));