axmap-cli 1.4.0 → 1.6.0

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.
Files changed (46) hide show
  1. package/README.ko.md +36 -0
  2. package/README.md +36 -0
  3. package/bin/axmap.mjs +6 -0
  4. package/mcp/README.md +6 -0
  5. package/mcp/pipeline.html +157 -0
  6. package/mcp/server.mjs +130 -20
  7. package/package.json +11 -3
  8. package/src/agentcli.mjs +17 -14
  9. package/src/agentcli.mts +19 -0
  10. package/src/bridge.mjs +299 -210
  11. package/src/bridge.mts +281 -0
  12. package/src/external-console.mjs +159 -0
  13. package/src/external-console.mts +128 -0
  14. package/src/managed-session.mjs +263 -169
  15. package/src/managed-session.mts +198 -0
  16. package/src/monitor-cli.mjs +87 -0
  17. package/src/monitor-cli.mts +64 -0
  18. package/src/monitor.mjs +240 -0
  19. package/src/monitor.mts +195 -0
  20. package/src/node-pty.d.ts +13 -0
  21. package/src/pipeline-mcp.mjs +109 -0
  22. package/src/pipeline-mcp.mts +103 -0
  23. package/src/pipeline-statusline.mjs +135 -0
  24. package/src/pipeline-statusline.mts +106 -0
  25. package/src/session-dialogue.mjs +168 -116
  26. package/src/session-dialogue.mts +136 -0
  27. package/src/session-status.mjs +54 -0
  28. package/src/session-status.mts +50 -0
  29. package/src/session-types.mjs +7 -0
  30. package/src/session-types.mts +118 -0
  31. package/src/session-usage.mjs +33 -28
  32. package/src/session-usage.mts +39 -0
  33. package/src/terminal-bridge.mjs +294 -209
  34. package/src/terminal-bridge.mts +235 -0
  35. package/src/windows-console.mjs +42 -0
  36. package/src/windows-console.mts +29 -0
  37. package/tools/bench-console.mjs +70 -0
  38. package/tools/bridge.mjs +20 -6
  39. package/tools/build.mjs +39 -0
  40. package/tools/monitor.mjs +5 -0
  41. package/tools/pipeline.mjs +26 -0
  42. package/tools/run-tests.mjs +3 -2
  43. package/tools/run.mjs +6 -2
  44. package/tools/setup.mjs +1 -1
  45. package/tools/windows-console.cs +116 -0
  46. package/tools/windows-console.ps1 +91 -0
@@ -0,0 +1,116 @@
1
+ // Uses only the Windows .NET Framework; no SDK, NuGet or model process required.
2
+ using System;
3
+ using System.Text;
4
+ using System.Diagnostics;
5
+ using System.Runtime.InteropServices;
6
+ using System.ComponentModel;
7
+ using System.Management;
8
+ using System.Collections.Generic;
9
+ using System.Web.Script.Serialization;
10
+ using System.Text.RegularExpressions;
11
+
12
+ public static class AxConsole {
13
+ [StructLayout(LayoutKind.Sequential)] public struct Coord { public short X,Y; }
14
+ [StructLayout(LayoutKind.Sequential)] public struct Rect { public short Left,Top,Right,Bottom; }
15
+ [StructLayout(LayoutKind.Sequential)] public struct Info { public Coord Size,Cursor; public short Attr; public Rect Window; public Coord Max; }
16
+ [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode, Size=20)] public struct Record {
17
+ [FieldOffset(0)] public short Type;
18
+ [FieldOffset(4)] public int Down;
19
+ [FieldOffset(8)] public short Repeat;
20
+ [FieldOffset(10)] public short Key;
21
+ [FieldOffset(14)] public char Char;
22
+ }
23
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool AttachConsole(uint pid);
24
+ [DllImport("kernel32.dll")] static extern bool FreeConsole();
25
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateFile(string name,uint access,uint share,IntPtr sec,uint mode,uint flags,IntPtr template);
26
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool GetConsoleScreenBufferInfo(IntPtr h,out Info info);
27
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool ReadConsoleOutputCharacter(IntPtr h,[Out] char[] text,uint count,Coord at,out uint read);
28
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool WriteConsoleInput(IntPtr h,Record[] input,uint count,out uint written);
29
+ [DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h);
30
+ static void Check(bool ok) { if(!ok) throw new Win32Exception(Marshal.GetLastWin32Error()); }
31
+ static IntPtr Open(string name) {
32
+ var h=CreateFile(name,0xC0000000,3,IntPtr.Zero,3,0,IntPtr.Zero);
33
+ if(h==new IntPtr(-1)) throw new Win32Exception(Marshal.GetLastWin32Error());
34
+ return h;
35
+ }
36
+ static string Snapshot() {
37
+ var h=Open("CONOUT$"); try {
38
+ Info info; Check(GetConsoleScreenBufferInfo(h,out info));
39
+ var text=new StringBuilder();
40
+ for(int y=info.Window.Top;y<=info.Window.Bottom;y++) {
41
+ uint read; var row=new char[info.Size.X];
42
+ Check(ReadConsoleOutputCharacter(h,row,(uint)row.Length,new Coord { X=0,Y=(short)y },out read));
43
+ text.AppendLine(new string(row,0,(int)read).TrimEnd());
44
+ }
45
+ return text.ToString();
46
+ } finally { CloseHandle(h); }
47
+ }
48
+ static void Send(string text) {
49
+ var h=Open("CONIN$"); try {
50
+ var records=new Record[text.Length*2];
51
+ for(int i=0;i<text.Length;i++) {
52
+ var r=new Record {Type=1,Down=1,Repeat=1,Char=text[i],Key=(short)(text[i]=='\r'?13:0)};
53
+ records[i*2]=r; r.Down=0; records[i*2+1]=r;
54
+ }
55
+ uint written; Check(WriteConsoleInput(h,records,(uint)records.Length,out written));
56
+ if(written!=records.Length) throw new Exception("Partial console delivery; do not retry automatically.");
57
+ } finally { CloseHandle(h); }
58
+ }
59
+ static string Get(Dictionary<string,object> payload, string key) {
60
+ object value; return payload.TryGetValue(key,out value) ? value as string : null;
61
+ }
62
+ static Dictionary<string,object> Identity(int pid) {
63
+ using(var proc=Process.GetProcessById(pid)) {
64
+ if(proc.ProcessName!="agy") throw new Exception("Target is not agy.exe");
65
+ return new Dictionary<string,object> { {"pid",pid}, {"startedAt",proc.StartTime.ToUniversalTime().ToString("o")} };
66
+ }
67
+ }
68
+ static bool Headless(string commandLine) {
69
+ return Regex.IsMatch(commandLine ?? "", @"--input-format|--print(?:\s|=|$)|(?:^|\s)-p(?:\s|$)");
70
+ }
71
+ public static int Main(string[] args) {
72
+ Console.InputEncoding=new UTF8Encoding(false); Console.OutputEncoding=new UTF8Encoding(false);
73
+ // AttachConsole replaces standard handles. Preserve the caller's pipe first.
74
+ var reply=Console.Out;
75
+ var json=new JavaScriptSerializer { MaxJsonLength=1024*1024 };
76
+ try {
77
+ int pid=int.Parse(args[0]); var action=args[1];
78
+ if(pid<=0) throw new Exception("A positive PID is required");
79
+ var payload=json.Deserialize<Dictionary<string,object>>(Console.In.ReadToEnd());
80
+ if(action!="snapshot" && action!="send" && action!="ancestor") throw new Exception("Unsupported console action");
81
+ if(action=="ancestor") {
82
+ for(int i=0;i<12 && pid>0;i++) {
83
+ using(var entry=new ManagementObject("Win32_Process.Handle='"+pid+"'")) {
84
+ entry.Get();
85
+ if((string)entry["Name"]=="agy.exe") {
86
+ var result=Headless((string)entry["CommandLine"]) ? new Dictionary<string,object> {{"ignored",true}} : Identity(pid);
87
+ reply.WriteLine(json.Serialize(result)); return 0;
88
+ }
89
+ pid=Convert.ToInt32(entry["ParentProcessId"]);
90
+ }
91
+ }
92
+ throw new Exception("No interactive agy ancestor found");
93
+ }
94
+ var identity=Identity(pid);
95
+ using(var entry=new ManagementObject("Win32_Process.Handle='"+pid+"'")) {
96
+ entry.Get();
97
+ if(Headless((string)entry["CommandLine"])) throw new Exception("Target is not an interactive agy console");
98
+ }
99
+ if(Get(payload,"startedAt")!=null && Get(payload,"startedAt")!=(string)identity["startedAt"]) throw new Exception("Process identity changed");
100
+ FreeConsole(); Check(AttachConsole((uint)pid));
101
+ var screen=Snapshot();
102
+ if(action=="send") {
103
+ if(Get(payload,"expectedScreen")!=screen) throw new Exception("Console changed before delivery; input was not sent");
104
+ var text=Get(payload,"text");
105
+ if(text==null || text.Length>65536) throw new Exception("Invalid console input");
106
+ // Recheck identity immediately before mutating the console input queue.
107
+ if((string)Identity(pid)["startedAt"]!=(string)identity["startedAt"]) throw new Exception("Process identity changed");
108
+ Send(text);
109
+ }
110
+ FreeConsole(); identity["screen"]=screen;
111
+ reply.WriteLine(json.Serialize(identity)); return 0;
112
+ } catch(Exception e) {
113
+ FreeConsole(); reply.WriteLine(json.Serialize(new Dictionary<string,object> {{"error",e.Message}})); return 1;
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,91 @@
1
+ param([int]$TargetPid, [string]$Action = 'snapshot')
2
+ $ErrorActionPreference = 'Stop'
3
+ $utf8 = New-Object System.Text.UTF8Encoding($false)
4
+ [Console]::InputEncoding = $utf8
5
+ [Console]::OutputEncoding = $utf8
6
+ $payload = [Console]::In.ReadToEnd() | ConvertFrom-Json
7
+ # Keep the original pipe writer: AttachConsole replaces the standard handles.
8
+ $reply = [Console]::Out
9
+ if ($Action -eq 'ancestor') {
10
+ for ($i = 0; $i -lt 12 -and $TargetPid -gt 0; $i++) {
11
+ $entry = Get-CimInstance Win32_Process -Filter "ProcessId=$TargetPid"
12
+ if (!$entry) { break }
13
+ if ($entry.Name -eq 'agy.exe') {
14
+ if ($entry.CommandLine -match '--input-format|--print(?:\s|=|$)|(?:^|\s)-p(?:\s|$)') { $reply.WriteLine('{"ignored":true}'); exit 0 }
15
+ $proc = Get-Process -Id $TargetPid
16
+ $reply.WriteLine((@{ pid=$TargetPid; startedAt=$proc.StartTime.ToUniversalTime().ToString('o') } | ConvertTo-Json -Compress))
17
+ exit 0
18
+ }
19
+ $TargetPid = $entry.ParentProcessId
20
+ }
21
+ throw 'No interactive agy ancestor found'
22
+ }
23
+ Add-Type -TypeDefinition @'
24
+ using System;
25
+ using System.Text;
26
+ using System.Runtime.InteropServices;
27
+ using System.ComponentModel;
28
+ public static class AxConsole {
29
+ [StructLayout(LayoutKind.Sequential)] public struct Coord { public short X,Y; }
30
+ [StructLayout(LayoutKind.Sequential)] public struct Rect { public short Left,Top,Right,Bottom; }
31
+ [StructLayout(LayoutKind.Sequential)] public struct Info { public Coord Size,Cursor; public short Attr; public Rect Window; public Coord Max; }
32
+ [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode, Size=20)] public struct Record {
33
+ [FieldOffset(0)] public short Type;
34
+ [FieldOffset(4)] public int Down;
35
+ [FieldOffset(8)] public short Repeat;
36
+ [FieldOffset(10)] public short Key;
37
+ [FieldOffset(14)] public char Char;
38
+ }
39
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool AttachConsole(uint pid);
40
+ [DllImport("kernel32.dll")] public static extern bool FreeConsole();
41
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateFile(string name,uint access,uint share,IntPtr sec,uint mode,uint flags,IntPtr template);
42
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool GetConsoleScreenBufferInfo(IntPtr h,out Info info);
43
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool ReadConsoleOutputCharacter(IntPtr h,[Out] char[] text,uint count,Coord at,out uint read);
44
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool WriteConsoleInput(IntPtr h,Record[] input,uint count,out uint written);
45
+ [DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h);
46
+ static void Check(bool ok) { if(!ok) throw new Win32Exception(Marshal.GetLastWin32Error()); }
47
+ public static void Attach(int pid) { FreeConsole(); Check(AttachConsole((uint)pid)); }
48
+ static IntPtr Open(string name) { var h=CreateFile(name,0xC0000000,3,IntPtr.Zero,3,0,IntPtr.Zero); if(h==new IntPtr(-1)) throw new Win32Exception(Marshal.GetLastWin32Error()); return h; }
49
+ public static string Snapshot() {
50
+ var h=Open("CONOUT$"); try {
51
+ Info info; Check(GetConsoleScreenBufferInfo(h,out info));
52
+ var text=new StringBuilder();
53
+ for(short y=info.Window.Top;y<=info.Window.Bottom;y++) {
54
+ uint read; var row=new char[info.Size.X]; Check(ReadConsoleOutputCharacter(h,row,(uint)row.Length,new Coord { X=0,Y=y },out read)); text.AppendLine(new string(row,0,(int)read).TrimEnd());
55
+ }
56
+ return text.ToString();
57
+ } finally { CloseHandle(h); }
58
+ }
59
+ public static void Send(string text) {
60
+ var h=Open("CONIN$"); try {
61
+ var records=new Record[text.Length*2];
62
+ for(int i=0;i<text.Length;i++) {
63
+ var r=new Record {Type=1,Down=1,Repeat=1,Char=text[i],Key=(short)(text[i]=='\r'?13:0)};
64
+ records[i*2]=r; r.Down=0; records[i*2+1]=r;
65
+ }
66
+ uint written; Check(WriteConsoleInput(h,records,(uint)records.Length,out written));
67
+ if(written!=records.Length) throw new Exception("Partial console delivery; do not retry automatically.");
68
+ } finally { CloseHandle(h); }
69
+ }
70
+ }
71
+ '@
72
+ try {
73
+ $proc = Get-Process -Id $TargetPid
74
+ if ($proc.ProcessName -ne 'agy') { throw 'Target is not agy.exe' }
75
+ $commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId=$TargetPid").CommandLine
76
+ if ($commandLine -match '--input-format|--print(?:\s|=|$)|(?:^|\s)-p(?:\s|$)') { throw 'Target is not an interactive agy console' }
77
+ if ($payload.startedAt -and $proc.StartTime.ToUniversalTime().ToString('o') -ne $payload.startedAt) { throw 'Process identity changed' }
78
+ [AxConsole]::Attach($TargetPid)
79
+ $screen = [AxConsole]::Snapshot()
80
+ if ($Action -eq 'send') {
81
+ if ($payload.expectedScreen -cne $screen) { throw 'Console changed before delivery; input was not sent' }
82
+ [AxConsole]::Send($payload.text)
83
+ }
84
+ $result = @{ screen = $screen; startedAt = $proc.StartTime.ToUniversalTime().ToString('o'); pid = $TargetPid }
85
+ [AxConsole]::FreeConsole() | Out-Null
86
+ $reply.WriteLine(($result | ConvertTo-Json -Compress))
87
+ } catch {
88
+ [AxConsole]::FreeConsole() | Out-Null
89
+ $reply.WriteLine((@{ error = $_.Exception.Message } | ConvertTo-Json -Compress))
90
+ exit 1
91
+ }