Skip to content

feat(cli): ceki contract group — port ceki-agent.js (task 423)#6

Merged
iWedmak merged 10 commits into
masterfrom
feature/ceki-contract-cli
Jun 19, 2026
Merged

feat(cli): ceki contract group — port ceki-agent.js (task 423)#6
iWedmak merged 10 commits into
masterfrom
feature/ceki-contract-cli

Conversation

@iWedmak

@iWedmak iWedmak commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New ceki contract command group in ceki_sdk (Python CLI) covering everything agent-contract-tasks/scripts/ceki-agent.js does: list / members / tasks / my-jobs / task / children / history / create / comment / propose / vote / poll / watch / tools / raw.
  • ContractClient (httpx) over POST /mcp/agent + REST /api/agent/polling. MCP unwrap: result.content[].text (JSON-parsed) → fallback result.structuredContent.
  • Env: CEKI_API_URL derives both endpoints; CEKI_AGENT_MCP_ENDPOINT / CEKI_API_BASE overrides preserved. Token: CEKI_AGENT_TOKEN primary, fallback to CEKI_API_KEY. CEKI_CONTRACT_IDS (csv / bracketed / json) for default contract(s).
  • --benefitable agent:N{type,value}; undefined fields stripped before send; polling handles 429 (rate-limit 10/min/token), watch enforces ≥6s interval.
  • Version 2.18.0 → 2.19.0.

Test plan

  • tests/test_contract.py — 33 unit tests (benefitable / clean / env resolve, MCP unwrap, tool-name mapping, payload shape, polling 200 / list / dict / 429 / error, CLI parser).
  • tests/test_cli.py — existing 36 tests still green (no regression in rental commands).
  • Manual smoke against dev MCP (clawapi.ittribe.org/mcp/agent) before merge.

Notes

  • ceki-agent.js shim is left untouched in agent-contract-tasks; removal is a separate task after this lands and works in production.
  • Token decision (CEKI_AGENT_TOKEN separate, fallback to CEKI_API_KEY) chosen because rental SDK uses CEKI_API_KEY while agent contract MCP uses Bearer ag_* via auth.agent.key middleware — likely different tokens for the same agent, so keep them separable, but fall back so a single-token setup keeps working.

ceki-plugin added 10 commits June 17, 2026 07:11
ContractClient (httpx) + `ceki contract …` subparser covering all
ceki-agent.js commands: list/members/tasks/my-jobs/task/children/history,
create/comment/propose/vote, poll/watch, tools/raw.

- /mcp/agent endpoint derived from CEKI_API_URL; CEKI_AGENT_MCP_ENDPOINT
  override preserved for backward compat
- Token: CEKI_AGENT_TOKEN primary, falls back to CEKI_API_KEY
- CEKI_CONTRACT_IDS default for tasks/create (csv / bracketed / json)
- benefitable "agent:N" → {type,value}; undefined fields stripped before send
- poll handles 429 explicitly (rate-limit 10/min/token), watch min 6s
- MCP unwrap: result.content[].text (json-parsed) or structuredContent

Tests: 33 new in tests/test_contract.py (benefitable, clean, env resolve,
MCP unwrap, tool name mapping, payload shape, polling, parser).

Version 2.18.0 → 2.19.0.
- ceki_sdk/timelog.py: TimelogClient wraps ContractClient
  (same /mcp/agent transport, env, auth) — start/stop/check by event_id
- ceki_sdk/cli.py: new top-level group `ceki timelog` (NOT under contract)
  - timelog start <event_id>            → MCP timelog-start
  - timelog stop  <event_id> [--label]  → MCP timelog-stop (duration server-side)
  - timelog check <event_id>            → MCP timelog-check
- tests/test_timelog.py: 13 tests (tool name mapping, --label payload,
  unwrap, error propagation, CLI parser, ensures it is NOT under contract)
- README: ceki timelog section
- pyproject: bump to 2.20.0
…t, sessions --json datetime (task 425) — v2.21.0

BUG-1 — Browser.type now accepts an optional CSS selector (-- 'ceki
type $SID --selector input[type=email] <text>'). The SDK focuses the
element via Runtime.evaluate before sending Ceki.typeText so native-
flow signups (signup.live.com) land keystrokes on the intended input
even when no prior click established focus.

BUG-3 — screenshot()/snapshot() pass 'optimizeForSpeed: true' to
Page.captureScreenshot and default the CDP timeout to 120s (up from
60s). Heavy pages routinely take 60+ seconds via the high-level
wrapper; the bumped budget plus the speed flag bring captures back
to sub-second on the same pages.

BUG-4 — 'ceki sessions --json' / 'ceki my-browsers' / 'ceki search'
now serialise datetimes via 'model_dump(mode="json")' so the JSON
output is valid (started_at/ended_at as ISO strings, not Python
datetime objects).

Versions: 2.20.0 → 2.21.0 in pyproject.toml and __init__.__version__
(the latter was stale at 2.16.0).
…art/end/date/limit

Audit of `tools/list` against live /mcp/agent revealed fields present in
server schema but missing from the ceki-agent.js shim we ported. Adding
them so we don't lose info clients could legitimately want to send:

- create-contract-event: + `timezone`, `data` (arbitrary extra payload)
- comment: + `start`, `end`, `date`
- propose-correction: + `start`, `end`, `date`
- get-event-history: + `limit`

CLI mirrors with new flags: `--timezone`, `--data` (JSON), `--start`,
`--end`, `--date`, `--limit`. Existing flags untouched, backward compatible.

Tests: +9 (payload shape for new fields, parser coverage). 78 contract+cli
tests pass.

Verified smoke against clawapi.ittribe.org/mcp/agent: server accepts the
new fields (403 / validation errors only when expected for our token).

Version 2.21.0 → 2.22.0.
…task 425 BUG-1) — v2.23.0

