libretto 0.5.4 → 0.5.6

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.
Files changed (101) hide show
  1. package/README.md +23 -10
  2. package/README.template.md +23 -10
  3. package/dist/cli/cli.js +10 -0
  4. package/dist/cli/commands/ai.js +77 -2
  5. package/dist/cli/commands/browser.js +71 -6
  6. package/dist/cli/commands/execution.js +101 -44
  7. package/dist/cli/commands/setup.js +376 -0
  8. package/dist/cli/commands/snapshot.js +2 -2
  9. package/dist/cli/commands/status.js +62 -0
  10. package/dist/cli/core/{snapshot-api-config.js → ai-model.js} +81 -7
  11. package/dist/cli/core/api-snapshot-analyzer.js +7 -5
  12. package/dist/cli/core/browser.js +81 -42
  13. package/dist/cli/core/{ai-config.js → config.js} +13 -79
  14. package/dist/cli/core/context.js +1 -25
  15. package/dist/cli/core/deploy-artifact.js +121 -61
  16. package/dist/cli/core/readonly-exec.js +231 -0
  17. package/dist/{shared/llm/client.js → cli/core/resolve-model.js} +4 -68
  18. package/dist/cli/core/session.js +44 -0
  19. package/dist/cli/core/skill-version.js +73 -0
  20. package/dist/cli/core/telemetry.js +1 -54
  21. package/dist/cli/index.js +1 -7
  22. package/dist/cli/router.js +4 -4
  23. package/dist/cli/workers/run-integration-runtime.js +29 -25
  24. package/dist/cli/workers/run-integration-worker-protocol.js +3 -2
  25. package/dist/index.d.ts +2 -4
  26. package/dist/index.js +2 -2
  27. package/dist/runtime/extract/extract.d.ts +2 -2
  28. package/dist/runtime/extract/extract.js +4 -2
  29. package/dist/runtime/extract/index.d.ts +1 -1
  30. package/dist/runtime/recovery/agent.d.ts +2 -3
  31. package/dist/runtime/recovery/agent.js +5 -3
  32. package/dist/runtime/recovery/errors.d.ts +2 -3
  33. package/dist/runtime/recovery/errors.js +4 -2
  34. package/dist/runtime/recovery/index.d.ts +1 -2
  35. package/dist/runtime/recovery/recovery.d.ts +2 -3
  36. package/dist/runtime/recovery/recovery.js +3 -3
  37. package/dist/shared/debug/pause.js +4 -21
  38. package/dist/shared/run/api.d.ts +2 -0
  39. package/dist/shared/run/browser.d.ts +4 -1
  40. package/dist/shared/run/browser.js +5 -3
  41. package/dist/shared/state/index.d.ts +1 -1
  42. package/dist/shared/state/index.js +2 -0
  43. package/dist/shared/state/session-state.d.ts +10 -1
  44. package/dist/shared/state/session-state.js +3 -0
  45. package/dist/shared/workflow/workflow.d.ts +2 -3
  46. package/dist/shared/workflow/workflow.js +16 -9
  47. package/package.json +3 -4
  48. package/scripts/postinstall.mjs +13 -11
  49. package/scripts/skills-libretto.mjs +14 -4
  50. package/skills/AGENTS.md +11 -0
  51. package/skills/libretto/SKILL.md +30 -9
  52. package/skills/libretto/references/auth-profiles.md +1 -1
  53. package/skills/libretto/references/code-generation-rules.md +6 -6
  54. package/skills/libretto/references/configuration-file-reference.md +11 -6
  55. package/skills/libretto-readonly/SKILL.md +95 -0
  56. package/src/cli/cli.ts +10 -0
  57. package/src/cli/commands/ai.ts +111 -1
  58. package/src/cli/commands/browser.ts +81 -7
  59. package/src/cli/commands/execution.ts +128 -61
  60. package/src/cli/commands/setup.ts +499 -0
  61. package/src/cli/commands/snapshot.ts +2 -2
  62. package/src/cli/commands/status.ts +77 -0
  63. package/src/cli/core/{snapshot-api-config.ts → ai-model.ts} +154 -14
  64. package/src/cli/core/api-snapshot-analyzer.ts +7 -5
  65. package/src/cli/core/browser.ts +107 -45
  66. package/src/cli/core/{ai-config.ts → config.ts} +13 -108
  67. package/src/cli/core/context.ts +1 -45
  68. package/src/cli/core/deploy-artifact.ts +141 -71
  69. package/src/cli/core/readonly-exec.ts +284 -0
  70. package/src/{shared/llm/client.ts → cli/core/resolve-model.ts} +3 -85
  71. package/src/cli/core/session.ts +62 -2
  72. package/src/cli/core/skill-version.ts +93 -0
  73. package/src/cli/core/telemetry.ts +0 -52
  74. package/src/cli/index.ts +0 -6
  75. package/src/cli/router.ts +4 -4
  76. package/src/cli/workers/run-integration-runtime.ts +36 -31
  77. package/src/cli/workers/run-integration-worker-protocol.ts +2 -1
  78. package/src/index.ts +1 -7
  79. package/src/runtime/extract/extract.ts +6 -5
  80. package/src/runtime/recovery/agent.ts +5 -4
  81. package/src/runtime/recovery/errors.ts +4 -3
  82. package/src/runtime/recovery/recovery.ts +4 -4
  83. package/src/shared/debug/pause.ts +4 -23
  84. package/src/shared/run/browser.ts +5 -1
  85. package/src/shared/state/index.ts +2 -0
  86. package/src/shared/state/session-state.ts +3 -0
  87. package/src/shared/workflow/workflow.ts +24 -15
  88. package/dist/cli/commands/init.js +0 -286
  89. package/dist/cli/commands/logs.js +0 -117
  90. package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
  91. package/dist/shared/llm/ai-sdk-adapter.js +0 -49
  92. package/dist/shared/llm/client.d.ts +0 -13
  93. package/dist/shared/llm/index.d.ts +0 -5
  94. package/dist/shared/llm/index.js +0 -6
  95. package/dist/shared/llm/types.d.ts +0 -67
  96. package/dist/shared/llm/types.js +0 -0
  97. package/src/cli/commands/init.ts +0 -331
  98. package/src/cli/commands/logs.ts +0 -128
  99. package/src/shared/llm/ai-sdk-adapter.ts +0 -81
  100. package/src/shared/llm/index.ts +0 -3
  101. package/src/shared/llm/types.ts +0 -63
