@xopcai/xopc 0.0.411 → 0.0.413
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-sGJnq-SA.js → agent-avatar-dicebear-BT6oqCMD.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BFmRRD_4.js +1 -0
- package/dist/gateway/static/root/assets/agents-Clwskw2J.js +77 -0
- package/dist/gateway/static/root/assets/{agents-admin-api-uGDeJR1f.js → agents-admin-api-CeBkHnl8.js} +1 -1
- package/dist/gateway/static/root/assets/app-management-settings-panel-BdCgtTfT.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-C3-hCFlF.js +1 -0
- package/dist/gateway/static/root/assets/apps-page-DJbKg3va.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CGdU_E5B.js → archive-plugin-BviFGCy4.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-BAPZY56r.js → automations-page-CfzazXE5.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-D_X0_1X3.js +8 -0
- package/dist/gateway/static/root/assets/{binary-plugins-B0ispXo2.js → binary-plugins-Dp-gtJEk.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-B9Ci-prO.js → block-editor-D-DEPdpi.js} +4 -4
- package/dist/gateway/static/root/assets/{browser-automation-inputs-D4-vQc4g.js → browser-automation-inputs-D1pDgMJu.js} +1 -1
- package/dist/gateway/static/root/assets/browser-automations-page-BI-ep6uM.js +3 -0
- package/dist/gateway/static/root/assets/browser-settings-page-BTyQ4-vN.js +3 -0
- package/dist/gateway/static/root/assets/capabilities-Bpeh6E22.js +1 -0
- package/dist/gateway/static/root/assets/capabilities-page-GNS95mG9.js +2 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DBiv1HN-.js +8 -0
- package/dist/gateway/static/root/assets/{capability-header-actions-Cic05oWh.js → capability-header-actions-CP2fI9Fe.js} +1 -1
- package/dist/gateway/static/root/assets/channels-DWlrUKBr.js +1 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-Bm-IGeDe.js → chat-terminal-dock-BL3tFntQ.js} +2 -2
- package/dist/gateway/static/root/assets/computer-settings-page-DEQ5GYfC.js +1 -0
- package/dist/gateway/static/root/assets/{connector-service-page-DcdASUU9.js → connector-service-page-CecwMnC5.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-BcpUvrtl.js +1 -0
- package/dist/gateway/static/root/assets/{date-picker-gVLWu9R0.js → date-picker-N5jnlvPs.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-C0LARyok.js → dependency-picker-C4jddxFl.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-v_le3sCs.js → desktop-pet-BCbiUNQG.js} +1 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-CkKU0JMY.js +1 -0
- package/dist/gateway/static/root/assets/device-pairing-wizard--E2GQoY1.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CBefVUPR.js → directory-picker-path-field-CbMbPimD.js} +1 -1
- package/dist/gateway/static/root/assets/extension-debug-page-OE1nJMQ8.js +1 -0
- package/dist/gateway/static/root/assets/{extension-page-C0-oP7S_.js → extension-page-BEomiKhy.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-BFOjQeSX.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-config-swr-BVqYVaGy.js → gateway-config-swr-DF_HV6NP.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-realtime-CY1LBGBS.js → gateway-realtime-DMAzNlm-.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-CoIgC1Al.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-CR4CsfNN.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-CPKGkr6D.js → gateway-startup-retry-B91CBTzO.js} +1 -1
- package/dist/gateway/static/root/assets/highlight-code-6ONPRH5r.js +1 -0
- package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-CN5PNRN3.js → highlighted-body-KQOG7T2V-EJ9Kg4iQ.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-B9w3a6r2.js +2 -0
- package/dist/gateway/static/root/assets/{image-generation-api-DRLXG9Ws.js → image-generation-api-D0HNic89.js} +1 -1
- package/dist/gateway/static/root/assets/imports-page-bxTINe-2.js +1 -0
- package/dist/gateway/static/root/assets/index-D1oivYvN.js +710 -0
- package/dist/gateway/static/root/assets/index-D_7q1UkI.css +1 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-Cmi-2SIh.js +1 -0
- package/dist/gateway/static/root/assets/local-app-workbench-page-NGCmXpa_.js +1 -0
- package/dist/gateway/static/root/assets/{local-apps-page-BChE6nUD.js → local-apps-page-jgaogH9Z.js} +1 -1
- package/dist/gateway/static/root/assets/{local-session-drafts-BCaC-1rH.js → local-session-drafts-DvmwiLUx.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-DAgpZxAS.js → locale-store-JogcXC4J.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-drrr-c2W.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-Dc1opXVr.js +1 -0
- package/dist/gateway/static/root/assets/{media-plugins-BmLuWkkZ.js → media-plugins-BRS9puQV.js} +1 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-B--edI1I.js +1 -0
- package/dist/gateway/static/root/assets/{messages-DMXSIJUO.js → messages-RzwPiQNL.js} +2 -2
- package/dist/gateway/static/root/assets/note-detail-panel-C3s4-ytB.js +5 -0
- package/dist/gateway/static/root/assets/note-image-lightbox-D7q5-KuS.js +1 -0
- package/dist/gateway/static/root/assets/notes-page-qVxoErUS.js +8 -0
- package/dist/gateway/static/root/assets/{page-context-capture-button-B7rDRlS7.js → page-context-capture-button-D_VOB2_C.js} +1 -1
- package/dist/gateway/static/root/assets/personal-page-B-T8NzQo.js +1 -0
- package/dist/gateway/static/root/assets/personal-settings-panel-BKA4fbYH.js +1 -0
- package/dist/gateway/static/root/assets/phone-dependencies-card-DhEsfEUN.js +1 -0
- package/dist/gateway/static/root/assets/phone-model-picker-J4mFDk21.js +1 -0
- package/dist/gateway/static/root/assets/phone-settings-page-BLflz8CQ.js +1 -0
- package/dist/gateway/static/root/assets/plugin-details-dialog-1BwvmKZi.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-Z4NM7_Dn.js → preview-open-alternatives-DaTaSKox.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-C5TcIaJj.js → product-open-page-wYlC0IVZ.js} +1 -1
- package/dist/gateway/static/root/assets/{product-preview-content-5xID6jIj.js → product-preview-content-Bxur-p6S.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-DwowzxzD.js +7 -0
- package/dist/gateway/static/root/assets/projects-page-Bwvn4i_e.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-B6kqbuw8.js → registry-api-BGqSwITi.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-DrHisOUe.js +1 -0
- package/dist/gateway/static/root/assets/runtime-tools-api-CSm5SpnR.js +5 -0
- package/dist/gateway/static/root/assets/runtime-tools-settings-panel-DXNidliV.js +1 -0
- package/dist/gateway/static/root/assets/{schema-form-DtPkiYwm.js → schema-form-DeSZ7SiO.js} +1 -1
- package/dist/gateway/static/root/assets/{session-api-D6-Vz8xK.js → session-api-DTxZyMta.js} +1 -1
- package/dist/gateway/static/root/assets/{session-manager-DWbcMHcj.js → session-manager-B2HDomj4.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-seP7t0kv.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-VqBu7gHu.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-DD3iD5t5.js → settings-form-section-BNYQjZU4.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-iLY3LRDq.js → settings-page-29Ix0jSQ.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-Dx5S-Msc.js → settings-page-header-CYpuTXUW.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-layout-B20AfmFG.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-COQZgKX9.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-B8h9lqDL.js → share-preview-page-CwMRULXc.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BWaykw9E.js +1 -0
- package/dist/gateway/static/root/assets/{skill-api-C9RsrvOp.js → skill-api-xfB5fUAh.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Bq-3_MCt.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-C3LfWsAZ.js +2 -0
- package/dist/gateway/static/root/assets/system-settings-panel-DRJu1ehM.js +1 -0
- package/dist/gateway/static/root/assets/task-detail-page-XCJFP-Aa.js +2 -0
- package/dist/gateway/static/root/assets/{task-result-evidence-DjNWPpn_.js → task-result-evidence-C4o_Yue_.js} +1 -1
- package/dist/gateway/static/root/assets/tracing-settings-page-CYslh-Pu.js +1 -0
- package/dist/gateway/static/root/assets/{tunnel-api-C6XfsSWy.js → tunnel-api-Bi_kw6bv.js} +1 -1
- package/dist/gateway/static/root/assets/usage-page-CTTmklCs.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-DO-Twmbm.js → use-autosave-BkB9uD93.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-BDqguzRE.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-MGe3HkS_.js → voice-api-key-field-bt8_Agih.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-CPKzUb4R.js → voice-config-api-D7xsWrdh.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-DptHqYh5.js → work-discovery-overlay-D3NG14dX.js} +1 -1
- package/dist/gateway/static/root/assets/{xopc-cloud-account-card-Y_UIryBH.js → xopc-cloud-account-card-B2OUKOQi.js} +1 -1
- package/dist/gateway/static/root/index.html +14 -14
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/capabilities.js +1 -0
- package/dist/src/agent/agent-manager.js +1 -1
- package/dist/src/agent/background-review/prompts.js +4 -3
- package/dist/src/agent/background-review/run-background-review.js +1 -1
- package/dist/src/agent/embedded/run-turn.js +1 -1
- package/dist/src/agent/embedded/xopc-stream-bridge.js +7 -3
- package/dist/src/agent/external-tools/cliProvider.js +2 -2
- package/dist/src/agent/external-tools/composio-provider.js +2 -2
- package/dist/src/agent/external-tools/endpoint-provider.js +2 -2
- package/dist/src/agent/prompt/system-prompt.js +1 -1
- package/dist/src/agent/service.js +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/tool-manuals/index.js +7 -0
- package/dist/src/agent/tool-manuals/phone.d.ts +1 -0
- package/dist/src/agent/tool-manuals/phone.js +16 -0
- package/dist/src/agent/tools/factory.js +15 -0
- package/dist/src/agent/tools/knowledge-memory-tool.js +1 -1
- package/dist/src/agent/tools/personal-read-tool.js +1 -1
- package/dist/src/agent/tools/personal-task-tool.d.ts +2 -1
- package/dist/src/agent/tools/personal-task-tool.js +43 -20
- package/dist/src/agent/tools/phone-use-tool.d.ts +8 -0
- package/dist/src/agent/tools/phone-use-tool.js +49 -0
- package/dist/src/agent/tools/session-recall-tool.js +2 -1
- package/dist/src/agent/tools/session-search-tool.js +11 -5
- package/dist/src/agent/tools/user-context-tool.js +1 -1
- package/dist/src/agent/tools/xopc-use-tool.js +2 -2
- package/dist/src/agent-catalog/capabilities.js +2 -2
- package/dist/src/agent-catalog/repository.js +5 -1
- package/dist/src/agent-config/index.js +2 -2
- package/dist/src/agent-config/resolver.js +5 -0
- package/dist/src/agent-config/schema.d.ts +24 -0
- package/dist/src/agent-config/schema.js +5 -2
- package/dist/src/automations/capabilities/drafts.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +1 -1
- package/dist/src/capabilities/runtime/dispatcher.js +1 -1
- package/dist/src/capabilities/runtime/product.js +1 -1
- package/dist/src/cli/command-manifest.js +1 -1
- package/dist/src/cli/commands/agents.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard/model.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/runtime.js +32 -4
- package/dist/src/cli/utils/init-workspace-core.js +6 -4
- package/dist/src/computer/diagnostics.d.ts +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/config-file.d.ts +6 -0
- package/dist/src/config/config-file.js +35 -0
- package/dist/src/config/loader.js +5 -4
- package/dist/src/config/models-json.d.ts +36 -0
- package/dist/src/config/models-json.js +9 -0
- package/dist/src/config/schema.d.ts +12 -0
- package/dist/src/config/schema.js +6 -2
- package/dist/src/execution-environments/local-worktree-manager.js +1 -1
- package/dist/src/extensions/agent-plugins/skill-catalog.js +1 -1
- package/dist/src/extensions/agent-plugins/validation.js +1 -1
- package/dist/src/gateway/agent-builtin-tools.d.ts +7 -1
- package/dist/src/gateway/agent-builtin-tools.js +7 -0
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/global-defaults-admin.js +2 -0
- package/dist/src/gateway/hono/lib/config-payload.d.ts +8 -0
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/models.js +3 -0
- package/dist/src/gateway/hono/routes/notes.js +2 -2
- package/dist/src/gateway/hono/routes/phone-dependencies.d.ts +3 -0
- package/dist/src/gateway/hono/routes/phone-dependencies.js +137 -0
- package/dist/src/gateway/hono/routes/phone.d.ts +4 -0
- package/dist/src/gateway/hono/routes/phone.js +445 -0
- package/dist/src/gateway/hono/routes/projects.js +2 -2
- package/dist/src/gateway/hono/routes/runtime-tools.js +2 -0
- package/dist/src/gateway/hono/routes/session-input-handler.js +1 -1
- package/dist/src/gateway/hono/routes/tasks.js +1 -1
- package/dist/src/gateway/image-generation-setup.js +1 -1
- package/dist/src/gateway/server.js +1 -0
- package/dist/src/gateway/service/config-coordinator.d.ts +8 -0
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +17 -12
- package/dist/src/gateway/xopc-cloud-capability-setup.js +1 -1
- package/dist/src/home-intelligence/host.js +2 -2
- package/dist/src/imports/importService.js +1 -1
- package/dist/src/knowledge-memory/repository.js +5 -0
- package/dist/src/local-apps/service.js +1 -1
- package/dist/src/memory-learning/forget-capture.js +12 -3
- package/dist/src/memory-learning/forgotten-history.d.ts +4 -0
- package/dist/src/memory-learning/forgotten-history.js +71 -4
- package/dist/src/memory-learning/work-capture.d.ts +1 -0
- package/dist/src/memory-learning/work-capture.js +28 -17
- package/dist/src/personal-agent/prompts.js +3 -2
- package/dist/src/personal-agent/service.js +1 -1
- package/dist/src/phone/adb-driver.d.ts +35 -0
- package/dist/src/phone/adb-driver.js +368 -0
- package/dist/src/phone/cloud-adb.d.ts +34 -0
- package/dist/src/phone/cloud-adb.js +223 -0
- package/dist/src/phone/cloud-phones.d.ts +29 -0
- package/dist/src/phone/cloud-phones.js +141 -0
- package/dist/src/phone/cloud-scrcpy.d.ts +20 -0
- package/dist/src/phone/cloud-scrcpy.js +122 -0
- package/dist/src/phone/contracts.d.ts +204 -0
- package/dist/src/phone/contracts.js +115 -0
- package/dist/src/phone/interactive-session.d.ts +34 -0
- package/dist/src/phone/interactive-session.js +164 -0
- package/dist/src/phone/live-provider.d.ts +57 -0
- package/dist/src/phone/live-provider.js +18 -0
- package/dist/src/phone/live-state.d.ts +28 -0
- package/dist/src/phone/live-state.js +71 -0
- package/dist/src/phone/model-adapter.d.ts +25 -0
- package/dist/src/phone/model-adapter.js +340 -0
- package/dist/src/phone/repository.d.ts +19 -0
- package/dist/src/phone/repository.js +100 -0
- package/dist/src/phone/runtime.d.ts +83 -0
- package/dist/src/phone/runtime.js +896 -0
- package/dist/src/phone/scrcpy.d.ts +62 -0
- package/dist/src/phone/scrcpy.js +345 -0
- package/dist/src/phone/stream-gateway.d.ts +26 -0
- package/dist/src/phone/stream-gateway.js +210 -0
- package/dist/src/process/phone-process.d.ts +4 -0
- package/dist/src/process/phone-process.js +38 -0
- package/dist/src/providers/extension-stream-bridge.js +8 -1
- package/dist/src/providers/model-call.js +6 -2
- package/dist/src/providers/model-catalog-persistence.js +3 -0
- package/dist/src/providers/model-catalog-types.d.ts +3 -0
- package/dist/src/providers/model-reference-auditor.js +5 -1
- package/dist/src/providers/model-registry.js +3 -0
- package/dist/src/runtime-tools/archive.d.ts +2 -2
- package/dist/src/runtime-tools/archive.js +1 -1
- package/dist/src/runtime-tools/downloader.d.ts +2 -2
- package/dist/src/runtime-tools/errors.d.ts +3 -3
- package/dist/src/runtime-tools/phone-dependencies.d.ts +40 -0
- package/dist/src/runtime-tools/phone-dependencies.js +188 -0
- package/dist/src/runtime-tools/phone-dependency-catalog.d.ts +16 -0
- package/dist/src/runtime-tools/phone-dependency-catalog.js +51 -0
- package/dist/src/runtime-tools/types.d.ts +1 -0
- package/dist/src/storage/sqlite/context-extraction-repository.d.ts +1 -0
- package/dist/src/storage/sqlite/context-extraction-repository.js +12 -2
- package/dist/src/tasks/capabilities/read.js +2 -2
- package/dist/src/tasks/capabilities/write.js +1 -1
- package/dist/src/tasks/task-research-result.js +19 -2
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- package/dist/src/usage/cloud-receipts.d.ts +5 -1
- package/dist/src/usage/cloud-receipts.js +10 -4
- package/dist/src/usage/cloud-wire.d.ts +37 -0
- package/dist/src/usage/cloud-wire.js +181 -0
- package/dist/src/usage/recorder.js +5 -4
- package/dist/src/user-model/capture/semantic.js +2 -2
- package/dist/src/voice/platform-catalog.d.ts +3 -0
- package/dist/src/voice/realtime/persona-context.js +1 -1
- package/package.json +9 -4
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BI7OTo-T.js +0 -1
- package/dist/gateway/static/root/assets/agents-RIXL0p7_.js +0 -77
- package/dist/gateway/static/root/assets/app-management-settings-panel-BGzhSS6H.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-DtEZq4b2.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-DZqBof2D.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-CI-yQnSj.js +0 -8
- package/dist/gateway/static/root/assets/browser-automations-page-Xzdif447.js +0 -3
- package/dist/gateway/static/root/assets/browser-settings-page-Bhu-83eC.js +0 -3
- package/dist/gateway/static/root/assets/capabilities-B9b5KCBb.js +0 -1
- package/dist/gateway/static/root/assets/capabilities-page-BIc0Zlsw.js +0 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-CQEfpLdj.js +0 -8
- package/dist/gateway/static/root/assets/channels-DQbEoo_o.js +0 -1
- package/dist/gateway/static/root/assets/computer-settings-page-C9xMfwXK.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-eh-xpMPK.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-B_-j-D4W.js +0 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-CxG_FcQ6.js +0 -1
- package/dist/gateway/static/root/assets/extension-debug-page-CTe7Molh.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-DYJmgMUg.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-Dafw95ol.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-C13P4lQd.js +0 -1
- package/dist/gateway/static/root/assets/highlight-code-CslBKXdS.js +0 -1
- package/dist/gateway/static/root/assets/home-page-B_XrkGmA.js +0 -2
- package/dist/gateway/static/root/assets/imports-page--cuF5vwU.js +0 -1
- package/dist/gateway/static/root/assets/index-6YjAt50A.js +0 -710
- package/dist/gateway/static/root/assets/index-caRW34rg.css +0 -1
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-5g2PxgBs.js +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-B6U1HQpp.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-B9mq03SK.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-2W_qJsha.js +0 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-61Il2HQR.js +0 -1
- package/dist/gateway/static/root/assets/note-detail-panel-C4gZsNBL.js +0 -5
- package/dist/gateway/static/root/assets/note-image-lightbox-XqJIaaRV.js +0 -1
- package/dist/gateway/static/root/assets/notes-page-Chdwrjni.js +0 -8
- package/dist/gateway/static/root/assets/personal-page-mQl9BftY.js +0 -1
- package/dist/gateway/static/root/assets/personal-settings-panel-BgcJLmZN.js +0 -1
- package/dist/gateway/static/root/assets/plugin-details-dialog-FvwSxZPB.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-CGOyx7zX.js +0 -7
- package/dist/gateway/static/root/assets/projects-page-Dd11tbRX.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-Bintk9nL.js +0 -1
- package/dist/gateway/static/root/assets/runtime-tools-api-CY3R7unQ.js +0 -5
- package/dist/gateway/static/root/assets/runtime-tools-settings-panel-CbmiiLsq.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BBlEP7y3.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BCQUth4g.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-v4P0IAkG.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-DMzej6z2.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-B20x7EE9.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-C_ulsnv7.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-PTRGMKkq.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-DEPwo4vf.js +0 -2
- package/dist/gateway/static/root/assets/tracing-settings-page-CVJt7IXC.js +0 -1
- package/dist/gateway/static/root/assets/usage-page-DgqLD8Y5.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-BwK37aob.js +0 -2
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { PhoneError, init_contracts } from "./contracts.js";
|
|
2
|
+
import { runPhoneCommandBuffer, runPhoneCommandText } from "../process/phone-process.js";
|
|
3
|
+
import { managedPhoneAdbPath } from "../runtime-tools/phone-dependency-catalog.js";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { delimiter, join } from "node:path";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
import { setTimeout } from "node:timers/promises";
|
|
9
|
+
import { DOMParser } from "linkedom";
|
|
10
|
+
import sharp from "sharp";
|
|
11
|
+
//#region src/phone/adb-driver.ts
|
|
12
|
+
init_contracts();
|
|
13
|
+
function resolveAdbPath(configured, stateDir) {
|
|
14
|
+
if (configured) return configured;
|
|
15
|
+
const roots = [
|
|
16
|
+
process.env.ANDROID_HOME,
|
|
17
|
+
process.env.ANDROID_SDK_ROOT,
|
|
18
|
+
join(homedir(), "Library/Android/sdk"),
|
|
19
|
+
join(homedir(), "Android/Sdk")
|
|
20
|
+
];
|
|
21
|
+
for (const root of roots) {
|
|
22
|
+
const candidate = root && join(root, "platform-tools", process.platform === "win32" ? "adb.exe" : "adb");
|
|
23
|
+
if (candidate && existsSync(candidate)) return candidate;
|
|
24
|
+
}
|
|
25
|
+
const managed = managedPhoneAdbPath(stateDir);
|
|
26
|
+
return existsSync(managed) ? managed : "adb";
|
|
27
|
+
}
|
|
28
|
+
function runAdb(path, args, signal, timeout = 15e3) {
|
|
29
|
+
signal?.throwIfAborted();
|
|
30
|
+
return runPhoneCommandBuffer(path, args, signal, timeout).catch(() => {
|
|
31
|
+
throw new PhoneError(signal?.aborted ? "PHONE_CANCELLED" : "PHONE_ADB_FAILED", signal?.aborted ? "Phone operation stopped" : `ADB command failed (${args.includes("screencap") ? "screenshot" : args.includes("devices") ? "discovery" : "device operation"}). Check ADB, device authorization and connection.`);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function parseAdbDevices(text) {
|
|
35
|
+
return text.split(/\r?\n/).slice(1).flatMap((line) => {
|
|
36
|
+
const match = /^(\S+)\s+(device|offline|unauthorized|recovery|sideload)\b(.*)$/.exec(line.trim());
|
|
37
|
+
if (!match) return [];
|
|
38
|
+
return [{
|
|
39
|
+
serial: match[1],
|
|
40
|
+
state: match[2],
|
|
41
|
+
name: /model:(\S+)/.exec(match[3])?.[1]?.replaceAll("_", " ") ?? match[1],
|
|
42
|
+
emulator: /^emulator-\d+$/.test(match[1])
|
|
43
|
+
}];
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function parsePhoneUi(xml) {
|
|
47
|
+
const document = new DOMParser().parseFromString(xml.slice(xml.indexOf("<?xml") >= 0 ? xml.indexOf("<?xml") : xml.indexOf("<hierarchy")), "text/xml");
|
|
48
|
+
return Array.from(document.querySelectorAll("node")).slice(0, 500).flatMap((node) => {
|
|
49
|
+
const bounds = /^\[(\d+),(\d+)\]\[(\d+),(\d+)\]$/.exec(node.getAttribute("bounds") ?? "");
|
|
50
|
+
if (!bounds || node.getAttribute("visible-to-user") === "false") return [];
|
|
51
|
+
const password = node.getAttribute("password") === "true";
|
|
52
|
+
return [{
|
|
53
|
+
text: password ? "" : (node.getAttribute("text") ?? "").slice(0, 500),
|
|
54
|
+
description: password ? "" : (node.getAttribute("content-desc") ?? "").slice(0, 300),
|
|
55
|
+
resourceId: node.getAttribute("resource-id") ?? "",
|
|
56
|
+
clickable: node.getAttribute("clickable") === "true",
|
|
57
|
+
focused: node.getAttribute("focused") === "true",
|
|
58
|
+
password,
|
|
59
|
+
editable: /(?:EditText|AutoCompleteTextView)$/.test(node.getAttribute("class") ?? ""),
|
|
60
|
+
bounds: bounds.slice(1).map(Number)
|
|
61
|
+
}];
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function parseAndroidLayout(raw) {
|
|
65
|
+
const windows = JSON.parse(raw);
|
|
66
|
+
if (!Array.isArray(windows)) return [];
|
|
67
|
+
const window = windows.find((item) => item.type === "APPLICATION" && item.focused);
|
|
68
|
+
if (!window || !Array.isArray(window.content)) return [];
|
|
69
|
+
return window.content.slice(0, 500).flatMap((node) => {
|
|
70
|
+
const bounds = /^\[(\d+),(\d+)\]\[(\d+),(\d+)\]$/.exec(String(node.bounds ?? ""));
|
|
71
|
+
if (!bounds || node.hidden || node["off-screen"]) return [];
|
|
72
|
+
const interactions = Array.isArray(node.interactions) ? node.interactions.map((value) => String(value).toUpperCase()) : [];
|
|
73
|
+
const state = Array.isArray(node.state) ? node.state.map((value) => String(value).toUpperCase()) : [];
|
|
74
|
+
const password = interactions.includes("PASSWORD");
|
|
75
|
+
return [{
|
|
76
|
+
text: password ? "" : String(node.text ?? "").slice(0, 500),
|
|
77
|
+
description: password ? "" : String(node["content-desc"] ?? "").slice(0, 300),
|
|
78
|
+
resourceId: String(node["resource-id"] ?? ""),
|
|
79
|
+
clickable: interactions.includes("CLICKABLE"),
|
|
80
|
+
focused: state.includes("FOCUSED"),
|
|
81
|
+
password,
|
|
82
|
+
editable: /(?:EditText|AutoCompleteTextView)$/.test(String(node.class ?? "")),
|
|
83
|
+
bounds: bounds.slice(1).map(Number)
|
|
84
|
+
}];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function androidCliPath() {
|
|
88
|
+
const filename = process.platform === "win32" ? "android.cmd" : "android";
|
|
89
|
+
return (process.env.PATH ?? "").split(delimiter).map((path) => join(path, filename)).find((path) => existsSync(path));
|
|
90
|
+
}
|
|
91
|
+
/** Read only the active native editor, never historical input-method entries. */
|
|
92
|
+
function parseAndroidFocusedEditor(raw, app) {
|
|
93
|
+
for (const section of raw.split("Input method client state for ").slice(1)) {
|
|
94
|
+
const client = section.split("Input method service state for ")[0];
|
|
95
|
+
if (!/\bmActive=true\b/.test(client) || !/\bmServedConnecting=false\b/.test(client)) continue;
|
|
96
|
+
const view = /^\s*mServedView=((?:[\w.]+\.)?(?:EditText|AutoCompleteTextView))\{([^\s}]+)/m.exec(client);
|
|
97
|
+
const connection = /^\s*mServedInputConnectionWrapper=([^\n]+)/m.exec(client)?.[1];
|
|
98
|
+
if (!view || !connection?.includes("finished=false") || !connection.includes("isActive()=true") || !connection.includes(`mServedView=${view[1]}{${view[2]} `)) continue;
|
|
99
|
+
const editor = /mCurrentTextBoxAttribute:\s*([\s\S]*?)(?=\n\s{0,2}\w|$)/.exec(client)?.[1];
|
|
100
|
+
const packageName = /\bpackageName=([\w.]+)/.exec(editor ?? "")?.[1];
|
|
101
|
+
const value = /\binputType=0x([0-9a-f]+)/i.exec(editor ?? "")?.[1];
|
|
102
|
+
if (packageName !== app || !value) continue;
|
|
103
|
+
const inputType = Number.parseInt(value, 16);
|
|
104
|
+
const kind = inputType & 15;
|
|
105
|
+
const variation = inputType & 4080;
|
|
106
|
+
if (![
|
|
107
|
+
1,
|
|
108
|
+
2,
|
|
109
|
+
3,
|
|
110
|
+
4
|
|
111
|
+
].includes(kind)) continue;
|
|
112
|
+
return {
|
|
113
|
+
text: "",
|
|
114
|
+
description: "",
|
|
115
|
+
resourceId: "android:active-editor",
|
|
116
|
+
bounds: [
|
|
117
|
+
0,
|
|
118
|
+
0,
|
|
119
|
+
0,
|
|
120
|
+
0
|
|
121
|
+
],
|
|
122
|
+
clickable: false,
|
|
123
|
+
focused: true,
|
|
124
|
+
editable: true,
|
|
125
|
+
password: kind === 1 ? [
|
|
126
|
+
128,
|
|
127
|
+
144,
|
|
128
|
+
224
|
|
129
|
+
].includes(variation) : kind === 2 && variation === 16
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
var AdbPhoneDriver = class {
|
|
134
|
+
constructor(adbPath = resolveAdbPath(), command = runAdb, useAndroidCli = true) {
|
|
135
|
+
this.adbPath = adbPath;
|
|
136
|
+
this.command = command;
|
|
137
|
+
this.useAndroidCli = useAndroidCli;
|
|
138
|
+
}
|
|
139
|
+
async discover(signal) {
|
|
140
|
+
return parseAdbDevices((await this.command(this.adbPath, ["devices", "-l"], signal)).toString());
|
|
141
|
+
}
|
|
142
|
+
async foreground(serial, signal) {
|
|
143
|
+
const activity = await this.shell(serial, [
|
|
144
|
+
"sh",
|
|
145
|
+
"-c",
|
|
146
|
+
"dumpsys activity activities | grep -m 1 -E \"mResumedActivity|topResumedActivity\""
|
|
147
|
+
], signal);
|
|
148
|
+
const app = /(?:mResumedActivity|topResumedActivity)[^\n]*?\s([\w.]+)\//.exec(activity)?.[1];
|
|
149
|
+
if (!app) throw new PhoneError("PHONE_APP_UNKNOWN", "Unable to identify the foreground app");
|
|
150
|
+
return app;
|
|
151
|
+
}
|
|
152
|
+
async shell(serial, args, signal) {
|
|
153
|
+
return (await this.command(this.adbPath, [
|
|
154
|
+
"-s",
|
|
155
|
+
serial,
|
|
156
|
+
"shell",
|
|
157
|
+
...args
|
|
158
|
+
], signal)).toString();
|
|
159
|
+
}
|
|
160
|
+
async observe(serial, signal) {
|
|
161
|
+
let elements = [];
|
|
162
|
+
const android = this.useAndroidCli ? androidCliPath() : void 0;
|
|
163
|
+
if (android && process.platform !== "win32") for (let attempt = 0; attempt < 3 && !elements.length; attempt++) {
|
|
164
|
+
try {
|
|
165
|
+
elements = parseAndroidLayout(await runPhoneCommandText(android, [
|
|
166
|
+
"layout",
|
|
167
|
+
"--device",
|
|
168
|
+
serial,
|
|
169
|
+
"--no-idle",
|
|
170
|
+
"--flat"
|
|
171
|
+
], signal));
|
|
172
|
+
} catch {
|
|
173
|
+
signal.throwIfAborted();
|
|
174
|
+
}
|
|
175
|
+
if (!elements.length) await setTimeout(350, void 0, { signal });
|
|
176
|
+
}
|
|
177
|
+
if (!elements.length) try {
|
|
178
|
+
const path = `/data/local/tmp/xopc-phone-${randomUUID()}.xml`;
|
|
179
|
+
try {
|
|
180
|
+
await this.shell(serial, [
|
|
181
|
+
"uiautomator",
|
|
182
|
+
"dump",
|
|
183
|
+
path
|
|
184
|
+
], signal);
|
|
185
|
+
elements = parsePhoneUi(await this.shell(serial, ["cat", path], signal));
|
|
186
|
+
} finally {
|
|
187
|
+
await this.shell(serial, [
|
|
188
|
+
"rm",
|
|
189
|
+
"-f",
|
|
190
|
+
path
|
|
191
|
+
], signal).catch(() => {});
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
signal.throwIfAborted();
|
|
195
|
+
if (error instanceof Error && error.message === "PHONE_UI_INVALID") throw error;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
...await this.capture(serial, signal),
|
|
199
|
+
elements
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
async observeForText(serial, signal) {
|
|
203
|
+
const observation = await this.capture(serial, signal);
|
|
204
|
+
try {
|
|
205
|
+
const editor = parseAndroidFocusedEditor(await this.shell(serial, ["dumpsys", "input_method"], signal), observation.package);
|
|
206
|
+
if (await this.foreground(serial, signal) !== observation.package) throw new PhoneError("PHONE_FRAME_CHANGED", "Application changed before text input");
|
|
207
|
+
if (editor) return {
|
|
208
|
+
...observation,
|
|
209
|
+
elements: [editor]
|
|
210
|
+
};
|
|
211
|
+
} catch (error) {
|
|
212
|
+
observation.frame.fill(0);
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
observation.frame.fill(0);
|
|
216
|
+
return this.observe(serial, signal);
|
|
217
|
+
}
|
|
218
|
+
/** Pixel capture avoids accessibility dumps on the human interaction path. */
|
|
219
|
+
async capture(serial, signal) {
|
|
220
|
+
const foreground = async () => {
|
|
221
|
+
const activity = await this.shell(serial, [
|
|
222
|
+
"dumpsys",
|
|
223
|
+
"activity",
|
|
224
|
+
"activities"
|
|
225
|
+
], signal);
|
|
226
|
+
const app = /(?:mResumedActivity|topResumedActivity)[^\n]*?\s([\w.]+)\//.exec(activity)?.[1];
|
|
227
|
+
if (!app) throw new PhoneError("PHONE_APP_UNKNOWN", "Unable to identify the foreground app");
|
|
228
|
+
return app;
|
|
229
|
+
};
|
|
230
|
+
const app = await foreground();
|
|
231
|
+
const capturedAt = Date.now();
|
|
232
|
+
const results = await Promise.allSettled([this.command(this.adbPath, [
|
|
233
|
+
"-s",
|
|
234
|
+
serial,
|
|
235
|
+
"exec-out",
|
|
236
|
+
"screencap",
|
|
237
|
+
"-p"
|
|
238
|
+
], signal), this.shell(serial, ["dumpsys", "input"], signal)]);
|
|
239
|
+
const pixels = results[0];
|
|
240
|
+
const display = results[1];
|
|
241
|
+
if (pixels.status === "rejected") throw pixels.reason;
|
|
242
|
+
const frame = pixels.value;
|
|
243
|
+
try {
|
|
244
|
+
if (display.status === "rejected") throw display.reason;
|
|
245
|
+
if (await foreground() !== app) throw new PhoneError("PHONE_FRAME_CHANGED", "Application changed while capturing the screen; retry");
|
|
246
|
+
const info = await sharp(frame, { limitInputPixels: 16e6 }).metadata();
|
|
247
|
+
if (info.format !== "png" || !info.width || !info.height) throw new Error("Invalid frame");
|
|
248
|
+
const rotation = Number(/SurfaceOrientation:\s*(\d)/.exec(display.value)?.[1] ?? 0);
|
|
249
|
+
return {
|
|
250
|
+
frameId: randomUUID(),
|
|
251
|
+
capturedAt,
|
|
252
|
+
width: info.width,
|
|
253
|
+
height: info.height,
|
|
254
|
+
rotation,
|
|
255
|
+
package: app,
|
|
256
|
+
frame,
|
|
257
|
+
elements: []
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
frame.fill(0);
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async execute(serial, action, observation, signal, options) {
|
|
265
|
+
const point = ([x, y]) => [String(Math.min(observation.width - 1, Math.round(x * observation.width / 1e3))), String(Math.min(observation.height - 1, Math.round(y * observation.height / 1e3)))];
|
|
266
|
+
const input = (args) => this.shell(serial, ["input", ...args], signal);
|
|
267
|
+
switch (action.type) {
|
|
268
|
+
case "launch": {
|
|
269
|
+
const component = (await this.shell(serial, [
|
|
270
|
+
"cmd",
|
|
271
|
+
"package",
|
|
272
|
+
"resolve-activity",
|
|
273
|
+
"--brief",
|
|
274
|
+
action.package
|
|
275
|
+
], signal)).trim().split(/\r?\n/).find((line) => /^[\w.]+\/[\w.$]+$/.test(line));
|
|
276
|
+
if (!component) throw new PhoneError("PHONE_APP_UNAVAILABLE", "The selected application has no launchable activity");
|
|
277
|
+
const output = await this.shell(serial, [
|
|
278
|
+
"am",
|
|
279
|
+
"start",
|
|
280
|
+
...options?.resetTask ? ["--activity-clear-top", "--activity-single-top"] : [],
|
|
281
|
+
"-n",
|
|
282
|
+
component
|
|
283
|
+
], signal);
|
|
284
|
+
if (/Error|Exception/.test(output)) throw new PhoneError("PHONE_LAUNCH_FAILED", "Unable to launch application");
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
case "tap":
|
|
288
|
+
await input(["tap", ...point(action.point)]);
|
|
289
|
+
break;
|
|
290
|
+
case "doubleTap":
|
|
291
|
+
await input(["tap", ...point(action.point)]);
|
|
292
|
+
signal.throwIfAborted();
|
|
293
|
+
await input(["tap", ...point(action.point)]);
|
|
294
|
+
break;
|
|
295
|
+
case "longPress": {
|
|
296
|
+
const p = point(action.point);
|
|
297
|
+
await input([
|
|
298
|
+
"swipe",
|
|
299
|
+
...p,
|
|
300
|
+
...p,
|
|
301
|
+
String(action.durationMs)
|
|
302
|
+
]);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
case "swipe":
|
|
306
|
+
await input([
|
|
307
|
+
"swipe",
|
|
308
|
+
...point(action.start),
|
|
309
|
+
...point(action.end),
|
|
310
|
+
String(action.durationMs)
|
|
311
|
+
]);
|
|
312
|
+
break;
|
|
313
|
+
case "typeText":
|
|
314
|
+
if (!observation.elements.some((node) => node.focused && node.editable && !node.password)) throw new PhoneError("PHONE_INPUT_NOT_FOCUSED", "Focus a non-password editable field before typing");
|
|
315
|
+
if (/^[a-zA-Z0-9 .,_@+-]+$/.test(action.text) && !action.text.includes("%")) await input(["text", action.text.replaceAll(" ", "%s")]);
|
|
316
|
+
else {
|
|
317
|
+
const previous = (await this.shell(serial, [
|
|
318
|
+
"settings",
|
|
319
|
+
"get",
|
|
320
|
+
"secure",
|
|
321
|
+
"default_input_method"
|
|
322
|
+
], signal)).trim();
|
|
323
|
+
const keyboard = "com.android.adbkeyboard/.AdbIME";
|
|
324
|
+
if (!(await this.shell(serial, [
|
|
325
|
+
"ime",
|
|
326
|
+
"list",
|
|
327
|
+
"-s"
|
|
328
|
+
], signal)).split(/\s+/).includes(keyboard)) throw new PhoneError("PHONE_KEYBOARD_REQUIRED", "Unicode input requires ADB Keyboard. Install and enable it on the device first.");
|
|
329
|
+
try {
|
|
330
|
+
await this.shell(serial, [
|
|
331
|
+
"ime",
|
|
332
|
+
"set",
|
|
333
|
+
keyboard
|
|
334
|
+
], signal);
|
|
335
|
+
const result = await this.shell(serial, [
|
|
336
|
+
"am",
|
|
337
|
+
"broadcast",
|
|
338
|
+
"-a",
|
|
339
|
+
"ADB_INPUT_B64",
|
|
340
|
+
"--es",
|
|
341
|
+
"msg",
|
|
342
|
+
Buffer.from(action.text).toString("base64")
|
|
343
|
+
], signal);
|
|
344
|
+
if (!/Broadcast completed/.test(result)) throw new PhoneError("PHONE_INPUT_FAILED", "Keyboard input was not acknowledged");
|
|
345
|
+
} finally {
|
|
346
|
+
if (/^[\w.]+\/[\w.$]+$/.test(previous)) await this.shell(serial, [
|
|
347
|
+
"ime",
|
|
348
|
+
"set",
|
|
349
|
+
previous
|
|
350
|
+
], AbortSignal.timeout(3e3));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
case "home":
|
|
355
|
+
await input(["keyevent", "KEYCODE_HOME"]);
|
|
356
|
+
break;
|
|
357
|
+
case "back":
|
|
358
|
+
await input(["keyevent", "KEYCODE_BACK"]);
|
|
359
|
+
break;
|
|
360
|
+
case "wait":
|
|
361
|
+
await setTimeout(action.durationMs, void 0, { signal });
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (options?.settleMs !== 0) await setTimeout(options?.settleMs ?? 350, void 0, { signal });
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
//#endregion
|
|
368
|
+
export { AdbPhoneDriver, parseAdbDevices, parseAndroidFocusedEditor, parseAndroidLayout, parsePhoneUi, resolveAdbPath, runAdb };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Adb } from '@yume-chan/adb';
|
|
2
|
+
import { AdbPhoneDriver } from './adb-driver.js';
|
|
3
|
+
/** Accept only an explicit TCP endpoint, never URLs, paths or ADB commands. */
|
|
4
|
+
export declare function parseCloudEndpoint(value: string): {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
endpoint: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function parseCloudPrivateKey(pem: string): Buffer;
|
|
10
|
+
export declare class PhoneCloudKeyStore {
|
|
11
|
+
private root;
|
|
12
|
+
constructor(root?: string);
|
|
13
|
+
private path;
|
|
14
|
+
save(key: Buffer): Promise<string>;
|
|
15
|
+
load(id: string): Promise<Buffer>;
|
|
16
|
+
remove(id: string): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** Direct daemon transport never starts or modifies the host ADB server. */
|
|
19
|
+
export declare class CloudAdbConnection {
|
|
20
|
+
readonly endpoint: string;
|
|
21
|
+
private key;
|
|
22
|
+
private socket?;
|
|
23
|
+
private adb?;
|
|
24
|
+
private pending?;
|
|
25
|
+
private idle?;
|
|
26
|
+
constructor(endpoint: string, key: Buffer);
|
|
27
|
+
close(): void;
|
|
28
|
+
dispose(): void;
|
|
29
|
+
/** A dedicated media connection can multiplex sync, video and control sockets. */
|
|
30
|
+
client(): Promise<Adb>;
|
|
31
|
+
private open;
|
|
32
|
+
command(_path: string, args: string[], signal?: AbortSignal, timeout?: number): Promise<Buffer>;
|
|
33
|
+
driver(): AdbPhoneDriver;
|
|
34
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { init_paths, resolveCredentialsDir } from "../config/paths.js";
|
|
2
|
+
import { PhoneError, init_contracts } from "./contracts.js";
|
|
3
|
+
import { AdbPhoneDriver } from "./adb-driver.js";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createPrivateKey, randomUUID } from "node:crypto";
|
|
6
|
+
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
7
|
+
import { connect } from "node:net";
|
|
8
|
+
import { Adb, AdbDaemonTransport, AdbPacket, AdbPacketSerializeStream, AdbSignatureAuthenticator } from "@yume-chan/adb";
|
|
9
|
+
import { Consumable, ReadableStream, StructDeserializeStream } from "@yume-chan/stream-extra";
|
|
10
|
+
//#region src/phone/cloud-adb.ts
|
|
11
|
+
init_paths();
|
|
12
|
+
init_contracts();
|
|
13
|
+
/** Accept only an explicit TCP endpoint, never URLs, paths or ADB commands. */
|
|
14
|
+
function parseCloudEndpoint(value) {
|
|
15
|
+
const match = /^(\[[a-fA-F0-9:]+\]|[a-zA-Z0-9](?:[a-zA-Z0-9.-]*[a-zA-Z0-9])?):([0-9]{1,5})$/.exec(value.trim());
|
|
16
|
+
if (!match || Number(match[2]) < 1 || Number(match[2]) > 65535) throw new PhoneError("PHONE_ENDPOINT_INVALID", "Enter the cloud ADB address as host:port");
|
|
17
|
+
return {
|
|
18
|
+
host: match[1].replace(/^\[|\]$/g, ""),
|
|
19
|
+
port: Number(match[2]),
|
|
20
|
+
endpoint: `${match[1].toLowerCase()}:${Number(match[2])}`
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function parseCloudPrivateKey(pem) {
|
|
24
|
+
try {
|
|
25
|
+
if (Buffer.byteLength(pem) > 16384) throw new Error();
|
|
26
|
+
const key = createPrivateKey(pem);
|
|
27
|
+
if (key.asymmetricKeyType !== "rsa" || key.asymmetricKeyDetails?.modulusLength !== 2048 || key.asymmetricKeyDetails?.publicExponent !== 65537n) throw new Error();
|
|
28
|
+
return key.export({
|
|
29
|
+
format: "der",
|
|
30
|
+
type: "pkcs8"
|
|
31
|
+
});
|
|
32
|
+
} catch {
|
|
33
|
+
throw new PhoneError("PHONE_KEY_INVALID", "Upload the unencrypted RSA 2048 private adbkey downloaded from the cloud console");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
var PhoneCloudKeyStore = class {
|
|
37
|
+
constructor(root = join(resolveCredentialsDir(), "phone-adb")) {
|
|
38
|
+
this.root = root;
|
|
39
|
+
}
|
|
40
|
+
path(id) {
|
|
41
|
+
if (!/^[0-9a-f-]{36}$/i.test(id)) throw new PhoneError("PHONE_KEY_INVALID", "Invalid phone credential reference");
|
|
42
|
+
return join(this.root, `${id}.der`);
|
|
43
|
+
}
|
|
44
|
+
async save(key) {
|
|
45
|
+
await mkdir(this.root, {
|
|
46
|
+
recursive: true,
|
|
47
|
+
mode: 448
|
|
48
|
+
});
|
|
49
|
+
await chmod(this.root, 448);
|
|
50
|
+
const id = randomUUID();
|
|
51
|
+
await writeFile(this.path(id), key, {
|
|
52
|
+
mode: 384,
|
|
53
|
+
flag: "wx"
|
|
54
|
+
});
|
|
55
|
+
return id;
|
|
56
|
+
}
|
|
57
|
+
async load(id) {
|
|
58
|
+
try {
|
|
59
|
+
return await readFile(this.path(id));
|
|
60
|
+
} catch {
|
|
61
|
+
throw new PhoneError("PHONE_KEY_MISSING", "Phone private key is unavailable. Remove this connection and add it again.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async remove(id) {
|
|
65
|
+
await rm(this.path(id), { force: true });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
/** Direct daemon transport never starts or modifies the host ADB server. */
|
|
69
|
+
var CloudAdbConnection = class {
|
|
70
|
+
socket;
|
|
71
|
+
adb;
|
|
72
|
+
pending;
|
|
73
|
+
idle;
|
|
74
|
+
constructor(endpoint, key) {
|
|
75
|
+
this.endpoint = endpoint;
|
|
76
|
+
this.key = key;
|
|
77
|
+
parseCloudEndpoint(endpoint);
|
|
78
|
+
}
|
|
79
|
+
close() {
|
|
80
|
+
clearTimeout(this.idle);
|
|
81
|
+
this.socket?.destroy();
|
|
82
|
+
this.socket = void 0;
|
|
83
|
+
this.adb = void 0;
|
|
84
|
+
}
|
|
85
|
+
dispose() {
|
|
86
|
+
this.close();
|
|
87
|
+
this.key.fill(0);
|
|
88
|
+
}
|
|
89
|
+
/** A dedicated media connection can multiplex sync, video and control sockets. */
|
|
90
|
+
async client() {
|
|
91
|
+
if (this.adb) return this.adb;
|
|
92
|
+
if (this.pending) return this.pending;
|
|
93
|
+
const opening = this.open();
|
|
94
|
+
this.pending = opening;
|
|
95
|
+
try {
|
|
96
|
+
return await opening;
|
|
97
|
+
} finally {
|
|
98
|
+
if (this.pending === opening) this.pending = void 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async open() {
|
|
102
|
+
const { host, port } = parseCloudEndpoint(this.endpoint);
|
|
103
|
+
const socket = connect({
|
|
104
|
+
host,
|
|
105
|
+
port
|
|
106
|
+
});
|
|
107
|
+
this.socket = socket;
|
|
108
|
+
socket.setNoDelay(true);
|
|
109
|
+
socket.on("error", () => {});
|
|
110
|
+
try {
|
|
111
|
+
await new Promise((resolve, reject) => {
|
|
112
|
+
socket.once("connect", resolve);
|
|
113
|
+
socket.once("error", reject);
|
|
114
|
+
socket.once("close", () => reject(/* @__PURE__ */ new Error("closed")));
|
|
115
|
+
});
|
|
116
|
+
const iterator = socket[Symbol.asyncIterator]();
|
|
117
|
+
const raw = new ReadableStream({
|
|
118
|
+
async pull(controller) {
|
|
119
|
+
const next = await iterator.next();
|
|
120
|
+
if (next.done) controller.close();
|
|
121
|
+
else controller.enqueue(next.value);
|
|
122
|
+
},
|
|
123
|
+
cancel() {
|
|
124
|
+
socket.destroy();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const serializer = new AdbPacketSerializeStream();
|
|
128
|
+
serializer.readable.pipeTo(new Consumable.WritableStream({
|
|
129
|
+
write(chunk) {
|
|
130
|
+
return new Promise((resolve, reject) => socket.write(chunk, (error) => error ? reject(error) : resolve()));
|
|
131
|
+
},
|
|
132
|
+
close() {
|
|
133
|
+
socket.destroy();
|
|
134
|
+
},
|
|
135
|
+
abort() {
|
|
136
|
+
socket.destroy();
|
|
137
|
+
}
|
|
138
|
+
})).catch(() => {
|
|
139
|
+
socket.destroy();
|
|
140
|
+
});
|
|
141
|
+
const key = this.key;
|
|
142
|
+
const transport = await AdbDaemonTransport.authenticate({
|
|
143
|
+
serial: this.endpoint,
|
|
144
|
+
connection: {
|
|
145
|
+
readable: raw.pipeThrough(new StructDeserializeStream(AdbPacket)),
|
|
146
|
+
writable: serializer.writable
|
|
147
|
+
},
|
|
148
|
+
credentialStore: {
|
|
149
|
+
*iterateKeys() {
|
|
150
|
+
yield {
|
|
151
|
+
buffer: key,
|
|
152
|
+
name: "xopc-cloud-phone"
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
generateKey() {
|
|
156
|
+
throw new Error("Key generation disabled");
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
authenticators: [AdbSignatureAuthenticator]
|
|
160
|
+
});
|
|
161
|
+
if (socket.destroyed) {
|
|
162
|
+
await transport.close();
|
|
163
|
+
throw new Error("closed");
|
|
164
|
+
}
|
|
165
|
+
const adb = new Adb(transport);
|
|
166
|
+
this.adb = adb;
|
|
167
|
+
adb.disconnected.catch(() => {}).finally(() => {
|
|
168
|
+
if (this.adb === adb) this.close();
|
|
169
|
+
});
|
|
170
|
+
return adb;
|
|
171
|
+
} catch {
|
|
172
|
+
socket.destroy();
|
|
173
|
+
throw new PhoneError("PHONE_CLOUD_AUTH_FAILED", "Cloud ADB connection failed. Check address, network access and the bound private key.", 502);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async command(_path, args, signal, timeout = 15e3) {
|
|
177
|
+
const combined = AbortSignal.any([...signal ? [signal] : [], AbortSignal.timeout(timeout)]);
|
|
178
|
+
combined.throwIfAborted();
|
|
179
|
+
let abort;
|
|
180
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
181
|
+
abort = () => {
|
|
182
|
+
this.close();
|
|
183
|
+
reject(new PhoneError("PHONE_CLOUD_TIMEOUT", "Cloud phone connection stopped or timed out. Check its ADB address and network.", 502));
|
|
184
|
+
};
|
|
185
|
+
combined.addEventListener("abort", abort, { once: true });
|
|
186
|
+
});
|
|
187
|
+
try {
|
|
188
|
+
return await Promise.race([cancelled, (async () => {
|
|
189
|
+
const adb = await this.client();
|
|
190
|
+
if (args[0] === "devices") return Buffer.from(`List of devices attached\n${this.endpoint}\tdevice model:${adb.banner.model ?? "Cloud_Phone"}\n`);
|
|
191
|
+
if (args[0] !== "-s" || args[1] !== this.endpoint || !["shell", "exec-out"].includes(args[2])) throw new PhoneError("PHONE_CLOUD_COMMAND_INVALID", "Unsupported cloud ADB command");
|
|
192
|
+
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
193
|
+
const channel = await adb.createSocket(`${args[2] === "exec-out" ? "exec" : "shell"}:${args.slice(3).map(quote).join(" ")}`);
|
|
194
|
+
const chunks = [];
|
|
195
|
+
let length = 0;
|
|
196
|
+
try {
|
|
197
|
+
for await (const chunk of channel.readable) {
|
|
198
|
+
length += chunk.length;
|
|
199
|
+
if (length > 16 * 1024 * 1024) throw new Error("Output exceeded");
|
|
200
|
+
chunks.push(Buffer.from(chunk));
|
|
201
|
+
}
|
|
202
|
+
return Buffer.concat(chunks);
|
|
203
|
+
} finally {
|
|
204
|
+
await channel.close();
|
|
205
|
+
}
|
|
206
|
+
})()]);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
this.close();
|
|
209
|
+
if (error instanceof PhoneError) throw error;
|
|
210
|
+
throw new PhoneError("PHONE_CLOUD_ADB_FAILED", "Cloud phone operation failed. Reconnect or run diagnostics.", 502);
|
|
211
|
+
} finally {
|
|
212
|
+
combined.removeEventListener("abort", abort);
|
|
213
|
+
clearTimeout(this.idle);
|
|
214
|
+
this.idle = setTimeout(() => this.close(), 3e4);
|
|
215
|
+
this.idle.unref();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
driver() {
|
|
219
|
+
return new AdbPhoneDriver("", this.command.bind(this), false);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
//#endregion
|
|
223
|
+
export { CloudAdbConnection, PhoneCloudKeyStore, parseCloudEndpoint, parseCloudPrivateKey };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CloudAdbConnection, PhoneCloudKeyStore } from './cloud-adb.js';
|
|
2
|
+
import { CloudScrcpyProvider } from './cloud-scrcpy.js';
|
|
3
|
+
import { type PhoneDevice } from './contracts.js';
|
|
4
|
+
import type { PhoneRepository } from './repository.js';
|
|
5
|
+
export declare class CloudPhones {
|
|
6
|
+
private repository;
|
|
7
|
+
private keys;
|
|
8
|
+
private connect;
|
|
9
|
+
private drafts;
|
|
10
|
+
private connections;
|
|
11
|
+
constructor(repository: PhoneRepository, keys?: PhoneCloudKeyStore, connect?: (endpoint: string, key: Buffer) => CloudAdbConnection);
|
|
12
|
+
private discard;
|
|
13
|
+
test(owner: string, address: string, pem: string, signal: AbortSignal): Promise<{
|
|
14
|
+
token: `${string}-${string}-${string}-${string}-${string}`;
|
|
15
|
+
expiresAt: number;
|
|
16
|
+
model: string;
|
|
17
|
+
androidVersion: string;
|
|
18
|
+
screenshot: string;
|
|
19
|
+
endpoint: string;
|
|
20
|
+
media: "snapshot";
|
|
21
|
+
}>;
|
|
22
|
+
add(owner: string, token: string, name: string, makeDefault: boolean): Promise<PhoneDevice>;
|
|
23
|
+
cancel(owner: string, token: string): void;
|
|
24
|
+
driver(device: PhoneDevice): Promise<import("./adb-driver.js").AdbPhoneDriver>;
|
|
25
|
+
interactive(device: PhoneDevice, failed: () => void): Promise<CloudScrcpyProvider>;
|
|
26
|
+
disconnect(id: string): void;
|
|
27
|
+
remove(device: PhoneDevice): Promise<void>;
|
|
28
|
+
close(): void;
|
|
29
|
+
}
|