mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
630dc7c99b
* feat(examples): add runnable Node.js SDK examples under examples/sdk/ examples/test-app is a fixture and the repo's only prior examples/ content; the real SDK usage patterns lived only in website/docs/docs/client-api.md with no runnable script anywhere. Adds four standalone, typechecked examples covering the minimum surface from #1463: root client session (create -> open -> snapshot/tap -> close with typed error handling), agent-device/metro (normalizeBaseUrl/resolveRuntimeTransport), agent-device/contracts (centerOfRect on a snapshot node), and agent-device/batch (runBatch for a custom transport). Each imports the published `agent-device/...` subpaths rather than relative src/ paths. examples/sdk/tsconfig.json path-maps those subpaths to src/sdk/ so `pnpm typecheck` (now also run against this tsconfig) checks the examples in CI without a prior build, workspace link, or publish step. Running an example for real still resolves `agent-device` as a self-referencing package after `pnpm build`. src/__tests__/client-api-examples-drift.test.ts guards the examples against drifting from client-api.md's subpath API manifest in both directions, picked up automatically by the existing unit-core vitest project (no new script or workflow needed). examples/README.md indexes the new examples and notes that test-app/ remains a fixture, not an example; it is not renamed or moved. Refs #1463 * fix: address Fallow findings on the new SDK examples Fallow flagged the four examples/sdk/*.ts files as unused files (not reachable from any entry point) and three functions as high complexity. - Register the examples as manual entry points in .fallowrc.json, matching how other standalone scripts (scripts/patch-xcuitest-runner-icon.ts, scripts/runner-request-count/run.ts) are already declared. - Reduce complexity in client-session.ts and contracts-result.ts by extracting device-resolution/error-reporting and rect-assertion helpers out of main(). - Reduce complexity in the drift guard's parseSubpathManifest by splitting bullet-matching and backtick-name extraction into their own functions. Verified: pnpm check:fallow --base <PR base sha> now reports no issues, and pnpm check:tooling / pnpm test:unit stay green. Refs #1463 * fix: compile client-api.md's actual code snippets, not just its symbol manifest Addresses review feedback on #1463's drift guard: the existing guard only parsed the doc's "Public subpath API" bullet manifest and compared imported symbol names, so a fenced ```ts snippet could drift or stop compiling without the guard noticing. Added test/integration/client-api-doc-snippets.test.ts, which extracts every fenced ```ts block from client-api.md and typechecks it against the real agent-device/* sources (reusing examples/sdk/tsconfig.json's existing paths mapping, read via `tsc --showConfig` so there's one source of truth). Free identifiers that continue a `client`/`snapshot` from an earlier snippet are stubbed — typed against the real SDK return type, not `any`, so continuation snippets still get real checking. Lives in the Node integration lane (test/integration/*.test.ts), not vitest's unit-core: it spawns a real tsc Program, well past the unit suite's 2.5s budget. Running this check against the existing doc surfaced real, pre-existing snippet bugs (unrelated to the new examples), fixed here: - "sessions.artifacts": `result.cloudArtifacts` accessed without narrowing the `CloudArtifactsResult | DaemonArtifactsResult` union first. - "Device cloud sessions": `platform`/`device` were passed into the client constructor config, which doesn't accept them; moved to the `apps.open()` call where those fields actually belong. - "Android ADB providers": the inline `exec` handler had no parameter types, so it failed under strict/noImplicitAny; annotated with the real `AndroidAdbExecutorOptions` type. Two further gaps the check surfaced are pre-existing product/API-surface questions out of scope for this PR (not the new examples), so they're allowlisted in KNOWN_DOC_GAPS with comments rather than silently patched: - "Remote Metro helpers" documents prepareRemoteMetro/reloadRemoteMetro/ stopMetroTunnel/resolveRemoteConfigProfile as public, but none of them are exported from agent-device/metro or agent-device/remote-config today. - "Web sessions"/audio probe pass `platform` to `observability.network()`/ `.audio()`, but NetworkOptions/AudioOptions have no `platform` field even though the CLI's network/audio commands accept `--platform`. Refs #1463 * fix: close the doc-snippet compiler's stubbing hole and the two suppressed gaps Addresses the second round of review feedback on #1463's drift guard: 1. stubFreeNamesAndRecompile auto-stubbed every "Cannot find name" as `any`, so a typo like `cliet.apps.open()` would silently pass on the second compile. It now only stubs identifiers in an explicit allowlist (KNOWN_FREE_NAME_STUB_TYPES) — the real SDK-derived continuations (`client`, `androidClient`, `snapshot`) plus the doc's own invented host-glue names, each typed precisely rather than loosely. Anything else is left as a real compile failure. Added a regression test that feeds a `cliet` typo through the guard and asserts it fails. 2. KNOWN_DOC_GAPS filtered six real compiler errors out of the final assertion while the test claimed every snippet compiles. Investigated both and fixed the actual contracts instead of suppressing them: - `prepareMetroRuntime`/`reloadMetro` (src/metro/client-metro.ts) and `stopMetroTunnel` (src/metro/metro.ts) already existed and matched the doc's described workflow almost exactly (same result shape) but were never re-exported from `agent-device/metro`; same for `resolveRemoteConfigProfile` and `agent-device/remote-config`. Added the four exports and fixed the doc's stale function names (`prepareRemoteMetro`/`reloadRemoteMetro`) and one stale field name (`profileKey` -> `companionProfileKey` on the prepare call) to match. - `NetworkOptions`/`AudioOptions` (src/contracts/client-observability.ts) had no `platform` field even though the CLI's `network`/`audio` commands accept `--platform` for the same use case, and the client methods already forward the options object to the daemon generically (`executeCommand('network'|'audio', options)`) — so this was a type gap, not a runtime one. Switched both from AgentDeviceRequestOverrides to DeviceCommandBaseOptions (matching PerfOptions' existing pattern), closing the gap for real instead of stripping `platform` from the doc. - The "Android installFromSource()" snippet was missing its `createAgentDeviceClient` import outright; added it. KNOWN_DOC_GAPS is gone — every fenced snippet now compiles for real, and the test's assertion matches what it claims. 3. Switched the raw `execFileSync` calls to `runCmdSync` from src/utils/exec.ts, per AGENTS.md's process-execution invariant (this is a .ts integration test, not a packaging fixture that needs to stay dependency-free). Refs #1463 * docs: fix stale reloadRemoteMetro() prose reference to reloadMetro() The prose right after the Remote Metro helpers snippet still named the old function; the compile guard only checks the fenced snippet, not surrounding prose, so it didn't catch this leftover from the prior rename. Refs #1463 --------- Co-authored-by: Claude <noreply@anthropic.com>
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
/**
|
|
* Batch orchestration for a custom transport: `runBatch` keeps step
|
|
* validation, inherited flags, serial execution, partial results, and
|
|
* daemon-shaped error envelopes aligned with the CLI's `batch` command, so a
|
|
* bridge that owns command dispatch itself does not have to reimplement them.
|
|
*
|
|
* Demonstrates: `runBatch` from `agent-device/batch`, consumed against the
|
|
* `DaemonResponse` result type from `agent-device/contracts`.
|
|
*
|
|
* Prerequisites: none — `dispatch` below is a stub; a real integration would
|
|
* replace it with a call into the bridge's own command dispatcher.
|
|
*
|
|
* Run: node --experimental-strip-types examples/sdk/batch-orchestration.ts
|
|
*/
|
|
import { runBatch } from 'agent-device/batch';
|
|
import type { DaemonResponse } from 'agent-device/contracts';
|
|
|
|
type BatchRequest = Parameters<typeof runBatch>[0];
|
|
|
|
async function dispatch(stepReq: unknown): Promise<Record<string, unknown>> {
|
|
console.log('dispatching step', stepReq);
|
|
return { handled: true };
|
|
}
|
|
|
|
function bridgeErrorToDaemonResponse(error: unknown): Extract<DaemonResponse, { ok: false }> {
|
|
return {
|
|
ok: false,
|
|
error: {
|
|
code: 'COMMAND_FAILED',
|
|
message: error instanceof Error ? error.message : 'Unknown bridge error',
|
|
},
|
|
};
|
|
}
|
|
|
|
async function handleBatch(req: BatchRequest): Promise<DaemonResponse> {
|
|
return await runBatch(req, req.session ?? 'default', async (stepReq) => {
|
|
try {
|
|
return { ok: true, data: await dispatch(stepReq) };
|
|
} catch (error) {
|
|
return bridgeErrorToDaemonResponse(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
const result = await handleBatch({
|
|
command: 'batch',
|
|
positionals: [],
|
|
flags: {
|
|
batchSteps: [
|
|
{ command: 'wait', input: { text: 'Welcome' } },
|
|
{ command: 'back', input: {} },
|
|
],
|
|
},
|
|
});
|
|
|
|
if (result.ok) {
|
|
console.log(`batch completed: ${JSON.stringify(result.data)}`);
|
|
} else {
|
|
console.error(`batch failed [${result.error.code}]: ${result.error.message}`);
|
|
process.exitCode = 1;
|
|
}
|