libretto 0.6.8 → 0.6.9

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.
@@ -687,6 +687,15 @@ const runCommand = SimpleCLI.command({
687
687
  `Creating ${providerName} browser session (session: ${ctx.session})...`
688
688
  );
689
689
  const providerSession = await provider.createSession();
690
+ ctx.logger.info("run-provider-session-created", {
691
+ provider: providerName,
692
+ sessionId: providerSession.sessionId,
693
+ cdpEndpoint: providerSession.cdpEndpoint,
694
+ liveViewUrl: providerSession.liveViewUrl
695
+ });
696
+ if (providerSession.liveViewUrl) {
697
+ console.log(`View live session: ${providerSession.liveViewUrl}`);
698
+ }
690
699
  console.log(`Connecting to ${providerName} browser...`);
691
700
  cdpEndpoint = providerSession.cdpEndpoint;
692
701
  providerInfo = {
@@ -714,7 +723,10 @@ const runCommand = SimpleCLI.command({
714
723
  } finally {
715
724
  if (provider && providerInfo) {
716
725
  try {
717
- await provider.closeSession(providerInfo.sessionId);
726
+ const result = await provider.closeSession(providerInfo.sessionId);
727
+ if (result.replayUrl) {
728
+ console.log(`View recording: ${result.replayUrl}`);
729
+ }
718
730
  } catch (cleanupErr) {
719
731
  console.error(
720
732
  `Failed to clean up ${providerInfo.name} session ${providerInfo.sessionId}:`,
@@ -431,8 +431,12 @@ async function runOpenWithProvider(rawUrl, providerName, provider, session, logg
431
431
  logger.info("open-provider-session-created", {
432
432
  provider: providerName,
433
433
  sessionId: providerSession.sessionId,
434
- cdpEndpoint: providerSession.cdpEndpoint
434
+ cdpEndpoint: providerSession.cdpEndpoint,
435
+ liveViewUrl: providerSession.liveViewUrl
435
436
  });
437
+ if (providerSession.liveViewUrl) {
438
+ console.log(`View live session: ${providerSession.liveViewUrl}`);
439
+ }
436
440
  console.log(`Connecting to ${providerName} browser...`);
437
441
  let browser = null;
438
442
  try {
@@ -572,6 +576,7 @@ async function runClose(session, logger) {
572
576
  console.log(`No browser running for session "${session}".`);
573
577
  return;
574
578
  }
579
+ let replayUrl;
575
580
  if (state.provider) {
576
581
  logger.info("close-provider", {
577
582
  session,
@@ -580,7 +585,8 @@ async function runClose(session, logger) {
580
585
  });
581
586
  try {
582
587
  const provider = getCloudProviderApi(state.provider.name);
583
- await provider.closeSession(state.provider.sessionId);
588
+ const result = await provider.closeSession(state.provider.sessionId);
589
+ replayUrl = result.replayUrl;
584
590
  } catch (err) {
585
591
  logger.warn("close-provider-error", {
586
592
  session,
@@ -601,8 +607,11 @@ async function runClose(session, logger) {
601
607
  }
602
608
  }
603
609
  clearSessionState(session, logger);
604
- logger.info("close-success", { session });
610
+ logger.info("close-success", { session, replayUrl });
605
611
  console.log(`Browser closed (session: ${session}).`);
612
+ if (replayUrl) {
613
+ console.log(`View recording: ${replayUrl}`);
614
+ }
606
615
  }
607
616
  function waitForCloseSignalWindow(ms) {
608
617
  return new Promise((r) => setTimeout(r, ms));
@@ -671,6 +680,7 @@ async function runCloseAll(logger, options) {
671
680
  return;
672
681
  }
673
682
  const failedProviderSessions = /* @__PURE__ */ new Set();
683
+ const replayUrls = [];
674
684
  for (const target of closable) {
675
685
  if (target.provider) {
676
686
  logger.info("close-all-provider", {
@@ -680,7 +690,13 @@ async function runCloseAll(logger, options) {
680
690
  });
681
691
  try {
682
692
  const provider = getCloudProviderApi(target.provider.name);
683
- await provider.closeSession(target.provider.sessionId);
693
+ const result = await provider.closeSession(target.provider.sessionId);
694
+ if (result.replayUrl) {
695
+ replayUrls.push({
696
+ session: target.session,
697
+ replayUrl: result.replayUrl
698
+ });
699
+ }
684
700
  } catch (err) {
685
701
  logger.warn("close-all-provider-error", {
686
702
  session: target.session,
@@ -781,6 +797,9 @@ async function runCloseAll(logger, options) {
781
797
  if (forceKilled > 0) {
782
798
  console.log(`Force-killed ${forceKilled} session(s).`);
783
799
  }
800
+ for (const { session, replayUrl } of replayUrls) {
801
+ console.log(`View recording (${session}): ${replayUrl}`);
802
+ }
784
803
  }
785
804
  async function runConnect(cdpUrl, session, logger, accessMode = "write-access") {
786
805
  logger.info("connect-start", { cdpUrl, session, accessMode });
@@ -45,6 +45,7 @@ function createBrowserbaseProvider() {
45
45
  `Browserbase API error closing session ${sessionId} (${resp.status}): ${body}`
46
46
  );
47
47
  }
48
+ return {};
48
49
  }
49
50
  };
50
51
  }
@@ -38,6 +38,7 @@ function createKernelProvider() {
38
38
  `Kernel API error closing session ${sessionId} (${resp.status}): ${body}`
39
39
  );
40
40
  }
41
+ return {};
41
42
  }
42
43
  };
43
44
  }
@@ -34,7 +34,8 @@ function createLibrettoCloudProvider() {
34
34
  const { json } = await resp.json();
35
35
  return {
36
36
  sessionId: json.session_id,
37
- cdpEndpoint: json.cdp_url
37
+ cdpEndpoint: json.cdp_url,
38
+ liveViewUrl: json.live_view_url ?? void 0
38
39
  };
39
40
  },
40
41
  async closeSession(sessionId) {
@@ -52,6 +53,8 @@ function createLibrettoCloudProvider() {
52
53
  `Libretto Cloud API error closing session ${sessionId} (${resp.status}): ${body}`
53
54
  );
54
55
  }
56
+ const { json } = await resp.json();
57
+ return { replayUrl: json.replay_url ?? void 0 };
55
58
  }
56
59
  };
57
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libretto",
3
- "version": "0.6.8",
3
+ "version": "0.6.9",
4
4
  "description": "AI-powered browser automation library and CLI built on Playwright",
5
5
  "license": "MIT",
6
6
  "homepage": "https://libretto.sh",
@@ -4,7 +4,7 @@ description: "Browser automation CLI for building, maintaining, and running brow
4
4
  license: MIT
5
5
  metadata:
6
6
  author: saffron-health
7
- version: "0.6.8"
7
+ version: "0.6.9"
8
8
  ---
9
9
 
10
10
  ## How Libretto Works
@@ -4,7 +4,7 @@ description: "Read-only Libretto workflow for diagnosing live browser state with
4
4
  license: MIT
5
5
  metadata:
6
6
  author: saffron-health
7
- version: "0.6.8"
7
+ version: "0.6.9"
8
8
  ---
9
9
 
10
10
  ## How Libretto Read-Only Works
@@ -882,6 +882,15 @@ export const runCommand = SimpleCLI.command({
882
882
  `Creating ${providerName} browser session (session: ${ctx.session})...`,
883
883
  );
884
884
  const providerSession = await provider.createSession();
885
+ ctx.logger.info("run-provider-session-created", {
886
+ provider: providerName,
887
+ sessionId: providerSession.sessionId,
888
+ cdpEndpoint: providerSession.cdpEndpoint,
889
+ liveViewUrl: providerSession.liveViewUrl,
890
+ });
891
+ if (providerSession.liveViewUrl) {
892
+ console.log(`View live session: ${providerSession.liveViewUrl}`);
893
+ }
885
894
  console.log(`Connecting to ${providerName} browser...`);
886
895
  cdpEndpoint = providerSession.cdpEndpoint;
887
896
  providerInfo = {
@@ -910,7 +919,10 @@ export const runCommand = SimpleCLI.command({
910
919
  } finally {
911
920
  if (provider && providerInfo) {
912
921
  try {
913
- await provider.closeSession(providerInfo.sessionId);
922
+ const result = await provider.closeSession(providerInfo.sessionId);
923
+ if (result.replayUrl) {
924
+ console.log(`View recording: ${result.replayUrl}`);
925
+ }
914
926
  } catch (cleanupErr) {
915
927
  console.error(
916
928
  `Failed to clean up ${providerInfo.name} session ${providerInfo.sessionId}:`,
@@ -590,8 +590,13 @@ export async function runOpenWithProvider(
590
590
  provider: providerName,
591
591
  sessionId: providerSession.sessionId,
592
592
  cdpEndpoint: providerSession.cdpEndpoint,
593
+ liveViewUrl: providerSession.liveViewUrl,
593
594
  });
594
595
 
596
+ if (providerSession.liveViewUrl) {
597
+ console.log(`View live session: ${providerSession.liveViewUrl}`);
598
+ }
599
+
595
600
  console.log(`Connecting to ${providerName} browser...`);
596
601
 
597
602
  let browser: Browser | null = null;
@@ -763,6 +768,7 @@ export async function runClose(
763
768
  return;
764
769
  }
765
770
 
771
+ let replayUrl: string | undefined;
766
772
  if (state.provider) {
767
773
  // Cloud provider session — close via provider API, no local pid to kill
768
774
  logger.info("close-provider", {
@@ -772,7 +778,8 @@ export async function runClose(
772
778
  });
773
779
  try {
774
780
  const provider = getCloudProviderApi(state.provider.name);
775
- await provider.closeSession(state.provider.sessionId);
781
+ const result = await provider.closeSession(state.provider.sessionId);
782
+ replayUrl = result.replayUrl;
776
783
  } catch (err) {
777
784
  logger.warn("close-provider-error", {
778
785
  session,
@@ -797,8 +804,11 @@ export async function runClose(
797
804
  }
798
805
 
799
806
  clearSessionState(session, logger);
800
- logger.info("close-success", { session });
807
+ logger.info("close-success", { session, replayUrl });
801
808
  console.log(`Browser closed (session: ${session}).`);
809
+ if (replayUrl) {
810
+ console.log(`View recording: ${replayUrl}`);
811
+ }
802
812
  }
803
813
 
804
814
  type ClosableSession = {
@@ -900,6 +910,7 @@ export async function runCloseAll(
900
910
 
901
911
  // Close provider sessions via their APIs
902
912
  const failedProviderSessions = new Set<string>();
913
+ const replayUrls: Array<{ session: string; replayUrl: string }> = [];
903
914
  for (const target of closable) {
904
915
  if (target.provider) {
905
916
  logger.info("close-all-provider", {
@@ -909,7 +920,13 @@ export async function runCloseAll(
909
920
  });
910
921
  try {
911
922
  const provider = getCloudProviderApi(target.provider.name);
912
- await provider.closeSession(target.provider.sessionId);
923
+ const result = await provider.closeSession(target.provider.sessionId);
924
+ if (result.replayUrl) {
925
+ replayUrls.push({
926
+ session: target.session,
927
+ replayUrl: result.replayUrl,
928
+ });
929
+ }
913
930
  } catch (err) {
914
931
  logger.warn("close-all-provider-error", {
915
932
  session: target.session,
@@ -1020,6 +1037,9 @@ export async function runCloseAll(
1020
1037
  if (forceKilled > 0) {
1021
1038
  console.log(`Force-killed ${forceKilled} session(s).`);
1022
1039
  }
1040
+ for (const { session, replayUrl } of replayUrls) {
1041
+ console.log(`View recording (${session}): ${replayUrl}`);
1042
+ }
1023
1043
  }
1024
1044
 
1025
1045
  export async function runConnect(
@@ -52,6 +52,7 @@ export function createBrowserbaseProvider(): ProviderApi {
52
52
  `Browserbase API error closing session ${sessionId} (${resp.status}): ${body}`,
53
53
  );
54
54
  }
55
+ return {};
55
56
  },
56
57
  };
57
58
  }
@@ -44,6 +44,7 @@ export function createKernelProvider(): ProviderApi {
44
44
  `Kernel API error closing session ${sessionId} (${resp.status}): ${body}`,
45
45
  );
46
46
  }
47
+ return {};
47
48
  },
48
49
  };
49
50
  }
@@ -37,11 +37,17 @@ export function createLibrettoCloudProvider(): ProviderApi {
37
37
  );
38
38
  }
39
39
  const { json } = (await resp.json()) as {
40
- json: { session_id: string; cdp_url: string };
40
+ json: {
41
+ session_id: string;
42
+ cdp_url: string;
43
+ live_view_url: string | null;
44
+ recording_url: string | null;
45
+ };
41
46
  };
42
47
  return {
43
48
  sessionId: json.session_id,
44
49
  cdpEndpoint: json.cdp_url,
50
+ liveViewUrl: json.live_view_url ?? undefined,
45
51
  };
46
52
  },
47
53
  async closeSession(sessionId) {
@@ -59,6 +65,10 @@ export function createLibrettoCloudProvider(): ProviderApi {
59
65
  `Libretto Cloud API error closing session ${sessionId} (${resp.status}): ${body}`,
60
66
  );
61
67
  }
68
+ const { json } = (await resp.json()) as {
69
+ json: { replay_url: string | null };
70
+ };
71
+ return { replayUrl: json.replay_url ?? undefined };
62
72
  },
63
73
  };
64
74
  }
@@ -1,9 +1,20 @@
1
1
  export type ProviderSession = {
2
2
  sessionId: string; // remote session id for cleanup
3
3
  cdpEndpoint: string; // CDP WebSocket URL
4
+ // Provider-hosted URL for watching the session live while it's running.
5
+ // Only libretto-cloud surfaces this today; direct-SDK providers leave it
6
+ // undefined.
7
+ liveViewUrl?: string;
8
+ };
9
+
10
+ export type ProviderCloseResult = {
11
+ // Provider-hosted URL for playback of the session recording, surfaced on
12
+ // successful close. Undefined when the provider didn't capture a
13
+ // recording or doesn't return one on close.
14
+ replayUrl?: string;
4
15
  };
5
16
 
6
17
  export type ProviderApi = {
7
18
  createSession(): Promise<ProviderSession>;
8
- closeSession(sessionId: string): Promise<void>;
19
+ closeSession(sessionId: string): Promise<ProviderCloseResult>;
9
20
  };