agent-device runs as an optional module on workers
维护者通常 1 天内回复
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 8/100
- Issue 类型
- 重构
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- typescript
调研方向
Start with src/daemon/main.ts, where agent-device is wired by static import on the worker path (lines 329-645), and src/core/config.ts, which holds its config type, validator and gateway warning. The change moves this into a new src/agent-device/definition.ts and worker.ts behind the module host from #595. Done means a worker with the module off never calls loadWorker, the old agentDevice key is rejected as unknown, and the listed tests pass under the new capabilities key.
由索引模型根据 Issue 内容生成。
描述
Part of #591.
Scope
Moves agent-device's worker side onto the module host from #595. After
this PR, agent-device is the registry's first module:
- It is turned on with
capabilities.agent-device.enabled. The top-level
agentDevicekey goes. - A worker with it turned off never loads its code.
src/daemon/main.tsno longer wires it on a worker.
Its behaviour is otherwise unchanged, with three exceptions:
- With agent-device off, simlock no longer stops an agent-device daemon
left by a crash (ADR 0030 §2). - On
daemon stop, agent-device's two stop steps are now bounded by the
Hook deadline;
today they have no limit. Its slowest stop is about 22 s: a running
--versioncheck or reap (ps5 s, SIGTERM 10 s, SIGKILL 2 s,
F18). Past 30 s the drivers are disposed with agent-device still
stopping, and the warning names it. - A gateway's warning for
commanduses the generic text (below).
The gateway side stays hand-wired in main.ts until #598. In this PR it
loads agent-device with a dynamic import(), only when it is turned on.
That way no daemon loads agent-device's code through a static import.
Test fixtures that build a whole Config lose their agentDevice line.
That edit is mechanical and is not counted in this task's size. Setup
lines that turn agent-device on (edge.test.ts, gateway-front.test.ts,
main.test.ts, worker-capabilities.e2e.test.ts,
lease-requires.e2e.test.ts) move to capabilities["agent-device"]
instead. The slow-lane files are rewritten (F14).
In short
An operator turns agent-device on under capabilities:
{ "capabilities": { "agent-device": { "enabled": true, "command": "agent-device", "leaseTtlCapMs": 60000 } } }
A config that still says { "agentDevice": { "enabled": true } } gets
Unknown config key: "agentDevice", and agent-device stays off. The
integration is unreleased, so nothing migrates the old key.
flowchart LR
R["module-registry: agent-device *"] --> H[module host]
H -->|on| L["loadWorker() → createWorker *"]
H -->|off| O["agent-device: off<br/>no code loaded, no reap *"]
If this goes wrong: agent-device does not start on a worker that turns it
on, or simlock daemon stop leaves its process behind.
Technical spec
Facts
| # | Fact | Source |
|---|---|---|
| F1 | main.ts statically imports 15 names from ../agent-device/index.js. |
src/daemon/main.ts:5-21 |
| F2 | On a worker, main.ts builds agent-device's supervisor, edge parts, lease-end participant, capability source (with an off stub when disabled), relay target, HTTP front, stop steps and start. |
src/daemon/main.ts:329-357, :372, :484-488, :522-527, :548-551, :571-575, :592-596, :630-645 |
| F3 | agentDevice.stop() runs in dispose before the drivers, because the Android driver stops the adb server agent-device uses. |
src/daemon/main.ts:548-551 |
| F4 | stopAuxiliary disposes the edge and cancels waiting allocates through leasing.cancelPending. |
src/daemon/main.ts:571-575 |
| F5 | onSocketClaimed starts the supervisor, not awaited, unless a test passed agentDeviceUpstream. |
src/daemon/main.ts:592-596 |
| F6 | With config.enabled false, the supervisor starts in off; start() reaps a recorded daemon, then stays off. |
src/agent-device/host/agent-device-supervisor.ts:163, :255-286 |
| F7 | Two supervisor tests pin the off path. | src/agent-device/host/agent-device-supervisor.test.ts:346, :1161 |
| F8 | The agent-device config is { enabled, command, leaseTtlCapMs? }, default { enabled: false, command: "agent-device" }, leaseTtlCapMs from 5000 to lease.maxTtlMs; command is worker-only. The core holds its type, the cap check and its call, its gateway key, its gateway warning, its default and its validator. The validators come from src/core/validation.ts, which src/core/index.ts does not export; invalidValue words an error Invalid config value for "<path>": expected <what>. |
src/core/config.ts:253-257, :410, :452-459, :485, :494, :522-530, :763, :916-920; src/core/validation.ts:17-19, :17-118 |
| F9 | Config tests for agent-device: one describe block of 8 tests, plus a gateway test. | src/core/config.test.ts:1961-2036, :1776 |
| F10 | configSchema names agentDevice. |
src/contract/schemas.ts:902-906 |
| F11 | Tests pass agentDeviceUpstream or agentDeviceHealth to startDaemon. |
src/agent-device/edge.test.ts:165; src/agent-device/gateway-front.test.ts:270; src/daemon/main.test.ts:2430; src/daemon/worker-capabilities.e2e.test.ts:66; src/daemon/lease-requires.e2e.test.ts:89 |
| F12 | Five assertions name the old key: three in those suites, two in the supervisor's tests. | src/agent-device/gateway-front.test.ts:703, :709-711; src/agent-device/edge.test.ts:2098-2105; src/agent-device/host/agent-device-supervisor.test.ts:421, :480 |
| F13 | The supervisor's remedies tell the user to set agentDevice.command. |
src/agent-device/host/agent-device-supervisor.ts:228-230 |
| F14 | The slow-lane tests write and delete agentDevice in a config. |
e2e/slow-agent-device.test.ts; e2e/slow-agent-device-gateway.test.ts:160, :173, :264, :280 |
| F15 | docs/CONFIGURATION.md documents the agentDevice.* keys, the gateway slice, and "A daemon a killed Simlock left behind is stopped at the next start". |
docs/CONFIGURATION.md:38-40, :162-169 |
| F16 | The gateway path reads config.agentDevice for enabled and leaseTtlCapMs. |
src/daemon/main.ts:827, :958-960 |
| F17 | dependency-cruiser records import() as dynamic-import, import type as type-only, and import("x").T as type-import. |
node_modules/dependency-cruiser/src/extract/tsc/extract-typescript-deps.mjs:82, :424, :436 (18.4.0) |
| F18 | The supervisor's waits: ps 5 s, --version 10 s, SIGTERM grace 10 s, SIGKILL grace 2 s. |
src/agent-device/host/agent-device-supervisor.ts:76-80 |
| F19 | agent-device's front imports src/http's helpers and types. |
src/agent-device/front/front.ts:1; src/agent-device/front/allocate.ts:4-6; src/agent-device/host/host.ts:3 |
| F20 | Today a gateway warns Ignoring "agentDevice.command": it names the program a worker runs, and this daemon runs in gateway mode. |
src/core/config.ts:528 |
Modules touched
flowchart LR
main["daemon/main.ts *"] --> host["modules host"]
main --> reg["module-registry *"]
reg --> def["agent-device/definition *new*"]
def -. loadWorker .-> w["agent-device/worker *new*"]
main -. "dynamic import (gateway, until #598)" .-> idx["agent-device/index *"]
w --> sup["agent-device supervisor *"]
cfg["core/config.ts, core/index.ts *"]
contract["contract/schemas.ts, protocol.ts *"]
Surface and extension points
src/agent-device/definition.ts exports agentDeviceModule, an
OptionalModuleDefinition:
name: "agent-device".config:- F8's validator without
enabled, which the core owns (#595), built
from validators thatsrc/core/index.tsnow exports
(objectValidator,nonEmptyString,integerInRange,
invalidValue); - F8's defaults without
enabled; gatewayKeys: ["leaseTtlCapMs"];- a
checkthatleaseTtlCapMsis at mostlease.maxTtlMs, failing
withinvalidValue's text,
Invalid config value for "capabilities.agent-device.leaseTtlCapMs": expected at most lease.maxTtlMs.
- F8's validator without
- Every agent-device piece F8 lists leaves
src/core/config.ts. loadWorker: () => import("./worker.js"). NoloadGatewayuntil #598.
src/agent-device/worker.ts exports createWorker(ctx). It builds the
supervisor and the edge parts from ctx (F2), and returns:
capability, from today's capability source;relayTarget, from today's relay target;mount, the HTTP front's handler (the front keeps usingsrc/http's
helpers, F19; ADR 0030 §6), withctx.config.leaseTtlCapMsand
maxRunMs = ctx.daemonConfig.lease.maxTtlMs;findings, today'sagentDeviceFindingsreturning
{ message, remedy }; the host now stampsmodule(#596's line moves
here);hooks.afterSocketClaimed:void supervisor.start()(F5);hooks.beforeDeviceReclaim: the lease-end participant;hooks.beforeDrain: dispose the edge, then cancel waiting allocates
throughctx.cancelPending(F4);hooks.beforeDriversDispose:supervisor.stop()(F3).
src/agent-device/testing.ts exports
agentDeviceModuleWith({ upstream?, health? }), a definition whose
createWorker uses the given fakes. With upstream, the supervisor is
never started, as F5 does today.
Other changes:
src/agent-device/index.tskeeps only what the gateway path uses
(F16).main.ts's gateway path loads it withawait import(...)only
whencapabilities.agent-device.enabledistrue. It reads the section
through the typedefinition.tsexports, and a missing section counts
as off. Every other name F1 lists leavesmain.ts.StartDaemonOptionsdropsagentDeviceUpstreamandagentDeviceHealth.- The supervisor drops its
enabledbranch (F6). It is built only for a
turned-on module. - The supervisor's remedies say
capabilities.agent-device.command(F13). - A gateway warns
Ignoring "capabilities.agent-device.command": it configures a worker, and this daemon runs in gateway mode.,
#595's generic text, in place of F20's. - New dependency-cruiser rule
main-loads-modules-lazily:main.ts
importssrc/agent-deviceonly asdynamic-import,type-onlyor
type-import(F17). #598 tightens it to no import at all.
Contract and event changes
configSchema drops agentDevice (F10); agent-device's section travels in
capabilities. This PR bumps the protocol by one. No event changes.
Other code on the same state
- The gateway path in
main.ts(F16) reads agent-device's config. It
switches toconfig.capabilities["agent-device"], with the same
behaviour. - #595's host tests pin agent-device's hand-wired entry first in the
capability list, and its stop steps before the hooks. With the hand
wiring gone, those tests assert the registry order only, with
agent-device as a registered module. - The core doctor gets only
host.findings()atmain.ts:329. - A leftover agent-device daemon while off: nothing acts on it any
more. The next start with agent-device on reaps it, as today (F6).
Rules in play
architecture.mdrules 18 and 21:main.tsnames no module on the
worker path.safety.md: the reap's ownership proof is unchanged.testing.md: delete only F7's two tests, whose path is gone. F9's tests
move, they are not deleted.documentation.md: F15, plusCLI.md,HTTP-API.mdandCLIENT.md
wherever they writeagentDevice.
Tests
Seam:
startDaemon({ optionalModules: [agentDeviceModuleWith({ upstream: fake })], configOverrides: { capabilities: { "agent-device": … } } })
in place of agentDeviceUpstream in the suites F11 names, modelled on
src/agent-device/edge.test.ts. Those suites still pass, with their setup
changed and F12's three assertions renamed to the new key.
- a worker with no
capabilities["agent-device"]section answers status.get with{ name: agent-device, state: off }, and so does one withenabled: false - a worker with agent-device off never calls agent-device's
loadWorker(a spying definition that wrapsagentDeviceModule) - a gateway with agent-device off, or with no section for it, mounts no
/v1/agent-device(as today) - a worker whose config has only the old
agentDevicekey warnsUnknown config key: "agentDevice"and reports agent-deviceoff - a worker with agent-device off starts no supervisor: no
psruns and no signal is sent, even with a recorded daemon on disk - F9's tests move to
src/agent-device/definition.test.ts, undercapabilities.agent-device(as today):- the defaults, and an explicit section kept as written;
commandrefused when empty, and when a number;leaseTtlCapMsaccepted at 5000 and atlease.maxTtlMs, refused as a fraction, refused below 5000, and refused abovelease.maxTtlMswithinvalidValue's text namingcapabilities.agent-device.leaseTtlCapMs;- a loaded config parsed through
configSchemakeeps the section; - on a gateway,
enabledandleaseTtlCapMsread without a warning, andcommandwarned about with the generic text
startDaemonwith agent-device on stops the agent-device child before the drivers are disposed (as today,main.test.ts:2449)- with agent-device on, a release still waits for agent-device's lease end before the device is reclaimed (as today)
daemon stopcancels an agent-device allocate waiting in the queue before leasing settles (as today)doctor.runreports{ kind: "module-unavailable", module: "agent-device" }, with the module stamped by the host- the supervisor's not-found and not-agent-device remedies name
capabilities.agent-device.command(F12's two supervisor assertions) config.getshows agent-device's section undercapabilities["agent-device"]and noagentDevicekey- the conformance suite runs over agent-device as a registered module
- dependency-cruiser fails a value import of
src/agent-devicefrommain.ts(a lint-harness case);pnpm lintpassing on this PR'smain.ts, with its dynamic and type imports, shows the allowed kinds pass - the protocol version is one above the base branch's
Done when
- Every Tests line passes, and every other test in the suites F11 names
passes with only its setup changed and F12's assertions renamed. - F7's two supervisor tests are deleted. No other agent-device test is
deleted. docs/CONFIGURATION.mddocumentscapabilities.agent-device.*. Its
leftover-daemon line says it applies with
capabilities.agent-device.enabledtrue.- F14's config writes and deletes use
capabilities["agent-device"]. On a
Mac with an iOS runtime:scripts/slow-e2e.sh e2e/slow-agent-device.test.ts
andscripts/slow-e2e.sh e2e/slow-agent-device-gateway.test.tspass.
Out of scope
- The gateway side moving onto the host (#598).
- The doctor finding's shape (#596).
Depends on
- #595
Written by an agent.
- 主要语言
- TypeScript
- 星标
- 19
- 派生
- 1
- 平均合并
- 7 小时 57 分钟
- 30 天内合并 PR
- 213
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
callstackincubator/simlock 的其他 Issue
-
Lint does not enforce index.ts boundaries for cleanup/, routing/, warm-pool/ and contract/ helpers未关闭bug:new
难度 2/5 1-3 小时 新手友好度 70/100
callstackincubator/simlock#445 ·
维护者通常 1 天内回复
-
bug:new
难度 2/5 1-3 小时 新手友好度 78/100
callstackincubator/simlock#424 ·
维护者通常 1 天内回复
-
daemon-lifecycle e2e: the it.fails status test passes for the wrong reason可能已有人在做 @V3RON 于 7 天前认领。 未关闭bug:triage
难度 2/5 1-3 小时 新手友好度 84/100
callstackincubator/simlock#422 · 1 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
feature:spec
难度 5/5 一周以上 新手友好度 8/100
callstackincubator/simlock#602 ·
维护者通常 1 天内回复
-
agent-device's gateway side runs as an optional module可能已有人在做 @V3RON 今天认领。 未关闭task:ready
callstackincubator/simlock#598 · 2 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
查看 callstackincubator/simlock 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 68/100
apache/rocketmq-dashboard#6872 ·
维护者通常 3 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 70/100
fab-geocommuns/RNB-site#539 ·
维护者通常 2 天内回复
-
Guest multiview tile label leaks raw virtual-source-id "Whip" instead of the guest name / slot label未关闭
难度 2/5 1-3 小时 新手友好度 62/100
Eyevinn/open-live-studio#211 ·
维护者通常 1 天内回复
-
documentation good first issue
难度 2/5 半天 新手友好度 72/100
sujithputta02/Esapay#13 ·
-
难度 2/5 1-3 小时 新手友好度 65/100
ehmpathy/rhachet-roles-bhuild#468 ·
维护者通常 1 天内回复