libretto 0.3.2 → 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.
Files changed (31) hide show
  1. package/dist/cli/cli.js +83 -223
  2. package/dist/cli/commands/ai.js +32 -18
  3. package/dist/cli/commands/browser.js +126 -85
  4. package/dist/cli/commands/execution.js +147 -108
  5. package/dist/cli/commands/init.js +234 -131
  6. package/dist/cli/commands/logs.js +90 -65
  7. package/dist/cli/commands/shared.js +50 -0
  8. package/dist/cli/commands/snapshot.js +62 -37
  9. package/dist/cli/core/ai-config.js +29 -44
  10. package/dist/cli/core/api-snapshot-analyzer.js +74 -0
  11. package/dist/cli/core/context.js +1 -1
  12. package/dist/cli/core/snapshot-analyzer.js +200 -87
  13. package/dist/cli/core/snapshot-api-config.js +137 -0
  14. package/dist/cli/framework/simple-cli.js +776 -0
  15. package/dist/cli/router.js +29 -0
  16. package/dist/shared/condense-dom/condense-dom.cjs +462 -0
  17. package/dist/shared/condense-dom/condense-dom.d.cts +34 -0
  18. package/dist/shared/condense-dom/condense-dom.d.ts +34 -0
  19. package/dist/shared/condense-dom/condense-dom.js +438 -0
  20. package/dist/shared/llm/ai-sdk-adapter.cjs +5 -1
  21. package/dist/shared/llm/ai-sdk-adapter.js +5 -1
  22. package/dist/shared/llm/client.cjs +106 -27
  23. package/dist/shared/llm/client.d.cts +8 -1
  24. package/dist/shared/llm/client.d.ts +8 -1
  25. package/dist/shared/llm/client.js +89 -23
  26. package/dist/shared/llm/types.d.cts +2 -1
  27. package/dist/shared/llm/types.d.ts +2 -1
  28. package/package.json +7 -4
  29. /package/{.agents/skills → skills}/libretto/SKILL.md +0 -0
  30. /package/{.agents/skills → skills}/libretto/code-generation-rules.md +0 -0
  31. /package/{.agents/skills → skills}/libretto/integration-approach-selection.md +0 -0
@@ -1,6 +1,13 @@
1
1
  import { LLMClient } from './types.js';
2
2
  import 'zod';
3
3
 
4
+ type Provider = "google" | "vertex" | "anthropic" | "openai";
5
+ declare function parseModel(model: string): {
6
+ provider: Provider;
7
+ modelId: string;
8
+ };
9
+ declare function hasProviderCredentials(provider: Provider, env?: NodeJS.ProcessEnv): boolean;
10
+ declare function missingProviderCredentialsMessage(provider: Provider): string;
4
11
  declare function createLLMClient(model: string): LLMClient;
5
12
 
6
- export { createLLMClient };
13
+ export { type Provider, createLLMClient, hasProviderCredentials, missingProviderCredentialsMessage, parseModel };
@@ -1,32 +1,87 @@
1
- import { createVertex } from "@ai-sdk/google-vertex";
2
- import { createAnthropic } from "@ai-sdk/anthropic";
3
- import { createOpenAI } from "@ai-sdk/openai";
4
1
  import { generateObject } from "ai";
