libretto 0.5.5 → 0.6.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/README.md +23 -10
- package/README.template.md +23 -10
- package/dist/cli/cli.js +10 -0
- package/dist/cli/commands/ai.js +77 -2
- package/dist/cli/commands/browser.js +98 -8
- package/dist/cli/commands/execution.js +152 -56
- package/dist/cli/commands/setup.js +390 -0
- package/dist/cli/commands/snapshot.js +2 -2
- package/dist/cli/commands/status.js +62 -0
- package/dist/cli/core/{snapshot-api-config.js → ai-model.js} +81 -7
- package/dist/cli/core/api-snapshot-analyzer.js +7 -5
- package/dist/cli/core/browser.js +202 -36
- package/dist/cli/core/{ai-config.js → config.js} +14 -79
- package/dist/cli/core/context.js +1 -25
- package/dist/cli/core/deploy-artifact.js +121 -61
- package/dist/cli/core/providers/browserbase.js +53 -0
- package/dist/cli/core/providers/index.js +48 -0
- package/dist/cli/core/providers/kernel.js +46 -0
- package/dist/cli/core/providers/libretto-cloud.js +58 -0
- package/dist/cli/core/readonly-exec.js +231 -0
- package/dist/{shared/llm/client.js → cli/core/resolve-model.js} +4 -68
- package/dist/cli/core/session.js +53 -0
- package/dist/cli/core/skill-version.js +73 -0
- package/dist/cli/core/telemetry.js +1 -54
- package/dist/cli/index.js +1 -7
- package/dist/cli/router.js +4 -4
- package/dist/cli/workers/run-integration-runtime.js +19 -13
- package/dist/cli/workers/run-integration-worker-protocol.js +5 -2
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -2
- package/dist/runtime/extract/extract.d.ts +2 -2
- package/dist/runtime/extract/extract.js +4 -2
- package/dist/runtime/extract/index.d.ts +1 -1
- package/dist/runtime/recovery/agent.d.ts +2 -3
- package/dist/runtime/recovery/agent.js +5 -3
- package/dist/runtime/recovery/errors.d.ts +2 -3
- package/dist/runtime/recovery/errors.js +4 -2
- package/dist/runtime/recovery/index.d.ts +1 -2
- package/dist/runtime/recovery/recovery.d.ts +2 -3
- package/dist/runtime/recovery/recovery.js +3 -3
- package/dist/shared/debug/pause.js +4 -21
- package/dist/shared/run/api.d.ts +2 -0
- package/dist/shared/run/browser.d.ts +9 -1
- package/dist/shared/run/browser.js +43 -3
- package/dist/shared/state/index.d.ts +1 -1
- package/dist/shared/state/index.js +2 -0
- package/dist/shared/state/session-state.d.ts +20 -1
- package/dist/shared/state/session-state.js +12 -2
- package/dist/shared/workflow/workflow.d.ts +2 -1
- package/dist/shared/workflow/workflow.js +16 -9
- package/package.json +17 -16
- package/scripts/postinstall.mjs +13 -11
- package/scripts/skills-libretto.mjs +14 -4
- package/skills/AGENTS.md +11 -0
- package/skills/libretto/SKILL.md +30 -9
- package/skills/libretto/references/auth-profiles.md +1 -1
- package/skills/libretto/references/code-generation-rules.md +3 -3
- package/skills/libretto/references/configuration-file-reference.md +11 -6
- package/skills/libretto-readonly/SKILL.md +95 -0
- package/src/cli/cli.ts +10 -0
- package/src/cli/commands/ai.ts +111 -1
- package/src/cli/commands/browser.ts +111 -9
- package/src/cli/commands/execution.ts +181 -74
- package/src/cli/commands/setup.ts +516 -0
- package/src/cli/commands/snapshot.ts +2 -2
- package/src/cli/commands/status.ts +79 -0
- package/src/cli/core/{snapshot-api-config.ts → ai-model.ts} +154 -14
- package/src/cli/core/api-snapshot-analyzer.ts +7 -5
- package/src/cli/core/browser.ts +242 -35
- package/src/cli/core/{ai-config.ts → config.ts} +14 -108
- package/src/cli/core/context.ts +1 -45
- package/src/cli/core/deploy-artifact.ts +141 -71
- package/src/cli/core/providers/browserbase.ts +57 -0
- package/src/cli/core/providers/index.ts +62 -0
- package/src/cli/core/providers/kernel.ts +49 -0
- package/src/cli/core/providers/libretto-cloud.ts +61 -0
- package/src/cli/core/providers/types.ts +9 -0
- package/src/cli/core/readonly-exec.ts +284 -0
- package/src/{shared/llm/client.ts → cli/core/resolve-model.ts} +3 -85
- package/src/cli/core/session.ts +75 -2
- package/src/cli/core/skill-version.ts +93 -0
- package/src/cli/core/telemetry.ts +0 -52
- package/src/cli/index.ts +0 -6
- package/src/cli/router.ts +4 -4
- package/src/cli/workers/run-integration-runtime.ts +18 -16
- package/src/cli/workers/run-integration-worker-protocol.ts +4 -1
- package/src/index.ts +1 -7
- package/src/runtime/extract/extract.ts +6 -5
- package/src/runtime/recovery/agent.ts +5 -4
- package/src/runtime/recovery/errors.ts +4 -3
- package/src/runtime/recovery/recovery.ts +4 -4
- package/src/shared/debug/pause.ts +4 -23
- package/src/shared/run/browser.ts +50 -1
- package/src/shared/state/index.ts +2 -0
- package/src/shared/state/session-state.ts +10 -0
- package/src/shared/workflow/workflow.ts +24 -13
- package/dist/cli/commands/init.js +0 -286
- package/dist/cli/commands/logs.js +0 -117
- package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.js +0 -49
- package/dist/shared/llm/client.d.ts +0 -13
- package/dist/shared/llm/index.d.ts +0 -5
- package/dist/shared/llm/index.js +0 -6
- package/dist/shared/llm/types.d.ts +0 -67
- package/src/cli/commands/init.ts +0 -331
- package/src/cli/commands/logs.ts +0 -128
- package/src/shared/llm/ai-sdk-adapter.ts +0 -81
- package/src/shared/llm/index.ts +0 -3
- package/src/shared/llm/types.ts +0 -63
- /package/dist/{shared/llm → cli/core/providers}/types.js +0 -0
|
@@ -6,7 +6,7 @@ Follow the user's existing codebase conventions, abstractions, and patterns when
|
|
|
6
6
|
|
|
7
7
|
## Workflow File Structure
|
|
8
8
|
|
|
9
|
-
Generated files must export a `workflow()` instance so they can be run via `npx libretto run <file
|
|
9
|
+
Generated files must default-export a `workflow()` instance so they can be run via `npx libretto run <file>`. Import `workflow` and its types from `"libretto"`:
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
12
|
import { workflow, pause, type LibrettoWorkflowContext } from "libretto";
|
|
@@ -22,7 +22,7 @@ type Output = {
|
|
|
22
22
|
results: Array<{ name: string; value: string }>;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
export
|
|
25
|
+
export default workflow<Input, Output>(
|
|
26
26
|
"myWorkflow",
|
|
27
27
|
async (ctx: LibrettoWorkflowContext, input): Promise<Output> => {
|
|
28
28
|
const { session, page } = ctx;
|
|
@@ -39,7 +39,7 @@ export const myWorkflow = workflow<Input, Output>(
|
|
|
39
39
|
Key points:
|
|
40
40
|
|
|
41
41
|
- `workflow(name, handler)` takes a unique workflow name and returns the workflow object that Libretto can run.
|
|
42
|
-
- `npx libretto run ./file.ts
|
|
42
|
+
- `npx libretto run ./file.ts` executes the file's default-exported workflow, so always use `export default workflow(...)`.
|
|
43
43
|
- `ctx` provides `session` and `page`. Use `console.log`/`console.warn`/`console.error` for logging — the runtime wraps these with structured metadata automatically.
|
|
44
44
|
- `input` comes from `--params '{"query":"foo"}'` or `--params-file params.json` on the CLI
|
|
45
45
|
- Use `await pause(ctx.session)` (or `await pause(session)`) to pause the workflow for debugging. It is a no-op in production.
|
|
@@ -12,8 +12,9 @@ Use this reference when you need to inspect or change the workspace configuratio
|
|
|
12
12
|
|
|
13
13
|
Libretto reads workspace config from `.libretto/config.json`.
|
|
14
14
|
|
|
15
|
-
- The file is
|
|
15
|
+
- The file is created by `npx libretto setup` during first-time onboarding (auto-pins the default model for the detected provider) or by `npx libretto ai configure ...` for explicit overrides.
|
|
16
16
|
- API credentials still come from your shell environment or `.env`. The config file stores the selected model, not the secret itself.
|
|
17
|
+
- Use `npx libretto status` to inspect the current AI configuration and open sessions without changing anything.
|
|
17
18
|
- For first-time setup instructions, follow the main `SKILL.md` flow instead of expanding this reference.
|
|
18
19
|
|
|
19
20
|
## Supported Settings
|
|
@@ -21,6 +22,7 @@ Libretto reads workspace config from `.libretto/config.json`.
|
|
|
21
22
|
- `ai.model` selects the configured analysis model for `snapshot`.
|
|
22
23
|
- `viewport` is an optional top-level setting used by `open` and `run` when you do not pass `--viewport`.
|
|
23
24
|
- Viewport precedence is: CLI `--viewport`, then `.libretto/config.json`, then the default `1366x768`.
|
|
25
|
+
- `sessionMode` sets the default session access mode for new sessions created by `open`, `connect`, and `run`. Must be `"read-only"` or `"write-access"`. When omitted, defaults to `"write-access"`. Pass `--read-only` or `--write-access` to `open`, `connect`, or `run` to override when creating a session.
|
|
24
26
|
|
|
25
27
|
Example:
|
|
26
28
|
|
|
@@ -34,20 +36,23 @@ Example:
|
|
|
34
36
|
"viewport": {
|
|
35
37
|
"width": 1280,
|
|
36
38
|
"height": 800
|
|
37
|
-
}
|
|
39
|
+
},
|
|
40
|
+
"sessionMode": "write-access"
|
|
38
41
|
}
|
|
39
42
|
```
|
|
40
43
|
|
|
41
44
|
## Common Commands
|
|
42
45
|
|
|
43
46
|
```bash
|
|
44
|
-
npx libretto
|
|
45
|
-
npx libretto
|
|
47
|
+
npx libretto setup # first-time onboarding, auto-pins default model
|
|
48
|
+
npx libretto status # inspect AI config and open sessions
|
|
49
|
+
npx libretto ai configure openai # explicitly change provider/model
|
|
46
50
|
npx libretto open https://app.example.com --viewport 1440x900
|
|
47
|
-
npx libretto run ./integration.ts
|
|
51
|
+
npx libretto run ./integration.ts --viewport 1440x900
|
|
48
52
|
```
|
|
49
53
|
|
|
50
54
|
## Notes
|
|
51
55
|
|
|
52
56
|
- If you want a persistent default viewport for the workspace, add `viewport` to `.libretto/config.json` instead of repeating `--viewport` on every command.
|
|
53
|
-
- If `snapshot` analysis is not configured yet,
|
|
57
|
+
- If `snapshot` analysis is not configured yet, run `npx libretto setup` to auto-configure, or see the main `SKILL.md` flow.
|
|
58
|
+
- Run `npx libretto status` at any time to check which model is active and whether credentials are present.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: libretto-readonly
|
|
3
|
+
description: "Read-only Libretto workflow for diagnosing live browser state without clicks, typing, navigation, or mutation requests."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: saffron-health
|
|
7
|
+
version: "0.5.6"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## How Libretto Read-Only Works
|
|
11
|
+
|
|
12
|
+
- Use this skill when the browser session must stay strictly read-only.
|
|
13
|
+
- Libretto stores read-only vs write-access on the session itself.
|
|
14
|
+
- The primary inspection tools are `snapshot` and `readonly-exec`.
|
|
15
|
+
- `readonly-exec` reuses Libretto's normal execution pipeline, but it only exposes read-only helpers and denies mutating Playwright methods.
|
|
16
|
+
- Only a user can change the session mode for an existing session. Never change a session's mode on your own — the user must change it themselves manually.
|
|
17
|
+
|
|
18
|
+
## Working Rules
|
|
19
|
+
|
|
20
|
+
- Announce which session you are using and what page you are inspecting.
|
|
21
|
+
- Do not use `exec`, `run`, or any direct Playwright action that could change browser or application state.
|
|
22
|
+
- Do not click, type, submit forms, navigate, upload files, dispatch DOM events, or send non-GET requests.
|
|
23
|
+
- Prefer `snapshot` first when the visible page state is unclear.
|
|
24
|
+
- Use `readonly-exec` for focused inspection: titles, HTML, locator text, counts, visibility checks, and GET requests.
|
|
25
|
+
- Keep snippets small and purpose-built. Do not run multiple `readonly-exec` commands at the same time.
|
|
26
|
+
- End with diagnosis and handoff guidance, not an attempted in-browser repair.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
### `connect`
|
|
31
|
+
|
|
32
|
+
- Use `connect` to attach to an existing CDP endpoint for a preserved browser session.
|
|
33
|
+
- Use `--read-only` when creating the Libretto session handle for a preserved browser session.
|
|
34
|
+
- Libretto read-only mode is enforced through Libretto commands; direct CDP clients that skip Libretto are outside this boundary.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx libretto connect http://127.0.0.1:9222 --read-only --session failed-job-debug
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### `pages`
|
|
41
|
+
|
|
42
|
+
- Use `pages` when a popup, new tab, or second page exists.
|
|
43
|
+
- If `readonly-exec` or `snapshot` complains about multiple pages, list ids first and then pass `--page`.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx libretto pages --session failed-job-debug
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `snapshot`
|
|
50
|
+
|
|
51
|
+
- Use `snapshot` as the first high-level observation tool.
|
|
52
|
+
- Always provide both `--objective` and `--context`.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx libretto snapshot \
|
|
56
|
+
--session failed-job-debug \
|
|
57
|
+
--objective "Identify the visible failure state and likely blocking UI condition" \
|
|
58
|
+
--context "The workflow already failed and the preserved browser must remain read-only."
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `readonly-exec`
|
|
62
|
+
|
|
63
|
+
- Use `readonly-exec` for narrow inspection code only.
|
|
64
|
+
- Denied operations fail with `ReadonlyExecDenied: ...`.
|
|
65
|
+
|
|
66
|
+
#### Helpers
|
|
67
|
+
|
|
68
|
+
- `page` — a read-only Playwright `Page` proxy. Standard Playwright read methods work normally (`url()`, `title()`, `content()`, `getByRole()`, `locator()`, `textContent()`, `isVisible()`, `count()`, `scrollIntoViewIfNeeded()`, etc.). Anything that mutates the page (`click`, `fill`, `goto`, `evaluate`, `keyboard`, `mouse`) is blocked.
|
|
69
|
+
- `state` — the current Libretto session state object.
|
|
70
|
+
- `get(url, options?)` — HTTP client restricted to **GET and HEAD** requests. Replaces `fetch`, which is blocked in readonly mode. Any request with a body or a non-GET/HEAD method throws `ReadonlyExecDenied`.
|
|
71
|
+
- `scrollBy(deltaX, deltaY)` — scroll the viewport by pixel offset. Use this to inspect content below the fold without targeting a specific element.
|
|
72
|
+
|
|
73
|
+
Standard JS globals `console`, `URL`, `Buffer`, `setTimeout`, and `setInterval` are also available.
|
|
74
|
+
|
|
75
|
+
#### Examples
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx libretto readonly-exec "return page.url()" --session failed-job-debug
|
|
79
|
+
npx libretto readonly-exec "return await page.getByRole('heading').first().textContent()" --session failed-job-debug
|
|
80
|
+
|
|
81
|
+
# HTTP GET inspection
|
|
82
|
+
echo "const r = await get('https://api.example.com/status'); return await r.json()" \
|
|
83
|
+
| npx libretto readonly-exec - --session failed-job-debug
|
|
84
|
+
|
|
85
|
+
# Scroll down to inspect below-the-fold content
|
|
86
|
+
npx libretto readonly-exec "await scrollBy(0, 500)" --session failed-job-debug
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### `close`
|
|
90
|
+
|
|
91
|
+
- Use `close` when the inspection session is no longer needed.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx libretto close --session failed-job-debug
|
|
95
|
+
```
|
package/src/cli/cli.ts
CHANGED
|
@@ -16,6 +16,10 @@ Examples:
|
|
|
16
16
|
|
|
17
17
|
libretto exec "await page.locator('button:has-text(\\"Sign in\\")').click()"
|
|
18
18
|
libretto exec "await page.fill('input[name=\\"email\\"]', 'test@example.com')"
|
|
19
|
+
libretto readonly-exec "return await page.title()" --session test1
|
|
20
|
+
libretto connect http://127.0.0.1:9222 --read-only --session test1
|
|
21
|
+
libretto run ./integration.ts --read-only --session test1
|
|
22
|
+
libretto status
|
|
19
23
|
libretto ai configure openai
|
|
20
24
|
libretto ai configure anthropic
|
|
21
25
|
libretto ai configure gemini
|
|
@@ -36,6 +40,9 @@ Examples:
|
|
|
36
40
|
Available in exec:
|
|
37
41
|
page, context, state, browser, networkLog, actionLog
|
|
38
42
|
|
|
43
|
+
Available in readonly-exec:
|
|
44
|
+
page, state, snapshot, scrollBy, get
|
|
45
|
+
|
|
39
46
|
Profiles:
|
|
40
47
|
Profiles are saved to .libretto/profiles/<domain>.json (git-ignored)
|
|
41
48
|
They persist cookies, localStorage, and session data across browser launches.
|
|
@@ -46,6 +53,9 @@ Sessions:
|
|
|
46
53
|
Session state is stored in .libretto/sessions/<session>/state.json
|
|
47
54
|
CLI logs are stored in .libretto/sessions/<session>/logs.jsonl
|
|
48
55
|
Each session runs an isolated browser instance on a dynamic port.
|
|
56
|
+
Session mode is stored per session as read-only or write-access.
|
|
57
|
+
Use --read-only on open, connect, or run to create a read-only session.
|
|
58
|
+
Session mode is enforced by Libretto commands, not by raw CDP clients outside Libretto.
|
|
49
59
|
`;
|
|
50
60
|
}
|
|
51
61
|
|
package/src/cli/commands/ai.ts
CHANGED
|
@@ -1,7 +1,117 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CURRENT_CONFIG_VERSION,
|
|
4
|
+
readAiConfig,
|
|
5
|
+
writeAiConfig,
|
|
6
|
+
clearAiConfig,
|
|
7
|
+
type AiConfig,
|
|
8
|
+
} from "../core/config.js";
|
|
9
|
+
import { LIBRETTO_CONFIG_PATH } from "../core/context.js";
|
|
10
|
+
import { DEFAULT_SNAPSHOT_MODELS } from "../core/ai-model.js";
|
|
3
11
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
4
12
|
|
|
13
|
+
const PROVIDER_ALIASES: Record<string, string> = {
|
|
14
|
+
claude: DEFAULT_SNAPSHOT_MODELS.anthropic,
|
|
15
|
+
gemini: DEFAULT_SNAPSHOT_MODELS.google,
|
|
16
|
+
google: DEFAULT_SNAPSHOT_MODELS.google,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const CONFIGURE_PROVIDERS = [
|
|
20
|
+
"openai",
|
|
21
|
+
"anthropic",
|
|
22
|
+
"gemini",
|
|
23
|
+
"vertex",
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
function formatConfigureProviders(separator = " | "): string {
|
|
27
|
+
return CONFIGURE_PROVIDERS.join(separator);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function printAiConfig(config: AiConfig, configPath: string): void {
|
|
31
|
+
console.log(`Model: ${config.model}`);
|
|
32
|
+
console.log(`Config file: ${configPath}`);
|
|
33
|
+
console.log(`Updated at: ${config.updatedAt}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the model string from a `ai configure` argument.
|
|
38
|
+
* Accepts a provider shorthand ("openai", "anthropic", "gemini", "vertex")
|
|
39
|
+
* or a full provider/model-id string ("openai/gpt-4o", "anthropic/claude-sonnet-4-6").
|
|
40
|
+
*/
|
|
41
|
+
function resolveModelFromInput(input: string): string | null {
|
|
42
|
+
const trimmed = input.trim();
|
|
43
|
+
if (!trimmed) return null;
|
|
44
|
+
|
|
45
|
+
// Full model string (contains a slash)
|
|
46
|
+
if (trimmed.includes("/")) return trimmed;
|
|
47
|
+
|
|
48
|
+
// Provider shorthand
|
|
49
|
+
const normalized = trimmed.toLowerCase();
|
|
50
|
+
return (
|
|
51
|
+
(DEFAULT_SNAPSHOT_MODELS as Record<string, string>)[normalized] ??
|
|
52
|
+
PROVIDER_ALIASES[normalized] ??
|
|
53
|
+
null
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function runAiConfigure(
|
|
58
|
+
input: {
|
|
59
|
+
preset?: string;
|
|
60
|
+
clear?: boolean;
|
|
61
|
+
},
|
|
62
|
+
options: {
|
|
63
|
+
configureCommandName?: string;
|
|
64
|
+
configPath?: string;
|
|
65
|
+
} = {},
|
|
66
|
+
): void {
|
|
67
|
+
const configureCommandName =
|
|
68
|
+
options.configureCommandName ?? "npx libretto ai configure";
|
|
69
|
+
const configPath = options.configPath ?? LIBRETTO_CONFIG_PATH;
|
|
70
|
+
|
|
71
|
+
const presetArg = input.preset?.trim();
|
|
72
|
+
|
|
73
|
+
if (!presetArg && !input.clear) {
|
|
74
|
+
const config = readAiConfig(configPath);
|
|
75
|
+
if (!config) {
|
|
76
|
+
console.log(
|
|
77
|
+
`No AI config set. Choose a default model: ${configureCommandName} ${formatConfigureProviders()}`,
|
|
78
|
+
);
|
|
79
|
+
console.log(
|
|
80
|
+
"Provider credentials still come from your shell or .env file.",
|
|
81
|
+
);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
printAiConfig(config, configPath);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (input.clear) {
|
|
89
|
+
const removed = clearAiConfig(configPath);
|
|
90
|
+
if (removed) {
|
|
91
|
+
console.log(`Cleared AI config: ${configPath}`);
|
|
92
|
+
} else {
|
|
93
|
+
console.log("No AI config was set.");
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const model = resolveModelFromInput(presetArg!);
|
|
99
|
+
if (!model) {
|
|
100
|
+
console.log(
|
|
101
|
+
`Usage: ${configureCommandName} <${CONFIGURE_PROVIDERS.join("|")}|provider/model-id>\n` +
|
|
102
|
+
` ${configureCommandName}\n` +
|
|
103
|
+
` ${configureCommandName} --clear`,
|
|
104
|
+
);
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Invalid provider or model. Use one of: ${formatConfigureProviders()}, or a full model string like "openai/gpt-4o".`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const config = writeAiConfig(model, configPath);
|
|
111
|
+
console.log("AI config saved.");
|
|
112
|
+
printAiConfig(config, configPath);
|
|
113
|
+
}
|
|
114
|
+
|
|
5
115
|
export const aiConfigureInput = SimpleCLI.input({
|
|
6
116
|
positionals: [
|
|
7
117
|
SimpleCLI.positional("preset", z.string().optional(), {
|
|
@@ -1,17 +1,27 @@
|
|
|
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,
|
|
5
6
|
runConnect as runConnectWithLogger,
|
|
6
7
|
runOpen,
|
|
8
|
+
runOpenWithProvider,
|
|
7
9
|
runPages,
|
|
8
10
|
runSave,
|
|
9
11
|
} from "../core/browser.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveProviderName,
|
|
14
|
+
getCloudProviderApi,
|
|
15
|
+
} from "../core/providers/index.js";
|
|
16
|
+
import { readLibrettoConfig } from "../core/config.js";
|
|
10
17
|
import { createLoggerForSession, withSessionLogger } from "../core/context.js";
|
|
11
18
|
import {
|
|
19
|
+
type SessionAccessMode,
|
|
12
20
|
assertSessionAvailableForStart,
|
|
21
|
+
setSessionMode,
|
|
13
22
|
validateSessionName,
|
|
14
23
|
} from "../core/session.js";
|
|
24
|
+
import { warnIfInstalledSkillOutOfDate } from "../core/skill-version.js";
|
|
15
25
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
16
26
|
import {
|
|
17
27
|
sessionOption,
|
|
@@ -42,6 +52,16 @@ export function parseViewportArg(
|
|
|
42
52
|
return { width, height };
|
|
43
53
|
}
|
|
44
54
|
|
|
55
|
+
function resolveRequestedSessionMode(
|
|
56
|
+
readOnly: boolean | undefined,
|
|
57
|
+
writeAccess: boolean | undefined,
|
|
58
|
+
): SessionAccessMode {
|
|
59
|
+
if (readOnly) return "read-only";
|
|
60
|
+
if (writeAccess) return "write-access";
|
|
61
|
+
const config = readLibrettoConfig();
|
|
62
|
+
return config.sessionMode ?? "write-access";
|
|
63
|
+
}
|
|
64
|
+
|
|
45
65
|
export const openInput = SimpleCLI.input({
|
|
46
66
|
positionals: [
|
|
47
67
|
SimpleCLI.positional("url", z.string().optional(), {
|
|
@@ -52,18 +72,34 @@ export const openInput = SimpleCLI.input({
|
|
|
52
72
|
session: sessionOption(),
|
|
53
73
|
headed: SimpleCLI.flag({ help: "Run browser in headed mode" }),
|
|
54
74
|
headless: SimpleCLI.flag({ help: "Run browser in headless mode" }),
|
|
75
|
+
readOnly: SimpleCLI.flag({
|
|
76
|
+
name: "read-only",
|
|
77
|
+
help: "Create the session in read-only mode",
|
|
78
|
+
}),
|
|
79
|
+
writeAccess: SimpleCLI.flag({
|
|
80
|
+
name: "write-access",
|
|
81
|
+
help: "Create the session in write-access mode (overrides config default)",
|
|
82
|
+
}),
|
|
55
83
|
viewport: SimpleCLI.option(z.string().optional(), {
|
|
56
84
|
help: "Viewport size as WIDTHxHEIGHT (e.g. 1920x1080)",
|
|
57
85
|
}),
|
|
86
|
+
provider: SimpleCLI.option(z.string().optional(), {
|
|
87
|
+
help: "Browser provider (local, kernel, browserbase)",
|
|
88
|
+
aliases: ["-p"],
|
|
89
|
+
}),
|
|
58
90
|
},
|
|
59
91
|
})
|
|
60
92
|
.refine(
|
|
61
93
|
(input) => Boolean(input.url),
|
|
62
|
-
`Usage: libretto open <url> [--headless] [--viewport WxH] [--session <name>]`,
|
|
94
|
+
`Usage: libretto open <url> [--headless] [--read-only|--write-access] [--viewport WxH] [--session <name>]`,
|
|
63
95
|
)
|
|
64
96
|
.refine(
|
|
65
97
|
(input) => !(input.headed && input.headless),
|
|
66
98
|
"Cannot pass both --headed and --headless.",
|
|
99
|
+
)
|
|
100
|
+
.refine(
|
|
101
|
+
(input) => !(input.readOnly && input.writeAccess),
|
|
102
|
+
"Cannot pass both --read-only and --write-access.",
|
|
67
103
|
);
|
|
68
104
|
|
|
69
105
|
export const openCommand = SimpleCLI.command({
|
|
@@ -72,10 +108,30 @@ export const openCommand = SimpleCLI.command({
|
|
|
72
108
|
.input(openInput)
|
|
73
109
|
.use(withAutoSession())
|
|
74
110
|
.handle(async ({ input, ctx }) => {
|
|
111
|
+
warnIfInstalledSkillOutOfDate();
|
|
75
112
|
assertSessionAvailableForStart(ctx.session, ctx.logger);
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
113
|
+
const providerName = resolveProviderName(input.provider);
|
|
114
|
+
if (providerName === "local") {
|
|
115
|
+
const headed = input.headed || !input.headless;
|
|
116
|
+
const viewport = parseViewportArg(input.viewport);
|
|
117
|
+
await runOpen(input.url!, headed, ctx.session, ctx.logger, {
|
|
118
|
+
viewport,
|
|
119
|
+
accessMode: resolveRequestedSessionMode(
|
|
120
|
+
input.readOnly,
|
|
121
|
+
input.writeAccess,
|
|
122
|
+
),
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
const provider = getCloudProviderApi(providerName);
|
|
126
|
+
await runOpenWithProvider(
|
|
127
|
+
input.url!,
|
|
128
|
+
providerName,
|
|
129
|
+
provider,
|
|
130
|
+
ctx.session,
|
|
131
|
+
ctx.logger,
|
|
132
|
+
resolveRequestedSessionMode(input.readOnly, input.writeAccess),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
79
135
|
});
|
|
80
136
|
|
|
81
137
|
export const connectInput = SimpleCLI.input({
|
|
@@ -86,11 +142,24 @@ export const connectInput = SimpleCLI.input({
|
|
|
86
142
|
],
|
|
87
143
|
named: {
|
|
88
144
|
session: sessionOption(),
|
|
145
|
+
readOnly: SimpleCLI.flag({
|
|
146
|
+
name: "read-only",
|
|
147
|
+
help: "Create the session in read-only mode",
|
|
148
|
+
}),
|
|
149
|
+
writeAccess: SimpleCLI.flag({
|
|
150
|
+
name: "write-access",
|
|
151
|
+
help: "Create the session in write-access mode (overrides config default)",
|
|
152
|
+
}),
|
|
89
153
|
},
|
|
90
|
-
})
|
|
91
|
-
(
|
|
92
|
-
|
|
93
|
-
|
|
154
|
+
})
|
|
155
|
+
.refine(
|
|
156
|
+
(input) => Boolean(input.cdpUrl),
|
|
157
|
+
`Usage: libretto connect <cdp-url> [--read-only|--write-access] --session <name>`,
|
|
158
|
+
)
|
|
159
|
+
.refine(
|
|
160
|
+
(input) => !(input.readOnly && input.writeAccess),
|
|
161
|
+
"Cannot pass both --read-only and --write-access.",
|
|
162
|
+
);
|
|
94
163
|
|
|
95
164
|
export const connectCommand = SimpleCLI.command({
|
|
96
165
|
description: "Connect to an existing Chrome DevTools Protocol (CDP) endpoint",
|
|
@@ -98,7 +167,13 @@ export const connectCommand = SimpleCLI.command({
|
|
|
98
167
|
.input(connectInput)
|
|
99
168
|
.use(withAutoSession())
|
|
100
169
|
.handle(async ({ input, ctx }) => {
|
|
101
|
-
|
|
170
|
+
warnIfInstalledSkillOutOfDate();
|
|
171
|
+
await runConnectWithLogger(
|
|
172
|
+
input.cdpUrl!,
|
|
173
|
+
ctx.session,
|
|
174
|
+
ctx.logger,
|
|
175
|
+
resolveRequestedSessionMode(input.readOnly, input.writeAccess),
|
|
176
|
+
);
|
|
102
177
|
});
|
|
103
178
|
|
|
104
179
|
export const saveInput = SimpleCLI.input({
|
|
@@ -140,6 +215,32 @@ export const pagesCommand = SimpleCLI.command({
|
|
|
140
215
|
await runPages(ctx.session, ctx.logger);
|
|
141
216
|
});
|
|
142
217
|
|
|
218
|
+
export const sessionModeInput = SimpleCLI.input({
|
|
219
|
+
positionals: [
|
|
220
|
+
SimpleCLI.positional("mode", SessionAccessModeSchema.optional(), {
|
|
221
|
+
help: "Session mode to set",
|
|
222
|
+
}),
|
|
223
|
+
],
|
|
224
|
+
named: {
|
|
225
|
+
session: sessionOption(),
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
export const sessionModeCommand = SimpleCLI.command({
|
|
230
|
+
description: "View or set the session access mode",
|
|
231
|
+
})
|
|
232
|
+
.input(sessionModeInput)
|
|
233
|
+
.use(withRequiredSession())
|
|
234
|
+
.handle(async ({ input, ctx }) => {
|
|
235
|
+
if (!input.mode) {
|
|
236
|
+
console.log(`Session "${ctx.session}" mode: ${ctx.sessionState.mode}`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const nextState = setSessionMode(ctx.session, input.mode, ctx.logger);
|
|
241
|
+
console.log(`Session "${ctx.session}" mode set to ${nextState.mode}.`);
|
|
242
|
+
});
|
|
243
|
+
|
|
143
244
|
export const closeInput = SimpleCLI.input({
|
|
144
245
|
positionals: [],
|
|
145
246
|
named: {
|
|
@@ -179,6 +280,7 @@ export const browserCommands = {
|
|
|
179
280
|
connect: connectCommand,
|
|
180
281
|
save: saveCommand,
|
|
181
282
|
pages: pagesCommand,
|
|
283
|
+
"session-mode": sessionModeCommand,
|
|
182
284
|
close: closeCommand,
|
|
183
285
|
};
|
|
184
286
|
|