feat: add in-process adapter research contract
This commit is contained in:
410
scripts/extract-inprocess-adapter-map.ps1
Normal file
410
scripts/extract-inprocess-adapter-map.ps1
Normal file
@@ -0,0 +1,410 @@
|
||||
param(
|
||||
[string]$IlDir = "runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il",
|
||||
[string]$OutputDir = "runs/inprocess-adapter-research",
|
||||
[string]$ReportPath = "docs/source-discovery/2026-07-12-inprocess-adapter-static-map.md",
|
||||
[int]$WindowRadius = 4,
|
||||
[int]$MaxEvidencePerAnchor = 16
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path
|
||||
$ilDirFull = Join-Path $repo $IlDir
|
||||
$outDir = Join-Path $repo $OutputDir
|
||||
$reportFullPath = Join-Path $repo $ReportPath
|
||||
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $reportFullPath) | Out-Null
|
||||
|
||||
$sources = @(
|
||||
@{ path = "IMPlatformClient.exe.il"; role = "main managed client; UI, AppContextManager, MessageScheduling, frmMain, login runtime" },
|
||||
@{ path = "smack.dll.il"; role = "XMPP/smack managed library; Chat and XMPPConnection send primitives" },
|
||||
@{ path = "IMPP.Service.dll.il"; role = "file transfer service implementation" },
|
||||
@{ path = "IMPP.ServiceBase.dll.il"; role = "file transfer DTOs and FileUploadPara/FileUploadResult contracts" },
|
||||
@{ path = "IMPP.Interface.dll.il"; role = "service and network interface contracts" },
|
||||
@{ path = "IMPP.Common.dll.il"; role = "common helper and runtime DTO evidence" }
|
||||
)
|
||||
|
||||
$anchors = @(
|
||||
@{
|
||||
key = "text_high_level_entry"
|
||||
display = "AppContextManager.SendTxtMessageByJid"
|
||||
command = "send_text"
|
||||
adapter_role = "preferred high-level managed entry if an in-process adapter can reach AppContextManager"
|
||||
confidence = "high_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("logged-in IMPPManager/UserInfo", "target JID", "initialized AppContextManager", "live XMPP connection")
|
||||
patterns = @("AppContextManager::SendTxtMessageByJid", "\bSendTxtMessageByJid\b")
|
||||
},
|
||||
@{
|
||||
key = "text_scheduling_entry"
|
||||
display = "MessageScheduling.SendChatMessage"
|
||||
command = "send_text"
|
||||
adapter_role = "fallback managed entry when scheduling layer has chat and contact args"
|
||||
confidence = "high_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("MessageScheduling instance or static caller", "RosterContactsArgs/contact context", "Chat object", "live XMPP connection")
|
||||
patterns = @("MessageScheduling::SendChatMessage", "SendChatMessage\(", "SendChatMessage\(class IMPP\.Client")
|
||||
},
|
||||
@{
|
||||
key = "text_low_level_chat"
|
||||
display = "com.vision.smack.Chat.SendMessage / XMPPConnection.send"
|
||||
command = "send_text"
|
||||
adapter_role = "lowest-level send target; requires already resolved Chat/XMPPConnection"
|
||||
confidence = "high_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("Chat instance", "XMPPConnection authenticated session", "message payload format")
|
||||
patterns = @("com\.vision\.smack\.Chat::SendMessage", "com\.vision\.smack\.Chat::sendMessage", "XMPPConnection::send\b")
|
||||
},
|
||||
@{
|
||||
key = "file_upload_orchestrator"
|
||||
display = "IMPP.Client.OffLineFileSend.sendFile"
|
||||
command = "upload_file_then_send_file"
|
||||
adapter_role = "UI-side upload orchestration and transition into chat file message"
|
||||
confidence = "high_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("local file path", "HTTP file server/auth state", "target roster/chat args", "upload completion callback")
|
||||
patterns = @("OffLineFileSend::sendFile", "sendFile\(\) cil managed", "OffLineFileSend::trans_UploadCompleted")
|
||||
},
|
||||
@{
|
||||
key = "file_upload_service"
|
||||
display = "IFileTransfer/FileTransfer.FileUpload + FileUploadPara"
|
||||
command = "upload_file"
|
||||
adapter_role = "service upload entry that must return FileUploadResult before chat file finalization"
|
||||
confidence = "high_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("FileUploadPara host/port/domain", "localFilePath", "token/auth", "FileUploadResult.FileID")
|
||||
patterns = @("FileTransfer::FileUpload", "FileUploadPara", "FileUploadResult::get_FileID", "FileUploadResult")
|
||||
},
|
||||
@{
|
||||
key = "file_message_finalizer"
|
||||
display = "MessageScheduling.SendFileMessage / Chat.sendFileMessage"
|
||||
command = "send_file_message"
|
||||
adapter_role = "chat file-message finalization after FileID/remote folder exists"
|
||||
confidence = "high_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("uploaded file id", "remote folder metadata", "file name/size", "target Chat/JID")
|
||||
patterns = @("MessageScheduling::SendFileMessage", "Chat::sendFileMessage", "sendFileMessage\(")
|
||||
},
|
||||
@{
|
||||
key = "logged_in_identity_auth"
|
||||
display = "IMPPManager.UserInfo / UnifiedAuthentication / Auth token"
|
||||
command = "runtime_context"
|
||||
adapter_role = "runtime state required before any production send/upload can be meaningful"
|
||||
confidence = "medium_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("UserInfo", "UnifiedAuthentication", "Auth token", "HTTPFileServer", "server resource/domain")
|
||||
patterns = @("IMPPManager::get_UserInfo", "IMPPManager.UserInfo", "UnifiedAuthentication", "HTTPFileServer", "Auth::get_token", "get_token")
|
||||
},
|
||||
@{
|
||||
key = "main_window_runtime_owner"
|
||||
display = "frmMain / MessageCenter / chat runtime ownership"
|
||||
command = "runtime_context"
|
||||
adapter_role = "likely object owner for initialized chat, reconnect, message center, and plugin runtime state"
|
||||
confidence = "medium_static"
|
||||
usable_offline = $false
|
||||
required_runtime = @("running frmMain", "initialized message center", "contact/session state")
|
||||
patterns = @("class IMPP\.Client\.frmMain", "frmMain::", "GetMessageCenter\(\)", "MessageCenter", "OnReConnectionSucceed", "GetChat\(")
|
||||
},
|
||||
@{
|
||||
key = "existing_http_not_bridge"
|
||||
display = "frmLogin.StartWinServer / HttpServerLib"
|
||||
command = "not_selected_bridge"
|
||||
adapter_role = "known local HTTP framework, but not a confirmed send bridge"
|
||||
confidence = "high_static_negative"
|
||||
usable_offline = $false
|
||||
required_runtime = @("logged-in state", "route module registration not found", "send route not found")
|
||||
patterns = @("StartWinServer", "HttpServerLib", "HttpService::\.ctor", "RegisterModule", "RouteAttribute")
|
||||
}
|
||||
)
|
||||
|
||||
function Get-RelativePath([string]$Path) {
|
||||
if ($Path.StartsWith($repo, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
return $Path.Substring($repo.Length).TrimStart([char[]]@('\', '/'))
|
||||
}
|
||||
return $Path
|
||||
}
|
||||
|
||||
function Write-Utf8NoBomLf([string]$Path, [string]$Text) {
|
||||
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
||||
$normalized = $Text -replace "`r`n", "`n"
|
||||
$normalized = $normalized -replace "`r", "`n"
|
||||
$normalized = $normalized.TrimEnd() + "`n"
|
||||
[System.IO.File]::WriteAllText($Path, $normalized, $utf8NoBom)
|
||||
}
|
||||
|
||||
function Get-EvidenceKind([string]$Line) {
|
||||
if ($Line -match "^\s*\.class\b") { return "type_definition" }
|
||||
if ($Line -match "^\s*\.method\b") { return "method_header" }
|
||||
if ($Line -match "\bend of method\b") { return "method_end" }
|
||||
if ($Line -match "\bnewobj\b") { return "constructor_call" }
|
||||
if ($Line -match "\bcall(?:virt)?\b") { return "callsite" }
|
||||
if ($Line -match "\bldstr\b") { return "string_literal" }
|
||||
if ($Line -match "^\s*(instance|static)\b.*\(") { return "signature_continuation" }
|
||||
if ($Line -match "^\s*\.field\b") { return "field" }
|
||||
return "reference"
|
||||
}
|
||||
|
||||
function Get-MethodContext($Lines, [int]$Index) {
|
||||
$start = [Math]::Max(0, $Index - 100)
|
||||
for ($i = $Index; $i -ge $start; $i--) {
|
||||
if ($Lines[$i] -match "^\s*\.method\b") {
|
||||
$header = $Lines[$i].Trim()
|
||||
if (($i + 1) -lt $Lines.Count -and $Lines[$i + 1] -match "^\s+(instance|static|default)\b") {
|
||||
$header = ($header + " " + $Lines[$i + 1].Trim())
|
||||
}
|
||||
return "L$($i + 1): $header"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function Get-WindowSnippet($Lines, [int]$Index, [int]$Radius) {
|
||||
$start = [Math]::Max(0, $Index - $Radius)
|
||||
$end = [Math]::Min($Lines.Count - 1, $Index + $Radius)
|
||||
$parts = New-Object System.Collections.Generic.List[string]
|
||||
for ($i = $start; $i -le $end; $i++) {
|
||||
$parts.Add(("L{0}: {1}" -f ($i + 1), $Lines[$i].Trim()))
|
||||
}
|
||||
return ($parts -join "`n")
|
||||
}
|
||||
|
||||
function Find-AnchorEvidence($Anchor) {
|
||||
$items = New-Object System.Collections.Generic.List[object]
|
||||
foreach ($source in $sources) {
|
||||
$sourcePath = Join-Path $ilDirFull $source.path
|
||||
if (-not (Test-Path -LiteralPath $sourcePath)) { continue }
|
||||
$lines = [System.IO.File]::ReadAllLines($sourcePath)
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
$line = $lines[$i]
|
||||
foreach ($pattern in $Anchor.patterns) {
|
||||
if ($line -match $pattern) {
|
||||
$items.Add([pscustomobject]@{
|
||||
source = Get-RelativePath $sourcePath
|
||||
source_role = $source.role
|
||||
line = $i + 1
|
||||
kind = Get-EvidenceKind $line
|
||||
pattern = $pattern
|
||||
snippet = $line.Trim()
|
||||
context = Get-MethodContext $lines $i
|
||||
window = Get-WindowSnippet $lines $i $WindowRadius
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($items.Count -ge $MaxEvidencePerAnchor) { break }
|
||||
}
|
||||
if ($items.Count -ge $MaxEvidencePerAnchor) { break }
|
||||
}
|
||||
return $items.ToArray()
|
||||
}
|
||||
|
||||
function Classify-Anchor($Anchor, $Evidence) {
|
||||
if ($Evidence.Count -eq 0) { return "missing_static_evidence" }
|
||||
if ($Anchor.command -eq "not_selected_bridge") { return "not_a_send_bridge" }
|
||||
if ($Anchor.command -eq "runtime_context") { return "runtime_dependency" }
|
||||
return "in_process_candidate_static_only"
|
||||
}
|
||||
|
||||
$sourceSummary = foreach ($source in $sources) {
|
||||
$path = Join-Path $ilDirFull $source.path
|
||||
$exists = Test-Path -LiteralPath $path
|
||||
$lineCount = 0
|
||||
$size = 0
|
||||
if ($exists) {
|
||||
$item = Get-Item -LiteralPath $path
|
||||
$size = $item.Length
|
||||
$lineCount = ([System.IO.File]::ReadLines($path) | Measure-Object).Count
|
||||
}
|
||||
[pscustomobject]@{
|
||||
path = Get-RelativePath $path
|
||||
role = $source.role
|
||||
exists = $exists
|
||||
lines = $lineCount
|
||||
size_bytes = $size
|
||||
}
|
||||
}
|
||||
|
||||
$anchorResults = foreach ($anchor in $anchors) {
|
||||
$evidence = Find-AnchorEvidence $anchor
|
||||
[pscustomobject]@{
|
||||
key = $anchor.key
|
||||
display = $anchor.display
|
||||
command = $anchor.command
|
||||
adapter_role = $anchor.adapter_role
|
||||
classification = Classify-Anchor $anchor $evidence
|
||||
confidence = $anchor.confidence
|
||||
usable_offline = $anchor.usable_offline
|
||||
required_runtime = $anchor.required_runtime
|
||||
evidence_count = $evidence.Count
|
||||
evidence = $evidence
|
||||
}
|
||||
}
|
||||
|
||||
$commands = @(
|
||||
[pscustomobject]@{
|
||||
op = "probe_runtime"
|
||||
status = "contract_only"
|
||||
purpose = "report whether adapter is inside logged-in IMPP runtime and can see required types/singletons"
|
||||
required_evidence = @("process identity", "loaded assemblies", "IMPPManager.UserInfo presence", "XMPP connection state")
|
||||
},
|
||||
[pscustomobject]@{
|
||||
op = "send_text"
|
||||
status = "blocked_until_logged_in_adapter"
|
||||
purpose = "invoke high-level text send path after target JID and content hash are validated"
|
||||
required_evidence = @("AppContextManager or MessageScheduling reachability", "target JID", "sent record or ack containing content hash/correlation")
|
||||
},
|
||||
[pscustomobject]@{
|
||||
op = "upload_file"
|
||||
status = "blocked_until_logged_in_adapter"
|
||||
purpose = "call file upload service and return server FileID/remote folder metadata"
|
||||
required_evidence = @("FileUploadPara values", "HTTP file server/auth", "FileUploadResult.FileID")
|
||||
},
|
||||
[pscustomobject]@{
|
||||
op = "send_file_message"
|
||||
status = "blocked_until_upload_evidence"
|
||||
purpose = "finalize file message after upload returns server id"
|
||||
required_evidence = @("uploaded FileID", "file name/size", "target JID/Chat", "sent record/ack")
|
||||
}
|
||||
)
|
||||
|
||||
$requiredRuntimeState = @(
|
||||
"logged-in IMPlatformClient.exe process",
|
||||
"loaded IMPP.Client/AppContextManager/MessageScheduling types",
|
||||
"initialized IMPPManager.UserInfo and UnifiedAuthentication/Auth token",
|
||||
"live XMPPConnection/Chat session",
|
||||
"target JID or group JID resolved by existing search tools",
|
||||
"file upload service parameters and FileUploadResult.FileID for file sends"
|
||||
)
|
||||
|
||||
$stopConditions = @(
|
||||
"No logged-in process is available for runtime reachability validation.",
|
||||
"Adapter cannot see AppContextManager, MessageScheduling, Chat, or IFileTransfer in process.",
|
||||
"Every viable path requires unsupported binary patching instead of a replaceable managed adapter/harness.",
|
||||
"Online evidence cannot produce sent-record/content-hash/ack correlation for text send.",
|
||||
"File upload cannot return FileUploadResult.FileID or equivalent remote folder metadata."
|
||||
)
|
||||
|
||||
$result = [pscustomobject]@{
|
||||
ok = $true
|
||||
generated_at = (Get-Date).ToUniversalTime().ToString("o")
|
||||
decision = "managed_in_process_adapter_contract_first"
|
||||
sources = $sourceSummary
|
||||
anchors = $anchorResults
|
||||
commands = $commands
|
||||
required_runtime_state = $requiredRuntimeState
|
||||
stop_conditions = $stopConditions
|
||||
originals_copied = $false
|
||||
originals_modified = $false
|
||||
}
|
||||
|
||||
$jsonPath = Join-Path $outDir "inprocess-adapter-map.json"
|
||||
$jsonText = $result | ConvertTo-Json -Depth 8
|
||||
Write-Utf8NoBomLf $jsonPath $jsonText
|
||||
|
||||
$md = New-Object System.Collections.Generic.List[string]
|
||||
$md.Add("# In-Process Adapter Static Map")
|
||||
$md.Add("")
|
||||
$md.Add("Date: 2026-07-12")
|
||||
$md.Add("")
|
||||
$md.Add("## Scope")
|
||||
$md.Add("")
|
||||
$md.Add("This report uses offline IL/static evidence only. It does not log in, attach hooks, inject into a process, modify client binaries, upload files, send messages, or open network connections.")
|
||||
$md.Add("")
|
||||
$md.Add("Machine-readable evidence: ``$(Get-RelativePath $jsonPath)``")
|
||||
$md.Add("")
|
||||
$md.Add("## Decision")
|
||||
$md.Add("")
|
||||
$md.Add("| Question | Answer |")
|
||||
$md.Add("| --- | --- |")
|
||||
$md.Add("| Selected next B-route branch | `managed_in_process_adapter_contract_first` |")
|
||||
$md.Add("| Can offline branch enable production send? | **No** |")
|
||||
$md.Add("| Go connector mode to expose now | `in_process_contract` |")
|
||||
$md.Add("| Required online proof later | logged-in adapter reachability plus sent-record/content-hash/ack evidence |")
|
||||
$md.Add("")
|
||||
$md.Add("## Sources")
|
||||
$md.Add("")
|
||||
$md.Add("| Source | Role | Exists | Lines | Size bytes |")
|
||||
$md.Add("| --- | --- | --- | ---: | ---: |")
|
||||
foreach ($source in $sourceSummary) {
|
||||
$md.Add("| ``$($source.path)`` | $($source.role) | $($source.exists) | $($source.lines) | $($source.size_bytes) |")
|
||||
}
|
||||
$md.Add("")
|
||||
$md.Add("## Command contract for future in-process adapter")
|
||||
$md.Add("")
|
||||
$md.Add("| Op | Current status | Purpose | Required evidence before production |")
|
||||
$md.Add("| --- | --- | --- | --- |")
|
||||
foreach ($cmd in $commands) {
|
||||
$required = ($cmd.required_evidence -join "; ")
|
||||
$md.Add("| ``$($cmd.op)`` | ``$($cmd.status)`` | $($cmd.purpose) | $required |")
|
||||
}
|
||||
$md.Add("")
|
||||
$md.Add("## Anchor summary")
|
||||
$md.Add("")
|
||||
$md.Add("| Anchor | Command | Classification | Confidence | Evidence | Required runtime |")
|
||||
$md.Add("| --- | --- | --- | --- | ---: | --- |")
|
||||
foreach ($anchor in $anchorResults) {
|
||||
$required = ($anchor.required_runtime -join "; ")
|
||||
$md.Add("| ``$($anchor.display)`` | ``$($anchor.command)`` | ``$($anchor.classification)`` | ``$($anchor.confidence)`` | $($anchor.evidence_count) | $required |")
|
||||
}
|
||||
$md.Add("")
|
||||
$md.Add("## Verified facts")
|
||||
$md.Add("")
|
||||
$md.Add("- Text send has static in-process candidates, but all require logged-in runtime objects and a live XMPP/Chat path.")
|
||||
$md.Add("- File send is split into upload and message-finalization; `FileUploadResult.FileID` is a required boundary value before `Chat.sendFileMessage` is meaningful.")
|
||||
$md.Add("- The earlier IPC/plugin/local HTTP scan did not find a confirmed external send bridge, so this branch must stay at `in_process_contract` until online reachability exists.")
|
||||
$md.Add("- `in_process_contract` is a Go-side contract and audit mode only; it must return blocked/no-side-effect metadata in this offline branch.")
|
||||
$md.Add("")
|
||||
$md.Add("## Required runtime state")
|
||||
$md.Add("")
|
||||
foreach ($state in $requiredRuntimeState) {
|
||||
$md.Add("- $state")
|
||||
}
|
||||
$md.Add("")
|
||||
$md.Add("## Anchor evidence")
|
||||
foreach ($anchor in $anchorResults) {
|
||||
$md.Add("")
|
||||
$md.Add("### $($anchor.display)")
|
||||
$md.Add("")
|
||||
$md.Add("Classification: ``$($anchor.classification)``; command: ``$($anchor.command)``; evidence count: $($anchor.evidence_count).")
|
||||
$md.Add("")
|
||||
$md.Add("Adapter role: $($anchor.adapter_role)")
|
||||
$md.Add("")
|
||||
if ($anchor.evidence_count -eq 0) {
|
||||
$md.Add("No static evidence found for this anchor in the configured IL set.")
|
||||
continue
|
||||
}
|
||||
$md.Add("| Source | Kind | Snippet | Context |")
|
||||
$md.Add("| --- | --- | --- | --- |")
|
||||
foreach ($e in $anchor.evidence) {
|
||||
$snippet = ($e.snippet -replace "\|", "\\|" )
|
||||
if ($snippet.Length -gt 220) { $snippet = $snippet.Substring(0, 217) + "..." }
|
||||
$context = ($e.context -replace "\|", "\\|" )
|
||||
if ($context.Length -gt 180) { $context = $context.Substring(0, 177) + "..." }
|
||||
$md.Add("| ``$($e.source):$($e.line)`` | ``$($e.kind)`` | ``$snippet`` | ``$context`` |")
|
||||
}
|
||||
}
|
||||
$md.Add("")
|
||||
$md.Add("## Stop conditions")
|
||||
$md.Add("")
|
||||
foreach ($condition in $stopConditions) {
|
||||
$md.Add("- $condition")
|
||||
}
|
||||
$md.Add("")
|
||||
$md.Add("## Next work")
|
||||
$md.Add("")
|
||||
$md.Add("1. Add Go connector mode `in_process_contract` that validates request shape and returns blocked/no-side-effect metadata.")
|
||||
$md.Add("2. In a logged-in environment, build a recorder/probe that only checks reachability of the listed runtime objects before attempting any send.")
|
||||
$md.Add("3. Promote text send only after sent-record/content-hash/ack evidence exists.")
|
||||
$md.Add("4. Promote file send only after upload returns `FileUploadResult.FileID` and file-message finalization is proven online.")
|
||||
|
||||
Write-Utf8NoBomLf $reportFullPath ($md -join "`n")
|
||||
|
||||
[pscustomobject]@{
|
||||
ok = $true
|
||||
json_path = $jsonPath
|
||||
report_path = $reportFullPath
|
||||
anchors = $anchorResults.Count
|
||||
decision = $result.decision
|
||||
originals_copied = $false
|
||||
originals_modified = $false
|
||||
} | ConvertTo-Json -Depth 5
|
||||
Reference in New Issue
Block a user