package/README.md CHANGED
@@ -22,13 +22,20 @@ https://github.com/user-attachments/assets/9b9a0ab3-5133-4b20-b3be-459943349d18
22
22
  ```bash
23
23
  npm install libretto
24
24
 
25
- # Install skill, download Chromium if not already installed, configure snapshot analysis
26
- npx libretto init
25
+ # First-time onboarding: install skill, download Chromium, and pin the default snapshot model
26
+ npx libretto setup
27
27
 
28
- # Configure or change the snapshot analysis model (see Configuration section below). `npx libretto init` sets this up the first time.
28
+ # Check workspace readiness at any time
29
+ npx libretto status
30
+
31
+ # Manually change the snapshot analysis model (advanced override)
29
32
  npx libretto ai configure <openai | anthropic | gemini | vertex>
30
33
  ```
31
34
 
35
+ `setup` detects available provider credentials (e.g. `OPENAI_API_KEY`) and automatically pins the default model to `.libretto/config.json`. Re-running `setup` on a healthy workspace shows the current configuration instead of re-prompting. If credentials are missing for a previously configured provider, `setup` offers an interactive repair flow.
36
+
37
+ Use `ai configure` when you want to explicitly switch providers or set a custom model string.
38
+
32
39
  ## Use cases
33
40
 
34
41
  Libretto is designed to be used as a skill through your coding agent. Here are some example prompts:
@@ -62,19 +69,19 @@ Agents can use Libretto to reproduce the failure, pause the workflow at any poin
62
69
  You can also use Libretto directly from the command line. All commands accept `--session <name>` to target a specific session.
63
70
 
