opensteer 0.1.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/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/bin/opensteer.mjs +388 -0
- package/dist/chunk-3H5RRIMZ.js +69 -0
- package/dist/chunk-6L24FEKD.js +9233 -0
- package/dist/chunk-DQIHOUXH.js +61 -0
- package/dist/chunk-L4FHT64T.js +109 -0
- package/dist/chunk-SRJLH34D.js +56 -0
- package/dist/cli/server.cjs +9921 -0
- package/dist/cli/server.d.cts +2 -0
- package/dist/cli/server.d.ts +2 -0
- package/dist/cli/server.js +433 -0
- package/dist/extractor-K5VU7HVC.js +8 -0
- package/dist/index.cjs +9659 -0
- package/dist/index.d.cts +718 -0
- package/dist/index.d.ts +718 -0
- package/dist/index.js +153 -0
- package/dist/resolver-WGFFHW4N.js +7 -0
- package/package.json +82 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
flattenExtractionDataToFieldPlan
|
|
3
|
+
} from "./chunk-3H5RRIMZ.js";
|
|
4
|
+
import {
|
|
5
|
+
buildExtractSystemPrompt,
|
|
6
|
+
buildExtractUserPrompt,
|
|
7
|
+
getModelProvider
|
|
8
|
+
} from "./chunk-L4FHT64T.js";
|
|
9
|
+
|
|
10
|
+
// src/ai/extractor.ts
|
|
11
|
+
function createExtractCallback(model, options) {
|
|
12
|
+
const temperature = options?.temperature ?? 1;
|
|
13
|
+
const maxTokens = options?.maxTokens ?? null;
|
|
14
|
+
return async (args) => {
|
|
15
|
+
let generateText;
|
|
16
|
+
try {
|
|
17
|
+
const aiMod = await import("ai");
|
|
18
|
+
generateText = aiMod.generateText;
|
|
19
|
+
} catch {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`To use AI extraction with model '${model}', install 'ai' with your package manager.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const modelProvider = await getModelProvider(model);
|
|
25
|
+
const request = {
|
|
26
|
+
model: modelProvider,
|
|
27
|
+
system: buildExtractSystemPrompt(),
|
|
28
|
+
prompt: buildExtractUserPrompt(args),
|
|
29
|
+
temperature,
|
|
30
|
+
...maxTokens == null ? {} : { maxOutputTokens: maxTokens }
|
|
31
|
+
};
|
|
32
|
+
const result = await generateText(request);
|
|
33
|
+
const parsed = parseExtractResponse(result.text);
|
|
34
|
+
if (!parsed.contains_data) {
|
|
35
|
+
return { fields: {} };
|
|
36
|
+
}
|
|
37
|
+
const fields = flattenExtractionDataToFieldPlan(parsed.data);
|
|
38
|
+
return { fields };
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function parseExtractResponse(text) {
|
|
42
|
+
const trimmed = text.trim();
|
|
43
|
+
const jsonStr = trimmed.startsWith("```") ? stripCodeFence(trimmed) : trimmed;
|
|
44
|
+
const parsed = JSON.parse(jsonStr);
|
|
45
|
+
return {
|
|
46
|
+
contains_data: Boolean(parsed.contains_data),
|
|
47
|
+
data: parsed.data ?? {}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function stripCodeFence(input) {
|
|
51
|
+
const firstBreak = input.indexOf("\n");
|
|
52
|
+
if (firstBreak === -1) return input.replace(/```/g, "").trim();
|
|
53
|
+
const body = input.slice(firstBreak + 1);
|
|
54
|
+
const lastFence = body.lastIndexOf("```");
|
|
55
|
+
if (lastFence === -1) return body.trim();
|
|
56
|
+
return body.slice(0, lastFence).trim();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
createExtractCallback
|
|
61
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/ai/model.ts
|
|
2
|
+
var PROVIDER_MAP = {
|
|
3
|
+
"gpt-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
|
|
4
|
+
"o1-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
|
|
5
|
+
"o3-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
|
|
6
|
+
"o4-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
|
|
7
|
+
"claude-": { pkg: "@ai-sdk/anthropic", providerFn: "anthropic" },
|
|
8
|
+
"gemini-": { pkg: "@ai-sdk/google", providerFn: "google" },
|
|
9
|
+
"grok-": { pkg: "@ai-sdk/xai", providerFn: "xai" },
|
|
10
|
+
"groq/": { pkg: "@ai-sdk/groq", providerFn: "groq" }
|
|
11
|
+
};
|
|
12
|
+
function resolveProviderInfo(modelStr) {
|
|
13
|
+
for (const [prefix, info] of Object.entries(PROVIDER_MAP)) {
|
|
14
|
+
if (modelStr.startsWith(prefix)) {
|
|
15
|
+
return info;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return { pkg: "@ai-sdk/openai", providerFn: "openai" };
|
|
19
|
+
}
|
|
20
|
+
async function getModelProvider(modelStr) {
|
|
21
|
+
const { pkg, providerFn } = resolveProviderInfo(modelStr);
|
|
22
|
+
let mod;
|
|
23
|
+
try {
|
|
24
|
+
mod = await import(pkg);
|
|
25
|
+
} catch {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`To use AI resolution with model '${modelStr}', install 'ai' and '${pkg}' with your package manager.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const provider = mod[providerFn];
|
|
31
|
+
if (typeof provider !== "function") {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Provider '${providerFn}' not found in '${pkg}'. Ensure you have the latest version installed.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const modelId = modelStr.startsWith("groq/") ? modelStr.slice("groq/".length) : modelStr;
|
|
37
|
+
return provider(modelId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/ai/prompts.ts
|
|
41
|
+
function buildResolveSystemPrompt() {
|
|
42
|
+
return `You identify interactive HTML elements in a page snapshot. Each element is annotated with a counter attribute c="N". Your job is to find the element that best matches the user's description for a given action.
|
|
43
|
+
|
|
44
|
+
Respond with a JSON object:
|
|
45
|
+
- "element": the counter number (integer) of the matching element, or -1 if no match
|
|
46
|
+
- "confidence": a number from 0 to 1 indicating how confident you are
|
|
47
|
+
- "reasoning": a brief explanation of why you chose this element
|
|
48
|
+
|
|
49
|
+
Only select elements that are interactive and relevant to the action described.`;
|
|
50
|
+
}
|
|
51
|
+
function buildResolveUserPrompt(args) {
|
|
52
|
+
const parts = [`Action: ${args.action}`, `Description: ${args.description}`];
|
|
53
|
+
if (args.url) {
|
|
54
|
+
parts.push(`URL: ${args.url}`);
|
|
55
|
+
}
|
|
56
|
+
parts.push("", "Page HTML:", args.html);
|
|
57
|
+
return parts.join("\n");
|
|
58
|
+
}
|
|
59
|
+
function buildExtractSystemPrompt() {
|
|
60
|
+
return `You extract structured data from HTML page snapshots. Each element is annotated with a counter attribute c="N".
|
|
61
|
+
|
|
62
|
+
You will receive a schema describing the fields to extract. For each leaf field in the schema, return a value descriptor that identifies both the source element and, when needed, the specific attribute.
|
|
63
|
+
|
|
64
|
+
Respond with ONLY a JSON object (no markdown, no explanation):
|
|
65
|
+
- "contains_data": boolean indicating whether the page contains the requested data
|
|
66
|
+
- "data": an object matching the schema structure where each leaf value is one of:
|
|
67
|
+
- integer counter N -> extract textContent from element c="N"
|
|
68
|
+
- object { "$c": N, "$a": "attributeName" } -> extract that attribute from element c="N"
|
|
69
|
+
- string "CURRENT_URL" -> use the current page URL
|
|
70
|
+
- null -> field not found
|
|
71
|
+
|
|
72
|
+
Rules:
|
|
73
|
+
- Use integer counters for text extraction
|
|
74
|
+
- Use { "$c": N, "$a": "..." } only when the value must come from an attribute (e.g. href, src, value, content)
|
|
75
|
+
- When a field expects a single image URL and both src and srcset exist, prefer "$a": "src" over "$a": "srcset"
|
|
76
|
+
- Use "CURRENT_URL" only when the field explicitly asks for the current page URL
|
|
77
|
+
- If a field's data is not found on the page, return null
|
|
78
|
+
- For array fields, expand to include ALL matching items on the page
|
|
79
|
+
- Do not infer URL/image/input behavior from field names; encode it explicitly with "$a" when needed`;
|
|
80
|
+
}
|
|
81
|
+
function buildExtractUserPrompt(args) {
|
|
82
|
+
const parts = [];
|
|
83
|
+
if (args.description) {
|
|
84
|
+
parts.push(`Description: ${args.description}`);
|
|
85
|
+
}
|
|
86
|
+
if (args.prompt) {
|
|
87
|
+
parts.push(`Instructions: ${args.prompt}`);
|
|
88
|
+
}
|
|
89
|
+
if (args.url) {
|
|
90
|
+
parts.push(`URL: ${args.url}`);
|
|
91
|
+
}
|
|
92
|
+
parts.push(
|
|
93
|
+
"",
|
|
94
|
+
"Schema:",
|
|
95
|
+
JSON.stringify(args.schema, null, 2),
|
|
96
|
+
"",
|
|
97
|
+
"Page HTML:",
|
|
98
|
+
args.html
|
|
99
|
+
);
|
|
100
|
+
return parts.join("\n");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
getModelProvider,
|
|
105
|
+
buildResolveSystemPrompt,
|
|
106
|
+
buildResolveUserPrompt,
|
|
107
|
+
buildExtractSystemPrompt,
|
|
108
|
+
buildExtractUserPrompt
|
|
109
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildResolveSystemPrompt,
|
|
3
|
+
buildResolveUserPrompt,
|
|
4
|
+
getModelProvider
|
|
5
|
+
} from "./chunk-L4FHT64T.js";
|
|
6
|
+
|
|
7
|
+
// src/ai/resolver.ts
|
|
8
|
+
function createResolveCallback(model, options) {
|
|
9
|
+
const temperature = options?.temperature ?? 1;
|
|
10
|
+
const maxTokens = options?.maxTokens ?? null;
|
|
11
|
+
return async (args) => {
|
|
12
|
+
let generateObject;
|
|
13
|
+
let z;
|
|
14
|
+
try {
|
|
15
|
+
const aiMod = await import("ai");
|
|
16
|
+
generateObject = aiMod.generateObject;
|
|
17
|
+
} catch {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`To use AI resolution with model '${model}', install 'ai' with your package manager.`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
z = await import("zod");
|
|
24
|
+
} catch {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`To use AI resolution with model '${model}', install 'zod' with your package manager.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
const modelProvider = await getModelProvider(model);
|
|
30
|
+
const schema = z.object({
|
|
31
|
+
element: z.number().describe(
|
|
32
|
+
"Counter number of the matching element, or -1 if no match"
|
|
33
|
+
),
|
|
34
|
+
confidence: z.number().describe("Confidence score from 0 to 1"),
|
|
35
|
+
reasoning: z.string().describe("Brief explanation of the choice")
|
|
36
|
+
});
|
|
37
|
+
const request = {
|
|
38
|
+
model: modelProvider,
|
|
39
|
+
schema,
|
|
40
|
+
system: buildResolveSystemPrompt(),
|
|
41
|
+
prompt: buildResolveUserPrompt(args),
|
|
42
|
+
temperature,
|
|
43
|
+
...maxTokens == null ? {} : { maxOutputTokens: maxTokens }
|
|
44
|
+
};
|
|
45
|
+
const result = await generateObject(request);
|
|
46
|
+
const { element, confidence } = result.object;
|
|
47
|
+
if (element < 0 || confidence < 0.1) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return element;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
createResolveCallback
|
|
56
|
+
};
|