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/dist/browser.d.ts +19 -0
- package/dist/browser.js +258 -0
- package/dist/browser.js.map +1 -0
- package/dist/heartbeat.d.ts +6 -0
- package/dist/heartbeat.js +35 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +160 -0
- package/dist/index.js.map +1 -0
- package/dist/pairing.d.ts +7 -0
- package/dist/pairing.js +21 -0
- package/dist/pairing.js.map +1 -0
- package/dist/reporter.d.ts +4 -0
- package/dist/reporter.js +24 -0
- package/dist/reporter.js.map +1 -0
- package/dist/sse-client.d.ts +13 -0
- package/dist/sse-client.js +59 -0
- package/dist/sse-client.js.map +1 -0
- package/package.json +23 -0
- package/src/browser.ts +338 -0
- package/src/heartbeat.ts +48 -0
- package/src/index.ts +206 -0
- package/src/pairing.ts +32 -0
- package/src/reporter.ts +35 -0
- package/src/sse-client.ts +87 -0
- package/tsconfig.json +16 -0
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
|
+
}
|
package/src/reporter.ts
ADDED
|
@@ -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
|
+
}
|