64
71
  ```bash
65
- npx libretto init # interactive; run yourself, not through an agent
72
+ npx libretto setup # interactive first-run onboarding; run yourself, not through an agent
73
+ npx libretto status # check AI config health and open sessions
66
74
  npx libretto open <url> # launch browser and open a URL (headed by default)
67
75
  npx libretto snapshot --objective "..." --context "..." # capture PNG + HTML and analyze with an LLM
68
76
  npx libretto exec "<code>" # execute Playwright TypeScript against the open page (single quoted argument)
69
77
  echo "<code>" | npx libretto exec - # intentionally read Playwright TypeScript from stdin
70
- npx libretto run <file> <workflowName> # run an exported workflow from a file
78
+ npx libretto run <file> # run the file's default-exported workflow
71
79
  npx libretto resume # resume a paused workflow
72
- npx libretto network # view captured network requests
73
- npx libretto actions # view captured user/agent actions
74
80
  npx libretto pages # list open pages in the session
75
81
  npx libretto save <domain> # save browser session (cookies, localStorage) for reuse
76
82
  npx libretto close # close the browser
77
- npx libretto ai configure <provider> # configure snapshot analysis model
83
+ npx libretto ai configure <provider> # manually change snapshot analysis model
84
+ npx libretto status # show AI config and open sessions
78
85
  ```
79
86
 
80
87
  ## Configuration
@@ -98,12 +105,18 @@ All Libretto state lives in a `.libretto/` directory at your project root. Confi
98
105
 
99
106
  The `ai` field configures which model Libretto uses for snapshot analysis — extracting selectors, identifying interactive elements, or diagnosing why a step failed. This keeps heavy visual context out of your coding agent's context window. Snapshot analysis is required.
100
107
 
101
- The easiest way to set the model is through the CLI:
108
+ `npx libretto setup` automatically pins the default model for the first provider whose credentials it finds. To explicitly change the provider or model afterward:
102
109
 
103
110
  ```bash
104
111
  npx libretto ai configure <openai | anthropic | gemini | vertex>
105
112
  ```
106
113
 
114
+ To inspect the current configuration without changing anything:
115
+
116
+ ```bash
117
+ npx libretto status
118
+ ```
119
+
107
120
  Provider credentials are read from environment variables or a `.env` file at your project root: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY` / `GOOGLE_GENERATIVE_AI_API_KEY`, or `GOOGLE_CLOUD_PROJECT` for Vertex.
108
121
 
109
122
  The `viewport` field sets the default browser viewport size. Both fields are optional.
@@ -157,6 +170,6 @@ Source layout:
157
170
  - `README.template.md` — source of truth for the repo and package READMEs
158
171
  - `skills/libretto/` — source of truth for the Libretto skill
159
172
 
160
- Run `pnpm sync:mirrors` after editing `README.template.md` or anything under `skills/libretto/`. `pnpm i` also resyncs the skill mirrors through `postinstall`.
173
+ Run `pnpm sync:mirrors` after editing `README.template.md` or anything under `skills/libretto/`.
161
174
 
162
175
  To check that generated READMEs, skill mirrors, and skill version metadata are in sync without fixing them, run `pnpm check:mirrors`. To release, run `pnpm prepare-release`.
@@ -20,13 +20,20 @@ https://github.com/user-attachments/assets/9b9a0ab3-5133-4b20-b3be-459943349d18
20
20
  ```bash
21
21
  npm install libretto
22
22
 
23
- # Install skill, download Chromium if not already installed, configure snapshot analysis
24
- npx libretto init
23
+ # First-time onboarding: install skill, download Chromium, and pin the default snapshot model
24
+ npx libretto setup
25
25
 
26
- # Configure or change the snapshot analysis model (see Configuration section below). `npx libretto init` sets this up the first time.
26
+ # Check workspace readiness at any time
27
+ npx libretto status
28
+
29
+ # Manually change the snapshot analysis model (advanced override)
27
30
  npx libretto ai configure <openai | anthropic | gemini | vertex>
28
31
  ```
29
32
 
33
+ `setup` detects available provider credentials (e.g. `OPENAI_API_KEY`) and automatically pins the default model to `.libretto/config.json`. Re-running `setup` on a healthy workspace shows the current configuration instead of re-prompting. If credentials are missing for a previously configured provider, `setup` offers an interactive repair flow.
34
+
35
+ Use `ai configure` when you want to explicitly switch providers or set a custom model string.
36
+
30
37
  ## Use cases
31
38
 
32
39
  Libretto is designed to be used as a skill through your coding agent. Here are some example prompts:
@@ -60,19 +67,19 @@ Agents can use Libretto to reproduce the failure, pause the workflow at any poin
60
67
  You can also use Libretto directly from the command line. All commands accept `--session <name>` to target a specific session.
61
68
 
62
69
  ```bash
