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.
- package/dist/cli.cjs +512 -374
- package/dist/cli.js +507 -369
- package/dist/lib/daemon-entry.cjs +424 -286
- package/dist/lib/daemon-entry.js +421 -283
- package/dist/server.cjs +406 -266
- package/dist/server.d.cts +1 -3
- package/dist/server.d.ts +1 -3
- package/dist/server.js +402 -261
- package/package.json +2 -2
package/dist/lib/daemon-entry.js
CHANGED
|
@@ -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
|
|
7
|
-
import
|
|
8
|
-
import
|
|
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 =
|
|
19
|
-
var CODE_DIR =
|
|
20
|
-
var STORAGE_DIR =
|
|
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 =
|
|
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 (
|
|
32
|
-
const data =
|
|
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
|
-
|
|
38
|
-
|
|
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 (
|
|
50
|
+
if (fs4.existsSync(REGISTRY_FILE)) {
|
|
52
51
|
try {
|
|
53
52
|
const backupFile = REGISTRY_FILE + ".backup." + Date.now();
|
|
54
|
-
|
|
55
|
-
|
|
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 (!
|
|
65
|
-
|
|
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
|
-
|
|
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 =
|
|
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 ||
|
|
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 =
|
|
109
|
-
const normalized =
|
|
110
|
-
const normalizedCwd =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 && !
|
|
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 =
|
|
1204
|
-
const entryRelativePath =
|
|
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
|
|
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 =
|
|
1490
|
-
const normalizedResolved =
|
|
1491
|
-
const normalizedCwd =
|
|
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 =
|
|
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 =
|
|
1584
|
-
const normalizedResolved =
|
|
1585
|
-
const normalizedCwd =
|
|
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 =
|
|
1648
|
-
var CREDENTIALS_PATH =
|
|
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 (!
|
|
1445
|
+
if (!fs4.existsSync(CREDENTIALS_PATH)) {
|
|
1651
1446
|
return null;
|
|
1652
1447
|
}
|
|
1653
|
-
const raw =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1817
|
-
|
|
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
|
|
1984
|
+
return path11.join(CODE_DIR, `code-server-${CODE_SERVER_VERSION}-${platform}-${arch}`);
|
|
1847
1985
|
}
|
|
1848
1986
|
function getCodeServerBin() {
|
|
1849
|
-
return
|
|
1987
|
+
return path11.join(getCodeServerDir(), "bin", "code-server");
|
|
1850
1988
|
}
|
|
1851
1989
|
function isCodeServerInstalled() {
|
|
1852
1990
|
const binPath = getCodeServerBin();
|
|
1853
|
-
return
|
|
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 =
|
|
1859
|
-
if (!
|
|
1860
|
-
|
|
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 (!
|
|
1863
|
-
|
|
2000
|
+
if (!fs4.existsSync(CODE_DIR)) {
|
|
2001
|
+
fs4.mkdirSync(CODE_DIR, { recursive: true });
|
|
1864
2002
|
}
|
|
1865
|
-
if (!
|
|
1866
|
-
|
|
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
|
|
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
|
|
2016
|
+
await fs4.promises.unlink(tarballPath);
|
|
1879
2017
|
const binPath = getCodeServerBin();
|
|
1880
|
-
if (
|
|
1881
|
-
await
|
|
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 (!
|
|
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 =
|
|
1911
|
-
|
|
2048
|
+
const workspaceStoragePath = path11.join(STORAGE_DIR, "User", "workspaceStorage");
|
|
2049
|
+
path11.join(STORAGE_DIR, "User", "globalStorage");
|
|
1912
2050
|
try {
|
|
1913
|
-
if (
|
|
1914
|
-
await
|
|
2051
|
+
if (fs4.existsSync(workspaceStoragePath)) {
|
|
2052
|
+
await fs4.promises.rm(workspaceStoragePath, { recursive: true, force: true });
|
|
1915
2053
|
}
|
|
1916
|
-
const stateDbPath =
|
|
1917
|
-
if (
|
|
1918
|
-
await
|
|
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() ||
|
|
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}`));
|