Browse Source

G-123 increment 3: punk-runtime server-trust consent gate (punkshell 0.34.0)

fetch - the executable-retrieving act - now gates at entry when the
origin is non-canonical (PUNKBIN_URL override): the explicit
-trust-server flag or PUNKBIN_TRUST_SERVER=1 env (unattended form) is
the acknowledgement, one vocabulary with make.tcl libfetch's gate
(G-139). The canonical punkbin origin gates nothing (G-058's unattended
tclsfe constraint fetch is unchanged); metadata-only actions
(list -remote, platforms -remote) never gate; never an interactive
prompt. Refusal wording mirrors libfetch's.

ps1: -trust-server is pre-scanned before parameter binding (hyphenated
dynamic-parameter names are fragile under the PS 5.1 binder the windows
wrap routes through). Trap recorded in the goal Progress: a ps1 comment
line must never BEGIN with the word 'requires' - PowerShell parses
'#requires ...' as the #Requires statement and fails the whole file
(found by the powershell.exe 5.1 smoke).

runtimecmd_freshness.test: env seam gains PUNKBIN_TRUST_SERVER=1 + the
matching teardown unset (the fixture is a non-canonical origin, so its
fetch-driving tests need the unattended acknowledgement) - flagged as a
test-contract edit in the goal Progress; every existing assertion
unchanged and passing. Gate refusal characterization lands with the
increment-4 fixture coverage.

Payload growth pushed template label :exit_multishell onto a 512-byte
cmd label-scan boundary - checkfile caught it as designed; fixed via a
+16-byte spacer line (the documented knob). bin/punk-runtime.cmd
re-wrapped. Verification: gate smokes in both payloads (refusal + both
acknowledgement forms; ps1 under powershell.exe 5.1); checkfile 0
ERROR; pinned tests all PASS (checkfile + freshness 12/12, roundtrip
byte-identical under the tclsh9.0.5-punk baseline runner).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 day ago
parent
commit
bc22def1dc
  1. 13
      CHANGELOG.md
  2. 75
      bin/punk-runtime.cmd
  3. 29
      goals/G-123-thirdparty-runtime-tiers.md
  4. 2
      punkproject.toml
  5. 33
      src/scriptapps/bin/punk-runtime.bash
  6. 42
      src/scriptapps/bin/punk-runtime.ps1
  7. 7
      src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test

13
CHANGELOG.md