63
- npx libretto init # interactive; run yourself, not through an agent
70
+ npx libretto setup # interactive first-run onboarding; run yourself, not through an agent
71
+ npx libretto status # check AI config health and open sessions
64
72
  npx libretto open <url> # launch browser and open a URL (headed by default)
65
73
  npx libretto snapshot --objective "..." --context "..." # capture PNG + HTML and analyze with an LLM
66
74
  npx libretto exec "<code>" # execute Playwright TypeScript against the open page (single quoted argument)
67
75
  echo "<code>" | npx libretto exec - # intentionally read Playwright TypeScript from stdin
68
- npx libretto run <file> <workflowName> # run an exported workflow from a file
76
+ npx libretto run <file> # run the file's default-exported workflow
69
77
  npx libretto resume # resume a paused workflow
70
- npx libretto network # view captured network requests
71
- npx libretto actions # view captured user/agent actions
72
78
  npx libretto pages # list open pages in the session
73
79
  npx libretto save <domain> # save browser session (cookies, localStorage) for reuse
74
80
  npx libretto close # close the browser
75
- npx libretto ai configure <provider> # configure snapshot analysis model
81
+ npx libretto ai configure <provider> # manually change snapshot analysis model
82
+ npx libretto status # show AI config and open sessions
76
83
  ```
77
84
 
78
85
  ## Configuration
@@ -96,12 +103,18 @@ All Libretto state lives in a `.libretto/` directory at your project root. Confi
96
103
 
97
104
  The `ai` field configures which model Libretto uses for snapshot analysis — extracting selectors, identifying interactive elements, or diagnosing why a step failed. This keeps heavy visual context out of your coding agent's context window. Snapshot analysis is required.
98
105
 
99
- The easiest way to set the model is through the CLI:
106
+ `npx libretto setup` automatically pins the default model for the first provider whose credentials it finds. To explicitly change the provider or model afterward:
100
107
 
101
108
  ```bash
102
109
  npx libretto ai configure <openai | anthropic | gemini | vertex>
103
110
  ```
104
111
 
112
+ To inspect the current configuration without changing anything:
113
+
114
+ ```bash
115
+ npx libretto status
116
+ ```
117
+
105
118
  Provider credentials are read from environment variables or a `.env` file at your project root: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY` / `GOOGLE_GENERATIVE_AI_API_KEY`, or `GOOGLE_CLOUD_PROJECT` for Vertex.
106
119
 
107
120
  The `viewport` field sets the default browser viewport size. Both fields are optional.
@@ -155,6 +168,6 @@ Source layout:
155
168
  - `{{LIBRETTO_PATH_PREFIX}}README.template.md` — source of truth for the repo and package READMEs
156
169
  - `{{LIBRETTO_PATH_PREFIX}}skills/libretto/` — source of truth for the Libretto skill
157
170
 
158
- Run `pnpm sync:mirrors` after editing `{{LIBRETTO_PATH_PREFIX}}README.template.md` or anything under `{{LIBRETTO_PATH_PREFIX}}skills/libretto/`. `pnpm i` also resyncs the skill mirrors through `postinstall`.
171
+ Run `pnpm sync:mirrors` after editing `{{LIBRETTO_PATH_PREFIX}}README.template.md` or anything under `{{LIBRETTO_PATH_PREFIX}}skills/libretto/`.
159
172
 
160
173
  To check that generated READMEs, skill mirrors, and skill version metadata are in sync without fixing them, run `pnpm check:mirrors`. To release, run `pnpm prepare-release`.
package/dist/cli/cli.js CHANGED
@@ -15,6 +15,10 @@ Examples:
15
15
 
16
16
  libretto exec "await page.locator('button:has-text(\\"Sign in\\")').click()"
17
17
  libretto exec "await page.fill('input[name=\\"email\\"]', 'test@example.com')"
