agent-browser-loop 0.2.2 → 0.3.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/.claude/skills/agent-browser-loop/REFERENCE.md +131 -0
- package/.claude/skills/agent-browser-loop/SKILL.md +36 -4
- package/README.md +39 -17
- package/package.json +1 -1
- package/src/browser.ts +22 -0
- package/src/cli.ts +451 -7
- package/src/commands.ts +11 -0
- package/src/daemon.ts +53 -8
- package/src/index.ts +15 -0
- package/src/profiles.ts +414 -0
- package/src/server.ts +148 -0
package/src/server.ts
CHANGED
|
@@ -19,7 +19,16 @@ import {
|
|
|
19
19
|
} from "./commands";
|
|
20
20
|
import { createIdGenerator } from "./id";
|
|
21
21
|
import { log } from "./log";
|
|
22
|
+
import {
|
|
23
|
+
deleteProfile,
|
|
24
|
+
listProfiles,
|
|
25
|
+
loadProfile,
|
|
26
|
+
resolveProfilePath,
|
|
27
|
+
resolveStorageStateOption,
|
|
28
|
+
saveProfile,
|
|
29
|
+
} from "./profiles";
|
|
22
30
|
import { formatStateText } from "./state";
|
|
31
|
+
import type { StorageState } from "./types";
|
|
23
32
|
|
|
24
33
|
export interface BrowserServerConfig {
|
|
25
34
|
host?: string;
|
|
@@ -232,6 +241,7 @@ function getWaitCondition(data: WaitRequest): WaitCondition {
|
|
|
232
241
|
const createSessionBodySchema = z.object({
|
|
233
242
|
headless: z.boolean().optional(),
|
|
234
243
|
userDataDir: z.string().optional(),
|
|
244
|
+
profile: z.string().optional(),
|
|
235
245
|
});
|
|
236
246
|
|
|
237
247
|
const sessionParamsSchema = z.object({
|
|
@@ -528,16 +538,43 @@ const waitRoute = createRoute({
|
|
|
528
538
|
},
|
|
529
539
|
});
|
|
530
540
|
|
|
541
|
+
const closeSessionBodySchema = z
|
|
542
|
+
.object({
|
|
543
|
+
saveProfile: z.string().optional(),
|
|
544
|
+
global: z.boolean().optional(),
|
|
545
|
+
private: z.boolean().optional(),
|
|
546
|
+
})
|
|
547
|
+
.optional();
|
|
548
|
+
|
|
531
549
|
const closeRoute = createRoute({
|
|
532
550
|
method: "post",
|
|
533
551
|
path: "/session/{sessionId}/close",
|
|
534
552
|
request: {
|
|
535
553
|
params: sessionParamsSchema,
|
|
554
|
+
body: {
|
|
555
|
+
required: false,
|
|
556
|
+
content: {
|
|
557
|
+
"application/json": {
|
|
558
|
+
schema: closeSessionBodySchema,
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
},
|
|
536
562
|
},
|
|
537
563
|
responses: {
|
|
538
564
|
204: {
|
|
539
565
|
description: "Session closed",
|
|
540
566
|
},
|
|
567
|
+
200: {
|
|
568
|
+
description: "Session closed with profile saved",
|
|
569
|
+
content: {
|
|
570
|
+
"application/json": {
|
|
571
|
+
schema: z.object({
|
|
572
|
+
profileSaved: z.string().optional(),
|
|
573
|
+
profilePath: z.string().optional(),
|
|
574
|
+
}),
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
},
|
|
541
578
|
404: {
|
|
542
579
|
description: "Session not found",
|
|
543
580
|
content: {
|
|
@@ -745,6 +782,14 @@ export function startBrowserServer(config: BrowserServerConfig) {
|
|
|
745
782
|
overrides.userDataDir = body.userDataDir;
|
|
746
783
|
}
|
|
747
784
|
|
|
785
|
+
// Handle profile option
|
|
786
|
+
if (body?.profile) {
|
|
787
|
+
const storageState = resolveStorageStateOption(body.profile);
|
|
788
|
+
if (typeof storageState === "object") {
|
|
789
|
+
overrides.storageState = storageState;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
748
793
|
const id = await createNewSession(overrides);
|
|
749
794
|
return c.json({ sessionId: id }, 200);
|
|
750
795
|
},
|
|
@@ -886,11 +931,29 @@ export function startBrowserServer(config: BrowserServerConfig) {
|
|
|
886
931
|
app.openapi(closeRoute, async (c) => {
|
|
887
932
|
const { sessionId } = c.req.valid("param");
|
|
888
933
|
const session = getSessionOrThrow(sessions, sessionId);
|
|
934
|
+
const body = c.req.valid("json");
|
|
889
935
|
|
|
890
936
|
return withSession(session, async () => {
|
|
937
|
+
let profilePath: string | undefined;
|
|
938
|
+
|
|
939
|
+
// Save profile before closing if requested
|
|
940
|
+
if (body?.saveProfile) {
|
|
941
|
+
const storageState =
|
|
942
|
+
(await session.browser.saveStorageState()) as StorageState;
|
|
943
|
+
profilePath = saveProfile(body.saveProfile, storageState, {
|
|
944
|
+
global: body.global,
|
|
945
|
+
private: body.private,
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
|
|
891
949
|
await session.browser.stop();
|
|
892
950
|
sessions.delete(sessionId);
|
|
893
951
|
idGenerator.release(sessionId);
|
|
952
|
+
|
|
953
|
+
if (profilePath) {
|
|
954
|
+
return c.json({ profileSaved: body?.saveProfile, profilePath }, 200);
|
|
955
|
+
}
|
|
956
|
+
|
|
894
957
|
return c.body(null, 204);
|
|
895
958
|
});
|
|
896
959
|
});
|
|
@@ -905,6 +968,91 @@ export function startBrowserServer(config: BrowserServerConfig) {
|
|
|
905
968
|
});
|
|
906
969
|
});
|
|
907
970
|
|
|
971
|
+
// ========================================================================
|
|
972
|
+
// Profile Endpoints
|
|
973
|
+
// ========================================================================
|
|
974
|
+
|
|
975
|
+
// GET /profiles - list all profiles
|
|
976
|
+
app.get("/profiles", (c) => {
|
|
977
|
+
const profiles = listProfiles();
|
|
978
|
+
return c.json(profiles);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
// GET /profiles/:name - get profile contents
|
|
982
|
+
app.get("/profiles/:name", (c) => {
|
|
983
|
+
const name = c.req.param("name");
|
|
984
|
+
const profile = loadProfile(name);
|
|
985
|
+
if (!profile) {
|
|
986
|
+
return c.json({ error: `Profile not found: ${name}` }, 404);
|
|
987
|
+
}
|
|
988
|
+
const resolved = resolveProfilePath(name);
|
|
989
|
+
return c.json({
|
|
990
|
+
name,
|
|
991
|
+
scope: resolved?.scope,
|
|
992
|
+
path: resolved?.path,
|
|
993
|
+
profile,
|
|
994
|
+
});
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
// POST /profiles/:name - save profile from session or body
|
|
998
|
+
app.post("/profiles/:name", async (c) => {
|
|
999
|
+
const name = c.req.param("name");
|
|
1000
|
+
const body = await c.req.json().catch(() => ({}));
|
|
1001
|
+
|
|
1002
|
+
let storageState: StorageState;
|
|
1003
|
+
|
|
1004
|
+
// If sessionId provided, get storage state from that session
|
|
1005
|
+
if (body.sessionId) {
|
|
1006
|
+
const session = sessions.get(body.sessionId);
|
|
1007
|
+
if (!session) {
|
|
1008
|
+
return c.json({ error: `Session not found: ${body.sessionId}` }, 404);
|
|
1009
|
+
}
|
|
1010
|
+
storageState = (await session.browser.saveStorageState()) as StorageState;
|
|
1011
|
+
} else if (body.cookies || body.origins) {
|
|
1012
|
+
// Direct storage state in body
|
|
1013
|
+
storageState = {
|
|
1014
|
+
cookies: body.cookies || [],
|
|
1015
|
+
origins: body.origins || [],
|
|
1016
|
+
};
|
|
1017
|
+
} else {
|
|
1018
|
+
return c.json(
|
|
1019
|
+
{
|
|
1020
|
+
error: "Either sessionId or storage state (cookies/origins) required",
|
|
1021
|
+
},
|
|
1022
|
+
400,
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
const savedPath = saveProfile(name, storageState, {
|
|
1027
|
+
global: body.global,
|
|
1028
|
+
private: body.private,
|
|
1029
|
+
description: body.description,
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
return c.json({
|
|
1033
|
+
name,
|
|
1034
|
+
path: savedPath,
|
|
1035
|
+
cookies: storageState.cookies.length,
|
|
1036
|
+
origins: storageState.origins.length,
|
|
1037
|
+
});
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
// DELETE /profiles/:name - delete profile
|
|
1041
|
+
app.delete("/profiles/:name", (c) => {
|
|
1042
|
+
const name = c.req.param("name");
|
|
1043
|
+
const resolved = resolveProfilePath(name);
|
|
1044
|
+
if (!resolved) {
|
|
1045
|
+
return c.json({ error: `Profile not found: ${name}` }, 404);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const deleted = deleteProfile(name);
|
|
1049
|
+
if (!deleted) {
|
|
1050
|
+
return c.json({ error: `Failed to delete profile: ${name}` }, 500);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
return c.json({ deleted: name, path: resolved.path });
|
|
1054
|
+
});
|
|
1055
|
+
|
|
908
1056
|
const server = Bun.serve({
|
|
909
1057
|
hostname: host,
|
|
910
1058
|
port,
|