mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-26 21:36:31 +02:00
feat(extensions): --ext PATH (short -e) for ad-hoc loading
Loads an extension from any directory for one zot session without needing to copy / install it under $ZOT_HOME. Repeatable. Resolved to absolute before spawn so paths like "." survive a later cwd change. Loaded BEFORE the installed-extension scan so explicit paths win on name conflicts, letting you shadow an installed copy with a work-in-progress version. zot --ext ./my-extension # one extension zot -e ./a -e ./b # multiple zot --ext . # the cwd is itself an extension Manager.LoadExplicit is the public entry point. Spawns happen in parallel like the regular Discover path, with per-path errors returned so a typo in one --ext doesn't break the others. Wired into both interactive (cli.go) and rpc (rpc.go) modes. Help text + docs/extensions.md updated. Verified end-to-end: disabling the installed scratchpad, running `zot rpc --ext .` from its directory, and asking the model to list its tools shows read_notes available again.
This commit is contained in:
parent
83b64e2562
commit
7e94b0776b
5 changed files with 79 additions and 0 deletions
|
|
@ -333,6 +333,21 @@ zot ext logs <name> [-f] cat / tail the extension's stderr
|
|||
shallow clone. Both validate that the destination contains an
|
||||
`extension.json` and roll back if not.
|
||||
|
||||
## Loading an extension for one run
|
||||
|
||||
For iteration on a working copy, skip the install + reload cycle
|
||||
and load straight from disk for one zot session:
|
||||
|
||||
```
|
||||
zot --ext ./my-extension # short form: -e ./my-extension
|
||||
zot --ext ./a -e ./b # repeatable
|
||||
```
|
||||
|
||||
`--ext` paths take precedence over installed extensions of the same
|
||||
name, so you can shadow an installed copy with a work-in-progress
|
||||
version without uninstalling first. Nothing is copied or persisted;
|
||||
the extension dies with zot like any other subprocess.
|
||||
|
||||
## SDKs
|
||||
|
||||
Writing the wire protocol by hand is fine for one-off scripts, but
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@ type Args struct {
|
|||
Tools []string
|
||||
MaxSteps int
|
||||
|
||||
// Exts is a list of directory paths the user passed via --ext.
|
||||
// Each must contain an extension.json. Loaded for one session
|
||||
// only; never persisted. Take precedence over installed exts of
|
||||
// the same name.
|
||||
Exts []string
|
||||
|
||||
ListModels bool
|
||||
Help bool
|
||||
Version bool
|
||||
|
|
@ -120,6 +126,15 @@ func ParseArgs(in []string) (Args, error) {
|
|||
return a, err
|
||||
}
|
||||
a.AppendSystemPrompt = append(a.AppendSystemPrompt, v)
|
||||
case "--ext", "-e":
|
||||
v, err := want(&i, arg)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
// Repeatable; each value is a directory containing an
|
||||
// extension.json. Resolved to absolute later so paths like
|
||||
// "." survive a later cwd change.
|
||||
a.Exts = append(a.Exts, v)
|
||||
case "--reasoning":
|
||||
v, err := want(&i, arg)
|
||||
if err != nil {
|
||||
|
|
@ -219,6 +234,9 @@ flags:
|
|||
--cwd PATH treat PATH as the working directory
|
||||
--no-tools disable all tools
|
||||
--tools csv only enable the listed tools
|
||||
-e, --ext PATH load an extension from PATH for this run
|
||||
(repeatable; takes precedence over
|
||||
installed extensions of the same name)
|
||||
|
||||
--max-steps N agent loop iteration cap (default 50)
|
||||
--list-models print known models and exit
|
||||
|
|
|
|||
|
|
@ -254,6 +254,11 @@ func runInteractive(ctx context.Context, args Args, version string) error {
|
|||
var iv *modes.Interactive
|
||||
extHooks := &interactiveExtHooks{ivPtr: &iv}
|
||||
extMgr := extensions.New(ZotHome(), r.CWD, version, r.Provider, r.Model, extHooks)
|
||||
// --ext paths first so they win against installed extensions of
|
||||
// the same name (loadOne's first-write-wins semantics).
|
||||
for _, e := range extMgr.LoadExplicit(ctx, args.Exts) {
|
||||
fmt.Fprintln(os.Stderr, "extension load:", e)
|
||||
}
|
||||
discoveryErrs := extMgr.Discover(ctx)
|
||||
for _, e := range discoveryErrs {
|
||||
fmt.Fprintln(os.Stderr, "extension load:", e)
|
||||
|
|
|
|||
|
|
@ -269,6 +269,44 @@ func (m *Manager) loadOne(ctx context.Context, dir string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// LoadExplicit loads each path as an ad-hoc extension. Used for
|
||||
// `zot --ext <path>` so extension authors can iterate on a working
|
||||
// copy without having to `zot ext install` after every change.
|
||||
//
|
||||
// Loaded BEFORE Discover so explicit paths win on name conflicts
|
||||
// against installed extensions. Spawns happen in parallel like the
|
||||
// regular discovery path; errors are returned per path.
|
||||
func (m *Manager) LoadExplicit(ctx context.Context, paths []string) []error {
|
||||
if len(paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errCh := make(chan error, len(paths))
|
||||
for _, p := range paths {
|
||||
abs, err := filepath.Abs(p)
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("%s: %w", p, err)
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(extDir string) {
|
||||
defer wg.Done()
|
||||
if err := m.loadOne(ctx, extDir); err != nil {
|
||||
errCh <- fmt.Errorf("%s: %w", extDir, err)
|
||||
}
|
||||
}(abs)
|
||||
}
|
||||
wg.Wait()
|
||||
close(errCh)
|
||||
|
||||
var errs []error
|
||||
for e := range errCh {
|
||||
errs = append(errs, e)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// WaitForReady blocks until every loaded extension has signalled
|
||||
// ReadyFromExt, or the grace period expires for the slowest one.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ func runRPCMode(ctx context.Context, args Args, version string) error {
|
|||
// emit RPC events instead of TUI lines so any consumer can react.
|
||||
extHooks := &rpcExtHooks{}
|
||||
extMgr := extensions.New(ZotHome(), r.CWD, version, r.Provider, r.Model, extHooks)
|
||||
for _, e := range extMgr.LoadExplicit(ctx, args.Exts) {
|
||||
fmt.Fprintln(os.Stderr, "extension load:", e)
|
||||
}
|
||||
discoveryErrs := extMgr.Discover(ctx)
|
||||
for _, e := range discoveryErrs {
|
||||
fmt.Fprintln(os.Stderr, "extension load:", e)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue