From 7264840da6027b90e9dae8061ede506319482441 Mon Sep 17 00:00:00 2001 From: Howard Enos Date: Mon, 6 Jul 2026 16:51:53 -0700 Subject: [PATCH] sync: auto-sync from HOWARD-HOME at 2026-07-06 16:51:24 Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-07-06 16:51:24 --- ...7-06-howard-screenconnect-fleet-listing.md | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 session-logs/2026-07/2026-07-06-howard-screenconnect-fleet-listing.md diff --git a/session-logs/2026-07/2026-07-06-howard-screenconnect-fleet-listing.md b/session-logs/2026-07/2026-07-06-howard-screenconnect-fleet-listing.md new file mode 100644 index 00000000..5618d76b --- /dev/null +++ b/session-logs/2026-07/2026-07-06-howard-screenconnect-fleet-listing.md @@ -0,0 +1,155 @@ +# 2026-07-06 — ScreenConnect skill: full-fleet listing fix + +## User +- **User:** Howard Enos (howard) +- **Machine:** Howard-Home +- **Role:** tech + +## Session Summary + +Fixed the `screenconnect` skill's reported inability to list the full agent fleet. +The prior state believed the RESTful API Manager extension on the ACG instance +(`computerguru.screenconnect.com`) exposed no full-fleet enumeration method — only +`GetSessionsByName`, an exact-name lookup — and the SKILL.md/reference docs framed +this as a gap needing Mike to update the extension. + +That premise was wrong about the cause. Empirical probing of the live extension +found that `GetSessionsByFilter` **does** exist; earlier probing had simply never +tried it. It was distinguishable because it returned a different error +(`InvalidOperationException: "Session filter cannot be null"`) than the +"web method does not exist" every other candidate returned. Discovery of its +signature followed: the parameter is named `sessionFilter` (not `filter`), it takes +a ScreenConnect session-filter expression, and string literals are single-quoted. +`SessionType = 'Access'` returns all 958 unattended agents; `SessionType = 'Support'` +returns the 24 on-demand sessions; `CustomProperty1 = ''` does per-client +listing; `Name LIKE '**'` does partial-name search; `AND`/`OR` compose. + +Implemented the fix across the client (`get_sessions_by_filter`, a +`build_session_filter` builder, `list_sessions` convenience), the CLI (`sessions` +now defaults to the whole Access fleet, with new `--like`, `--company`, `--site`, +`--tag`, `--type`, `--filter`, `--limit` flags; `status` reports real fleet counts), +the self-test (added coverage), and both doc files (documented the filter language, +removed the stale "MISSING / needs extension update" framing). + +Ran `/code-review` at high effort via the workflow backend. It caught three real +regressions I had introduced plus four robustness/quality issues. All seven were +fixed and re-verified live. The self-test grew from the original set to 19 checks, +all passing against the live instance. The repo's background auto-sync committed and +pushed the changes (commits `7dc5198` then `12c3d83`); `main` is level with +`origin/main`. + +## Key Decisions + +- **Probe the live extension rather than trust the documented gap.** The "GetSessions + unavailable" note was a conclusion from incomplete probing; treating the API surface + as empirically discoverable (not fixed) surfaced `GetSessionsByFilter`. +- **Route full-fleet listing through a filter builder, not ad-hoc strings.** A + `build_session_filter(**parts)` keeps CLI flags declarative and centralizes literal + escaping. +- **`sessions` defaults to the whole Access fleet.** The headline use case is "show me + every agent"; a bare `sessions` now does that (958), with `--company`/`--like`/etc. + narrowing. +- **A targeted selector defaults `--type` to `all`, a bare listing defaults to + `access`.** This restores the old type-agnostic by-name behavior (Support/Meeting + sessions resolve) while keeping the fleet default meaningful. +- **`status` auth check stays on the cheap `GetSessionsByName('')`; fleet counts are + best-effort.** Keeps `status` fast and functional even on an extension version + lacking `GetSessionsByFilter`. +- **Kept SQL-style `''` quote-doubling for escaping** after verifying it round-trips + live (matched "Andy's Mobile Fuel"). + +## Problems Encountered + +- **Full-fleet listing believed impossible.** Root cause: `GetSessionsByFilter` never + probed. Resolved by discovering the method, its `sessionFilter` param name, and the + `SessionType = 'Access'` match-all form. +- **`GetSessionsByName('')` returns only blank-name sessions (3), not the fleet.** + Confirmed it is an exact `Name ==` match, so it cannot be coaxed into listing — + hence the filter method is required. +- **Three regressions from the first-pass rewrite** (caught by `/code-review`): + (1) `--limit` truncated `--json`, breaking the "json is always full" contract — + fixed so `--limit` only caps the human-readable print; (2) `--type all` fell through + to the Access-only default — fixed with an `(SessionType = 'Access' OR SessionType = + 'Support')` clause (verified 982 = 958 + 24); (3) `--name` lost type-agnostic + lookup — fixed via context-aware `--type` defaulting. All re-verified live. +- **`status` was downloading the full fleet twice just to count, and would fail if the + filter method were absent.** Decoupled the auth check (cheap `GetSessionsByName`) + from best-effort fleet counting wrapped in try/except. +- **`/tmp` write blocked by the harness hook** during a shell sanity check (expected + Windows `/tmp` rule); switched to a repo-relative scratch path. + +## Configuration Changes + +Modified (all under `.claude/skills/screenconnect/`): +- `scripts/sc_client.py` — added `get_sessions_by_filter()`, `_filter_literal()`, + `build_session_filter()`, `list_sessions()`; added `SESSION_TYPE_LITERALS`; updated + module/method docstrings. +- `scripts/sc.py` — `cmd_status` decoupled (cheap auth + best-effort counts); + `cmd_sessions` reworked with `_effective_type()`, limit-only-on-print; added + `--like/--company/--site/--tag/--type/--filter/--limit` flags; updated usage + docstring. +- `scripts/selftest.py` — restored `Authenticated` assertion, added `--type all`, + `--like`, `--filter`, `--company`, `--limit`, and a "limit does not truncate json" + check (`out_json_min_len`). Now 19 checks. +- `SKILL.md` — documented `GetSessionsByFilter` as the enumeration path + CLI examples; + replaced the "MISSING, needs Mike" framing with "absent aliases, not needed". +- `references/api-reference.md` — added `GetSessionsByFilter` row + a session-filter + language section. + +## Credentials & Secrets + +No new credentials. API secret unchanged: SOPS vault `msp-tools/screenconnect.sops.yaml` +field `credentials.api_secret` (env override `SCREENCONNECT_API_SECRET`). Auth remains +two headers — `CTRLAuthHeader: ` (no "Basic" prefix) and +`Origin: https://computerguru.screenconnect.com`. + +## Infrastructure & Servers + +- **Instance:** `https://computerguru.screenconnect.com` +- **Extension GUID:** `2d558935-686a-4bd0-9991-07539f5fe749` (RESTful API Manager) +- **Endpoint:** `POST /App_Extensions//Service.ashx/` +- **Fleet snapshot (2026-07-06):** 958 Access (unattended agents) + 24 Support = 982. +- **Custom properties:** CP1=Company, CP2=Site, CP3=Tag (up to CP8; CP4 seen holding a + role like Server/Desktop, CP5 "Syncro-Matched" on some records). +- **SessionType literals:** `Access` = 2, `Support` = 0, `Meeting` = none present. + +## Commands & Outputs + +```bash +SC="bash .claude/scripts/py.sh C:/claudetools/.claude/skills/screenconnect/scripts/sc.py" + +# full fleet (958), per-client, partial name, all types +$SC sessions +$SC sessions --company "Ridgetop Group" # 14 +$SC sessions --like DELL --limit 3 +$SC sessions --type all --json | ... # 982 + +# the discovery: filter method exists, param is sessionFilter +$SC raw --method GetSessionsByFilter --body '{"sessionFilter":"SessionType = '"'"'Access'"'"'"}' +``` + +Key probe results: +- `GetSessionsByFilter {}` -> `InvalidOperationException: "Session filter cannot be null"` (method EXISTS). +- `{"filter":""}` -> `"Unknown parameter: filter"`; `{"sessionFilter":"x"}` -> `[]` (valid, matched none). +- `SessionType = 'Access'` -> 958; `'Support'` -> 24; `'Access' OR 'Support'` -> 982. +- `CustomProperty1 = 'Safesite'` -> 62; `Name LIKE '*DELL*'` -> 26. +- Escaping verified: `CustomProperty1 LIKE '*Andy''s*'` -> 1 (matched "Andy's Mobile Fuel"). + +Self-test: `19/19 passed, 0 failed` against the live instance. + +## Pending / Incomplete Tasks + +- None for the skill itself. The GuruRMM addon plan it prototypes is unchanged and + still gated on Mike's go (SPEC-024, RMM_THOUGHTS Feature 7 / Refinement 7a). +- Unrelated working-tree drift left untouched: `projects/dataforth-dos`, + `projects/discord-bot`, `projects/msp-tools/guru-rmm`, + `projects/msp-tools/security-assessment` (submodule pointers), `projects/radio-show` + (untracked). Not part of this work. + +## Reference Information + +- Commits: `7dc5198` (initial fix) and `12c3d83` (review fixes) — both auto-sync + commits from HOWARD-HOME; `main` == `origin/main`. +- Skill dir: `.claude/skills/screenconnect/` +- Filter language ref: `.claude/skills/screenconnect/references/api-reference.md` + (session-filter section).