@ -5,6 +5,19 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy.
## [0.34.0] - 2026-07-31
- `punk-runtime` fetch now carries the G-123 server-trust consent gate (one
vocabulary with `make.tcl libfetch`'s `-trust-server`): fetching runtime
binaries from a non-canonical origin (`PUNKBIN_URL` override) requires the
explicit `-trust-server` flag or `PUNKBIN_TRUST_SERVER=1` (the unattended
form); the canonical punkbin origin gates nothing, and the metadata-only
listings (`list -remote`, `platforms -remote`) never gate - never an
interactive prompt. Both payloads; `bin/punk-runtime.cmd` re-wrapped, with
a spacer adjustment clearing the 512-byte cmd label-scan boundary the
payload growth pushed a template label onto (caught by scriptwrap
checkfile exactly as designed).
## [0.33.0] - 2026-07-31
- Runtime artifact records move to schema v2 (G-123): `[provenance]` gains the

75
bin/punk-runtime.cmd

@ -1291,7 +1291,8 @@ basename=$(basename "$scriptpath") #e.g punk-runtime.bash
scriptroot="${basename%.*}" #e.g "punk-runtime"
#artifact server base url - overridable for mirrors/testing
url_kitbase="${PUNKBIN_URL:-https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master}"
url_canonical="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
url_kitbase="${PUNKBIN_URL:-$url_canonical}"
#$OSTYPE varies in capitalization across for example zsh and bash
#uname probably a more consistent bet
arch=$(uname -m) #machine/architecture
@ -1387,6 +1388,11 @@ fi
action="${1:-}"
[[ $# -gt 0 ]] && shift
platform_opt=""
#G-123 server-trust consent: -trust-server flag > PUNKBIN_TRUST_SERVER=1 env
#(unattended form) - acknowledgement for a NON-CANONICAL origin; see the fetch
#action's gate. Never an interactive prompt.
trust_server=0
[[ "${PUNKBIN_TRUST_SERVER:-}" == "1" ]] && trust_server=1
case "$action" in
fetch|list|use|info|platforms)
newargs=()
@ -1398,6 +1404,8 @@ case "$action" in
exit 1
fi
platform_opt="$1"
elif [[ "$1" == "-trust-server" ]]; then
trust_server=1
else
newargs+=("$1")
fi
@ -1552,7 +1560,7 @@ metadata_summary() {
#Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage.
show_usage() {
echo "Usage: $0 {fetch|list|use|run|info|platforms|help}"
echo " fetch ?<name>? ?-platform <p>? download+verify a runtime from punkbin"
echo " fetch ?<name>? ?-platform <p>? ?-trust-server? download+verify a runtime from punkbin"
echo " list ?-remote? ?-platform <p>? installed (or server) runtimes + metadata"
echo " use <name> ?-platform <p>? select active / materialize -r<N> artifact"
echo " run ?args...? launch the active local-platform runtime"
@ -1633,7 +1641,11 @@ show_help() {
echo " chmod on the receiving side)."
echo ""
echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM,"
echo " PUNK_ACTIVE_RUNTIME (run-time selection override)"
echo " PUNK_ACTIVE_RUNTIME (run-time selection override),"
echo " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a"
echo " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to"
echo " server trust, G-139/G-123; the canonical origin and the metadata-only"
echo " list/platforms -remote actions never gate)"
echo ""
echo "Examples:"
echo " $0 platforms -remote"
@ -1772,6 +1784,20 @@ case "$action" in
echo "(canonical names: 'help platforms' in the punk shell)"
exit 1
fi
#G-123 server-trust consent gate (one vocabulary with make.tcl libfetch's
#-serverurl/-trust-server gate, G-139): FETCH retrieves runtime binaries,
#so a non-canonical origin (PUNKBIN_URL override) requires the explicit
#acknowledgement. The canonical punkbin origin gates nothing (existing
#unattended flows unchanged); metadata-only actions (list -remote,
#platforms -remote) never gate. Never an interactive prompt.
if [[ "$url_kitbase" != "$url_canonical" && "$trust_server" -ne 1 ]]; then
echo "punk-runtime fetch: origin '$url_kitbase' is not the canonical punkbin origin" >&2
echo " canonical: $url_canonical" >&2
echo " fetching runtime binaries from a non-canonical server requires the explicit" >&2
echo " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)" >&2
echo " (consent keyed to server trust - G-139/G-123)" >&2
exit 1
fi
runtime="${1:-}"
fetch_default=""
if [[ -z "$runtime" ]]; then
@ -2388,6 +2414,7 @@ esac
#(512B spacer) byte-alignment padding so template labels beyond the payloads clear cmd's
#512-byte label-scan boundaries - resize this comment if scriptwrap checkfile reports a
#boundary-spanning label after a payload edit (see bin/AGENTS.md polyglot workflow)
#padding-512-ok
#</shell-payload>
@ -2795,7 +2822,7 @@ function Get-PunkRuntimeCandidates {
#Keep in sync with the bash payload's show_help/show_usage.
function Show-PunkRuntimeUsage {
write-host "Usage: punk-runtime.cmd {fetch|list|use|run|info|platforms|help}"
write-host " fetch ?<name>? ?-platform <p>? download+verify a runtime from punkbin"
write-host " fetch ?<name>? ?-platform <p>? ?-trust-server? download+verify a runtime from punkbin"
write-host " list ?-remote? ?-platform <p>? installed (or server) runtimes + metadata"
write-host " use <name> ?-platform <p>? select active / materialize -r<N> artifact"
write-host " run ?args...? launch the active local-platform runtime"
@ -2875,7 +2902,11 @@ function Show-PunkRuntimeHelp {
write-host " chmod on the receiving side)."
write-host ""
write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM,"
write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)"
write-host " PUNK_ACTIVE_RUNTIME (run-time selection override),"
write-host " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a"
write-host " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to"
write-host " server trust, G-139/G-123; the canonical origin and the metadata-only"
write-host " list/platforms -remote actions never gate)"
write-host ""
write-host "Examples:"
write-host " punk-runtime.cmd platforms -remote"
@ -3403,12 +3434,33 @@ function psmain {
$rtfolder = Join-Path -Path $outbase -ChildPath "runtime"
#Binary artifact server url. (git is not ideal for this - but will do for now - todo - use artifact system within gitea?)
#overridable for mirrors/testing - keep in sync with the bash payload
$artifacturl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
$canonicalurl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
$artifacturl = $canonicalurl
if ($env:PUNKBIN_URL) {
$artifacturl = $env:PUNKBIN_URL
}
switch ($action) {
'fetch' {
#G-123 server-trust consent gate (one vocabulary with make.tcl
#libfetch's -serverurl/-trust-server gate, G-139): FETCH retrieves
#runtime binaries, so a non-canonical origin (PUNKBIN_URL override)
#needs the explicit acknowledgement (-trust-server flag,
#pre-scanned before parameter binding, or PUNKBIN_TRUST_SERVER=1
#for unattended flows). The canonical punkbin origin gates nothing;
#metadata-only actions (list -remote, platforms -remote) never
#gate. Never an interactive prompt. Keep in sync with the bash
#payload's gate. (A comment line must never BEGIN with the word
#'requires' - PowerShell parses '#requires ...' as the #Requires
#statement and fails the whole file.)
$trustok = $script:PunkTrustServer -or ($env:PUNKBIN_TRUST_SERVER -eq "1")
if (($artifacturl -ne $canonicalurl) -and (-not $trustok)) {
write-host "punk-runtime fetch: origin '$artifacturl' is not the canonical punkbin origin"
write-host " canonical: $canonicalurl"
write-host " fetching runtime binaries from a non-canonical server requires the explicit"
write-host " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)"
write-host " (consent keyed to server trust - G-139/G-123)"
exit 1
}
$arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"]
$archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
$archurl = "$artifacturl/$arch"
@ -4192,7 +4244,16 @@ function psmain {
#$returnvalue = psmain @args
#Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan
#return $returnvalue
psmain @args | out-null
#G-123: -trust-server is pre-scanned here rather than declared as a dynamic
#parameter - a hyphenated parameter name is fragile under the PS 5.1 binder the
#windows wrap routes through, and the bash payload's manual option scan is the
#behavioural twin. Keep the flag spelling identical to make.tcl libfetch's.
$script:PunkTrustServer = $false
$punkargs_filtered = @()
foreach ($punkarg in $args) {
if ($punkarg -eq "-trust-server") { $script:PunkTrustServer = $true } else { $punkargs_filtered += $punkarg }
}
psmain @punkargs_filtered | out-null
exit 0

29
goals/G-123-thirdparty-runtime-tiers.md

@ -227,3 +227,32 @@ see goals/archive/G-058-static-runtime-packages.md).
origin tag sourced from REMOTE records was deliberately scoped out of
listings; the per-name detail view for a not-yet-fetched artifact
stays a candidate follow-on if acceptance walking finds it wanted.
- 2026-07-31 increment 3 (punkshell 0.34.0): server-trust consent gate,
both payloads, one vocabulary with libfetch (G-139).
- fetch (the executable-retrieving act) gates at entry when the origin
is non-canonical (PUNKBIN_URL override): explicit -trust-server flag
or PUNKBIN_TRUST_SERVER=1 env (unattended form). The canonical origin
gates nothing (G-058's unattended tclsfe constraint fetch unchanged);
metadata-only actions (list -remote / platforms -remote) never gate;
never an interactive prompt. Refusal wording mirrors libfetch's gate.
- ps1 implementation note: -trust-server is PRE-SCANNED before parameter
binding (a hyphenated dynamic-parameter name is fragile under the
PS 5.1 binder the windows wrap routes through). TRAP RECORDED: a ps1
comment line must never BEGIN with the word 'requires' - PowerShell
parses '#requires ...' as the #Requires statement and fails the WHOLE
file (found by the powershell.exe 5.1 smoke; fixed by rewording).
- runtimecmd_freshness.test env seam gains PUNKBIN_TRUST_SERVER=1 (the
fixture is a non-canonical origin, so its fetch-driving tests need the
unattended acknowledgement) + matching teardown unset. TEST-CONTRACT
EDIT flagged for maintainer review; all existing assertions unchanged
and passing. The gate's own refusal characterization is increment-4
scope.
- Payload growth pushed template label :exit_multishell onto a 512-byte
cmd label-scan boundary (callsite-relative offset 497 + 16-byte label;
checkfile caught it exactly as designed) - fixed with a +16-byte
spacer line, the documented knob, and re-wrapped.
- Verified: gate smoke both payloads (refusal without acknowledgement,
flag + env forms both pass the gate; ps1 driven under powershell.exe
5.1); checkfile clean (0 ERROR lines); pinned tests ALL PASS -
checkfile + freshness 12/12, roundtrip byte-identical under the
tclsh9.0.5-punk baseline runner.

2
punkproject.toml

@ -1,4 +1,4 @@
[project]
name = "punkshell"
version = "0.33.0"
version = "0.34.0"
license = "BSD-2-Clause"

33
src/scriptapps/bin/punk-runtime.bash

@ -11,7 +11,8 @@ basename=$(basename "$scriptpath") #e.g punk-runtime.bash
scriptroot="${basename%.*}" #e.g "punk-runtime"
#artifact server base url - overridable for mirrors/testing
url_kitbase="${PUNKBIN_URL:-https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master}"
url_canonical="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
url_kitbase="${PUNKBIN_URL:-$url_canonical}"
#$OSTYPE varies in capitalization across for example zsh and bash
#uname probably a more consistent bet
arch=$(uname -m) #machine/architecture
@ -107,6 +108,11 @@ fi
action="${1:-}"
[[ $# -gt 0 ]] && shift
platform_opt=""
#G-123 server-trust consent: -trust-server flag > PUNKBIN_TRUST_SERVER=1 env
#(unattended form) - acknowledgement for a NON-CANONICAL origin; see the fetch
#action's gate. Never an interactive prompt.
trust_server=0
[[ "${PUNKBIN_TRUST_SERVER:-}" == "1" ]] && trust_server=1
case "$action" in
fetch|list|use|info|platforms)
newargs=()
@ -118,6 +124,8 @@ case "$action" in
exit 1
fi
platform_opt="$1"
elif [[ "$1" == "-trust-server" ]]; then
trust_server=1
else
newargs+=("$1")
fi
@ -272,7 +280,7 @@ metadata_summary() {
#Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage.
show_usage() {
echo "Usage: $0 {fetch|list|use|run|info|platforms|help}"
echo " fetch ?<name>? ?-platform <p>? download+verify a runtime from punkbin"
echo " fetch ?<name>? ?-platform <p>? ?-trust-server? download+verify a runtime from punkbin"
echo " list ?-remote? ?-platform <p>? installed (or server) runtimes + metadata"
echo " use <name> ?-platform <p>? select active / materialize -r<N> artifact"
echo " run ?args...? launch the active local-platform runtime"
@ -353,7 +361,11 @@ show_help() {
echo " chmod on the receiving side)."
echo ""
echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM,"
echo " PUNK_ACTIVE_RUNTIME (run-time selection override)"
echo " PUNK_ACTIVE_RUNTIME (run-time selection override),"
echo " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a"
echo " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to"
echo " server trust, G-139/G-123; the canonical origin and the metadata-only"
echo " list/platforms -remote actions never gate)"
echo ""
echo "Examples:"
echo " $0 platforms -remote"
@ -492,6 +504,20 @@ case "$action" in
echo "(canonical names: 'help platforms' in the punk shell)"
exit 1
fi
#G-123 server-trust consent gate (one vocabulary with make.tcl libfetch's
#-serverurl/-trust-server gate, G-139): FETCH retrieves runtime binaries,
#so a non-canonical origin (PUNKBIN_URL override) requires the explicit
#acknowledgement. The canonical punkbin origin gates nothing (existing
#unattended flows unchanged); metadata-only actions (list -remote,
#platforms -remote) never gate. Never an interactive prompt.
if [[ "$url_kitbase" != "$url_canonical" && "$trust_server" -ne 1 ]]; then
echo "punk-runtime fetch: origin '$url_kitbase' is not the canonical punkbin origin" >&2
echo " canonical: $url_canonical" >&2
echo " fetching runtime binaries from a non-canonical server requires the explicit" >&2
echo " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)" >&2
echo " (consent keyed to server trust - G-139/G-123)" >&2
exit 1
fi
runtime="${1:-}"
fetch_default=""
if [[ -z "$runtime" ]]; then
@ -1108,3 +1134,4 @@ esac
#(512B spacer) byte-alignment padding so template labels beyond the payloads clear cmd's
#512-byte label-scan boundaries - resize this comment if scriptwrap checkfile reports a
#boundary-spanning label after a payload edit (see bin/AGENTS.md polyglot workflow)
#padding-512-ok

42
src/scriptapps/bin/punk-runtime.ps1

@ -111,7 +111,7 @@ function Get-PunkRuntimeCandidates {
#Keep in sync with the bash payload's show_help/show_usage.
function Show-PunkRuntimeUsage {
write-host "Usage: punk-runtime.cmd {fetch|list|use|run|info|platforms|help}"
write-host " fetch ?<name>? ?-platform <p>? download+verify a runtime from punkbin"
write-host " fetch ?<name>? ?-platform <p>? ?-trust-server? download+verify a runtime from punkbin"
write-host " list ?-remote? ?-platform <p>? installed (or server) runtimes + metadata"
write-host " use <name> ?-platform <p>? select active / materialize -r<N> artifact"
write-host " run ?args...? launch the active local-platform runtime"
@ -191,7 +191,11 @@ function Show-PunkRuntimeHelp {
write-host " chmod on the receiving side)."
write-host ""
write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM,"
write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)"
write-host " PUNK_ACTIVE_RUNTIME (run-time selection override),"
write-host " PUNKBIN_TRUST_SERVER=1 (unattended form of -trust-server: fetch from a"
write-host " non-canonical PUNKBIN_URL origin requires one of them - consent keyed to"
write-host " server trust, G-139/G-123; the canonical origin and the metadata-only"
write-host " list/platforms -remote actions never gate)"
write-host ""
write-host "Examples:"
write-host " punk-runtime.cmd platforms -remote"
@ -719,12 +723,33 @@ function psmain {
$rtfolder = Join-Path -Path $outbase -ChildPath "runtime"
#Binary artifact server url. (git is not ideal for this - but will do for now - todo - use artifact system within gitea?)
#overridable for mirrors/testing - keep in sync with the bash payload
$artifacturl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
$canonicalurl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master"
$artifacturl = $canonicalurl
if ($env:PUNKBIN_URL) {
$artifacturl = $env:PUNKBIN_URL
}
switch ($action) {
'fetch' {
#G-123 server-trust consent gate (one vocabulary with make.tcl
#libfetch's -serverurl/-trust-server gate, G-139): FETCH retrieves
#runtime binaries, so a non-canonical origin (PUNKBIN_URL override)
#needs the explicit acknowledgement (-trust-server flag,
#pre-scanned before parameter binding, or PUNKBIN_TRUST_SERVER=1
#for unattended flows). The canonical punkbin origin gates nothing;
#metadata-only actions (list -remote, platforms -remote) never
#gate. Never an interactive prompt. Keep in sync with the bash
#payload's gate. (A comment line must never BEGIN with the word
#'requires' - PowerShell parses '#requires ...' as the #Requires
#statement and fails the whole file.)
$trustok = $script:PunkTrustServer -or ($env:PUNKBIN_TRUST_SERVER -eq "1")
if (($artifacturl -ne $canonicalurl) -and (-not $trustok)) {
write-host "punk-runtime fetch: origin '$artifacturl' is not the canonical punkbin origin"
write-host " canonical: $canonicalurl"
write-host " fetching runtime binaries from a non-canonical server requires the explicit"
write-host " -trust-server flag (or PUNKBIN_TRUST_SERVER=1 for unattended flows)"
write-host " (consent keyed to server trust - G-139/G-123)"
exit 1
}
$arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"]
$archfolder = Join-Path -Path $rtfolder -ChildPath "$arch"
$archurl = "$artifacturl/$arch"
@ -1508,6 +1533,15 @@ function psmain {
#$returnvalue = psmain @args
#Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan
#return $returnvalue
psmain @args | out-null
#G-123: -trust-server is pre-scanned here rather than declared as a dynamic
#parameter - a hyphenated parameter name is fragile under the PS 5.1 binder the
#windows wrap routes through, and the bash payload's manual option scan is the
#behavioural twin. Keep the flag spelling identical to make.tcl libfetch's.
$script:PunkTrustServer = $false
$punkargs_filtered = @()
foreach ($punkarg in $args) {
if ($punkarg -eq "-trust-server") { $script:PunkTrustServer = $true } else { $punkargs_filtered += $punkarg }
}
psmain @punkargs_filtered | out-null
exit 0

7
src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test

@ -277,13 +277,17 @@ namespace eval ::testspace {
if {$fixtureok} {
#point both payloads at the fixture server; shield the run from
#ambient punk-runtime env state (restored at end of file)
foreach v {PUNKBIN_URL PUNK_RUNTIME_PLATFORM PUNK_ACTIVE_RUNTIME} {
foreach v {PUNKBIN_URL PUNK_RUNTIME_PLATFORM PUNK_ACTIVE_RUNTIME PUNKBIN_TRUST_SERVER} {
if {[info exists ::env($v)]} {
dict set saved_env $v $::env($v)
unset ::env($v)
}
}
set ::env(PUNKBIN_URL) $baseurl
#G-123 server-trust consent: the fixture is a non-canonical origin, so
#fetch-driving tests need the unattended acknowledgement (the gate
#itself has its own characterization coverage)
set ::env(PUNKBIN_TRUST_SERVER) 1
}
}
testConstraint fixtureserver $fixtureok
@ -570,6 +574,7 @@ namespace eval ::testspace {
if {$fixtureok} {
server_stop
unset -nocomplain ::env(PUNKBIN_URL)
unset -nocomplain ::env(PUNKBIN_TRUST_SERVER)
dict for {v val} $saved_env {
set ::env($v) $val
}

Loading…
Cancel
Save