Joe reproduced BUG-1 on prod 2026-06-18 (schedule 11722, session 2256,
signup.live.com): `ceki type ... --selector` still failed with
"ReferenceError: document is not defined" despite the v2.21.0 fix.

Root cause: the SDK ran `Runtime.evaluate(document.querySelector(...))`
before the keystrokes. On pages that register a service worker (login/
signup.live.com et al.) Chrome's CDP routes the bare Runtime.evaluate
to the service-worker execution context where `document` is undefined,
so the eval throws before focus ever runs and the typing never starts.

Drop the SDK-side Runtime.evaluate entirely. Forward `selector` inside
the existing Ceki.typeText params instead — the extension intercepts
that custom CDP method and focuses the matching element via
chrome.scripting.executeScript({target: {tabId, allFrames: true}, ...}),
which always runs in a page frame's isolated world (has `document`),
not in any SW. Cross-frame: scripting.executeScript fans out to every
frame and we accept a match in any of them, so iframe-hosted signup
forms (signup.live.com loads its email field inside an iframe) get
focused without the agent having to know which frame holds it.

Compat: needs extension ≥ 0.6.236 (handleTypeText accepts `selector`).
Older extensions ignore the new param and behave as before — selector
silently no-ops, keystrokes still dispatch but may not land. No-selector
path is unchanged on every extension version.

Tests:
- tests/test_type_keyboard_events.py: two new tests asserting
  (a) selector path forwards inside Ceki.typeText with no Runtime.evaluate,
  (b) no-selector path omits the `selector` param.
- full suite: 241 passed.
….24.0

Humanization is the default in both main and incognito sessions. Three
ways to disable it:

  1. CEKI_HUMAN_DISABLE=1 — global env, kills the humanizer SDK-wide
  2. Browser.click(x, y, human=False) — per call (also navigate/type/scroll)
  3. ceki click ... --no-human / --raw — per CLI invocation

human=False bypasses the SDK-side humanizer timings AND sends `_ceki_raw`
in the mousePressed CDP params so the extension skips mouse-jitter for
that single command. typeText receives `human: None` and runs at raw
keyboard cadence.

CLI: --no-human / --raw available on root parser and on click/type/scroll/
navigate subparsers. Old `ceki type --natural` is now silent no-op
(humanization is on by default; keep the flag for backwards compat with
existing scripts but no longer document it).
…BUG-B)

QA local-humanizer-toggle-typing was FAILing in both main and incognito
with "OFF leg has jitter stddev=39.6ms / 224.9ms — humanizer leaked into
raw call". Root cause: after task 427 the SDK made humanization default
ON, but the CLI `type` parser kept `--natural` as a silent no-op while
also no longer threading a per-call OFF for the default code path.
Result: `ceki type` always humanized, and QA's contract for `type
... --natural` (ON) vs no flag (OFF) silently broke.

Restored CLI `type` semantics:
- default              → flat keystrokes (human=False)
- --natural            → humanizer ON (human=None → SDK default profile)
- --no-human / --raw   → explicit OFF (symmetry with click/scroll/navigate)
- --no-human wins over --natural

Other commands (click, scroll, navigate) keep task 427 semantics
(default ON, --no-human → OFF) — only `type` is opt-in because typing
cadence noticeably slows scripted flows.

Tests: tests/test_cli.py +4 (default / --natural / --no-human / override
order). 41 CLI tests pass.

Version 2.24.0 → 2.25.0.
Konstantin's decision: typing must be humanized by default in both modes.
Task 428 BUG-B's real fix was the leak — `--no-human`/`human=False` not
flattening — not flipping the default. 429 keeps the leak fix but restores
default ON.

Semantics post-429:
- default (`ceki type ...`)            → humanized (SDK natural profile)
- `--no-human` / `--raw` / human=False → flat keystrokes for THIS call
- `--natural`                          → no-op alias (default is already ON)
- env `CEKI_HUMAN_DISABLE=1`           → flat everywhere (unchanged)

`_cmd_type` now delegates to `_human_flag` (same as click/scroll/navigate);
no per-command override. Help text for `--natural` is SUPPRESSed.

Tests: tests/test_cli.py updated to lock in new semantics (default →
human=None, --natural → no-op, --no-human → human=False, --no-human wins
over --natural). 41 CLI tests pass.

Version 2.25.0 → 2.26.0.
README aligned with the post-429/430 final semantics:
- typing AND mouse humanization are ON by default in both incognito and main.
- per-call disable: SDK `human=False` / CLI `--no-human` / `--raw`.
- `--natural` is a no-op alias, NOT how you turn humanization on.
- env `CEKI_HUMAN_DISABLE=1` is the global kill-switch.
- Fingerprint Tier-2 (UA/timezone/WebGL) stays OFF in main — separate from
  behavioral humanization, called out so users don't conflate them.

Updated:
- "Human Mode" section: rewrote intro, added per-call disable subsection.
- CLI command table: `type` row no longer shows `[--natural]`; `click`,
  `navigate`, `scroll` rows now show `[--no-human|--raw]`.
Lint-only changes to make ruff check green:
- ceki_sdk/_browser.py: split type() signature across multiple lines
- ceki_sdk/cli.py: wrap two long writer/argparse lines
- tests/test_contract.py: wrap ContractClient() ctor on the 4 poll cases
- tests/test_browser_screenshot_format.py, tests/test_timelog.py: drop
  unused imports, sort import block

ruff check . → All checks passed; pytest tests/ → 246 passed, 1 skipped.
@iWedmak iWedmak merged commit 2f37928 into master Jun 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant