Skip to content

Commit 1bd20e7

Browse files
authored
Expose SDK bridge connectivity check (#25)
## Summary - add `isConnected()` to `@agent-cdp/sdk` so app code can cheaply detect whether the agent-cdp runtime bridge is attached before starting a profiling or capture workflow - reuse a shared binding lookup inside `AgentRuntimeClient` and cover the new connectivity check with SDK tests - document the new SDK call in `docs/SDK.md` ## Backward-compatibility analysis - existing SDK APIs remain unchanged; this only adds a new optional helper - daemon commands, CLI commands, output formats, saved artifacts, and daemon lifecycle behavior are unchanged - the change does not alter defaults or bridge timing; it only exposes the current bridge-installed state as a boolean ## Risks - `isConnected()` reports bridge presence, so it reflects whether the runtime bridge is installed in the app context rather than proving a future command cannot race with a disconnect - consumers may treat the boolean as a stronger guarantee than it is if they do not account for target reconnects between the check and a later SDK call - documentation now needs to keep the distinction clear between bridge presence and successful execution of a later profiling command ## Manual testing - in an app wired with `@agent-cdp/sdk`, start the daemon and select the target with `agent-cdp target select <target-id>` - call `isConnected()` before target selection and verify it returns `false` - select the target, call `isConnected()` again, and verify it returns `true` - run `pnpm --filter @agent-cdp/protocol build`, `pnpm --filter @agent-cdp/sdk test`, and `pnpm --filter @agent-cdp/sdk typecheck`
1 parent 35b22f1 commit 1bd20e7

3 files changed

Lines changed: 36 additions & 4 deletions

File tree

docs/SDK.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,16 @@ Available calls:
3737
- `cpuProfile.start({ name?, samplingIntervalUs? })`
3838
- `cpuProfile.status()`
3939
- `cpuProfile.stop()`
40+
- `isConnected()`
4041

4142
`cpuProfile.status()` returns whether a profile is active plus the active name, elapsed time, and retained session count. `cpuProfile.stop()` returns the recorded session ID.
4243

44+
`isConnected()` returns `true` when the runtime bridge has been injected into the app, which means `agent-cdp` is currently attached to the selected target. Use it when you want a cheap connectivity check before attempting a profiling or capture command.
45+
4346
## Workflow
4447

4548
1. Connect `agent-cdp` to the app target.
4649
2. Trigger `cpuProfile.start()` from app code just before the work you want.
4750
3. Run the target flow.
4851
4. Call `cpuProfile.stop()` and keep the returned session ID.
49-
5. Analyze that same session with the CLI.
52+
5. Analyze that same session with the CLI.

packages/sdk/src/index.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ describe("AgentRuntimeClient", () => {
3232
expect(request.command).toEqual({ type: "js-profile-stop" });
3333
});
3434

35+
it("reports whether the runtime bridge is installed", async () => {
36+
const client = new AgentRuntimeClient();
37+
expect(client.isConnected()).toBe(false);
38+
39+
agentCdpGlobals()[AGENT_CDP_BINDING_NAME] = () => undefined;
40+
expect(client.isConnected()).toBe(true);
41+
42+
vi.resetModules();
43+
const sdk = await import("./index.js");
44+
expect(sdk.isConnected()).toBe(true);
45+
46+
delete agentCdpGlobals()[AGENT_CDP_BINDING_NAME];
47+
expect(client.isConnected()).toBe(false);
48+
expect(sdk.isConnected()).toBe(false);
49+
});
50+
3551
it("sends trace commands and resolves typed bridge responses", async () => {
3652
const sent: string[] = [];
3753
agentCdpGlobals()[AGENT_CDP_BINDING_NAME] = (payload: string) => {

packages/sdk/src/index.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export class AgentRuntimeClient {
6363
this.installReceiver();
6464
}
6565

66+
isConnected(): boolean {
67+
return this.getBinding() !== null;
68+
}
69+
6670
startCpuProfile(options: { name?: string; samplingIntervalUs?: number } = {}): Promise<string> {
6771
return this.send({
6872
type: "js-profile-start",
@@ -186,9 +190,9 @@ export class AgentRuntimeClient {
186190
const startedAt = Date.now();
187191

188192
while (Date.now() - startedAt <= this.timeoutMs) {
189-
const binding = agentCdpGlobals()[this.bindingName];
190-
if (typeof binding === "function") {
191-
return binding as (payload: string) => void;
193+
const binding = this.getBinding();
194+
if (binding) {
195+
return binding;
192196
}
193197

194198
await sleep(BINDING_POLL_INTERVAL_MS);
@@ -203,6 +207,11 @@ export class AgentRuntimeClient {
203207
};
204208
}
205209

210+
private getBinding(): ((payload: string) => void) | null {
211+
const binding = agentCdpGlobals()[this.bindingName];
212+
return typeof binding === "function" ? (binding as (payload: string) => void) : null;
213+
}
214+
206215
private receive(payload: string): void {
207216
let response: AgentRuntimeBridgeResponse;
208217
try {
@@ -228,6 +237,10 @@ export class AgentRuntimeClient {
228237

229238
const defaultClient = new AgentRuntimeClient();
230239

240+
export function isConnected(): boolean {
241+
return defaultClient.isConnected();
242+
}
243+
231244
export const cpuProfile = {
232245
start: (options?: { name?: string; samplingIntervalUs?: number }) => defaultClient.startCpuProfile(options),
233246
status: () => defaultClient.getCpuProfileStatus(),

0 commit comments

Comments
 (0)