Commit Graph
100 Commits
Author SHA1 Message Date
q792602257 cdef630e67 feat(cloud-console): add user authentication and administration 2026-07-13 17:54:53 +08:00
q792602257 035b177128 云端地址
Tests / Test passed: 545
2026-07-13 16:41:30 +08:00
q792602257 8f74eac50c Jenkins
Tests / Test passed: 545
2026-07-13 16:11:40 +08:00
q792602257 9f4641e4fc Jenkins
Tests / Test tests.test_workspace_packaging.test_workspace_distributions_own_expected_import_packages failed
2026-07-13 16:02:57 +08:00
q792602257 97f23be394 Jenkins
Tests / Test tests.test_workspace_packaging.test_workspace_distributions_own_expected_import_packages failed
2026-07-13 15:56:44 +08:00
q792602257andClaude Opus 4.6 e5ea32a44a build(pytest): use importlib import mode to fix app test collection
Tests / Test tests.test_workspace_packaging.test_workspace_distributions_own_expected_import_packages failed
apps/cloud-api/tests and apps/device-host-agent/tests both contain
test_app.py without __init__.py, so pytest's default prepend mode
collided on the bare module name and aborted collection when Jenkins
passed both paths together. Switching to importlib imports each file
by its full path and avoids the collision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 15:24:13 +08:00
q792602257andClaude Opus 4.6 8507a5a508 build(workspace): pin Aliyun PyPI mirror and refresh uv.lock
Tests / Test .apps.device-host-agent.tests.test_app failed
Set `index-url` under `[tool.uv]` so all consumers (local devs and the
Jenkins image) resolve from `mirrors.aliyun.com/pypi/simple` by default
instead of relying on the per-stage `UV_INDEX_URL` env var in Jenkinsfile.
Local overrides remain available via `UV_INDEX_URL=... uv sync`.

