agentworks-cli 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/CHANGELOG.md +28 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/PKG-INFO +25 -25
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/README.md +24 -24
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/agents/manager.py +132 -60
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/catalog.py +3 -1
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/cli.py +160 -92
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/completions/bash.py +1 -1
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/completions/powershell.py +1 -1
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/completions/spec.py +8 -5
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/completions/zsh.py +2 -2
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/config.py +58 -10
- agentworks_cli-0.4.0/agentworks/errors.py +117 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/output.py +36 -53
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sample-config.toml +25 -2
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sessions/console.py +15 -7
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sessions/manager.py +159 -57
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sessions/multi_console.py +546 -73
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sources.py +3 -1
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/ssh.py +7 -5
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vm_hosts/manager.py +22 -5
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/backup.py +14 -5
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/initializer.py +12 -3
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/manager.py +130 -35
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/provisioners/azure.py +2 -1
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/provisioners/lima.py +12 -5
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/provisioners/proxmox_api.py +3 -1
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/workspaces/backends/vm.py +9 -4
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/workspaces/manager.py +199 -60
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/pyproject.toml +1 -1
- agentworks_cli-0.4.0/tests/test_agents.py +67 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_completions.py +41 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_config.py +48 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_consoles.py +411 -18
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/uv.lock +1 -1
- agentworks_cli-0.3.0/tests/test_agents.py +0 -30
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/.gitignore +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/.python-version +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/agents/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/agents/templates.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/catalog.toml +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/completions/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/completions/install.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/db.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/doctor.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/git_credentials/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/git_credentials/azdo.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/git_credentials/base.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/git_credentials/github.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/nerf-config.yaml +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/remote_exec.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sessions/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sessions/templates.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/sessions/tmux.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/ssh_config.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vm_hosts/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/base.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/bootstrap_script.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/cloud_init.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/provisioners/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/provisioners/proxmox.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/provisioners/wsl2.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/vms/templates.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/workspaces/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/workspaces/backends/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/workspaces/templates.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/agentworks/workspaces/tmuxinator.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/pypi-dist/.gitignore +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/__init__.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/conftest.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_authorized_keys.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_bootstrap_script.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_catalog.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_cloud_init.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_db.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_doctor.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_error_wrapper.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_exec_target.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_initializer.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_name_validation.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_nerf_plugin.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_proxmox_api.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_remote_exec.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_session_liveness.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_ssh_config.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_templates.py +0 -0
- {agentworks_cli-0.3.0 → agentworks_cli-0.4.0}/tests/test_tmuxinator.py +0 -0
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0](https://github.com/WayfarerLabs/agentworks/compare/v0.3.0...v0.4.0) (2026-06-01)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **console:** configurable layout and restore-session ([73bdf68](https://github.com/WayfarerLabs/agentworks/commit/73bdf681581825c8838398bc291839ddb8cbe82f))
|
|
9
|
+
* **console:** configurable layout and restore-session for accidental shell kills ([5d5ae48](https://github.com/WayfarerLabs/agentworks/commit/5d5ae487869ac31bb82bad21f1b9bc191e0aacae))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **console:** include console name in untagged-pane warnings and check set-option result ([a48adf8](https://github.com/WayfarerLabs/agentworks/commit/a48adf80dfb78bb6264918b55fef1072d5898b10))
|
|
15
|
+
* **console:** only mirror swap-pane into local map on success ([7f33652](https://github.com/WayfarerLabs/agentworks/commit/7f336525aa27ef4eedefd0d93c0c9a517e8c1f72))
|
|
16
|
+
* **console:** pass console name through _live_best_effort and refine restore-session error messages ([eec0b62](https://github.com/WayfarerLabs/agentworks/commit/eec0b62afb14cd946ccc9234ef19266f5340a6bd))
|
|
17
|
+
* **console:** restore-session raises on any partial split/tag failure ([97f72aa](https://github.com/WayfarerLabs/agentworks/commit/97f72aa32c65923ffe43ff844219b3e92ef0976d))
|
|
18
|
+
* **console:** shlex-quote cd-failure diagnostic and reword restore-session help ([c0fa16b](https://github.com/WayfarerLabs/agentworks/commit/c0fa16bda5c0de0348a998b27d9e4e5c8b711fb5))
|
|
19
|
+
* **console:** validate restore-session tag permutation, allowlist named_console, clean docstring and sample-config ([72d3815](https://github.com/WayfarerLabs/agentworks/commit/72d3815ea7ac6a84b13e8be175f9bc6e99a47d0d))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Performance Improvements
|
|
23
|
+
|
|
24
|
+
* **console:** single-pass tag validation, in-memory reorder, warn on missing pane id ([c8f8362](https://github.com/WayfarerLabs/agentworks/commit/c8f8362cb6f07c45744650f5ee579fc8da605696))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Documentation
|
|
28
|
+
|
|
29
|
+
* **console:** clarify reinit_workspace docstring on detection vs always-applied steps ([b7e548b](https://github.com/WayfarerLabs/agentworks/commit/b7e548bcf3396f3fa339551c0b93485209a20890))
|
|
30
|
+
|
|
3
31
|
## [0.3.0](https://github.com/WayfarerLabs/agentworks/compare/v0.2.1...v0.3.0) (2026-06-01)
|
|
4
32
|
|
|
5
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentworks-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: CLI for orchestrating workspace lifecycle across multiple compute targets
|
|
5
5
|
Project-URL: Homepage, https://github.com/WayfarerLabs/agentworks
|
|
6
6
|
Project-URL: Repository, https://github.com/WayfarerLabs/agentworks
|
|
@@ -380,7 +380,7 @@ Manage workspaces on VMs.
|
|
|
380
380
|
| `agentworks workspace list` | List workspaces |
|
|
381
381
|
| `agentworks workspace copy <source> <name>` | Copy a workspace to a new VM |
|
|
382
382
|
| `agentworks workspace rehome <name>` | Move workspace to a new path |
|
|
383
|
-
| `agentworks workspace
|
|
383
|
+
| `agentworks workspace reinit <name>` | Reinit workspace infrastructure |
|
|
384
384
|
| `agentworks workspace delete <name>` | Delete a workspace |
|
|
385
385
|
|
|
386
386
|
`workspace create <name>` takes the workspace name as a required positional. Optional flags: `--vm`,
|
|
@@ -400,19 +400,18 @@ during deletion. Pass `--yes` to skip the confirmation prompt.
|
|
|
400
400
|
|
|
401
401
|
Manage agents (isolated Linux users) on VMs. Agents are VM-scoped and access workspaces via grants.
|
|
402
402
|
|
|
403
|
-
| Command
|
|
404
|
-
|
|
|
405
|
-
| `agentworks agent create <name> [--vm]`
|
|
406
|
-
| `agentworks agent list [--vm <vm>]`
|
|
407
|
-
| `agentworks agent describe <name>`
|
|
408
|
-
| `agentworks agent reinit <name>`
|
|
409
|
-
| `agentworks agent workspace
|
|
410
|
-
| `agentworks agent workspace
|
|
411
|
-
| `agentworks agent workspace
|
|
412
|
-
| `agentworks agent workspace
|
|
413
|
-
| `agentworks agent workspace
|
|
414
|
-
| `agentworks agent
|
|
415
|
-
| `agentworks agent delete <name>` | Delete an agent |
|
|
403
|
+
| Command | Description |
|
|
404
|
+
| -------------------------------------------------- | ------------------------------ |
|
|
405
|
+
| `agentworks agent create <name> [--vm]` | Create an agent on a VM |
|
|
406
|
+
| `agentworks agent list [--vm <vm>]` | List agents |
|
|
407
|
+
| `agentworks agent describe <name>` | Show agent details and grants |
|
|
408
|
+
| `agentworks agent reinit <name>` | Re-run agent setup |
|
|
409
|
+
| `agentworks agent grant-workspace <name> <ws>...` | Grant workspace access |
|
|
410
|
+
| `agentworks agent grant-workspace <name> --all` | Grant access to all workspaces |
|
|
411
|
+
| `agentworks agent revoke-workspace <name> <ws>...` | Revoke workspace access |
|
|
412
|
+
| `agentworks agent revoke-workspace <name> --all` | Revoke all explicit grants |
|
|
413
|
+
| `agentworks agent shell <name> [--workspace <ws>]` | Shell into an agent |
|
|
414
|
+
| `agentworks agent delete <name>` | Delete an agent |
|
|
416
415
|
|
|
417
416
|
`agent create <name>` takes the agent name as a required positional. Optional flags: `--vm`,
|
|
418
417
|
`--template`, and `--grant-all-workspaces`.
|
|
@@ -564,8 +563,9 @@ aw-console-backend
|
|
|
564
563
|
|
|
565
564
|
The tmux session is built lazily on first `attach` (or rebuilt with `--recreate`). Adding or
|
|
566
565
|
removing sessions/shells while the console is attached updates tmux immediately; when offline, only
|
|
567
|
-
the DB is touched and changes appear on next attach. The
|
|
568
|
-
|
|
566
|
+
the DB is touched and changes appear on next attach. The mutation commands (`add-session`,
|
|
567
|
+
`remove-session`, `add-shell`) never auto-boot the VM; the explicit attach/repair commands
|
|
568
|
+
(`attach`, `restore-session`) do start a stopped VM, since their job is to bring live state up.
|
|
569
569
|
|
|
570
570
|
#### VM console (deprecated)
|
|
571
571
|
|
|
@@ -610,17 +610,17 @@ Template commands support `{{session_name}}` and `{{workspace_name}}` variable s
|
|
|
610
610
|
by `session restart` -- useful for tools like Claude Code where `--resume` picks up the previous
|
|
611
611
|
conversation. If omitted, the regular `command` is used.
|
|
612
612
|
|
|
613
|
-
###
|
|
613
|
+
### Catalog
|
|
614
614
|
|
|
615
615
|
Browse and inspect the built-in catalog of installable tools.
|
|
616
616
|
|
|
617
|
-
| Command
|
|
618
|
-
|
|
|
619
|
-
| `agentworks
|
|
620
|
-
| `agentworks
|
|
617
|
+
| Command | Description |
|
|
618
|
+
| ------------------------------------ | ---------------------------------- |
|
|
619
|
+
| `agentworks catalog list` | List all available catalog entries |
|
|
620
|
+
| `agentworks catalog describe <name>` | Show details of a catalog entry |
|
|
621
621
|
|
|
622
|
-
`
|
|
623
|
-
|
|
622
|
+
`catalog list` accepts `--type` (apt-source, apt-package, system-install-cmd, user-install-cmd) and
|
|
623
|
+
`--source` (built-in, custom) filters.
|
|
624
624
|
|
|
625
625
|
### Config
|
|
626
626
|
|
|
@@ -696,7 +696,7 @@ standalone via `claude plugin marketplace add`.
|
|
|
696
696
|
### Built-in Catalog
|
|
697
697
|
|
|
698
698
|
Agentworks ships a built-in catalog of common tools (apt sources, apt packages, system install
|
|
699
|
-
commands, and user install commands). Run `agentworks
|
|
699
|
+
commands, and user install commands). Run `agentworks catalog list` to see what is available.
|
|
700
700
|
Reference catalog entries by name in `vm_templates`, `admin.config`, and `agent_templates`.
|
|
701
701
|
User-defined entries in your config override built-in entries with the same name.
|
|
702
702
|
|
|
@@ -363,7 +363,7 @@ Manage workspaces on VMs.
|
|
|
363
363
|
| `agentworks workspace list` | List workspaces |
|
|
364
364
|
| `agentworks workspace copy <source> <name>` | Copy a workspace to a new VM |
|
|
365
365
|
| `agentworks workspace rehome <name>` | Move workspace to a new path |
|
|
366
|
-
| `agentworks workspace
|
|
366
|
+
| `agentworks workspace reinit <name>` | Reinit workspace infrastructure |
|
|
367
367
|
| `agentworks workspace delete <name>` | Delete a workspace |
|
|
368
368
|
|
|
369
369
|
`workspace create <name>` takes the workspace name as a required positional. Optional flags: `--vm`,
|
|
@@ -383,19 +383,18 @@ during deletion. Pass `--yes` to skip the confirmation prompt.
|
|
|
383
383
|
|
|
384
384
|
Manage agents (isolated Linux users) on VMs. Agents are VM-scoped and access workspaces via grants.
|
|
385
385
|
|
|
386
|
-
| Command
|
|
387
|
-
|
|
|
388
|
-
| `agentworks agent create <name> [--vm]`
|
|
389
|
-
| `agentworks agent list [--vm <vm>]`
|
|
390
|
-
| `agentworks agent describe <name>`
|
|
391
|
-
| `agentworks agent reinit <name>`
|
|
392
|
-
| `agentworks agent workspace
|
|
393
|
-
| `agentworks agent workspace
|
|
394
|
-
| `agentworks agent workspace
|
|
395
|
-
| `agentworks agent workspace
|
|
396
|
-
| `agentworks agent workspace
|
|
397
|
-
| `agentworks agent
|
|
398
|
-
| `agentworks agent delete <name>` | Delete an agent |
|
|
386
|
+
| Command | Description |
|
|
387
|
+
| -------------------------------------------------- | ------------------------------ |
|
|
388
|
+
| `agentworks agent create <name> [--vm]` | Create an agent on a VM |
|
|
389
|
+
| `agentworks agent list [--vm <vm>]` | List agents |
|
|
390
|
+
| `agentworks agent describe <name>` | Show agent details and grants |
|
|
391
|
+
| `agentworks agent reinit <name>` | Re-run agent setup |
|
|
392
|
+
| `agentworks agent grant-workspace <name> <ws>...` | Grant workspace access |
|
|
393
|
+
| `agentworks agent grant-workspace <name> --all` | Grant access to all workspaces |
|
|
394
|
+
| `agentworks agent revoke-workspace <name> <ws>...` | Revoke workspace access |
|
|
395
|
+
| `agentworks agent revoke-workspace <name> --all` | Revoke all explicit grants |
|
|
396
|
+
| `agentworks agent shell <name> [--workspace <ws>]` | Shell into an agent |
|
|
397
|
+
| `agentworks agent delete <name>` | Delete an agent |
|
|
399
398
|
|
|
400
399
|
`agent create <name>` takes the agent name as a required positional. Optional flags: `--vm`,
|
|
401
400
|
`--template`, and `--grant-all-workspaces`.
|
|
@@ -547,8 +546,9 @@ aw-console-backend
|
|
|
547
546
|
|
|
548
547
|
The tmux session is built lazily on first `attach` (or rebuilt with `--recreate`). Adding or
|
|
549
548
|
removing sessions/shells while the console is attached updates tmux immediately; when offline, only
|
|
550
|
-
the DB is touched and changes appear on next attach. The
|
|
551
|
-
|
|
549
|
+
the DB is touched and changes appear on next attach. The mutation commands (`add-session`,
|
|
550
|
+
`remove-session`, `add-shell`) never auto-boot the VM; the explicit attach/repair commands
|
|
551
|
+
(`attach`, `restore-session`) do start a stopped VM, since their job is to bring live state up.
|
|
552
552
|
|
|
553
553
|
#### VM console (deprecated)
|
|
554
554
|
|
|
@@ -593,17 +593,17 @@ Template commands support `{{session_name}}` and `{{workspace_name}}` variable s
|
|
|
593
593
|
by `session restart` -- useful for tools like Claude Code where `--resume` picks up the previous
|
|
594
594
|
conversation. If omitted, the regular `command` is used.
|
|
595
595
|
|
|
596
|
-
###
|
|
596
|
+
### Catalog
|
|
597
597
|
|
|
598
598
|
Browse and inspect the built-in catalog of installable tools.
|
|
599
599
|
|
|
600
|
-
| Command
|
|
601
|
-
|
|
|
602
|
-
| `agentworks
|
|
603
|
-
| `agentworks
|
|
600
|
+
| Command | Description |
|
|
601
|
+
| ------------------------------------ | ---------------------------------- |
|
|
602
|
+
| `agentworks catalog list` | List all available catalog entries |
|
|
603
|
+
| `agentworks catalog describe <name>` | Show details of a catalog entry |
|
|
604
604
|
|
|
605
|
-
`
|
|
606
|
-
|
|
605
|
+
`catalog list` accepts `--type` (apt-source, apt-package, system-install-cmd, user-install-cmd) and
|
|
606
|
+
`--source` (built-in, custom) filters.
|
|
607
607
|
|
|
608
608
|
### Config
|
|
609
609
|
|
|
@@ -679,7 +679,7 @@ standalone via `claude plugin marketplace add`.
|
|
|
679
679
|
### Built-in Catalog
|
|
680
680
|
|
|
681
681
|
Agentworks ships a built-in catalog of common tools (apt sources, apt packages, system install
|
|
682
|
-
commands, and user install commands). Run `agentworks
|
|
682
|
+
commands, and user install commands). Run `agentworks catalog list` to see what is available.
|
|
683
683
|
Reference catalog entries by name in `vm_templates`, `admin.config`, and `agent_templates`.
|
|
684
684
|
User-defined entries in your config override built-in entries with the same name.
|
|
685
685
|
|
|
@@ -6,7 +6,15 @@ from typing import TYPE_CHECKING
|
|
|
6
6
|
|
|
7
7
|
from agentworks import output
|
|
8
8
|
from agentworks.config import validate_name
|
|
9
|
-
from agentworks.
|
|
9
|
+
from agentworks.errors import (
|
|
10
|
+
AlreadyExistsError,
|
|
11
|
+
AuthorizationError,
|
|
12
|
+
ExternalError,
|
|
13
|
+
NotFoundError,
|
|
14
|
+
StateError,
|
|
15
|
+
UserAbort,
|
|
16
|
+
ValidationError,
|
|
17
|
+
)
|
|
10
18
|
from agentworks.ssh import admin_exec_target
|
|
11
19
|
|
|
12
20
|
if TYPE_CHECKING:
|
|
@@ -110,7 +118,11 @@ def create_agent(
|
|
|
110
118
|
validate_name(name)
|
|
111
119
|
|
|
112
120
|
if db.get_agent(name) is not None:
|
|
113
|
-
raise
|
|
121
|
+
raise AlreadyExistsError(
|
|
122
|
+
f"agent '{name}' already exists",
|
|
123
|
+
entity_kind="agent",
|
|
124
|
+
entity_name=name,
|
|
125
|
+
)
|
|
114
126
|
|
|
115
127
|
vm = _require_vm(db, vm_name)
|
|
116
128
|
linux_user = derive_linux_user(name)
|
|
@@ -146,7 +158,12 @@ def create_agent(
|
|
|
146
158
|
raise
|
|
147
159
|
except Exception as e:
|
|
148
160
|
_safe_rollback()
|
|
149
|
-
raise
|
|
161
|
+
raise ExternalError(
|
|
162
|
+
f"creating agent: {e}",
|
|
163
|
+
entity_kind="agent",
|
|
164
|
+
entity_name=name,
|
|
165
|
+
hint=f"SSH log: {ssh_logger.path}",
|
|
166
|
+
) from e
|
|
150
167
|
finally:
|
|
151
168
|
ssh_logger.close()
|
|
152
169
|
|
|
@@ -178,7 +195,11 @@ def delete_agent(
|
|
|
178
195
|
"""Delete an agent from a VM."""
|
|
179
196
|
agent = db.get_agent(name)
|
|
180
197
|
if agent is None:
|
|
181
|
-
raise
|
|
198
|
+
raise NotFoundError(
|
|
199
|
+
f"agent '{name}' not found",
|
|
200
|
+
entity_kind="agent",
|
|
201
|
+
entity_name=name,
|
|
202
|
+
)
|
|
182
203
|
|
|
183
204
|
# Check for sessions using this agent
|
|
184
205
|
all_sessions = db.list_sessions()
|
|
@@ -186,8 +207,11 @@ def delete_agent(
|
|
|
186
207
|
if agent_sessions and not force:
|
|
187
208
|
for s in agent_sessions:
|
|
188
209
|
output.detail(f"{s.name}")
|
|
189
|
-
raise
|
|
190
|
-
f"agent '{name}' has {len(agent_sessions)} session(s).
|
|
210
|
+
raise StateError(
|
|
211
|
+
f"agent '{name}' has {len(agent_sessions)} session(s).",
|
|
212
|
+
entity_kind="agent",
|
|
213
|
+
entity_name=name,
|
|
214
|
+
hint="Delete the sessions first, or pass --force to also stop them.",
|
|
191
215
|
)
|
|
192
216
|
|
|
193
217
|
if not yes:
|
|
@@ -195,7 +219,7 @@ def delete_agent(
|
|
|
195
219
|
if agent_sessions:
|
|
196
220
|
msg += f" ({len(agent_sessions)} session(s) will also be stopped)"
|
|
197
221
|
if not output.confirm(msg):
|
|
198
|
-
raise
|
|
222
|
+
raise UserAbort("delete cancelled")
|
|
199
223
|
|
|
200
224
|
vm = _require_vm(db, agent.vm_name)
|
|
201
225
|
|
|
@@ -232,9 +256,12 @@ def delete_agent(
|
|
|
232
256
|
elif status == SessionStatus.UNKNOWN:
|
|
233
257
|
unstoppable.append(session.name)
|
|
234
258
|
if unstoppable:
|
|
235
|
-
raise
|
|
259
|
+
raise StateError(
|
|
236
260
|
f"cannot delete agent '{name}': {len(unstoppable)} session(s) could not be stopped "
|
|
237
|
-
f"({', '.join(unstoppable)}).
|
|
261
|
+
f"({', '.join(unstoppable)}).",
|
|
262
|
+
entity_kind="agent",
|
|
263
|
+
entity_name=name,
|
|
264
|
+
hint="Resolve the stuck sessions manually before retrying.",
|
|
238
265
|
)
|
|
239
266
|
for session in agent_sessions:
|
|
240
267
|
db.delete_session(session.name)
|
|
@@ -266,7 +293,11 @@ def reinit_agent(
|
|
|
266
293
|
|
|
267
294
|
agent = db.get_agent(name)
|
|
268
295
|
if agent is None:
|
|
269
|
-
raise
|
|
296
|
+
raise NotFoundError(
|
|
297
|
+
f"agent '{name}' not found",
|
|
298
|
+
entity_kind="agent",
|
|
299
|
+
entity_name=name,
|
|
300
|
+
)
|
|
270
301
|
|
|
271
302
|
agent_tmpl = resolve_template(config, agent.template)
|
|
272
303
|
if agent.template and agent.template != "default":
|
|
@@ -290,7 +321,12 @@ def reinit_agent(
|
|
|
290
321
|
)
|
|
291
322
|
raise
|
|
292
323
|
except Exception as e:
|
|
293
|
-
raise
|
|
324
|
+
raise ExternalError(
|
|
325
|
+
f"reinitializing agent: {e}",
|
|
326
|
+
entity_kind="agent",
|
|
327
|
+
entity_name=name,
|
|
328
|
+
hint=f"SSH log: {ssh_logger.path}",
|
|
329
|
+
) from e
|
|
294
330
|
finally:
|
|
295
331
|
ssh_logger.close()
|
|
296
332
|
|
|
@@ -370,7 +406,11 @@ def describe_agent(
|
|
|
370
406
|
"""Show detailed information about an agent."""
|
|
371
407
|
agent = db.get_agent(name)
|
|
372
408
|
if agent is None:
|
|
373
|
-
raise
|
|
409
|
+
raise NotFoundError(
|
|
410
|
+
f"agent '{name}' not found",
|
|
411
|
+
entity_kind="agent",
|
|
412
|
+
entity_name=name,
|
|
413
|
+
)
|
|
374
414
|
|
|
375
415
|
output.info(f"Name: {agent.name}")
|
|
376
416
|
output.info(f"VM: {agent.vm_name}")
|
|
@@ -410,7 +450,11 @@ def shell_agent(
|
|
|
410
450
|
"""Open a shell as an agent user on a VM."""
|
|
411
451
|
agent = db.get_agent(name)
|
|
412
452
|
if agent is None:
|
|
413
|
-
raise
|
|
453
|
+
raise NotFoundError(
|
|
454
|
+
f"agent '{name}' not found",
|
|
455
|
+
entity_kind="agent",
|
|
456
|
+
entity_name=name,
|
|
457
|
+
)
|
|
414
458
|
|
|
415
459
|
vm = _require_vm(db, agent.vm_name)
|
|
416
460
|
|
|
@@ -427,9 +471,18 @@ def shell_agent(
|
|
|
427
471
|
if workspace_name:
|
|
428
472
|
ws = db.get_workspace(workspace_name)
|
|
429
473
|
if ws is None:
|
|
430
|
-
raise
|
|
474
|
+
raise NotFoundError(
|
|
475
|
+
f"workspace '{workspace_name}' not found",
|
|
476
|
+
entity_kind="workspace",
|
|
477
|
+
entity_name=workspace_name,
|
|
478
|
+
)
|
|
431
479
|
if not db.has_any_grant(name, workspace_name):
|
|
432
|
-
raise
|
|
480
|
+
raise AuthorizationError(
|
|
481
|
+
f"agent '{name}' does not have access to workspace '{workspace_name}'",
|
|
482
|
+
entity_kind="agent",
|
|
483
|
+
entity_name=name,
|
|
484
|
+
hint=f"Run 'agent grant-workspace {name} {workspace_name}' to grant access.",
|
|
485
|
+
)
|
|
433
486
|
import shlex
|
|
434
487
|
|
|
435
488
|
q_path = shlex.quote(ws.workspace_path)
|
|
@@ -454,11 +507,20 @@ def exec_agent(
|
|
|
454
507
|
|
|
455
508
|
agent = db.get_agent(name)
|
|
456
509
|
if agent is None:
|
|
457
|
-
raise
|
|
510
|
+
raise NotFoundError(
|
|
511
|
+
f"agent '{name}' not found",
|
|
512
|
+
entity_kind="agent",
|
|
513
|
+
entity_name=name,
|
|
514
|
+
)
|
|
458
515
|
|
|
459
516
|
vm = _require_vm(db, agent.vm_name)
|
|
460
517
|
if vm.tailscale_host is None:
|
|
461
|
-
raise
|
|
518
|
+
raise StateError(
|
|
519
|
+
f"VM '{vm.name}' has no Tailscale IP",
|
|
520
|
+
entity_kind="vm",
|
|
521
|
+
entity_name=vm.name,
|
|
522
|
+
hint="VM init may not be complete. Check 'vm describe' for status.",
|
|
523
|
+
)
|
|
462
524
|
|
|
463
525
|
remote_cmd = command[0] if len(command) == 1 else shlex.join(command)
|
|
464
526
|
su_cmd = f"sudo -n su --login {agent.linux_user} -c {shlex.quote(remote_cmd)}"
|
|
@@ -484,9 +546,21 @@ def grant_workspaces(
|
|
|
484
546
|
grant_all: bool = False,
|
|
485
547
|
) -> None:
|
|
486
548
|
"""Grant an agent explicit access to workspaces."""
|
|
549
|
+
if not grant_all and not workspace_names:
|
|
550
|
+
raise ValidationError(
|
|
551
|
+
f"grant for '{agent_name}' needs at least one workspace name "
|
|
552
|
+
f"or workspace_names empty + grant_all=True",
|
|
553
|
+
entity_kind="agent",
|
|
554
|
+
entity_name=agent_name,
|
|
555
|
+
)
|
|
556
|
+
|
|
487
557
|
agent = db.get_agent(agent_name)
|
|
488
558
|
if agent is None:
|
|
489
|
-
raise
|
|
559
|
+
raise NotFoundError(
|
|
560
|
+
f"agent '{agent_name}' not found",
|
|
561
|
+
entity_kind="agent",
|
|
562
|
+
entity_name=agent_name,
|
|
563
|
+
)
|
|
490
564
|
|
|
491
565
|
vm = _require_vm(db, agent.vm_name)
|
|
492
566
|
|
|
@@ -509,22 +583,34 @@ def grant_workspaces(
|
|
|
509
583
|
output.detail(f"Granted: {ws_name}")
|
|
510
584
|
|
|
511
585
|
|
|
512
|
-
def
|
|
586
|
+
def revoke_workspaces(
|
|
513
587
|
db: Database,
|
|
514
588
|
config: Config,
|
|
515
589
|
*,
|
|
516
590
|
agent_name: str,
|
|
517
591
|
workspace_names: list[str],
|
|
518
|
-
|
|
592
|
+
revoke_all: bool = False,
|
|
519
593
|
) -> None:
|
|
520
|
-
"""
|
|
594
|
+
"""Revoke explicit workspace grants from an agent."""
|
|
595
|
+
if not revoke_all and not workspace_names:
|
|
596
|
+
raise ValidationError(
|
|
597
|
+
f"revoke for '{agent_name}' needs at least one workspace name "
|
|
598
|
+
f"or workspace_names empty + revoke_all=True",
|
|
599
|
+
entity_kind="agent",
|
|
600
|
+
entity_name=agent_name,
|
|
601
|
+
)
|
|
602
|
+
|
|
521
603
|
agent = db.get_agent(agent_name)
|
|
522
604
|
if agent is None:
|
|
523
|
-
raise
|
|
605
|
+
raise NotFoundError(
|
|
606
|
+
f"agent '{agent_name}' not found",
|
|
607
|
+
entity_kind="agent",
|
|
608
|
+
entity_name=agent_name,
|
|
609
|
+
)
|
|
524
610
|
|
|
525
611
|
vm = _require_vm(db, agent.vm_name)
|
|
526
612
|
|
|
527
|
-
if
|
|
613
|
+
if revoke_all:
|
|
528
614
|
db.update_agent_grant_all(agent_name, False)
|
|
529
615
|
db.delete_explicit_grants(agent_name)
|
|
530
616
|
# Remove from groups where no implicit grants remain
|
|
@@ -535,7 +621,7 @@ def deny_workspaces(
|
|
|
535
621
|
remaining_implicit.append(ws_name)
|
|
536
622
|
else:
|
|
537
623
|
_remove_from_workspace_group(vm, config, db, agent.linux_user, ws_name, logger=None)
|
|
538
|
-
output.info(f"All explicit grants
|
|
624
|
+
output.info(f"All explicit grants revoked for agent '{agent_name}'")
|
|
539
625
|
if remaining_implicit:
|
|
540
626
|
output.warn(
|
|
541
627
|
f"agent still has implicit access via sessions to: {', '.join(remaining_implicit)}"
|
|
@@ -546,39 +632,9 @@ def deny_workspaces(
|
|
|
546
632
|
db.delete_agent_grant(agent_name, ws_name, "explicit")
|
|
547
633
|
if not db.has_any_grant(agent_name, ws_name):
|
|
548
634
|
_remove_from_workspace_group(vm, config, db, agent.linux_user, ws_name, logger=None)
|
|
549
|
-
output.detail(f"
|
|
635
|
+
output.detail(f"Revoked: {ws_name}")
|
|
550
636
|
else:
|
|
551
|
-
output.detail(f"
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
def list_grants(
|
|
555
|
-
db: Database,
|
|
556
|
-
*,
|
|
557
|
-
agent_name: str,
|
|
558
|
-
) -> None:
|
|
559
|
-
"""List workspace grants for an agent."""
|
|
560
|
-
agent = db.get_agent(agent_name)
|
|
561
|
-
if agent is None:
|
|
562
|
-
raise AgentError(f"agent '{agent_name}' not found")
|
|
563
|
-
|
|
564
|
-
if agent.grant_all:
|
|
565
|
-
output.info(f"Agent '{agent_name}' has grant-all enabled (access to all workspaces)")
|
|
566
|
-
|
|
567
|
-
grants = db.list_granted_workspaces_with_types(agent_name)
|
|
568
|
-
if not grants:
|
|
569
|
-
output.info("No workspace grants.")
|
|
570
|
-
return
|
|
571
|
-
|
|
572
|
-
output.info(f"{'WORKSPACE':<25} {'TYPE'}")
|
|
573
|
-
output.info("-" * 45)
|
|
574
|
-
for ws_name, has_explicit, has_implicit in grants:
|
|
575
|
-
if has_explicit and has_implicit:
|
|
576
|
-
grant_type = "explicit + implicit"
|
|
577
|
-
elif has_explicit:
|
|
578
|
-
grant_type = "explicit"
|
|
579
|
-
else:
|
|
580
|
-
grant_type = "implicit (via sessions)"
|
|
581
|
-
output.info(f"{ws_name:<25} {grant_type}")
|
|
637
|
+
output.detail(f"Revoked: {ws_name} (still has implicit access via sessions)")
|
|
582
638
|
|
|
583
639
|
|
|
584
640
|
# -- VM operations ---------------------------------------------------------
|
|
@@ -593,7 +649,11 @@ def _resolve_ws_group(db: Database, workspace_name: str) -> str:
|
|
|
593
649
|
"""
|
|
594
650
|
ws = db.get_workspace(workspace_name)
|
|
595
651
|
if ws is None:
|
|
596
|
-
raise
|
|
652
|
+
raise NotFoundError(
|
|
653
|
+
f"workspace '{workspace_name}' not found",
|
|
654
|
+
entity_kind="workspace",
|
|
655
|
+
entity_name=workspace_name,
|
|
656
|
+
)
|
|
597
657
|
return ws.linux_group
|
|
598
658
|
|
|
599
659
|
|
|
@@ -1127,19 +1187,31 @@ def _build_agent_test_command(
|
|
|
1127
1187
|
def _require_vm(db: Database, vm_name: str) -> VMRow:
|
|
1128
1188
|
vm = db.get_vm(vm_name)
|
|
1129
1189
|
if vm is None:
|
|
1130
|
-
raise
|
|
1190
|
+
raise NotFoundError(
|
|
1191
|
+
f"VM '{vm_name}' not found",
|
|
1192
|
+
entity_kind="vm",
|
|
1193
|
+
entity_name=vm_name,
|
|
1194
|
+
)
|
|
1131
1195
|
return vm
|
|
1132
1196
|
|
|
1133
1197
|
|
|
1134
1198
|
def _require_workspace(db: Database, workspace_name: str) -> WorkspaceRow:
|
|
1135
1199
|
ws = db.get_workspace(workspace_name)
|
|
1136
1200
|
if ws is None:
|
|
1137
|
-
raise
|
|
1201
|
+
raise NotFoundError(
|
|
1202
|
+
f"workspace '{workspace_name}' not found",
|
|
1203
|
+
entity_kind="workspace",
|
|
1204
|
+
entity_name=workspace_name,
|
|
1205
|
+
)
|
|
1138
1206
|
return ws
|
|
1139
1207
|
|
|
1140
1208
|
|
|
1141
1209
|
def _require_vm_for_workspace(db: Database, ws: WorkspaceRow) -> VMRow:
|
|
1142
1210
|
vm = db.get_vm(ws.vm_name)
|
|
1143
1211
|
if vm is None:
|
|
1144
|
-
raise
|
|
1212
|
+
raise NotFoundError(
|
|
1213
|
+
f"VM '{ws.vm_name}' not found",
|
|
1214
|
+
entity_kind="vm",
|
|
1215
|
+
entity_name=ws.vm_name,
|
|
1216
|
+
)
|
|
1145
1217
|
return vm
|
|
@@ -13,11 +13,13 @@ from dataclasses import dataclass, field
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
|
+
from agentworks.errors import ExternalError
|
|
17
|
+
|
|
16
18
|
if TYPE_CHECKING:
|
|
17
19
|
from agentworks.config import Config
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
class CatalogError(
|
|
22
|
+
class CatalogError(ExternalError):
|
|
21
23
|
"""Raised when the catalog is invalid."""
|
|
22
24
|
|
|
23
25
|
|