deadbyte-mcp 0.15.42 → 0.15.44
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/MANIFEST.SHA256 +131 -68
- package/PROVENANCE.json +37 -13
- package/README.txt +18 -18
- package/RELEASE.json +40 -14
- package/SBOM.cdx.json +14 -2
- package/TEST-MATRIX.json +52 -10
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/bin/deadbyte-native-inspect-r67.exe +0 -0
- package/bin/deadbyte-native-inspect-r67.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R67-ALPHA-REFERENCE-DIFFERENTIAL.md +42 -0
- package/docs/R67-ALPHA-STATIC-COMPARISON.json +205 -0
- package/docs/R67-CANDIDATE-PARENT.json +10 -0
- package/docs/R67-EVIDENCE-RECONCILE-CLOSEOUT-20261011.md +64 -0
- package/docs/R67-FULL-REGRESSION-UNKNOWN.json +27 -0
- package/docs/R67-NATIVE-INSPECT-BINARY.json +11 -0
- package/docs/R67-NPM-EXDEV-PRESERVATION-20261011.md +39 -0
- package/docs/R67-OBSERVATION-TRUTH.md +82 -0
- package/docs/R67-REFERENCE-GAP-MATRIX.json +57 -0
- package/docs/R67-REGRESSION-CLOSURE-20261011.md +24 -0
- package/native/deadbyte-native-inspect-r67.cpp +252 -0
- package/package.json +7 -3
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/build-native-inspector-r67.ps1 +28 -0
- package/scripts/gate-windows-r66-prepublish.ps1 +65 -0
- package/scripts/gate-windows-r67.ps1 +56 -0
- package/scripts/npm-preserve-artifact-r67.mjs +99 -0
- package/scripts/npm-release-r48.mjs +10 -2
- package/scripts/npm-release-readiness-r67.mjs +26 -0
- package/scripts/r67-acquire-alpha-reference.mjs +95 -0
- package/scripts/r67-audit-nonpass-source.mjs +39 -0
- package/scripts/r67-compare-desktop-alpha.mjs +48 -0
- package/scripts/r67-diagnose-failed-shard.mjs +23 -0
- package/scripts/r67-durable-batch.mjs +42 -0
- package/scripts/r67-focused-gate.mjs +58 -0
- package/scripts/r67-full-regression.mjs +28 -0
- package/scripts/r67-independent-observation-verify.mjs +36 -0
- package/scripts/r67-independent-runtime-observation-verify.mjs +63 -0
- package/scripts/r67-nonpass-adjudicate.mjs +20 -0
- package/scripts/r67-publish-boundary-audit.mjs +32 -0
- package/scripts/r67-regression-reconcile.mjs +10 -0
- package/scripts/r67-regression-shard-run.mjs +70 -0
- package/scripts/r67-run-shard-batch.mjs +18 -0
- package/scripts/r67-stage-candidate.mjs +30 -0
- package/scripts/r67-verify-parent-ping.mjs +22 -0
- package/scripts/release-manifest.mjs +1 -0
- package/src/activation-continuity-engine-r65.mjs +2 -1
- package/src/autonomous-runtime.mjs +1 -1
- package/src/deadbyte-cli.mjs +33 -1
- package/src/doctor-runtime-r58.mjs +3 -1
- package/src/machine-fs-smoke-client.mjs +13 -5
- package/src/machine-identity.mjs +59 -2
- package/src/machine-mcp-tools.mjs +8 -0
- package/src/mcp-server.mjs +11 -2
- package/src/native-inspector-r67.mjs +66 -0
- package/src/r67-diagnostic-export.mjs +118 -0
- package/src/r67-diagnostics-verify.mjs +82 -0
- package/src/r67-evidence-reconcile.mjs +128 -0
- package/src/r67-native-observation-authority.mjs +45 -0
- package/src/r67-observation-receipt.mjs +43 -0
- package/src/r67-regression-shards.mjs +176 -0
- package/src/remote-live-log.mjs +14 -10
- package/src/tool-audit.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -1
- package/test/autonomous-loop.test.mjs +11 -6
- package/test/autonomous-runtime.test.mjs +1 -1
- package/test/coding-plane.test.mjs +1 -1
- package/test/full-max-catalog-r46.test.mjs +1 -1
- package/test/output-schema.test.mjs +1 -1
- package/test/r51-live-log-polling.test.mjs +12 -0
- package/test/r53-release-contract.test.mjs +1 -1
- package/test/r54-release-contract.test.mjs +1 -1
- package/test/r55-release-contract.test.mjs +1 -1
- package/test/r56-release-contract.test.mjs +1 -1
- package/test/r57-release-contract.test.mjs +1 -1
- package/test/r58-bundled-bridge.test.mjs +1 -1
- package/test/r58-doctor-runtime.test.mjs +18 -0
- package/test/r58-release-contract.test.mjs +2 -2
- package/test/r58-windows-gate.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r60-release-contract.test.mjs +3 -3
- package/test/r61-release-contract.test.mjs +1 -1
- package/test/r62-release-contract.test.mjs +1 -1
- package/test/r63-release-contract.test.mjs +1 -1
- package/test/r64-release-contract.test.mjs +1 -1
- package/test/r65-epoch-native-activation-integration.test.mjs +4 -4
- package/test/r65-release-contract.test.mjs +2 -1
- package/test/r66-release-contract.test.mjs +57 -0
- package/test/r67-cli-integration.test.mjs +41 -0
- package/test/r67-diagnostics-cli.test.mjs +60 -0
- package/test/r67-diagnostics-verify.test.mjs +49 -0
- package/test/r67-diagnostics-zip-oracle.test.mjs +38 -0
- package/test/r67-diagnostics.test.mjs +60 -0
- package/test/r67-evidence-reconcile.test.mjs +186 -0
- package/test/r67-isolated-slot-rollback-restore.test.mjs +62 -0
- package/test/r67-live-log-secret-canary.test.mjs +44 -0
- package/test/r67-machine-fs-smoke-epoch.test.mjs +22 -0
- package/test/r67-native-inspector.test.mjs +131 -0
- package/test/r67-native-observation-authority.test.mjs +49 -0
- package/test/r67-npm-cross-volume.test.mjs +74 -0
- package/test/r67-npm-publish-gate.test.mjs +38 -0
- package/test/r67-regression-shards.test.mjs +184 -0
- package/test/r67-release-contract.test.mjs +44 -0
- package/test/r67-runtime-observation-binding.test.mjs +86 -0
- package/test/r67-server-observation-wiring.test.mjs +12 -0
- package/test/r67-signed-receipt.test.mjs +66 -0
- package/test/r67-skip-rationale.test.mjs +31 -0
- package/test/r67-tool-annotations.test.mjs +21 -0
- package/test/r67-windows-gate-argv.test.mjs +13 -0
- package/test/release-version.test.mjs +1 -1
- package/test/remote-live-log-r36.test.mjs +10 -0
- package/test/remote-live-log-r37.test.mjs +29 -0
- package/test/semantic-multilang.test.mjs +1 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#define WIN32_LEAN_AND_MEAN
|
|
2
|
+
#define NOMINMAX
|
|
3
|
+
#include <windows.h>
|
|
4
|
+
#include <tlhelp32.h>
|
|
5
|
+
#include <bcrypt.h>
|
|
6
|
+
#include <winnt.h>
|
|
7
|
+
#include <cstdint>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <iomanip>
|
|
10
|
+
#include <limits>
|
|
11
|
+
#include <iostream>
|
|
12
|
+
#include <sstream>
|
|
13
|
+
#include <stdexcept>
|
|
14
|
+
#include <string>
|
|
15
|
+
#include <vector>
|
|
16
|
+
#include <algorithm>
|
|
17
|
+
|
|
18
|
+
#pragma comment(lib,"bcrypt.lib")
|
|
19
|
+
struct Handle { HANDLE h=INVALID_HANDLE_VALUE; explicit Handle(HANDLE p):h(p){} ~Handle(){if(h&&h!=INVALID_HANDLE_VALUE)CloseHandle(h);} Handle(const Handle&)=delete; Handle& operator=(const Handle&)=delete; };
|
|
20
|
+
static std::string hex(const uint64_t x){std::ostringstream s;s<<"0x"<<std::hex<<x;return s.str();}
|
|
21
|
+
static std::string escape(const std::string& in){
|
|
22
|
+
std::ostringstream s;s<<'"';
|
|
23
|
+
for(unsigned char c:in){if(c=='"'||c=='\\')s<<'\\'<<char(c);else if(c=='\n')s<<"\\n";else if(c=='\r')s<<"\\r";else if(c=='\t')s<<"\\t";else if(c<32){s<<"\\u00"<<std::hex<<std::setw(2)<<std::setfill('0')<<int(c)<<std::dec;}else s<<char(c);}
|
|
24
|
+
s<<'"';return s.str();
|
|
25
|
+
}
|
|
26
|
+
static std::string utf8(const std::wstring& wide){
|
|
27
|
+
if(wide.empty())return {};
|
|
28
|
+
const int n=WideCharToMultiByte(CP_UTF8,WC_ERR_INVALID_CHARS,wide.data(),int(wide.size()),nullptr,0,nullptr,nullptr);
|
|
29
|
+
if(n<=0)throw std::runtime_error("invalid UTF-16"); std::string s(size_t(n),'\0');
|
|
30
|
+
if(WideCharToMultiByte(CP_UTF8,WC_ERR_INVALID_CHARS,wide.data(),int(wide.size()),s.data(),n,nullptr,nullptr)!=n)throw std::runtime_error("UTF-8 conversion failed");return s;
|
|
31
|
+
}
|
|
32
|
+
static std::string sha256(const uint8_t* p,size_t n){
|
|
33
|
+
BCRYPT_ALG_HANDLE alg=nullptr;BCRYPT_HASH_HANDLE ctx=nullptr;
|
|
34
|
+
if(!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&alg,BCRYPT_SHA256_ALGORITHM,nullptr,0)))throw std::runtime_error("CNG open failed");
|
|
35
|
+
DWORD length=0,read=0;
|
|
36
|
+
if(!BCRYPT_SUCCESS(BCryptGetProperty(alg,BCRYPT_HASH_LENGTH,reinterpret_cast<PUCHAR>(&length),sizeof(length),&read,0))||length!=32){BCryptCloseAlgorithmProvider(alg,0);throw std::runtime_error("CNG SHA length invalid");}
|
|
37
|
+
std::vector<uint8_t> digest(length);
|
|
38
|
+
NTSTATUS rc=BCryptCreateHash(alg,&ctx,nullptr,0,nullptr,0,0);
|
|
39
|
+
if(BCRYPT_SUCCESS(rc))rc=BCryptHashData(ctx,const_cast<PUCHAR>(p),ULONG(n),0);
|
|
40
|
+
if(BCRYPT_SUCCESS(rc))rc=BCryptFinishHash(ctx,digest.data(),length,0);
|
|
41
|
+
if(ctx)BCryptDestroyHash(ctx);BCryptCloseAlgorithmProvider(alg,0);
|
|
42
|
+
if(!BCRYPT_SUCCESS(rc))throw std::runtime_error("CNG hash failed");
|
|
43
|
+
std::ostringstream s;for(uint8_t b:digest)s<<std::hex<<std::setfill('0')<<std::setw(2)<<int(b);return s.str();
|
|
44
|
+
}
|
|
45
|
+
static std::string sha256(const std::string& s){return sha256(reinterpret_cast<const uint8_t*>(s.data()),s.size());}
|
|
46
|
+
static uint64_t fticks(FILETIME t){ULARGE_INTEGER u{};u.LowPart=t.dwLowDateTime;u.HighPart=t.dwHighDateTime;return u.QuadPart;}
|
|
47
|
+
static std::string timestamp(FILETIME t){SYSTEMTIME s{};if(!FileTimeToSystemTime(&t,&s))return "";std::ostringstream o;o<<std::setfill('0')<<std::setw(4)<<s.wYear<<"-"<<std::setw(2)<<s.wMonth<<"-"<<std::setw(2)<<s.wDay<<"T"<<std::setw(2)<<s.wHour<<":"<<std::setw(2)<<s.wMinute<<":"<<std::setw(2)<<s.wSecond<<"."<<std::setw(3)<<s.wMilliseconds<<"Z";return o.str();}
|
|
48
|
+
static std::string image(HANDLE h){
|
|
49
|
+
std::vector<wchar_t> b(32768,L'\0');DWORD size=DWORD(b.size());
|
|
50
|
+
if(!QueryFullProcessImageNameW(h,0,b.data(),&size))return "null";
|
|
51
|
+
return escape(utf8(std::wstring(b.data(),size)));
|
|
52
|
+
}
|
|
53
|
+
static std::string process_item(DWORD pid,DWORD ppid,const std::wstring& name){
|
|
54
|
+
std::ostringstream o;o<<"{\"pid\":"<<pid<<",\"parent_pid\":"<<ppid<<",\"name\":"<<escape(utf8(name));
|
|
55
|
+
Handle proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,FALSE,pid));
|
|
56
|
+
if(!proc.h){const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();}
|
|
57
|
+
FILETIME c{},e{},k{},u{};
|
|
58
|
+
if(!GetProcessTimes(proc.h,&c,&e,&k,&u)){const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();}
|
|
59
|
+
std::vector<wchar_t> filename(32768,L'\0'); DWORD chars=DWORD(filename.size());
|
|
60
|
+
if(!QueryFullProcessImageNameW(proc.h,0,filename.data(),&chars)){
|
|
61
|
+
const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();
|
|
62
|
+
}
|
|
63
|
+
const std::wstring full(filename.data(),chars);
|
|
64
|
+
const size_t leafIndex=full.find_last_of(L"\\/");
|
|
65
|
+
const std::wstring leaf=full.substr(leafIndex==std::wstring::npos?0:leafIndex+1);
|
|
66
|
+
if(CompareStringOrdinal(leaf.c_str(),-1,name.c_str(),-1,TRUE)!=CSTR_EQUAL){
|
|
67
|
+
o<<",\"state\":\"stale_or_reused\",\"snapshot_binding\":\"NAME_MISMATCH\",\"win32_error\":0}";return o.str();
|
|
68
|
+
}
|
|
69
|
+
const DWORD wait=WaitForSingleObject(proc.h,0);
|
|
70
|
+
if(wait!=WAIT_OBJECT_0&&wait!=WAIT_TIMEOUT){
|
|
71
|
+
const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();
|
|
72
|
+
}
|
|
73
|
+
o<<",\"state\":\"observed\",\"snapshot_binding\":\"UNVERIFIED\",\"creation_time_utc\":"<<escape(timestamp(c))
|
|
74
|
+
<<",\"creation_filetime\":"<<escape(std::to_string(fticks(c)))
|
|
75
|
+
<<",\"image_path\":"<<escape(utf8(full))
|
|
76
|
+
<<",\"process_exited\":"<<(wait==WAIT_OBJECT_0?"true":"false")<<",\"win32_error\":0}";
|
|
77
|
+
return o.str();
|
|
78
|
+
}
|
|
79
|
+
static std::string snapshot(size_t limit){
|
|
80
|
+
Handle snap(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0));
|
|
81
|
+
if(snap.h==INVALID_HANDLE_VALUE)throw std::runtime_error("CreateToolhelp32Snapshot failed "+std::to_string(GetLastError()));
|
|
82
|
+
PROCESSENTRY32W row{};row.dwSize=sizeof(row);
|
|
83
|
+
if(!Process32FirstW(snap.h,&row))throw std::runtime_error("Process32FirstW failed "+std::to_string(GetLastError()));
|
|
84
|
+
std::ostringstream o;o<<"{\"api\":\"CreateToolhelp32Snapshot+OpenProcess+GetProcessTimes+QueryFullProcessImageNameW\",\"processes\":[";
|
|
85
|
+
size_t count=0;bool more=false;
|
|
86
|
+
do{if(count>=limit){more=true;break;}if(count)o<<",";o<<process_item(row.th32ProcessID,row.th32ParentProcessID,row.szExeFile);++count;}while(Process32NextW(snap.h,&row));
|
|
87
|
+
o<<"],\"count\":"<<count<<",\"truncated\":"<<(more?"true":"false")<<"}";return o.str();
|
|
88
|
+
}
|
|
89
|
+
static std::string identity(DWORD pid){
|
|
90
|
+
if(pid==0)throw std::runtime_error("PID 0 has no process identity");
|
|
91
|
+
Handle proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|SYNCHRONIZE,FALSE,pid));
|
|
92
|
+
if(!proc.h)throw std::runtime_error("OpenProcess identity win32="+std::to_string(GetLastError()));
|
|
93
|
+
FILETIME c{},e{},k{},u{};
|
|
94
|
+
if(!GetProcessTimes(proc.h,&c,&e,&k,&u))throw std::runtime_error("GetProcessTimes identity win32="+std::to_string(GetLastError()));
|
|
95
|
+
const DWORD wait=WaitForSingleObject(proc.h,0);
|
|
96
|
+
if(wait!=WAIT_OBJECT_0&&wait!=WAIT_TIMEOUT)throw std::runtime_error("WaitForSingleObject identity win32="+std::to_string(GetLastError()));
|
|
97
|
+
std::ostringstream o;
|
|
98
|
+
o<<"{\"api\":\"OpenProcess+GetProcessTimes+QueryFullProcessImageNameW\",\"pid\":"<<pid
|
|
99
|
+
<<",\"creation_time_utc\":"<<escape(timestamp(c))
|
|
100
|
+
<<",\"creation_filetime\":"<<escape(std::to_string(fticks(c)))
|
|
101
|
+
<<",\"image_path\":"<<image(proc.h)
|
|
102
|
+
<<",\"process_exited\":"<<(wait==WAIT_OBJECT_0?"true":"false")<<"}";
|
|
103
|
+
return o.str();
|
|
104
|
+
}
|
|
105
|
+
static std::string memory_map(DWORD pid,size_t limit,uint64_t expectedCreation=0){
|
|
106
|
+
if(pid==0)throw std::runtime_error("PID 0 is not a process handle");
|
|
107
|
+
Handle proc(OpenProcess(PROCESS_QUERY_INFORMATION|SYNCHRONIZE,FALSE,pid));
|
|
108
|
+
if(!proc.h)throw std::runtime_error("OpenProcess PROCESS_QUERY_INFORMATION win32="+std::to_string(GetLastError()));
|
|
109
|
+
FILETIME c{},e{},k{},u{};
|
|
110
|
+
if(!GetProcessTimes(proc.h,&c,&e,&k,&u))throw std::runtime_error("GetProcessTimes win32="+std::to_string(GetLastError()));
|
|
111
|
+
const uint64_t cid=fticks(c);
|
|
112
|
+
if(expectedCreation!=0&&expectedCreation!=cid)throw std::runtime_error("PROCESS_IDENTITY_MISMATCH: PID is stale or reused");
|
|
113
|
+
std::ostringstream o;o<<"{\"api\":\"VirtualQueryEx\",\"pid\":"<<pid
|
|
114
|
+
<<",\"creation_time_utc\":"<<escape(timestamp(c))
|
|
115
|
+
<<",\"creation_filetime\":"<<escape(std::to_string(cid))
|
|
116
|
+
<<",\"image_path\":"<<image(proc.h)<<",\"regions\":[";
|
|
117
|
+
uint64_t pos=0;size_t count=0;bool end=false;DWORD error=0;
|
|
118
|
+
const uint64_t maxAddr=static_cast<uint64_t>(std::numeric_limits<uintptr_t>::max());
|
|
119
|
+
while(count<limit){
|
|
120
|
+
MEMORY_BASIC_INFORMATION mbi{};
|
|
121
|
+
SIZE_T result=VirtualQueryEx(proc.h,reinterpret_cast<LPCVOID>(static_cast<uintptr_t>(pos)),&mbi,sizeof(mbi));
|
|
122
|
+
if(!result){
|
|
123
|
+
error=GetLastError();
|
|
124
|
+
if(WaitForSingleObject(proc.h,0)==WAIT_OBJECT_0)throw std::runtime_error("PROCESS_EXITED_DURING_QUERY");
|
|
125
|
+
if(error==ERROR_INVALID_PARAMETER&&pos!=0)end=true;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
const uint64_t base=reinterpret_cast<uint64_t>(mbi.BaseAddress);
|
|
129
|
+
const uint64_t size=uint64_t(mbi.RegionSize);
|
|
130
|
+
if(!size||base>maxAddr-size)throw std::runtime_error("VirtualQueryEx non-progress/overflow");
|
|
131
|
+
if(count)o<<',';
|
|
132
|
+
o<<"{\"base\":"<<escape(hex(base))<<",\"allocation_base\":"<<escape(hex(reinterpret_cast<uint64_t>(mbi.AllocationBase)))
|
|
133
|
+
<<",\"size\":"<<escape(std::to_string(size))<<",\"state\":"<<mbi.State
|
|
134
|
+
<<",\"protect\":"<<mbi.Protect<<",\"type\":"<<mbi.Type<<"}";
|
|
135
|
+
++count;const uint64_t next=base+size;if(next<=pos)throw std::runtime_error("VirtualQueryEx non-progress");
|
|
136
|
+
pos=next;
|
|
137
|
+
}
|
|
138
|
+
if(error&&!end)throw std::runtime_error("VirtualQueryEx win32="+std::to_string(error));
|
|
139
|
+
o<<"],\"count\":"<<count<<",\"truncated\":"<<((count==limit&&!end)?"true":"false")
|
|
140
|
+
<<",\"terminal_win32_error\":"<<(end?0:error)<<",\"terminal\":\""<<(end?"end":"limit")<<"\"";
|
|
141
|
+
FILETIME c2{},e2{},k2{},u2{};
|
|
142
|
+
if(!GetProcessTimes(proc.h,&c2,&e2,&k2,&u2)||fticks(c2)!=cid)throw std::runtime_error("process identity changed during inspection");
|
|
143
|
+
const DWORD wait=WaitForSingleObject(proc.h,0);
|
|
144
|
+
if(wait!=WAIT_OBJECT_0&&wait!=WAIT_TIMEOUT)throw std::runtime_error("WaitForSingleObject win32="+std::to_string(GetLastError()));
|
|
145
|
+
o<<",\"process_exited\":"<<(wait==WAIT_OBJECT_0?"true":"false")
|
|
146
|
+
<<",\"outcome\":\""<<(wait==WAIT_OBJECT_0?"PROCESS_EXITED":"HANDLE_IDENTITY_CONFIRMED")<<"\"}";return o.str();
|
|
147
|
+
}
|
|
148
|
+
static std::vector<uint8_t> filebytes(const std::wstring& filename){
|
|
149
|
+
Handle h(CreateFileW(filename.c_str(),GENERIC_READ,FILE_SHARE_READ,nullptr,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,nullptr));
|
|
150
|
+
if(h.h==INVALID_HANDLE_VALUE)throw std::runtime_error("CreateFileW win32="+std::to_string(GetLastError()));
|
|
151
|
+
LARGE_INTEGER length{};if(!GetFileSizeEx(h.h,&length))throw std::runtime_error("GetFileSizeEx failed");
|
|
152
|
+
if(length.QuadPart<64||length.QuadPart>64*1024*1024)throw std::runtime_error("PE file size outside 64B..64MiB");
|
|
153
|
+
std::vector<uint8_t> data(size_t(length.QuadPart));
|
|
154
|
+
size_t offset=0;
|
|
155
|
+
while(offset<data.size()){DWORD received=0;DWORD chunk=DWORD(std::min<size_t>(1024*1024,data.size()-offset));if(!ReadFile(h.h,data.data()+offset,chunk,&received,nullptr)||!received)throw std::runtime_error("ReadFile failed/short");offset+=received;}
|
|
156
|
+
LARGE_INTEGER final{};if(!GetFileSizeEx(h.h,&final)||final.QuadPart!=length.QuadPart)throw std::runtime_error("PE file size changed during read");
|
|
157
|
+
return data;
|
|
158
|
+
}
|
|
159
|
+
template<typename T>static T read_at(const std::vector<uint8_t>& b,size_t off){
|
|
160
|
+
if(off>b.size()||sizeof(T)>b.size()-off)throw std::runtime_error("PE structure truncated");
|
|
161
|
+
T v{};std::memcpy(&v,b.data()+off,sizeof(T));return v;
|
|
162
|
+
}
|
|
163
|
+
static std::string pe(const std::wstring& filename){
|
|
164
|
+
const auto b=filebytes(filename);
|
|
165
|
+
const auto dos=read_at<IMAGE_DOS_HEADER>(b,0);
|
|
166
|
+
if(dos.e_magic!=IMAGE_DOS_SIGNATURE||dos.e_lfanew<sizeof(IMAGE_DOS_HEADER))throw std::runtime_error("invalid MZ/PE offset");
|
|
167
|
+
const size_t off=size_t(dos.e_lfanew);
|
|
168
|
+
if(read_at<DWORD>(b,off)!=IMAGE_NT_SIGNATURE)throw std::runtime_error("invalid PE signature");
|
|
169
|
+
const auto coff=read_at<IMAGE_FILE_HEADER>(b,off+4);
|
|
170
|
+
if(coff.NumberOfSections==0||coff.NumberOfSections>96)throw std::runtime_error("invalid number of sections");
|
|
171
|
+
const size_t opt=off+4+sizeof(IMAGE_FILE_HEADER);
|
|
172
|
+
if(opt>b.size()||coff.SizeOfOptionalHeader>b.size()-opt)throw std::runtime_error("optional header truncated");
|
|
173
|
+
const WORD magic=read_at<WORD>(b,opt);
|
|
174
|
+
uint64_t base=0;DWORD entry=0;
|
|
175
|
+
if(magic==IMAGE_NT_OPTIONAL_HDR32_MAGIC){
|
|
176
|
+
if(coff.SizeOfOptionalHeader<sizeof(IMAGE_OPTIONAL_HEADER32))throw std::runtime_error("PE32 optional header truncated");
|
|
177
|
+
const auto h=read_at<IMAGE_OPTIONAL_HEADER32>(b,opt);base=h.ImageBase;entry=h.AddressOfEntryPoint;
|
|
178
|
+
}else if(magic==IMAGE_NT_OPTIONAL_HDR64_MAGIC){
|
|
179
|
+
if(coff.SizeOfOptionalHeader<sizeof(IMAGE_OPTIONAL_HEADER64))throw std::runtime_error("PE32+ optional header truncated");
|
|
180
|
+
const auto h=read_at<IMAGE_OPTIONAL_HEADER64>(b,opt);base=h.ImageBase;entry=h.AddressOfEntryPoint;
|
|
181
|
+
}else throw std::runtime_error("unsupported optional header magic");
|
|
182
|
+
const size_t sec=opt+coff.SizeOfOptionalHeader;
|
|
183
|
+
if(sec>b.size()||size_t(coff.NumberOfSections)>((b.size()-sec)/sizeof(IMAGE_SECTION_HEADER)))throw std::runtime_error("section table truncated");
|
|
184
|
+
std::ostringstream o;o<<"{\"api\":\"CreateFileW+ReadFile\",\"format\":"<<escape(magic==IMAGE_NT_OPTIONAL_HDR64_MAGIC?"PE32+":"PE32")
|
|
185
|
+
<<",\"file_path\":"<<escape(utf8(filename))<<",\"file_size\":"<<b.size()
|
|
186
|
+
<<",\"file_sha256\":"<<escape(sha256(b.data(),b.size()))<<",\"machine\":"<<coff.Machine
|
|
187
|
+
<<",\"number_of_sections\":"<<coff.NumberOfSections<<",\"image_base\":"<<escape(hex(base))
|
|
188
|
+
<<",\"entry_rva\":"<<escape(hex(entry))<<",\"sections\":[";
|
|
189
|
+
for(size_t i=0;i<coff.NumberOfSections;++i){
|
|
190
|
+
const auto s=read_at<IMAGE_SECTION_HEADER>(b,sec+i*sizeof(IMAGE_SECTION_HEADER));
|
|
191
|
+
std::string name(reinterpret_cast<const char*>(s.Name),8);const auto zero=name.find('\0');if(zero!=std::string::npos)name.resize(zero);
|
|
192
|
+
if(s.SizeOfRawData&&(size_t(s.PointerToRawData)>b.size()||size_t(s.SizeOfRawData)>b.size()-size_t(s.PointerToRawData)))throw std::runtime_error("section raw data exceeds file");
|
|
193
|
+
if(i)o<<',';
|
|
194
|
+
o<<"{\"name\":"<<escape(name)<<",\"rva\":"<<escape(hex(s.VirtualAddress))
|
|
195
|
+
<<",\"virtual_size\":"<<s.Misc.VirtualSize<<",\"raw_offset\":"<<s.PointerToRawData
|
|
196
|
+
<<",\"raw_size\":"<<s.SizeOfRawData<<",\"characteristics\":"<<s.Characteristics<<"}";
|
|
197
|
+
}
|
|
198
|
+
o<<"]}";return o.str();
|
|
199
|
+
}
|
|
200
|
+
int wmain(int argc,wchar_t** argv){
|
|
201
|
+
try{
|
|
202
|
+
if(argc<2)throw std::runtime_error("usage: process [limit] | map self [limit] | map PID creation_filetime [limit] | pe FILE");
|
|
203
|
+
const std::wstring op=argv[1];std::string data;
|
|
204
|
+
if(op==L"process"){
|
|
205
|
+
size_t limit=64;if(argc>2){auto x=std::stoul(argv[2]);if(x<1||x>256)throw std::runtime_error("limit must be 1..256");limit=x;}
|
|
206
|
+
data=snapshot(limit);
|
|
207
|
+
}else if(op==L"identity"){
|
|
208
|
+
if(argc!=3)throw std::runtime_error("identity requires one PID");
|
|
209
|
+
const std::wstring value=argv[2];
|
|
210
|
+
if(value.empty()||!std::all_of(value.begin(),value.end(),[](wchar_t c){return c>=L'0'&&c<=L'9';}))throw std::runtime_error("invalid PID");
|
|
211
|
+
const auto n=std::stoull(value);if(n<1||n>MAXDWORD)throw std::runtime_error("invalid PID");
|
|
212
|
+
data=identity(DWORD(n));
|
|
213
|
+
}else if(op==L"map"){
|
|
214
|
+
if(argc<3)throw std::runtime_error("map requires self or PID");
|
|
215
|
+
const bool self=std::wstring(argv[2])==L"self";
|
|
216
|
+
DWORD pid=0;size_t limit=128;uint64_t expected=0;
|
|
217
|
+
const auto parseUnsigned=[](const wchar_t* text)->uint64_t{
|
|
218
|
+
const std::wstring s=text;
|
|
219
|
+
if(s.empty()||!std::all_of(s.begin(),s.end(),[](wchar_t c){return c>=L'0'&&c<=L'9';}))
|
|
220
|
+
throw std::runtime_error("invalid unsigned number");
|
|
221
|
+
return std::stoull(s);
|
|
222
|
+
};
|
|
223
|
+
if(self){
|
|
224
|
+
if(argc>4)throw std::runtime_error("map self [limit]");
|
|
225
|
+
pid=GetCurrentProcessId();
|
|
226
|
+
if(argc==4){const auto n=parseUnsigned(argv[3]);if(n<1||n>512)throw std::runtime_error("limit must be 1..512");limit=size_t(n);}
|
|
227
|
+
}else{
|
|
228
|
+
if(argc<4||argc>5)throw std::runtime_error("map PID requires creation_filetime [limit]");
|
|
229
|
+
const auto n=parseUnsigned(argv[2]);if(n<1||n>MAXDWORD)throw std::runtime_error("invalid PID");pid=DWORD(n);
|
|
230
|
+
expected=parseUnsigned(argv[3]);if(expected==0)throw std::runtime_error("creation_filetime must be nonzero");
|
|
231
|
+
if(argc==5){const auto nLimit=parseUnsigned(argv[4]);if(nLimit<1||nLimit>512)throw std::runtime_error("limit must be 1..512");limit=size_t(nLimit);}
|
|
232
|
+
}
|
|
233
|
+
data=memory_map(pid,limit,expected);
|
|
234
|
+
}else if(op==L"pe"){
|
|
235
|
+
if(argc!=3)throw std::runtime_error("pe requires one file path");
|
|
236
|
+
data=pe(argv[2]);
|
|
237
|
+
}else throw std::runtime_error("unknown operation");
|
|
238
|
+
std::cout<<"{\"schema\":\"deadbyte.native-inspect.r67.v1\",\"status\":\"ok\",\"operation\":"<<escape(utf8(op))
|
|
239
|
+
<<",\"data\":"<<data<<",\"data_sha256\":"<<escape(sha256(data))<<"}\n";
|
|
240
|
+
return 0;
|
|
241
|
+
}catch(const std::exception& e){
|
|
242
|
+
const std::string message=e.what();const auto start=message.rfind("win32=");
|
|
243
|
+
unsigned long win32=0;std::string category="INVALID_INPUT";
|
|
244
|
+
if(message.find("PROCESS_IDENTITY_MISMATCH")!=std::string::npos)category="PROCESS_IDENTITY_MISMATCH";
|
|
245
|
+
else if(message.find("PROCESS_EXITED_DURING_QUERY")!=std::string::npos)category="PROCESS_EXITED";
|
|
246
|
+
if(start!=std::string::npos){try{win32=std::stoul(message.substr(start+6));}catch(...){win32=0;}
|
|
247
|
+
category=win32==ERROR_ACCESS_DENIED?"ACCESS_DENIED":(win32==ERROR_INVALID_PARAMETER?"INVALID_PARAMETER":"WIN32_FAILURE");}
|
|
248
|
+
std::cout<<"{\"schema\":\"deadbyte.native-inspect.r67.v1\",\"status\":\"failed\",\"error_code\":"<<escape(category)
|
|
249
|
+
<<",\"win32_error\":"<<win32<<",\"error\":"<<escape(message)<<"}\n";
|
|
250
|
+
return 2;
|
|
251
|
+
}
|
|
252
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.44",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"contained:run": "node src/contained-cli.mjs workspaces/demo input.txt",
|
|
43
43
|
"contained:smoke": "node src/contained-smoke.mjs",
|
|
44
44
|
"mcp:contained:smoke": "node src/mcp-contained-smoke-client.mjs",
|
|
45
|
-
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-
|
|
45
|
+
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r67.ps1",
|
|
46
46
|
"authority:smoke": "node src/authority-smoke.mjs",
|
|
47
47
|
"mcp:remote:smoke": "node src/remote-mcp-smoke-client.mjs",
|
|
48
48
|
"mcp:host:smoke": "node src/mcp-host-smoke-client.mjs",
|
|
@@ -90,7 +90,11 @@
|
|
|
90
90
|
"r62:focused:test": "node --test --test-concurrency=1 test/r62-release-contract.test.mjs test/r62-doctor-transactions-reconcile.test.mjs test/r62-doctor-receipt-semantics.test.mjs test/r62-rollback-failure-receipt.test.mjs test/r58-doctor-transactions.test.mjs test/r58-completed-update-receipt.test.mjs test/runtime-self-update.test.mjs test/r56-rollback-launcher-restore.test.mjs",
|
|
91
91
|
"r63:focused:test": "node --test --test-concurrency=1 test/r63-release-contract.test.mjs test/r63-one-shot-coherence.test.mjs test/r51-runtime-coherence.test.mjs test/remote-session-cli.test.mjs",
|
|
92
92
|
"r64:focused:test": "node --test --test-concurrency=1 test/r64-release-contract.test.mjs test/r64-machine-fs-large-read.test.mjs test/machine-fs-runtime.test.mjs test/compact-gateway-r36.test.mjs test/r61-control-lane.test.mjs test/r60-bridge-dispatch.test.mjs",
|
|
93
|
-
"r65:focused:test": "node scripts/r65-focused.mjs"
|
|
93
|
+
"r65:focused:test": "node scripts/r65-focused.mjs",
|
|
94
|
+
"r66:focused:test": "node --test --test-concurrency=1 test/r66-release-contract.test.mjs test/r58-doctor-runtime.test.mjs test/r58-doctor-foreground.test.mjs test/r58-doctor-installation.test.mjs test/r65-controller-authority-epoch.test.mjs test/r65-authority-observation-epoch.test.mjs",
|
|
95
|
+
"r67:focused:test": "node scripts/r67-focused-gate.mjs",
|
|
96
|
+
"r67:shard:run": "node scripts/r67-regression-shard-run.mjs",
|
|
97
|
+
"r67:shard:verify": "node scripts/r67-regression-reconcile.mjs"
|
|
94
98
|
},
|
|
95
99
|
"bundleDependencies": [
|
|
96
100
|
"@modelcontextprotocol/server",
|
|
@@ -39,7 +39,7 @@ deadbyte-process-host.cpp
|
|
|
39
39
|
exit_code=0
|
|
40
40
|
==== probe-dependencies ====
|
|
41
41
|
|
|
42
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
42
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\containment-probe.exe
|
|
43
43
|
|
|
44
44
|
File Type: EXECUTABLE IMAGE
|
|
45
45
|
|
|
@@ -59,7 +59,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
59
59
|
exit_code=0
|
|
60
60
|
==== contained-launcher-dependencies ====
|
|
61
61
|
|
|
62
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
62
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-exec.exe
|
|
63
63
|
|
|
64
64
|
File Type: EXECUTABLE IMAGE
|
|
65
65
|
|
|
@@ -83,7 +83,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
83
83
|
exit_code=0
|
|
84
84
|
==== contained-worker-dependencies ====
|
|
85
85
|
|
|
86
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
86
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\contained-transform-worker.exe
|
|
87
87
|
|
|
88
88
|
File Type: EXECUTABLE IMAGE
|
|
89
89
|
|
|
@@ -104,7 +104,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
104
104
|
exit_code=0
|
|
105
105
|
==== contained-reverse-worker-dependencies ====
|
|
106
106
|
|
|
107
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
107
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\contained-reverse-worker.exe
|
|
108
108
|
|
|
109
109
|
File Type: EXECUTABLE IMAGE
|
|
110
110
|
|
|
@@ -125,7 +125,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
125
125
|
exit_code=0
|
|
126
126
|
==== named-tunnel-host-dependencies ====
|
|
127
127
|
|
|
128
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
128
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-tunnel-host.exe
|
|
129
129
|
|
|
130
130
|
File Type: EXECUTABLE IMAGE
|
|
131
131
|
|
|
@@ -144,7 +144,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
144
144
|
exit_code=0
|
|
145
145
|
==== process-host-dependencies ====
|
|
146
146
|
|
|
147
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
147
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-process-host.exe
|
|
148
148
|
|
|
149
149
|
File Type: EXECUTABLE IMAGE
|
|
150
150
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
param([Parameter(Mandatory=$true)][ValidatePattern('^[0-9a-f]{64}$')][string]$SourceSha256)
|
|
2
|
+
$ErrorActionPreference='Stop'
|
|
3
|
+
Set-StrictMode -Version Latest
|
|
4
|
+
$root=Split-Path -Parent $PSScriptRoot
|
|
5
|
+
$src=Join-Path $root 'native\deadbyte-native-inspect-r67.cpp'
|
|
6
|
+
$observed=(Get-FileHash -LiteralPath $src -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
7
|
+
if($observed -ne $SourceSha256){throw "R67 source SHA mismatch; refusing build"}
|
|
8
|
+
Write-Output "R67_SOURCE_SHA256=$observed"
|
|
9
|
+
$bin=Join-Path $root 'bin'
|
|
10
|
+
$exe=Join-Path $bin 'deadbyte-native-inspect-r67.exe'
|
|
11
|
+
$obj=Join-Path $bin 'deadbyte-native-inspect-r67.obj'
|
|
12
|
+
$vswhere='C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe'
|
|
13
|
+
if(!(Test-Path -LiteralPath $vswhere)){throw 'vswhere missing'}
|
|
14
|
+
$install=(& $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1)
|
|
15
|
+
if(!$install){throw 'MSVC x64 C++ toolchain missing'}
|
|
16
|
+
$dev=Join-Path $install 'Common7\Tools\VsDevCmd.bat'
|
|
17
|
+
$cmd=if($env:ComSpec){$env:ComSpec}else{Join-Path $env:SystemRoot 'System32\cmd.exe'}
|
|
18
|
+
New-Item -ItemType Directory -Path $bin -Force | Out-Null
|
|
19
|
+
$common='/nologo /std:c++17 /permissive- /W4 /WX /O2 /MT /EHsc /guard:cf /diagnostics:caret /utf-8 /Brepro /DUNICODE /D_UNICODE /D_WIN32_WINNT=0x0A00'
|
|
20
|
+
$compile='cl {0} "{1}" /Fo:"{2}" /Fe:"{3}" /link bcrypt.lib /Brepro /INCREMENTAL:NO' -f $common,$src,$obj,$exe
|
|
21
|
+
$line='call "{0}" -no_logo -arch=x64 -host_arch=x64 && {1}' -f $dev,$compile
|
|
22
|
+
$output=& $cmd /d /s /c $line 2>&1
|
|
23
|
+
$rc=$LASTEXITCODE
|
|
24
|
+
$output | ForEach-Object { Write-Output ([string]$_) }
|
|
25
|
+
if($rc -ne 0){throw "R67 native build failed exit=$rc"}
|
|
26
|
+
if(!(Test-Path -LiteralPath $exe -PathType Leaf)){throw 'R67 native exe missing'}
|
|
27
|
+
$sha=(Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
28
|
+
Write-Output "R67_NATIVE_BUILD_PASS sha256=$sha path=$exe"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[ValidateSet('all','preflight','focused','full','manifest','doctor')]
|
|
3
|
+
[string]$Stage='all'
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
$ErrorActionPreference='Stop'
|
|
7
|
+
Set-StrictMode -Version 3.0
|
|
8
|
+
$root=Split-Path $PSScriptRoot -Parent
|
|
9
|
+
Set-Location -LiteralPath $root
|
|
10
|
+
|
|
11
|
+
function Invoke-Npm([string]$Name,[string[]]$NpmArgs){
|
|
12
|
+
Write-Host "==== $Name ===="
|
|
13
|
+
& npm.cmd @NpmArgs
|
|
14
|
+
if($LASTEXITCODE -ne 0){ throw "R66 prepublish gate failed at $Name exit=$LASTEXITCODE" }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Get-FileSha256([string]$File){
|
|
18
|
+
$hasher=[Security.Cryptography.SHA256]::Create()
|
|
19
|
+
try{
|
|
20
|
+
$bytes=[IO.File]::ReadAllBytes($File)
|
|
21
|
+
return (($hasher.ComputeHash($bytes) | ForEach-Object { $_.ToString('x2') }) -join '')
|
|
22
|
+
}finally{ $hasher.Dispose() }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
$sourceRoot='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.42-r65'
|
|
26
|
+
$sourceManifest='1b4754103dd4ca43eb81f8e33cf188b36eee3051cb182396ccc6abe2a6e6c250'
|
|
27
|
+
if(-not (Test-Path -LiteralPath $sourceRoot -PathType Container)){ throw 'R66 source predecessor missing' }
|
|
28
|
+
$observedSourceManifest=Get-FileSha256 (Join-Path $sourceRoot 'MANIFEST.SHA256')
|
|
29
|
+
if($observedSourceManifest -ne $sourceManifest){ throw 'R66 source predecessor manifest identity mismatch' }
|
|
30
|
+
& node (Join-Path $root 'scripts\verify-r65-parent-source.mjs') --root $sourceRoot --expected-manifest-sha256 $sourceManifest
|
|
31
|
+
if($LASTEXITCODE -ne 0){ throw 'R66 source predecessor declared-manifest verification failed' }
|
|
32
|
+
|
|
33
|
+
$stateRoot=Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
34
|
+
$pointer=Join-Path $stateRoot 'active-release.json'
|
|
35
|
+
if(-not (Test-Path -LiteralPath $pointer -PathType Leaf)){ throw "R66 runtime predecessor pointer missing: $pointer" }
|
|
36
|
+
$active=Get-Content -LiteralPath $pointer -Raw | ConvertFrom-Json
|
|
37
|
+
if($active.release_id -ne 'v0.15.42-1b4754103dd4ca43'){ throw "R66 runtime predecessor release mismatch: $($active.release_id)" }
|
|
38
|
+
if($active.manifest_sha256 -ne $sourceManifest){ throw 'R66 runtime predecessor manifest mismatch' }
|
|
39
|
+
if($active.generation_manifest_sha256 -ne '62dc68be55ad627f8d872b8be8c1369ecca0edf08fdedc8336c9ccee2d2809fb'){ throw 'R66 runtime predecessor generation mismatch' }
|
|
40
|
+
if($Stage -eq 'preflight'){Write-Host '==== R66 WINDOWS GATE: PASS preflight ====';return}
|
|
41
|
+
|
|
42
|
+
if($Stage -eq 'all' -or $Stage -eq 'focused'){
|
|
43
|
+
Invoke-Npm 'r66-focused' @('run','r66:focused:test')
|
|
44
|
+
if($Stage -eq 'focused'){Write-Host '==== R66 WINDOWS GATE: PASS focused ====';return}
|
|
45
|
+
}
|
|
46
|
+
if($Stage -eq 'all' -or $Stage -eq 'full'){
|
|
47
|
+
Invoke-Npm 'full-regression' @('test')
|
|
48
|
+
if($Stage -eq 'full'){Write-Host '==== R66 WINDOWS GATE: PASS full ====';return}
|
|
49
|
+
}
|
|
50
|
+
if($Stage -eq 'all' -or $Stage -eq 'manifest'){
|
|
51
|
+
Invoke-Npm 'manifest-write' @('run','manifest:write')
|
|
52
|
+
Invoke-Npm 'manifest-verify' @('run','manifest:verify')
|
|
53
|
+
if($Stage -eq 'manifest'){Write-Host '==== R66 WINDOWS GATE: PASS manifest ====';return}
|
|
54
|
+
}
|
|
55
|
+
if($Stage -eq 'all' -or $Stage -eq 'doctor'){
|
|
56
|
+
$doctorRoot='D:\Projects\deadbyte-mcp\testing\0.15.43-r66\doctor-isolated-empty'
|
|
57
|
+
if(Test-Path -LiteralPath (Join-Path $doctorRoot 'active-release.json')){ throw 'R66 isolated doctor root unexpectedly contains a release pointer' }
|
|
58
|
+
$priorStateRoot=$env:DEADBYTE_STATE_ROOT
|
|
59
|
+
try{
|
|
60
|
+
$env:DEADBYTE_STATE_ROOT=$doctorRoot
|
|
61
|
+
Invoke-Npm 'doctor-isolated-empty-state' @('run','doctor')
|
|
62
|
+
}finally{ $env:DEADBYTE_STATE_ROOT=$priorStateRoot }
|
|
63
|
+
if($Stage -eq 'doctor'){Write-Host '==== R66 WINDOWS GATE: PASS doctor ====';return}
|
|
64
|
+
}
|
|
65
|
+
Write-Host '==== R66 PREPUBLISH WINDOWS GATE: PASS ===='
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[ValidateSet('preflight','focused','evidence','manifest','doctor','all')]
|
|
3
|
+
[string]$Stage='preflight',
|
|
4
|
+
[string]$OperationId=''
|
|
5
|
+
)
|
|
6
|
+
$ErrorActionPreference='Stop'
|
|
7
|
+
Set-StrictMode -Version Latest
|
|
8
|
+
$root=Split-Path -Parent $PSScriptRoot
|
|
9
|
+
$parent='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.43-r66'
|
|
10
|
+
$expected='4c4a64a4c8e9f5a6e9eecce85fd0007ebcac99121b29c07f1a1fc81d1643cd33'
|
|
11
|
+
$expectedGen='ef3b041e2a87c2e143d4733a98a52d9db80cd16ad7007e112fcb5202e70c77c3'
|
|
12
|
+
$expectedRelease='v0.15.43-4c4a64a4c8e9f5a6'
|
|
13
|
+
if(!(Test-Path -LiteralPath $parent -PathType Container)){throw 'R67 predecessor source missing'}
|
|
14
|
+
$prev=(Get-FileHash -LiteralPath (Join-Path $parent 'MANIFEST.SHA256') -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
15
|
+
if($prev -ne $expected){throw 'R67 predecessor manifest not exact'}
|
|
16
|
+
$stateRoot=Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
17
|
+
$pointerPath=Join-Path $stateRoot 'active-release.json'
|
|
18
|
+
if(!(Test-Path -LiteralPath $pointerPath -PathType Leaf)){throw 'R67 active predecessor pointer missing'}
|
|
19
|
+
$pointer=Get-Content -LiteralPath $pointerPath -Raw | ConvertFrom-Json
|
|
20
|
+
if($pointer.release_id -ne $expectedRelease -or $pointer.manifest_sha256 -ne $expected -or
|
|
21
|
+
$pointer.generation_manifest_sha256 -ne $expectedGen){throw 'R67 active predecessor binding mismatch'}
|
|
22
|
+
$pkg=Get-Content -LiteralPath (Join-Path $root 'package.json') -Raw | ConvertFrom-Json
|
|
23
|
+
if($pkg.version -ne '0.15.44'){throw 'R67 package candidate version mismatch'}
|
|
24
|
+
Write-Output 'R67_PREDECESSOR_PREFLIGHT=PASS'
|
|
25
|
+
if($Stage -eq 'preflight'){return}
|
|
26
|
+
function Invoke-Node([string]$Label,[string[]]$NodeArgs){
|
|
27
|
+
Write-Output "R67_STAGE_START=$Label"
|
|
28
|
+
& node @NodeArgs
|
|
29
|
+
if($LASTEXITCODE -ne 0){throw "R67 stage $Label exit=$LASTEXITCODE"}
|
|
30
|
+
Write-Output "R67_STAGE_PASS=$Label"
|
|
31
|
+
}
|
|
32
|
+
if($Stage -eq 'all' -or $Stage -eq 'focused'){
|
|
33
|
+
Invoke-Node 'focused' @('scripts/r67-focused-gate.mjs')
|
|
34
|
+
Invoke-Node 'machine-fs-smoke' @('src/machine-fs-smoke-client.mjs')
|
|
35
|
+
if($Stage -eq 'focused'){return}
|
|
36
|
+
}
|
|
37
|
+
if($Stage -eq 'all' -or $Stage -eq 'evidence'){
|
|
38
|
+
if($OperationId -notmatch '^[a-z0-9][a-z0-9._-]{5,63}$'){throw 'R67 full evidence operation ID required'}
|
|
39
|
+
Invoke-Node 'evidence' @('scripts/r67-regression-reconcile.mjs',$OperationId)
|
|
40
|
+
if($Stage -eq 'evidence'){return}
|
|
41
|
+
}
|
|
42
|
+
if($Stage -eq 'all' -or $Stage -eq 'manifest'){
|
|
43
|
+
Invoke-Node 'manifest-verify' @('scripts/release-manifest.mjs','verify')
|
|
44
|
+
if($Stage -eq 'manifest'){return}
|
|
45
|
+
}
|
|
46
|
+
if($Stage -eq 'all' -or $Stage -eq 'doctor'){
|
|
47
|
+
$prior=$env:DEADBYTE_STATE_ROOT
|
|
48
|
+
$isolated='D:\Projects\deadbyte-mcp\testing\0.15.44-r67\doctor-isolated-empty'
|
|
49
|
+
if(Test-Path -LiteralPath (Join-Path $isolated 'active-release.json')){throw 'isolated doctor root unexpectedly has active release pointer'}
|
|
50
|
+
try{
|
|
51
|
+
$env:DEADBYTE_STATE_ROOT=$isolated
|
|
52
|
+
Invoke-Node 'isolated-doctor' @('src/deadbyte-cli.mjs','doctor')
|
|
53
|
+
}finally{$env:DEADBYTE_STATE_ROOT=$prior}
|
|
54
|
+
if($Stage -eq 'doctor'){return}
|
|
55
|
+
}
|
|
56
|
+
Write-Output 'R67_WINDOWS_GATE=PASS_PREDEPLOY_CANDIDATE'
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {createHash,randomBytes} from 'node:crypto';
|
|
2
|
+
import {lstat,readFile,open,link,unlink} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
function sha256(bytes){return createHash('sha256').update(bytes).digest('hex');}
|
|
6
|
+
function integrity(bytes){return 'sha512-'+createHash('sha512').update(bytes).digest('base64');}
|
|
7
|
+
async function regular(pathname,label){
|
|
8
|
+
const stat=await lstat(pathname);
|
|
9
|
+
if(!stat.isFile()||stat.isSymbolicLink())throw new Error(label+' must be a regular non-symlink file');
|
|
10
|
+
return stat;
|
|
11
|
+
}
|
|
12
|
+
function assertSameIdentity(before,after){
|
|
13
|
+
if(before.dev!==after.dev||before.ino!==after.ino||before.size!==after.size)
|
|
14
|
+
throw new Error('source artifact identity changed during preservation; do not remove original');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Preserve an npm-packed immutable tarball across Windows volumes.
|
|
19
|
+
* Uses an exclusive staging file ON THE DESTINATION VOLUME, syncs complete
|
|
20
|
+
* verified bytes, and publishes with a same-volume, no-clobber hard link.
|
|
21
|
+
* No rename(D: -> C:), no partial destination visibility, no npm publish.
|
|
22
|
+
* Preserving exact bytes is NOT permission to send them to npm.
|
|
23
|
+
*/
|
|
24
|
+
export async function preserveNpmTarball({source,destination,expectedIntegrity,expectedShasum}={}){
|
|
25
|
+
if(typeof source!=='string'||typeof destination!=='string'||
|
|
26
|
+
!path.isAbsolute(source)||!path.isAbsolute(destination)||
|
|
27
|
+
path.resolve(source)===path.resolve(destination))throw new Error('absolute distinct source and destination required');
|
|
28
|
+
if(typeof expectedIntegrity!=='string'||!/^sha512-[A-Za-z0-9+/]+=*$/.test(expectedIntegrity))
|
|
29
|
+
throw new Error('npm SHA-512 integrity precondition required');
|
|
30
|
+
if(expectedShasum!==undefined&&!/^[a-f0-9]{40}$/.test(expectedShasum))
|
|
31
|
+
throw new Error('invalid npm packed artifact SHA-1 precondition');
|
|
32
|
+
|
|
33
|
+
const original=await regular(source,'source artifact');
|
|
34
|
+
if(original.size===0||original.size>128*1024*1024)throw new Error('npm artifact size outside permitted budget');
|
|
35
|
+
const bytes=await readFile(source);
|
|
36
|
+
assertSameIdentity(original,await regular(source,'source artifact'));
|
|
37
|
+
if(bytes.length!==original.size||integrity(bytes)!==expectedIntegrity)
|
|
38
|
+
throw new Error('npm packed artifact integrity mismatch; original retained');
|
|
39
|
+
if(expectedShasum!==undefined&&createHash('sha1').update(bytes).digest('hex')!==expectedShasum)
|
|
40
|
+
throw new Error('npm packed artifact shasum mismatch; original retained');
|
|
41
|
+
const hash=sha256(bytes);
|
|
42
|
+
let committed=false;
|
|
43
|
+
try{
|
|
44
|
+
const existing=await regular(destination,'preserved artifact');
|
|
45
|
+
const preserved=await readFile(destination);
|
|
46
|
+
if(existing.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
|
|
47
|
+
throw new Error('existing preserved artifact differs; manual conflict reconciliation required');
|
|
48
|
+
committed=true;
|
|
49
|
+
}catch(error){
|
|
50
|
+
if(error?.code!=='ENOENT')throw error;
|
|
51
|
+
}
|
|
52
|
+
let staging=null;
|
|
53
|
+
if(!committed){
|
|
54
|
+
staging=path.join(path.dirname(destination),'.'+path.basename(destination)+'.stage-'+process.pid+'-'+randomBytes(8).toString('hex'));
|
|
55
|
+
try{
|
|
56
|
+
const handle=await open(staging,'wx',0o600);
|
|
57
|
+
try{
|
|
58
|
+
await handle.writeFile(bytes);
|
|
59
|
+
await handle.sync();
|
|
60
|
+
}finally{await handle.close();}
|
|
61
|
+
const staged=await readFile(staging);
|
|
62
|
+
if(staged.length!==bytes.length||sha256(staged)!==hash||integrity(staged)!==expectedIntegrity)
|
|
63
|
+
throw new Error('staged artifact integrity mismatch; original retained');
|
|
64
|
+
// Atomic visibility of already-fsynced bytes, exclusively on the destination volume.
|
|
65
|
+
try{await link(staging,destination);}
|
|
66
|
+
catch(error){
|
|
67
|
+
if(error?.code!=='EEXIST')throw error;
|
|
68
|
+
const existing=await regular(destination,'preserved artifact');
|
|
69
|
+
const preserved=await readFile(destination);
|
|
70
|
+
if(existing.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
|
|
71
|
+
throw new Error('concurrent preserved artifact conflict; original retained');
|
|
72
|
+
}
|
|
73
|
+
committed=true;
|
|
74
|
+
}finally{
|
|
75
|
+
if(staging)await unlink(staging).catch(e=>{if(e?.code!=='ENOENT')throw e;});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Final post-commit verification before relinquishing the source artifact.
|
|
79
|
+
const final=await regular(destination,'preserved artifact');
|
|
80
|
+
const preserved=await readFile(destination);
|
|
81
|
+
if(final.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
|
|
82
|
+
throw new Error('preserved artifact changed after commit; original retained');
|
|
83
|
+
const current=await regular(source,'source artifact');
|
|
84
|
+
assertSameIdentity(original,current);
|
|
85
|
+
if(sha256(await readFile(source))!==hash)throw new Error('source bytes changed after commit; retain for reconciliation');
|
|
86
|
+
await unlink(source);
|
|
87
|
+
return {status:staging?'VERIFIED_PRESERVED':'VERIFIED_ALREADY_PRESERVED',
|
|
88
|
+
sha256:hash,bytes:bytes.length,integrity:expectedIntegrity};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function assertVacantPackDestination({root,filename}={}){
|
|
92
|
+
if(typeof root!=='string'||!path.isAbsolute(root)||typeof filename!=='string'||
|
|
93
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]*\.tgz$/.test(filename)||filename!==path.basename(filename))
|
|
94
|
+
throw new Error('invalid npm pack target');
|
|
95
|
+
const target=path.join(root,filename);
|
|
96
|
+
try{await lstat(target);}
|
|
97
|
+
catch(error){if(error?.code==='ENOENT')return target;throw error;}
|
|
98
|
+
throw new Error('PACK_BLOCKED existing npm tarball must be archived and reconciled before npm pack: '+target);
|
|
99
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { reconcileNpmPublication } from './npm-publication-r65.mjs';
|
|
4
|
-
import { mkdtemp, readFile, rm, unlink, mkdir
|
|
4
|
+
import { mkdtemp, readFile, rm, unlink, mkdir } from 'node:fs/promises';
|
|
5
|
+
import {preserveNpmTarball,assertVacantPackDestination} from './npm-preserve-artifact-r67.mjs';
|
|
6
|
+
import {assertR67NpmPublishReady} from './npm-release-readiness-r67.mjs';
|
|
5
7
|
import { existsSync } from 'node:fs';
|
|
6
8
|
import { resolveStateRoot } from '../src/state-root.mjs';
|
|
7
9
|
import path from 'node:path';
|
|
@@ -86,6 +88,7 @@ async function main() {
|
|
|
86
88
|
if (provenance.version !== version) {
|
|
87
89
|
throw new Error(`PROVENANCE.json version mismatch: package=${version} provenance=${provenance.version}`);
|
|
88
90
|
}
|
|
91
|
+
if (!isVerifyOnly && version==='0.15.44') assertR67NpmPublishReady(release);
|
|
89
92
|
if (release.parent?.release_id !== provenance.parent_release_id ||
|
|
90
93
|
release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
|
|
91
94
|
release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
|
|
@@ -113,6 +116,7 @@ async function main() {
|
|
|
113
116
|
if(publicationDir)await mkdir(publicationDir,{recursive:true});
|
|
114
117
|
if(resuming&&!existsSync(preservedTarball))throw new Error('immutable npm publication claim exists but exact local tarball is missing; manual artifact reconciliation required');
|
|
115
118
|
if(!resuming&&preservedTarball&&existsSync(preservedTarball))throw new Error('unclaimed preserved npm tarball exists; inspect artifact evidence before another pack');
|
|
119
|
+
if(!resuming)await assertVacantPackDestination({root,filename:`deadbyte-mcp-${version}.tgz`});
|
|
116
120
|
const packed=resuming?null:parseJson(runNpm(['pack', '--json'], { timeout: 180000 }));
|
|
117
121
|
const packRecord=resuming?{filename:path.basename(preservedTarball)}:(Array.isArray(packed) ? packed[0] : packed?.[pkg.name]);
|
|
118
122
|
const filename = packRecord?.filename;
|
|
@@ -196,7 +200,11 @@ async function main() {
|
|
|
196
200
|
return;
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
if(!resuming)
|
|
203
|
+
if(!resuming){
|
|
204
|
+
const preserved=await preserveNpmTarball({source:tarball,destination:preservedTarball,
|
|
205
|
+
expectedIntegrity:packRecord.integrity,expectedShasum:packRecord.shasum});
|
|
206
|
+
console.log('Immutable cross-volume artifact preservation verified: '+JSON.stringify(preserved));
|
|
207
|
+
}
|
|
200
208
|
const publishTarball=preservedTarball;
|
|
201
209
|
const localBytes = await readFile(publishTarball);
|
|
202
210
|
const localIntegrity=`sha512-${createHash('sha512').update(localBytes).digest('base64')}`;
|