Re-run `uv lock` to rewrite package sources/URLs from `pypi.org` to
the Aliyun mirror; versions, hashes, and resolution are unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 15:15:49 +08:00
q792602257andClaude Opus 4.6 50f0b8ade9 feat(driver): add Android UiAutomator2 driver
Tests / Test No test results found
Register `SUPPORTED_DRIVER_TYPES["uiautomator2"]` backed by the new
`AndroidDriver`, mirroring `WDADriver` method-for-method. tap/swipe/home use
the confirmed UiAutomator2 mobile commands (`clickGesture`, `dragGesture`,
`pressKey`); swipe converts `duration_ms` to a drag speed with a zero-distance
guard. Adds 34 mocked unit tests, an env-var-gated integration test, and
corrects the outdated "Android not registered" note in the iPhone setup doc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 14:47:01 +08:00
q792602257andClaude Opus 4.6 2ccbc63d95 feat(cloud-api): bake cloud-console SPA into the image and serve at /console
Multi-stage Dockerfile: stage 1 (node:20-bookworm-slim) builds cloud-console
with vite base "/console/"; stage 2 (uv) copies dist/ to /app/console-static.
Cloud API mounts the SPA at /console via SpaStaticFiles (StaticFiles subclass
that falls back to index.html for deep-link refreshes) when the new
CLOUD_CONSOLE_STATIC_DIR env is set, and 307-redirects / to /console/. Static
files bypass bearer auth (the SPA shell is public; tokens are still required
for /v1/*). Compose enables the mount by default; local dev still uses
npm run dev + CLOUD_CONSOLE_CORS_ORIGINS.

Jenkinsfile passes mirror overrides (NODE_IMAGE, NPM_REGISTRY, UV_IMAGE,
APT_MIRROR, UV_INDEX_URL) as --build-arg, defaulting to CN mirrors
(registry.jerryyan.net, registry.npmmirror.com, registry-ghcr.jerryyan.top,
mirrors.aliyun.com) so CN builds don't time out; Dockerfile ARGs default to
official upstreams so `docker build .` still works anywhere.

Backend suite: 443 passed (-m "not integration"); cloud-console typecheck
and production build succeed with the new base path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 14:40:52 +08:00
q792602257 d673e77171 Jenkins打包
Tests / Test No test results found
2026-07-13 14:00:43 +08:00
q792602257andClaude Opus 4.6 2169bb03d9 feat(cloud-console): task listing, attempt history, CORS, and console SPA
Implements the cloud-console OpenSpec change: adds GET /v1/tasks (filterable,
bounded pagination, tasks:read) and GET /v1/tasks/{id}/attempts (404 on unknown
task) to the platform SDK, with matching CloudClient methods and a closed-by-
default CLOUD_CONSOLE_CORS_ORIGINS allow-list wired through CloudControlConfig.
Ships an independent Vue 3 + Vite SPA at cloud-console/ that authenticates with
an operator-supplied bearer token held in sessionStorage, renders tasks with
attempt history, device pool, host registry, and the plugin registry with a
registration form.

Backend test suite: 438 passed (-m "not integration"); cloud-console typecheck
and production build both succeed. PostgreSQL-backed repository tests and
manual end-to-end verification remain pending external infrastructure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-13 14:00:23 +08:00
q792602257 62923b9285 docs(openspec): confirm android-driver UiAutomator2 commands via research
Resolve the previously-open design questions in the android-driver
change by researching the appium-uiautomator2-driver docs and Appium 3
release notes:

- tap -> mobile: clickGesture
- swipe -> mobile: dragGesture (duration_ms converted to speed px/s)
- home -> mobile: pressKey (KEYCODE_HOME)
- port isolation -> appium:systemPort capability
- Appium 3 breaking changes confirmed to not affect this design

Updates design.md (Decisions/Risks/Open Questions/Migration Plan) and
tasks.md (section 1 and tasks 2.1/2.4/4.1) accordingly.
2026-07-13 13:56:04 +08:00
q792602257 e61dcca801 feat(cloud): add edge host enrollment 2026-07-13 13:54:16 +08:00
q792602257 cd56facbbf chore(openspec): add android-driver proposal 2026-07-13 12:48:09 +08:00
q792602257 960361493c chore(openspec): add cloud-console proposal 2026-07-13 11:49:34 +08:00
q792602257 ad32819ae4 Jenkins打包
Tests / Test No test results found
2026-07-13 10:47:06 +08:00
q792602257 52dae806ef Jenkins打包
Tests / Test No test results found
2026-07-13 10:42:31 +08:00
q792602257 e9006c3a29 chore(openspec): archive cloud control plane integration 2026-07-13 09:00:53 +08:00
q792602257 70d15f8f0b docs(openspec): sync cloud control plane specs 2026-07-13 08:58:04 +08:00
q792602257 ea9b15fce7 docs(project): record cloud control plane maturity 2026-07-13 08:25:43 +08:00
q792602257 7b717a05a9 test(openspec): map cloud integration scenarios 2026-07-13 08:21:44 +08:00
q792602257 ffc3df6c5b test(runtime): verify local interfaces 2026-07-13 08:11:18 +08:00
q792602257 e162ec5041 test(cloud): verify public execution outcomes 2026-07-13 08:10:11 +08:00
q792602257 1b0b3790c2 fix(host-agent): advertise connected devices 2026-07-13 08:07:45 +08:00
q792602257 22d37ca95b chore(cloud): verify integration change 2026-07-13 08:06:15 +08:00
q792602257 3c5f5509c2 docs(cloud): state deployment limitations 2026-07-13 07:59:56 +08:00
q792602257 e28705b0f2 docs(cloud): add deployment runbook 2026-07-13 07:58:24 +08:00
q792602257 90961bd0a4 fix(host-agent): load configured devices 2026-07-13 07:56:41 +08:00
q792602257 dbd70732e1 build(deploy): add cloud stack containers 2026-07-12 22:58:17 +08:00
q792602257 1f95d23beb feat(cloud-sdk): authenticate client requests 2026-07-12 22:53:34 +08:00
q792602257 16bcbf5a27 feat(cloud-sdk): expose distributed task status 2026-07-12 19:57:03 +08:00
q792602257 77fadbd9e6 test(host-agent): cover distributed recovery flows 2026-07-12 19:54:30 +08:00
q792602257 b1303569e3 feat(host-agent): shut down gracefully 2026-07-12 19:47:50 +08:00
q792602257 91f08509a7 feat(host-agent): report assignment outcomes 2026-07-12 19:09:07 +08:00
q792602257 ec1e8c20d8 feat(host-agent): renew active assignment leases 2026-07-12 19:06:22 +08:00
q792602257 6dc2803ccc feat(host-agent): execute cloud assignments 2026-07-12 18:52:32 +08:00
q792602257 ab538513b0 feat(host-agent): compose execution factories 2026-07-12 18:50:21 +08:00
q792602257 6e59f2f3ca feat(host-agent): synchronize device snapshots 2026-07-12 18:48:17 +08:00
q792602257 f6bc8f6b98 feat(host-agent): add control plane client 2026-07-12 18:44:18 +08:00
q792602257 1e10d5aa95 test(cloud-api): verify restart recovery 2026-07-12 18:40:29 +08:00
q792602257 8131c0124b feat(cloud-api): add correlated lifecycle logging 2026-07-12 18:37:51 +08:00
q792602257 a5de7399f8 feat(cloud-api): expose health readiness 2026-07-12 18:33:45 +08:00
q792602257 b8d02abf87 fix(cloud-api): retry lifecycle iterations 2026-07-12 18:31:19 +08:00
q792602257 937a4646e1 feat(cloud-api): run lifecycle workers 2026-07-12 18:26:41 +08:00
q792602257 bd261e4992 feat(cloud-api): compose control plane services 2026-07-12 18:21:57 +08:00
q792602257 09cfe54dd9 test(host-protocol): verify internal API isolation 2026-07-12 18:17:39 +08:00
q792602257 a24efc1043 feat(host-protocol): renew and complete leases 2026-07-12 18:15:04 +08:00
q792602257 c7f8d0c128 feat(host-protocol): long poll assignments 2026-07-12 18:12:33 +08:00
q792602257 efb754fbe7 feat(host-protocol): accept heartbeat snapshots 2026-07-12 18:10:42 +08:00
q792602257 b4cbd83dcd feat(host-protocol): define internal API models 2026-07-12 18:07:42 +08:00
q792602257 a05465458c test(cloud-auth): cover authorization failures 2026-07-12 18:05:30 +08:00
q792602257 7d22b5234d feat(cloud-auth): require production credentials 2026-07-12 18:02:49 +08:00
q792602257 82b66f74a2 feat(cloud-auth): bind host principals 2026-07-12 17:58:20 +08:00
q792602257 71f0a892e3 feat(cloud-auth): enforce public scopes 2026-07-12 17:56:14 +08:00
q792602257 b916b394c7 feat(cloud-auth): verify scoped bearer tokens 2026-07-12 17:45:31 +08:00
q792602257 4cb116f527 test(cloud-scheduler): prove assignment concurrency 2026-07-12 17:42:20 +08:00
q792602257 c58d9e5316 feat(cloud-scheduler): reap expired leases 2026-07-12 17:32:11 +08:00
q792602257 e1af4403f5 feat(cloud-scheduler): record terminal results 2026-07-12 17:25:57 +08:00
q792602257 1b15a8a218 feat(cloud-scheduler): renew active leases 2026-07-12 17:23:07 +08:00
q792602257 b0a5b1426f feat(cloud-scheduler): claim host assignments 2026-07-12 17:20:51 +08:00
q792602257 ac7734c7dc feat(cloud-scheduler): reserve devices atomically 2026-07-12 17:19:10 +08:00
q792602257 0a9392b7ea feat(cloud-store): persist task lease history 2026-07-12 17:15:09 +08:00
q792602257 52d2a86a4c test(cloud-store): verify repository contracts 2026-07-12 17:11:10 +08:00
q792602257 7f5f740e2a test(cloud-store): verify schema migrations 2026-07-12 16:59:08 +08:00
q792602257 24d9dbf38b feat(cloud-store): add Alembic baseline migration 2026-07-12 16:55:46 +08:00
q792602257 64aa9b39bc feat(cloud-store): support SQLite and PostgreSQL engines 2026-07-12 16:53:02 +08:00
q792602257 05a5f0bfa7 refactor(cloud-store): adopt SQLAlchemy adapter 2026-07-12 16:51:03 +08:00
q792602257 ae29477f6a feat(cloud-store): define repository contract 2026-07-12 16:48:07 +08:00
q792602257 638216d6e7 feat(cloud): validate control plane and host configuration 2026-07-12 16:45:12 +08:00
q792602257 5e98708a94 feat(host-agent): add workspace application entrypoint 2026-07-12 16:42:51 +08:00
q792602257 2af8909b29 feat(cloud-api): add workspace application entrypoint 2026-07-12 16:41:17 +08:00
q792602257 47dfc3b8c5 chore(cloud): verify workspace preconditions 2026-07-12 16:38:37 +08:00
q792602257 99ea1509ef chore(openspec): add cloud control plane proposal 2026-07-12 14:30:29 +08:00
q792602257 c94b3efe87 docs(workspace): record packaging boundary 2026-07-12 14:29:55 +08:00
q792602257 2e2ca9dc7d test(workspace): validate packaging specification 2026-07-12 14:25:58 +08:00
q792602257 a42362f4f8 test(workspace): verify runtime and console entrypoints 2026-07-12 14:25:07 +08:00
q792602257 f5cc86ccd7 test(workspace): pass full non integration suite 2026-07-12 14:23:58 +08:00
q792602257 b9e8a6dea4 test(workspace): verify isolated wheel installs 2026-07-12 14:22:53 +08:00
q792602257 d551a36f04 build(workspace): verify member wheel ownership 2026-07-12 14:21:39 +08:00
q792602257 d2f9567909 docs(workspace): document uv member workflows 2026-07-12 14:17:59 +08:00
q792602257 5db68bfa0d test(workspace): verify member scoped commands 2026-07-12 14:16:39 +08:00
q792602257 2d7ea87d74 test(workspace): verify locked clean sync 2026-07-12 14:15:51 +08:00
q792602257 77a27dece3 build(workspace): lock workspace dependencies 2026-07-12 14:14:56 +08:00
q792602257 351b2c6637 test(workspace): verify distribution boundaries 2026-07-12 14:14:09 +08:00
q792602257 00bf5ee428 test(workspace): enforce cloud package ownership 2026-07-12 14:12:51 +08:00
q792602257 bfd56fd2da refactor(cloud): move package into workspace member 2026-07-12 14:11:51 +08:00
q792602257 07b7c9fc48 build(workspace): link cloud platform to runtime 2026-07-12 14:09:54 +08:00
q792602257 b739f6d175 build(workspace): add cloud platform member 2026-07-12 14:09:08 +08:00
q792602257 10df245f66 build(workspace): initialize uv workspace root 2026-07-12 14:08:03 +08:00
q792602257 a868dd5fc8 chore(workspace): record packaging migration baseline 2026-07-12 14:06:54 +08:00
q792602257andClaude Sonnet 5 635ed91041 docs(macos-setup): add macOS migration and real iPhone control guide
Documents moving the repo from Windows/other dev machines to macOS and
driving a real iPhone end-to-end via Appium + WebDriverAgent: Xcode/Appium
toolchain setup, WDA signing (using the extra_capabilities passthrough
already supported by WDADriverConfig), minimal real-device verification,
and starting a Runtime API that explicitly registers and connects the
device (neither the Console nor REST API currently expose a
connect/disconnect endpoint). Also covers multi-device port allocation,
common failure modes, and a completion checklist. README gains a pointer
to it under a new "Operator Guides" section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 13:49:57 +08:00
q792602257andClaude Sonnet 5 61ff3b425d feat(agent-runtime): add LLM-driven AI Planner with dual-provider tool calling
Replaces the stub Planner's fixed describe_screen/[] behavior with a real
decision-maker: AIPlanner uses native tool/function calling (Anthropic or
OpenAI, pluggable via AI_PLANNER_PROVIDER) to select exactly one grounded
action per turn, with an explicit finish_task(success, reason) tool for
completion/failure instead of an ambiguous "no tool call" signal. Default
disabled (AI_PLANNER_ENABLED=false) and additive; TaskRunner falls back to
the existing stub Planner unchanged when disabled.

Amends CONSTITUTION.md's Perception Boundary with one narrow exception:
only the AI Planner may receive the current step's raw screenshot bytes
alongside Scene, for vision-grounded coordinate grounding. Also fixes a
latent gap in TaskRunner.run(): observe/plan exceptions are now caught per
iteration and turned into a failed task with a failure_reason, instead of
propagating uncaught.

openspec change: ai-planner-runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 13:48:50 +08:00
q792602257andClaude Opus 4.6 b94abde92a feat(skill-catalog-subscription): synced catalog + HTTP sync + MCP tools
Consumes the external Subscription Platform as source of truth for skill
content; reuses skills_learning domain models (extended with KnowledgeSkill)
and workflow.skill_exec resolver. HTTP/MCP deps land in api/ per
CONSTITUTION.md; synced skills use a physically separate SQLite file
(tasks/skills.sqlite3) to preserve the skill-authoring capability boundary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-07 10:33:23 +08:00
q792602257 763c1b2299 test(perception-provider): cover NullPerceptionProvider/DefaultPerceptionProvider directly
Existing tests only exercised perception.scene_builder.build_scene()
directly or substituted a FakePerceptionProvider, so the real provider
port classes had no direct coverage. Both were already correct on
manual inspection; this closes the coverage gap.

openspec: perception-provider capability, archived change device-agent-runtime-foundation
2026-07-07 08:31:14 +08:00
q792602257 a279441aee fix(skill-versioning): consume divergence_tolerance, replace vacuous catalog-isolation guard
- divergence_tolerance was loaded from config but diff_flow_versions
  never consulted it, so structural_divergence was always plain
  sequence-equality regardless of the configured tolerance. Now
  actually applied per design.md D4.
- The skill-catalog-subscription isolation guard test asserted
  'skills.catalog' not in sys.modules, which is vacuously true since
  that module doesn't exist anywhere yet. Replaced with a real check
  against store.py's actual imports.

openspec: skill-versioning capability, archived change skill-learning-runtime
2026-07-07 08:31:11 +08:00
q792602257 ca5e300289 fix(semantic-scene): preserve page/intents when all widgets are dangling, drop dead exception-name set
- _with_known_widgets_only used to discard the entire SemanticScene
  (returning None) when filtering dangling-element-id widgets left an
  empty list, losing valid page/intents. Now returns a SemanticScene
  with widgets=[] instead, matching design.md's 'dropped, not a hard
  failure' intent.
- Removed _SDK_UNAVAILABLE_EXCEPTION_NAMES, dead code left over from an
  abandoned name-based exception-matching approach; the blanket except
  Exception already maps every SDK failure correctly.

openspec: semantic-scene capability, archived change semantic-scene-runtime
2026-07-07 08:31:03 +08:00
q792602257 725bf4cd9c fix(plugin-system): implement register_driver_type and stop discover() aborting on one bad manifest
- driver/registry.py never actually defined register_driver_type, so
  the plugin-system's 'driver plugin registered successfully' scenario
  was unreachable in production (only simulated via test monkeypatch).
  Added a real implementation wired into the existing driver factory
  registry.
- PluginRegistry.discover() only caught PluginValidationError/
  DuplicatePluginError, so a driver-kind manifest that failed wiring
  (DriverRegistryUnavailableError/PluginTargetResolutionError) aborted
  the whole scan, silently skipping co-located tool/skill manifests.
  Now caught and skipped per manifest instead.

openspec: plugin-system capability, archived change cloud-runtime
2026-07-07 08:31:00 +08:00
q792602257 031b929067 fix(multi-agent-collaboration): compose TaskRunner bookkeeping instead of forking it
CollaborativeTaskRunner hand-rolled its own step loop instead of
composing runtime/task.py's TaskRunner as design.md D3 requires,
silently dropping Timeline recording, WorldModel wiring,
TaskMetadataStore sync, and the on_task_succeeded/skill-synthesis
hook. Now calls TaskRunner's shared _start_world_view/
_record_step_result helpers for that bookkeeping. Also fixes
pre_observation being reused stale across steps in a multi-step plan
instead of refreshing to the prior step's post-observation.

openspec: multi-agent-collaboration capability, archived change multi-agent-runtime
2026-07-07 08:30:57 +08:00
q792602257 49ad589c2d fix(semantic-scene): move describe_screen_semantic out of the tools/ layer
tools/ is a Hexagonal inner layer that must never depend on LLM
concerns (ADR 0002), but describe_screen_semantic.py imported
semantic.enricher, which pulls in the Anthropic client by default.
Relocated the wrapper to runtime/, which is where LLM-dependent code
is allowed to live; updated the tool registry and all test imports
accordingly. No behavior change.

openspec: semantic-scene capability, archived change semantic-scene-runtime
2026-07-07 08:30:49 +08:00
q792602257 1bdc3784dc fix(device-pool): scope primary key by host and thread through capability_tags
- pooled_devices primary key changed from device_id alone to
  (host_id, device_id), so two hosts reporting the same local
  device_id no longer crash sync_host_devices() with an uncaught
  sqlite3.IntegrityError.
- Device gained a capability_tags field so DevicePool.sync_host_devices()
  can actually populate PooledDevice.capability_tags from a real host
  sync instead of always falling back to an empty list.

openspec: device-pool capability, archived change cloud-runtime
2026-07-07 08:30:47 +08:00