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/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,