sync: auto-sync from HOWARD-HOME at 2026-07-14 19:13:30

Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-07-14 19:13:30
This commit is contained in:
2026-07-14 19:14:01 -07:00
parent ec2fe6060b
commit 941de499c2
9 changed files with 835 additions and 731 deletions

View File

@@ -35,7 +35,7 @@ py "$MSP" create-company --name "New Client LLC" --confirm
py "$MSP" create-user --email backup@client.com --company "New Client LLC" \
--first Jane --last Doe --notify admin@acg.com --confirm
py "$MSP" grant-license --user-id <UUID> --license-id <UUID> --confirm
py "$MSP" release-license --user-id <UUID> --confirm
py "$MSP" release-license --user-id <UUID> --license-id <UUID> --confirm
py "$MSP" delete-user --id <UUID> [--keep-data] --confirm
py "$MSP" delete-company --id <UUID> --confirm
py "$MSP" raw POST /api/Accounts/CreateDestination --data '{...}' --confirm
@@ -91,6 +91,11 @@ RequestCustomBuilds), **Administrators** (GET/POST/PUT/DELETE). Reference:
- **`/api/Computers` returns HTTP 400 "Remote Management API methods are not enabled"** —
that feature is off on this account. `computers` prints a clean [INFO] and points you at
`users` + `monitoring`, which cover endpoint/backup data without it.
- **Licensing API is also feature-gated:** `POST /api/Licenses/Grant|Release|Revoke` returns
HTTP 400 "Licensing API methods are not enabled for your account" — license moves must be
done in the mspbackups.com console (Mike's admin account) until that's enabled.
- **Release/Revoke require `--license-id`:** the API rejects a Release/Revoke payload without
`LicenseID` (HTTP 400 ModelState). The CLI now enforces it; get the ID from `licenses --json`.
- **Git-Bash path mangling:** a leading-slash `raw` path like `/api/Users` gets rewritten to
`C:/Program Files/Git/api/Users` by MSYS. The `raw` command auto-recovers the `/api/...`
tail, so pass the path normally.

View File

@@ -392,12 +392,14 @@ def build_parser() -> argparse.ArgumentParser:
sp.add_argument("--confirm", action="store_true")
sp.set_defaults(func=cmd_delete_user)
for name, fn, verb in (("grant-license", cmd_grant_license, "grant"),
("release-license", cmd_release_license, "release"),
("revoke-license", cmd_revoke_license, "revoke")):
# API requires LicenseID for Release/Revoke (HTTP 400 without it); Grant can
# auto-pick a license, so it stays optional there.
for name, fn, verb, lic_required in (("grant-license", cmd_grant_license, "grant", False),
("release-license", cmd_release_license, "release", True),
("revoke-license", cmd_revoke_license, "revoke", True)):
sp = sub.add_parser(name, help=f"{verb} a license [--confirm]")
sp.add_argument("--user-id", required=True)
sp.add_argument("--license-id")
sp.add_argument("--license-id", required=lic_required)
sp.add_argument("--confirm", action="store_true")
sp.set_defaults(func=fn)