Addresses review feedback on this branch:
- Fix TS2352 build error in dispatch.ts: `getSession()` returns `Session`,
which has no index signature, so `(s as Record<string, unknown>)` is rejected
by tsc. `Session.agent_group_id` exists — read it directly.
- Fix a regression introduced by dropping the `groupField in data` guard:
the post-handler scope check now runs for *every* command under a whitelisted
resource, including custom ops, which return ad-hoc shapes. `ncl groups config
get` (access:open, reachable by a group-scoped agent) returns a config object
with no `id` field → `data['id'] !== ctx.agentGroupId` → `forbidden`, even on
the agent's own config. Fix: tag the auto-generated list/get handlers with
`generic: 'list' | 'get'` on `CommandDef` (set in `registerResource`) and run
the post-handler check only when `cmd.generic` is set. Generic handlers return
raw DB rows that carry `scopeField`; custom ops are already pinned to the
caller's group by the pre-handler `--id` auto-fill or the approval gate.
Fail-closed-when-`scopeField`-missing is preserved (now scoped to generic
list/get).
- Tests: `dispatch.test.ts` mocks `getResource` (the real resources aren't
registered in this unit), tags the two post-handler test commands as `generic`,
and adds coverage for: custom op returning a non-row object not being rejected;
`sessions-get` pre-handler returning "session not found" for foreign and
non-existent UUIDs (no existence oracle) and allowing the caller's own session;
generic list/get failing closed when a resource declares no `scopeField`.
Full suite: 323 passing.
- Remove FORK.md from the PR diff — it's the fork's personal README, carried in
because the branch was cut from the fork's `main` rather than upstream.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Group-scoped agents could previously:
- See all agent groups via `groups list` (generic list skips --id filter)
- Look up any session by UUID via `sessions get`
- Request cli_scope change to global via config update approval
Fixed by:
- Post-handler filtering: list results filtered, get results verified
against caller's agent_group_id
- Pre-handler --id check scoped to resources where id IS the group ID
(groups, destinations) so session UUIDs aren't falsely rejected
- cli_scope/cli-scope args blocked outright for group-scoped agents,
before the approval gate
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add cli_scope column to container_configs with three levels:
- disabled: agent never learns about ncl (instructions excluded from
CLAUDE.md) and host dispatch rejects any cli_request
- group (default): agent can only access groups, sessions, destinations,
and members resources, scoped to its own agent group with auto-filled
--id/--agent_group_id/--group args. Help output reflects the scope.
- global: unrestricted access (current behavior)
Enforcement is host-side only — no image rebuild or env var needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`ncl groups config get` now works alongside `ncl groups config-get`.
Parser joins all positionals with dashes; dispatcher falls back by
trimming the last segment as a target ID (`ncl groups get abc123`).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a container agent calls an approval-gated ncl command, dispatch
now sends an approval card to an admin instead of returning a stub
error. On approve, the handler re-dispatches the original command
and notifies the agent with the result.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resource-first CLI: `nc groups list`, `nc wirings get <id>`, etc.
Seven resources defined (groups, messaging-groups, wirings, users,
roles, members, sessions) with full column documentation that serves
as the single source of truth for help output and arg validation.
- CRUD helper auto-registers list/get/create/update/delete from
declarative resource definitions with generic SQL
- Custom operations for composite-PK resources (roles grant/revoke,
members add/remove)
- Access model: open (reads) / approval (writes) / hidden
- `nc help` lists resources; `nc <resource> help` shows fields
- Positional target IDs: `nc groups get <id>`
- Removed unused priority column from wirings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-commit hook ran prettier on the prior commit but left the reformats
unstaged. Folding them in here so the branch is clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a transport-agnostic CLI control plane shared between three eventual
callers (host shell, Claude in project, container agent) — though only the
host-side socket transport is wired in this commit. Container DB transport
and approval flow land alongside the first risky command.
- src/cli/frame.ts: wire format (RequestFrame, ResponseFrame, CallerContext)
- src/cli/registry.ts: command registry with RiskClass
- src/cli/dispatch.ts: transport-agnostic dispatcher
- src/cli/transport.ts: Transport interface
- src/cli/socket-client.ts: SocketTransport against data/nc.sock
- src/cli/socket-server.ts: host-side listener (chmod 0600, line-delimited JSON)
- src/cli/format.ts: human table / --json output modes
- src/cli/client.ts: `nc` argv -> frame -> transport -> stdout
- src/cli/commands/list-groups.ts: first command (riskClass: safe)
- bin/nc: bash launcher (resolves project root via symlink)
- src/index.ts: start/stop server + import command barrel
`data/nc.sock` is intentionally separate from `data/cli.sock` (which the
existing chat-style channel adapter still owns).
Verified end-to-end: `nc list-groups`, `nc list groups`, `--json`,
unknown-command error, host-down ENOENT message with start instructions.
typecheck clean; eslint reports only the same `no-catch-all` warnings the
rest of the codebase has.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>