Reference
Persistent, humanized discovery before replay.
Keeper is a persistent WSession controlled through a local Unix socket. It is the discovery and authenticated-session surface used to prove a journey before its successful action history becomes a reusable trajectory.
What keeper is#
weles keeper start --session SESSION_ID owns the browser context and serves structured commands at ~/.weles/keeper/SESSION_ID/socket. The shared trajectory transport sends one JSON object per line and receives one JSON answer per command. Synchronous trajectory clients use src/trajectories/_shared/keeper/action.mjs with SESSION set and the JSON command on stdin; they do not attach the separate Tencent helper.
Run weles keeper status --session SESSION_ID to ask the live owner for its URL, state and current action. A missing socket reports stopped; an existing but unreachable socket is a connection refusal, not proof of a running browser. Run weles keeper stop --session SESSION_ID to close the owned WSession and finish its capture before the stop result. Both commands return JSON and never launch a replacement browser.
Socket status and stop actions provide the same lifecycle operations. Browser commands execute in arrival order across connections; status remains available during an action, and stop interrupts the session rather than waiting behind that action. Commands queued when stopping begins are refused without being executed. SIGINT, SIGTERM and page closure share the same evidence-finalizing cleanup; a page crash or server failure retains its cause.
A socket-listen failure after the browser starts closes the newly owned WSession and retains the actual bind error. It removes only a socket this owner successfully bound; it never deletes another session's socket to make startup succeed.
The authenticated worker API exposes GET /keepers, GET /keepers/SESSION_ID and POST /keepers/SESSION_ID/stop. List results include each session's actual owner response or its connection refusal; a directory or socket alone is not reported as running. Session paths and symbolic-link control paths are refused. Missing owner sockets report stopped for status; stopping a missing session is a refusal with the attempted path and cause.
In Weles Desktop, Authorization > Keeper sessions reads this API on the selected worker, displays its endpoint, current page, action and state, and offers Refresh keepers and Stop keeper. It does not treat this Mac's local keeper directories as remote owner health and never launches a browser on the workstation. A failed request remains visible with the operation, endpoint and cause.
Run node tests/keeper/api.mjs on the Stado-selected dedicated host with WELES_BIN, WELES_WORKER_API_BASE, WELES_WORKER_TOKEN and WELES_GATEWAY_SOURCE_REVISION supplied through the normal worker configuration. Before starting a browser, it records the actual worker identity and requires the selected host and source revision. The journey starts an isolated real keeper, reads it through the authenticated API, confirms an unauthenticated stop is refused without stopping it, then stops it and checks owner exit and socket removal. Repeated stop is refused. Reports and recordings stay under build/real-tests/keeper-api. The existing node tests/keeper/actions.mjs journey covers the CLI and humanized page actions.
The native client journey is in weles-desktop/tests/keeper/Lifecycle.swift, registered as WelesKeeperTests. Run swift test --filter deployedKeeperLifecycle on the dedicated worker host with the normal worker connection, WELES_BIN, WELES_TEST_WORKER_REVISION and WELES_TEST_DESKTOP_REVISION. It checks the serving host and worker revision before starting its isolated owner, exercises the same list and stop client used by Authorization, verifies owner exit and socket removal, and checks that a repeated stop retains the operation, endpoint, session and missing-socket cause. Observations, process errors and recordings remain under .build/keeper-lifecycle.
The Google Ads keyword-planner trajectory and its API facade use the shared keeperRequest transport. A complete JSON reply, an unreadable reply, connection failure or the peer ending its connection settles the request; a peer that closes before replying does not leave these callers pending. The facade asks the socket for its URL instead of accepting or rejecting readiness solely from a socket-file check. Its keeper_not_ready report retains the socket and actual request-error message. Neither caller starts a replacement keeper.
The ChatGPT, Claude and Kimi subscription collectors and keeper_google_sso_account use the shared keeperRequest transport. An unavailable session is a failed request whose error names the attempted command, socket and underlying connection failure. It is not evidence that authentication, browser reading or subscription persistence succeeded.
Keeper is not a second production scheduler and not blanket permission to improvise against a website. It is a controlled discovery mode; production still resolves an approved trajectory and verified browser release.
Explicit subscription identities#
Claude and Kimi billing collectors require the billed account and its exact service credential. Neither collector supplies a private account or credential by default. Run collection only on the Stado-selected dedicated host with an already authorized keeper session.
From the Weles checkout, use node src/trajectories/subscriptions/collect_claude_billing_from_keeper.mjs --session SESSION_ID --account ACCOUNT --service-credential-id ITEM_ID, or the corresponding collect_kimi_subscription_from_keeper.mjs entrypoint. SUBSCRIPTION_ACCOUNT can supply the billed account; --service-credential-id remains explicit.
Missing account input returns missing --account or SUBSCRIPTION_ACCOUNT. A missing credential reference returns missing --service-credential-id. These refusals occur before asking the keeper to read a page or writing a subscription. Explicit input chooses the requested identity; it does not prove the open provider page is authenticated as that account.
The credential helper also requires its destination: node src/lib/service_credentials.mjs ensure-kimi-google-sso ITEM_ID SOURCE_CREDENTIAL_ID. An optional final EMAIL overrides the source credential's account address. Omitting ITEM_ID is refused before any vault read or write. Existing stored items are not renamed or migrated by this input change.
Subscription record observations#
The keeper billing collectors write a service subscription record only after reading their selected provider page. The row must name service_name, provider, account_identifier, status and last_verified_at as nonempty strings; last_verified_at must be a date-time supplied by the collector. Upsert refuses missing or malformed observation fields before touching Skarbiec, rather than manufacturing an unknown status or a current verification timestamp. An unknown status explicitly stated by a collector stays unknown.
The internal record CLI is node src/lib/service_subscriptions.mjs list, get ID, upsert '<row-json>', update ID '<patch-json>' or delete ID. A status change through update also requires last_verified_at in the same patch; an unrelated patch retains the previous observation. Get, update and delete refuse a record id not found in the service-subscription inventory instead of touching another vault item. The row is persisted in Skarbiec exactly with the supplied status and observation time; updated_at marks the write, not verification.
node tests/subscriptions/record.mjs initializes a real isolated Skarbiec vault under build/real-tests/, exercises missing and invalid observation refusals, then creates, reads, updates and deletes a record through this CLI and checks the stored state after each operation. It retains the source revision, dependency digest, commands, exit statuses, stderr and vault path. This tests record semantics and does not claim that a provider subscription is active; billing collection still requires a real authenticated keeper journey on the dedicated host.
Standalone Tencent helper lifecycle#
src/keeper/keeper.mjs is the separate Tencent helper served through CDP port 9223, not the socket-based keeper interface above. Run it only on the Stado-selected dedicated host. Its existing verified Chromium resolver remains authoritative; this helper does not choose an unverified executable or replace the production scheduler.
The helper keeps its own context until SIGINT/SIGTERM or an observed browser failure, rather than awaiting an unresolved promise. It disables the native launch, navigation and action time limits for that newly owned context. KEEPER_CONTEXT_CLOSED, KEEPER_PAGE_CLOSED and KEEPER_PAGE_CRASHED identify unexpected lifecycle events and retain the portal, page URL and CDP port. KEEPER_NAVIGATION_REFUSED includes the unsuccessful HTTP status; navigation exceptions propagate instead of producing a ready message. Only an absent cookie-jar file is optional: read, JSON and cookie-injection errors remain failures.
An operator stop closes the owned context and allows an interrupted startup to settle. Completion removes page, context and signal observers and awaits the same context-close operation. The ready message describes a loaded browser page, not an authenticated Tencent session. The separate CDP attachment helper keeps its inherited time limits.
Interaction commands#
| Command | Arguments | Effect |
|---|---|---|
humanclick | x, y | Click through the humanized pointer atom. |
humanmove | x, y | Move the pointer through the humanized motion atom. |
humanscroll | totalDeltaY | Scroll the stated pixel distance (positive down, negative up) in small wheel events, observing the rendered page after each; a missing or non-numeric distance is refused. |
type | text | Type through the active element. |
press | key | Send one keyboard key. |
click | selector | Click the first matching visible element through the humanized pointer atom. |
fill | selector, text | Fill the first matching visible control through the humanized keyboard atom. An explicit empty text clears it; missing or non-string text is refused. |
shows | question | Ask the vision model, through Jeden's Brama route, whether the settled page shows what the yes/no question describes; answers { ok: true, shows }. Trajectories use it instead of matching page wording, so a page in another language or with reworded copy is still read. |
click_described | description | Ask the vision model where the control the description names is on the settled page and click it through the humanized pointer atom; answers { ok: true, x, y }, or { ok: false, error } naming the description and the page when the model finds no such control. |
Diagnostics and refusals#
| Command | Arguments | Effect |
|---|---|---|
eval | js | Evaluate JavaScript in the active page and return its result. Use the interaction commands for browser input. |