browserclaw-agent 0.1.0 → 0.2.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/dist/agent-loop.d.ts +23 -15
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +123 -47
- package/dist/agent-loop.js.map +1 -1
- package/dist/config.d.ts +10 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -16
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +7 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -12
- package/dist/index.js.map +1 -1
- package/dist/lesson-store.d.ts +11 -0
- package/dist/lesson-store.d.ts.map +1 -0
- package/dist/lesson-store.js +323 -0
- package/dist/lesson-store.js.map +1 -0
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +40 -26
- package/dist/llm.js.map +1 -1
- package/dist/port-probe.d.ts +8 -0
- package/dist/port-probe.d.ts.map +1 -0
- package/dist/port-probe.js +22 -0
- package/dist/port-probe.js.map +1 -0
- package/dist/prompt-log.d.ts +17 -0
- package/dist/prompt-log.d.ts.map +1 -0
- package/dist/prompt-log.js +28 -0
- package/dist/prompt-log.js.map +1 -0
- package/dist/routes.d.ts +3 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +227 -0
- package/dist/routes.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +121 -0
- package/dist/server.js.map +1 -0
- package/dist/session-manager.d.ts +40 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +635 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/skill-generator.d.ts +5 -0
- package/dist/skill-generator.d.ts.map +1 -0
- package/dist/skill-generator.js +173 -0
- package/dist/skill-generator.js.map +1 -0
- package/dist/skill-store.d.ts +7 -0
- package/dist/skill-store.d.ts.map +1 -0
- package/dist/skill-store.js +85 -0
- package/dist/skill-store.js.map +1 -0
- package/dist/skills/extract-items.d.ts +9 -0
- package/dist/skills/extract-items.d.ts.map +1 -0
- package/dist/skills/extract-items.js +131 -0
- package/dist/skills/extract-items.js.map +1 -0
- package/dist/skills/loop-detection.d.ts.map +1 -1
- package/dist/skills/loop-detection.js +19 -2
- package/dist/skills/loop-detection.js.map +1 -1
- package/dist/skills/press-and-hold.d.ts.map +1 -1
- package/dist/skills/press-and-hold.js +2 -11
- package/dist/skills/press-and-hold.js.map +1 -1
- package/dist/trajectory-store.d.ts +20 -0
- package/dist/trajectory-store.d.ts.map +1 -0
- package/dist/trajectory-store.js +61 -0
- package/dist/trajectory-store.js.map +1 -0
- package/package.json +7 -15
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AgentLoopResult, SkillOutput } from './types.js';
|
|
2
|
+
export declare function generateSkillTags(prompt: string, skill: SkillOutput): Promise<string[]>;
|
|
3
|
+
export declare function generateSkill(prompt: string, result: AgentLoopResult): Promise<SkillOutput>;
|
|
4
|
+
export declare function mergeSkills(existing: SkillOutput, prompt: string, result: AgentLoopResult): Promise<SkillOutput>;
|
|
5
|
+
//# sourceMappingURL=skill-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-generator.d.ts","sourceRoot":"","sources":["../src/skill-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAa,MAAM,YAAY,CAAC;AAM1E,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW7F;AAoHD,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAgDjG;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,WAAW,CAAC,CA2CtB"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { llmJson } from './llm.js';
|
|
2
|
+
export async function generateSkillTags(prompt, skill) {
|
|
3
|
+
try {
|
|
4
|
+
const result = await llmJson({
|
|
5
|
+
system: `Generate 3-5 short tags for a browser automation skill. Tags should describe the type of task (e.g. "search", "booking", "form-fill", "navigation", "price-check"). Respond with JSON: {"tags": ["tag1", "tag2", ...]}`,
|
|
6
|
+
message: `Prompt: ${prompt}\nSkill: ${skill.title} — ${skill.description}`,
|
|
7
|
+
maxTokens: 128,
|
|
8
|
+
});
|
|
9
|
+
return result.tags;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const SYSTEM_PROMPT = `You are a skill documentation generator. Given a browser automation task and the actions that were taken (including failures), generate a clean, structured skill document with reflective analysis.
|
|
16
|
+
|
|
17
|
+
You MUST respond with valid JSON matching this schema:
|
|
18
|
+
{
|
|
19
|
+
"title": "short descriptive title",
|
|
20
|
+
"description": "one-sentence description of what this skill does",
|
|
21
|
+
"steps": [
|
|
22
|
+
{
|
|
23
|
+
"number": 1,
|
|
24
|
+
"description": "what this step does in plain language",
|
|
25
|
+
"action": "click | type | navigate | select | scroll | wait",
|
|
26
|
+
"details": "specific details like what was clicked or typed (optional)"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"tips": [
|
|
30
|
+
"practical tips about this site that would save time on the next visit"
|
|
31
|
+
],
|
|
32
|
+
"what_worked": [
|
|
33
|
+
"patterns or approaches that succeeded and should be repeated"
|
|
34
|
+
],
|
|
35
|
+
"what_to_avoid": [
|
|
36
|
+
"patterns or approaches that failed or wasted steps — so future runs skip them"
|
|
37
|
+
],
|
|
38
|
+
"site_quirks": [
|
|
39
|
+
"site-specific behaviors discovered: slow loading, aggressive popups, unusual navigation, anti-bot, etc."
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Rules:
|
|
44
|
+
- Title should be concise (under 60 chars).
|
|
45
|
+
- Collapse redundant or failed steps — only include the successful logical steps.
|
|
46
|
+
- Description should be one sentence explaining the end-to-end task.
|
|
47
|
+
- Steps should be human-readable — use natural language, not technical refs.
|
|
48
|
+
- Omit intermediate waits and scrolls unless they're meaningful to the workflow.
|
|
49
|
+
- Tips should capture site-specific knowledge: cookie banners, autocomplete behavior, loading delays, popup dismissals, anti-bot challenges, hidden buttons, required wait times, URL patterns.
|
|
50
|
+
- what_worked: analyze the successful patterns — what approach worked? What shortcuts did the agent find?
|
|
51
|
+
- what_to_avoid: analyze the failures — what approaches failed? What dead ends should future runs skip?
|
|
52
|
+
- site_quirks: what unusual behaviors did this site exhibit? Overlays, slow loads, redirects, anti-bot?`;
|
|
53
|
+
function buildPrompt(userPrompt, result) {
|
|
54
|
+
let message = `Original task: ${userPrompt}\n\n`;
|
|
55
|
+
message += `Final URL: ${result.final_url ?? 'unknown'}\n`;
|
|
56
|
+
message += `Total steps executed: ${String(result.steps.length)}\n`;
|
|
57
|
+
message += `Duration: ${String(result.duration_ms)}ms\n\n`;
|
|
58
|
+
message += "Action history (including failures — analyze what worked and what didn't):\n";
|
|
59
|
+
for (const step of result.steps) {
|
|
60
|
+
const action = step.action;
|
|
61
|
+
let detail = `Step ${String(step.step)}: ${action.action} — ${action.reasoning}`;
|
|
62
|
+
if (action.ref !== undefined && action.ref !== '')
|
|
63
|
+
detail += ` (ref: ${action.ref})`;
|
|
64
|
+
if (action.text !== undefined && action.text !== '')
|
|
65
|
+
detail += ` (text: "${action.text}")`;
|
|
66
|
+
if (action.url !== undefined && action.url !== '')
|
|
67
|
+
detail += ` (url: ${action.url})`;
|
|
68
|
+
if (step.page_title !== undefined && step.page_title !== '')
|
|
69
|
+
detail += ` [page: ${step.page_title}]`;
|
|
70
|
+
if (step.outcome !== undefined && step.outcome !== '')
|
|
71
|
+
detail += ` → ${step.outcome}`;
|
|
72
|
+
if (action.error_feedback !== undefined && action.error_feedback !== '')
|
|
73
|
+
detail += ` ⚠ FAILED: ${action.error_feedback}`;
|
|
74
|
+
message += ` ${detail}\n`;
|
|
75
|
+
}
|
|
76
|
+
return message;
|
|
77
|
+
}
|
|
78
|
+
function toMarkdown(title, description, steps, tips, whatWorked, prompt, url, durationMs) {
|
|
79
|
+
const lines = [`# ${title}`, '', description, '', '## Steps', ''];
|
|
80
|
+
for (const step of steps) {
|
|
81
|
+
lines.push(`${String(step.number)}. **${step.description}**`);
|
|
82
|
+
if (step.details !== undefined && step.details !== '')
|
|
83
|
+
lines.push(` ${step.details}`);
|
|
84
|
+
}
|
|
85
|
+
if (tips.length > 0) {
|
|
86
|
+
lines.push('', '## Tips', '');
|
|
87
|
+
for (const tip of tips) {
|
|
88
|
+
lines.push(`- ${tip}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (whatWorked.length > 0) {
|
|
92
|
+
lines.push('', '## What Worked', '');
|
|
93
|
+
for (const w of whatWorked) {
|
|
94
|
+
lines.push(`- ${w}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
lines.push('', '---', '');
|
|
98
|
+
lines.push(`- **Prompt:** ${prompt}`);
|
|
99
|
+
lines.push(`- **Final URL:** ${url}`);
|
|
100
|
+
lines.push(`- **Duration:** ${(durationMs / 1000).toFixed(1)}s`);
|
|
101
|
+
lines.push(`- **Generated:** ${new Date().toISOString()}`);
|
|
102
|
+
lines.push(`- **Engine:** [BrowserClaw](https://github.com/idan-rubin/browserclaw)`);
|
|
103
|
+
lines.push('');
|
|
104
|
+
return lines.join('\n');
|
|
105
|
+
}
|
|
106
|
+
export async function generateSkill(prompt, result) {
|
|
107
|
+
const parsed = await llmJson({
|
|
108
|
+
system: SYSTEM_PROMPT,
|
|
109
|
+
message: buildPrompt(prompt, result),
|
|
110
|
+
maxTokens: 2048,
|
|
111
|
+
});
|
|
112
|
+
// Merge what_to_avoid and site_quirks into tips for a unified guidance section
|
|
113
|
+
const tips = [...(parsed.tips ?? [])];
|
|
114
|
+
if (parsed.what_to_avoid !== undefined) {
|
|
115
|
+
for (const avoid of parsed.what_to_avoid) {
|
|
116
|
+
tips.push(`⚠ Avoid: ${avoid}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (parsed.site_quirks !== undefined) {
|
|
120
|
+
for (const quirk of parsed.site_quirks) {
|
|
121
|
+
tips.push(`🔍 Site quirk: ${quirk}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const whatWorked = parsed.what_worked ?? [];
|
|
125
|
+
const metadata = {
|
|
126
|
+
prompt,
|
|
127
|
+
url: result.final_url ?? '',
|
|
128
|
+
total_steps: result.steps.length,
|
|
129
|
+
duration_ms: result.duration_ms,
|
|
130
|
+
generated_at: new Date().toISOString(),
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
title: parsed.title,
|
|
134
|
+
description: parsed.description,
|
|
135
|
+
steps: parsed.steps,
|
|
136
|
+
tips,
|
|
137
|
+
what_worked: whatWorked,
|
|
138
|
+
metadata,
|
|
139
|
+
markdown: toMarkdown(parsed.title, parsed.description, parsed.steps, tips, whatWorked, prompt, metadata.url, metadata.duration_ms),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export async function mergeSkills(existing, prompt, result) {
|
|
143
|
+
const newSkill = await generateSkill(prompt, result);
|
|
144
|
+
// Merge tips: deduplicate by keeping unique entries from both
|
|
145
|
+
const allTips = [...existing.tips];
|
|
146
|
+
for (const tip of newSkill.tips) {
|
|
147
|
+
if (!allTips.some((t) => t.toLowerCase().includes(tip.toLowerCase().slice(0, 30)))) {
|
|
148
|
+
allTips.push(tip);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Merge what_worked
|
|
152
|
+
const allWorked = [...(existing.what_worked ?? [])];
|
|
153
|
+
for (const w of newSkill.what_worked ?? []) {
|
|
154
|
+
if (!allWorked.some((aw) => aw.toLowerCase().includes(w.toLowerCase().slice(0, 30)))) {
|
|
155
|
+
allWorked.push(w);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Always keep the shorter (more efficient) steps. mergeSkills is called when
|
|
159
|
+
// the new run took more steps, so existing steps are usually shorter — but
|
|
160
|
+
// we check explicitly in case the new run found a shorter path.
|
|
161
|
+
const steps = newSkill.steps.length < existing.steps.length ? newSkill.steps : existing.steps;
|
|
162
|
+
return {
|
|
163
|
+
title: existing.title,
|
|
164
|
+
description: existing.description,
|
|
165
|
+
steps,
|
|
166
|
+
tips: allTips,
|
|
167
|
+
what_worked: allWorked,
|
|
168
|
+
failure_notes: existing.failure_notes,
|
|
169
|
+
metadata: newSkill.metadata,
|
|
170
|
+
markdown: toMarkdown(existing.title, existing.description, steps, allTips, allWorked, prompt, newSkill.metadata.url, newSkill.metadata.duration_ms),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=skill-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-generator.js","sourceRoot":"","sources":["../src/skill-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CatalogSkill, SkillOutput } from './types.js';
|
|
2
|
+
export declare function initSkillStore(): Promise<void>;
|
|
3
|
+
export declare function extractDomain(url: string): string;
|
|
4
|
+
export declare function getSkillForDomain(domain: string): Promise<CatalogSkill | null>;
|
|
5
|
+
export declare function getSkillsForDomains(domains: string[]): Promise<Map<string, CatalogSkill>>;
|
|
6
|
+
export declare function saveSkill(domain: string, skill: SkillOutput, tags: string[], runCount?: number): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=skill-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-store.d.ts","sourceRoot":"","sources":["../src/skill-store.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM5D,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBpD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAWpF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAY/F;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBpH"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { S3Client, CreateBucketCommand, HeadBucketCommand, GetObjectCommand, PutObjectCommand, } from '@aws-sdk/client-s3';
|
|
2
|
+
import { getMinioConfig } from './config.js';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
let s3;
|
|
5
|
+
let bucket;
|
|
6
|
+
export async function initSkillStore() {
|
|
7
|
+
const config = getMinioConfig();
|
|
8
|
+
bucket = config.bucket;
|
|
9
|
+
s3 = new S3Client({
|
|
10
|
+
endpoint: config.endpoint,
|
|
11
|
+
region: 'us-east-1',
|
|
12
|
+
credentials: {
|
|
13
|
+
accessKeyId: config.accessKey,
|
|
14
|
+
secretAccessKey: config.secretKey,
|
|
15
|
+
},
|
|
16
|
+
forcePathStyle: true,
|
|
17
|
+
});
|
|
18
|
+
try {
|
|
19
|
+
await s3.send(new HeadBucketCommand({ Bucket: bucket }));
|
|
20
|
+
logger.info({ bucket }, 'Skill store ready');
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
await s3.send(new CreateBucketCommand({ Bucket: bucket }));
|
|
24
|
+
logger.info({ bucket }, 'Created skill store bucket');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function extractDomain(url) {
|
|
28
|
+
try {
|
|
29
|
+
const hostname = new URL(url).hostname;
|
|
30
|
+
return hostname.replace(/^www\./, '');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function getSkillForDomain(domain) {
|
|
37
|
+
if (domain === '')
|
|
38
|
+
return null;
|
|
39
|
+
try {
|
|
40
|
+
const res = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: `skills/${domain}.json` }));
|
|
41
|
+
const raw = await res.Body?.transformToString();
|
|
42
|
+
if (raw === undefined || raw === '')
|
|
43
|
+
return null;
|
|
44
|
+
return JSON.parse(raw);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function getSkillsForDomains(domains) {
|
|
51
|
+
const results = new Map();
|
|
52
|
+
const fetches = domains.filter(Boolean).map(async (domain) => {
|
|
53
|
+
try {
|
|
54
|
+
const skill = await getSkillForDomain(domain);
|
|
55
|
+
if (skill)
|
|
56
|
+
results.set(domain, skill);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// skip domains that fail to fetch
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
await Promise.all(fetches);
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
65
|
+
export async function saveSkill(domain, skill, tags, runCount) {
|
|
66
|
+
if (domain === '')
|
|
67
|
+
return;
|
|
68
|
+
const now = new Date().toISOString();
|
|
69
|
+
const catalogSkill = {
|
|
70
|
+
id: crypto.randomUUID(),
|
|
71
|
+
domain,
|
|
72
|
+
skill,
|
|
73
|
+
tags,
|
|
74
|
+
created_at: now,
|
|
75
|
+
run_count: runCount ?? 1,
|
|
76
|
+
};
|
|
77
|
+
await s3.send(new PutObjectCommand({
|
|
78
|
+
Bucket: bucket,
|
|
79
|
+
Key: `skills/${domain}.json`,
|
|
80
|
+
Body: JSON.stringify(catalogSkill, null, 2),
|
|
81
|
+
ContentType: 'application/json',
|
|
82
|
+
}));
|
|
83
|
+
logger.info({ title: skill.title, domain, runCount: catalogSkill.run_count }, 'Saved skill');
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=skill-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-store.js","sourceRoot":"","sources":["../src/skill-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CrawlPage } from 'browserclaw';
|
|
2
|
+
export interface ExtractItemsResult {
|
|
3
|
+
source: 'next-data' | 'apollo' | 'initial-state' | 'json-ld' | 'dom' | 'none';
|
|
4
|
+
count: number;
|
|
5
|
+
records: Record<string, unknown>[];
|
|
6
|
+
truncated: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function extractItems(page: CrawlPage): Promise<ExtractItemsResult>;
|
|
9
|
+
//# sourceMappingURL=extract-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-items.d.ts","sourceRoot":"","sources":["../../src/skills/extract-items.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;CACpB;AA+GD,wBAAsB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoB/E"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { logger } from '../logger.js';
|
|
2
|
+
const MAX_RECORDS = 20;
|
|
3
|
+
const EXTRACTION_FN = `
|
|
4
|
+
(function() {
|
|
5
|
+
const MAX = 20;
|
|
6
|
+
const seen = new Set();
|
|
7
|
+
|
|
8
|
+
function isItemLike(obj) {
|
|
9
|
+
if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) return false;
|
|
10
|
+
const keys = Object.keys(obj);
|
|
11
|
+
if (keys.length < 2) return false;
|
|
12
|
+
const txt = keys.join(',').toLowerCase();
|
|
13
|
+
const hints = ['price', 'rent', 'amount', 'address', 'street', 'location', 'bedroom', 'bed', 'url', 'link', 'href', 'title', 'name'];
|
|
14
|
+
let hits = 0;
|
|
15
|
+
for (const h of hints) if (txt.includes(h)) hits++;
|
|
16
|
+
return hits >= 2;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function harvestFromJson(node, out, depth) {
|
|
20
|
+
if (out.length >= MAX) return;
|
|
21
|
+
if (depth > 10) return;
|
|
22
|
+
if (Array.isArray(node)) {
|
|
23
|
+
for (const item of node) harvestFromJson(item, out, depth + 1);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (node === null || typeof node !== 'object') return;
|
|
27
|
+
if (isItemLike(node)) {
|
|
28
|
+
const key = JSON.stringify(Object.keys(node).sort());
|
|
29
|
+
if (!seen.has(key) || out.length < MAX) {
|
|
30
|
+
out.push(node);
|
|
31
|
+
seen.add(key);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (const v of Object.values(node)) harvestFromJson(v, out, depth + 1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function tryNextData() {
|
|
38
|
+
const el = document.getElementById('__NEXT_DATA__');
|
|
39
|
+
if (!el || !el.textContent) return null;
|
|
40
|
+
try {
|
|
41
|
+
const data = JSON.parse(el.textContent);
|
|
42
|
+
const out = [];
|
|
43
|
+
harvestFromJson(data, out, 0);
|
|
44
|
+
return out.length > 0 ? out : null;
|
|
45
|
+
} catch (e) { return null; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function tryGlobalState(name) {
|
|
49
|
+
const v = window[name];
|
|
50
|
+
if (!v || typeof v !== 'object') return null;
|
|
51
|
+
const out = [];
|
|
52
|
+
harvestFromJson(v, out, 0);
|
|
53
|
+
return out.length > 0 ? out : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function tryJsonLd() {
|
|
57
|
+
const scripts = document.querySelectorAll('script[type="application/ld+json"]');
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const s of scripts) {
|
|
60
|
+
try {
|
|
61
|
+
const data = JSON.parse(s.textContent || '');
|
|
62
|
+
harvestFromJson(data, out, 0);
|
|
63
|
+
if (out.length >= MAX) break;
|
|
64
|
+
} catch (e) {}
|
|
65
|
+
}
|
|
66
|
+
return out.length > 0 ? out : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function tryDom() {
|
|
70
|
+
const candidates = document.querySelectorAll('article, li, [class*="card" i], [class*="listing" i], [class*="item" i], [data-testid*="card" i], [data-testid*="listing" i]');
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const el of candidates) {
|
|
73
|
+
if (out.length >= MAX) break;
|
|
74
|
+
const text = (el.innerText || '').trim();
|
|
75
|
+
if (text.length < 20 || text.length > 800) continue;
|
|
76
|
+
const link = el.querySelector('a[href]');
|
|
77
|
+
const href = link ? link.href : null;
|
|
78
|
+
if (!href) continue;
|
|
79
|
+
const rec = { text: text.slice(0, 400), url: href };
|
|
80
|
+
const priceMatch = text.match(/\\$[\\d,]+(?:\\/(?:mo|month))?/i);
|
|
81
|
+
if (priceMatch) rec.price = priceMatch[0];
|
|
82
|
+
const bedMatch = text.match(/(\\d+)\\s*(?:bed|br|bedroom)/i);
|
|
83
|
+
if (bedMatch) rec.bedrooms = bedMatch[1];
|
|
84
|
+
const studioMatch = /\\bstudio\\b/i.test(text);
|
|
85
|
+
if (studioMatch && !rec.bedrooms) rec.bedrooms = 'studio';
|
|
86
|
+
out.push(rec);
|
|
87
|
+
}
|
|
88
|
+
return out.length > 0 ? out : null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const results = tryNextData();
|
|
92
|
+
if (results) return { source: 'next-data', records: results.slice(0, MAX) };
|
|
93
|
+
|
|
94
|
+
const apollo = tryGlobalState('__APOLLO_STATE__');
|
|
95
|
+
if (apollo) return { source: 'apollo', records: apollo.slice(0, MAX) };
|
|
96
|
+
|
|
97
|
+
const initial = tryGlobalState('__INITIAL_STATE__');
|
|
98
|
+
if (initial) return { source: 'initial-state', records: initial.slice(0, MAX) };
|
|
99
|
+
|
|
100
|
+
const ld = tryJsonLd();
|
|
101
|
+
if (ld) return { source: 'json-ld', records: ld.slice(0, MAX) };
|
|
102
|
+
|
|
103
|
+
const dom = tryDom();
|
|
104
|
+
if (dom) return { source: 'dom', records: dom.slice(0, MAX) };
|
|
105
|
+
|
|
106
|
+
return { source: 'none', records: [] };
|
|
107
|
+
})()
|
|
108
|
+
`;
|
|
109
|
+
export async function extractItems(page) {
|
|
110
|
+
try {
|
|
111
|
+
const raw = await page.evaluate(EXTRACTION_FN);
|
|
112
|
+
if (raw === null || typeof raw !== 'object') {
|
|
113
|
+
return { source: 'none', count: 0, records: [], truncated: false };
|
|
114
|
+
}
|
|
115
|
+
const result = raw;
|
|
116
|
+
const records = Array.isArray(result.records) ? result.records : [];
|
|
117
|
+
const source = result.source;
|
|
118
|
+
logger.info({ source, count: records.length }, 'extract-items');
|
|
119
|
+
return {
|
|
120
|
+
source,
|
|
121
|
+
count: records.length,
|
|
122
|
+
records: records.slice(0, MAX_RECORDS),
|
|
123
|
+
truncated: records.length > MAX_RECORDS,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
logger.warn({ err: err instanceof Error ? err.message : err }, 'extract-items failed');
|
|
128
|
+
return { source: 'none', count: 0, records: [], truncated: false };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=extract-items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-items.js","sourceRoot":"","sources":["../../src/skills/extract-items.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAStC,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyGrB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAe;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC/C,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACrE,CAAC;QACD,MAAM,MAAM,GAAG,GAA2C,CAAC;QAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,OAAqC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAsC,CAAC;QAC7D,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;QAChE,OAAO;YACL,MAAM;YACN,KAAK,EAAE,OAAO,CAAC,MAAM;YACrB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC;YACtC,SAAS,EAAE,OAAO,CAAC,MAAM,GAAG,WAAW;SACxC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,CAAC;QACvF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-detection.d.ts","sourceRoot":"","sources":["../../src/skills/loop-detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,UAAU,SAAS;IACjB,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"loop-detection.d.ts","sourceRoot":"","sources":["../../src/skills/loop-detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,UAAU,SAAS;IACjB,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,GAAG,IAAI,CA4D3G"}
|
|
@@ -5,12 +5,29 @@ const LOOP_WINDOW = 20;
|
|
|
5
5
|
* by recovery.ts which provides richer diagnostics.
|
|
6
6
|
*/
|
|
7
7
|
export function detectLoop(action, history) {
|
|
8
|
+
const actionKey = `${action.action}:${action.ref ?? ''}`;
|
|
9
|
+
if (action.action === 'web_search' &&
|
|
10
|
+
history.length >= 1 &&
|
|
11
|
+
history[history.length - 1].action.action === 'web_search') {
|
|
12
|
+
return {
|
|
13
|
+
level: 'urgent',
|
|
14
|
+
message: 'You just ran web_search twice in a row. The previous search already returned results — pick one URL from those results and navigate there. Do not refine the query again. If every returned site turned out to be blocked, navigate directly to a known working alternative (e.g. Zumper, Apartments.com) instead of searching yet again.',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (action.ref !== undefined && action.ref !== '' && history.length >= 2) {
|
|
18
|
+
const lastTwo = history.slice(-2);
|
|
19
|
+
const bothFailed = lastTwo.every((h) => h.action.error_feedback !== undefined && `${h.action.action}:${h.action.ref ?? ''}` === actionKey);
|
|
20
|
+
if (bothFailed) {
|
|
21
|
+
return {
|
|
22
|
+
level: 'urgent',
|
|
23
|
+
message: `STOP: ref "${action.ref}" just failed twice on "${action.action}". Do NOT try this ref again — it is blocked or not actionable. Pick a DIFFERENT ref from the current snapshot, or press "Escape" to dismiss any overlay before retrying. If neither helps, try a different element or navigate to a different page.`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
if (history.length < 5)
|
|
9
28
|
return null;
|
|
10
|
-
const actionKey = `${action.action}:${action.ref ?? ''}`;
|
|
11
29
|
const window = history.slice(-LOOP_WINDOW);
|
|
12
30
|
const windowKeys = window.map((h) => `${h.action.action}:${h.action.ref ?? ''}`);
|
|
13
|
-
// Count how many times this exact action appears in the window
|
|
14
31
|
const repetitions = windowKeys.filter((k) => k === actionKey).length;
|
|
15
32
|
if (repetitions >= 12) {
|
|
16
33
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-detection.js","sourceRoot":"","sources":["../../src/skills/loop-detection.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,EAAE,CAAC;AAOvB;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAwC,EAAE,OAAoB;IACvF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"loop-detection.js","sourceRoot":"","sources":["../../src/skills/loop-detection.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,EAAE,CAAC;AAOvB;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAwC,EAAE,OAAoB;IACvF,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC;IAEzD,IACE,MAAM,CAAC,MAAM,KAAK,YAAY;QAC9B,OAAO,CAAC,MAAM,IAAI,CAAC;QACnB,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,YAAY,EAC1D,CAAC;QACD,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,OAAO,EACL,2UAA2U;SAC9U,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,IAAI,MAAM,CAAC,GAAG,KAAK,EAAE,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACzE,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,KAAK,SAAS,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,KAAK,SAAS,CACzG,CAAC;QACF,IAAI,UAAU,EAAE,CAAC;YACf,OAAO;gBACL,KAAK,EAAE,QAAQ;gBACf,OAAO,EAAE,cAAc,MAAM,CAAC,GAAG,2BAA2B,MAAM,CAAC,MAAM,sPAAsP;aAChU,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;IAEjF,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAErE,IAAI,WAAW,IAAI,EAAE,EAAE,CAAC;QACtB,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,OAAO,EACL,2TAA2T;SAC9T,CAAC;IACJ,CAAC;IAED,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,OAAO,EACL,qPAAqP;SACxP,CAAC;IACJ,CAAC;IAED,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,OAAO,EACL,6QAA6Q;SAChR,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"press-and-hold.d.ts","sourceRoot":"","sources":["../../src/skills/press-and-hold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,qBAAqB,GAAG,IAAI,CAAC;AAoH1E,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAelE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAOzF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAkB1D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAc3F;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"press-and-hold.d.ts","sourceRoot":"","sources":["../../src/skills/press-and-hold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,qBAAqB,GAAG,IAAI,CAAC;AAoH1E,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAelE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAOzF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAkB1D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAc3F;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CA4BhG"}
|
|
@@ -154,7 +154,6 @@ export async function pressAndHold(page, opts) {
|
|
|
154
154
|
}
|
|
155
155
|
const { x, y } = coords;
|
|
156
156
|
logger.info({ x, y }, 'press-and-hold: found button');
|
|
157
|
-
const urlBefore = await page.url();
|
|
158
157
|
const jitterX = x + Math.floor(Math.random() * 20) - 10;
|
|
159
158
|
const jitterY = y + Math.floor(Math.random() * 10) - 5;
|
|
160
159
|
const holdMs = opts?.holdMs ?? humanHoldMs();
|
|
@@ -164,16 +163,8 @@ export async function pressAndHold(page, opts) {
|
|
|
164
163
|
logger.info({ holdMs }, 'press-and-hold: released');
|
|
165
164
|
await page.waitFor({ timeMs: 2000 });
|
|
166
165
|
const stillBlocked = await isStillBlocked(page, 'press_and_hold');
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
await page.goto(urlBefore);
|
|
170
|
-
await page.waitFor({ timeMs: 3000 });
|
|
171
|
-
const blockedAfterRefresh = await isStillBlocked(page, 'press_and_hold');
|
|
172
|
-
logger.info({ blockedAfterRefresh }, 'press-and-hold: result after refresh');
|
|
173
|
-
return !blockedAfterRefresh;
|
|
174
|
-
}
|
|
175
|
-
logger.info('press-and-hold: resolved');
|
|
176
|
-
return true;
|
|
166
|
+
logger.info({ stillBlocked }, 'press-and-hold: resolved');
|
|
167
|
+
return !stillBlocked;
|
|
177
168
|
}
|
|
178
169
|
catch (err) {
|
|
179
170
|
logger.error({ err: err instanceof Error ? err.message : err }, 'press-and-hold: failed');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"press-and-hold.js","sourceRoot":"","sources":["../../src/skills/press-and-hold.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"press-and-hold.js","sourceRoot":"","sources":["../../src/skills/press-and-hold.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentStep } from './types.js';
|
|
2
|
+
export declare const TRAJECTORY_STATUS: {
|
|
3
|
+
readonly completed: "completed";
|
|
4
|
+
readonly failed: "failed";
|
|
5
|
+
};
|
|
6
|
+
export type TrajectoryStatus = (typeof TRAJECTORY_STATUS)[keyof typeof TRAJECTORY_STATUS];
|
|
7
|
+
export interface TrajectoryRecord {
|
|
8
|
+
session_id: string;
|
|
9
|
+
prompt: string;
|
|
10
|
+
status: TrajectoryStatus;
|
|
11
|
+
steps: AgentStep[];
|
|
12
|
+
answer?: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
duration_ms: number;
|
|
15
|
+
saved_at: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function initTrajectoryStore(): void;
|
|
18
|
+
export declare function saveTrajectory(record: TrajectoryRecord): Promise<void>;
|
|
19
|
+
export declare function loadTrajectory(sessionId: string): Promise<TrajectoryRecord | null>;
|
|
20
|
+
//# sourceMappingURL=trajectory-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trajectory-store.d.ts","sourceRoot":"","sources":["../src/trajectory-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AACX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAE1F,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAgB,mBAAmB,IAAI,IAAI,CAU1C;AAYD,wBAAsB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB5E;AAED,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAWxF"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { S3Client, GetObjectCommand, PutObjectCommand, NoSuchKey } from '@aws-sdk/client-s3';
|
|
2
|
+
import { getMinioConfig } from './config.js';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
const KEY_PREFIX = 'trajectories/';
|
|
5
|
+
const CONTENT_TYPE = 'application/json';
|
|
6
|
+
export const TRAJECTORY_STATUS = {
|
|
7
|
+
completed: 'completed',
|
|
8
|
+
failed: 'failed',
|
|
9
|
+
};
|
|
10
|
+
let s3;
|
|
11
|
+
let bucket;
|
|
12
|
+
export function initTrajectoryStore() {
|
|
13
|
+
const config = getMinioConfig();
|
|
14
|
+
bucket = config.bucket;
|
|
15
|
+
s3 = new S3Client({
|
|
16
|
+
endpoint: config.endpoint,
|
|
17
|
+
region: 'us-east-1',
|
|
18
|
+
credentials: { accessKeyId: config.accessKey, secretAccessKey: config.secretKey },
|
|
19
|
+
forcePathStyle: true,
|
|
20
|
+
});
|
|
21
|
+
logger.info('Trajectory store ready');
|
|
22
|
+
}
|
|
23
|
+
function keyFor(sessionId) {
|
|
24
|
+
return `${KEY_PREFIX}${sessionId}.json`;
|
|
25
|
+
}
|
|
26
|
+
async function readBody(body) {
|
|
27
|
+
const chunks = [];
|
|
28
|
+
for await (const chunk of body)
|
|
29
|
+
chunks.push(Buffer.from(chunk));
|
|
30
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
31
|
+
}
|
|
32
|
+
export async function saveTrajectory(record) {
|
|
33
|
+
try {
|
|
34
|
+
await s3.send(new PutObjectCommand({
|
|
35
|
+
Bucket: bucket,
|
|
36
|
+
Key: keyFor(record.session_id),
|
|
37
|
+
Body: JSON.stringify(record),
|
|
38
|
+
ContentType: CONTENT_TYPE,
|
|
39
|
+
}));
|
|
40
|
+
logger.info({ sessionId: record.session_id, steps: record.steps.length, status: record.status }, 'Saved trajectory');
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
logger.warn({ err, sessionId: record.session_id }, 'Failed to save trajectory');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function loadTrajectory(sessionId) {
|
|
47
|
+
try {
|
|
48
|
+
const res = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: keyFor(sessionId) }));
|
|
49
|
+
if (res.Body === undefined)
|
|
50
|
+
return null;
|
|
51
|
+
const body = await readBody(res.Body);
|
|
52
|
+
return JSON.parse(body);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (err instanceof NoSuchKey)
|
|
56
|
+
return null;
|
|
57
|
+
logger.warn({ err, sessionId }, 'Failed to load trajectory');
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=trajectory-store.js.map
|