M1 PR3: thomas cloud login / whoami / sync / logout#30
Merged
thomas-supervisor merged 1 commit intomainfrom May 5, 2026
Merged
M1 PR3: thomas cloud login / whoami / sync / logout#30thomas-supervisor merged 1 commit intomainfrom
thomas-supervisor merged 1 commit intomainfrom
Conversation
Federated CLI surface for the thomas-cloud SaaS. Per the design, Claude
Code drives thomas-cloud only via these commands — the device token
never leaks into Claude Code's context, the same SKILL.md semantics
(JSON envelope, error codes) extend to the cloud surface.
src/cloud/
types.ts DeviceBegin/Poll wire shapes, CloudIdentity, CloudSnapshot
identity.ts r/w ~/.thomas/cloud.json (device token + workspace, 0600)
cache.ts r/w ~/.thomas/cloud-cache.json (last /v1/sync snapshot)
client.ts fetch wrapper: device-token auth, timeout, ThomasError
mapping (E_CLOUD_UNAUTHORIZED / _UNREACHABLE / _TIMEOUT)
device.ts RFC 8628-shaped begin + poll loop, respects server interval
sync.ts one-shot /v1/sync → snapshot → write cache + lastSyncAt
src/commands/cloud/
login.ts interactive: prints user_code + URL, polls until approved
logout.ts local-only: clear cloud.json (server revoke = future PR)
whoami.ts local-only: render cloud.json contents (loggedIn?, ws, ...)
sync.ts drive sync.ts → emit summary
src/cli.ts cloud dispatch + help text (already in commit 2)
src/config/paths.ts paths.cloud + paths.cloudCache
Default base URL https://thomas.trustunknown.com. THOMAS_CLOUD_BASE_URL
env override (or --base-url flag on login) for local dev / private deploy.
login is the only non-JSON command (intrinsically interactive long-poll);
whoami / sync / logout all support --json with stable schemas
(CloudWhoamiData / CloudSyncData / CloudLogoutData declared in commit 2).
Tests: 5 cases against an in-process fake cloud server — happy path
(login → whoami → sync → logout), already-logged-in check, sync without
login, whoami when not logged in, sync against revoked token (401).
Real-machine E2E walkthrough: registered a user on thomas-cloud, drove
the full flow with the just-built CLI, verified cloud.json on disk +
Bearer auth on /v1/sync + lastSyncAt updates + idempotent logout.
SKILL.md adds "Driving thomas-cloud (optional SaaS)" section + new
E_CLOUD_* error codes in the troubleshooting table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wires the local thomas CLI to the thomas-cloud SaaS. Federated design — Claude Code (or any other agent driving thomas) never holds the SaaS API key directly; it goes through
thomas cloud …verbs that own the device token in~/.thomas/cloud.json.CLI surface
loginis interactive (no --json) — it prints a verification URL + 8-char user code, then long-polls/v1/devices/polluntil the user approves in their browser. The other three speak the JSON envelope (CloudWhoamiData / CloudSyncData / CloudLogoutData).THOMAS_CLOUD_BASE_URLenv override +--base-urlflag let you point at a local thomas-cloud (http://localhost:8000) for dev. Default is https://thomas.trustunknown.com.What's in the box
New error codes
E_CLOUD_NOT_LOGGED_IN,E_CLOUD_UNAUTHORIZED,E_CLOUD_UNREACHABLE,E_CLOUD_TIMEOUT— all documented in SKILL.md with remediation.Tests
5 new tests in
tests/cloud.test.tsagainst an in-process fake cloud server that mirrors the realapps/apiendpoints:E_CLOUD_NOT_LOGGED_INloggedIn: false(exit 0, no error)E_CLOUD_UNAUTHORIZED(simulates revoked token)256 / 256 tests pass, typecheck clean, build 184 KB.
Real-machine E2E
Drove the full flow against a running
apps/api(the thomas-cloud repo's PR2):thomas cloud whoami→loggedIn: false✓thomas cloud login --base-url http://127.0.0.1:8000→ prints user_code, pollscurl /v1/devices/_dev/approve(acting as web user via DEV_MODE bypass)~/.thomas/cloud.jsonwritten with deviceToken + workspaceId ✓thomas cloud sync→ empty snapshot stored in cloud-cache.json ✓thomas cloud whoami→lastSyncAtpopulated ✓thomas cloud logout→wasLoggedIn: true, cloud.json deleted ✓thomas cloud logout(idempotent) →wasLoggedIn: false✓Each
/v1/syncrequest carriedAuthorization: Bearer <token>correctly.Depends on
trustunknown/thomas-cloudPR 请问OpenGuardrails-Text-2510是基于Qwen2.5模型的吗 #1 (self-hosted auth + device-approve) — already merged-readyOut of scope
runs.jsonlupward (M3 — needs/v1/runs/batchon the cloud)🤖 Generated with Claude Code