API functions
With the Tatin API you can write DevOps scripts in APL.
The API functions are similar to the user-commands, but not identical. Not all have equivalent user commands.
BuildPackage InitPackageConfig CheckForLaterVersion InstallPackages ClearCache ListDeprecated CopyRegistry ListCache CreateAPIfromCFG ListLicenses CreateBuildParms ListPackages CreateCopyRegistryParms ListRegistries CreateReInstallParms ListTags DeletePackages ListVersions DeprecatePackage LoadDependencies FindDependencies LoadPackages GetDeletePolicy Ping GetDependencyTree PublishPackage GetNoCachingFlag ReInstallDependencies GetPathToPackageCache ReadPackageConfigFile GetUserHomeFolder UnInstallPackages InitialisePackage Version
Unlike user commands, API function names are case-sensitive.
The Tatin API is in ⎕SE.Tatin
.
Call an API function like this:
⎕SE.Tatin.BuildPackage parms
API code cache
The Tatin code package is loaded into ⎕SE._Tatin
, but the API is exposed via ⎕SE.Tatin
.
Do not call functions in ⎕SE._Tatin
.
Build package
zipFilename←BuildPackage parms
Zips all files required for a package into a file at parms.targetPath
and returns its name.
Parameter space parms
is typically created with CreateBuildParms
and specifies:
dependencyFolder
- Path to folder with packages the project depends on. Default is
''
. -
Tatin searches the project for dependencies in (in order of precedence)
- the folder specified in
dependencyFolder
cider.config
(for projects managed by Cider)packages/apl-dependencies.txt
apl-dependencies.txt
- the folder specified in
projectPath
- Path to folder from which to create the package. (Required.)
projectspace
-
Namespace that is to contain the package contents.
Until version 0.118.0 this was known as
tatinVars
Now, Tatin signals an error if it finds a
tatinVars
parameter. targetPath
- Path to folder in which to write the ZIP file. Default is
''
: useprojectPath
. version
-
String. One of:
- A rule to modify the version number in the package config file:
'+0.0.1'
bumps the patch number'+0.1.0'
bumps the minor number and resets the patch number'+1.0.0'
bumps the major number and resets both the patch number and the minor number
- An empty character vector
- A string that replaces
version
in the package config file
See Glossary for details.
- A rule to modify the version number in the package config file:
Check for later version
r←{flags} CheckForLaterVersion path
Scans all known registries (with priority >0) for later versions of the principal packages installed in path
and returns a matrix with columns:
1 - Original package ID
2 - Latest package ID
3 - Original URL
4 - Flag: whether later version is available
5 - URL of latest version; empty if unchanged
By default, only principal packages and minor and patch numbers are checked.
Change this with optional left argument flags
: the sum of the following. (Defaults to 0: do neither.)
1 - List later major versions
2 - Check package dependencies as well
Clear cache
(rc report)←ClearCache url
Clears the cache and returns FIXME.
If url
is
- empty all subdirectories but
temp\
are removed - not empty, only the given domain is removed from the cache
The cache is the folder GetPathToPackageCache
points to.
Copy registry
list←CopyRegistry parms
Copies packages from a managed Tatin registry to a local folder and returns as a list of strings the packages copied
Pause or stop any running local server while CopyRegistry
is running.
Argument parms
is typically created with CreateCopyRegistryParms, and then amended.
Required parameters are marked; others are optional.
dry
-
List all packages the user command would copy without actually doing it.
If set you may omit both URL (defaults to
[tatin]
) andpath
. force
- Copy already available packages again. By default packages already saved in the target folder are not requested again.
group=
-
Restrict the packages to be copied to a particular group.
Dependencies will be copied as well, no matter which group they belong to.
latest
-
Copy only the latest version of each major version of each non-deprecated package.
By default all packages are copied, even deprecated ones.
list=
-
Packages to copy, as one of
-
A comma-separated list of package names
-
A file of package names, one per row, specified with the
file://
protocol -
A variable name, prefixed by either
#.
or⎕SE.
Specify packages consistently: either
<group>-<pkgName>
or<group>-<pkgName>-<major>
-
noDeps
- Flag. If set, ignore dependencies. (Useful only for test cases.)
path
- A local folder in which to write the packages. Required unless
dry
set. url
- URL of the Tatin registry from which to copy. Required unless
dry
set. verbose=
-
By default, prints the names of all packages copied to
⎕SE
.1 – print a detailed report for all packages (
Fetched
indicates success)2 – print a message for every package as the list is processed
CopyRegistry
was introduced with version 0.110.0
Tatin registries running on earlier versions will not respond to it.
Create API from CFG
{noOf}←{names} CreateAPIfromCFG (source cfg)
Where
source | reference | namespace containing the package objects |
cfg | What? | package configuration |
names | strings | (optional) names of objects in the namespace to be exposed in the API |
then Tatin creates an API space as a child of source
(with cover functions for the package’s public interface) and returns as a shy result the number of objects exposed.
If names
is absent, the function looks for a constant source.Public
.
Not for use with a scripted namespace.
See Public Interface for more.
Create build parms
parms←CreateBuildParms projectPath
Creates a parameter space for BuildPackage
with parameters:
dependencyFolder
projectPath
targetPath
tatinVars
version
Create CopyRegistry parms
parms←CreateCopyRegistryParms y
Where y
is either an empty vector or a parameter space,
returns a parameter space for CopyRegistry
.
Create ReInstall parms
parms←CreateReInstallParms
Returns a parameter space with default parameters for the (optional) left argument of ReInstallDependencies
, in particular, noBetas
, update
and dry
.
Delete packages
(statusCode errMsg)←DeletePackages (regID packageIDs)
Deletes packages
from Tatin registry regID
and returns HTTP statusCode
and errMsg
, empty if successful.
All packages specified are removed from the same registry.
Specify regID
as either
- a URL
- a Registry alias
- a Registry ID
Specify packages
as a string or list of strings, with each package and version precisely identified:
<group-name>-<package-name>-<precise_version_number>
A Tatin registry will delete packages only if its configured Delete policy permits.
The principal Tatin server operates a None
policy, meaning that you cannot delete anything from it.
Why can’t I delete a package from the Tatin server?
The main design objective is to ensure a build that includes packages from the principal Tatin registries can always be reproduced in precisely the same way.
If deleting a package is allowed – even when it is a beta version – then this cannot be guaranteed.
If you happen to publish a package and realize seconds later that you made a formidable mistake? Increase the patch number, fix the problem and publish a new version: that’s the only way.
Deprecate package
msg←DeprecatePackage (registry comment package)
Where
registry
is a URL or aliascomment
is a string, such asSee package xyz instead
package
identifies a package with group name, package name, and (optionally) major version number
marks the package on the registry as deprecated.
If no major version number is provided, then all major versions of the package are deprecated.
As the registry is defined, no scanning is done.
Find dependencies
r←{depth} FindDependencies (target pkgList [verbose])
Where
target | string | one of:
|
pkgList | string | a list of package names separated by commas |
verbose | flag | optional: whether to include more detail in the result; default 0 |
depth | integer | optional: limit on recursive search |
then Tatin recursively scans the registry for the packages in pkgList
and returns
a fully qualified list of all matches.
By default only the folder containing a file apl-dependencies.txt
with at least one of the defined packages is returned.
If verbose
is set the actual package folders are returned instead of the hosting folder/s, revealing the precise version/s installed.
Specify packages in pkgList
partially or in full. Name is required but group and version can be omitted. You can specify a major version number, but minor and patch numbers are ignored if specified.
The function scans folder
recursively for a file apl-dependencies.txt
. Folders containing such a file are searched for the packages listed in pkgList
.
The search is not case-sensitive.
Useful for discovering where packages are used.
Set optional left argument depth
to limit the number of levels searched.
A server sets this to 1 because it knows it only needs to search the child folders of the Registry folder, which greatly reduces the search time.
You can use it similarly if you know exactly what is stored where.
Get delete policy
r←GetDeletePolicy server
Where server
is the alias or URL of a Tatin server, returns its delete policy:
None | a published package cannot be deleted |
Any | any package can be deleted |
JustBetas | only beta versions can be deleted |
⎕SE.Tatin.GetDeletePolicy 'https://tatin.dev'
None
⎕SE.Tatin.GetDeletePolicy '[tatin]'
None
Get dependency tree
tree←GetDependencyTree pkg
Where pkg
is one of:
- an HTTP request
- a folder containing a package, e.g.
file://C:\Temp\{group}-{name}-{major.minor.patch}\
- a path to a package in a local registry, e.g.
C:\MyReg\{packageID}
- a package ID (Tatin will search the registries defined in your config file)
Tatin returns a dependency tree as a matrix:
col | contains |
---|---|
1 | flag: 1 – principal package; 0 – dependency |
2 | ID of package that required the dependency |
3 | full package ID |
4 | full URL: either a local path (without protocol) or http(s)://... |
This function requires the version number to be fully specified.
The function accepts an optional left argument for INTERNAL use only.
Get NoCaching flag
flag←GetNoCachingFlag registry
Where registry
is a URI or alias, Tatin searches MyUserSettings
for it.
If found the value of the noCaching
property is returned, otherwise 0.
⎕SE.Tatin.GetNoCachingFlag '[tatin]' ⍝ actual
0
⎕SE.Tatin.GetNoCachingFlag '[fubar]' ⍝ default
0
Get path to package cache
path←GetPathToPackageCache
Returns MyUserSettings.path2cache
if not empty, otherwise the standard path for caching, according to the operating system.
⎕SE.Tatin.GetPathToPackageCache ⍝ macOS
/Applications/Dyalog/tatin-package-cache
Get user home folder
path←{aplVersion} GetUserHomeFolder str
Returns the standard path for user-specific data, with string str
appended.
C:\Users\%USERPROFILE%\AppData\Roaming\Tatin
/home/{⎕AN}/Tatin
/Users/{⎕AN}/Tatin
⎕SE.Tatin.GetUserHomeFolder 'foo' ⍝ macOS
/Users/sjt/Library/Application Support/Tatin/foo
Optional string aplVersion
is used by test cases to simulate different versions of APL.
Initialise package
config←{configParms} InitialisePackage folder
Where folder
is a path to a folder, and (optional) configParms
is a parameter space, Tatin creates the folder if necessary and initializes it with a config file.
If the folder exists and already contains a config file, Tatin signals an error.
Optional left argument configParms
is typically created with InitPackageConfig
but can be made from scratch:
parms←⎕NS''
parms.(group name version)←'aplteam' 'Foo' '1.0.0'
cfg←parms ⎕SE.Tatin.InitialisePackage 'path/to/folder'
InitPackage config
cfg←{sourcePath} InitPackageConfig parms
Where
parms
- is a an empty vector or a parameter space
sourcePath
- (optional) is a path to the package source folder
Tatin returns a parameter space for InitialisePackage
.
The source
parameter in the result is set by (in order of precedence)
sourcePath
parms.source
- global default in
MyUserSettings
showParms ⎕SE.Tatin.InitPackageConfig ⍬
api :
assets :
description :
documentation :
files :
group :
io : 0
license :
lx :
maintainer :
minimumAplVersion : 18.0
ml : 0
name :
os_lin : 1
os_mac : 1
os_win : 1
project_url :
source :
tags :
userCommandScript :
version : 0.1.0
Install packages
r←{noBetas} InstallPackages (identifiers targetFolder)
Where
identifiers | string | comma-separated list of package identifiers |
noBetas | flag | whether to exclude beta versions; default 0 |
targetFolder | string | '[MyUCMDs]' , or '[MyUCMDs]{pkgname}' , or a path to a folder |
Tatin creates the target folder if necessary, installs the packages in it and returns (as a list of strings) the full names of the principal packages installed. The list length will be the number of packages specified as identifiers
.
A package identifier is case-insensitive and one of:
- an HTTP request for a package
- a ZIP file containing a package
- a folder containing a package, e.g.
file://C:\Temp\group-name-version\
- a path to a package in a registry, e.g.
[RegistryAlias]{group}-{name}-{major.minor.patch}
orC:\MyReg\{group}-{name}-{major.minor.patch}
- a package ID that Tatin will search for in the registries specified in your config file
If targetFolder
is '[MyUCMDs]'
, or '[MyUCMDs]{pkgname}'
(case independent) it is replaced by the actual path to the MyUCMDs/
folder followed by the name specified or, if none was specified, the name of the package.
To install the latest version, omit minor+patch or even major+minor+patch.
List cache
list←{fullpath} ListCache registry [principalFlag]
Where
fullpath | flag | optional: report full paths; default 0 |
principalFlag | flag | optional: default 0 |
registry | string | empty, or a registry domain name or alias |
returns the contents of the Tatin package cache as a nested list with an item for each domain represented in the cache.
Each result item is a pair:
- URL of the domain
- package names as a list of strings: if
fullpath
is set then the full paths instead
If the cache is empty the result is an empty list.
List deprecated
list←{all} ListDeprecated source
Where
all | flag | optional: include all versions; default 0 |
source | string | is one of
|
returns (as a 1-column matrix of strings) a list of deprecated packages.
Only the last published version of a major version number is included.
Set the all
flag to include all versions of any major version marked as deprecated.
⎕SE.Tatin.ListDeprecated '[tatin]'
┌────────────────┐
│aplteam-APLGit-0│
└────────────────┘
⍴⎕SE.Tatin.ListDeprecated 'https://tatin.dev'
1 1
List licenses
licences←{verbose} ListLicences registry
Where registry
is the URL or alias of a Tatin server and verbose
is a flag (default 0), returns a list of licences.
The result is a list of strings – if verbose
is set, a 2-column matrix of which the first column is licence names and the second their URLsThe result is a list of strings – if verbose
is set, a 2-column matrix of which the first column is licence names and the second their URLs.
⎕SE.Tatin.ListLicenses '[tatin]'
Unlicense CC0 0BSD EPL MIT BSL ISC Apache BSD-2 BSD-3
1 ⎕SE.Tatin.ListLicenses 'https://tatin.dev'
Unlicense https://en.wikipedia.org/wiki/Unlicense
CC0 https://en.wikipedia.org/wiki/Creative_Commons_license#Zero_/_public_domain
...
List packages
packages←{parms} ListPackages source
Where
parms | namespace | optional: parameter space |
source | string | one of
|
returns (as a matrix) a list of packages.
The result matrix lists all packages at the source except those where the last package of a major version has been deprecated.
(See ListDeprecated
).
The result matrix has 2–4 columns:
1 | package ID |
2 | if source is a folder, a '*' flags principal packagesif source is a registry, the number of major versions |
[3] | if parms.date is set, publication date |
[3|4] | if parms.projectUrl is set, the project URL |
Leaving aside any filters specified in parms
, if source
specifies a: | result lists: |
---|---|
registry | all packages, aggregated by major version |
package ID without a full version number | matching packages |
package name without a group name | matching packages |
Argument parms
is optional; if specified, it must contain at least the first three parameters below.
aggregate | flag | aggregate results by minor and patch numbers (default 1) |
group | string | list only packages from this group |
tags | string | (comma separated, case-insensitive) list only packages with these tags |
date | flag | append a result column with publication dates |
packageID | string | package identifier: see below |
project_url | flag | append a result column with the project URL |
since | int | string | date as e.g. 20220601 or '2022-06-01' or '20220601' : packages published earlier are ignored |
userCommand | flag | whether to list only packages that are user commands |
Parameter aggregate
combines with any package ID in source
to determine what packages get listed.
package ID | aggregate | column 1 | column 2 |
---|---|---|---|
(empty) | 1 | all packages | # of major versions |
(empty) | 0 | all packages | ⍬ |
{name} | 0 | packages that match name (might belong to different groups) |
# of major versions |
{group}-{name} | 0 | all versions of the package | # of major versions |
{group}-{name} | 1 | all major versions of that package | # of major versions |
{group}-{name}-{major} | – | all versions (minor and patch) | # of major versions |
{group}-{name}-{major}-{minor} | – | all patch versions of the package | ??? |
⍴r←⎕SE.Tatin.ListPackages '[tatin]'
58 2
3↑[1] r
abrudz-sort 1
aplteam-ADOC 1
aplteam-APLGit2 1
List registries
registries←ListRegistries type
Where type
is a flag or ⍬
, returns as a matrix all registries specified in your config file.
Result registries
has columns:
1 – Alias
2 – URL
3 – ID
4 – Port
5 – Priority
6 – No-caching flag
7 – Proxy
[8 – API key]
If type
is set the result has an eighth column containing the API key.
⎕SE.Tatin.ListRegistries ⍬
tatin https://tatin.dev/ 29fbeb21-c6a0-4691-b6b6-8a ...
tatin-test https://test.tatin.dev/ 2a282315-bfd6-4b15-8fe7-8c ...
The result of the API function and the user command differ.
If the registry does not respond, Tatin signals an error.
List tags
list←{parms} ListTags registry
Where registry
is the alias or URL of a registry, and parms
is a parameter space, returns as a 2-column matrix all tags in use there:
1 – tag name
2 – number of occurrences
If optional argument parms
contains a list of tags (comma-separated string) as parameter tags
, then the result lists only tags shared by packages that carry all the specified tags.
p←⎕NS ''
p.tags←'markdown'
p ⎕SE.Tatin.ListTags '[tatin]'
converter 15
help 8
markdown 23
List versions
mat←{dateFlag} ListVersions pkg
Where (optional) dateFlag
is a flag and pkg
identifies a package, returns a list of all versions of the package.
String pkg
is case-insensitive and can be
- a package name
- a group name and a package name
- a URL pointing to a registry together with a package name
- a registry alias and a package name
- a local path to a registry together with a package name
A package name can be just the name or the group and the name. Also, you can specify a major version number, or a major and a minor version number.
(Specifying a patch number makes no sense; if specified, it is ignored.)
Examples:
'example-versions'
'example-versions-1'
'example-versions-1.0'
'[tatin-test]versions'
'[tatin-test]example-versions'
'[tatin-test]example-versions-1'
'[tatin-test]example-versions-1.0'
'[tatin-test]example-versions-1.0.1' ⍝ same as previous
In the first three cases known registries with a priority above zero are scanned.
Result mat
has a column with full package names.
- If
pkg
does not specify a registry, the first column is registry URLs. - If
dateFlag
is set, a last column has publication dates.
1 ⎕SE.Tatin.ListVersions 'MarkAPL'
https://tatin.dev/ aplteam-MarkAPL-11.0.0 20210427.09
https://tatin.dev/ aplteam-MarkAPL-11.0.1 20210725.15
https://tatin.dev/ aplteam-MarkAPL-11.0.2 20211012.07
https://tatin.dev/ aplteam-MarkAPL-11.0.3 20220509.17
...
⍴ ⎕SE.Tatin.ListVersions '[tatin]MarkAPL'
15 1
Load dependencies
{refs}←{options} LoadDependencies folder [target]
Where
options | 2 flags | optional: default 0 |
folder | string | path to package source folder, or '[MyUCMDs]' |
target | reference | optional: target namespace |
Loads all packages into target
according to a build list in folder
and returns a list of references to the loaded packages. (Principal packages only, not dependencies.)
If unspecified the target
namespace defaults to #
, unless folder
is '[MyUCMDs]'
, when it defaults to ⎕SE
.
The flags in options
:
- Overwrite if the package is already loaded
- Make home relative. See details below.
User commands
Case-insensitive alias '[MyUCMDs]'
denotes the special folder MyUCMDs/
, whose location depends on the operating system.
So where a Tatin package has been installed as a user command (and perhaps bundled into the Dyalog runtime) you cannot use absolute paths for referring to assets.
In that case the paths must be relative to MyUCMDs/
.
This is what the second options
flag is for.
The flag affects the result of HOME
and GetFullPath2AssetsFolder
.
Rather than returning the full path, only the folder containing the packages, and its parent are returned, making it a relative path.
Where no name is specified after [MyUCMDs]
the subfolder is named after the package.
To install multiple user-command packages, specify no name after [MyUCMDs]
– otherwise Tatin signals an error.
Load packages
no←{noBetas} LoadPackages (identifiers targetSpace)
Where
noBetas | flag | optional: ignore beta versions; default 0 |
identifiers | string | comma-separated list of packages |
targetSpace | ref | fully-qualified namespace: target |
Tatin loads packages dynamically into the target space and returns the number of principal packages loaded.
Detail
Tatin actually loads the package into [#|⎕SE]._tatin.{packageName}
and puts a reference to it in targetSpace
.
Also loads any dependencies into [#|⎕SE]._tatin
but does not create references for them in targetSpace
.
In identifiers
specify a package as one of
- an HTTP request
- a ZIP file containing a package
- a folder containing a package, e.g.
'file://C:/Temp/group-name-version'
- a path to a package in a registry, e.g.
'[RegistryAlias]{packageID}'
or'C:\MyReg\{packageID}'
- a package ID
Where a package is specified as a package ID, Tatin searches the registries it knows with a priority above 0. The first hit wins.
If the target space already exists but is not an ordinary namespace, Tatin signals an error.
Ping
flag←Ping source
Where source
is
- a registry alias or URL,
flag
is whether the host is up and running - a folder,
flag
is whether it exists
⎕SE.Tatin.Ping '[tatin]'
1
⎕SE.Tatin.Ping 'https://tatin.dev'
1
⎕SE.Tatin.Ping '/Users/sjt/tmp/Foo'
1
Publish package
{fn}←{deps} PublishPackage (source registry)
Where
deps | parm space | optional: argument for BuildPackage |
source | string | folder from which to create the package |
registry | string | registry to which to publish the package |
Tatin
- Confirms no package has already been published to the registry under this name (case insensitive).
- If
source
is a folder, usesBuildPackage
to zip the package into a temp folder;deps
, if specified, is passed as the argument. - Moves the ZIP into the registry
- If the registry is local, updates its index
and returns
rc | HTTP return code (whether the registry is remote or not) |
emsg | error message: empty if rc is 200 |
zfn | zip file name: empty if source is a ZIP file, otherwise name of the ZIP file created |
Delete policy
The package is published no matter what the server’s delete policy is.
That differs from the user command, which asks you to confirm publication of a package that cannot then be deleted.
Reinstall dependencies
{refs}←{parms} ReInstallDependencies deps folder [reg]
Where
parms | parm space | optional: typically created by calling CreateReInstallParms |
deps | ??? | ??? |
folder | string | target folder: contains apl-dependencies.txt |
reg | string | optional: registry alias or URL |
Tatin, in the target folder,
- deletes file
apl-buildlist.json
and all directories - re-installs all files listed in
apl-dependencies.txt
(ignoring lines that start with a lamp⍝
)
and returns a list of references to the principal packages installed.
Packages originally installed from ZIP files are just re-installed from their ZIP files without further ado.
If reg
is omitted Tatin scans all known registries with a priority above 0
.
(Packages with different major version numbers are considered as different packages.)
Optional argument parms
can specify three flags.
All default to 0,
noBetas | Ignore beta versions |
update | Update to a later version if available |
dry | Report what the function would do but don’t do it |
Registry scans
For each dependency Tatin scans known registries, even if reg
was specified.
Tatin queries every known registry with a priority above 0, highest priority number first. First hit wins.
If you installed a package from a Tatin registry and later removed that registry from your user settings, or set its priority to 0, then ReInstallDependencies
will not scan it, despite knowing perfectly well where the package came from.
Read package config file
cfg←ReadPackageConfigFile path
Where path
is a path to a package Tatin returns its config file as a parameter namespace.
The path
argument may optionally include the config file name apl-package.json
.
path←'path/to/packages/aplteam-APLTreeUtils2-1.4.0'
q←⎕SE.Tatin.ReadPackageConfigFile path
showParms q
api : APLTreeUtils2
assets :
date : 20240325.14
description : General utilities required by most members of the APLTree library
documentation :
files :
group : aplteam
io : 1
license : MIT
lx :
maintainer : kai@aplteam.com
minimumAplVersion : 18.0
ml : 1
name : APLTreeUtils2
os_lin : 1
os_mac : 1
os_win : 1
project_url : https://github.com/aplteam/APLTreeUtils2
source : APLSource/APLTreeUtils2.aplc
tags : tools,utilities
uri : https://tatin.dev/
userCommandScript :
version : 1.4.0+78
Uninstall packages
(list emsg)←UnInstallPackage (packageID folder)
Where
packageID | string | a full package ID or an alias |
folder | string | either
|
Tatin attempts to un-install the package packageID
and any of its dependencies
(that are neither principal packages nor required by other packages)
and returns:
list | strings | Fully qualified names of all removed packages. (Might include aliases.) |
emsg | string | Error message, ideally empty. |
If the package was installed with an alias then packageID
must be its alias.
If packageID
matches more than one package, Tatin signals an error
If packageID
is empty, Tatin attempts to clean up: remove any packages that are neither principal packages nor required by other packages
Deleting parent folders
Removing the folders hosting the packages might fail for all sorts of reasons, even after successfully removing the package and any dependencies from both the dependency file and the build list.
Version
r←Version
Returns as strings Tatin’s name, version and date.
⎕SE.Tatin.Version
┌─────┬────────────┬──────────┐
│Tatin│0.112.1+1942│2024-08-16│
└─────┴────────────┴──────────┘