sf-builder-agent 0.7.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/src/pairing.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Pairing — exchange a one-time code for a persistent auth token.
3
+ */
4
+
5
+ export async function pair(
6
+ baseUrl: string,
7
+ sessionId: string,
8
+ code: string
9
+ ): Promise<{ token: string; sessionId: string }> {
10
+ const url = `${baseUrl}/api/sessions/${sessionId}/agent/pair`;
11
+
12
+ const res = await fetch(url, {
13
+ method: "POST",
14
+ headers: { "Content-Type": "application/json" },
15
+ body: JSON.stringify({ code }),
16
+ });
17
+
18
+ if (!res.ok) {
19
+ const body = await res.text().catch(() => "");
20
+ throw new Error(
21
+ `Pairing failed (${res.status}): ${body || res.statusText}`
22
+ );
23
+ }
24
+
25
+ const data = (await res.json()) as { token?: string; sessionId?: string };
26
+
27
+ if (!data.token) {
28
+ throw new Error("Pairing response did not include a token");
29
+ }
30
+
31
+ return { token: data.token, sessionId: data.sessionId ?? sessionId };
32
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Reporter — POST command results back to the web app.
3
+ */
4
+
5
+ export async function reportResult(
6
+ baseUrl: string,
7
+ sessionId: string,
8
+ token: string,
9
+ commandId: string,
10
+ status: "success" | "error",
11
+ result?: Record<string, unknown>
12
+ ): Promise<void> {
13
+ const url = `${baseUrl}/api/sessions/${sessionId}/agent/result`;
14
+
15
+ try {
16
+ const res = await fetch(url, {
17
+ method: "POST",
18
+ headers: {
19
+ Authorization: `Bearer ${token}`,
20
+ "Content-Type": "application/json",
21
+ },
22
+ body: JSON.stringify({ commandId, status, result }),
23
+ });
24
+
25
+ if (!res.ok) {
26
+ const body = await res.text().catch(() => "");
27
+ console.error(`\u274C Report failed (${res.status}): ${body || res.statusText}`);
28
+ }
29
+ } catch (err) {
30
+ console.error(
31
+ `\u274C Report error:`,
32
+ err instanceof Error ? err.message : err
33
+ );
34
+ }
35
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * SSE client — listens for browser-automation commands from the server.
3
+ */
4
+
5
+ import { EventSource } from "eventsource";
6
+
7
+ interface IncomingCommand {
8
+ id: string;
9
+ commandType: string;
10
+ stepKey: string | null;
11
+ payload: Record<string, unknown>;
12
+ }
13
+
14
+ export function connectSSE(
15
+ baseUrl: string,
16
+ sessionId: string,
17
+ token: string,
18
+ onCommand: (command: IncomingCommand) => void,
19
+ onError: (error: Error) => void
20
+ ): { close: () => void } {
21
+ const url = `${baseUrl}/api/sessions/${sessionId}/agent/commands?token=${encodeURIComponent(token)}`;
22
+
23
+ let es: EventSource | null = null;
24
+ let closed = false;
25
+ let retryDelay = 1_000;
26
+ const MAX_RETRY = 30_000;
27
+
28
+ function connect() {
29
+ if (closed) return;
30
+
31
+ es = new EventSource(url);
32
+
33
+ es.addEventListener("command", (evt: MessageEvent) => {
34
+ try {
35
+ const data = JSON.parse(evt.data) as IncomingCommand;
36
+ onCommand(data);
37
+ // Reset backoff on successful message
38
+ retryDelay = 1_000;
39
+ } catch (err) {
40
+ console.error(
41
+ "\u274C Failed to parse command:",
42
+ err instanceof Error ? err.message : err
43
+ );
44
+ }
45
+ });
46
+
47
+ es.addEventListener("ping", () => {
48
+ // Keep-alive — nothing to do
49
+ });
50
+
51
+ es.addEventListener("open", () => {
52
+ console.log("\u2705 Connected to command stream");
53
+ retryDelay = 1_000;
54
+ });
55
+
56
+ es.onerror = (err: Event) => {
57
+ if (closed) return;
58
+
59
+ const message =
60
+ err && typeof err === "object" && "message" in err
61
+ ? (err as unknown as { message: string }).message
62
+ : "SSE connection error";
63
+
64
+ console.error(`\u274C SSE error: ${message} — reconnecting in ${retryDelay / 1_000}s`);
65
+ onError(new Error(message));
66
+
67
+ // Close the broken connection and schedule reconnect
68
+ es?.close();
69
+ es = null;
70
+
71
+ setTimeout(() => {
72
+ retryDelay = Math.min(retryDelay * 2, MAX_RETRY);
73
+ connect();
74
+ }, retryDelay);
75
+ };
76
+ }
77
+
78
+ connect();
79
+
80
+ return {
81
+ close() {
82
+ closed = true;
83
+ es?.close();
84
+ es = null;
85
+ },
86
+ };
87
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "declaration": true,
11
+ "sourceMap": true,
12
+ "resolveJsonModule": true,
13
+ "skipLibCheck": true
14
+ },
15
+ "include": ["src"]
16
+ }