@nuanu-ai/agentbrowse 0.2.47 → 0.2.49
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 +81 -10
- package/dist/agentpay-gateway.d.ts +9 -0
- package/dist/agentpay-gateway.d.ts.map +1 -1
- package/dist/agentpay-gateway.js +31 -1
- package/dist/agentpay-stagehand-llm.d.ts.map +1 -1
- package/dist/agentpay-stagehand-llm.js +9 -1
- package/dist/command-api-tracing.d.ts +19 -0
- package/dist/command-api-tracing.d.ts.map +1 -0
- package/dist/command-api-tracing.js +137 -0
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +822 -670
- package/dist/commands/act.test-harness.d.ts +6 -0
- package/dist/commands/act.test-harness.d.ts.map +1 -1
- package/dist/commands/act.test-harness.js +44 -1
- package/dist/commands/action-acceptance.d.ts.map +1 -1
- package/dist/commands/action-acceptance.js +115 -0
- package/dist/commands/captcha-solve.d.ts.map +1 -1
- package/dist/commands/captcha-solve.js +83 -16
- package/dist/commands/click-action-executor.d.ts +0 -1
- package/dist/commands/click-action-executor.d.ts.map +1 -1
- package/dist/commands/click-action-executor.js +31 -77
- package/dist/commands/close.d.ts +3 -3
- package/dist/commands/close.d.ts.map +1 -1
- package/dist/commands/close.js +178 -0
- package/dist/commands/descriptor-validation.d.ts.map +1 -1
- package/dist/commands/descriptor-validation.js +75 -57
- package/dist/commands/end-session.d.ts +25 -0
- package/dist/commands/end-session.d.ts.map +1 -0
- package/dist/commands/end-session.js +161 -0
- package/dist/commands/extract-stagehand-executor.js +1 -1
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +339 -202
- package/dist/commands/fill-secret.d.ts +3 -3
- package/dist/commands/fill-secret.d.ts.map +1 -1
- package/dist/commands/fill-secret.js +419 -234
- package/dist/commands/get-secrets-catalog.d.ts.map +1 -1
- package/dist/commands/get-secrets-catalog.js +66 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +6 -3
- package/dist/commands/interaction-kernel.d.ts +46 -0
- package/dist/commands/interaction-kernel.d.ts.map +1 -0
- package/dist/commands/interaction-kernel.js +215 -0
- package/dist/commands/launch.d.ts +1 -3
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +115 -27
- package/dist/commands/navigate.d.ts.map +1 -1
- package/dist/commands/navigate.js +188 -45
- package/dist/commands/observe-accessibility.d.ts.map +1 -1
- package/dist/commands/observe-accessibility.js +46 -39
- package/dist/commands/observe-dom-label-contract.d.ts.map +1 -1
- package/dist/commands/observe-dom-label-contract.js +5 -0
- package/dist/commands/observe-inventory.d.ts +13 -0
- package/dist/commands/observe-inventory.d.ts.map +1 -1
- package/dist/commands/observe-inventory.js +320 -65
- package/dist/commands/observe-persistence.d.ts.map +1 -1
- package/dist/commands/observe-persistence.js +3 -0
- package/dist/commands/observe-projection.d.ts +1 -0
- package/dist/commands/observe-projection.d.ts.map +1 -1
- package/dist/commands/observe-projection.js +7 -2
- package/dist/commands/observe-protected.d.ts +1 -0
- package/dist/commands/observe-protected.d.ts.map +1 -1
- package/dist/commands/observe-protected.js +9 -4
- package/dist/commands/observe-semantics.d.ts.map +1 -1
- package/dist/commands/observe-semantics.js +5 -2
- package/dist/commands/observe-stagehand.d.ts +1 -0
- package/dist/commands/observe-stagehand.d.ts.map +1 -1
- package/dist/commands/observe-stagehand.js +2 -0
- package/dist/commands/observe.d.ts +2 -0
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +387 -203
- package/dist/commands/observe.test-harness.d.ts +8 -0
- package/dist/commands/observe.test-harness.d.ts.map +1 -1
- package/dist/commands/observe.test-harness.js +48 -1
- package/dist/commands/poll-secret.d.ts +6 -0
- package/dist/commands/poll-secret.d.ts.map +1 -0
- package/dist/commands/poll-secret.js +159 -0
- package/dist/commands/request-secret.d.ts +6 -0
- package/dist/commands/request-secret.d.ts.map +1 -0
- package/dist/commands/request-secret.js +284 -0
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/commands/screenshot.js +172 -7
- package/dist/commands/select-action-executor.d.ts.map +1 -1
- package/dist/commands/semantic-observe.d.ts +4 -0
- package/dist/commands/semantic-observe.d.ts.map +1 -1
- package/dist/commands/semantic-observe.js +388 -17
- package/dist/commands/start-session.d.ts +31 -0
- package/dist/commands/start-session.d.ts.map +1 -0
- package/dist/commands/start-session.js +347 -0
- package/dist/commands/status.d.ts +2 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +166 -144
- package/dist/control-semantics.d.ts +1 -0
- package/dist/control-semantics.d.ts.map +1 -1
- package/dist/control-semantics.js +51 -9
- package/dist/generated/build-config.d.ts +2 -0
- package/dist/generated/build-config.d.ts.map +1 -0
- package/dist/generated/build-config.js +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +163 -63
- package/dist/otel-exporter.d.ts +58 -0
- package/dist/otel-exporter.d.ts.map +1 -0
- package/dist/otel-exporter.js +263 -0
- package/dist/otel-projector.d.ts +75 -0
- package/dist/otel-projector.d.ts.map +1 -0
- package/dist/otel-projector.js +409 -0
- package/dist/owned-browser.d.ts +1 -1
- package/dist/owned-browser.d.ts.map +1 -1
- package/dist/owned-browser.js +13 -1
- package/dist/owned-process.d.ts +2 -0
- package/dist/owned-process.d.ts.map +1 -1
- package/dist/owned-process.js +7 -3
- package/dist/playwright-runtime.d.ts +1 -1
- package/dist/playwright-runtime.d.ts.map +1 -1
- package/dist/playwright-runtime.js +8 -8
- package/dist/run-observability.d.ts +25 -0
- package/dist/run-observability.d.ts.map +1 -0
- package/dist/run-observability.js +115 -0
- package/dist/run-store.d.ts +274 -0
- package/dist/run-store.d.ts.map +1 -0
- package/dist/run-store.js +631 -0
- package/dist/runtime-metrics.d.ts +27 -0
- package/dist/runtime-metrics.d.ts.map +1 -0
- package/dist/runtime-metrics.js +66 -0
- package/dist/runtime-page-state.d.ts +11 -0
- package/dist/runtime-page-state.d.ts.map +1 -0
- package/dist/runtime-page-state.js +62 -0
- package/dist/runtime-protected-state.d.ts +16 -0
- package/dist/runtime-protected-state.d.ts.map +1 -0
- package/dist/runtime-protected-state.js +157 -0
- package/dist/runtime-state.d.ts +10 -44
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +57 -222
- package/dist/secrets/backend.d.ts +65 -16
- package/dist/secrets/backend.d.ts.map +1 -1
- package/dist/secrets/backend.js +135 -95
- package/dist/secrets/catalog-sync.d.ts.map +1 -1
- package/dist/secrets/catalog-sync.js +4 -1
- package/dist/secrets/form-matcher.d.ts +5 -5
- package/dist/secrets/form-matcher.d.ts.map +1 -1
- package/dist/secrets/form-matcher.js +292 -164
- package/dist/secrets/intent-output.d.ts +6 -10
- package/dist/secrets/intent-output.d.ts.map +1 -1
- package/dist/secrets/intent-output.js +4 -58
- package/dist/secrets/mock-agentpay-cabinet.d.ts +38 -27
- package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -1
- package/dist/secrets/mock-agentpay-cabinet.js +177 -111
- package/dist/secrets/protected-artifact-guard.d.ts +2 -2
- package/dist/secrets/protected-artifact-guard.d.ts.map +1 -1
- package/dist/secrets/protected-artifact-guard.js +2 -2
- package/dist/secrets/protected-bindings.d.ts +1 -1
- package/dist/secrets/protected-bindings.d.ts.map +1 -1
- package/dist/secrets/protected-bindings.js +6 -0
- package/dist/secrets/protected-field-semantics.d.ts +9 -0
- package/dist/secrets/protected-field-semantics.d.ts.map +1 -0
- package/dist/secrets/protected-field-semantics.js +154 -0
- package/dist/secrets/protected-field-values.d.ts.map +1 -1
- package/dist/secrets/protected-field-values.js +3 -3
- package/dist/secrets/protected-fill.d.ts +1 -1
- package/dist/secrets/protected-fill.d.ts.map +1 -1
- package/dist/secrets/protected-fill.js +45 -149
- package/dist/secrets/protected-value-adapters.d.ts +2 -1
- package/dist/secrets/protected-value-adapters.d.ts.map +1 -1
- package/dist/secrets/protected-value-adapters.js +80 -1
- package/dist/secrets/request-output.d.ts +11 -0
- package/dist/secrets/request-output.d.ts.map +1 -0
- package/dist/secrets/request-output.js +75 -0
- package/dist/secrets/types.d.ts +15 -9
- package/dist/secrets/types.d.ts.map +1 -1
- package/dist/secrets/types.js +3 -0
- package/dist/session-event-exporter.d.ts +36 -0
- package/dist/session-event-exporter.d.ts.map +1 -0
- package/dist/session-event-exporter.js +428 -0
- package/dist/session.d.ts +16 -7
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +150 -23
- package/dist/sessions-backend.d.ts +354 -0
- package/dist/sessions-backend.d.ts.map +1 -0
- package/dist/sessions-backend.js +126 -0
- package/dist/solver/browser-launcher.d.ts +1 -1
- package/dist/solver/browser-launcher.d.ts.map +1 -1
- package/dist/solver/browser-launcher.js +39 -13
- package/dist/solver/captcha-solver.d.ts.map +1 -1
- package/dist/solver/captcha-solver.js +8 -1
- package/dist/solver/config.d.ts +0 -1
- package/dist/solver/config.d.ts.map +1 -1
- package/dist/solver/config.js +0 -22
- package/dist/solver/types.d.ts +1 -0
- package/dist/solver/types.d.ts.map +1 -1
- package/dist/workflow-session-completion.d.ts +33 -0
- package/dist/workflow-session-completion.d.ts.map +1 -0
- package/dist/workflow-session-completion.js +156 -0
- package/package.json +11 -1
- package/dist/commands/create-intent.d.ts +0 -6
- package/dist/commands/create-intent.d.ts.map +0 -1
- package/dist/commands/create-intent.js +0 -75
- package/dist/commands/poll-intent.d.ts +0 -6
- package/dist/commands/poll-intent.d.ts.map +0 -1
- package/dist/commands/poll-intent.js +0 -57
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { randomBytes, randomUUID } from 'node:crypto';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
const PACKAGE_JSON_URL = new URL('../package.json', import.meta.url);
|
|
6
|
+
const SERVICE_NAME = '@nuanu-ai/agentbrowse';
|
|
7
|
+
const RUN_LOCK_DIRNAME = '.lock';
|
|
8
|
+
const RUN_LOCK_TIMEOUT_MS = 2_000;
|
|
9
|
+
const RUN_LOCK_RETRY_MS = 10;
|
|
10
|
+
const RUN_LOCK_STALE_MS = 30_000;
|
|
11
|
+
const lockSleepView = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
12
|
+
export const DEFAULT_RUN_RETENTION_DAYS = 7;
|
|
13
|
+
function ensureRunsDir() {
|
|
14
|
+
const runsDir = getRunsDir();
|
|
15
|
+
if (!existsSync(runsDir)) {
|
|
16
|
+
mkdirSync(runsDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function getRunDir(runId) {
|
|
20
|
+
return join(getRunsDir(), runId);
|
|
21
|
+
}
|
|
22
|
+
function getRunRecordPath(runId) {
|
|
23
|
+
return join(getRunDir(runId), 'run.json');
|
|
24
|
+
}
|
|
25
|
+
function getRunsDir() {
|
|
26
|
+
return join(homedir(), '.agentpay', 'runs');
|
|
27
|
+
}
|
|
28
|
+
function getRunStepsDir(runId) {
|
|
29
|
+
return join(getRunDir(runId), 'steps');
|
|
30
|
+
}
|
|
31
|
+
function getRunArtifactsDir(runId) {
|
|
32
|
+
return join(getRunDir(runId), 'artifacts');
|
|
33
|
+
}
|
|
34
|
+
function getRunSnapshotsDir(runId) {
|
|
35
|
+
return join(getRunDir(runId), 'snapshots');
|
|
36
|
+
}
|
|
37
|
+
function getRunEventsDir(runId) {
|
|
38
|
+
return join(getRunDir(runId), 'events');
|
|
39
|
+
}
|
|
40
|
+
function getStepRecordPath(runId, stepId) {
|
|
41
|
+
return join(getRunStepsDir(runId), `${stepId}.json`);
|
|
42
|
+
}
|
|
43
|
+
function getArtifactManifestPath(runId, artifactManifestId) {
|
|
44
|
+
return join(getRunArtifactsDir(runId), `${artifactManifestId}.json`);
|
|
45
|
+
}
|
|
46
|
+
function getBrowserStateSnapshotPath(runId, snapshotId) {
|
|
47
|
+
return join(getRunSnapshotsDir(runId), `${snapshotId}.json`);
|
|
48
|
+
}
|
|
49
|
+
function getRunEventRecordPath(runId, eventId) {
|
|
50
|
+
return join(getRunEventsDir(runId), `${eventId}.json`);
|
|
51
|
+
}
|
|
52
|
+
function getRunLockDir(runId) {
|
|
53
|
+
return join(getRunDir(runId), RUN_LOCK_DIRNAME);
|
|
54
|
+
}
|
|
55
|
+
function randomHex(bytes) {
|
|
56
|
+
return randomBytes(bytes).toString('hex');
|
|
57
|
+
}
|
|
58
|
+
function readCurrentPackageVersion() {
|
|
59
|
+
const raw = JSON.parse(readFileSync(PACKAGE_JSON_URL, 'utf-8'));
|
|
60
|
+
if (!raw.version || typeof raw.version !== 'string') {
|
|
61
|
+
throw new Error('Package version is missing from package.json.');
|
|
62
|
+
}
|
|
63
|
+
return raw.version;
|
|
64
|
+
}
|
|
65
|
+
function parseHostHints(url) {
|
|
66
|
+
if (!url) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return [new URL(url).host];
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function parseTimestamp(value) {
|
|
77
|
+
if (!value) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const parsed = new Date(value).getTime();
|
|
81
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
82
|
+
}
|
|
83
|
+
function formatUnknownError(error) {
|
|
84
|
+
if (error instanceof Error) {
|
|
85
|
+
return error.message;
|
|
86
|
+
}
|
|
87
|
+
if (typeof error === 'string') {
|
|
88
|
+
return error;
|
|
89
|
+
}
|
|
90
|
+
if (error && typeof error === 'object') {
|
|
91
|
+
try {
|
|
92
|
+
return JSON.stringify(error);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return Object.prototype.toString.call(error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return String(error);
|
|
99
|
+
}
|
|
100
|
+
function isFsError(error, code) {
|
|
101
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
|
|
102
|
+
}
|
|
103
|
+
function sleepSync(ms) {
|
|
104
|
+
Atomics.wait(lockSleepView, 0, 0, ms);
|
|
105
|
+
}
|
|
106
|
+
function atomicWriteUtf8(filePath, contents) {
|
|
107
|
+
const tempPath = `${filePath}.${process.pid}.${randomHex(4)}.tmp`;
|
|
108
|
+
try {
|
|
109
|
+
writeFileSync(tempPath, contents, 'utf-8');
|
|
110
|
+
renameSync(tempPath, filePath);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
rmSync(tempPath, { force: true });
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function listRunStepRecords(runId) {
|
|
118
|
+
const stepsDir = getRunStepsDir(runId);
|
|
119
|
+
if (!existsSync(stepsDir)) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const records = [];
|
|
123
|
+
for (const entry of readdirSync(stepsDir, { withFileTypes: true })) {
|
|
124
|
+
if (!entry.isFile() || !entry.name.endsWith('.json')) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const parsed = JSON.parse(readFileSync(join(stepsDir, entry.name), 'utf-8'));
|
|
129
|
+
if (parsed.runId === runId) {
|
|
130
|
+
records.push({
|
|
131
|
+
...parsed,
|
|
132
|
+
eventIds: loadDerivedEventIds(runId, parsed.stepId, parsed.eventIds),
|
|
133
|
+
childSpanIds: parsed.childSpanIds ??
|
|
134
|
+
parsed.childSpans?.map((childSpan) => childSpan.spanId) ??
|
|
135
|
+
[],
|
|
136
|
+
childSpans: parsed.childSpans ?? [],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
records.sort((left, right) => {
|
|
144
|
+
if (left.ordinal !== right.ordinal) {
|
|
145
|
+
return left.ordinal - right.ordinal;
|
|
146
|
+
}
|
|
147
|
+
const leftStartedAt = parseTimestamp(left.startedAt) ?? 0;
|
|
148
|
+
const rightStartedAt = parseTimestamp(right.startedAt) ?? 0;
|
|
149
|
+
if (leftStartedAt !== rightStartedAt) {
|
|
150
|
+
return leftStartedAt - rightStartedAt;
|
|
151
|
+
}
|
|
152
|
+
return left.stepId.localeCompare(right.stepId);
|
|
153
|
+
});
|
|
154
|
+
return records;
|
|
155
|
+
}
|
|
156
|
+
function listRunEventRecords(runId, stepId) {
|
|
157
|
+
const eventsDir = getRunEventsDir(runId);
|
|
158
|
+
if (!existsSync(eventsDir)) {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
const records = [];
|
|
162
|
+
for (const entry of readdirSync(eventsDir, { withFileTypes: true })) {
|
|
163
|
+
if (!entry.isFile() || !entry.name.endsWith('.json')) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const parsed = JSON.parse(readFileSync(join(eventsDir, entry.name), 'utf-8'));
|
|
168
|
+
if (parsed.runId === runId && (!stepId || parsed.stepId === stepId)) {
|
|
169
|
+
records.push(parsed);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
records.sort((left, right) => {
|
|
176
|
+
const leftEmittedAt = parseTimestamp(left.emittedAt) ?? 0;
|
|
177
|
+
const rightEmittedAt = parseTimestamp(right.emittedAt) ?? 0;
|
|
178
|
+
if (leftEmittedAt !== rightEmittedAt) {
|
|
179
|
+
return leftEmittedAt - rightEmittedAt;
|
|
180
|
+
}
|
|
181
|
+
return left.eventId.localeCompare(right.eventId);
|
|
182
|
+
});
|
|
183
|
+
return records;
|
|
184
|
+
}
|
|
185
|
+
function loadDerivedStepIds(runId, fallbackStepIds) {
|
|
186
|
+
const derived = listRunStepRecords(runId).map((step) => step.stepId);
|
|
187
|
+
if (derived.length > 0) {
|
|
188
|
+
return derived;
|
|
189
|
+
}
|
|
190
|
+
return [...(fallbackStepIds ?? [])];
|
|
191
|
+
}
|
|
192
|
+
function loadDerivedEventIds(runId, stepId, fallbackEventIds) {
|
|
193
|
+
const derived = listRunEventRecords(runId, stepId).map((event) => event.eventId);
|
|
194
|
+
if (derived.length > 0) {
|
|
195
|
+
return derived;
|
|
196
|
+
}
|
|
197
|
+
return [...(fallbackEventIds ?? [])];
|
|
198
|
+
}
|
|
199
|
+
function withRunLock(runId, fn) {
|
|
200
|
+
ensureRunsDir();
|
|
201
|
+
mkdirSync(getRunDir(runId), { recursive: true });
|
|
202
|
+
const lockDir = getRunLockDir(runId);
|
|
203
|
+
const deadline = Date.now() + RUN_LOCK_TIMEOUT_MS;
|
|
204
|
+
while (true) {
|
|
205
|
+
try {
|
|
206
|
+
mkdirSync(lockDir);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
if (!isFsError(error, 'EEXIST')) {
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
let lockAgeMs = null;
|
|
214
|
+
try {
|
|
215
|
+
lockAgeMs = Date.now() - statSync(lockDir).mtimeMs;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (lockAgeMs > RUN_LOCK_STALE_MS) {
|
|
221
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (Date.now() >= deadline) {
|
|
225
|
+
throw new Error(`run_lock_timeout:${runId}`);
|
|
226
|
+
}
|
|
227
|
+
sleepSync(RUN_LOCK_RETRY_MS);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
return fn();
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function isTerminalStatus(status) {
|
|
238
|
+
return status === 'completed' || status === 'failed' || status === 'aborted';
|
|
239
|
+
}
|
|
240
|
+
export function createCliRunRecord(options) {
|
|
241
|
+
const now = options.now ?? new Date();
|
|
242
|
+
const packageVersion = readCurrentPackageVersion();
|
|
243
|
+
return {
|
|
244
|
+
runId: randomUUID(),
|
|
245
|
+
sessionId: options.sessionId,
|
|
246
|
+
startedAt: now.toISOString(),
|
|
247
|
+
status: 'running',
|
|
248
|
+
source: 'cli',
|
|
249
|
+
entryCommand: options.entryCommand,
|
|
250
|
+
...(options.displayName ? { displayName: options.displayName } : {}),
|
|
251
|
+
profile: options.profile,
|
|
252
|
+
runtimeVersion: process.version,
|
|
253
|
+
cliVersion: packageVersion,
|
|
254
|
+
cwd: options.cwd ?? process.cwd(),
|
|
255
|
+
hostHints: parseHostHints(options.url),
|
|
256
|
+
traceContext: {
|
|
257
|
+
traceId: randomHex(16),
|
|
258
|
+
rootSpanId: randomHex(8),
|
|
259
|
+
propagationMode: 'single-trace-across-cli-steps',
|
|
260
|
+
},
|
|
261
|
+
resource: {
|
|
262
|
+
serviceName: SERVICE_NAME,
|
|
263
|
+
serviceVersion: packageVersion,
|
|
264
|
+
serviceInstanceId: options.sessionId,
|
|
265
|
+
telemetrySdkLanguage: 'nodejs',
|
|
266
|
+
},
|
|
267
|
+
stepIds: [],
|
|
268
|
+
protectedRun: options.protectedRun === true,
|
|
269
|
+
ingestState: 'local_only',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
export function saveRunRecord(record) {
|
|
273
|
+
ensureRunsDir();
|
|
274
|
+
const runDir = getRunDir(record.runId);
|
|
275
|
+
mkdirSync(runDir, { recursive: true });
|
|
276
|
+
const { stepIds: _stepIds, ...persisted } = record;
|
|
277
|
+
atomicWriteUtf8(getRunRecordPath(record.runId), JSON.stringify(persisted, null, 2) + '\n');
|
|
278
|
+
}
|
|
279
|
+
export function startCliRun(options) {
|
|
280
|
+
const record = createCliRunRecord(options);
|
|
281
|
+
saveRunRecord(record);
|
|
282
|
+
return record;
|
|
283
|
+
}
|
|
284
|
+
export function loadRunRecord(runId) {
|
|
285
|
+
const recordPath = getRunRecordPath(runId);
|
|
286
|
+
if (!existsSync(recordPath)) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
const persisted = JSON.parse(readFileSync(recordPath, 'utf-8'));
|
|
291
|
+
return {
|
|
292
|
+
...persisted,
|
|
293
|
+
stepIds: loadDerivedStepIds(runId, persisted.stepIds),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
export function loadStepRecord(runId, stepId) {
|
|
301
|
+
const recordPath = getStepRecordPath(runId, stepId);
|
|
302
|
+
if (!existsSync(recordPath)) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const persisted = JSON.parse(readFileSync(recordPath, 'utf-8'));
|
|
307
|
+
return {
|
|
308
|
+
...persisted,
|
|
309
|
+
eventIds: loadDerivedEventIds(runId, stepId, persisted.eventIds),
|
|
310
|
+
childSpanIds: persisted.childSpanIds ?? persisted.childSpans?.map((childSpan) => childSpan.spanId) ?? [],
|
|
311
|
+
childSpans: persisted.childSpans ?? [],
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
export function saveStepRecord(record) {
|
|
319
|
+
ensureRunsDir();
|
|
320
|
+
mkdirSync(getRunDir(record.runId), { recursive: true });
|
|
321
|
+
mkdirSync(getRunStepsDir(record.runId), { recursive: true });
|
|
322
|
+
const { eventIds: _eventIds, childSpanIds: _childSpanIds, ...persisted } = record;
|
|
323
|
+
atomicWriteUtf8(getStepRecordPath(record.runId, record.stepId), JSON.stringify(persisted, null, 2) + '\n');
|
|
324
|
+
}
|
|
325
|
+
export function loadArtifactManifest(runId, artifactManifestId) {
|
|
326
|
+
const manifestPath = getArtifactManifestPath(runId, artifactManifestId);
|
|
327
|
+
if (!existsSync(manifestPath)) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
return JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
export function saveArtifactManifest(runId, manifest) {
|
|
338
|
+
withRunLock(runId, () => {
|
|
339
|
+
ensureRunsDir();
|
|
340
|
+
mkdirSync(getRunDir(runId), { recursive: true });
|
|
341
|
+
mkdirSync(getRunArtifactsDir(runId), { recursive: true });
|
|
342
|
+
atomicWriteUtf8(getArtifactManifestPath(runId, manifest.artifactManifestId), JSON.stringify(manifest, null, 2) + '\n');
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
export function loadBrowserStateSnapshot(runId, snapshotId) {
|
|
346
|
+
const snapshotPath = getBrowserStateSnapshotPath(runId, snapshotId);
|
|
347
|
+
if (!existsSync(snapshotPath)) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
return JSON.parse(readFileSync(snapshotPath, 'utf-8'));
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
export function recordStepSnapshot(options) {
|
|
358
|
+
return withRunLock(options.runId, () => {
|
|
359
|
+
const step = loadStepRecord(options.runId, options.stepId);
|
|
360
|
+
if (!step) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
const snapshot = {
|
|
364
|
+
snapshotId: randomUUID(),
|
|
365
|
+
runId: options.runId,
|
|
366
|
+
stepId: options.stepId,
|
|
367
|
+
phase: options.phase,
|
|
368
|
+
capturedAt: options.capturedAt ?? new Date().toISOString(),
|
|
369
|
+
pageRef: options.pageRef,
|
|
370
|
+
url: options.url,
|
|
371
|
+
...(options.title ? { title: options.title } : {}),
|
|
372
|
+
tabs: options.tabs.map((tab) => ({ ...tab })),
|
|
373
|
+
runtimeSummary: {
|
|
374
|
+
...options.runtimeSummary,
|
|
375
|
+
},
|
|
376
|
+
...(options.protectionState ? { protectionState: { ...options.protectionState } } : {}),
|
|
377
|
+
...(options.artifactRefs ? { artifactRefs: { ...options.artifactRefs } } : {}),
|
|
378
|
+
};
|
|
379
|
+
mkdirSync(getRunDir(options.runId), { recursive: true });
|
|
380
|
+
mkdirSync(getRunSnapshotsDir(options.runId), { recursive: true });
|
|
381
|
+
atomicWriteUtf8(getBrowserStateSnapshotPath(options.runId, snapshot.snapshotId), JSON.stringify(snapshot, null, 2) + '\n');
|
|
382
|
+
if (options.phase === 'before' || options.phase === 'after') {
|
|
383
|
+
const nextStep = {
|
|
384
|
+
...step,
|
|
385
|
+
...(options.phase === 'before'
|
|
386
|
+
? { beforeSnapshotId: step.beforeSnapshotId ?? snapshot.snapshotId }
|
|
387
|
+
: { afterSnapshotId: step.afterSnapshotId ?? snapshot.snapshotId }),
|
|
388
|
+
};
|
|
389
|
+
saveStepRecord(nextStep);
|
|
390
|
+
}
|
|
391
|
+
return snapshot;
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
export function loadRunEventRecord(runId, eventId) {
|
|
395
|
+
const eventPath = getRunEventRecordPath(runId, eventId);
|
|
396
|
+
if (!existsSync(eventPath)) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
return JSON.parse(readFileSync(eventPath, 'utf-8'));
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
export function listRunEventRecordsForStep(runId, stepId) {
|
|
407
|
+
return listRunEventRecords(runId, stepId);
|
|
408
|
+
}
|
|
409
|
+
export function appendRunEvent(options) {
|
|
410
|
+
return withRunLock(options.runId, () => {
|
|
411
|
+
const step = loadStepRecord(options.runId, options.stepId);
|
|
412
|
+
if (!step) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
const event = {
|
|
416
|
+
eventId: randomUUID(),
|
|
417
|
+
runId: options.runId,
|
|
418
|
+
stepId: options.stepId,
|
|
419
|
+
traceId: step.otel.traceId,
|
|
420
|
+
spanId: step.otel.spanId,
|
|
421
|
+
emittedAt: options.emittedAt ?? new Date().toISOString(),
|
|
422
|
+
source: options.source,
|
|
423
|
+
name: options.name,
|
|
424
|
+
attributes: { ...(options.attributes ?? {}) },
|
|
425
|
+
};
|
|
426
|
+
mkdirSync(getRunDir(options.runId), { recursive: true });
|
|
427
|
+
mkdirSync(getRunEventsDir(options.runId), { recursive: true });
|
|
428
|
+
atomicWriteUtf8(getRunEventRecordPath(options.runId, event.eventId), JSON.stringify(event, null, 2) + '\n');
|
|
429
|
+
return event;
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
export function startRunStep(options) {
|
|
433
|
+
if (!existsSync(getRunRecordPath(options.runId))) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
return withRunLock(options.runId, () => {
|
|
437
|
+
const run = loadRunRecord(options.runId);
|
|
438
|
+
if (!run) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
const previousStep = listRunStepRecords(run.runId).at(-1) ?? null;
|
|
442
|
+
const ordinal = run.stepIds.length + 1;
|
|
443
|
+
const stepId = randomUUID();
|
|
444
|
+
const step = {
|
|
445
|
+
stepId,
|
|
446
|
+
runId: run.runId,
|
|
447
|
+
ordinal,
|
|
448
|
+
command: options.command,
|
|
449
|
+
startedAt: (options.now ?? new Date()).toISOString(),
|
|
450
|
+
otel: {
|
|
451
|
+
traceId: run.traceContext.traceId,
|
|
452
|
+
spanId: randomHex(8),
|
|
453
|
+
parentSpanId: run.traceContext.rootSpanId,
|
|
454
|
+
...(previousStep ? { linkedSpanIds: [previousStep.otel.spanId] } : {}),
|
|
455
|
+
spanName: `agentbrowse.${options.command}`,
|
|
456
|
+
spanKind: 'internal',
|
|
457
|
+
statusCode: 'unset',
|
|
458
|
+
instrumentationScope: 'agentbrowse.local-run-store',
|
|
459
|
+
},
|
|
460
|
+
input: { ...(options.input ?? {}) },
|
|
461
|
+
...(options.refs ? { refs: options.refs } : {}),
|
|
462
|
+
eventIds: [],
|
|
463
|
+
childSpanIds: [],
|
|
464
|
+
childSpans: [],
|
|
465
|
+
protectedStep: options.protectedStep === true,
|
|
466
|
+
};
|
|
467
|
+
saveStepRecord(step);
|
|
468
|
+
return step;
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
export function finishRunStep(options) {
|
|
472
|
+
return withRunLock(options.runId, () => {
|
|
473
|
+
const step = loadStepRecord(options.runId, options.stepId);
|
|
474
|
+
if (!step) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
const finished = {
|
|
478
|
+
...step,
|
|
479
|
+
endedAt: step.endedAt ?? (options.now ?? new Date()).toISOString(),
|
|
480
|
+
otel: {
|
|
481
|
+
...step.otel,
|
|
482
|
+
statusCode: step.otel.statusCode === 'unset'
|
|
483
|
+
? options.success
|
|
484
|
+
? 'ok'
|
|
485
|
+
: 'error'
|
|
486
|
+
: step.otel.statusCode,
|
|
487
|
+
...(step.otel.statusMessage
|
|
488
|
+
? { statusMessage: step.otel.statusMessage }
|
|
489
|
+
: options.reason
|
|
490
|
+
? { statusMessage: options.reason }
|
|
491
|
+
: {}),
|
|
492
|
+
},
|
|
493
|
+
outputSummary: step.outputSummary ?? {
|
|
494
|
+
success: options.success,
|
|
495
|
+
...(options.outcomeType ? { outcomeType: options.outcomeType } : {}),
|
|
496
|
+
...(options.message ? { message: options.message } : {}),
|
|
497
|
+
...(options.reason ? { reason: options.reason } : {}),
|
|
498
|
+
},
|
|
499
|
+
artifactManifestId: step.artifactManifestId ?? options.artifactManifestId,
|
|
500
|
+
};
|
|
501
|
+
saveStepRecord(finished);
|
|
502
|
+
return finished;
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
export function appendStepChildSpan(options) {
|
|
506
|
+
return withRunLock(options.runId, () => {
|
|
507
|
+
const step = loadStepRecord(options.runId, options.stepId);
|
|
508
|
+
if (!step) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
const childSpan = {
|
|
512
|
+
spanId: randomHex(8),
|
|
513
|
+
parentSpanId: step.otel.spanId,
|
|
514
|
+
name: options.name,
|
|
515
|
+
kind: options.kind ?? 'client',
|
|
516
|
+
startedAt: options.startedAt,
|
|
517
|
+
endedAt: options.endedAt,
|
|
518
|
+
statusCode: options.statusCode,
|
|
519
|
+
...(options.statusMessage ? { statusMessage: options.statusMessage } : {}),
|
|
520
|
+
attributes: { ...(options.attributes ?? {}) },
|
|
521
|
+
};
|
|
522
|
+
const nextStep = {
|
|
523
|
+
...step,
|
|
524
|
+
childSpanIds: [...(step.childSpanIds ?? []), childSpan.spanId],
|
|
525
|
+
childSpans: [...(step.childSpans ?? []), childSpan],
|
|
526
|
+
};
|
|
527
|
+
saveStepRecord(nextStep);
|
|
528
|
+
return childSpan;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
export function finishRunRecord(runId, options) {
|
|
532
|
+
return withRunLock(runId, () => {
|
|
533
|
+
const existing = loadRunRecord(runId);
|
|
534
|
+
if (!existing) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
const record = {
|
|
538
|
+
...existing,
|
|
539
|
+
status: existing.endedAt ? existing.status : options.status,
|
|
540
|
+
endedAt: existing.endedAt ?? (options.now ?? new Date()).toISOString(),
|
|
541
|
+
finalOutcome: existing.finalOutcome ?? options.finalOutcome,
|
|
542
|
+
};
|
|
543
|
+
saveRunRecord(record);
|
|
544
|
+
return record;
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
export function setRunIngestState(runId, ingestState) {
|
|
548
|
+
return withRunLock(runId, () => {
|
|
549
|
+
const existing = loadRunRecord(runId);
|
|
550
|
+
if (!existing) {
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
if (existing.ingestState === ingestState) {
|
|
554
|
+
return existing;
|
|
555
|
+
}
|
|
556
|
+
const record = {
|
|
557
|
+
...existing,
|
|
558
|
+
ingestState,
|
|
559
|
+
};
|
|
560
|
+
saveRunRecord(record);
|
|
561
|
+
return record;
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
export function pruneLocalRuns(options = {}) {
|
|
565
|
+
const now = options.now ?? new Date();
|
|
566
|
+
const retentionDays = options.retentionDays ?? DEFAULT_RUN_RETENTION_DAYS;
|
|
567
|
+
const cutoffMs = now.getTime() - retentionDays * 24 * 60 * 60 * 1000;
|
|
568
|
+
const runsDir = getRunsDir();
|
|
569
|
+
if (!existsSync(runsDir)) {
|
|
570
|
+
return {
|
|
571
|
+
deletedRunIds: [],
|
|
572
|
+
keptRunIds: [],
|
|
573
|
+
failedRunIds: [],
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
const deletedRunIds = [];
|
|
577
|
+
const keptRunIds = [];
|
|
578
|
+
const failedRunIds = [];
|
|
579
|
+
for (const entry of readdirSync(runsDir, { withFileTypes: true })) {
|
|
580
|
+
if (!entry.isDirectory()) {
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const runId = entry.name;
|
|
584
|
+
if (runId === RUN_LOCK_DIRNAME) {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (runId === options.activeRunId) {
|
|
588
|
+
keptRunIds.push(runId);
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
const runDir = getRunDir(runId);
|
|
592
|
+
if (existsSync(getRunLockDir(runId))) {
|
|
593
|
+
keptRunIds.push(runId);
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
const record = loadRunRecord(runId);
|
|
597
|
+
const fallbackMtimeMs = statSync(runDir).mtimeMs;
|
|
598
|
+
const referenceTimeMs = parseTimestamp(record?.endedAt) ??
|
|
599
|
+
parseTimestamp(record?.startedAt) ??
|
|
600
|
+
(Number.isFinite(fallbackMtimeMs) ? fallbackMtimeMs : null);
|
|
601
|
+
const isPrunableRecord = !record || isTerminalStatus(record.status);
|
|
602
|
+
if (!isPrunableRecord || referenceTimeMs === null || referenceTimeMs > cutoffMs) {
|
|
603
|
+
keptRunIds.push(runId);
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
rmSync(runDir, { recursive: true, force: true });
|
|
608
|
+
deletedRunIds.push(runId);
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
failedRunIds.push({
|
|
612
|
+
runId,
|
|
613
|
+
reason: formatUnknownError(error),
|
|
614
|
+
});
|
|
615
|
+
keptRunIds.push(runId);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
deletedRunIds,
|
|
620
|
+
keptRunIds,
|
|
621
|
+
failedRunIds,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
export function deleteLocalRun(runId) {
|
|
625
|
+
const runDir = getRunDir(runId);
|
|
626
|
+
if (!existsSync(runDir)) {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
rmSync(runDir, { recursive: true, force: true });
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BrowseSession } from './session.js';
|
|
2
|
+
import { type BrowsePayloadBudgetMetrics, type BrowseRuntimeMetrics } from './runtime-state.js';
|
|
3
|
+
type NumericBrowseRuntimeMetricKey = Exclude<{
|
|
4
|
+
[K in keyof BrowseRuntimeMetrics]: BrowseRuntimeMetrics[K] extends number | undefined ? K : never;
|
|
5
|
+
}[keyof BrowseRuntimeMetrics], undefined>;
|
|
6
|
+
type LlmUsageLike = Partial<{
|
|
7
|
+
prompt_tokens: number;
|
|
8
|
+
completion_tokens: number;
|
|
9
|
+
total_tokens: number;
|
|
10
|
+
cached_input_tokens: number;
|
|
11
|
+
reasoning_tokens: number;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function incrementMetric(session: BrowseSession, metric: NumericBrowseRuntimeMetricKey, by?: number): number;
|
|
14
|
+
export declare function recordActionResult(session: BrowseSession, success: boolean, durationMs: number): BrowseRuntimeMetrics;
|
|
15
|
+
export declare function recordLlmUsage(session: BrowseSession, params: {
|
|
16
|
+
purpose: string;
|
|
17
|
+
usage?: LlmUsageLike | null;
|
|
18
|
+
inputChars?: number;
|
|
19
|
+
promptTokens?: number;
|
|
20
|
+
completionTokens?: number;
|
|
21
|
+
totalTokens?: number;
|
|
22
|
+
cachedInputTokens?: number;
|
|
23
|
+
reasoningTokens?: number;
|
|
24
|
+
}): BrowseRuntimeMetrics;
|
|
25
|
+
export declare function recordPayloadBudget(session: BrowseSession, patch: Partial<BrowsePayloadBudgetMetrics>): BrowseRuntimeMetrics;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=runtime-metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-metrics.d.ts","sourceRoot":"","sources":["../src/runtime-metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAKL,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAE5B,KAAK,6BAA6B,GAAG,OAAO,CAC1C;KACG,CAAC,IAAI,MAAM,oBAAoB,GAAG,oBAAoB,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,GACjF,CAAC,GACD,KAAK;CACV,CAAC,MAAM,oBAAoB,CAAC,EAC7B,SAAS,CACV,CAAC;AAEF,KAAK,YAAY,GAAG,OAAO,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC,CAAC;AAEH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,6BAA6B,EACrC,EAAE,SAAI,GACL,MAAM,CAIR;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,GACjB,oBAAoB,CAetB;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE;IACN,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,oBAAoB,CAmCtB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,OAAO,CAAC,0BAA0B,CAAC,GACzC,oBAAoB,CAgBtB"}
|