libretto 0.4.0 → 0.4.1
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/dist/cli/cli.js +83 -223
- package/dist/cli/commands/ai.js +32 -16
- package/dist/cli/commands/browser.js +126 -85
- package/dist/cli/commands/execution.js +147 -108
- package/dist/cli/commands/init.js +100 -40
- package/dist/cli/commands/logs.js +90 -65
- package/dist/cli/commands/shared.js +50 -0
- package/dist/cli/commands/snapshot.js +31 -16
- package/dist/cli/framework/simple-cli.js +776 -0
- package/dist/cli/router.js +29 -0
- package/package.json +2 -4
- /package/{.agents/skills → skills}/libretto/SKILL.md +0 -0
- /package/{.agents/skills → skills}/libretto/code-generation-rules.md +0 -0
- /package/{.agents/skills → skills}/libretto/integration-approach-selection.md +0 -0
package/dist/cli/cli.js
CHANGED
|
@@ -1,61 +1,18 @@
|
|
|
1
|
-
import yargs from "yargs";
|
|
2
|
-
import { hideBin } from "yargs/helpers";
|
|
3
|
-
import { registerAICommands } from "./commands/ai.js";
|
|
4
|
-
import { registerBrowserCommands } from "./commands/browser.js";
|
|
5
|
-
import { registerExecutionCommands } from "./commands/execution.js";
|
|
6
|
-
import { registerLogCommands } from "./commands/logs.js";
|
|
7
|
-
import { registerInitCommand } from "./commands/init.js";
|
|
8
|
-
import { registerSnapshotCommands } from "./commands/snapshot.js";
|
|
9
1
|
import {
|
|
10
2
|
closeLogger,
|
|
11
3
|
createLoggerForSession,
|
|
12
4
|
ensureLibrettoSetup
|
|
13
5
|
} from "./core/context.js";
|
|
14
6
|
import {
|
|
15
|
-
|
|
7
|
+
SESSION_DEFAULT,
|
|
16
8
|
validateSessionName
|
|
17
9
|
} from "./core/session.js";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"open",
|
|
22
|
-
"run",
|
|
23
|
-
"ai",
|
|
24
|
-
"save",
|
|
25
|
-
"exec",
|
|
26
|
-
"snapshot",
|
|
27
|
-
"network",
|
|
28
|
-
"actions",
|
|
29
|
-
"pages",
|
|
30
|
-
"resume",
|
|
31
|
-
"close",
|
|
32
|
-
"init",
|
|
33
|
-
"--help",
|
|
34
|
-
"-h",
|
|
35
|
-
"help"
|
|
36
|
-
]);
|
|
37
|
-
function printUsage() {
|
|
38
|
-
console.log(`Usage: libretto-cli <command> [--session <name>]
|
|
39
|
-
|
|
40
|
-
Commands:
|
|
41
|
-
init [--skip-browsers] Initialize libretto (install browsers, check AI setup)
|
|
42
|
-
open <url> [--headless] Launch browser and open URL (headed by default)
|
|
43
|
-
Automatically loads saved profile if available
|
|
44
|
-
run <integrationFile> <integrationExport> [--params <json> | --params-file <path>] [--tsconfig <path>] [--headed|--headless] Run an exported Libretto workflow from a file
|
|
45
|
-
ai configure [preset] [-- <command prefix...>] Configure AI runtime for analysis commands
|
|
46
|
-
save <url|domain> Save current browser session (cookies, localStorage, etc.)
|
|
47
|
-
exec <code> [--visualize] Execute Playwright typescript code (--visualize enables ghost cursor + highlight)
|
|
48
|
-
snapshot [--objective <text> --context <text>] Capture PNG + HTML; analyze when objective is provided (context optional)
|
|
49
|
-
network [--last N] [--filter regex] [--method M] [--clear] View captured network requests
|
|
50
|
-
actions [--last N] [--filter regex] [--action TYPE] [--source SOURCE] [--clear] View captured actions
|
|
51
|
-
pages List open pages in the active session
|
|
52
|
-
resume Resume a paused workflow in the active session
|
|
53
|
-
close [--all] [--force] Close the browser for the session, or all tracked sessions with --all
|
|
10
|
+
import { createCLIApp } from "./router.js";
|
|
11
|
+
function renderUsage(app) {
|
|
12
|
+
return `${app.renderHelp()}
|
|
54
13
|
|
|
55
14
|
Options:
|
|
56
|
-
--session <name> Use a named session
|
|
57
|
-
If omitted for open/run, a session id is auto-generated
|
|
58
|
-
All other stateful commands require --session
|
|
15
|
+
--session <name> Use a named session (default: "default")
|
|
59
16
|
Built-in sessions: default, dev-server, browser-agent
|
|
60
17
|
|
|
61
18
|
Examples:
|
|
@@ -67,10 +24,11 @@ Examples:
|
|
|
67
24
|
|
|
68
25
|
libretto-cli exec "await page.locator('button:has-text(\\"Sign in\\")').click()"
|
|
69
26
|
libretto-cli exec "await page.fill('input[name=\\"email\\"]', 'test@example.com')"
|
|
70
|
-
libretto-cli ai configure
|
|
71
|
-
libretto-cli ai configure
|
|
27
|
+
libretto-cli ai configure openai
|
|
28
|
+
libretto-cli ai configure anthropic
|
|
72
29
|
libretto-cli ai configure gemini
|
|
73
|
-
libretto-cli ai configure
|
|
30
|
+
libretto-cli ai configure vertex
|
|
31
|
+
libretto-cli ai configure openai/gpt-4o
|
|
74
32
|
libretto-cli snapshot
|
|
75
33
|
libretto-cli snapshot --objective "Find the submit button" --context "Submitting a referral form, already filled in patient details"
|
|
76
34
|
libretto-cli resume --session default
|
|
@@ -96,201 +54,103 @@ Sessions:
|
|
|
96
54
|
Session state is stored in .libretto/sessions/<session>/state.json
|
|
97
55
|
CLI logs are stored in .libretto/sessions/<session>/logs.jsonl
|
|
98
56
|
Each session runs an isolated browser instance on a dynamic port.
|
|
99
|
-
|
|
57
|
+
`;
|
|
100
58
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
59
|
+
function readSessionArgBeforePassthrough(rawArgs) {
|
|
60
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
61
|
+
const token = rawArgs[index];
|
|
62
|
+
if (token === "--") return void 0;
|
|
63
|
+
if (token === "--session") {
|
|
64
|
+
const value2 = rawArgs[index + 1];
|
|
65
|
+
if (!value2 || value2 === "--" || value2.startsWith("--")) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return value2;
|
|
108
69
|
}
|
|
70
|
+
if (!token.startsWith("--session=")) continue;
|
|
71
|
+
const value = token.slice("--session=".length);
|
|
72
|
+
if (value.length === 0 || value === "--" || value.startsWith("--")) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
109
76
|
}
|
|
110
|
-
return
|
|
77
|
+
return void 0;
|
|
111
78
|
}
|
|
112
79
|
function parseSessionForLog(rawArgs) {
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
if (!value || value.startsWith("--") || CLI_COMMANDS.has(value)) {
|
|
117
|
-
return null;
|
|
80
|
+
const value = readSessionArgBeforePassthrough(rawArgs);
|
|
81
|
+
if (value === void 0 || value === null) {
|
|
82
|
+
return SESSION_DEFAULT;
|
|
118
83
|
}
|
|
119
84
|
try {
|
|
120
85
|
validateSessionName(value);
|
|
121
86
|
return value;
|
|
122
87
|
} catch {
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
function hasExplicitSession(rawArgs) {
|
|
127
|
-
return rawArgs.includes("--session");
|
|
128
|
-
}
|
|
129
|
-
function randomSessionId() {
|
|
130
|
-
const digits = Math.floor(Math.random() * 1e4).toString().padStart(4, "0");
|
|
131
|
-
return `ses-${digits}`;
|
|
132
|
-
}
|
|
133
|
-
function generateSessionId() {
|
|
134
|
-
const activeSessions = new Set(listSessionsWithStateFile());
|
|
135
|
-
for (let attempt = 0; attempt < 1e4; attempt += 1) {
|
|
136
|
-
const candidate = randomSessionId();
|
|
137
|
-
if (!activeSessions.has(candidate)) {
|
|
138
|
-
return candidate;
|
|
139
|
-
}
|
|
88
|
+
return SESSION_DEFAULT;
|
|
140
89
|
}
|
|
141
|
-
throw new Error(
|
|
142
|
-
"Could not generate an available session id. Close an existing session and try again."
|
|
143
|
-
);
|
|
144
90
|
}
|
|
145
|
-
function
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (token === "--visualize") {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (token === "--page") {
|
|
156
|
-
const maybeValue = filteredArgs[i + 1];
|
|
157
|
-
if (maybeValue && !maybeValue.startsWith("--")) {
|
|
158
|
-
i += 1;
|
|
159
|
-
}
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
if (token.startsWith("--page=")) {
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (token.startsWith("-")) {
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
return true;
|
|
91
|
+
function validateLegacySessionArg(rawArgs) {
|
|
92
|
+
const value = readSessionArgBeforePassthrough(rawArgs);
|
|
93
|
+
if (value === void 0) return;
|
|
94
|
+
if (value === null) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"Usage: libretto-cli <command> [--session <name>]\nMissing or invalid --session value."
|
|
97
|
+
);
|
|
169
98
|
}
|
|
170
|
-
|
|
99
|
+
validateSessionName(value);
|
|
171
100
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return
|
|
101
|
+
function initializeLogger(rawArgs) {
|
|
102
|
+
const sessionForLog = parseSessionForLog(rawArgs);
|
|
103
|
+
const logger = createLoggerForSession(sessionForLog);
|
|
104
|
+
logger.info("cli-start", {
|
|
105
|
+
args: rawArgs,
|
|
106
|
+
cwd: process.cwd(),
|
|
107
|
+
session: sessionForLog
|
|
108
|
+
});
|
|
109
|
+
return logger;
|
|
181
110
|
}
|
|
182
|
-
function
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
args: rawArgs,
|
|
189
|
-
generatedSession: null,
|
|
190
|
-
resolvedSession: explicitSession
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
if (hasExplicitSession(rawArgs)) {
|
|
194
|
-
return {
|
|
195
|
-
args: rawArgs,
|
|
196
|
-
generatedSession: null,
|
|
197
|
-
resolvedSession: explicitSession
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
if (!AUTO_SESSION_COMMANDS.has(command)) {
|
|
201
|
-
return {
|
|
202
|
-
args: rawArgs,
|
|
203
|
-
generatedSession: null,
|
|
204
|
-
resolvedSession: null
|
|
205
|
-
};
|
|
111
|
+
async function withCliLogger(rawArgs, run) {
|
|
112
|
+
const logger = initializeLogger(rawArgs);
|
|
113
|
+
try {
|
|
114
|
+
return await run(logger);
|
|
115
|
+
} finally {
|
|
116
|
+
await closeLogger(logger);
|
|
206
117
|
}
|
|
207
|
-
const generatedSession = generateSessionId();
|
|
208
|
-
return {
|
|
209
|
-
args: [...rawArgs, "--session", generatedSession],
|
|
210
|
-
generatedSession,
|
|
211
|
-
resolvedSession: generatedSession
|
|
212
|
-
};
|
|
213
118
|
}
|
|
214
|
-
function
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
global: true,
|
|
219
|
-
requiresArg: true
|
|
220
|
-
}).middleware((argv) => {
|
|
221
|
-
if (argv.session !== void 0) {
|
|
222
|
-
validateSessionName(String(argv.session));
|
|
223
|
-
}
|
|
224
|
-
}).exitProcess(false).help(false).version(false).fail((msg, err) => {
|
|
225
|
-
if (err) throw err;
|
|
226
|
-
throw new Error(msg || "Command failed");
|
|
227
|
-
});
|
|
228
|
-
parser = registerBrowserCommands(parser, logger);
|
|
229
|
-
parser = registerExecutionCommands(parser, logger);
|
|
230
|
-
parser = registerLogCommands(parser);
|
|
231
|
-
parser = registerAICommands(parser);
|
|
232
|
-
parser = registerSnapshotCommands(parser, logger);
|
|
233
|
-
parser = registerInitCommand(parser);
|
|
234
|
-
parser = parser.command("help", "Show usage", () => {
|
|
235
|
-
}, () => {
|
|
236
|
-
printUsage();
|
|
237
|
-
});
|
|
238
|
-
return parser;
|
|
119
|
+
function isRootHelpRequest(rawArgs) {
|
|
120
|
+
if (rawArgs.length === 0) return true;
|
|
121
|
+
if (rawArgs[0] === "--help" || rawArgs[0] === "-h") return true;
|
|
122
|
+
return rawArgs[0] === "help" && rawArgs.length === 1;
|
|
239
123
|
}
|
|
240
124
|
async function runLibrettoCLI() {
|
|
241
125
|
const rawArgs = process.argv.slice(2);
|
|
242
126
|
let exitCode = 0;
|
|
243
|
-
let effectiveArgs = rawArgs;
|
|
244
|
-
let generatedSession = null;
|
|
245
|
-
let resolvedSession = null;
|
|
246
|
-
({
|
|
247
|
-
args: effectiveArgs,
|
|
248
|
-
generatedSession,
|
|
249
|
-
resolvedSession
|
|
250
|
-
} = resolveSessionArgs(rawArgs));
|
|
251
127
|
ensureLibrettoSetup();
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
128
|
+
await withCliLogger(rawArgs, async (logger) => {
|
|
129
|
+
const app = createCLIApp(logger);
|
|
130
|
+
try {
|
|
131
|
+
validateLegacySessionArg(rawArgs);
|
|
132
|
+
if (isRootHelpRequest(rawArgs)) {
|
|
133
|
+
console.log(renderUsage(app));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
logger.info("cli-command", { args: rawArgs });
|
|
137
|
+
const result = await app.run(rawArgs);
|
|
138
|
+
if (typeof result === "string") {
|
|
139
|
+
console.log(result);
|
|
140
|
+
}
|
|
141
|
+
} catch (err) {
|
|
142
|
+
logger.error("cli-error", { error: err, args: rawArgs });
|
|
143
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
144
|
+
if (message.startsWith("Unknown command: ")) {
|
|
145
|
+
console.error(`${message}
|
|
261
146
|
`);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
`Missing required --session for "${command}".`,
|
|
270
|
-
"Pass --session <name>, or use open/run without --session to auto-create one."
|
|
271
|
-
].join("\n")
|
|
272
|
-
);
|
|
273
|
-
process.exit(1);
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
const sessionForLogger = resolvedSession ?? "cli";
|
|
277
|
-
const logger = createLoggerForSession(sessionForLogger);
|
|
278
|
-
try {
|
|
279
|
-
const parser = createParser(logger);
|
|
280
|
-
await parser.parseAsync(effectiveArgs);
|
|
281
|
-
} catch (err) {
|
|
282
|
-
logger.error("cli-error", {
|
|
283
|
-
error: err,
|
|
284
|
-
args: rawArgs,
|
|
285
|
-
effectiveArgs,
|
|
286
|
-
generatedSession
|
|
287
|
-
});
|
|
288
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
289
|
-
console.error(message);
|
|
290
|
-
exitCode = 1;
|
|
291
|
-
} finally {
|
|
292
|
-
await closeLogger(logger);
|
|
293
|
-
}
|
|
147
|
+
console.log(renderUsage(app));
|
|
148
|
+
} else {
|
|
149
|
+
console.error(message);
|
|
150
|
+
}
|
|
151
|
+
exitCode = 1;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
294
154
|
process.exit(exitCode);
|
|
295
155
|
}
|
|
296
156
|
export {
|
package/dist/cli/commands/ai.js
CHANGED
|
@@ -1,19 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import { runAiConfigure } from "../core/ai-config.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
4
|
+
const aiConfigureInput = SimpleCLI.input({
|
|
5
|
+
positionals: [
|
|
6
|
+
SimpleCLI.positional("preset", z.string().optional(), {
|
|
7
|
+
help: "Provider shorthand or provider/model-id"
|
|
8
|
+
})
|
|
9
|
+
],
|
|
10
|
+
named: {
|
|
11
|
+
clear: SimpleCLI.flag({ help: "Clear existing AI config" })
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const aiCommands = SimpleCLI.group({
|
|
15
|
+
description: "AI commands",
|
|
16
|
+
routes: {
|
|
17
|
+
configure: SimpleCLI.command({
|
|
18
|
+
description: "Configure AI runtime"
|
|
19
|
+
}).input(aiConfigureInput).handle(async ({ input }) => {
|
|
20
|
+
runAiConfigure(
|
|
21
|
+
{
|
|
22
|
+
clear: input.clear,
|
|
23
|
+
preset: input.preset
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
configureCommandName: "libretto-cli ai configure"
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
});
|
|
17
32
|
export {
|
|
18
|
-
|
|
33
|
+
aiCommands,
|
|
34
|
+
aiConfigureInput
|
|
19
35
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import {
|
|
2
3
|
runClose as runCloseWithLogger,
|
|
3
4
|
runCloseAll as runCloseAllWithLogger,
|
|
@@ -6,91 +7,123 @@ import {
|
|
|
6
7
|
runSave
|
|
7
8
|
} from "../core/browser.js";
|
|
8
9
|
import { withSessionLogger } from "../core/context.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
10
|
+
import { assertSessionAvailableForStart } from "../core/session.js";
|
|
11
|
+
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
12
|
+
import {
|
|
13
|
+
loadSessionStateMiddleware,
|
|
14
|
+
resolveSessionMiddleware,
|
|
15
|
+
sessionOption
|
|
16
|
+
} from "./shared.js";
|
|
17
|
+
function parseViewportArg(viewportArg) {
|
|
18
|
+
if (!viewportArg) return void 0;
|
|
19
|
+
const match = viewportArg.match(/^(\d+)x(\d+)$/i);
|
|
20
|
+
if (!match) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
"Invalid --viewport format. Expected WIDTHxHEIGHT (e.g. 1920x1080)."
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
const width = Number(match[1]);
|
|
26
|
+
const height = Number(match[2]);
|
|
27
|
+
if (width < 1 || height < 1) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"Invalid --viewport dimensions. Width and height must be at least 1."
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return { width, height };
|
|
33
|
+
}
|
|
34
|
+
const openInput = SimpleCLI.input({
|
|
35
|
+
positionals: [
|
|
36
|
+
SimpleCLI.positional("url", z.string().optional(), {
|
|
37
|
+
help: "URL to open"
|
|
38
|
+
})
|
|
39
|
+
],
|
|
40
|
+
named: {
|
|
41
|
+
session: sessionOption(),
|
|
42
|
+
headed: SimpleCLI.flag({ help: "Run browser in headed mode" }),
|
|
43
|
+
headless: SimpleCLI.flag({ help: "Run browser in headless mode" }),
|
|
44
|
+
viewport: SimpleCLI.option(z.string().optional(), {
|
|
45
|
+
help: "Viewport size as WIDTHxHEIGHT (e.g. 1920x1080)"
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}).refine(
|
|
49
|
+
(input) => Boolean(input.url),
|
|
50
|
+
"Usage: libretto-cli open <url> [--headless] [--viewport WxH] [--session <name>]"
|
|
51
|
+
).refine(
|
|
52
|
+
(input) => !(input.headed && input.headless),
|
|
53
|
+
"Cannot pass both --headed and --headless."
|
|
54
|
+
);
|
|
55
|
+
function createOpenCommand(logger) {
|
|
56
|
+
return SimpleCLI.command({
|
|
57
|
+
description: "Launch browser and open URL (headed by default)"
|
|
58
|
+
}).input(openInput).use(resolveSessionMiddleware).handle(async ({ input, ctx }) => {
|
|
59
|
+
assertSessionAvailableForStart(ctx.session, logger);
|
|
60
|
+
const headed = input.headed || !input.headless;
|
|
61
|
+
const viewport = parseViewportArg(input.viewport);
|
|
62
|
+
await runOpen(input.url, headed, ctx.session, logger, { viewport });
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const saveInput = SimpleCLI.input({
|
|
66
|
+
positionals: [
|
|
67
|
+
SimpleCLI.positional("urlOrDomain", z.string().optional(), {
|
|
68
|
+
help: "URL or domain to save"
|
|
69
|
+
})
|
|
70
|
+
],
|
|
71
|
+
named: {
|
|
72
|
+
session: sessionOption()
|
|
73
|
+
}
|
|
74
|
+
}).refine(
|
|
75
|
+
(input) => Boolean(input.urlOrDomain),
|
|
76
|
+
"Usage: libretto-cli save <url|domain> [--session <name>]"
|
|
77
|
+
);
|
|
78
|
+
function createSaveCommand(logger) {
|
|
79
|
+
return SimpleCLI.command({
|
|
80
|
+
description: "Save current browser session"
|
|
81
|
+
}).input(saveInput).use(resolveSessionMiddleware).use(loadSessionStateMiddleware).handle(async ({ input, ctx }) => {
|
|
82
|
+
await runSave(input.urlOrDomain, ctx.session, logger);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const pagesInput = SimpleCLI.input({
|
|
86
|
+
positionals: [],
|
|
87
|
+
named: {
|
|
88
|
+
session: sessionOption()
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
function createPagesCommand(logger) {
|
|
92
|
+
return SimpleCLI.command({
|
|
93
|
+
description: "List open pages in the session"
|
|
94
|
+
}).input(pagesInput).use(resolveSessionMiddleware).use(loadSessionStateMiddleware).handle(async ({ ctx }) => {
|
|
95
|
+
await runPages(ctx.session, logger);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const closeInput = SimpleCLI.input({
|
|
99
|
+
positionals: [],
|
|
100
|
+
named: {
|
|
101
|
+
session: sessionOption(),
|
|
102
|
+
all: SimpleCLI.flag({ help: "Close all tracked sessions in this workspace" }),
|
|
103
|
+
force: SimpleCLI.flag({ help: "Force kill sessions that ignore SIGTERM (requires --all)" })
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
function createCloseCommand(logger) {
|
|
107
|
+
return SimpleCLI.command({
|
|
108
|
+
description: "Close the browser"
|
|
109
|
+
}).input(closeInput).use(resolveSessionMiddleware).handle(async ({ input, ctx }) => {
|
|
110
|
+
if (input.force && !input.all) {
|
|
111
|
+
throw new Error("Usage: libretto-cli close --all [--force]");
|
|
66
112
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"close",
|
|
71
|
-
"Close the browser",
|
|
72
|
-
(cmd) => cmd.option("all", {
|
|
73
|
-
type: "boolean",
|
|
74
|
-
default: false,
|
|
75
|
-
describe: "Close all tracked sessions in this workspace"
|
|
76
|
-
}).option("force", {
|
|
77
|
-
type: "boolean",
|
|
78
|
-
default: false,
|
|
79
|
-
describe: "Force kill sessions that ignore SIGTERM (requires --all)"
|
|
80
|
-
}),
|
|
81
|
-
async (argv) => {
|
|
82
|
-
const closeAll = Boolean(argv.all);
|
|
83
|
-
const force = Boolean(argv.force);
|
|
84
|
-
if (force && !closeAll) {
|
|
85
|
-
throw new Error("Usage: libretto-cli close --all [--force]");
|
|
86
|
-
}
|
|
87
|
-
if (closeAll) {
|
|
88
|
-
await runCloseAllWithLogger(logger, { force });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
await runCloseWithLogger(String(argv.session), logger);
|
|
113
|
+
if (input.all) {
|
|
114
|
+
await runCloseAllWithLogger(logger, { force: input.force });
|
|
115
|
+
return;
|
|
92
116
|
}
|
|
93
|
-
|
|
117
|
+
await runCloseWithLogger(ctx.session, logger);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function createBrowserCommands(logger) {
|
|
121
|
+
return {
|
|
122
|
+
open: createOpenCommand(logger),
|
|
123
|
+
save: createSaveCommand(logger),
|
|
124
|
+
pages: createPagesCommand(logger),
|
|
125
|
+
close: createCloseCommand(logger)
|
|
126
|
+
};
|
|
94
127
|
}
|
|
95
128
|
async function runClose(session) {
|
|
96
129
|
await withSessionLogger(session, async (logger) => {
|
|
@@ -98,6 +131,14 @@ async function runClose(session) {
|
|
|
98
131
|
});
|
|
99
132
|
}
|
|
100
133
|
export {
|
|
101
|
-
|
|
102
|
-
|
|
134
|
+
closeInput,
|
|
135
|
+
createBrowserCommands,
|
|
136
|
+
createCloseCommand,
|
|
137
|
+
createOpenCommand,
|
|
138
|
+
createPagesCommand,
|
|
139
|
+
createSaveCommand,
|
|
140
|
+
openInput,
|
|
141
|
+
pagesInput,
|
|
142
|
+
runClose,
|
|
143
|
+
saveInput
|
|
103
144
|
};
|