18
+ libretto readonly-exec "return await page.title()" --session test1
19
+ libretto connect http://127.0.0.1:9222 --read-only --session test1
20
+ libretto run ./integration.ts --read-only --session test1
21
+ libretto status
18
22
  libretto ai configure openai
19
23
  libretto ai configure anthropic
20
24
  libretto ai configure gemini
@@ -35,6 +39,9 @@ Examples:
35
39
  Available in exec:
36
40
  page, context, state, browser, networkLog, actionLog
37
41
 
42
+ Available in readonly-exec:
43
+ page, state, snapshot, scrollBy, get
44
+
38
45
  Profiles:
39
46
  Profiles are saved to .libretto/profiles/<domain>.json (git-ignored)
40
47
  They persist cookies, localStorage, and session data across browser launches.
@@ -45,6 +52,9 @@ Sessions:
45
52
  Session state is stored in .libretto/sessions/<session>/state.json
46
53
  CLI logs are stored in .libretto/sessions/<session>/logs.jsonl
47
54
  Each session runs an isolated browser instance on a dynamic port.
55
+ Session mode is stored per session as read-only or write-access.
56
+ Use --read-only on open, connect, or run to create a read-only session.
57
+ Session mode is enforced by Libretto commands, not by raw CDP clients outside Libretto.
48
58
  `;
49
59
  }
50
60
  function isRootHelpRequest(rawArgs) {
@@ -1,6 +1,80 @@
1
1
  import { z } from "zod";
2
- import { runAiConfigure } from "../core/ai-config.js";
2
+ import {
3
+ readAiConfig,
4
+ writeAiConfig,
5
+ clearAiConfig
6
+ } from "../core/config.js";
7
+ import { LIBRETTO_CONFIG_PATH } from "../core/context.js";
8
+ import { DEFAULT_SNAPSHOT_MODELS } from "../core/ai-model.js";
3
9
  import { SimpleCLI } from "../framework/simple-cli.js";
10
+ const PROVIDER_ALIASES = {
11
+ claude: DEFAULT_SNAPSHOT_MODELS.anthropic,
12
+ gemini: DEFAULT_SNAPSHOT_MODELS.google,
13
+ google: DEFAULT_SNAPSHOT_MODELS.google
14
+ };
15
+ const CONFIGURE_PROVIDERS = [
16
+ "openai",
17
+ "anthropic",
18
+ "gemini",
19
+ "vertex"
20
+ ];
21
+ function formatConfigureProviders(separator = " | ") {
22
+ return CONFIGURE_PROVIDERS.join(separator);
23
+ }
24
+ function printAiConfig(config, configPath) {
25
+ console.log(`Model: ${config.model}`);
26
+ console.log(`Config file: ${configPath}`);
27
+ console.log(`Updated at: ${config.updatedAt}`);
28
+ }
29
+ function resolveModelFromInput(input) {
30
+ const trimmed = input.trim();
31
+ if (!trimmed) return null;
32
+ if (trimmed.includes("/")) return trimmed;
33
+ const normalized = trimmed.toLowerCase();
34
+ return DEFAULT_SNAPSHOT_MODELS[normalized] ?? PROVIDER_ALIASES[normalized] ?? null;
35
+ }
36
+ function runAiConfigure(input, options = {}) {
37
+ const configureCommandName = options.configureCommandName ?? "npx libretto ai configure";
38
+ const configPath = options.configPath ?? LIBRETTO_CONFIG_PATH;
39
+ const presetArg = input.preset?.trim();
40
+ if (!presetArg && !input.clear) {
41
+ const config2 = readAiConfig(configPath);
42
+ if (!config2) {
43
+ console.log(
44
+ `No AI config set. Choose a default model: ${configureCommandName} ${formatConfigureProviders()}`
45
+ );
46
+ console.log(
47
+ "Provider credentials still come from your shell or .env file."
48
+ );
49
+ return;
50
+ }
51
+ printAiConfig(config2, configPath);
52
+ return;
53
+ }
54
+ if (input.clear) {
55
+ const removed = clearAiConfig(configPath);
56
+ if (removed) {
57
+ console.log(`Cleared AI config: ${configPath}`);
58
+ } else {
59
+ console.log("No AI config was set.");
60
+ }
61
+ return;
62
+ }
63
+ const model = resolveModelFromInput(presetArg);
64
+ if (!model) {
65
+ console.log(
66
+ `Usage: ${configureCommandName} <${CONFIGURE_PROVIDERS.join("|")}|provider/model-id>
67
+ ${configureCommandName}
68
+ ${configureCommandName} --clear`
69
+ );
70
+ throw new Error(
71
+ `Invalid provider or model. Use one of: ${formatConfigureProviders()}, or a full model string like "openai/gpt-4o".`
72
+ );
73
+ }
74
+ const config = writeAiConfig(model, configPath);
75
+ console.log("AI config saved.");
76
+ printAiConfig(config, configPath);
77
+ }
4
78
  const aiConfigureInput = SimpleCLI.input({
5
79
  positionals: [
6
80
  SimpleCLI.positional("preset", z.string().optional(), {
@@ -31,5 +105,6 @@ const aiCommands = SimpleCLI.group({
31
105
  });
32
106
  export {
33
107
  aiCommands,
34
- aiConfigureInput
108
+ aiConfigureInput,
109
+ runAiConfigure
35
110
  };
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { SessionAccessModeSchema } from "../../shared/state/index.js";
2
3
  import {
3
4
  runClose as runCloseWithLogger,
4
5
  runCloseAll as runCloseAllWithLogger,
@@ -7,11 +8,14 @@ import {
7
8
  runPages,
8
9
  runSave
9
10
  } from "../core/browser.js";
11
+ import { readLibrettoConfig } from "../core/config.js";
10
12
  import { createLoggerForSession, withSessionLogger } from "../core/context.js";
11
13
  import {
12
14
  assertSessionAvailableForStart,
15
+ setSessionMode,
13
16
  validateSessionName
14
17
  } from "../core/session.js";
18
+ import { warnIfInstalledSkillOutOfDate } from "../core/skill-version.js";
15
19
  import { SimpleCLI } from "../framework/simple-cli.js";
16
20
  import {
17
21
  sessionOption,
@@ -35,6 +39,12 @@ function parseViewportArg(viewportArg) {
35
39
  }
36
40
  return { width, height };
37
41
  }
42
+ function resolveRequestedSessionMode(readOnly, writeAccess) {
43
+ if (readOnly) return "read-only";
44
+ if (writeAccess) return "write-access";
45
+ const config = readLibrettoConfig();
46
+ return config.sessionMode ?? "write-access";
47
+ }
38
48
  const openInput = SimpleCLI.input({
39
49
  positionals: [
40
50
  SimpleCLI.positional("url", z.string().optional(), {
@@ -45,24 +55,39 @@ const openInput = SimpleCLI.input({
45
55
  session: sessionOption(),
46
56
  headed: SimpleCLI.flag({ help: "Run browser in headed mode" }),
47
57
  headless: SimpleCLI.flag({ help: "Run browser in headless mode" }),
58
+ readOnly: SimpleCLI.flag({
59
+ name: "read-only",
60
+ help: "Create the session in read-only mode"
61
+ }),
62
+ writeAccess: SimpleCLI.flag({
63
+ name: "write-access",
64
+ help: "Create the session in write-access mode (overrides config default)"
65
+ }),
48
66
  viewport: SimpleCLI.option(z.string().optional(), {
49
67
  help: "Viewport size as WIDTHxHEIGHT (e.g. 1920x1080)"
50
68
  })
51
69
  }
52
70
  }).refine(
53
71
  (input) => Boolean(input.url),
54
- `Usage: libretto open <url> [--headless] [--viewport WxH] [--session <name>]`
72
+ `Usage: libretto open <url> [--headless] [--read-only|--write-access] [--viewport WxH] [--session <name>]`
55
73
  ).refine(
56
74
  (input) => !(input.headed && input.headless),
57
75
  "Cannot pass both --headed and --headless."
76
+ ).refine(
77
+ (input) => !(input.readOnly && input.writeAccess),
78
+ "Cannot pass both --read-only and --write-access."
58
79
  );
59
80
  const openCommand = SimpleCLI.command({
60
81
  description: "Launch browser and open URL (headed by default)"
61
82
  }).input(openInput).use(withAutoSession()).handle(async ({ input, ctx }) => {
83
+ warnIfInstalledSkillOutOfDate();
62
84
  assertSessionAvailableForStart(ctx.session, ctx.logger);
63
85
  const headed = input.headed || !input.headless;
64
86
  const viewport = parseViewportArg(input.viewport);
65
- await runOpen(input.url, headed, ctx.session, ctx.logger, { viewport });
87
+ await runOpen(input.url, headed, ctx.session, ctx.logger, {
88
+ viewport,
89
+ accessMode: resolveRequestedSessionMode(input.readOnly, input.writeAccess)
90
+ });
66
91
  });
67
92
  const connectInput = SimpleCLI.input({
68
93
  positionals: [
@@ -71,16 +96,33 @@ const connectInput = SimpleCLI.input({
71
96
  })
72
97
  ],
73
98
  named: {
74
- session: sessionOption()
99
+ session: sessionOption(),
100
+ readOnly: SimpleCLI.flag({
101
+ name: "read-only",
102
+ help: "Create the session in read-only mode"
103
+ }),
104
+ writeAccess: SimpleCLI.flag({
105
+ name: "write-access",
106
+ help: "Create the session in write-access mode (overrides config default)"
107
+ })
75
108
  }
76
109
  }).refine(
77
110
  (input) => Boolean(input.cdpUrl),
78
- `Usage: libretto connect <cdp-url> --session <name>`
111
+ `Usage: libretto connect <cdp-url> [--read-only|--write-access] --session <name>`
112
+ ).refine(
113
+ (input) => !(input.readOnly && input.writeAccess),
114
+ "Cannot pass both --read-only and --write-access."
79
115
  );
80
116
  const connectCommand = SimpleCLI.command({
81
117
  description: "Connect to an existing Chrome DevTools Protocol (CDP) endpoint"
82
118
  }).input(connectInput).use(withAutoSession()).handle(async ({ input, ctx }) => {
83
- await runConnectWithLogger(input.cdpUrl, ctx.session, ctx.logger);
119
+ warnIfInstalledSkillOutOfDate();
120
+ await runConnectWithLogger(
121
+ input.cdpUrl,
122
+ ctx.session,
123
+ ctx.logger,
124
+ resolveRequestedSessionMode(input.readOnly, input.writeAccess)
125
+ );
84
126
  });
85
127
  const saveInput = SimpleCLI.input({
86
128
  positionals: [
@@ -111,6 +153,26 @@ const pagesCommand = SimpleCLI.command({
111
153
  }).input(pagesInput).use(withRequiredSession()).handle(async ({ ctx }) => {
112
154
  await runPages(ctx.session, ctx.logger);
113
155
  });
156
+ const sessionModeInput = SimpleCLI.input({
157
+ positionals: [
158
+ SimpleCLI.positional("mode", SessionAccessModeSchema.optional(), {
159
+ help: "Session mode to set"
160
+ })
161
+ ],
162
+ named: {
163
+ session: sessionOption()
164
+ }
165
+ });
166
+ const sessionModeCommand = SimpleCLI.command({
167
+ description: "View or set the session access mode"
168
+ }).input(sessionModeInput).use(withRequiredSession()).handle(async ({ input, ctx }) => {
169
+ if (!input.mode) {
170
+ console.log(`Session "${ctx.session}" mode: ${ctx.sessionState.mode}`);
171
+ return;
172
+ }
173
+ const nextState = setSessionMode(ctx.session, input.mode, ctx.logger);
174
+ console.log(`Session "${ctx.session}" mode set to ${nextState.mode}.`);
175
+ });
114
176
  const closeInput = SimpleCLI.input({
115
177
  positionals: [],
116
178
  named: {
@@ -147,6 +209,7 @@ const browserCommands = {
147
209
  connect: connectCommand,
148
210
  save: saveCommand,
149
211
  pages: pagesCommand,
212
+ "session-mode": sessionModeCommand,
150
213
  close: closeCommand
151
214
  };
152
215
  async function runClose(session) {
@@ -167,5 +230,7 @@ export {
167
230
  parseViewportArg,
168
231
  runClose,
169
232
  saveCommand,
170
- saveInput
233
+ saveInput,
234
+ sessionModeCommand,
235
+ sessionModeInput
171
236
  };