2
+ const GEMINI_API_KEY_ENV_VARS = [
3
+ "GEMINI_API_KEY",
4
+ "GOOGLE_GENERATIVE_AI_API_KEY"
5
+ ];
6
+ const VERTEX_PROJECT_ENV_VARS = [
7
+ "GOOGLE_CLOUD_PROJECT",
8
+ "GCLOUD_PROJECT"
9
+ ];
10
+ const SUPPORTED_PROVIDER_ALIASES = {
11
+ google: "google",
12
+ gemini: "google",
13
+ vertex: "vertex",
14
+ anthropic: "anthropic",
15
+ codex: "openai",
16
+ openai: "openai"
17
+ };
18
+ function readFirstEnvValue(env, names) {
19
+ for (const name of names) {
20
+ const value = env[name]?.trim();
21
+ if (value) return value;
22
+ }
23
+ return null;
24
+ }
5
25
  function parseModel(model) {
6
26
  const slashIndex = model.indexOf("/");
7
27
  if (slashIndex === -1) {
8
28
  throw new Error(
9
- `Invalid model string "${model}". Expected format: "provider/model-id" (e.g. "google/gemini-3-flash-preview").`
29
+ `Invalid model string "${model}". Expected format: "provider/model-id" (for example "openai/gpt-5.4", "anthropic/claude-sonnet-4-6", "google/gemini-2.5-pro", or "vertex/gemini-2.5-pro").`
10
30
  );
11
31
  }
12
- const provider = model.slice(0, slashIndex);
32
+ const providerInput = model.slice(0, slashIndex).toLowerCase();
33
+ const provider = SUPPORTED_PROVIDER_ALIASES[providerInput];
13
34
  const modelId = model.slice(slashIndex + 1);
14
- if (!["google", "anthropic", "openai"].includes(provider)) {
35
+ if (!provider) {
15
36
  throw new Error(
16
- `Unsupported provider "${provider}". Supported providers: google, anthropic, openai.`
37
+ `Unsupported provider "${providerInput}". Supported providers: openai/codex, anthropic, google (Gemini API), and vertex.`
17
38
  );
18
39
  }
19
40
  return { provider, modelId };
20
41
  }
21
- function getProviderModel(provider, modelId) {
42
+ function hasProviderCredentials(provider, env = process.env) {
43
+ switch (provider) {
44
+ case "google":
45
+ return readFirstEnvValue(env, GEMINI_API_KEY_ENV_VARS) !== null;
46
+ case "vertex":
47
+ return readFirstEnvValue(env, VERTEX_PROJECT_ENV_VARS) !== null;
48
+ case "anthropic":
49
+ return Boolean(env.ANTHROPIC_API_KEY?.trim());
50
+ case "openai":
51
+ return Boolean(env.OPENAI_API_KEY?.trim());
52
+ }
53
+ }
54
+ function missingProviderCredentialsMessage(provider) {
55
+ switch (provider) {
56
+ case "google":
57
+ return "Missing Gemini API key. Set GEMINI_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY.";
58
+ case "vertex":
59
+ return "Missing Vertex AI project. Set GOOGLE_CLOUD_PROJECT (or GCLOUD_PROJECT) and ensure application default credentials are configured.";
60
+ case "anthropic": {
61
+ return "Missing Anthropic API key. Set ANTHROPIC_API_KEY.";
62
+ }
63
+ case "openai": {
64
+ return "Missing OpenAI API key. Set OPENAI_API_KEY.";
65
+ }
66
+ }
67
+ }
68
+ async function getProviderModel(provider, modelId) {
22
69
  switch (provider) {
23
70
  case "google": {
24
- const project = process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
71
+ const apiKey = readFirstEnvValue(process.env, GEMINI_API_KEY_ENV_VARS);
72
+ if (!apiKey) {
73
+ throw new Error(missingProviderCredentialsMessage(provider));
74
+ }
75
+ const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
76
+ const google = createGoogleGenerativeAI({ apiKey });
77
+ return google(modelId);
78
+ }
79
+ case "vertex": {
80
+ const project = readFirstEnvValue(process.env, VERTEX_PROJECT_ENV_VARS);
25
81
  if (!project) {
26
- throw new Error(
27
- "Missing GCP project for Vertex AI. Set GOOGLE_CLOUD_PROJECT environment variable and ensure application default credentials are configured (gcloud auth application-default login)."
28
- );
82
+ throw new Error(missingProviderCredentialsMessage(provider));
29
83
  }
84
+ const { createVertex } = await import("@ai-sdk/google-vertex");
30
85
  const vertex = createVertex({
31
86
  project,
32
87
  location: process.env.GOOGLE_CLOUD_LOCATION || "global"
@@ -34,22 +89,20 @@ function getProviderModel(provider, modelId) {
34
89
  return vertex(modelId);
35
90
  }
36
91
  case "anthropic": {
37
- const apiKey = process.env.ANTHROPIC_API_KEY;
92
+ const apiKey = process.env.ANTHROPIC_API_KEY?.trim();
38
93
  if (!apiKey) {
39
- throw new Error(
40
- "Missing API key for Anthropic. Set ANTHROPIC_API_KEY environment variable."
41
- );
94
+ throw new Error(missingProviderCredentialsMessage(provider));
42
95
  }
96
+ const { createAnthropic } = await import("@ai-sdk/anthropic");
43
97
  const anthropic = createAnthropic({ apiKey });
44
98
  return anthropic(modelId);
45
99
  }
46
100
  case "openai": {
47
- const apiKey = process.env.OPENAI_API_KEY;
101
+ const apiKey = process.env.OPENAI_API_KEY?.trim();
48
102
  if (!apiKey) {
49
- throw new Error(
50
- "Missing API key for OpenAI. Set OPENAI_API_KEY environment variable."
51
- );
103
+ throw new Error(missingProviderCredentialsMessage(provider));
52
104
  }
105
+ const { createOpenAI } = await import("@ai-sdk/openai");
53
106
  const openai = createOpenAI({ apiKey });
54
107
  return openai(modelId);
55
108
  }
@@ -60,7 +113,11 @@ function convertUserContentParts(parts) {
60
113
  if (part.type === "text") {
61
114
  return { type: "text", text: part.text };
62
115
  }
63
- return { type: "image", image: part.image };
116
+ return {
117
+ type: "image",
118
+ image: part.image,
119
+ ...part.mediaType ? { mediaType: part.mediaType } : {}
120
+ };
64
121
  });
65
122
  }
66
123
  function convertAssistantContentParts(parts) {
@@ -88,9 +145,14 @@ function convertMessages(messages) {
88
145
  }
89
146
  function createLLMClient(model) {
90
147
  const { provider, modelId } = parseModel(model);
91
- const aiModel = getProviderModel(provider, modelId);
148
+ let modelPromise = null;
149
+ const getModel = () => {
150
+ modelPromise ??= getProviderModel(provider, modelId);
151
+ return modelPromise;
152
+ };
92
153
  return {
93
154
  async generateObject(opts) {
155
+ const aiModel = await getModel();
94
156
  const result = await generateObject({
95
157
  model: aiModel,
96
158
  prompt: opts.prompt,
@@ -100,6 +162,7 @@ function createLLMClient(model) {
100
162
  return result.object;
101
163
  },
102
164
  async generateObjectFromMessages(opts) {
165
+ const aiModel = await getModel();
103
166
  const result = await generateObject({
104
167
  model: aiModel,
105
168
  messages: convertMessages(opts.messages),
@@ -111,5 +174,8 @@ function createLLMClient(model) {
111
174
  };
112
175
  }
113
176
  export {
114
- createLLMClient
177
+ createLLMClient,
178
+ hasProviderCredentials,
179
+ missingProviderCredentialsMessage,
180
+ parseModel
115
181
  };
@@ -5,7 +5,8 @@ type MessageContentPart = {
5
5
  text: string;
6
6
  } | {
7
7
  type: "image";
8
- image: string;
8
+ image: string | Uint8Array;
9
+ mediaType?: string;
9
10
  };
10
11
  type Message = {
11
12
  role: "user" | "assistant";
@@ -5,7 +5,8 @@ type MessageContentPart = {
5
5
  text: string;
6
6
  } | {
7
7
  type: "image";
8
- image: string;
8
+ image: string | Uint8Array;
9
+ mediaType?: string;
9
10
  };
10
11
  type Message = {
11
12
  role: "user" | "assistant";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libretto",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "AI-powered browser automation library and CLI built on Playwright",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "files": [
16
16
  "dist",
17
- ".agents/skills/libretto"
17
+ "skills/libretto"
18
18
  ],
19
19
  "bin": {
20
20
  "libretto": "./dist/cli/index.js",
@@ -42,6 +42,7 @@
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@ai-sdk/anthropic": "^3.0.58",
45
+ "@ai-sdk/google": "^3.0.51",
45
46
  "@ai-sdk/google-vertex": "^4.0.80",
46
47
  "@ai-sdk/openai": "^3.0.41"
47
48
  },
@@ -49,6 +50,9 @@
49
50
  "@ai-sdk/anthropic": {
50
51
  "optional": true
51
52
  },
53
+ "@ai-sdk/google": {
54
+ "optional": true
55
+ },
52
56
  "@ai-sdk/google-vertex": {
53
57
  "optional": true
54
58
  },
@@ -59,10 +63,10 @@
59
63
  "devDependencies": {
60
64
  "@anthropic-ai/claude-agent-sdk": "^0.2.75",
61
65
  "@ai-sdk/anthropic": "^3.0.58",
66
+ "@ai-sdk/google": "^3.0.51",
62
67
  "@ai-sdk/google-vertex": "^4.0.80",
63
68
  "@ai-sdk/openai": "^3.0.41",
64
69
  "@types/node": "^25.5.0",
65
- "@types/yargs": "^17.0.35",
66
70
  "openai": "^6.29.0",
67
71
  "tsup": "^8.5.1",
68
72
  "typescript": "^5.9.3",
@@ -72,7 +76,6 @@
72
76
  "ai": "^6.0.116",
73
77
  "playwright": "^1.58.2",
74
78
  "tsx": "^4.21.0",
75
- "yargs": "^18.0.0",
76
79
  "zod": "^4.3.6"
77
80
  }
78
81
  }
File without changes