For AI agents: This document provides a deep-dive analysis of Eagle's
[package]command internals, including the 23 sub-commands, the multi-source package index discovery pipeline (host, filesystem, plugin, bundle), tagged package indexes, the auto-path system and its integration with interpreter initialization, package aliases with circular-reference detection, the security verification chain (Authenticode, StrongName, locked/rejected packages), the package require fallback chain, and the.noPkgIndexdisable mechanism. For basic command syntax, seecore_language.md. For usage examples, seecore_examples.md. For package toolset procedures, seecore_script_library.md.
Eagle's [package] command provides Tcl-compatible package management
with substantial extensions for security, multi-source discovery, and
enterprise deployment. It manages the full lifecycle of packages —
registering, discovering, loading, versioning, aliasing, and withdrawing
reusable collections of commands and procedures.
There are four key areas of complexity beyond standard Tcl:
-
Multi-source index discovery pipeline — Package indexes are discovered from four sources in configurable order: the interpreter host (built-in library packages), the filesystem (primary and tagged index files), plugin assemblies (embedded resources), and script bundle databases. The
FindAllorchestrator coordinates these sources with deduplication and configurable precedence viaPreferFileSystem/PreferHost. -
Tagged package indexes — Beyond the standard
pkgIndex.eaglefile, Eagle supports tagged index files namedpkgIndex_XXXXXXXXXXXXXXXX.eagle(16 hex digits). During evaluation, the tag is available via the$tagvariable, enabling a single index script to handle multiple package variants, versions, or configurations. -
Auto-path integration — The
auto_pathvariable is built from multiple sources (environment variables, assembly location, platform paths) and is deeply integrated with the package subsystem. Changes toauto_pathautomatically trigger a full package index rescan via a variable trace callback, and the auto-path is initialized in two phases during interpreter creation. -
Security verification during discovery — Package scanning can enforce Authenticode signature validation and StrongName verification on assemblies found during index discovery. Packages can be locked (preventing replacement) or locked+rejected (generating errors on replacement attempts). The
.noPkgIndexmarker file disables indexing for individual files or entire directory trees.
The command carries CommandFlags.Unsafe | CommandFlags.Standard | CommandFlags.Initialize | CommandFlags.SecuritySdk | CommandFlags.LicenseSdk and belongs to the "scriptEnvironment" object
group.
Key source files:
| File | Lines | Role |
|---|---|---|
Eagle/Library/Commands/Package.cs |
~1,450 | Main command implementation (23 sub-commands) |
Eagle/Library/Components/Private/PackageOps.cs |
4,767 | Package operations: index discovery, version comparison, security checks, script generation |
Eagle/Library/Components/Public/Interpreter.cs |
124,855 | Package storage, alias resolution, fallback chain, auto-path management |
Eagle/Library/Components/Public/PackageData.cs |
~191 | Package metadata implementation (IPackageData) |
Eagle/Library/Components/Private/PackageContextClientData.cs |
~250 | State management during index evaluation |
Eagle/Library/Containers/Private/PackageIndexDictionary.cs |
~71 | Index file → flags mapping |
Eagle/Library/Containers/Private/PackageAliasDictionary.cs |
~100 | Alias name → (package, version, flags) mapping |
Eagle/Library/Components/Public/Enumerations.cs |
large | PackageFlags, PackageIndexFlags, PackageType enums |
Eagle/Library/Components/Private/GlobalState.cs |
large | Auto-path list construction and caching |
Eagle's [package] supports the core Tcl sub-commands (require,
provide, ifneeded, forget, names, versions, vcompare,
vsatisfies, unknown) with compatible semantics. Tcl scripts that
use standard package management patterns work unchanged. The one
exception is vsatisfies: Eagle drops Tcl's same-major-version
requirement, making it more permissive (see §3.5).
Eagle adds 14 sub-commands beyond Tcl's standard set:
| Sub-command | Purpose |
|---|---|
absent |
Pre-condition: verify package is NOT loaded |
alias / aliases |
Package name aliasing with version and flag overrides |
indexes |
List discovered package index files |
[info] |
Detailed package metadata (flags, paths, loaded status) |
loaded / vloaded |
Query loaded packages (with/without version info) |
pending |
Check if packages are currently being loaded |
present |
Post-condition: verify package IS loaded |
relativefilename |
Convert paths relative to package directory |
reset |
Clear all package index information |
scan |
Multi-source index discovery with 30+ options |
vsort |
Version sorting |
withdraw |
Unload a package without removing its registration |
Eagle's package index files are named pkgIndex.eagle (not
pkgIndex.tcl). They contain Eagle scripts that register packages via
[package ifneeded], just like Tcl, but the scripts can use Eagle's
full feature set including .NET interop.
package require ?options? package ?version?| Option | Type | Description |
|---|---|---|
-exact |
flag | Require exact version match (Tcl-compatible) |
-autoscan |
bool | Enable/disable auto-scanning on failure |
The require sub-command implements a multi-stage fallback chain
when a package is not immediately available:
1. interpreter.RequirePackage(name, version, exact)
└─ If fails:
2. PkgAutoScan() — rescan package indexes (if -autoscan enabled)
└─ RequirePackage() again
└─ If fails:
3. PackageFallback delegate — custom callback (if configured)
└─ RequirePackage() again
└─ If fails:
4. PackageUnknown script — evaluate unknown handler
└─ RequirePackage() again
└─ If fails: return combined error listEach stage is gated by interpreter flags:
InterpreterFlags.NoPackageFallback— skip stage 3InterpreterFlags.NoPackageUnknown— skip stage 4
Returns the version of the loaded package on success.
package present ?-exact? package ?version?Checks if a package is already loaded without attempting to load it. Returns the version if present; raises an error otherwise. Useful as a post-condition check.
package absent ?-exact? package ?version?Checks that a package is NOT loaded. Returns success if absent; raises an error if present. Useful as a pre-condition check before loading a specific version.
package provide package ?version?Declares that the current script provides a package at a version. Without a version argument, returns the currently provided version.
Special behavior: If the PackageFlags.NoProvide flag is set on the
interpreter, [package provide] silently does nothing and returns an
empty string. This is used during security package initialization.
package ifneeded package version ?script? ?flags?Registers a script to execute when [package require] needs a specific
version. Without a script argument, returns the currently registered
script.
Eagle extends Tcl's ifneeded with an optional flags parameter
(PackageFlags enum) that controls package behavior:
# Register with flags
package ifneeded mypackage 1.0 \
[list source [file join $dir mypackage.eagle]] \
{Core, Locked}Locked package behavior: If a package has the Locked flag:
Lockedalone:ifneededsilently succeeds without modifying the registration.Locked | Rejected:ifneededreturns an error:"rejected: package <name> is locked".
package scan ?options? ?dir dir ...?This is the most complex sub-command, with 30+ options controlling multi-source package index discovery. See §5 for the full pipeline description.
Key option groups:
| Category | Options | Effect |
|---|---|---|
| Source control | -host, -nohost, -bundle, -nobundle, -plugin, -noplugin, -normal, -nonormal |
Which sources to search |
| Index types | -primary, -noprimary, -tagged, -notagged |
Which index file types to include |
| Search behavior | -recursive, -refresh, -resolve |
How to search |
| Precedence | -preferfilesystem, -preferhost |
Source priority order |
| Security | -notrusted, -noverified |
Skip signature checks |
| Output | -trace, -verbose, -dump, -whatif |
Diagnostics |
| State | -reset, -autopath, -temporary |
State management |
| Error handling | -nocomplain, -fileerror |
Error behavior |
| Flags | -flags |
Direct PackageIndexFlags enum value |
| Scope | -interpreter |
Use interpreter-specific vs global defaults |
What-if mode (-whatif): Runs the discovery pipeline without
modifying interpreter state, returning a preview of what would be
found. Cannot be combined with -host, -bundle, or -plugin.
package indexes ?pattern?Returns the list of discovered package index files, optionally filtered by a glob pattern.
package resetClears all package index information, forcing a full rediscovery on the
next [package require] or [package scan].
package alias ?options? name ?package? ?version?| Option | Type | Description |
|---|---|---|
-overwrite |
flag | Overwrite existing alias |
-disabled |
flag | Create a disabled alias |
-exact |
flag | Require exact version match |
Creates an alias name that redirects to [package] at [version]. When
package require name is called, the alias is transparently resolved
to the target package.
Alias resolution uses a while-loop with circular reference
detection: a found dictionary tracks visited aliases, and if an
alias is encountered twice, the loop terminates to prevent infinite
recursion.
Disabled aliases: The -disabled flag creates an alias entry that
exists but is skipped during resolution. This can be used to temporarily
disable an alias without removing it.
package aliases ?pattern?Returns all package aliases matching the optional glob pattern.
package info nameReturns detailed metadata about a package as a key-value list:
| Key | Value | Notes |
|---|---|---|
kind |
IdentifierKind | Package type identifier |
id |
Guid | Unique package ID |
name |
string | Package name |
description |
string | Package description |
indexFileName |
string | Path to index file (scrubbed in safe mode) |
provideFileName |
string | Path to provide script (scrubbed in safe mode) |
flags |
PackageFlags | Current package flags |
loaded |
Version | Currently loaded version |
ifNeeded |
dict | Version → script mapping (hidden in safe mode) |
wasNeeded |
string | Version that was last requested |
package names ?pattern?
package loaded ?pattern?
package vloaded ?pattern?
package versions packageQuery commands for listing known packages, loaded packages (with or without version info), and available versions of a specific package.
package pending ?name?Without a name: returns true if any package is currently being loaded
(PackageLevels > 0). With a name: checks if a specific package has
the PackageFlags.Loading flag set. Useful for dependency cycle
detection.
package forget ?package package ...?Completely removes packages from the interpreter. The package entry is deleted, and all associated metadata is discarded. This is a permanent removal.
package withdraw package ?version?With a version argument, marks the package as unloaded by setting
package.Loaded = null, but preserves the package's registration and
ifNeeded scripts (the version must match the loaded version, otherwise
a conflicting versions withdrawn error is raised). Without a version
argument, package withdraw <name> is a getter: it returns the
currently-loaded version and leaves the package loaded — it does not
unload anything. The package can be loaded again via [package require].
This is a temporary unload — the distinction from forget is that
withdraw preserves the package entry while forget deletes it.
package vcompare version1 version2
package vsatisfies version1 version2
package vsort version1 version2Standard Tcl version operations. vcompare returns -1, 0, or 1.
vsatisfies returns a boolean. vsort sorts two versions.
Eagle's vsatisfies differs from Tcl. Eagle returns true whenever
version1 is greater than or equal to version2, dropping Tcl's
requirement that the two share the same major version. For example,
package vsatisfies 2.0 1.0 returns True in Eagle but 0 in Tcl
8.4/8.5/8.6 (Tcl rejects the cross-major bump).
Fallback behavior: If version strings fail to parse as .NET
Version objects, vcompare raises an error while vsort falls back to string
comparison rather than raising an error.
AlwaysSatisfy flag: If PackageFlags.AlwaysSatisfy is set on the
interpreter, vsatisfies always returns true. This is used during
security package initialization to prevent version conflicts.
package unknown ?command?Gets or sets the unknown package handler script, evaluated as the last
resort in the [package require] fallback chain.
package relativefilename fileName ?type?Converts a file path to be relative to the package index directory.
The optional type parameter (PathComparisonType enum) controls
the comparison strategy.
The auto_path variable is a Tcl list of directories that the package
subsystem searches for package index files. Eagle extends this concept
with multi-source path construction, environment variable integration,
and an automatic rescan mechanism via variable traces.
The auto-path is built by GlobalState.GetAutoPathList(), which
combines interpreter-specific paths and shared global paths:
interpreterLibraryPath— The interpreter's library path overrideinterpreterAutoPathList— Paths explicitly set on the interpreter
The shared auto-path is populated from these sources, in this priority order:
| Source | Environment Variable | Description |
|---|---|---|
| Eagle library | EAGLE_LIBRARY |
Explicit Eagle library path |
| Assembly location | (computed) | Directory containing Eagle.dll |
| Tcl library | TCL_LIBRARY |
Tcl library path (for compatibility) |
| Eagle lib paths | EAGLELIBPATH |
Additional search directories (space-separated list) |
| Tcl lib paths | TCLLIBPATH |
Tcl library paths (space-separated list) |
| Unix package paths | (computed) | /usr/local/lib/eagle<ver>, /usr/lib/eagle<ver> |
| Binary directory | (computed) | Package subdirectories under the binary location |
| Assembly directory | (computed) | Package subdirectories under the assembly location |
| Peer directories | (computed) | Sibling directories of the binary/assembly location |
| Root directories | (computed) | Root-level package paths |
A path is only added to the auto-path if:
- It is not null or empty.
- It is not already in the list (no duplicates).
- If strict mode is enabled: the directory must exist on the filesystem.
- If a
No_<pathName>environment variable exists: the path is skipped (allows selective disabling of individual paths).
The shared auto-path list is cached in GlobalState for efficiency.
It is only rebuilt when:
- First accessed (lazy initialization).
- Explicitly refreshed via
GlobalState.RefreshAutoPathList(). - The
-autopathflag is used with[package scan].
The auto-path is initialized in two phases during interpreter creation:
Phase 1 — Early initialization (before script library loading):
- Calls
PrivateInitializeAutoPath(null, true, true, true). - Sets a minimal auto-path to bootstrap the interpreter.
- Does not load the full global auto-path list yet.
Phase 2 — Final initialization (after script library loading):
- Calls
PrivateInitializeAutoPath(autoPathList, false, false, false). - If
InitializeFlags.SetAutoPathis set:- If
InitializeFlags.GlobalAutoPathis set: callsGlobalState.GetAutoPathList(interpreter)to build the full path list. - If
InitializeFlags.MergeAutoPathis set: merges new paths with any existing paths. - Calls
SetAutoPathList()to set theauto_pathvariable. - Attaches the AutoPathTraceCallback to monitor future changes.
- If
After initialization, a variable trace is attached to auto_path that
automatically triggers package index rescanning whenever the variable
is modified:
On auto_path SET (BeforeVariableSet):
- Parses the new
auto_pathvalue as a Tcl list of directories. - In safe interpreters: validates that all directories are under the interpreter's base path (security constraint).
- Calls
PackageOps.FindAll()withPackageIndexFlags.AutoPathflags to perform a full index rescan of the new directories. - Updates
interpreter.PackageIndexeswith the discovery results.
On auto_path UNSET (BeforeVariableUnset):
- Clears the
NoRemoveflag to allow the variable to be unset. - Calls
interpreter.ResetPkgIndexes()to clear all package index information.
This means that simply appending a directory to auto_path triggers
automatic discovery of any packages in that directory — no explicit
[package scan] is needed.
The AutoPath flag is a composite that configures the rescan triggered
by auto-path changes:
AutoPath = Host | Bundle | Normal | Primary | Tagged
| NoNormal | Recursive | NoSort | DumpIn DEBUG builds, it also includes MaybeNoTrusted | MaybeNoVerified
to relax security checks during development.
The -autopath flag on [package scan] triggers a special mode:
- Calls
GlobalState.GetAutoPathList(interpreter, true)withrefresh=trueto re-read environment variables and rebuild the path list. - Updates the
auto_pathvariable with the refreshed paths viainterpreter.SetAutoPathList(). - Uses the refreshed paths for the scan operation.
This is useful after environment changes to force the interpreter to pick up new package directories.
The PackageOps.FindAll() method orchestrates multi-source package
index discovery. It searches four sources and can be configured to
search them in different orders:
FindAll(interpreter, paths, flags, ...)
├─ ShouldPreferFileSystem() → determines order
│
├─ Path A: FileSystem-First (PreferFileSystem = true)
│ ├─ FindFile() — filesystem pkgIndex.eagle files
│ ├─ FindPlugin() — plugin assembly embedded indexes
│ └─ FindHost() — built-in host/library indexes
│
└─ Path B: Host-First (default)
├─ FindHost() — built-in host/library indexes
├─ FindPlugin() — plugin assembly embedded indexes
└─ FindFile() — filesystem pkgIndex.eagle files
│
└─ RemoveLogicalDuplicates() — deduplicate across sourcesDiscovers package indexes embedded in the Eagle runtime:
| PackageType | Index path | Purpose |
|---|---|---|
Loader |
lib/Loader1.0/pkgIndex.eagle |
Plugin loader package |
Library |
lib/Eagle1.0/pkgIndex.eagle |
Core script library |
Test |
lib/Test1.0/pkgIndex.eagle |
Test framework |
Kit |
lib/Kit1.0/pkgIndex.eagle |
Kit packages |
Bundle |
(from BundleManager) | Bundle-embedded indexes |
Host |
(from host list file) | Host-defined packages |
Bundle indexes are gathered via DataOps.GatherBundleScripts() from
mounted bundle databases. Host indexes are read from a host list file
in the interpreter's script library.
Searches specified directories for two types of index files:
Primary indexes (-primary flag):
- Pattern:
pkgIndex.eagle - Standard package index files, one per directory.
Tagged indexes (-tagged flag):
- Pattern:
pkgIndex_XXXXXXXXXXXXXXXX.eagle(16 hex digits) - Regex:
^pkgIndex_([0-9a-f]{16})\.eagle$ - Multiple tagged indexes can coexist in a single directory.
- The tag is extracted and made available as the
$tagvariable during script evaluation.
For each directory:
- Check if disabled via
.noPkgIndex(see §7). - Search for primary and/or tagged index files.
- Extract tags from tagged filenames via regex.
- Call
InvokeCallback()for each file found.
The -recursive flag enables subdirectory searching.
Searches for .dll files in plugin directories and extracts embedded
pkgIndex.eagle resources:
- Find
.dllfiles matching plugin patterns (default:*.dll). - For each assembly:
- Check if disabled via
.noPkgIndex. - Skip the Eagle core assembly itself.
- Verify it is a managed assembly (
RuntimeOps.IsManagedAssembly()). - If not
-notrusted: verify Authenticode signature. - If not
-noverified: verify StrongName signature.
- Check if disabled via
- Call
InvokeCallback(), which usesRuntimeOps.PreviewPluginResources()to extract and evaluate embeddedpkgIndex.eagleresources.
When PackageIndexFlags.Bundle is set, the discovery pipeline queries
the interpreter's BundleManager for mounted bundle databases
containing package index scripts. See sql.md for details
on the bundle system.
Each discovered index file is processed through InvokeCallback(),
which manages interpreter state during script evaluation:
- Set context: If what-if mode, swap the interpreter's
ContextClientDatato track discoveries without modifying state. - Begin pending: Call
interpreter.BeginPendingPackageIndexes()to prevent recursive package index evaluation. - Set temporary mode: If
-temporary, callinterpreter.SetTemporaryPackages()to mark any packages added during evaluation as temporary. - Execute callback: Call the
IndexCallbackdelegate, which:- Sets the
$dirvariable to the directory containing the index file. - Sets the
$tagvariable to the extracted tag (for tagged indexes). - Evaluates the index script.
- Restores
$dirand$tagto their previous values.
- Sets the
- Clean up: Restore all interpreter state in
finallyblocks.
Tagged package indexes are files named pkgIndex_XXXXXXXXXXXXXXXX.eagle
where XXXXXXXXXXXXXXXX is exactly 16 hexadecimal digits. They allow
multiple index files to coexist in a single directory, each identified
by a unique tag.
The tag is extracted from the filename using a compiled regex:
^pkgIndex_([0-9a-f]{16})\.eagle$The regex is case-insensitive and captures the 16-hex-digit tag as group 1. Tags that don't match this exact pattern are ignored.
When a tagged index script is evaluated, the $tag variable is set to
the extracted tag value. The script can use this to make decisions:
# In pkgIndex_00000000deadbeef.eagle
package ifneeded mypackage-$tag 1.0 \
[list source [file join $dir mypackage_$tag.eagle]]The $tag variable is automatically set before evaluation and restored
(or unset) after evaluation. For non-tagged (primary) index files,
$tag is not set.
- Variant packages: Multiple builds or configurations of the same package in one directory, distinguished by tag.
- Plugin identification: Tags derived from assembly public key tokens (which are also 16 hex digits), linking index files to specific signed assemblies.
- Version-specific indexes: Different index files for different deployment contexts.
Tagged indexes are only discovered when the PackageIndexFlags.Tagged
flag is set. This can be controlled via:
package scan -tagged— enable tagged index discovery.package scan -notagged— disable tagged index discovery.- The
PackageIndexFlags.AutoPathcomposite flag includesTaggedby default, so auto-path rescans always discover tagged indexes.
During FindPlugin and IsDirectory checks, assemblies can be
verified for code signing:
- Authenticode verification (
RuntimeOps.IsFileTrusted()): Checks that the assembly has a valid Authenticode signature from a trusted certificate authority. - StrongName verification (
RuntimeOps.IsStrongNameVerified()): Checks that the assembly's strong name signature is valid and the assembly has not been tampered with.
Both checks can be skipped individually:
package scan -notrusted— Skip Authenticode verification.package scan -noverified— Skip StrongName verification.
The Locked and Rejected flags in PackageFlags protect packages
from modification:
| Flags | [package ifneeded] behavior |
|---|---|
| (none) | Normal: registers or updates the package script |
Locked |
Silent no-op: returns success without modifying the registration |
Locked | Rejected |
Error: returns "rejected: package <name> is locked" |
These flags prevent untrusted code from overriding critical package registrations.
Plugin-based packages can include public key token verification in
their [load] commands. The token (16 hex digits) is extracted from
the assembly and embedded in the generated [package ifneeded] script:
package ifneeded MyPlugin 1.0 \
[list load -publickeytoken "00000000deadbeef" MyPlugin.dll]In safe interpreters:
[package info]scrubs file paths viaPathOps.ScrubPath()forindexFileNameandprovideFileName.- The
ifNeededdictionary is hidden entirely. - Certain sub-commands may be disallowed via
PolicyOps.DisallowedPackageSubCommandNames.
Package indexing can be disabled for individual files or entire directory trees using marker files:
- Disable a specific index file: Create a sibling file named
<fileName>.noPkgIndex(e.g.,pkgIndex.eagle.noPkgIndex). - Disable an entire directory: Create a file or directory named
.noPkgIndexwithin the directory.
The IsDisabled() method checks recursively up the directory tree, so
a .noPkgIndex in a parent directory disables all descendants.
| Flag | Value | Description |
|---|---|---|
System |
0x2 | System package (do not modify) |
Loading |
0x4 | Currently being loaded via [package require] |
Static |
0x8 | Provided statically |
Core |
0x10 | Included with the Eagle runtime |
Plugin |
0x20 | Provided by a loaded plugin |
Library |
0x40 | Part of the script library |
Interactive |
0x80 | From the interactive shell |
Automatic |
0x100 | Added automatically |
Locked |
0x200 | Cannot be replaced via [package ifneeded] |
Rejected |
0x400 | With Locked, generates error on replacement |
Temporary |
0x800 | Added via core script file evaluation |
| Flag | Value | Description |
|---|---|---|
NoUpdate |
0x1000 | Skip updating flags on provide |
NoProvide |
0x2000 | [package provide] does nothing |
AlwaysSatisfy |
0x4000 | [package vsatisfies] always returns true |
KeepExisting |
0x8000 | [package ifneeded] preserves existing info |
FailExisting |
0x10000 | [package ifneeded] fails if info exists |
NoAttributes |
0x20000 | Skip querying managed type flags |
AutoScan |
0x1000000 | Enable auto-scan on [package require] failure |
| Flag | Value | Description |
|---|---|---|
NoAlias |
0x100000 | Disable alias resolution for this package |
Overwrite |
0x200000 | Overwrite existing alias entry |
Disabled |
0x400000 | Alias exists but is skipped during resolution |
Exact |
0x800000 | Require exact version match |
| Mask | Composition | Purpose |
|---|---|---|
SecurityPackageMask |
NoProvide | AlwaysSatisfy |
Security package initialization |
InstanceMask |
All instance flags | Identify package state flags |
ActionMask |
All action flags | Identify behavior modifier flags |
AliasMask |
Disabled | Exact |
Identify alias-related flags |
| Flag | Value | Description |
|---|---|---|
PreferFileSystem |
0x4 | Search filesystem before host |
PreferHost |
0x8 | Search host before filesystem |
Host |
0x10 | Search interpreter host resources |
Bundle |
0x20 | Search bundle databases |
Plugin |
0x40 | Search plugin assemblies |
Normal |
0x80 | Search external filesystem |
NoNormal |
0x100 | Forbid filesystem search |
| Flag | Value | Description |
|---|---|---|
Primary |
0x10000000 | Include primary pkgIndex.eagle files |
Tagged |
0x20000000 | Include tagged pkgIndex_XXXX.eagle files |
| Flag | Value | Description |
|---|---|---|
Recursive |
0x200 | Search subdirectories |
Refresh |
0x400 | Force re-discovery and re-evaluation |
Resolve |
0x800 | Resolve fully qualified names |
Temporary |
0x8000000 | Mark discovered packages as temporary |
WhatIf |
0x4000000 | Preview without modifying state |
Safe |
0x10000 | Evaluate index scripts in safe mode |
| Flag | Value | Description |
|---|---|---|
NoTrusted |
0x200000 | Skip Authenticode verification |
NoVerified |
0x400000 | Skip StrongName verification |
| Flag | Value | Description |
|---|---|---|
Trace |
0x1000 | Enable operation tracing |
Verbose |
0x2000 | Enable verbose output |
Dump |
0x40000000 | Dump all indexes at end |
NoFileError |
0x80000 | Don't fail on GetFiles exceptions |
The [package require] fallback chain is Eagle's most important
extension to Tcl's package loading:
Calls interpreter.RequirePackage(name, version, exact). This checks
if the package is already provided or if an ifNeeded script is
registered.
Alias resolution happens first: MaybeUsePackageAliases() resolves
any alias chain (with circular reference detection) before the actual
require.
If stage 1 fails and auto-scan is enabled (via -autoscan true or the
PackageFlags.AutoScan flag):
- Calls
Interpreter.PkgAutoScan(). - This generates and evaluates a
[package scan]command with the interpreter's auto-path directories. - Retries
RequirePackage().
If stage 2 fails and InterpreterFlags.NoPackageFallback is not set:
- Retrieves
interpreter.PackageFallback(aPackageCallbackdelegate). - Calls the delegate with the interpreter, package name, version, flags, and exact flag.
- The delegate is responsible for making the package available (e.g., downloading it, extracting it, registering it).
- If the delegate returns
Ok, retriesRequirePackage().
The delegate signature:
ReturnCode PackageCallback(
Interpreter interpreter,
string name,
Version version,
string text,
PackageFlags flags,
bool exact,
ref Result result
);If stage 3 fails and InterpreterFlags.NoPackageUnknown is not set:
- Retrieves
interpreter.PackageUnknown(a script string). - Constructs a command via
ScriptOps.GetPackageUnknownScript(). - Evaluates the script.
- Retries
RequirePackage().
This is equivalent to Tcl's [package unknown] handler.
If all stages fail, errors from each stage are collected into a
ResultList and returned as a combined error message.
The PackageFallback delegate is a per-interpreter callback that
provides a programmatic hook for package resolution. It is stored as a
thread-safe property on the interpreter:
// C# code to install a package fallback
interpreter.PackageFallback = delegate(
Interpreter interp, string name, Version version,
string text, PackageFlags flags, bool exact,
ref Result result)
{
// Download the package from a repository
if (DownloadPackage(name, version, out string path))
{
// Register the package
interp.PkgIfNeeded(name, version,
$"source {path}", null, PackageFlags.None,
ref result);
return ReturnCode.Ok;
}
result = $"package {name} not found in repository";
return ReturnCode.Error;
};The corresponding IPackageCallback interface allows the same
functionality via interface implementation rather than delegates:
public interface IPackageCallback
{
ReturnCode PackageFallback(
Interpreter interpreter, string name,
Version version, string text,
PackageFlags flags, bool exact,
ref Result result);
}The PackageType enum classifies packages by their origin:
| Type | Value | Description |
|---|---|---|
None |
0x0 | Unspecified |
Invalid |
0x1 | Invalid package type |
Loader |
0x2 | Plugin loader package |
Library |
0x4 | Script library package (Eagle1.0) |
Test |
0x8 | Test suite package (Test1.0) |
Kit |
0x10 | Kit packages |
Host |
0x20 | Host-defined package |
Bundle |
0x40 | Bundle-defined package |
Automatic |
0x80 | Auto-determine type |
Default |
0x100 | Internal use only |
Each type maps to a specific index file location:
Loader→lib/Loader1.0/pkgIndex.eagleLibrary→lib/Eagle1.0/pkgIndex.eagleTest→lib/Test1.0/pkgIndex.eagleKit→lib/Kit1.0/pkgIndex.eagleHost,Bundle,None→pkgIndex.eagle(generic)
# Load a package (with auto-scan fallback)
package require Eagle.Library
# Require exact version
package require -exact MyPackage 2.0
# Check if loaded without loading
if {[catch {package present MyPackage}]} {
puts "MyPackage not loaded"
}# In mypackage.eagle
package provide mypackage 1.0
namespace eval ::mypackage {
proc hello {} { return "Hello from mypackage" }
namespace export hello
}# In pkgIndex.eagle (same directory)
package ifneeded mypackage 1.0 \
[list source [file join $dir mypackage.eagle]]# In pkgIndex_00000000deadbeef.eagle
# The $tag variable is "00000000deadbeef"
package ifneeded mypackage-$tag 1.0 \
[list source [file join $dir mypackage_$tag.eagle]]# Create an alias
package alias json-parser json 2.0
# Now this loads json 2.0:
package require json-parser
# List all aliases
package aliases
# Disable an alias temporarily
package alias -disabled json-parser json 2.0# Full security scan
package scan -host -normal -plugin -primary -tagged \
-recursive -- /usr/local/lib/eagle
# Skip security verification (development)
package scan -notrusted -noverified -normal -primary \
-recursive -- /tmp/dev-packages
# Preview what would be found
package scan -whatif -normal -primary -tagged \
-recursive -- /usr/local/lib/eagle# Register and lock a package
package ifneeded critical-pkg 1.0 \
[list source [file join $dir critical.eagle]] \
{Core, Locked, Rejected}
# This now raises an error:
catch {
package ifneeded critical-pkg 1.0 {evil script}
} err
# err = "rejected: package critical-pkg is locked"# Adding to auto_path triggers automatic package discovery
lappend auto_path /new/package/directory
# The package is now available without explicit scanning:
package require newly-discovered-package# Disable indexing for a specific directory
# Create: /packages/untested/.noPkgIndex
# Disable a specific index file
# Create: /packages/stable/pkgIndex.eagle.noPkgIndex
# The disable marker is checked recursively up the tree# Load a package
package require mypackage 1.0
# Withdraw it (unload, but keep registration)
package withdraw mypackage 1.0
# Load it again (re-evaluates ifneeded script)
package require mypackage 1.0
# Permanently remove it
package forget mypackage// Install a fallback that downloads packages on demand
interpreter.PackageFallback = delegate(
Interpreter interp, string name, Version version,
string text, PackageFlags flags, bool exact,
ref Result result)
{
string url = $"https://packages.example.com/{name}/{version}";
string dir = Path.Combine(tempDir, name);
if (DownloadAndExtract(url, dir))
{
// Scan the downloaded directory
interp.EvaluateScript(
$"package scan -normal -primary -- {dir}");
return ReturnCode.Ok;
}
result = $"failed to download package {name}";
return ReturnCode.Error;
};| Feature | Tcl | Eagle |
|---|---|---|
| Index file name | pkgIndex.tcl |
pkgIndex.eagle |
| Tagged indexes | Not available | pkgIndex_XXXXXXXXXXXXXXXX.eagle with $tag variable |
| Index sources | Filesystem only | Host, filesystem, plugin assemblies, bundle databases |
| Discovery order | Fixed | Configurable (PreferFileSystem/PreferHost) |
| Auto-path rescan | Manual | Automatic via variable trace callback |
| Package aliases | Not available | [package alias] with circular reference detection |
| Locked packages | Not available | Locked / Locked+Rejected flags |
| Signature verification | Not available | Authenticode + StrongName on plugin assemblies |
.noPkgIndex disable |
Not available | Recursive disable markers |
[package absent] |
Not available | Pre-condition: verify not loaded |
[package present] |
Not available | Post-condition: verify loaded |
[package withdraw] |
Not available | Unload without removing registration |
[package info] |
Not available | Detailed metadata query |
[package scan] options |
Basic | 30+ options with what-if mode |
[package ifneeded] flags |
Not available | PackageFlags parameter |
| Package fallback delegate | Not available | Programmatic PackageCallback hook |
[package indexes] |
Not available | List discovered index files |
[package pending] |
Not available | Loading cycle detection |
[package reset] |
Not available | Clear all index information |
| What-if scanning | Not available | Preview discovery without state changes |
-
CommandFlags.Unsafe— The command is restricted in safe interpreters. TheDisallowedSubCommandspolicy controls which sub-commands are available. -
Safe mode scrubbing — In safe interpreters,
[package info]scrubs file paths and hides theifNeededscript dictionary to prevent information disclosure. -
Authenticode and StrongName — Plugin assemblies can be verified during
[package scan]. Use-notrustedand-noverifiedto skip these checks during development, but keep them enabled in production. -
Locked packages — Use
Locked | Rejectedto protect critical packages from being overridden by untrusted code. -
.noPkgIndexmarkers — Can be used as a security control to prevent specific directories from being indexed. -
Auto-path in safe interpreters — The
AutoPathTraceCallbackvalidates that all directories inauto_pathare under the interpreter's base path, preventing sandbox escapes via path manipulation. -
SecurityPackageMask(NoProvide | AlwaysSatisfy) — Used during security package initialization to prevent version conflicts and ensure security packages load correctly.
| Related command | Relationship |
|---|---|
[source] |
Evaluates package scripts discovered via [package ifneeded] |
[load] |
Loads .NET plugin assemblies registered by package indexes; see load.md |
[interp] |
Safe interpreter policies control package sub-command access; see interp.md |
[library] |
Native library loading; separate from package system; see library.md |
[sql] |
Script bundle databases can contain package indexes; see sql.md |
[info] |
[info loaded] shows loaded packages from a different angle |
[uri] |
Package toolset uses [uri] for downloading packages; see uri.md |
[tcl] |
Tcl packages can be used via [tcl eval]; see tcl.md |
- Source code:
Eagle/Library/Commands/Package.cs—[package]command (23 sub-commands) - Source code:
Eagle/Library/Components/Private/PackageOps.cs— package operations (4,767 lines) - Source code:
Eagle/Library/Components/Public/Interpreter.cs— package storage, alias resolution, auto-path - Source code:
Eagle/Library/Components/Public/PackageData.cs— package metadata - Source code:
Eagle/Library/Components/Private/PackageContextClientData.cs— index evaluation state - Source code:
Eagle/Library/Components/Private/GlobalState.cs— auto-path construction and caching - Source code:
Eagle/Library/Components/Public/Enumerations.cs— PackageFlags, PackageIndexFlags, PackageType - Source code:
Eagle/Library/Containers/Private/PackageAliasDictionary.cs— alias storage - Command reference:
core_language.md—[package]syntax and options - Examples:
core_examples.md—[package]examples - Script library:
core_script_library.md— Package Toolset (pkgt.eagle) procedures - Related:
load.md— plugin loading (often triggered by package indexes) - Related:
sql.md— script bundle databases (package index source) - Tcl reference: Tcl
[package]manual page