Class ::nsmcp::CodeUpstreamTool (public)
::nx::Class ::nsmcp::CodeUpstreamTool
Defined in /usr/local/ns/tcl/nsmcp/code-upstream.tcl
- Testcases:
- No testcase defined.
Source code: :property {name code_upstream_compare} :property {permission diagnostics.code.read} :property {description {Compare an installed Tcl/NX/XOTcl procedure or method with literal source declarations in administrator-mapped public GitHub repositories. Returns immutable commit/file links, body and argument comparisons, bounded source excerpts and recent file commits. Optional configured baseline distinguishes differences from a declared installation baseline; differences alone do not prove a bug or an upstream fix. Never evaluates remote code or sends local source to GitHub. Requires enableCodeTools and enableCodeUpstream. Unsupported/dynamic/ambiguous source is unavailable, never equal.}} :property {inputSchema {{"type":"object","properties":{"name":{"type":"string"},"ref":{"type":"string","description":"Optional branch, tag or commit within the configured repository; defaults to the mapping ref."}},"required":["name"],"additionalProperties":false}}} :public method describe {} { # # Describe the upstream comparison tool and its network annotation. # # Start with the base descriptor and mark the configured GitHub comparison # as an open-world operation. # # @return The rooted JSON tool descriptor. # set description [next] set fields {} foreach {key type value} [lindex $description 2] { if {$key eq "annotations"} { set value {readOnlyHint boolean true destructiveHint boolean false openWorldHint boolean true} } lappend fields $key $type $value } return [list {} object $fields] } :method options {arguments} { set opts {name {} ref {}} foreach {key value} [:jsonArguments $arguments] { dict set opts [string range $key 1 end] $value } if {[dict get $opts name] eq {} || [string length [dict get $opts name]]>1024 || [regexp {[\x00\r\n]} [dict get $opts name]]} { error "Invalid definition name" } if {[dict get $opts ref] ne {}} { ::nsmcp::codeUpstreamRef [dict get $opts ref] } return $opts } :public method validate {arguments} { # # Validate upstream-comparison options. # # Decode the rooted JSON argument object and enforce this tool's option, # range and combination constraints. # # @param arguments Rooted ns_json typed triples for the tool's JSON argument # object. # # @return An empty result on success; invalid input raises RPC error -32602. # if {[catch { :options $arguments } message]} { return -code error -errorcode {NSMCP RPC -32602} $message } } :method mappings {} { return [ns_config ns/server/[ns_info server]/module/nsmcp codeUpstreamMappings {}] } :method http {url accept} { set headers [ns_set create Accept $accept User-Agent nsmcp-code-upstream Accept-Encoding identity X-GitHub-Api-Version 2022-11-28] try { # No redirect following, cookies, bearer token or arbitrary URL. # Fail closed on runtimes without bounded response support. set reply [ns_http run -method GET -headers $headers -binary -timeout 3s -expire 5s -maxresponse 1MiB $url] try { if {[dict get $reply status]==200} { set bytes [dict get $reply body] set decoded [encoding convertfrom utf-8 $bytes] if {[encoding convertto utf-8 $decoded] ne $bytes} { error "invalid-upstream-utf8" } dict set reply body $decoded } return [dict remove $reply headers] } finally { if {[dict exists $reply headers]} { ns_set free [dict get $reply headers] } } } finally { ns_set free $headers } } :method get {endpoint accept} { if {![regexp {^/repos/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/(commits|contents)(/|\?)} $endpoint] || [regexp {[\r\n]} $endpoint]} { error "invalid-github-endpoint" } if {[incr :requests]>5} { error "upstream-request-limit" } set url https://api.github.com$endpoint # Small successful-response cache, server-local and size bounded. set cache nsmcp:upstream-cache:[ns_info server] set key [ns_crypto::md string -digest sha256 -- [list $url $accept]] if {[nsv_exists $cache $key]} { set cached [nsv_get $cache $key] if {[clock seconds]-[dict get $cached at]<60} { return [dict get $cached body] } } if {[catch { :http $url $accept } reply]} { error "github-transport-failed" } set status [dict get $reply status] if {$status!=200} { error "github-http-$status" } set body [dict get $reply body] if {[string bytelength $body]>1048576} { error "github-response-too-large" } if {[llength [nsv_array names $cache]]>=16} { nsv_unset -nocomplain $cache } nsv_set $cache $key [dict create at [clock seconds] body $body] return $body } :method commit {repo ref} { set ref [ns_urlencode $ref] set sha [string trim [:get /repos/$repo/commits/$ref application/vnd.github.sha]] if {![regexp {^[a-f0-9]{40}$} $sha]} { error "invalid-github-commit" } return $sha } :method source {repo sha path} { set encoded {} foreach segment [split $path /] { lappend encoded [ns_urlencode $segment] } return [:get /repos/$repo/contents/[join $encoded /]?ref=$sha application/vnd.github.raw+json] } :method comparison {local remote} { set args [::nsmcp::codeCanonicalArguments [dict get $remote arguments]] set body [dict get $remote body] set sameArgs [expr {[dict get $local arguments] eq $args}] set sameBody [expr {[dict get $local body] eq $body}] set normalized [expr {[dict exists $local comparisonBody] ? [dict get $local comparisonBody] : [dict get $local body]}] set normalizedSame [expr {$normalized eq $body}] return [list identical boolean [expr {$sameArgs && $sameBody}] argumentsIdentical boolean $sameArgs bodyIdentical boolean $sameBody normalizedIdentical boolean [expr {$sameArgs && $normalizedSame}] normalizedBodyIdentical boolean $normalizedSame bodySha256 string [ns_crypto::md string -digest sha256 -- $body] arguments string [string range $args 0 4095] argumentsTruncated boolean [expr {[string length $args]>4096}]] } :method selectDefinition {source name local} { set state [::nsmcp::codeStaticDefinitions $source $name] set candidates [dict get $state matches] set conditional false foreach candidate $candidates { if {[llength [dict get $candidate conditions]]} { set conditional true } } if {[llength $candidates]>1 && !$conditional} { error "upstream-definition-missing-or-ambiguous" } if {!$conditional} { return [dict create selected true selection unique-literal-declaration remote [dict merge [lindex $candidates 0] [dict remove $state matches]] candidates $candidates] } set normalizedMatches {} set index 0 foreach candidate $candidates { set comparison {} foreach {key type value} [:comparison $local $candidate] { dict set comparison $key $value } if {[dict get $comparison normalizedIdentical]} { lappend normalizedMatches $index } incr index } set matches $normalizedMatches if {[llength $matches]!=1} { return [dict create selected false selection unresolved-conditional-variants candidates $candidates matchingCandidates $matches] } set index [lindex $matches 0] return [dict create selected true selection matching-loaded-conditional-variant selectedCandidate $index remote [dict merge [lindex $candidates $index] [dict remove $state matches]] candidates $candidates] } :method candidates {selection local} { set rows {} set i 0 set list [dict get $selection candidates] set budget [expr {max(1,8192/[llength $list])}] foreach candidate $list { set conditions {} set j 0 foreach condition [dict get $candidate conditions] { lappend conditions $j string $condition incr j } set body [dict get $candidate body] lappend rows $i object [list candidate number $i form string [dict get $candidate form] conditions array $conditions comparison object [:comparison $local $candidate] bodyExcerpt string [string range $body 0 [expr {$budget-1}]] bodyExcerptTruncated boolean [expr {[string length $body]>$budget}]] incr i } return $rows } :public method execute {context arguments} { # # Compare a loaded definition with its configured GitHub upstream. # # Use administrator-owned repository mappings and immutable revisions; # return explicit unavailable reasons rather than claiming an unperformed # comparison. # # @param context Authenticated request context carrying the principal, # grants and selected server. # @param arguments Rooted ns_json typed triples for the tool's JSON argument # object. # # @return A rooted JSON tool-result object, including availability or job # metadata as appropriate. # set opts [:options $arguments] try { return [:compare $context $opts] } on error {message options} { # Return a bounded diagnostic, not SQL, transport details or remote # response bodies. Unavailable is distinct from an equal comparison. set reason upstream-comparison-unavailable if {[regexp {^(upstream-[a-z-]+|github-(?:http-[0-9]{3}|transport-failed|response-too-large)|invalid-(?:upstream-[a-z-]+|github-[a-z-]+)|local-(?:source-file-metadata-unavailable|definition-not-scripted))$} $message]} { set reason $message } return [list {} object [list available boolean false name string [dict get $opts name] server string [$context server] observedAt number [clock seconds] reason string $reason upstreamFixVerified boolean false comparisonPerformed boolean false]] } } :method compare {context opts} { set name [dict get $opts name] set doc [::nsmcp::codeMetadata $name] if {$doc eq {} && [string first :: $name]==0} { set doc [::nsmcp::codeMetadata [string range $name 2 end]] } if {![dict exists $doc script]} { error "local-source-file-metadata-unavailable" } set path [string trimleft [dict get $doc script] /] set mapping [::nsmcp::codeUpstreamMapping $path [:mappings]] set local [::nsmcp::codeLocalComparable $name $doc] set localObservedAt [clock seconds] set repo [dict get $mapping repository] set file [dict get $mapping path] set ref [dict get $opts ref] if {$ref eq {}} { set ref [dict get $mapping ref] } set :requests 0 set sha [:commit $repo $ref] set source [:source $repo $sha $file] set selection [:selectDefinition $source $name $local] set candidates [:candidates $selection $local] set normalizations {} set ni 0 foreach normalization [dict get $local normalizations] { lappend normalizations $ni string $normalization incr ni } if {![dict get $selection selected]} { return [list {} object [list available boolean false comparisonPerformed boolean false reason string upstream-conditional-variant-unresolved name string $name sourceFile string $path repository string $repo upstreamPath string $file commit string $sha upstreamUrl string https://github.com/$repo/blob/$sha/$file localDefinitionSha256 string [dict get $local definitionSha256] localNormalizations array $normalizations candidateCount number [llength [dict get $selection candidates]] candidates array $candidates conditionsEvaluated boolean false selectedCandidate null {} matchingCandidateCount number [llength [dict get $selection matchingCandidates]] upstreamFixVerified boolean false limitations string {The loaded definition does not uniquely identify one literal upstream conditional variant. Conditions were not evaluated. Candidate comparisons are evidence, not proof of the active upstream implementation or a local modification.}]] } set remote [dict get $selection remote] set comparison [:comparison $local $remote] set baseline {} set status unknown-no-baseline if {[dict exists $mapping baselineRef] && [dict get $mapping baselineRef] ne {}} { set baseSha [:commit $repo [dict get $mapping baselineRef]] set baseSelection [:selectDefinition [:source $repo $baseSha $file] $name $local] if {[dict get $baseSelection selected]} { set baseCompare [:comparison $local [dict get $baseSelection remote]] set baseDict {} foreach {k t v} $baseCompare { dict set baseDict $k $v } set status [expr {[dict get $baseDict normalizedIdentical] ? "matches-configured-baseline" : "differs-from-configured-baseline"}] set baseline [list available boolean true commit string $baseSha url string https://github.com/$repo/blob/$baseSha/$file comparison object $baseCompare] } else { set status unknown-baseline-variant set baseline [list available boolean false reason string upstream-conditional-variant-unresolved commit string $baseSha url string https://github.com/$repo/blob/$baseSha/$file candidates array [:candidates $baseSelection $local]] } } # File history supplies candidates, not proof that each commit changes # this procedure or fixes the diagnosed failure. set history {} set historyAvailable true if {[catch { set records [ns_json parse [:get /repos/$repo/commits?sha=$sha&path=[ns_urlencode $file]&per_page=5 application/vnd.github+json]] set i 0 foreach record [lrange $records 0 4] { set hash [dict get $record sha] if {![regexp {^[a-f0-9]{40}$} $hash]} { error "invalid-github-commit" } set parents {} set j 0 set parentsAvailable [dict exists $record parents] if {$parentsAvailable} { foreach parent [lrange [dict get $record parents] 0 1] { set parentSha [dict get $parent sha] if {![regexp {^[a-f0-9]{40}$} $parentSha]} { error "invalid-github-commit" } lappend parents $j object [list commit string $parentSha url string https://github.com/$repo/commit/$parentSha] incr j } } set parentsComplete [expr {$parentsAvailable && [llength [dict get $record parents]]<=2}] lappend history $i object [list commit string $hash url string https://github.com/$repo/commit/$hash message string [string range [dict get $record commit message] 0 2047] parents array $parents parentsComplete boolean $parentsComplete] incr i } }]} { set historyAvailable false } set localBody [dict get $local body] set remoteBody [dict get $remote body] set first 0 set a [split $localBody \n] set b [split $remoteBody \n] while {$first<min([llength $a],[llength $b]) && [lindex $a $first] eq [lindex $b $first]} { incr first } set localExcerpt [string range [join [lrange $a [expr {max(0,$first-3)}] [expr {$first+30}]] \n] 0 8191] set remoteExcerpt [string range [join [lrange $b [expr {max(0,$first-3)}] [expr {$first+30}]] \n] 0 8191] return [list {} object [list available boolean true comparisonPerformed boolean true resolutionCacheSeconds number 60 comparisonScope string literal-source localNormalizations array $normalizations localComparisonBodySha256 string [ns_crypto::md string -digest sha256 -- [dict get $local comparisonBody]] candidateCount number [llength [dict get $selection candidates]] selectedCandidate number [:dict getdef $selection selectedCandidate 0] candidates array $candidates conditionsEvaluated boolean false server string [$context server] observedAt number [clock seconds] name string $name sourceFile string $path repository string $repo upstreamPath string $file requestedRef string $ref commit string $sha upstreamUrl string https://github.com/$repo/blob/$sha/$file localObservedAt number $localObservedAt localArguments string [dict get $local arguments] localDefinitionSha256 string [dict get $local definitionSha256] localBodySha256 string [ns_crypto::md string -digest sha256 -- $localBody] upstreamFileSha256 string [ns_crypto::md string -digest sha256 -- $source] comparison object $comparison baseline object $baseline localModificationStatus string $status upstreamFixVerified boolean false firstDifferentBodyLine number [expr {$localBody eq $remoteBody ? 0 : $first+1}] excerptStartBodyLine number [expr {max(0,$first-3)+1}] localExcerpt string $localExcerpt upstreamExcerpt string $remoteExcerpt excerptsComplete boolean [expr {$localExcerpt eq $localBody && $remoteExcerpt eq $remoteBody}] extraction object [list form string [dict get $remote form] dynamicCommands number [dict get $remote dynamicCommands] unsupportedCommands number [dict get $remote unsupportedCommands] selection string [dict get $selection selection]] recentFileCommits array $history historyAvailable boolean $historyAvailable historyLimit number 5 diskCompared boolean false limitations string {Conditional selection identifies a unique literal definition matching loaded code; it does not evaluate guards or establish the active implementation in an upstream installation. Raw identical/bodyIdentical comparisons preserve all bytes. normalizedIdentical/normalizedBodyIdentical separately account for explicitly reported NSF-generated preambles. Equality compares exact bodies and canonical argument lists, not semantics or full object state. Generated wrappers, formatting and framework versions can cause differences. A configured baseline is an administrator assertion, not verified installation provenance. File history is bounded and not procedure-specific. Excerpts may omit other changes. An upstream fix requires connecting code changes to the observed failure; commit messages alone are insufficient. No local source was uploaded and no remote code was executed.}]] }XQL Not present: Generic, PostgreSQL, Oracle
![[i]](/resources/acs-subsite/ZoomIn16.gif)