amai 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +549 -93
- package/dist/cli.js +547 -91
- package/dist/lib/daemon-entry.cjs +488 -32
- package/dist/lib/daemon-entry.js +487 -31
- package/dist/server.cjs +488 -33
- package/dist/server.d.cts +1 -3
- package/dist/server.d.ts +1 -3
- package/dist/server.js +488 -32
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var events = require('events');
|
|
4
|
+
var pc5 = require('picocolors');
|
|
5
|
+
var WebSocket2 = require('ws');
|
|
7
6
|
var zod = require('zod');
|
|
8
7
|
var promises = require('fs/promises');
|
|
9
8
|
var path9 = require('path');
|
|
10
9
|
var fs3 = require('fs');
|
|
11
10
|
var os2 = require('os');
|
|
11
|
+
var crypto = require('crypto');
|
|
12
12
|
var child_process = require('child_process');
|
|
13
13
|
var util = require('util');
|
|
14
14
|
var hono = require('hono');
|
|
@@ -20,8 +20,8 @@ var readline = require('readline');
|
|
|
20
20
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
21
21
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
22
|
|
|
23
|
-
var
|
|
24
|
-
var
|
|
23
|
+
var pc5__default = /*#__PURE__*/_interopDefault(pc5);
|
|
24
|
+
var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
|
|
25
25
|
var path9__default = /*#__PURE__*/_interopDefault(path9);
|
|
26
26
|
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
27
27
|
var os2__default = /*#__PURE__*/_interopDefault(os2);
|
|
@@ -612,15 +612,133 @@ function calculateDiffStats(oldContent, newContent) {
|
|
|
612
612
|
}
|
|
613
613
|
return { linesAdded, linesRemoved };
|
|
614
614
|
}
|
|
615
|
-
|
|
616
|
-
|
|
615
|
+
var CheckpointStore = class {
|
|
616
|
+
checkpoints = /* @__PURE__ */ new Map();
|
|
617
|
+
fileCheckpoints = /* @__PURE__ */ new Map();
|
|
618
|
+
// filePath -> checkpointIds
|
|
619
|
+
/**
|
|
620
|
+
* Compute SHA-256 hash of content
|
|
621
|
+
*/
|
|
622
|
+
computeHash(content) {
|
|
623
|
+
return crypto.createHash("sha256").update(content, "utf8").digest("hex");
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Create a new checkpoint before an edit operation
|
|
627
|
+
*/
|
|
628
|
+
createCheckpoint(id, filePath, beforeContent, afterContent) {
|
|
629
|
+
const checkpoint = {
|
|
630
|
+
id,
|
|
631
|
+
filePath,
|
|
632
|
+
beforeContent,
|
|
633
|
+
afterContent,
|
|
634
|
+
beforeHash: this.computeHash(beforeContent),
|
|
635
|
+
afterHash: this.computeHash(afterContent),
|
|
636
|
+
timestamp: Date.now()
|
|
637
|
+
};
|
|
638
|
+
this.checkpoints.set(id, checkpoint);
|
|
639
|
+
const fileCheckpointIds = this.fileCheckpoints.get(filePath) || [];
|
|
640
|
+
fileCheckpointIds.push(id);
|
|
641
|
+
this.fileCheckpoints.set(filePath, fileCheckpointIds);
|
|
642
|
+
return checkpoint;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Get a checkpoint by ID
|
|
646
|
+
*/
|
|
647
|
+
getCheckpoint(id) {
|
|
648
|
+
return this.checkpoints.get(id);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Get all checkpoints for a file (ordered by timestamp)
|
|
652
|
+
*/
|
|
653
|
+
getCheckpointsForFile(filePath) {
|
|
654
|
+
const ids = this.fileCheckpoints.get(filePath) || [];
|
|
655
|
+
return ids.map((id) => this.checkpoints.get(id)).filter((cp) => cp !== void 0).sort((a, b) => a.timestamp - b.timestamp);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Verify if current file content matches expected state
|
|
659
|
+
* Returns true if safe to revert
|
|
660
|
+
*/
|
|
661
|
+
verifyFileState(checkpointId, currentContent) {
|
|
662
|
+
const checkpoint = this.checkpoints.get(checkpointId);
|
|
663
|
+
if (!checkpoint) {
|
|
664
|
+
return {
|
|
665
|
+
safe: false,
|
|
666
|
+
reason: "Checkpoint not found"
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
const currentHash = this.computeHash(currentContent);
|
|
670
|
+
if (currentHash === checkpoint.afterHash) {
|
|
671
|
+
return {
|
|
672
|
+
safe: true,
|
|
673
|
+
checkpoint,
|
|
674
|
+
currentHash
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
if (currentHash === checkpoint.beforeHash) {
|
|
678
|
+
return {
|
|
679
|
+
safe: false,
|
|
680
|
+
reason: "File appears to already be reverted",
|
|
681
|
+
checkpoint,
|
|
682
|
+
currentHash
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
return {
|
|
686
|
+
safe: false,
|
|
687
|
+
reason: "File was modified after this edit. Current content does not match expected state.",
|
|
688
|
+
checkpoint,
|
|
689
|
+
currentHash
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Remove a checkpoint after successful revert or accept
|
|
694
|
+
*/
|
|
695
|
+
removeCheckpoint(id) {
|
|
696
|
+
const checkpoint = this.checkpoints.get(id);
|
|
697
|
+
if (!checkpoint) return false;
|
|
698
|
+
this.checkpoints.delete(id);
|
|
699
|
+
const fileCheckpointIds = this.fileCheckpoints.get(checkpoint.filePath);
|
|
700
|
+
if (fileCheckpointIds) {
|
|
701
|
+
const filtered = fileCheckpointIds.filter((cpId) => cpId !== id);
|
|
702
|
+
if (filtered.length === 0) {
|
|
703
|
+
this.fileCheckpoints.delete(checkpoint.filePath);
|
|
704
|
+
} else {
|
|
705
|
+
this.fileCheckpoints.set(checkpoint.filePath, filtered);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Get all checkpoints (for debugging/listing)
|
|
712
|
+
*/
|
|
713
|
+
getAllCheckpoints() {
|
|
714
|
+
return Array.from(this.checkpoints.values()).sort((a, b) => b.timestamp - a.timestamp);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Clear all checkpoints (for cleanup)
|
|
718
|
+
*/
|
|
719
|
+
clear() {
|
|
720
|
+
this.checkpoints.clear();
|
|
721
|
+
this.fileCheckpoints.clear();
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Get statistics
|
|
725
|
+
*/
|
|
726
|
+
getStats() {
|
|
727
|
+
return {
|
|
728
|
+
totalCheckpoints: this.checkpoints.size,
|
|
729
|
+
filesTracked: this.fileCheckpoints.size
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
var checkpointStore = new CheckpointStore();
|
|
617
734
|
zod.z.object({
|
|
618
735
|
file_path: zod.z.string().describe("The path to the file you want to search and replace in. You can use either a relative path in the workspace or an absolute path. If an absolute path is provided, it will be preserved as is"),
|
|
619
736
|
new_string: zod.z.string().describe("The edited text to replace the old_string (must be different from the old_string)"),
|
|
620
|
-
old_string: zod.z.string().describe("The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)")
|
|
737
|
+
old_string: zod.z.string().describe("The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)"),
|
|
738
|
+
toolCallId: zod.z.string().optional().describe("Optional tool call ID for checkpoint tracking")
|
|
621
739
|
});
|
|
622
740
|
var apply_patch = async function(input, projectCwd) {
|
|
623
|
-
const { file_path, new_string, old_string } = input;
|
|
741
|
+
const { file_path, new_string, old_string, toolCallId } = input;
|
|
624
742
|
try {
|
|
625
743
|
if (!file_path) {
|
|
626
744
|
return {
|
|
@@ -695,6 +813,13 @@ var apply_patch = async function(input, projectCwd) {
|
|
|
695
813
|
};
|
|
696
814
|
}
|
|
697
815
|
const newContent = fileContent.replace(old_string, new_string);
|
|
816
|
+
const checkpointId = toolCallId || crypto.randomUUID();
|
|
817
|
+
const checkpoint = checkpointStore.createCheckpoint(
|
|
818
|
+
checkpointId,
|
|
819
|
+
absolute_file_path,
|
|
820
|
+
fileContent,
|
|
821
|
+
newContent
|
|
822
|
+
);
|
|
698
823
|
try {
|
|
699
824
|
await promises.writeFile(absolute_file_path, newContent, "utf-8");
|
|
700
825
|
const diffStats = calculateDiffStats(fileContent, newContent);
|
|
@@ -704,9 +829,14 @@ var apply_patch = async function(input, projectCwd) {
|
|
|
704
829
|
new_string,
|
|
705
830
|
linesAdded: diffStats.linesAdded,
|
|
706
831
|
linesRemoved: diffStats.linesRemoved,
|
|
707
|
-
message: `Successfully replaced string in file: ${file_path}
|
|
832
|
+
message: `Successfully replaced string in file: ${file_path}`,
|
|
833
|
+
// Include checkpoint info for frontend
|
|
834
|
+
checkpointId: checkpoint.id,
|
|
835
|
+
beforeHash: checkpoint.beforeHash,
|
|
836
|
+
afterHash: checkpoint.afterHash
|
|
708
837
|
};
|
|
709
838
|
} catch (error) {
|
|
839
|
+
checkpointStore.removeCheckpoint(checkpointId);
|
|
710
840
|
return {
|
|
711
841
|
success: false,
|
|
712
842
|
message: `Failed to write to file: ${file_path}`,
|
|
@@ -724,10 +854,11 @@ var apply_patch = async function(input, projectCwd) {
|
|
|
724
854
|
zod.z.object({
|
|
725
855
|
target_file: zod.z.string().describe("The relative path to the file to modify. The tool will create any directories in the path that don't exist"),
|
|
726
856
|
content: zod.z.string().describe("The content to write to the file"),
|
|
727
|
-
providedNewFile: zod.z.boolean().describe("The new file content to write to the file").optional()
|
|
857
|
+
providedNewFile: zod.z.boolean().describe("The new file content to write to the file").optional(),
|
|
858
|
+
toolCallId: zod.z.string().optional().describe("Optional tool call ID for checkpoint tracking")
|
|
728
859
|
});
|
|
729
860
|
var editFiles = async function(input, projectCwd) {
|
|
730
|
-
const { target_file, content, providedNewFile } = input;
|
|
861
|
+
const { target_file, content, providedNewFile, toolCallId } = input;
|
|
731
862
|
try {
|
|
732
863
|
if (projectCwd) {
|
|
733
864
|
const validation = validatePath(target_file, projectCwd);
|
|
@@ -759,7 +890,19 @@ var editFiles = async function(input, projectCwd) {
|
|
|
759
890
|
isNewFile = true;
|
|
760
891
|
}
|
|
761
892
|
}
|
|
762
|
-
|
|
893
|
+
const checkpointId = toolCallId || crypto.randomUUID();
|
|
894
|
+
const checkpoint = checkpointStore.createCheckpoint(
|
|
895
|
+
checkpointId,
|
|
896
|
+
filePath,
|
|
897
|
+
existingContent,
|
|
898
|
+
content
|
|
899
|
+
);
|
|
900
|
+
try {
|
|
901
|
+
await fs3__default.default.promises.writeFile(filePath, content);
|
|
902
|
+
} catch (writeError) {
|
|
903
|
+
checkpointStore.removeCheckpoint(checkpointId);
|
|
904
|
+
throw writeError;
|
|
905
|
+
}
|
|
763
906
|
const diffStats = calculateDiffStats(existingContent, content);
|
|
764
907
|
if (isNewFile) {
|
|
765
908
|
return {
|
|
@@ -769,7 +912,11 @@ var editFiles = async function(input, projectCwd) {
|
|
|
769
912
|
new_string: content,
|
|
770
913
|
message: `Created new file: ${target_file}`,
|
|
771
914
|
linesAdded: diffStats.linesAdded,
|
|
772
|
-
linesRemoved: diffStats.linesRemoved
|
|
915
|
+
linesRemoved: diffStats.linesRemoved,
|
|
916
|
+
// Include checkpoint info for frontend
|
|
917
|
+
checkpointId: checkpoint.id,
|
|
918
|
+
beforeHash: checkpoint.beforeHash,
|
|
919
|
+
afterHash: checkpoint.afterHash
|
|
773
920
|
};
|
|
774
921
|
} else {
|
|
775
922
|
return {
|
|
@@ -779,7 +926,11 @@ var editFiles = async function(input, projectCwd) {
|
|
|
779
926
|
new_string: content,
|
|
780
927
|
message: `Modified file: ${target_file}`,
|
|
781
928
|
linesAdded: diffStats.linesAdded,
|
|
782
|
-
linesRemoved: diffStats.linesRemoved
|
|
929
|
+
linesRemoved: diffStats.linesRemoved,
|
|
930
|
+
// Include checkpoint info for frontend
|
|
931
|
+
checkpointId: checkpoint.id,
|
|
932
|
+
beforeHash: checkpoint.beforeHash,
|
|
933
|
+
afterHash: checkpoint.afterHash
|
|
783
934
|
};
|
|
784
935
|
}
|
|
785
936
|
} catch (error) {
|
|
@@ -1274,15 +1425,6 @@ var startHttpServer = (connection) => {
|
|
|
1274
1425
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1275
1426
|
|
|
1276
1427
|
`));
|
|
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
1428
|
const heartbeatInterval = setInterval(() => {
|
|
1287
1429
|
try {
|
|
1288
1430
|
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
@@ -1293,7 +1435,6 @@ var startHttpServer = (connection) => {
|
|
|
1293
1435
|
}
|
|
1294
1436
|
}, 15e3);
|
|
1295
1437
|
c.req.raw.signal.addEventListener("abort", () => {
|
|
1296
|
-
statusEmitter.off("status", statusHandler);
|
|
1297
1438
|
clearInterval(heartbeatInterval);
|
|
1298
1439
|
});
|
|
1299
1440
|
}
|
|
@@ -1335,7 +1476,15 @@ var startHttpServer = (connection) => {
|
|
|
1335
1476
|
});
|
|
1336
1477
|
app.post("/revert", async (c) => {
|
|
1337
1478
|
try {
|
|
1338
|
-
const {
|
|
1479
|
+
const {
|
|
1480
|
+
filePath,
|
|
1481
|
+
oldString,
|
|
1482
|
+
newString,
|
|
1483
|
+
projectCwd,
|
|
1484
|
+
checkpointId,
|
|
1485
|
+
expectedAfterHash,
|
|
1486
|
+
force = false
|
|
1487
|
+
} = await c.req.json();
|
|
1339
1488
|
if (!filePath || oldString === void 0) {
|
|
1340
1489
|
return c.json({ error: "filePath and oldString required" }, 400);
|
|
1341
1490
|
}
|
|
@@ -1359,25 +1508,127 @@ var startHttpServer = (connection) => {
|
|
|
1359
1508
|
}
|
|
1360
1509
|
return c.json({ error: `Failed to read file: ${error.message}` }, 500);
|
|
1361
1510
|
}
|
|
1511
|
+
if (checkpointId) {
|
|
1512
|
+
const verification = checkpointStore.verifyFileState(checkpointId, currentContent);
|
|
1513
|
+
if (!verification.safe && !force) {
|
|
1514
|
+
return c.json({
|
|
1515
|
+
success: false,
|
|
1516
|
+
conflict: true,
|
|
1517
|
+
error: verification.reason,
|
|
1518
|
+
currentHash: verification.currentHash,
|
|
1519
|
+
expectedHash: verification.checkpoint?.afterHash,
|
|
1520
|
+
checkpointId
|
|
1521
|
+
}, 409);
|
|
1522
|
+
}
|
|
1523
|
+
if (verification.checkpoint) {
|
|
1524
|
+
try {
|
|
1525
|
+
await promises.writeFile(resolved, verification.checkpoint.beforeContent, "utf-8");
|
|
1526
|
+
checkpointStore.removeCheckpoint(checkpointId);
|
|
1527
|
+
return c.json({ success: true, usedCheckpoint: true });
|
|
1528
|
+
} catch (writeError) {
|
|
1529
|
+
return c.json({ error: `Failed to write file: ${writeError.message}` }, 500);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (expectedAfterHash && !force) {
|
|
1534
|
+
const currentHash = checkpointStore.computeHash(currentContent);
|
|
1535
|
+
if (currentHash !== expectedAfterHash) {
|
|
1536
|
+
return c.json({
|
|
1537
|
+
success: false,
|
|
1538
|
+
conflict: true,
|
|
1539
|
+
error: "File was modified after this edit. Current content does not match expected state.",
|
|
1540
|
+
currentHash,
|
|
1541
|
+
expectedHash: expectedAfterHash
|
|
1542
|
+
}, 409);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1362
1545
|
let finalContent;
|
|
1363
1546
|
if (newString && newString !== oldString) {
|
|
1364
1547
|
if (!currentContent.includes(newString)) {
|
|
1365
|
-
return c.json({
|
|
1548
|
+
return c.json({
|
|
1549
|
+
success: false,
|
|
1550
|
+
conflict: true,
|
|
1551
|
+
error: "Cannot revert: the new content is not found in the current file. The file may have been modified."
|
|
1552
|
+
}, 409);
|
|
1366
1553
|
}
|
|
1367
1554
|
const occurrences = currentContent.split(newString).length - 1;
|
|
1368
1555
|
if (occurrences > 1) {
|
|
1369
|
-
return c.json({
|
|
1556
|
+
return c.json({
|
|
1557
|
+
success: false,
|
|
1558
|
+
conflict: true,
|
|
1559
|
+
error: "Cannot revert: the new content appears multiple times in the file"
|
|
1560
|
+
}, 409);
|
|
1370
1561
|
}
|
|
1371
1562
|
finalContent = currentContent.replace(newString, oldString);
|
|
1372
1563
|
} else {
|
|
1373
1564
|
finalContent = oldString;
|
|
1374
1565
|
}
|
|
1375
1566
|
await promises.writeFile(resolved, finalContent, "utf-8");
|
|
1567
|
+
if (checkpointId) {
|
|
1568
|
+
checkpointStore.removeCheckpoint(checkpointId);
|
|
1569
|
+
}
|
|
1376
1570
|
return c.json({ success: true });
|
|
1377
1571
|
} catch (error) {
|
|
1378
1572
|
return c.json({ error: error.message }, 500);
|
|
1379
1573
|
}
|
|
1380
1574
|
});
|
|
1575
|
+
app.post("/revert/force", async (c) => {
|
|
1576
|
+
try {
|
|
1577
|
+
const { filePath, checkpointId, projectCwd } = await c.req.json();
|
|
1578
|
+
if (!checkpointId) {
|
|
1579
|
+
return c.json({ error: "checkpointId is required for force revert" }, 400);
|
|
1580
|
+
}
|
|
1581
|
+
const checkpoint = checkpointStore.getCheckpoint(checkpointId);
|
|
1582
|
+
if (!checkpoint) {
|
|
1583
|
+
return c.json({ error: "Checkpoint not found" }, 404);
|
|
1584
|
+
}
|
|
1585
|
+
let resolved;
|
|
1586
|
+
if (projectCwd) {
|
|
1587
|
+
resolved = path9__default.default.isAbsolute(filePath || checkpoint.filePath) ? filePath || checkpoint.filePath : path9__default.default.resolve(projectCwd, filePath || checkpoint.filePath);
|
|
1588
|
+
const normalizedResolved = path9__default.default.normalize(resolved);
|
|
1589
|
+
const normalizedCwd = path9__default.default.normalize(projectCwd);
|
|
1590
|
+
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
1591
|
+
return c.json({ error: "Path is outside project directory" }, 403);
|
|
1592
|
+
}
|
|
1593
|
+
} else {
|
|
1594
|
+
resolved = checkpoint.filePath;
|
|
1595
|
+
}
|
|
1596
|
+
try {
|
|
1597
|
+
await promises.writeFile(resolved, checkpoint.beforeContent, "utf-8");
|
|
1598
|
+
checkpointStore.removeCheckpoint(checkpointId);
|
|
1599
|
+
return c.json({ success: true, forced: true });
|
|
1600
|
+
} catch (writeError) {
|
|
1601
|
+
return c.json({ error: `Failed to write file: ${writeError.message}` }, 500);
|
|
1602
|
+
}
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
return c.json({ error: error.message }, 500);
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
app.get("/checkpoints/:checkpointId", (c) => {
|
|
1608
|
+
const checkpointId = c.req.param("checkpointId");
|
|
1609
|
+
const checkpoint = checkpointStore.getCheckpoint(checkpointId);
|
|
1610
|
+
if (!checkpoint) {
|
|
1611
|
+
return c.json({ error: "Checkpoint not found" }, 404);
|
|
1612
|
+
}
|
|
1613
|
+
return c.json({
|
|
1614
|
+
id: checkpoint.id,
|
|
1615
|
+
filePath: checkpoint.filePath,
|
|
1616
|
+
beforeHash: checkpoint.beforeHash,
|
|
1617
|
+
afterHash: checkpoint.afterHash,
|
|
1618
|
+
timestamp: checkpoint.timestamp
|
|
1619
|
+
});
|
|
1620
|
+
});
|
|
1621
|
+
app.get("/checkpoints", (c) => {
|
|
1622
|
+
const stats = checkpointStore.getStats();
|
|
1623
|
+
const checkpoints = checkpointStore.getAllCheckpoints().map((cp) => ({
|
|
1624
|
+
id: cp.id,
|
|
1625
|
+
filePath: cp.filePath,
|
|
1626
|
+
beforeHash: cp.beforeHash,
|
|
1627
|
+
afterHash: cp.afterHash,
|
|
1628
|
+
timestamp: cp.timestamp
|
|
1629
|
+
}));
|
|
1630
|
+
return c.json({ stats, checkpoints });
|
|
1631
|
+
});
|
|
1381
1632
|
app.get("/projects", (c) => {
|
|
1382
1633
|
const projects = projectRegistry.list();
|
|
1383
1634
|
return c.json({ projects });
|
|
@@ -1423,7 +1674,7 @@ function saveTokens(tokens) {
|
|
|
1423
1674
|
"utf8"
|
|
1424
1675
|
);
|
|
1425
1676
|
} catch (error) {
|
|
1426
|
-
console.error(
|
|
1677
|
+
console.error(pc5__default.default.red("Failed to save credentials"), error);
|
|
1427
1678
|
}
|
|
1428
1679
|
}
|
|
1429
1680
|
function logout() {
|
|
@@ -1432,7 +1683,7 @@ function logout() {
|
|
|
1432
1683
|
fs3__default.default.unlinkSync(CREDENTIALS_PATH);
|
|
1433
1684
|
}
|
|
1434
1685
|
} catch (error) {
|
|
1435
|
-
console.error(
|
|
1686
|
+
console.error(pc5__default.default.red("Failed to logout"), error);
|
|
1436
1687
|
}
|
|
1437
1688
|
}
|
|
1438
1689
|
function getTokens() {
|
|
@@ -1507,27 +1758,27 @@ async function pollForTokens({
|
|
|
1507
1758
|
async function login() {
|
|
1508
1759
|
try {
|
|
1509
1760
|
const device = await authorizeDevice();
|
|
1510
|
-
console.log(
|
|
1761
|
+
console.log(pc5__default.default.bold("To sign in, follow these steps:"));
|
|
1511
1762
|
if (device.verification_uri_complete) {
|
|
1512
1763
|
console.log(
|
|
1513
|
-
` 1. Open this URL in your browser: ${
|
|
1764
|
+
` 1. Open this URL in your browser: ${pc5__default.default.cyan(
|
|
1514
1765
|
device.verification_uri_complete
|
|
1515
1766
|
)}`
|
|
1516
1767
|
);
|
|
1517
1768
|
} else {
|
|
1518
1769
|
console.log(
|
|
1519
|
-
` 1. Open this URL in your browser: ${
|
|
1770
|
+
` 1. Open this URL in your browser: ${pc5__default.default.cyan(
|
|
1520
1771
|
device.verification_uri
|
|
1521
1772
|
)}`
|
|
1522
1773
|
);
|
|
1523
1774
|
console.log(
|
|
1524
|
-
` 2. Enter this code when prompted: ${
|
|
1775
|
+
` 2. Enter this code when prompted: ${pc5__default.default.bold(device.user_code)}`
|
|
1525
1776
|
);
|
|
1526
1777
|
}
|
|
1527
1778
|
console.log(" 3. Come back here; we will detect when you finish logging in.");
|
|
1528
1779
|
console.log();
|
|
1529
1780
|
console.log(
|
|
1530
|
-
|
|
1781
|
+
pc5__default.default.gray(
|
|
1531
1782
|
`Waiting for authorization (expires in ~${Math.round(
|
|
1532
1783
|
device.expires_in / 60
|
|
1533
1784
|
)} minutes)...`
|
|
@@ -1539,14 +1790,28 @@ async function login() {
|
|
|
1539
1790
|
expiresIn: device.expires_in,
|
|
1540
1791
|
interval: device.interval
|
|
1541
1792
|
});
|
|
1542
|
-
console.log(
|
|
1793
|
+
console.log(pc5__default.default.green("Successfully authenticated!"));
|
|
1543
1794
|
saveTokens(tokens);
|
|
1544
1795
|
return tokens;
|
|
1545
1796
|
} catch (error) {
|
|
1546
|
-
console.error(
|
|
1797
|
+
console.error(pc5__default.default.red(error.message || "Login failed"));
|
|
1547
1798
|
throw error;
|
|
1548
1799
|
}
|
|
1549
1800
|
}
|
|
1801
|
+
var getUserId = () => {
|
|
1802
|
+
try {
|
|
1803
|
+
if (!fs3__default.default.existsSync(CREDENTIALS_PATH)) {
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
const raw = fs3__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1807
|
+
const data = JSON.parse(raw);
|
|
1808
|
+
return {
|
|
1809
|
+
userId: data.user.id
|
|
1810
|
+
};
|
|
1811
|
+
} catch {
|
|
1812
|
+
throw new Error("Error while getting userId");
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1550
1815
|
var ExplanationSchema = zod.z.object({
|
|
1551
1816
|
explanation: zod.z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1552
1817
|
});
|
|
@@ -1635,8 +1900,201 @@ var runTerminalCommand = async (input, projectCwd) => {
|
|
|
1635
1900
|
}
|
|
1636
1901
|
};
|
|
1637
1902
|
|
|
1903
|
+
// src/lib/rpc-handlers.ts
|
|
1904
|
+
var rpcHandlers = {
|
|
1905
|
+
"daemon:get_workspace_folders": async () => {
|
|
1906
|
+
const projects = projectRegistry.list();
|
|
1907
|
+
return {
|
|
1908
|
+
folders: projects.map((p) => ({
|
|
1909
|
+
id: p.id,
|
|
1910
|
+
cwd: p.cwd,
|
|
1911
|
+
name: p.name,
|
|
1912
|
+
active: p.active
|
|
1913
|
+
}))
|
|
1914
|
+
};
|
|
1915
|
+
},
|
|
1916
|
+
"daemon:get_environment": async ({ gitRepositoryUrl }) => {
|
|
1917
|
+
const projects = projectRegistry.list();
|
|
1918
|
+
if (projects.length === 0) {
|
|
1919
|
+
const error = {
|
|
1920
|
+
_tag: "ProjectUnlinkedError",
|
|
1921
|
+
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>\``
|
|
1922
|
+
};
|
|
1923
|
+
throw error;
|
|
1924
|
+
}
|
|
1925
|
+
return {
|
|
1926
|
+
project: projects[0],
|
|
1927
|
+
env: {
|
|
1928
|
+
platform: process.platform,
|
|
1929
|
+
arch: process.arch,
|
|
1930
|
+
nodeVersion: process.version
|
|
1931
|
+
}
|
|
1932
|
+
};
|
|
1933
|
+
},
|
|
1934
|
+
"daemon:get_context": async ({ cwd }) => {
|
|
1935
|
+
try {
|
|
1936
|
+
const files = getContext(cwd);
|
|
1937
|
+
return { files, cwd };
|
|
1938
|
+
} catch (error) {
|
|
1939
|
+
const rpcError = {
|
|
1940
|
+
_tag: "ContextError",
|
|
1941
|
+
message: error.message || "Failed to get context"
|
|
1942
|
+
};
|
|
1943
|
+
throw rpcError;
|
|
1944
|
+
}
|
|
1945
|
+
},
|
|
1946
|
+
"daemon:get_ide_projects": async () => {
|
|
1947
|
+
const projects = await scanIdeProjects();
|
|
1948
|
+
return { projects };
|
|
1949
|
+
},
|
|
1950
|
+
"daemon:register_project": async ({ projectId, cwd, name }) => {
|
|
1951
|
+
if (!projectId || !cwd) {
|
|
1952
|
+
const error = {
|
|
1953
|
+
_tag: "ValidationError",
|
|
1954
|
+
message: "projectId and cwd are required"
|
|
1955
|
+
};
|
|
1956
|
+
throw error;
|
|
1957
|
+
}
|
|
1958
|
+
projectRegistry.register(projectId, cwd, name);
|
|
1959
|
+
return { success: true, projectId, cwd };
|
|
1960
|
+
},
|
|
1961
|
+
"daemon:unregister_project": async ({ projectId }) => {
|
|
1962
|
+
if (!projectId) {
|
|
1963
|
+
const error = {
|
|
1964
|
+
_tag: "ValidationError",
|
|
1965
|
+
message: "projectId is required"
|
|
1966
|
+
};
|
|
1967
|
+
throw error;
|
|
1968
|
+
}
|
|
1969
|
+
projectRegistry.unregister(projectId);
|
|
1970
|
+
return { success: true, projectId };
|
|
1971
|
+
},
|
|
1972
|
+
"daemon:get_project": async ({ projectId }) => {
|
|
1973
|
+
const project = projectRegistry.getProject(projectId);
|
|
1974
|
+
if (!project) {
|
|
1975
|
+
const error = {
|
|
1976
|
+
_tag: "ProjectNotFoundError",
|
|
1977
|
+
message: `Project not found: ${projectId}`
|
|
1978
|
+
};
|
|
1979
|
+
throw error;
|
|
1980
|
+
}
|
|
1981
|
+
return { project };
|
|
1982
|
+
},
|
|
1983
|
+
"daemon:list_projects": async () => {
|
|
1984
|
+
const projects = projectRegistry.list();
|
|
1985
|
+
return { projects };
|
|
1986
|
+
},
|
|
1987
|
+
"daemon:status": async () => {
|
|
1988
|
+
return {
|
|
1989
|
+
connected: true,
|
|
1990
|
+
timestamp: Date.now(),
|
|
1991
|
+
platform: process.platform,
|
|
1992
|
+
arch: process.arch
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1995
|
+
};
|
|
1996
|
+
var reconnectTimeout = null;
|
|
1997
|
+
var connectToUserStreams = async (serverUrl) => {
|
|
1998
|
+
const userId = getUserId();
|
|
1999
|
+
if (!userId?.userId) {
|
|
2000
|
+
throw new Error("User ID not found");
|
|
2001
|
+
}
|
|
2002
|
+
const params = new URLSearchParams({
|
|
2003
|
+
userId: userId.userId,
|
|
2004
|
+
type: "cli"
|
|
2005
|
+
});
|
|
2006
|
+
const tokens = getTokens();
|
|
2007
|
+
if (!tokens) {
|
|
2008
|
+
throw new Error("No tokens found");
|
|
2009
|
+
}
|
|
2010
|
+
const wsUrl = `${serverUrl}/api/v1/user-streams?${params.toString()}`;
|
|
2011
|
+
const ws = new WebSocket2__default.default(wsUrl, {
|
|
2012
|
+
headers: {
|
|
2013
|
+
Authorization: `Bearer ${tokens.access_token}`
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
2016
|
+
ws.on("open", () => {
|
|
2017
|
+
console.log(pc5__default.default.green("CLI connected to user-streams"));
|
|
2018
|
+
if (reconnectTimeout) {
|
|
2019
|
+
clearTimeout(reconnectTimeout);
|
|
2020
|
+
reconnectTimeout = null;
|
|
2021
|
+
}
|
|
2022
|
+
});
|
|
2023
|
+
ws.on("message", async (event) => {
|
|
2024
|
+
try {
|
|
2025
|
+
const message = JSON.parse(event.toString());
|
|
2026
|
+
if (message._tag === "rpc_call") {
|
|
2027
|
+
const { requestId, method, input } = message;
|
|
2028
|
+
console.log(pc5__default.default.gray(`RPC call: ${method}`));
|
|
2029
|
+
const handler = rpcHandlers[method];
|
|
2030
|
+
if (!handler) {
|
|
2031
|
+
ws.send(JSON.stringify({
|
|
2032
|
+
_tag: "rpc_result",
|
|
2033
|
+
requestId,
|
|
2034
|
+
data: {
|
|
2035
|
+
_tag: "UnknownMethodError",
|
|
2036
|
+
message: `Unknown RPC method: ${method}`
|
|
2037
|
+
}
|
|
2038
|
+
}));
|
|
2039
|
+
console.log(pc5__default.default.yellow(`Unknown RPC method: ${method}`));
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
try {
|
|
2043
|
+
const result = await handler(input || {});
|
|
2044
|
+
ws.send(JSON.stringify({
|
|
2045
|
+
_tag: "rpc_result",
|
|
2046
|
+
requestId,
|
|
2047
|
+
data: result
|
|
2048
|
+
}));
|
|
2049
|
+
console.log(pc5__default.default.green(`RPC completed: ${method}`));
|
|
2050
|
+
} catch (error) {
|
|
2051
|
+
const rpcError = error._tag ? error : {
|
|
2052
|
+
_tag: "RpcError",
|
|
2053
|
+
message: error.message || String(error)
|
|
2054
|
+
};
|
|
2055
|
+
ws.send(JSON.stringify({
|
|
2056
|
+
_tag: "rpc_result",
|
|
2057
|
+
requestId,
|
|
2058
|
+
data: rpcError
|
|
2059
|
+
}));
|
|
2060
|
+
console.log(pc5__default.default.red(`RPC failed: ${method} - ${rpcError.message}`));
|
|
2061
|
+
}
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
if (message.type === "presence_request") {
|
|
2065
|
+
if (message.status === "connected") {
|
|
2066
|
+
ws.send(JSON.stringify({
|
|
2067
|
+
type: "presence_request",
|
|
2068
|
+
status: "connected"
|
|
2069
|
+
}));
|
|
2070
|
+
}
|
|
2071
|
+
if (message.status === "disconnected") {
|
|
2072
|
+
ws.send(JSON.stringify({
|
|
2073
|
+
type: "presence_request",
|
|
2074
|
+
status: "disconnected"
|
|
2075
|
+
}));
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
} catch (parseError) {
|
|
2079
|
+
console.error(pc5__default.default.red(`Failed to parse message: ${parseError}`));
|
|
2080
|
+
}
|
|
2081
|
+
});
|
|
2082
|
+
ws.on("close", (code, reason) => {
|
|
2083
|
+
console.log(pc5__default.default.yellow(`CLI disconnected from user-streams (code: ${code})`));
|
|
2084
|
+
console.log(pc5__default.default.gray("Reconnecting in 5 seconds..."));
|
|
2085
|
+
reconnectTimeout = setTimeout(() => {
|
|
2086
|
+
connectToUserStreams(serverUrl).catch((err) => {
|
|
2087
|
+
console.error(pc5__default.default.red(`Reconnection failed: ${err.message}`));
|
|
2088
|
+
});
|
|
2089
|
+
}, 5e3);
|
|
2090
|
+
});
|
|
2091
|
+
ws.on("error", (error) => {
|
|
2092
|
+
console.error(pc5__default.default.red(`User streams WebSocket error: ${error.message}`));
|
|
2093
|
+
});
|
|
2094
|
+
return ws;
|
|
2095
|
+
};
|
|
2096
|
+
|
|
1638
2097
|
// src/server.ts
|
|
1639
|
-
var statusEmitter = new events.EventEmitter();
|
|
1640
2098
|
var toolExecutors = {
|
|
1641
2099
|
editFile: editFiles,
|
|
1642
2100
|
deleteFile,
|
|
@@ -1648,7 +2106,7 @@ var toolExecutors = {
|
|
|
1648
2106
|
runTerminalCommand
|
|
1649
2107
|
};
|
|
1650
2108
|
function getConnectionStatus(ws) {
|
|
1651
|
-
return ws.readyState ===
|
|
2109
|
+
return ws.readyState === WebSocket2__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket2__default.default.OPEN ? "open" : ws.readyState === WebSocket2__default.default.CLOSING ? "closing" : "closed";
|
|
1652
2110
|
}
|
|
1653
2111
|
function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
1654
2112
|
const tokens = getTokens();
|
|
@@ -1656,14 +2114,13 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1656
2114
|
throw new Error("No tokens found");
|
|
1657
2115
|
}
|
|
1658
2116
|
const wsUrl = `${serverUrl}/agent-streams`;
|
|
1659
|
-
const ws = new
|
|
2117
|
+
const ws = new WebSocket2__default.default(wsUrl, {
|
|
1660
2118
|
headers: {
|
|
1661
2119
|
"Authorization": `Bearer ${tokens.access_token}`
|
|
1662
2120
|
}
|
|
1663
2121
|
});
|
|
1664
2122
|
ws.on("open", () => {
|
|
1665
|
-
console.log(
|
|
1666
|
-
statusEmitter.emit("status", { connected: true });
|
|
2123
|
+
console.log(pc5__default.default.green("Connected to server agent streams"));
|
|
1667
2124
|
});
|
|
1668
2125
|
ws.on("message", async (data) => {
|
|
1669
2126
|
const message = JSON.parse(data.toString());
|
|
@@ -1680,33 +2137,32 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1680
2137
|
id: message.id,
|
|
1681
2138
|
result
|
|
1682
2139
|
}));
|
|
1683
|
-
console.log(
|
|
2140
|
+
console.log(pc5__default.default.green(`Tool completed: ${message.tool}`));
|
|
1684
2141
|
} catch (error) {
|
|
1685
2142
|
ws.send(JSON.stringify({
|
|
1686
2143
|
type: "tool_result",
|
|
1687
2144
|
id: message.id,
|
|
1688
2145
|
error: error.message
|
|
1689
2146
|
}));
|
|
1690
|
-
console.error(
|
|
2147
|
+
console.error(pc5__default.default.red(`Tool failed: ${message.tool} ${error.message}`));
|
|
1691
2148
|
}
|
|
1692
2149
|
}
|
|
1693
2150
|
});
|
|
1694
2151
|
ws.on("close", () => {
|
|
1695
|
-
console.log(
|
|
1696
|
-
statusEmitter.emit("status", { connected: false });
|
|
2152
|
+
console.log(pc5__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1697
2153
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1698
2154
|
});
|
|
1699
2155
|
ws.on("error", (error) => {
|
|
1700
|
-
console.error(
|
|
1701
|
-
statusEmitter.emit("status", { connected: false });
|
|
2156
|
+
console.error(pc5__default.default.red(`WebSocket error: ${error.message}`));
|
|
1702
2157
|
});
|
|
1703
2158
|
return ws;
|
|
1704
2159
|
}
|
|
1705
2160
|
async function main() {
|
|
1706
2161
|
const serverUrl = DEFAULT_SERVER_URL;
|
|
1707
|
-
console.log(
|
|
1708
|
-
console.log(
|
|
2162
|
+
console.log(pc5__default.default.green("Starting local amai..."));
|
|
2163
|
+
console.log(pc5__default.default.gray(`Connecting to server at ${serverUrl}`));
|
|
1709
2164
|
const connection = connectToServer2(serverUrl);
|
|
2165
|
+
await connectToUserStreams(serverUrl);
|
|
1710
2166
|
startHttpServer(connection);
|
|
1711
2167
|
}
|
|
1712
2168
|
var execAsync2 = util.promisify(child_process.exec);
|
|
@@ -1758,22 +2214,22 @@ async function installCodeServer() {
|
|
|
1758
2214
|
if (!fs3__default.default.existsSync(STORAGE_DIR)) {
|
|
1759
2215
|
fs3__default.default.mkdirSync(STORAGE_DIR, { recursive: true });
|
|
1760
2216
|
}
|
|
1761
|
-
console.log(
|
|
1762
|
-
console.log(
|
|
2217
|
+
console.log(pc5__default.default.cyan(`Downloading code-server v${CODE_SERVER_VERSION}...`));
|
|
2218
|
+
console.log(pc5__default.default.gray(downloadUrl));
|
|
1763
2219
|
const response = await fetch(downloadUrl);
|
|
1764
2220
|
if (!response.ok) {
|
|
1765
2221
|
throw new Error(`Failed to download code-server: ${response.statusText}`);
|
|
1766
2222
|
}
|
|
1767
2223
|
const buffer = await response.arrayBuffer();
|
|
1768
2224
|
await fs3__default.default.promises.writeFile(tarballPath, Buffer.from(buffer));
|
|
1769
|
-
console.log(
|
|
2225
|
+
console.log(pc5__default.default.cyan("Extracting code-server..."));
|
|
1770
2226
|
await execAsync2(`tar -xzf ${tarballPath} -C ${CODE_DIR}`);
|
|
1771
2227
|
await fs3__default.default.promises.unlink(tarballPath);
|
|
1772
2228
|
const binPath = getCodeServerBin();
|
|
1773
2229
|
if (fs3__default.default.existsSync(binPath)) {
|
|
1774
2230
|
await fs3__default.default.promises.chmod(binPath, 493);
|
|
1775
2231
|
}
|
|
1776
|
-
console.log(
|
|
2232
|
+
console.log(pc5__default.default.green("\u2713 code-server installed successfully"));
|
|
1777
2233
|
}
|
|
1778
2234
|
async function killExistingCodeServer() {
|
|
1779
2235
|
try {
|
|
@@ -1812,7 +2268,7 @@ async function startCodeServer(cwd) {
|
|
|
1812
2268
|
}
|
|
1813
2269
|
} catch {
|
|
1814
2270
|
}
|
|
1815
|
-
console.log(
|
|
2271
|
+
console.log(pc5__default.default.cyan(`Starting code-server in ${workDir}...`));
|
|
1816
2272
|
const codeServer = child_process.spawn(
|
|
1817
2273
|
binPath,
|
|
1818
2274
|
[
|
|
@@ -1831,7 +2287,7 @@ async function startCodeServer(cwd) {
|
|
|
1831
2287
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1832
2288
|
}
|
|
1833
2289
|
);
|
|
1834
|
-
console.log(
|
|
2290
|
+
console.log(pc5__default.default.green(`\u2713 code-server running at http://localhost:8081/?folder=${encodeURIComponent(workDir)}`));
|
|
1835
2291
|
return codeServer;
|
|
1836
2292
|
}
|
|
1837
2293
|
var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
@@ -1895,7 +2351,7 @@ function getDaemonPid() {
|
|
|
1895
2351
|
return null;
|
|
1896
2352
|
}
|
|
1897
2353
|
}
|
|
1898
|
-
var VERSION = "0.0.
|
|
2354
|
+
var VERSION = "0.0.6";
|
|
1899
2355
|
var PROJECT_DIR = process.cwd();
|
|
1900
2356
|
function promptUser(question) {
|
|
1901
2357
|
const rl = readline__default.default.createInterface({
|
|
@@ -1911,19 +2367,19 @@ function promptUser(question) {
|
|
|
1911
2367
|
}
|
|
1912
2368
|
async function startWithCodeServer() {
|
|
1913
2369
|
if (!isCodeServerInstalled()) {
|
|
1914
|
-
console.log(
|
|
2370
|
+
console.log(pc5__default.default.cyan("First run detected. Setting up code-server..."));
|
|
1915
2371
|
try {
|
|
1916
2372
|
await installCodeServer();
|
|
1917
2373
|
} catch (error) {
|
|
1918
|
-
console.error(
|
|
1919
|
-
console.log(
|
|
2374
|
+
console.error(pc5__default.default.red(`Failed to install code-server: ${error.message}`));
|
|
2375
|
+
console.log(pc5__default.default.yellow("Continuing without code-server..."));
|
|
1920
2376
|
}
|
|
1921
2377
|
}
|
|
1922
2378
|
if (isCodeServerInstalled()) {
|
|
1923
2379
|
try {
|
|
1924
2380
|
await startCodeServer(PROJECT_DIR);
|
|
1925
2381
|
} catch (error) {
|
|
1926
|
-
console.error(
|
|
2382
|
+
console.error(pc5__default.default.red(`Failed to start code-server: ${error.message}`));
|
|
1927
2383
|
}
|
|
1928
2384
|
}
|
|
1929
2385
|
main();
|
|
@@ -1931,7 +2387,7 @@ async function startWithCodeServer() {
|
|
|
1931
2387
|
var args = process.argv.slice(2);
|
|
1932
2388
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
1933
2389
|
console.log(`
|
|
1934
|
-
${
|
|
2390
|
+
${pc5__default.default.bold("amai cli")} ${pc5__default.default.gray(VERSION)}
|
|
1935
2391
|
|
|
1936
2392
|
Usage: amai [command] [options]
|
|
1937
2393
|
|
|
@@ -1959,32 +2415,32 @@ Example:
|
|
|
1959
2415
|
}
|
|
1960
2416
|
if (args[0] === "start") {
|
|
1961
2417
|
if (isDaemonRunning()) {
|
|
1962
|
-
console.log(
|
|
2418
|
+
console.log(pc5__default.default.yellow("Daemon is already running"));
|
|
1963
2419
|
process.exit(0);
|
|
1964
2420
|
}
|
|
1965
2421
|
if (!isAuthenticated()) {
|
|
1966
|
-
console.log(
|
|
2422
|
+
console.log(pc5__default.default.yellow("Not authenticated. Please log in first."));
|
|
1967
2423
|
login().then(() => {
|
|
1968
|
-
console.log(
|
|
2424
|
+
console.log(pc5__default.default.green("Starting daemon..."));
|
|
1969
2425
|
startDaemon();
|
|
1970
|
-
console.log(
|
|
2426
|
+
console.log(pc5__default.default.green("Daemon started successfully"));
|
|
1971
2427
|
process.exit(0);
|
|
1972
2428
|
}).catch(() => {
|
|
1973
|
-
console.error(
|
|
2429
|
+
console.error(pc5__default.default.red("Login failed. Cannot start daemon."));
|
|
1974
2430
|
process.exit(1);
|
|
1975
2431
|
});
|
|
1976
2432
|
} else {
|
|
1977
2433
|
startDaemon();
|
|
1978
|
-
console.log(
|
|
1979
|
-
console.log(
|
|
2434
|
+
console.log(pc5__default.default.green(pc5__default.default.bold("amai started in background mode")));
|
|
2435
|
+
console.log(pc5__default.default.gray(`Tip: You can check status any time with ${pc5__default.default.bold("amai status")}`));
|
|
1980
2436
|
process.exit(0);
|
|
1981
2437
|
}
|
|
1982
2438
|
}
|
|
1983
2439
|
if (args[0] === "stop") {
|
|
1984
2440
|
if (stopDaemon()) {
|
|
1985
|
-
console.log(
|
|
2441
|
+
console.log(pc5__default.default.green("Daemon stopped successfully"));
|
|
1986
2442
|
} else {
|
|
1987
|
-
console.log(
|
|
2443
|
+
console.log(pc5__default.default.yellow("Daemon was not running"));
|
|
1988
2444
|
}
|
|
1989
2445
|
process.exit(0);
|
|
1990
2446
|
}
|
|
@@ -1992,9 +2448,9 @@ if (args[0] === "status") {
|
|
|
1992
2448
|
const running = isDaemonRunning();
|
|
1993
2449
|
const pid = getDaemonPid();
|
|
1994
2450
|
if (running && pid) {
|
|
1995
|
-
console.log(
|
|
2451
|
+
console.log(pc5__default.default.green(`Daemon is running (PID: ${pid})`));
|
|
1996
2452
|
} else {
|
|
1997
|
-
console.log(
|
|
2453
|
+
console.log(pc5__default.default.yellow("Daemon is not running"));
|
|
1998
2454
|
}
|
|
1999
2455
|
process.exit(0);
|
|
2000
2456
|
}
|
|
@@ -2002,36 +2458,36 @@ if (args[0] === "project") {
|
|
|
2002
2458
|
if (args[1] === "add") {
|
|
2003
2459
|
const projectPath = args[2];
|
|
2004
2460
|
if (!projectPath) {
|
|
2005
|
-
console.error(
|
|
2461
|
+
console.error(pc5__default.default.red("Please provide a project path"));
|
|
2006
2462
|
console.log("Usage: amai project add <path>");
|
|
2007
2463
|
process.exit(1);
|
|
2008
2464
|
}
|
|
2009
2465
|
const resolvedPath = path9__default.default.resolve(projectPath);
|
|
2010
2466
|
if (!fs3__default.default.existsSync(resolvedPath)) {
|
|
2011
|
-
console.error(
|
|
2467
|
+
console.error(pc5__default.default.red(`Path does not exist: ${resolvedPath}`));
|
|
2012
2468
|
process.exit(1);
|
|
2013
2469
|
}
|
|
2014
2470
|
if (!fs3__default.default.statSync(resolvedPath).isDirectory()) {
|
|
2015
|
-
console.error(
|
|
2471
|
+
console.error(pc5__default.default.red(`Path is not a directory: ${resolvedPath}`));
|
|
2016
2472
|
process.exit(1);
|
|
2017
2473
|
}
|
|
2018
2474
|
const projectId = path9__default.default.basename(resolvedPath);
|
|
2019
2475
|
projectRegistry.register(projectId, resolvedPath);
|
|
2020
|
-
console.log(
|
|
2476
|
+
console.log(pc5__default.default.green(`Project registered: ${projectId} -> ${resolvedPath}`));
|
|
2021
2477
|
process.exit(0);
|
|
2022
2478
|
} else if (args[1] === "list") {
|
|
2023
2479
|
const projects = projectRegistry.list();
|
|
2024
2480
|
if (projects.length === 0) {
|
|
2025
|
-
console.log(
|
|
2481
|
+
console.log(pc5__default.default.yellow("No projects registered"));
|
|
2026
2482
|
} else {
|
|
2027
|
-
console.log(
|
|
2483
|
+
console.log(pc5__default.default.bold("Registered projects:"));
|
|
2028
2484
|
projects.forEach((project) => {
|
|
2029
|
-
console.log(` ${
|
|
2485
|
+
console.log(` ${pc5__default.default.cyan(project.id)}: ${project.cwd} ${project.active ? pc5__default.default.green("(active)") : ""}`);
|
|
2030
2486
|
});
|
|
2031
2487
|
}
|
|
2032
2488
|
process.exit(0);
|
|
2033
2489
|
} else {
|
|
2034
|
-
console.error(
|
|
2490
|
+
console.error(pc5__default.default.red(`Unknown project command: ${args[1]}`));
|
|
2035
2491
|
console.log('Use "amai project add <path>" or "amai project list"');
|
|
2036
2492
|
process.exit(1);
|
|
2037
2493
|
}
|
|
@@ -2040,39 +2496,39 @@ if (args[0] === "login" || args[0] === "--login") {
|
|
|
2040
2496
|
login().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
2041
2497
|
} else if (args[0] === "logout" || args[0] === "--logout") {
|
|
2042
2498
|
logout();
|
|
2043
|
-
console.log(
|
|
2499
|
+
console.log(pc5__default.default.green("Logged out successfully"));
|
|
2044
2500
|
process.exit(0);
|
|
2045
2501
|
} else {
|
|
2046
2502
|
(async () => {
|
|
2047
2503
|
if (!isAuthenticated()) {
|
|
2048
|
-
console.log(
|
|
2504
|
+
console.log(pc5__default.default.yellow("Not authenticated. Please log in first."));
|
|
2049
2505
|
try {
|
|
2050
2506
|
await login();
|
|
2051
2507
|
} catch {
|
|
2052
|
-
console.error(
|
|
2508
|
+
console.error(pc5__default.default.red("Login failed. Cannot start server."));
|
|
2053
2509
|
process.exit(1);
|
|
2054
2510
|
}
|
|
2055
2511
|
}
|
|
2056
2512
|
if (isDaemonRunning()) {
|
|
2057
|
-
console.log(
|
|
2513
|
+
console.log(pc5__default.default.yellow('Daemon is already running. Use "amai status" to check its status.'));
|
|
2058
2514
|
process.exit(0);
|
|
2059
2515
|
}
|
|
2060
2516
|
console.log("");
|
|
2061
|
-
console.log(
|
|
2062
|
-
console.log(
|
|
2517
|
+
console.log(pc5__default.default.bold("How would you like to run amai?"));
|
|
2518
|
+
console.log(pc5__default.default.gray("Background mode is highly recommended for better performance and stability."));
|
|
2063
2519
|
const answer = await promptUser(
|
|
2064
|
-
|
|
2520
|
+
pc5__default.default.cyan("Run in background? (Y/n): ")
|
|
2065
2521
|
);
|
|
2066
2522
|
const runInBackground = answer === "" || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
2067
2523
|
if (runInBackground) {
|
|
2068
|
-
console.log(
|
|
2524
|
+
console.log(pc5__default.default.green("Starting daemon in background..."));
|
|
2069
2525
|
startDaemon();
|
|
2070
|
-
console.log(
|
|
2071
|
-
console.log(
|
|
2072
|
-
console.log(
|
|
2526
|
+
console.log(pc5__default.default.green("Daemon started successfully!"));
|
|
2527
|
+
console.log(pc5__default.default.gray('Use "amai status" to check daemon status.'));
|
|
2528
|
+
console.log(pc5__default.default.gray('Use "amai stop" to stop the daemon.'));
|
|
2073
2529
|
process.exit(0);
|
|
2074
2530
|
} else {
|
|
2075
|
-
console.log(
|
|
2531
|
+
console.log(pc5__default.default.yellow("Starting in foreground mode..."));
|
|
2076
2532
|
startWithCodeServer();
|
|
2077
2533
|
}
|
|
2078
2534
|
})();
|