@beeos-ai/device-agent 0.4.0
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/.pi/extensions/android-tools.ts +139 -0
- package/.pi/skills/.gitkeep +0 -0
- package/.pi/skills/ground-mobile-fallback/SKILL.md +50 -0
- package/LICENSE +201 -0
- package/dist/action/handler.d.ts +9 -0
- package/dist/action/handler.js +124 -0
- package/dist/action/handler.js.map +1 -0
- package/dist/adapters/agent-gateway-client.d.ts +256 -0
- package/dist/adapters/agent-gateway-client.js +332 -0
- package/dist/adapters/agent-gateway-client.js.map +1 -0
- package/dist/adapters/agentcore-inner-loop.d.ts +29 -0
- package/dist/adapters/agentcore-inner-loop.js +72 -0
- package/dist/adapters/agentcore-inner-loop.js.map +1 -0
- package/dist/adapters/agentcore-pi.d.ts +215 -0
- package/dist/adapters/agentcore-pi.js +464 -0
- package/dist/adapters/agentcore-pi.js.map +1 -0
- package/dist/adapters/agentcore-runner.d.ts +70 -0
- package/dist/adapters/agentcore-runner.js +255 -0
- package/dist/adapters/agentcore-runner.js.map +1 -0
- package/dist/adapters/device-android-adb.d.ts +41 -0
- package/dist/adapters/device-android-adb.js +279 -0
- package/dist/adapters/device-android-adb.js.map +1 -0
- package/dist/adapters/device-desktop.d.ts +37 -0
- package/dist/adapters/device-desktop.js +101 -0
- package/dist/adapters/device-desktop.js.map +1 -0
- package/dist/adapters/identity-file.d.ts +30 -0
- package/dist/adapters/identity-file.js +50 -0
- package/dist/adapters/identity-file.js.map +1 -0
- package/dist/adapters/transport-bridge-ws.d.ts +39 -0
- package/dist/adapters/transport-bridge-ws.js +49 -0
- package/dist/adapters/transport-bridge-ws.js.map +1 -0
- package/dist/adapters/vlm-inner-loop.d.ts +88 -0
- package/dist/adapters/vlm-inner-loop.js +199 -0
- package/dist/adapters/vlm-inner-loop.js.map +1 -0
- package/dist/agent-runtime/adapters/base.d.ts +145 -0
- package/dist/agent-runtime/adapters/base.js +29 -0
- package/dist/agent-runtime/adapters/base.js.map +1 -0
- package/dist/agent-runtime/adapters/index.d.ts +17 -0
- package/dist/agent-runtime/adapters/index.js +17 -0
- package/dist/agent-runtime/adapters/index.js.map +1 -0
- package/dist/agent-runtime/adapters/mock.d.ts +44 -0
- package/dist/agent-runtime/adapters/mock.js +54 -0
- package/dist/agent-runtime/adapters/mock.js.map +1 -0
- package/dist/agent-runtime/adapters/openai.d.ts +63 -0
- package/dist/agent-runtime/adapters/openai.js +302 -0
- package/dist/agent-runtime/adapters/openai.js.map +1 -0
- package/dist/agent-runtime/adapters/registry.d.ts +49 -0
- package/dist/agent-runtime/adapters/registry.js +129 -0
- package/dist/agent-runtime/adapters/registry.js.map +1 -0
- package/dist/agent-runtime/adapters/ui-tars.d.ts +13 -0
- package/dist/agent-runtime/adapters/ui-tars.js +22 -0
- package/dist/agent-runtime/adapters/ui-tars.js.map +1 -0
- package/dist/agent-runtime/agent-runner.d.ts +149 -0
- package/dist/agent-runtime/agent-runner.js +356 -0
- package/dist/agent-runtime/agent-runner.js.map +1 -0
- package/dist/agent-runtime/core.d.ts +170 -0
- package/dist/agent-runtime/core.js +624 -0
- package/dist/agent-runtime/core.js.map +1 -0
- package/dist/agent-runtime/index.d.ts +16 -0
- package/dist/agent-runtime/index.js +17 -0
- package/dist/agent-runtime/index.js.map +1 -0
- package/dist/agent-runtime/plan-executor.d.ts +118 -0
- package/dist/agent-runtime/plan-executor.js +501 -0
- package/dist/agent-runtime/plan-executor.js.map +1 -0
- package/dist/agent-runtime/planner/base.d.ts +128 -0
- package/dist/agent-runtime/planner/base.js +157 -0
- package/dist/agent-runtime/planner/base.js.map +1 -0
- package/dist/agent-runtime/planner/llm-planner.d.ts +58 -0
- package/dist/agent-runtime/planner/llm-planner.js +112 -0
- package/dist/agent-runtime/planner/llm-planner.js.map +1 -0
- package/dist/agent-runtime/planner/noop-planner.d.ts +13 -0
- package/dist/agent-runtime/planner/noop-planner.js +30 -0
- package/dist/agent-runtime/planner/noop-planner.js.map +1 -0
- package/dist/agent-runtime/planner/prompts.d.ts +33 -0
- package/dist/agent-runtime/planner/prompts.js +165 -0
- package/dist/agent-runtime/planner/prompts.js.map +1 -0
- package/dist/agent-runtime/reflection.d.ts +81 -0
- package/dist/agent-runtime/reflection.js +189 -0
- package/dist/agent-runtime/reflection.js.map +1 -0
- package/dist/agent-runtime/reflectors/action-reflector.d.ts +97 -0
- package/dist/agent-runtime/reflectors/action-reflector.js +233 -0
- package/dist/agent-runtime/reflectors/action-reflector.js.map +1 -0
- package/dist/agent-runtime/reflectors/policy.d.ts +53 -0
- package/dist/agent-runtime/reflectors/policy.js +106 -0
- package/dist/agent-runtime/reflectors/policy.js.map +1 -0
- package/dist/agent-runtime/reflectors/trajectory-reflector.d.ts +94 -0
- package/dist/agent-runtime/reflectors/trajectory-reflector.js +223 -0
- package/dist/agent-runtime/reflectors/trajectory-reflector.js.map +1 -0
- package/dist/agent-runtime/skills/embedding.d.ts +53 -0
- package/dist/agent-runtime/skills/embedding.js +134 -0
- package/dist/agent-runtime/skills/embedding.js.map +1 -0
- package/dist/agent-runtime/skills/fs-store.d.ts +88 -0
- package/dist/agent-runtime/skills/fs-store.js +483 -0
- package/dist/agent-runtime/skills/fs-store.js.map +1 -0
- package/dist/agent-runtime/skills/index.d.ts +10 -0
- package/dist/agent-runtime/skills/index.js +10 -0
- package/dist/agent-runtime/skills/index.js.map +1 -0
- package/dist/agent-runtime/skills/matcher-evolver.d.ts +88 -0
- package/dist/agent-runtime/skills/matcher-evolver.js +110 -0
- package/dist/agent-runtime/skills/matcher-evolver.js.map +1 -0
- package/dist/agent-runtime/skills/memory-store.d.ts +23 -0
- package/dist/agent-runtime/skills/memory-store.js +94 -0
- package/dist/agent-runtime/skills/memory-store.js.map +1 -0
- package/dist/agent-runtime/skills/normalise.d.ts +40 -0
- package/dist/agent-runtime/skills/normalise.js +152 -0
- package/dist/agent-runtime/skills/normalise.js.map +1 -0
- package/dist/agent-runtime/skills/replay.d.ts +46 -0
- package/dist/agent-runtime/skills/replay.js +106 -0
- package/dist/agent-runtime/skills/replay.js.map +1 -0
- package/dist/agent-runtime/skills/sqlite-store.d.ts +60 -0
- package/dist/agent-runtime/skills/sqlite-store.js +234 -0
- package/dist/agent-runtime/skills/sqlite-store.js.map +1 -0
- package/dist/agent-runtime/skills/types.d.ts +99 -0
- package/dist/agent-runtime/skills/types.js +14 -0
- package/dist/agent-runtime/skills/types.js.map +1 -0
- package/dist/agent.d.ts +110 -0
- package/dist/agent.js +162 -0
- package/dist/agent.js.map +1 -0
- package/dist/api.d.ts +27 -0
- package/dist/api.js +48 -0
- package/dist/api.js.map +1 -0
- package/dist/app/agent-host.d.ts +59 -0
- package/dist/app/agent-host.js +263 -0
- package/dist/app/agent-host.js.map +1 -0
- package/dist/app/device-manager.d.ts +28 -0
- package/dist/app/device-manager.js +64 -0
- package/dist/app/device-manager.js.map +1 -0
- package/dist/app/message-store.d.ts +55 -0
- package/dist/app/message-store.js +99 -0
- package/dist/app/message-store.js.map +1 -0
- package/dist/app/session-manager.d.ts +132 -0
- package/dist/app/session-manager.js +350 -0
- package/dist/app/session-manager.js.map +1 -0
- package/dist/bootstrap.d.ts +94 -0
- package/dist/bootstrap.js +459 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/chat.d.ts +16 -0
- package/dist/chat.js +175 -0
- package/dist/chat.js.map +1 -0
- package/dist/config/loader.d.ts +77 -0
- package/dist/config/loader.js +292 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +162 -0
- package/dist/config/schema.js +243 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/actions.d.ts +60 -0
- package/dist/core/actions.js +120 -0
- package/dist/core/actions.js.map +1 -0
- package/dist/core/errors.d.ts +8 -0
- package/dist/core/errors.js +9 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/screen.d.ts +7 -0
- package/dist/core/screen.js +8 -0
- package/dist/core/screen.js.map +1 -0
- package/dist/core/session.d.ts +42 -0
- package/dist/core/session.js +29 -0
- package/dist/core/session.js.map +1 -0
- package/dist/debug/dump-system-prompt.d.ts +1 -0
- package/dist/debug/dump-system-prompt.js +34 -0
- package/dist/debug/dump-system-prompt.js.map +1 -0
- package/dist/device/android.d.ts +25 -0
- package/dist/device/android.js +231 -0
- package/dist/device/android.js.map +1 -0
- package/dist/device/apps.d.ts +3 -0
- package/dist/device/apps.js +77 -0
- package/dist/device/apps.js.map +1 -0
- package/dist/device/base.d.ts +20 -0
- package/dist/device/base.js +2 -0
- package/dist/device/base.js.map +1 -0
- package/dist/device/ui-xml.d.ts +29 -0
- package/dist/device/ui-xml.js +194 -0
- package/dist/device/ui-xml.js.map +1 -0
- package/dist/fleet/adapters/process/node-process-manager.d.ts +157 -0
- package/dist/fleet/adapters/process/node-process-manager.js +664 -0
- package/dist/fleet/adapters/process/node-process-manager.js.map +1 -0
- package/dist/fleet/adapters/sources/local-adb.d.ts +105 -0
- package/dist/fleet/adapters/sources/local-adb.js +353 -0
- package/dist/fleet/adapters/sources/local-adb.js.map +1 -0
- package/dist/fleet/adapters/sources/remote-adb.d.ts +97 -0
- package/dist/fleet/adapters/sources/remote-adb.js +138 -0
- package/dist/fleet/adapters/sources/remote-adb.js.map +1 -0
- package/dist/fleet/adapters/state/beeos-cli-store.d.ts +72 -0
- package/dist/fleet/adapters/state/beeos-cli-store.js +250 -0
- package/dist/fleet/adapters/state/beeos-cli-store.js.map +1 -0
- package/dist/fleet/adapters/state/port-allocator.d.ts +43 -0
- package/dist/fleet/adapters/state/port-allocator.js +173 -0
- package/dist/fleet/adapters/state/port-allocator.js.map +1 -0
- package/dist/fleet/domain/device.d.ts +138 -0
- package/dist/fleet/domain/device.js +49 -0
- package/dist/fleet/domain/device.js.map +1 -0
- package/dist/fleet/domain/events.d.ts +102 -0
- package/dist/fleet/domain/events.js +15 -0
- package/dist/fleet/domain/events.js.map +1 -0
- package/dist/fleet/domain/fsm.d.ts +122 -0
- package/dist/fleet/domain/fsm.js +475 -0
- package/dist/fleet/domain/fsm.js.map +1 -0
- package/dist/fleet/http/status-client.d.ts +31 -0
- package/dist/fleet/http/status-client.js +115 -0
- package/dist/fleet/http/status-client.js.map +1 -0
- package/dist/fleet/http/status-server.d.ts +64 -0
- package/dist/fleet/http/status-server.js +200 -0
- package/dist/fleet/http/status-server.js.map +1 -0
- package/dist/fleet/index.d.ts +27 -0
- package/dist/fleet/index.js +17 -0
- package/dist/fleet/index.js.map +1 -0
- package/dist/fleet/installer/index.d.ts +12 -0
- package/dist/fleet/installer/index.js +13 -0
- package/dist/fleet/installer/index.js.map +1 -0
- package/dist/fleet/installer/launchd-mac.d.ts +73 -0
- package/dist/fleet/installer/launchd-mac.js +291 -0
- package/dist/fleet/installer/launchd-mac.js.map +1 -0
- package/dist/fleet/installer/service-spec.d.ts +70 -0
- package/dist/fleet/installer/service-spec.js +43 -0
- package/dist/fleet/installer/service-spec.js.map +1 -0
- package/dist/fleet/installer/spawn-env.d.ts +45 -0
- package/dist/fleet/installer/spawn-env.js +224 -0
- package/dist/fleet/installer/spawn-env.js.map +1 -0
- package/dist/fleet/ports/clock.d.ts +19 -0
- package/dist/fleet/ports/clock.js +26 -0
- package/dist/fleet/ports/clock.js.map +1 -0
- package/dist/fleet/ports/device-source.d.ts +28 -0
- package/dist/fleet/ports/device-source.js +18 -0
- package/dist/fleet/ports/device-source.js.map +1 -0
- package/dist/fleet/ports/process-manager.d.ts +110 -0
- package/dist/fleet/ports/process-manager.js +27 -0
- package/dist/fleet/ports/process-manager.js.map +1 -0
- package/dist/fleet/ports/state-registry.d.ts +49 -0
- package/dist/fleet/ports/state-registry.js +12 -0
- package/dist/fleet/ports/state-registry.js.map +1 -0
- package/dist/fleet/supervisor.d.ts +107 -0
- package/dist/fleet/supervisor.js +508 -0
- package/dist/fleet/supervisor.js.map +1 -0
- package/dist/grounding/grounder.d.ts +46 -0
- package/dist/grounding/grounder.js +82 -0
- package/dist/grounding/grounder.js.map +1 -0
- package/dist/inbound/acp/handlers/device.d.ts +43 -0
- package/dist/inbound/acp/handlers/device.js +153 -0
- package/dist/inbound/acp/handlers/device.js.map +1 -0
- package/dist/inbound/acp/handlers/initialize.d.ts +71 -0
- package/dist/inbound/acp/handlers/initialize.js +53 -0
- package/dist/inbound/acp/handlers/initialize.js.map +1 -0
- package/dist/inbound/acp/handlers/models.d.ts +16 -0
- package/dist/inbound/acp/handlers/models.js +23 -0
- package/dist/inbound/acp/handlers/models.js.map +1 -0
- package/dist/inbound/acp/handlers/session.d.ts +51 -0
- package/dist/inbound/acp/handlers/session.js +61 -0
- package/dist/inbound/acp/handlers/session.js.map +1 -0
- package/dist/inbound/acp/handlers/skills.d.ts +8 -0
- package/dist/inbound/acp/handlers/skills.js +9 -0
- package/dist/inbound/acp/handlers/skills.js.map +1 -0
- package/dist/inbound/acp/notifier.d.ts +35 -0
- package/dist/inbound/acp/notifier.js +45 -0
- package/dist/inbound/acp/notifier.js.map +1 -0
- package/dist/inbound/acp/router.d.ts +67 -0
- package/dist/inbound/acp/router.js +179 -0
- package/dist/inbound/acp/router.js.map +1 -0
- package/dist/inbound/cli/fleet-command.d.ts +34 -0
- package/dist/inbound/cli/fleet-command.js +792 -0
- package/dist/inbound/cli/fleet-command.js.map +1 -0
- package/dist/inbound/cli/main.d.ts +38 -0
- package/dist/inbound/cli/main.js +364 -0
- package/dist/inbound/cli/main.js.map +1 -0
- package/dist/inbound/cli/run-command.d.ts +101 -0
- package/dist/inbound/cli/run-command.js +405 -0
- package/dist/inbound/cli/run-command.js.map +1 -0
- package/dist/inbound/cli/skills-command.d.ts +39 -0
- package/dist/inbound/cli/skills-command.js +333 -0
- package/dist/inbound/cli/skills-command.js.map +1 -0
- package/dist/inbound/http/server.d.ts +7 -0
- package/dist/inbound/http/server.js +8 -0
- package/dist/inbound/http/server.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/index.d.ts +37 -0
- package/dist/logger/index.js +84 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/mcp-client/autospawn.d.ts +67 -0
- package/dist/mcp-client/autospawn.js +173 -0
- package/dist/mcp-client/autospawn.js.map +1 -0
- package/dist/mcp-client/client.d.ts +78 -0
- package/dist/mcp-client/client.js +168 -0
- package/dist/mcp-client/client.js.map +1 -0
- package/dist/mcp-client/index.d.ts +7 -0
- package/dist/mcp-client/index.js +4 -0
- package/dist/mcp-client/index.js.map +1 -0
- package/dist/mcp-client/inproc.d.ts +47 -0
- package/dist/mcp-client/inproc.js +229 -0
- package/dist/mcp-client/inproc.js.map +1 -0
- package/dist/mcp-client/runtime.d.ts +34 -0
- package/dist/mcp-client/runtime.js +15 -0
- package/dist/mcp-client/runtime.js.map +1 -0
- package/dist/model/adapters/autoglm.d.ts +31 -0
- package/dist/model/adapters/autoglm.js +409 -0
- package/dist/model/adapters/autoglm.js.map +1 -0
- package/dist/model/adapters/base.d.ts +13 -0
- package/dist/model/adapters/base.js +27 -0
- package/dist/model/adapters/base.js.map +1 -0
- package/dist/model/adapters/guiowl.d.ts +1 -0
- package/dist/model/adapters/guiowl.js +253 -0
- package/dist/model/adapters/guiowl.js.map +1 -0
- package/dist/model/adapters/index.d.ts +6 -0
- package/dist/model/adapters/index.js +8 -0
- package/dist/model/adapters/index.js.map +1 -0
- package/dist/model/adapters/maiui.d.ts +1 -0
- package/dist/model/adapters/maiui.js +117 -0
- package/dist/model/adapters/maiui.js.map +1 -0
- package/dist/model/adapters/qwenvl.d.ts +1 -0
- package/dist/model/adapters/qwenvl.js +108 -0
- package/dist/model/adapters/qwenvl.js.map +1 -0
- package/dist/model/adapters/showui.d.ts +1 -0
- package/dist/model/adapters/showui.js +132 -0
- package/dist/model/adapters/showui.js.map +1 -0
- package/dist/model/adapters/tool-call.d.ts +6 -0
- package/dist/model/adapters/tool-call.js +102 -0
- package/dist/model/adapters/tool-call.js.map +1 -0
- package/dist/model/adapters/uitars.d.ts +1 -0
- package/dist/model/adapters/uitars.js +180 -0
- package/dist/model/adapters/uitars.js.map +1 -0
- package/dist/model/base.d.ts +32 -0
- package/dist/model/base.js +2 -0
- package/dist/model/base.js.map +1 -0
- package/dist/model/client.d.ts +26 -0
- package/dist/model/client.js +300 -0
- package/dist/model/client.js.map +1 -0
- package/dist/model/image/compress.d.ts +53 -0
- package/dist/model/image/compress.js +89 -0
- package/dist/model/image/compress.js.map +1 -0
- package/dist/model/image/transport.d.ts +134 -0
- package/dist/model/image/transport.js +154 -0
- package/dist/model/image/transport.js.map +1 -0
- package/dist/orchestrator-system-prompt.d.ts +6 -0
- package/dist/orchestrator-system-prompt.js +81 -0
- package/dist/orchestrator-system-prompt.js.map +1 -0
- package/dist/pi-context.d.ts +82 -0
- package/dist/pi-context.js +62 -0
- package/dist/pi-context.js.map +1 -0
- package/dist/pi-local-skills.d.ts +17 -0
- package/dist/pi-local-skills.js +24 -0
- package/dist/pi-local-skills.js.map +1 -0
- package/dist/plugins/acp/index.d.ts +46 -0
- package/dist/plugins/acp/index.js +111 -0
- package/dist/plugins/acp/index.js.map +1 -0
- package/dist/plugins/agent-sync/index.d.ts +40 -0
- package/dist/plugins/agent-sync/index.js +98 -0
- package/dist/plugins/agent-sync/index.js.map +1 -0
- package/dist/plugins/http-debug/index.d.ts +30 -0
- package/dist/plugins/http-debug/index.js +64 -0
- package/dist/plugins/http-debug/index.js.map +1 -0
- package/dist/plugins/http-debug/server.d.ts +92 -0
- package/dist/plugins/http-debug/server.js +464 -0
- package/dist/plugins/http-debug/server.js.map +1 -0
- package/dist/ports/agent-core.d.ts +158 -0
- package/dist/ports/agent-core.js +2 -0
- package/dist/ports/agent-core.js.map +1 -0
- package/dist/ports/agent-host.d.ts +125 -0
- package/dist/ports/agent-host.js +23 -0
- package/dist/ports/agent-host.js.map +1 -0
- package/dist/ports/device.d.ts +53 -0
- package/dist/ports/device.js +13 -0
- package/dist/ports/device.js.map +1 -0
- package/dist/ports/identity.d.ts +22 -0
- package/dist/ports/identity.js +9 -0
- package/dist/ports/identity.js.map +1 -0
- package/dist/ports/plugin.d.ts +62 -0
- package/dist/ports/plugin.js +35 -0
- package/dist/ports/plugin.js.map +1 -0
- package/dist/ports/transport.d.ts +32 -0
- package/dist/ports/transport.js +12 -0
- package/dist/ports/transport.js.map +1 -0
- package/dist/ports/vlm.d.ts +59 -0
- package/dist/ports/vlm.js +14 -0
- package/dist/ports/vlm.js.map +1 -0
- package/dist/test-lan-autoglm.d.ts +7 -0
- package/dist/test-lan-autoglm.js +46 -0
- package/dist/test-lan-autoglm.js.map +1 -0
- package/dist/test-mem-breakdown.d.ts +1 -0
- package/dist/test-mem-breakdown.js +103 -0
- package/dist/test-mem-breakdown.js.map +1 -0
- package/dist/test-mem.d.ts +1 -0
- package/dist/test-mem.js +60 -0
- package/dist/test-mem.js.map +1 -0
- package/dist/test-mobile-use.d.ts +1 -0
- package/dist/test-mobile-use.js +18 -0
- package/dist/test-mobile-use.js.map +1 -0
- package/dist/test-screenshot-loop.d.ts +1 -0
- package/dist/test-screenshot-loop.js +43 -0
- package/dist/test-screenshot-loop.js.map +1 -0
- package/dist/test-screenshot.d.ts +1 -0
- package/dist/test-screenshot.js +10 -0
- package/dist/test-screenshot.js.map +1 -0
- package/dist/test-ui-xml-dump.d.ts +15 -0
- package/dist/test-ui-xml-dump.js +49 -0
- package/dist/test-ui-xml-dump.js.map +1 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* inbound/cli/fleet-command — `device-agent fleet …` subcommand family.
|
|
3
|
+
*
|
|
4
|
+
* The fleet supervisor process is a single long-running daemon that:
|
|
5
|
+
*
|
|
6
|
+
* - Listens to ADB events (`adb track-devices`)
|
|
7
|
+
* - Watches `~/.beeos/devices.json` for binding changes
|
|
8
|
+
* - For every BOUND device, spawns and supervises:
|
|
9
|
+
* device-mcp-server (local AI tooling)
|
|
10
|
+
* device-agent (BeeOS cloud control daemon, Ed25519 → Agent Gateway)
|
|
11
|
+
* - Exposes a tiny HTTP status server on `127.0.0.1:7950` for the
|
|
12
|
+
* other CLI subcommands (`status`, `devices`, `restart`, `reload`).
|
|
13
|
+
*
|
|
14
|
+
* The CLI splits into two halves:
|
|
15
|
+
*
|
|
16
|
+
* server side: `start [--no-daemon]` → builds the `FleetSupervisor` and
|
|
17
|
+
* serves `/fleet/*` over HTTP.
|
|
18
|
+
* client side: `status | devices | restart | reload` → opens an HTTP
|
|
19
|
+
* connection to the running fleet on `:7950`.
|
|
20
|
+
*
|
|
21
|
+
* All client-side commands fail fast with an actionable hint when the
|
|
22
|
+
* fleet isn't running. There is intentionally NO auto-spawn: the user is
|
|
23
|
+
* expected to start the fleet via `fleet start` (foreground) or `fleet
|
|
24
|
+
* enable` (launchd / systemd, M5 in a follow-up plan).
|
|
25
|
+
*/
|
|
26
|
+
import * as os from "node:os";
|
|
27
|
+
import * as path from "node:path";
|
|
28
|
+
import * as fs from "node:fs/promises";
|
|
29
|
+
import { BeeosCliStore, defaultDevicesPath, } from "../../fleet/adapters/state/beeos-cli-store.js";
|
|
30
|
+
import { StickyPortAllocator, defaultFleetStatePath } from "../../fleet/adapters/state/port-allocator.js";
|
|
31
|
+
import { LocalAdbSource } from "../../fleet/adapters/sources/local-adb.js";
|
|
32
|
+
import { NodeProcessManager } from "../../fleet/adapters/process/node-process-manager.js";
|
|
33
|
+
import { SystemClock } from "../../fleet/ports/clock.js";
|
|
34
|
+
import { FleetSupervisor } from "../../fleet/supervisor.js";
|
|
35
|
+
import { StatusServer } from "../../fleet/http/status-server.js";
|
|
36
|
+
import { FleetStatusClient, FleetClientError } from "../../fleet/http/status-client.js";
|
|
37
|
+
import { buildFleetServiceSpec, deriveFleetSpawnEnv, FLEET_LABEL, fleetPlistPath, installFleetLaunchd, migrateFromAttach, uninstallFleetLaunchd, } from "../../fleet/installer/index.js";
|
|
38
|
+
import { getLogger } from "../../logger/index.js";
|
|
39
|
+
export async function fleetCommand(argv, io = {}) {
|
|
40
|
+
const stdout = io.stdout ?? process.stdout;
|
|
41
|
+
const stderr = io.stderr ?? process.stderr;
|
|
42
|
+
const sub = argv[0];
|
|
43
|
+
if (!sub || sub === "-h" || sub === "--help" || sub === "help") {
|
|
44
|
+
printUsage(stdout);
|
|
45
|
+
return sub ? 0 : 2;
|
|
46
|
+
}
|
|
47
|
+
switch (sub) {
|
|
48
|
+
case "start":
|
|
49
|
+
return startCommand(argv.slice(1), { stdout, stderr });
|
|
50
|
+
case "status":
|
|
51
|
+
return statusCommand({ stdout, stderr });
|
|
52
|
+
case "devices":
|
|
53
|
+
return devicesCommand(argv.slice(1), { stdout, stderr });
|
|
54
|
+
case "restart":
|
|
55
|
+
return restartCommand(argv.slice(1), { stdout, stderr });
|
|
56
|
+
case "reload":
|
|
57
|
+
return reloadCommand({ stdout, stderr });
|
|
58
|
+
case "stop":
|
|
59
|
+
return stopCommand({ stdout, stderr });
|
|
60
|
+
case "enable":
|
|
61
|
+
return enableCommand(argv.slice(1), { stdout, stderr });
|
|
62
|
+
case "disable":
|
|
63
|
+
return disableCommand({ stdout, stderr });
|
|
64
|
+
case "migrate-from-attach":
|
|
65
|
+
return migrateCommand({ stdout, stderr });
|
|
66
|
+
case "logs":
|
|
67
|
+
return logsCommand(argv.slice(1), { stdout, stderr });
|
|
68
|
+
default:
|
|
69
|
+
stderr.write(`device-agent fleet: unknown subcommand '${sub}'\n`);
|
|
70
|
+
printUsage(stderr);
|
|
71
|
+
return 2;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function parseStartArgs(argv) {
|
|
75
|
+
const out = {};
|
|
76
|
+
for (let i = 0; i < argv.length; i++) {
|
|
77
|
+
const a = argv[i];
|
|
78
|
+
const next = () => {
|
|
79
|
+
const v = argv[++i];
|
|
80
|
+
if (v === undefined)
|
|
81
|
+
throw new Error(`Missing value for ${a}`);
|
|
82
|
+
return v;
|
|
83
|
+
};
|
|
84
|
+
switch (a) {
|
|
85
|
+
case "-h":
|
|
86
|
+
case "--help":
|
|
87
|
+
out.help = true;
|
|
88
|
+
break;
|
|
89
|
+
case "--no-daemon":
|
|
90
|
+
out.noDaemon = true;
|
|
91
|
+
break;
|
|
92
|
+
case "--agent-gateway-url":
|
|
93
|
+
out.agentGatewayUrl = next();
|
|
94
|
+
break;
|
|
95
|
+
case "--grace-ms":
|
|
96
|
+
out.graceMs = Number(next());
|
|
97
|
+
break;
|
|
98
|
+
case "--keep-ms":
|
|
99
|
+
out.keepMs = Number(next());
|
|
100
|
+
break;
|
|
101
|
+
case "--port":
|
|
102
|
+
out.port = Number(next());
|
|
103
|
+
break;
|
|
104
|
+
default:
|
|
105
|
+
if (a.startsWith("--")) {
|
|
106
|
+
throw new Error(`device-agent fleet start: unknown flag ${a}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
async function startCommand(argv, io) {
|
|
113
|
+
let flags;
|
|
114
|
+
try {
|
|
115
|
+
flags = parseStartArgs(argv);
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
io.stderr.write(`${e.message}\n`);
|
|
119
|
+
return 2;
|
|
120
|
+
}
|
|
121
|
+
if (flags.help) {
|
|
122
|
+
io.stdout.write(`device-agent fleet start — run the supervisor in the foreground
|
|
123
|
+
|
|
124
|
+
Usage:
|
|
125
|
+
device-agent fleet start [options]
|
|
126
|
+
|
|
127
|
+
Options:
|
|
128
|
+
--no-daemon (reserved) skip OS service detach (current default)
|
|
129
|
+
--agent-gateway-url URL Default Agent Gateway URL injected into spawned daemons
|
|
130
|
+
--grace-ms <n> HEALTHY → DEGRADED window in ms (default 5000, env FLEET_ADB_GRACE_MS)
|
|
131
|
+
--keep-ms <n> DEGRADED → GONE window in ms (default 300000, env FLEET_DAEMON_KEEP_MS)
|
|
132
|
+
--port <n> Status HTTP port on loopback (default 7950, env FLEET_STATUS_PORT)
|
|
133
|
+
-h, --help Show this help
|
|
134
|
+
`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
const log = getLogger().child({ component: "fleet" });
|
|
138
|
+
const supervisorLogger = {
|
|
139
|
+
debug: (msg, data) => log.debug({ ...data }, msg),
|
|
140
|
+
info: (msg, data) => log.info({ ...data }, msg),
|
|
141
|
+
warn: (msg, data) => log.warn({ ...data }, msg),
|
|
142
|
+
error: (msg, data) => log.error({ ...data }, msg),
|
|
143
|
+
};
|
|
144
|
+
const registry = new BeeosCliStore({ filePath: defaultDevicesPath() });
|
|
145
|
+
const allocator = new StickyPortAllocator({ filePath: defaultFleetStatePath() });
|
|
146
|
+
// NOTE: every adapter inside `fleet/` defines its OWN logger shape
|
|
147
|
+
// — `(msg: string, data?: Record<string, unknown>)`. Pino's runtime
|
|
148
|
+
// calling convention is the OPPOSITE: `(obj, msg)`. Passing the
|
|
149
|
+
// raw pino logger therefore swallows every structured field
|
|
150
|
+
// (observed in M11 dogfooding: `child error` arrived without
|
|
151
|
+
// `serial` / `error`). The wrapper above (`supervisorLogger`)
|
|
152
|
+
// bridges the two conventions, so every adapter must use it
|
|
153
|
+
// — not the bare `log`.
|
|
154
|
+
const source = new LocalAdbSource({ logger: supervisorLogger });
|
|
155
|
+
const processManager = new NodeProcessManager({ logger: supervisorLogger });
|
|
156
|
+
const clock = new SystemClock();
|
|
157
|
+
const supervisor = new FleetSupervisor({
|
|
158
|
+
source,
|
|
159
|
+
registry,
|
|
160
|
+
allocator,
|
|
161
|
+
processManager,
|
|
162
|
+
clock,
|
|
163
|
+
graceMs: flags.graceMs,
|
|
164
|
+
keepMs: flags.keepMs,
|
|
165
|
+
logger: supervisorLogger,
|
|
166
|
+
defaultAgentGatewayUrl: flags.agentGatewayUrl ?? process.env.AGENT_GATEWAY_URL ?? undefined,
|
|
167
|
+
});
|
|
168
|
+
let stopping = false;
|
|
169
|
+
const shutdown = async (signal) => {
|
|
170
|
+
if (stopping)
|
|
171
|
+
return 0;
|
|
172
|
+
stopping = true;
|
|
173
|
+
log.info({ signal }, "fleet shutdown initiated");
|
|
174
|
+
try {
|
|
175
|
+
await status.stop();
|
|
176
|
+
await supervisor.stop();
|
|
177
|
+
log.info("fleet shutdown complete");
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
log.error({ err: String(e) }, "fleet shutdown failed");
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const status = new StatusServer({
|
|
186
|
+
supervisor,
|
|
187
|
+
port: flags.port ?? Number(process.env.FLEET_STATUS_PORT ?? 7950),
|
|
188
|
+
logger: supervisorLogger,
|
|
189
|
+
onShutdown: () => {
|
|
190
|
+
// POST /fleet/shutdown — defer to the same path as SIGTERM so we
|
|
191
|
+
// exit with the same lifecycle order regardless of whether the
|
|
192
|
+
// signal came from launchd, the user, or the CLI's `fleet stop`.
|
|
193
|
+
void shutdown("HTTP /fleet/shutdown").then((c) => process.exit(c));
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
process.once("SIGINT", () => {
|
|
197
|
+
void shutdown("SIGINT").then((c) => process.exit(c));
|
|
198
|
+
});
|
|
199
|
+
process.once("SIGTERM", () => {
|
|
200
|
+
void shutdown("SIGTERM").then((c) => process.exit(c));
|
|
201
|
+
});
|
|
202
|
+
try {
|
|
203
|
+
await supervisor.start();
|
|
204
|
+
await status.start();
|
|
205
|
+
log.info({
|
|
206
|
+
host: status.address.host,
|
|
207
|
+
port: status.address.port,
|
|
208
|
+
beeosHome: defaultDevicesPath(),
|
|
209
|
+
host_os: os.platform(),
|
|
210
|
+
}, "fleet supervisor running");
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
log.error({ err: String(e) }, "fleet supervisor failed to start");
|
|
214
|
+
await status.stop().catch(() => { });
|
|
215
|
+
await supervisor.stop().catch(() => { });
|
|
216
|
+
return 1;
|
|
217
|
+
}
|
|
218
|
+
await new Promise((resolve) => {
|
|
219
|
+
process.once("beforeExit", () => resolve());
|
|
220
|
+
});
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
// ── client-side commands ────────────────────────────────────────────────────
|
|
224
|
+
async function statusCommand(io) {
|
|
225
|
+
const client = new FleetStatusClient();
|
|
226
|
+
const alive = await client.healthz();
|
|
227
|
+
if (!alive) {
|
|
228
|
+
io.stdout.write("fleet: NOT RUNNING\n");
|
|
229
|
+
io.stdout.write("hint: start it with `device-agent fleet start`\n");
|
|
230
|
+
return 1;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const devices = await client.listDevices();
|
|
234
|
+
io.stdout.write(`fleet: RUNNING (${devices.length} device${devices.length === 1 ? "" : "s"})\n`);
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
if (e instanceof FleetClientError) {
|
|
239
|
+
io.stderr.write(`${e.message}\n`);
|
|
240
|
+
return 1;
|
|
241
|
+
}
|
|
242
|
+
throw e;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function parseDevicesArgs(argv) {
|
|
246
|
+
const out = {};
|
|
247
|
+
for (const a of argv) {
|
|
248
|
+
if (a === "--json")
|
|
249
|
+
out.json = true;
|
|
250
|
+
else if (a === "-h" || a === "--help")
|
|
251
|
+
out.help = true;
|
|
252
|
+
else if (a.startsWith("--"))
|
|
253
|
+
throw new Error(`fleet devices: unknown flag ${a}`);
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
async function devicesCommand(argv, io) {
|
|
258
|
+
let flags;
|
|
259
|
+
try {
|
|
260
|
+
flags = parseDevicesArgs(argv);
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
io.stderr.write(`${e.message}\n`);
|
|
264
|
+
return 2;
|
|
265
|
+
}
|
|
266
|
+
if (flags.help) {
|
|
267
|
+
io.stdout.write(`device-agent fleet devices — list managed devices
|
|
268
|
+
|
|
269
|
+
Usage:
|
|
270
|
+
device-agent fleet devices [--json]
|
|
271
|
+
`);
|
|
272
|
+
return 0;
|
|
273
|
+
}
|
|
274
|
+
const client = new FleetStatusClient();
|
|
275
|
+
let devices;
|
|
276
|
+
try {
|
|
277
|
+
devices = await client.listDevices();
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
if (e instanceof FleetClientError) {
|
|
281
|
+
io.stderr.write(`${e.message}\n`);
|
|
282
|
+
return 1;
|
|
283
|
+
}
|
|
284
|
+
throw e;
|
|
285
|
+
}
|
|
286
|
+
if (flags.json) {
|
|
287
|
+
io.stdout.write(`${JSON.stringify(devices, null, 2)}\n`);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
if (devices.length === 0) {
|
|
291
|
+
io.stdout.write("(no devices discovered)\n");
|
|
292
|
+
return 0;
|
|
293
|
+
}
|
|
294
|
+
const rows = [
|
|
295
|
+
["SERIAL", "NAME", "STATE", "INSTANCE", "MCP", "DAEMON", "SCRCPY", "AGE"],
|
|
296
|
+
];
|
|
297
|
+
for (const d of devices) {
|
|
298
|
+
rows.push([
|
|
299
|
+
d.serial,
|
|
300
|
+
d.name,
|
|
301
|
+
d.state.toUpperCase(),
|
|
302
|
+
d.instanceId ?? "—",
|
|
303
|
+
d.mcpUrl
|
|
304
|
+
? `${d.mcpUrl}${d.mcpPid ? ` pid:${d.mcpPid}` : ""}`
|
|
305
|
+
: "—",
|
|
306
|
+
d.daemonPid ? `pid:${d.daemonPid}` : "—",
|
|
307
|
+
formatScrcpy(d.scrcpy),
|
|
308
|
+
formatAge(d.sinceMs),
|
|
309
|
+
]);
|
|
310
|
+
}
|
|
311
|
+
io.stdout.write(formatTable(rows));
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
async function restartCommand(argv, io) {
|
|
315
|
+
const target = argv[0];
|
|
316
|
+
if (!target || target === "-h" || target === "--help") {
|
|
317
|
+
io.stdout.write(`device-agent fleet restart — restart a device's mcp + daemon pair
|
|
318
|
+
|
|
319
|
+
Usage:
|
|
320
|
+
device-agent fleet restart <serial-or-name>
|
|
321
|
+
`);
|
|
322
|
+
return target ? 0 : 2;
|
|
323
|
+
}
|
|
324
|
+
const client = new FleetStatusClient();
|
|
325
|
+
// Resolve name → serial first if needed.
|
|
326
|
+
let serial = target;
|
|
327
|
+
try {
|
|
328
|
+
const devices = await client.listDevices();
|
|
329
|
+
const match = devices.find((d) => d.serial === target || d.name === target);
|
|
330
|
+
if (!match) {
|
|
331
|
+
io.stderr.write(`fleet: no device matches '${target}'.\n`);
|
|
332
|
+
io.stderr.write("hint: run `device-agent fleet devices` to see available serials.\n");
|
|
333
|
+
return 1;
|
|
334
|
+
}
|
|
335
|
+
serial = match.serial;
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
if (e instanceof FleetClientError) {
|
|
339
|
+
io.stderr.write(`${e.message}\n`);
|
|
340
|
+
return 1;
|
|
341
|
+
}
|
|
342
|
+
throw e;
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
const ok = await client.restartDevice(serial);
|
|
346
|
+
if (!ok) {
|
|
347
|
+
io.stderr.write(`fleet: serial '${serial}' is unknown to the supervisor.\n`);
|
|
348
|
+
return 1;
|
|
349
|
+
}
|
|
350
|
+
io.stdout.write(`fleet: restart accepted for ${serial}\n`);
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
if (e instanceof FleetClientError) {
|
|
355
|
+
io.stderr.write(`${e.message}\n`);
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
358
|
+
throw e;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async function reloadCommand(io) {
|
|
362
|
+
const client = new FleetStatusClient();
|
|
363
|
+
try {
|
|
364
|
+
await client.reload();
|
|
365
|
+
io.stdout.write("fleet: reload accepted\n");
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
if (e instanceof FleetClientError) {
|
|
370
|
+
io.stderr.write(`${e.message}\n`);
|
|
371
|
+
return 1;
|
|
372
|
+
}
|
|
373
|
+
throw e;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function parseEnableArgs(argv) {
|
|
377
|
+
const out = {};
|
|
378
|
+
for (let i = 0; i < argv.length; i++) {
|
|
379
|
+
const a = argv[i];
|
|
380
|
+
const next = () => {
|
|
381
|
+
const v = argv[++i];
|
|
382
|
+
if (v === undefined)
|
|
383
|
+
throw new Error(`Missing value for ${a}`);
|
|
384
|
+
return v;
|
|
385
|
+
};
|
|
386
|
+
switch (a) {
|
|
387
|
+
case "-h":
|
|
388
|
+
case "--help":
|
|
389
|
+
out.help = true;
|
|
390
|
+
break;
|
|
391
|
+
case "--cli-entry":
|
|
392
|
+
out.cliEntry = next();
|
|
393
|
+
break;
|
|
394
|
+
case "--agent-gateway-url":
|
|
395
|
+
out.agentGatewayUrl = next();
|
|
396
|
+
break;
|
|
397
|
+
case "--grace-ms":
|
|
398
|
+
out.graceMs = Number(next());
|
|
399
|
+
break;
|
|
400
|
+
case "--keep-ms":
|
|
401
|
+
out.keepMs = Number(next());
|
|
402
|
+
break;
|
|
403
|
+
case "--port":
|
|
404
|
+
out.port = Number(next());
|
|
405
|
+
break;
|
|
406
|
+
default:
|
|
407
|
+
if (a.startsWith("--")) {
|
|
408
|
+
throw new Error(`device-agent fleet enable: unknown flag ${a}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return out;
|
|
413
|
+
}
|
|
414
|
+
async function enableCommand(argv, io) {
|
|
415
|
+
let flags;
|
|
416
|
+
try {
|
|
417
|
+
flags = parseEnableArgs(argv);
|
|
418
|
+
}
|
|
419
|
+
catch (e) {
|
|
420
|
+
io.stderr.write(`${e.message}\n`);
|
|
421
|
+
return 2;
|
|
422
|
+
}
|
|
423
|
+
if (flags.help) {
|
|
424
|
+
io.stdout.write(`device-agent fleet enable — register the fleet supervisor as an OS service
|
|
425
|
+
|
|
426
|
+
Usage:
|
|
427
|
+
device-agent fleet enable [options]
|
|
428
|
+
|
|
429
|
+
Options:
|
|
430
|
+
--cli-entry PATH Absolute path to the device-agent CLI entry. Defaults
|
|
431
|
+
to the entry that's running this command.
|
|
432
|
+
--agent-gateway-url URL Agent Gateway URL injected into spawned daemons.
|
|
433
|
+
Defaults to the AGENT_GATEWAY_URL env var.
|
|
434
|
+
--grace-ms <n> HEALTHY → DEGRADED window (env FLEET_ADB_GRACE_MS).
|
|
435
|
+
--keep-ms <n> DEGRADED → GONE window (env FLEET_DAEMON_KEEP_MS).
|
|
436
|
+
--port <n> Override status server port (env FLEET_STATUS_PORT).
|
|
437
|
+
|
|
438
|
+
Currently macOS-only. Linux (systemd --user) and Windows (Task Scheduler)
|
|
439
|
+
support is tracked separately.
|
|
440
|
+
`);
|
|
441
|
+
return 0;
|
|
442
|
+
}
|
|
443
|
+
if (process.platform !== "darwin") {
|
|
444
|
+
io.stderr.write(`device-agent fleet enable: not yet supported on ${process.platform}.\n` +
|
|
445
|
+
"Run `device-agent fleet start` manually instead.\n");
|
|
446
|
+
return 2;
|
|
447
|
+
}
|
|
448
|
+
const cliEntry = flags.cliEntry ?? resolveCliEntryGuess();
|
|
449
|
+
if (!cliEntry) {
|
|
450
|
+
io.stderr.write("fleet enable: could not detect device-agent CLI entry path. " +
|
|
451
|
+
"Re-run with `--cli-entry /absolute/path/to/cli.js`.\n");
|
|
452
|
+
return 2;
|
|
453
|
+
}
|
|
454
|
+
if (!path.isAbsolute(cliEntry)) {
|
|
455
|
+
io.stderr.write(`fleet enable: --cli-entry must be absolute: ${cliEntry}\n`);
|
|
456
|
+
return 2;
|
|
457
|
+
}
|
|
458
|
+
if (!(await fileExists(cliEntry))) {
|
|
459
|
+
io.stderr.write(`fleet enable: --cli-entry not found: ${cliEntry}\n`);
|
|
460
|
+
return 2;
|
|
461
|
+
}
|
|
462
|
+
// Build the operator-level overrides (CLI flags + explicit env). The
|
|
463
|
+
// derivation step seeds defaults (PATH, BEEOS_DEVICE_*_BIN,
|
|
464
|
+
// AGENT_GATEWAY_URL/BRIDGE_URL from ~/.beeos/config.toml) which the
|
|
465
|
+
// overrides win over.
|
|
466
|
+
const overrides = {};
|
|
467
|
+
const explicitAgw = flags.agentGatewayUrl ?? process.env.AGENT_GATEWAY_URL;
|
|
468
|
+
if (explicitAgw)
|
|
469
|
+
overrides.AGENT_GATEWAY_URL = explicitAgw;
|
|
470
|
+
if (flags.graceMs !== undefined && Number.isFinite(flags.graceMs)) {
|
|
471
|
+
overrides.FLEET_ADB_GRACE_MS = String(flags.graceMs);
|
|
472
|
+
}
|
|
473
|
+
if (flags.keepMs !== undefined && Number.isFinite(flags.keepMs)) {
|
|
474
|
+
overrides.FLEET_DAEMON_KEEP_MS = String(flags.keepMs);
|
|
475
|
+
}
|
|
476
|
+
if (flags.port !== undefined && Number.isFinite(flags.port)) {
|
|
477
|
+
overrides.FLEET_STATUS_PORT = String(flags.port);
|
|
478
|
+
}
|
|
479
|
+
const { env, warnings: envWarnings } = await deriveFleetSpawnEnv({
|
|
480
|
+
cliEntry,
|
|
481
|
+
overrides,
|
|
482
|
+
});
|
|
483
|
+
const spec = buildFleetServiceSpec({ cliEntry, env });
|
|
484
|
+
let result;
|
|
485
|
+
try {
|
|
486
|
+
result = await installFleetLaunchd(spec);
|
|
487
|
+
}
|
|
488
|
+
catch (e) {
|
|
489
|
+
io.stderr.write(`fleet enable: install failed: ${e.message}\n`);
|
|
490
|
+
return 1;
|
|
491
|
+
}
|
|
492
|
+
io.stdout.write(`fleet enabled (label=${spec.label})\n`);
|
|
493
|
+
io.stdout.write(` plist: ${result.plistPath}\n`);
|
|
494
|
+
io.stdout.write(` log: ${spec.logFile}\n`);
|
|
495
|
+
io.stdout.write(` bootstrap: ${result.bootstrapped ? "ok" : "skipped (see warnings)"}\n`);
|
|
496
|
+
// Visibility into discovered binaries / config so operators can spot
|
|
497
|
+
// a misconfigured AGENT_GATEWAY_URL or missing scrcpy-bridge BEFORE
|
|
498
|
+
// children start crashing. Logged in dependency order so a missing
|
|
499
|
+
// PATH (rare) shows up first.
|
|
500
|
+
if (env.PATH)
|
|
501
|
+
io.stdout.write(` PATH: captured\n`);
|
|
502
|
+
if (env.AGENT_GATEWAY_URL) {
|
|
503
|
+
io.stdout.write(` agent-gateway-url: ${env.AGENT_GATEWAY_URL}\n`);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
io.stdout.write(` agent-gateway-url: <unset> (set --agent-gateway-url or platform.agent_gateway_url in ~/.beeos/config.toml)\n`);
|
|
507
|
+
}
|
|
508
|
+
if (env.BEEOS_DEVICE_AGENT_BIN) {
|
|
509
|
+
io.stdout.write(` device-agent: ${env.BEEOS_DEVICE_AGENT_BIN}\n`);
|
|
510
|
+
}
|
|
511
|
+
if (env.BEEOS_DEVICE_MCP_BIN) {
|
|
512
|
+
io.stdout.write(` device-mcp: ${env.BEEOS_DEVICE_MCP_BIN}\n`);
|
|
513
|
+
}
|
|
514
|
+
if (env.BEEOS_SCRCPY_BRIDGE_BIN) {
|
|
515
|
+
io.stdout.write(` scrcpy-bridge: ${env.BEEOS_SCRCPY_BRIDGE_BIN}\n`);
|
|
516
|
+
}
|
|
517
|
+
for (const w of envWarnings) {
|
|
518
|
+
io.stdout.write(` warning: ${w}\n`);
|
|
519
|
+
}
|
|
520
|
+
for (const w of result.warnings) {
|
|
521
|
+
io.stdout.write(` warning: ${w}\n`);
|
|
522
|
+
}
|
|
523
|
+
io.stdout.write("Verify: launchctl print gui/$(id -u)/" + spec.label + " # or `device-agent fleet status`\n");
|
|
524
|
+
return 0;
|
|
525
|
+
}
|
|
526
|
+
async function disableCommand(io) {
|
|
527
|
+
if (process.platform !== "darwin") {
|
|
528
|
+
io.stderr.write(`device-agent fleet disable: not yet supported on ${process.platform}.\n`);
|
|
529
|
+
return 2;
|
|
530
|
+
}
|
|
531
|
+
let result;
|
|
532
|
+
try {
|
|
533
|
+
result = await uninstallFleetLaunchd(FLEET_LABEL);
|
|
534
|
+
}
|
|
535
|
+
catch (e) {
|
|
536
|
+
io.stderr.write(`fleet disable failed: ${e.message}\n`);
|
|
537
|
+
return 1;
|
|
538
|
+
}
|
|
539
|
+
if (!result.removed) {
|
|
540
|
+
io.stdout.write(`fleet: nothing installed (${result.plistPath} not found)\n`);
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
io.stdout.write(`fleet disabled — removed ${result.plistPath}\n`);
|
|
544
|
+
}
|
|
545
|
+
for (const w of result.warnings) {
|
|
546
|
+
io.stdout.write(` warning: ${w}\n`);
|
|
547
|
+
}
|
|
548
|
+
return 0;
|
|
549
|
+
}
|
|
550
|
+
async function migrateCommand(io) {
|
|
551
|
+
if (process.platform !== "darwin") {
|
|
552
|
+
io.stderr.write(`device-agent fleet migrate-from-attach: not yet supported on ${process.platform}.\n`);
|
|
553
|
+
return 2;
|
|
554
|
+
}
|
|
555
|
+
let result;
|
|
556
|
+
try {
|
|
557
|
+
result = await migrateFromAttach();
|
|
558
|
+
}
|
|
559
|
+
catch (e) {
|
|
560
|
+
io.stderr.write(`fleet migrate failed: ${e.message}\n`);
|
|
561
|
+
return 1;
|
|
562
|
+
}
|
|
563
|
+
const a = result.removedDeviceAgents.length;
|
|
564
|
+
const b = result.removedScrcpyBridges.length;
|
|
565
|
+
if (a === 0 && b === 0) {
|
|
566
|
+
io.stdout.write("fleet: no legacy per-device launchd entries found\n");
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
io.stdout.write(`Removed ${a} device-agent + ${b} scrcpy-bridge launchd entries; fleet now exclusive\n`);
|
|
570
|
+
for (const label of result.removedDeviceAgents) {
|
|
571
|
+
io.stdout.write(` - ${label}\n`);
|
|
572
|
+
}
|
|
573
|
+
for (const label of result.removedScrcpyBridges) {
|
|
574
|
+
io.stdout.write(` - ${label}\n`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
for (const w of result.warnings) {
|
|
578
|
+
io.stdout.write(` warning: ${w}\n`);
|
|
579
|
+
}
|
|
580
|
+
return 0;
|
|
581
|
+
}
|
|
582
|
+
async function stopCommand(io) {
|
|
583
|
+
// Prefer the in-band shutdown route — it lets a fleet started in the
|
|
584
|
+
// foreground (e.g. via `goreman` or `pnpm dev`) exit cleanly without
|
|
585
|
+
// launchctl. Fall back to launchctl stop only when the HTTP route is
|
|
586
|
+
// unreachable AND we're on macOS.
|
|
587
|
+
const client = new FleetStatusClient();
|
|
588
|
+
if (await client.healthz()) {
|
|
589
|
+
try {
|
|
590
|
+
const accepted = await client.shutdown();
|
|
591
|
+
if (accepted) {
|
|
592
|
+
io.stdout.write("fleet: shutdown requested via HTTP\n");
|
|
593
|
+
return 0;
|
|
594
|
+
}
|
|
595
|
+
io.stdout.write("fleet: HTTP shutdown not supported by this supervisor build; falling back to launchctl\n");
|
|
596
|
+
}
|
|
597
|
+
catch (e) {
|
|
598
|
+
if (!(e instanceof FleetClientError))
|
|
599
|
+
throw e;
|
|
600
|
+
io.stderr.write(`fleet: HTTP shutdown failed: ${e.message}\n`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (process.platform !== "darwin") {
|
|
604
|
+
io.stderr.write("fleet stop: HTTP unreachable and launchctl fallback only available on macOS.\n");
|
|
605
|
+
return 1;
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
const { execFile } = await import("node:child_process");
|
|
609
|
+
const { promisify } = await import("node:util");
|
|
610
|
+
const execFileP = promisify(execFile);
|
|
611
|
+
await execFileP("launchctl", ["stop", FLEET_LABEL]);
|
|
612
|
+
io.stdout.write("fleet: launchctl stop issued\n");
|
|
613
|
+
return 0;
|
|
614
|
+
}
|
|
615
|
+
catch (e) {
|
|
616
|
+
io.stderr.write(`fleet stop: launchctl fallback failed: ${e.message}\n`);
|
|
617
|
+
return 1;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function parseLogsArgs(argv) {
|
|
621
|
+
const out = {};
|
|
622
|
+
for (const a of argv) {
|
|
623
|
+
if (a === "-f" || a === "--follow")
|
|
624
|
+
out.follow = true;
|
|
625
|
+
else if (a === "-h" || a === "--help")
|
|
626
|
+
out.help = true;
|
|
627
|
+
else if (a.startsWith("--"))
|
|
628
|
+
throw new Error(`fleet logs: unknown flag ${a}`);
|
|
629
|
+
else if (!out.serial)
|
|
630
|
+
out.serial = a;
|
|
631
|
+
}
|
|
632
|
+
return out;
|
|
633
|
+
}
|
|
634
|
+
async function logsCommand(argv, io) {
|
|
635
|
+
let flags;
|
|
636
|
+
try {
|
|
637
|
+
flags = parseLogsArgs(argv);
|
|
638
|
+
}
|
|
639
|
+
catch (e) {
|
|
640
|
+
io.stderr.write(`${e.message}\n`);
|
|
641
|
+
return 2;
|
|
642
|
+
}
|
|
643
|
+
if (flags.help) {
|
|
644
|
+
io.stdout.write(`device-agent fleet logs — tail the fleet supervisor log
|
|
645
|
+
|
|
646
|
+
Usage:
|
|
647
|
+
device-agent fleet logs [-f] [serial]
|
|
648
|
+
|
|
649
|
+
Options:
|
|
650
|
+
-f, --follow Follow new lines (like \`tail -f\`)
|
|
651
|
+
serial Filter to lines containing this serial (substring match)
|
|
652
|
+
`);
|
|
653
|
+
return 0;
|
|
654
|
+
}
|
|
655
|
+
const logFile = path.join(beeosHome(), "logs", "device-fleet.log");
|
|
656
|
+
if (!(await fileExists(logFile))) {
|
|
657
|
+
io.stderr.write(`fleet logs: ${logFile} does not exist yet — has the fleet been started?\n` +
|
|
658
|
+
" hint: `device-agent fleet enable` (launchd) or `device-agent fleet start` (foreground)\n");
|
|
659
|
+
return 1;
|
|
660
|
+
}
|
|
661
|
+
const grep = flags.serial;
|
|
662
|
+
// Use `tail` for follow + grep — far simpler than reimplementing
|
|
663
|
+
// streaming + filter in Node and matches what an operator would type
|
|
664
|
+
// by hand. This is intentionally a thin convenience wrapper.
|
|
665
|
+
const { spawn } = await import("node:child_process");
|
|
666
|
+
const tailArgs = flags.follow ? ["-n", "200", "-f", logFile] : ["-n", "1000", logFile];
|
|
667
|
+
const tail = spawn("tail", tailArgs, { stdio: ["ignore", "pipe", "inherit"] });
|
|
668
|
+
return await new Promise((resolve) => {
|
|
669
|
+
if (!tail.stdout) {
|
|
670
|
+
io.stderr.write("fleet logs: failed to capture tail stdout\n");
|
|
671
|
+
resolve(1);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
let buf = "";
|
|
675
|
+
tail.stdout.setEncoding("utf8");
|
|
676
|
+
tail.stdout.on("data", (chunk) => {
|
|
677
|
+
buf += chunk;
|
|
678
|
+
const lines = buf.split("\n");
|
|
679
|
+
buf = lines.pop() ?? "";
|
|
680
|
+
for (const line of lines) {
|
|
681
|
+
if (grep && !line.includes(grep))
|
|
682
|
+
continue;
|
|
683
|
+
io.stdout.write(`${line}\n`);
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
tail.on("close", (code) => {
|
|
687
|
+
if (buf) {
|
|
688
|
+
if (!grep || buf.includes(grep))
|
|
689
|
+
io.stdout.write(buf);
|
|
690
|
+
}
|
|
691
|
+
resolve(code ?? 0);
|
|
692
|
+
});
|
|
693
|
+
tail.on("error", (err) => {
|
|
694
|
+
io.stderr.write(`fleet logs: tail spawn failed: ${err.message}\n`);
|
|
695
|
+
resolve(1);
|
|
696
|
+
});
|
|
697
|
+
// Forward Ctrl-C cleanly when running in --follow.
|
|
698
|
+
process.once("SIGINT", () => {
|
|
699
|
+
tail.kill("SIGINT");
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
704
|
+
/**
|
|
705
|
+
* Best-effort detection of the device-agent CLI entry — what the current
|
|
706
|
+
* process is running. Used as the default `--cli-entry` for `fleet
|
|
707
|
+
* enable` so users don't have to type a path. Returns null when running
|
|
708
|
+
* via REPL / unit test where the entry isn't a stable file.
|
|
709
|
+
*/
|
|
710
|
+
function resolveCliEntryGuess() {
|
|
711
|
+
// Node populates argv[1] with the script path when launched via
|
|
712
|
+
// `node /path/to/script.js …`. Anything else (e.g. REPL, --eval) is
|
|
713
|
+
// not a reliable path we can hand to launchd.
|
|
714
|
+
const script = process.argv[1];
|
|
715
|
+
if (!script || !path.isAbsolute(script))
|
|
716
|
+
return null;
|
|
717
|
+
return script;
|
|
718
|
+
}
|
|
719
|
+
/** Resolve the BeeOS home dir (override-aware). */
|
|
720
|
+
function beeosHome() {
|
|
721
|
+
const override = process.env.BEEOS_HOME;
|
|
722
|
+
if (override && override.length > 0)
|
|
723
|
+
return override;
|
|
724
|
+
return path.join(os.homedir(), ".beeos");
|
|
725
|
+
}
|
|
726
|
+
async function fileExists(p) {
|
|
727
|
+
try {
|
|
728
|
+
await fs.stat(p);
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
// Avoid unused-warning for fleetPlistPath (will be used by tests/docs).
|
|
736
|
+
void fleetPlistPath;
|
|
737
|
+
function printUsage(out) {
|
|
738
|
+
out.write(`device-agent fleet — multi-device supervisor
|
|
739
|
+
|
|
740
|
+
Usage:
|
|
741
|
+
device-agent fleet start [options] Run the supervisor (foreground)
|
|
742
|
+
device-agent fleet enable [--cli-entry PATH] Register OS service (macOS launchd)
|
|
743
|
+
device-agent fleet disable Stop + unregister OS service
|
|
744
|
+
device-agent fleet migrate-from-attach Remove legacy per-device launchd entries
|
|
745
|
+
device-agent fleet stop Graceful shutdown of running supervisor
|
|
746
|
+
device-agent fleet logs [-f] [serial] Tail fleet log (filtered by serial)
|
|
747
|
+
device-agent fleet status Print supervisor liveness
|
|
748
|
+
device-agent fleet devices [--json] List managed devices
|
|
749
|
+
device-agent fleet restart <serial-or-name> Restart one device's mcp + daemon
|
|
750
|
+
device-agent fleet reload Re-read ~/.beeos/devices.json
|
|
751
|
+
|
|
752
|
+
Environment:
|
|
753
|
+
FLEET_STATUS_PORT HTTP status server port (default 7950)
|
|
754
|
+
FLEET_ADB_GRACE_MS HEALTHY → DEGRADED window (default 5000)
|
|
755
|
+
FLEET_DAEMON_KEEP_MS DEGRADED → GONE window (default 300000)
|
|
756
|
+
AGENT_GATEWAY_URL Default Agent Gateway URL for spawned daemons
|
|
757
|
+
BEEOS_HOME Override ~/.beeos location
|
|
758
|
+
`);
|
|
759
|
+
}
|
|
760
|
+
function formatTable(rows) {
|
|
761
|
+
if (rows.length === 0)
|
|
762
|
+
return "";
|
|
763
|
+
const widths = rows[0].map((_, i) => Math.max(...rows.map((r) => (r[i] ?? "").length)));
|
|
764
|
+
const lines = [];
|
|
765
|
+
for (const r of rows) {
|
|
766
|
+
lines.push(r
|
|
767
|
+
.map((cell, i) => (cell ?? "").padEnd(widths[i]))
|
|
768
|
+
.join(" ")
|
|
769
|
+
.trimEnd());
|
|
770
|
+
}
|
|
771
|
+
return `${lines.join("\n")}\n`;
|
|
772
|
+
}
|
|
773
|
+
function formatScrcpy(status) {
|
|
774
|
+
if (!status.enabled)
|
|
775
|
+
return "—";
|
|
776
|
+
if (status.running)
|
|
777
|
+
return status.pid ? `pid:${status.pid}` : "running";
|
|
778
|
+
return status.lastError ? `err:${status.lastError}` : "off";
|
|
779
|
+
}
|
|
780
|
+
function formatAge(ms) {
|
|
781
|
+
if (ms < 1_000)
|
|
782
|
+
return `${ms}ms`;
|
|
783
|
+
const s = Math.floor(ms / 1_000);
|
|
784
|
+
if (s < 60)
|
|
785
|
+
return `${s}s`;
|
|
786
|
+
const m = Math.floor(s / 60);
|
|
787
|
+
if (m < 60)
|
|
788
|
+
return `${m}m${s % 60}s`;
|
|
789
|
+
const h = Math.floor(m / 60);
|
|
790
|
+
return `${h}h${m % 60}m`;
|
|
791
|
+
}
|
|
792
|
+
//# sourceMappingURL=fleet-command.js.map
|