sitelooper 0.3.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/LICENSE +21 -0
- package/README.md +625 -0
- package/bin/sitelooper.js +6 -0
- package/dist/agent/llm.js +460 -0
- package/dist/agent/llm.js.map +1 -0
- package/dist/agent/loop.js +870 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/prompt.js +40 -0
- package/dist/agent/prompt.js.map +1 -0
- package/dist/agent/report.js +545 -0
- package/dist/agent/report.js.map +1 -0
- package/dist/agent/tools.js +1147 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/cli.js +1692 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon/browser.js +218 -0
- package/dist/daemon/browser.js.map +1 -0
- package/dist/daemon/codegen.js +241 -0
- package/dist/daemon/codegen.js.map +1 -0
- package/dist/daemon/dialogs.js +57 -0
- package/dist/daemon/dialogs.js.map +1 -0
- package/dist/daemon/diff.js +198 -0
- package/dist/daemon/diff.js.map +1 -0
- package/dist/daemon/fingerprint.js +98 -0
- package/dist/daemon/fingerprint.js.map +1 -0
- package/dist/daemon/inputs.js +134 -0
- package/dist/daemon/inputs.js.map +1 -0
- package/dist/daemon/recorder.js +1232 -0
- package/dist/daemon/recorder.js.map +1 -0
- package/dist/daemon/refs.js +194 -0
- package/dist/daemon/refs.js.map +1 -0
- package/dist/daemon/server.js +1724 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/state.js +239 -0
- package/dist/daemon/state.js.map +1 -0
- package/dist/doctor.js +90 -0
- package/dist/doctor.js.map +1 -0
- package/dist/shared/paths.js +80 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/protocol.js +28 -0
- package/dist/shared/protocol.js.map +1 -0
- package/dist/shared/secrets.js +92 -0
- package/dist/shared/secrets.js.map +1 -0
- package/dist/shared/text.js +39 -0
- package/dist/shared/text.js.map +1 -0
- package/dist/skills/compile.js +1420 -0
- package/dist/skills/compile.js.map +1 -0
- package/dist/skills/components.js +456 -0
- package/dist/skills/components.js.map +1 -0
- package/dist/skills/flow.js +1041 -0
- package/dist/skills/flow.js.map +1 -0
- package/dist/skills/learn.js +406 -0
- package/dist/skills/learn.js.map +1 -0
- package/dist/skills/ledger.js +304 -0
- package/dist/skills/ledger.js.map +1 -0
- package/dist/skills/relabel.js +206 -0
- package/dist/skills/relabel.js.map +1 -0
- package/dist/skills/repair.js +570 -0
- package/dist/skills/repair.js.map +1 -0
- package/dist/skills/replay.js +1281 -0
- package/dist/skills/replay.js.map +1 -0
- package/dist/skills/store.js +147 -0
- package/dist/skills/store.js.map +1 -0
- package/dist/spec/check.js +428 -0
- package/dist/spec/check.js.map +1 -0
- package/dist/spec/diagnostics.js +58 -0
- package/dist/spec/diagnostics.js.map +1 -0
- package/dist/spec/emit.js +2084 -0
- package/dist/spec/emit.js.map +1 -0
- package/dist/spec/index.js +62 -0
- package/dist/spec/index.js.map +1 -0
- package/dist/spec/ir.js +216 -0
- package/dist/spec/ir.js.map +1 -0
- package/dist/spec/lift.js +162 -0
- package/dist/spec/lift.js.map +1 -0
- package/dist/spec/locators.js +270 -0
- package/dist/spec/locators.js.map +1 -0
- package/dist/spec/lower.js +124 -0
- package/dist/spec/lower.js.map +1 -0
- package/dist/spec/repair.js +657 -0
- package/dist/spec/repair.js.map +1 -0
- package/dist/spec/rerecord.js +169 -0
- package/dist/spec/rerecord.js.map +1 -0
- package/dist/spec/rethread.js +120 -0
- package/dist/spec/rethread.js.map +1 -0
- package/package.json +50 -0
- package/skills/sitelooper/SKILL.md +228 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { specOf } from '../skills/replay.js';
|
|
2
|
+
import { VOLATILE_TOKEN_SHAPE, WILDCARD, volatileMatcher } from '../shared/text.js';
|
|
3
|
+
const SLOT = /\{\{([vd]\d+)\}\}/;
|
|
4
|
+
const SLOT_G = new RegExp(SLOT.source, 'g');
|
|
5
|
+
const defaultSlot = (s) => '${p.' + s + '}';
|
|
6
|
+
/** The escapes a single-quoted and a template literal share. */
|
|
7
|
+
function escapeFor(text) {
|
|
8
|
+
return text
|
|
9
|
+
.replace(/\\/g, '\\\\')
|
|
10
|
+
.replace(/\n/g, '\\n')
|
|
11
|
+
.replace(/\r/g, '\\r')
|
|
12
|
+
.replace(/\t/g, '\\t')
|
|
13
|
+
.replace(/\u2028/g, '\\u2028')
|
|
14
|
+
.replace(/\u2029/g, '\\u2029');
|
|
15
|
+
}
|
|
16
|
+
/** A JS single-quoted string literal for `text`. */
|
|
17
|
+
function quote(text) {
|
|
18
|
+
return `'${escapeFor(text).replace(/'/g, "\\'")}'`;
|
|
19
|
+
}
|
|
20
|
+
/** Literal text inside a template literal: a backtick or a `${` would end it or open a hole. */
|
|
21
|
+
function escapeTemplate(text) {
|
|
22
|
+
return escapeFor(text).replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
23
|
+
}
|
|
24
|
+
/** JS string source for recorded text: a single-quoted literal, or a template literal when it
|
|
25
|
+
* carries {{slots}}. */
|
|
26
|
+
export function stringSource(text, o = {}) {
|
|
27
|
+
if (!SLOT.test(text))
|
|
28
|
+
return quote(text);
|
|
29
|
+
const slot = o.slot ?? defaultSlot;
|
|
30
|
+
// split() with one capture group alternates literal, slot name, literal...
|
|
31
|
+
const body = text
|
|
32
|
+
.split(SLOT_G)
|
|
33
|
+
.map((p, i) => (i % 2 ? slot(p) : escapeTemplate(p)))
|
|
34
|
+
.join('');
|
|
35
|
+
return `\`${body}\``;
|
|
36
|
+
}
|
|
37
|
+
// The \u0001 delimiters keep a sentinel's digits from fusing with neighbouring
|
|
38
|
+
// text into a clock or date token, and escapeRe leaves all three characters
|
|
39
|
+
// alone, so the sentinel comes back out of the pattern intact.
|
|
40
|
+
const SENTINEL = (i) => `\u0001${i}\u0001`;
|
|
41
|
+
const SENTINEL_G = /\u0001(\d+)\u0001/g;
|
|
42
|
+
/**
|
|
43
|
+
* The pieces of the RegExp `volatileMatcher` would build, with the slots
|
|
44
|
+
* pulled back out - or null when it would return a plain string.
|
|
45
|
+
*
|
|
46
|
+
* Built by asking volatileMatcher itself rather than restating its token
|
|
47
|
+
* shape here: each slot is swapped for a sentinel that survives its escaping
|
|
48
|
+
* untouched, and the sentinels are cut back out of the resulting `source`.
|
|
49
|
+
* Duplicating the wildcard pattern would let the two drift, and the whole
|
|
50
|
+
* point of this module is that they cannot.
|
|
51
|
+
*
|
|
52
|
+
* Volatility is judged with the slots still unbound, because a parameter is
|
|
53
|
+
* unknown at compile time: a date arriving through `{{v1}}` stays an exact
|
|
54
|
+
* match in the spec where replay would have wildcarded it. Stricter, never
|
|
55
|
+
* looser - a spec that quietly matched another day's row would be a lie.
|
|
56
|
+
*/
|
|
57
|
+
function matcherPieces(text) {
|
|
58
|
+
const names = [];
|
|
59
|
+
const probe = text.replace(SLOT_G, (_m, name) => {
|
|
60
|
+
names.push(name);
|
|
61
|
+
return SENTINEL(names.length - 1);
|
|
62
|
+
});
|
|
63
|
+
const matcher = volatileMatcher(probe);
|
|
64
|
+
if (typeof matcher === 'string')
|
|
65
|
+
return null;
|
|
66
|
+
const pieces = [];
|
|
67
|
+
let last = 0;
|
|
68
|
+
for (const m of matcher.source.matchAll(SENTINEL_G)) {
|
|
69
|
+
const at = m.index ?? 0;
|
|
70
|
+
if (at > last)
|
|
71
|
+
pieces.push({ lit: matcher.source.slice(last, at) });
|
|
72
|
+
pieces.push({ slot: names[Number(m[1])] });
|
|
73
|
+
last = at + m[0].length;
|
|
74
|
+
}
|
|
75
|
+
if (last < matcher.source.length)
|
|
76
|
+
pieces.push({ lit: matcher.source.slice(last) });
|
|
77
|
+
return pieces;
|
|
78
|
+
}
|
|
79
|
+
/** The expression inside a slot's interpolation, so it can be wrapped in escapeRe(). */
|
|
80
|
+
function slotExpr(name, o) {
|
|
81
|
+
const rendered = (o.slot ?? defaultSlot)(name);
|
|
82
|
+
return /^\$\{[\s\S]*\}$/.test(rendered) ? rendered.slice(2, -1) : null;
|
|
83
|
+
}
|
|
84
|
+
/** Source for the matcher makeLocator would build: stringSource, or a RegExp literal when
|
|
85
|
+
* volatileMatcher would return a RegExp (mirror its construction exactly; slots inside a
|
|
86
|
+
* regex become `${escapeRe(p.vN)}` via new RegExp(...) source). */
|
|
87
|
+
export function matcherSource(text, o = {}) {
|
|
88
|
+
const pieces = matcherPieces(text);
|
|
89
|
+
if (!pieces)
|
|
90
|
+
return stringSource(text, o);
|
|
91
|
+
if (!pieces.some((p) => 'slot' in p)) {
|
|
92
|
+
const source = pieces.map((p) => p.lit).join('');
|
|
93
|
+
// A literal keeps the generated file readable, and RegExp.source is
|
|
94
|
+
// already literal-safe - it escapes the slashes that would end one, which
|
|
95
|
+
// is why nothing is escaped again here. Only a line terminator has no
|
|
96
|
+
// literal form, so that case goes through new RegExp.
|
|
97
|
+
return /[\n\r\u2028\u2029]/.test(source) ? `new RegExp(${quote(source)})` : `/${source}/`;
|
|
98
|
+
}
|
|
99
|
+
// escapeRe is inlined into the generated file: a bound parameter is DATA, so
|
|
100
|
+
// its own regex metacharacters must not become pattern - which is exactly
|
|
101
|
+
// what volatileMatcher does to the recorded text before splicing in the
|
|
102
|
+
// wildcards.
|
|
103
|
+
const body = pieces
|
|
104
|
+
.map((p) => {
|
|
105
|
+
if ('lit' in p)
|
|
106
|
+
return escapeTemplate(p.lit);
|
|
107
|
+
const expr = slotExpr(p.slot, o);
|
|
108
|
+
// A caller whose slot renderer is not an interpolation owns its own
|
|
109
|
+
// escaping; there is no expression here to wrap.
|
|
110
|
+
return expr === null ? (o.slot ?? defaultSlot)(p.slot) : '${escapeRe(' + expr + ')}';
|
|
111
|
+
})
|
|
112
|
+
.join('');
|
|
113
|
+
return `new RegExp(\`${body}\`)`;
|
|
114
|
+
}
|
|
115
|
+
/** Regex SOURCE for one piece of recorded text: literals escaped, slots spliced in as
|
|
116
|
+
* `${escapeRe(p.vN)}` (a bound parameter is DATA, so its own metacharacters must not
|
|
117
|
+
* become pattern), all of it safe to sit inside a template literal. */
|
|
118
|
+
function patternBody(text, o) {
|
|
119
|
+
return text
|
|
120
|
+
.split(SLOT_G)
|
|
121
|
+
.map((p, i) => {
|
|
122
|
+
if (i % 2 === 0)
|
|
123
|
+
return escapeTemplate(reEscape(p));
|
|
124
|
+
const expr = slotExpr(p, o);
|
|
125
|
+
return expr === null ? (o.slot ?? defaultSlot)(p) : '${escapeRe(' + expr + ')}';
|
|
126
|
+
})
|
|
127
|
+
.join('');
|
|
128
|
+
}
|
|
129
|
+
const reEscape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
130
|
+
/**
|
|
131
|
+
* Source for a matcher over a recorded PAGE LINE - one whose volatile tokens the
|
|
132
|
+
* STORE already replaced with `{{*}}` (maskVolatile), unlike a locator name, which
|
|
133
|
+
* arrives verbatim and is masked here by `matcherSource`/`volatileMatcher`.
|
|
134
|
+
*
|
|
135
|
+
* Rendered as the literal string it looks like, `{{*}}` can never match anything:
|
|
136
|
+
* kanboard's `textbox "{{*}} {{*}}"` (a due-date field the app names after the
|
|
137
|
+
* current date and time) failed on every replay of the compiled spec. So each
|
|
138
|
+
* wildcard becomes the token shape it stood for - NOT `.*`. The mask is only ever
|
|
139
|
+
* applied to a clock or calendar token, so the token shape is exactly what was
|
|
140
|
+
* masked out, and it is what `volatileMatcher` would have produced from the
|
|
141
|
+
* unmasked line; `.*` would let `textbox "Due date"` satisfy an assertion recorded
|
|
142
|
+
* for a dated field, which is an assertion looser than the evidence behind it.
|
|
143
|
+
*
|
|
144
|
+
* `anchor` mirrors the caller's `exact`: a presence check is a substring match
|
|
145
|
+
* (Playwright ignores `exact` for a RegExp name, so the anchoring has to carry it),
|
|
146
|
+
* while the already-in-effect guard reads presence as a reason NOT to act and must
|
|
147
|
+
* stay line-exact. Null when the text is nothing BUT wildcards: that names no
|
|
148
|
+
* element at all, and the caller leaves it as an observation.
|
|
149
|
+
*/
|
|
150
|
+
export function maskedMatcherSource(text, o = {}) {
|
|
151
|
+
if (!text.includes(WILDCARD))
|
|
152
|
+
return matcherSource(text, o);
|
|
153
|
+
const parts = text.split(WILDCARD);
|
|
154
|
+
if (!parts.some((p) => p.trim()))
|
|
155
|
+
return null;
|
|
156
|
+
// The shape goes through escapeTemplate too: it is regex source living inside
|
|
157
|
+
// a template literal, and `\d` there is just "d".
|
|
158
|
+
const body = parts.map((p) => patternBody(p, o)).join(escapeTemplate(VOLATILE_TOKEN_SHAPE));
|
|
159
|
+
return `new RegExp(\`${o.anchor ? `^${body}$` : body}\`)`;
|
|
160
|
+
}
|
|
161
|
+
/** Playwright Locator expression for ONE candidate, mirroring makeLocator kind by kind
|
|
162
|
+
* (testid -> getByTestId / [attr="v"], role -> getByRole(role,{name,exact:true}), label,
|
|
163
|
+
* placeholder, text -> getByText(..,{exact:true}), id/css -> locator(sel), scoped ->
|
|
164
|
+
* locator(container,{hasText}).locator(sel), nth -> .nth(n)). Returns null for 'point'
|
|
165
|
+
* (not expressible without a runtime). */
|
|
166
|
+
export function candidateSource(c, o = {}) {
|
|
167
|
+
const page = o.page ?? 'page';
|
|
168
|
+
let src;
|
|
169
|
+
switch (c.kind) {
|
|
170
|
+
case 'testid':
|
|
171
|
+
src =
|
|
172
|
+
c.attr === 'data-testid'
|
|
173
|
+
? `${page}.getByTestId(${stringSource(c.value, o)})`
|
|
174
|
+
: `${page}.locator(${stringSource(`[${c.attr}=${JSON.stringify(c.value)}]`, o)})`;
|
|
175
|
+
break;
|
|
176
|
+
case 'role':
|
|
177
|
+
src = `${page}.getByRole(${quote(c.role)}, { name: ${matcherSource(c.name, o)}, exact: true })`;
|
|
178
|
+
break;
|
|
179
|
+
case 'label':
|
|
180
|
+
src = `${page}.getByLabel(${matcherSource(c.label, o)})`;
|
|
181
|
+
break;
|
|
182
|
+
case 'placeholder':
|
|
183
|
+
src = `${page}.getByPlaceholder(${matcherSource(c.placeholder, o)})`;
|
|
184
|
+
break;
|
|
185
|
+
case 'text':
|
|
186
|
+
src = `${page}.getByText(${matcherSource(c.text, o)}, { exact: true })`;
|
|
187
|
+
break;
|
|
188
|
+
case 'id':
|
|
189
|
+
case 'css':
|
|
190
|
+
src = `${page}.locator(${stringSource(c.selector, o)})`;
|
|
191
|
+
break;
|
|
192
|
+
case 'scoped':
|
|
193
|
+
// hasText stays a plain substring match on the container, exactly as
|
|
194
|
+
// makeLocator passes it: the recorded value names the RECORD.
|
|
195
|
+
src = `${page}.locator(${stringSource(c.container, o)}, { hasText: ${stringSource(c.hasText, o)} })`;
|
|
196
|
+
if (c.selector)
|
|
197
|
+
src += `.locator(${stringSource(c.selector, o)})`;
|
|
198
|
+
break;
|
|
199
|
+
case 'point':
|
|
200
|
+
// markPoint tags the element under the recorded coordinates at replay
|
|
201
|
+
// time; a plain spec has nothing to tag, so the candidate is dropped
|
|
202
|
+
// rather than approximated into a selector that names a position.
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
return c.nth !== undefined ? `${src}.nth(${c.nth})` : src;
|
|
206
|
+
}
|
|
207
|
+
/** The whole chain as ONE expression: candidates ordered by specOf (identity, handles,
|
|
208
|
+
* path), joined with .or(), point dropped. When the chain carries identity (a scoped
|
|
209
|
+
* candidate with hasText), every non-identity candidate is guarded with
|
|
210
|
+
* .filter({ hasText }) so a fallback cannot land on another record - mirrors
|
|
211
|
+
* ResolvePolicy.requireIdentity. Returns the source, the candidates that could not be
|
|
212
|
+
* expressed, and the hasText guards applied. Multi-line pretty form: one candidate per
|
|
213
|
+
* line, `.or(` continuation lines indented by `indent`. */
|
|
214
|
+
/** The chain as an ordered list of expressions, best first, with the identity guards applied. */
|
|
215
|
+
export function candidateSources(chain, o = {}) {
|
|
216
|
+
const spec = specOf(chain);
|
|
217
|
+
const ordered = [...spec.identity, ...spec.handles, ...spec.path];
|
|
218
|
+
const scoped = spec.identity;
|
|
219
|
+
const guards = [...new Set(scoped.map((c) => c.hasText).filter(Boolean))];
|
|
220
|
+
const dropped = [];
|
|
221
|
+
const applied = new Set();
|
|
222
|
+
const sources = [];
|
|
223
|
+
for (const c of ordered) {
|
|
224
|
+
let src = candidateSource(c, o);
|
|
225
|
+
if (src === null) {
|
|
226
|
+
dropped.push(c);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (c.kind !== 'scoped') {
|
|
230
|
+
// requireIdentity's rule: a value the candidate already carries needs no
|
|
231
|
+
// guard, everything else must still land on a node bearing the record's
|
|
232
|
+
// text. Unlike replay there is no exemption for the primary - a spec
|
|
233
|
+
// re-resolves nothing and has no recovery to fail over to, so a fallback
|
|
234
|
+
// that would silently work ANOTHER record must instead match nothing and
|
|
235
|
+
// let the step fail loudly.
|
|
236
|
+
const expr = JSON.stringify(c);
|
|
237
|
+
for (const g of guards.filter((v) => !expr.includes(v))) {
|
|
238
|
+
src += `.filter({ hasText: ${stringSource(g, o)} })`;
|
|
239
|
+
applied.add(g);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// A recorded chain routinely names the same element twice - an `id`
|
|
243
|
+
// candidate and the `css` candidate built from the same selector - and
|
|
244
|
+
// two identical expressions are two identical resolutions: no extra
|
|
245
|
+
// coverage, one more line for a reviewer to read past.
|
|
246
|
+
if (!sources.includes(src))
|
|
247
|
+
sources.push(src);
|
|
248
|
+
}
|
|
249
|
+
return { sources, dropped, identity: [...applied] };
|
|
250
|
+
}
|
|
251
|
+
/** The whole chain as ONE expression: candidates ordered by specOf (identity, handles,
|
|
252
|
+
* path), joined with .or(), point dropped. When the chain carries identity (a scoped
|
|
253
|
+
* candidate with hasText), every non-identity candidate is guarded with
|
|
254
|
+
* .filter({ hasText }) so a fallback cannot land on another record - mirrors
|
|
255
|
+
* ResolvePolicy.requireIdentity. Returns the source, the candidates that could not be
|
|
256
|
+
* expressed, and the hasText guards applied. Multi-line pretty form: one candidate per
|
|
257
|
+
* line, `.or(` continuation lines indented by `indent`.
|
|
258
|
+
*
|
|
259
|
+
* `.or()` is a UNION, so this expresses "any of these", not "the first of
|
|
260
|
+
* these that names exactly one element". That is right for a presence check
|
|
261
|
+
* and wrong for an action - see the emitter's `pick` helper.
|
|
262
|
+
*/
|
|
263
|
+
export function chainSource(chain, o = {}) {
|
|
264
|
+
const indent = o.indent ?? ' ';
|
|
265
|
+
const { sources, dropped, identity } = candidateSources(chain, o);
|
|
266
|
+
const source = sources.length ? sources[0] + sources.slice(1).map((p) => `
|
|
267
|
+
${indent}.or(${p})`).join('') : '';
|
|
268
|
+
return { source, dropped, identity };
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=locators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locators.js","sourceRoot":"","sources":["../../src/spec/locators.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuild one segment as a `Skill`.
|
|
3
|
+
*
|
|
4
|
+
* `id` and the procedure fields (template/params/preconditions/steps/derived)
|
|
5
|
+
* are copied straight from the segment — those are exactly what `toSegment`
|
|
6
|
+
* (ir.ts) kept, so copying them back is the whole round trip. Everything else
|
|
7
|
+
* is bookkeeping `toSegment` deliberately drops and this function has to
|
|
8
|
+
* invent: zeroed stats (a compiled spec carries no replay history), no
|
|
9
|
+
* fingerprint (the emitted spec never had one to begin with — a live DOM
|
|
10
|
+
* count vector needs a runtime, not a JSON literal), and a synthetic
|
|
11
|
+
* provenance naming the spec as its own source rather than a recording
|
|
12
|
+
* session.
|
|
13
|
+
*
|
|
14
|
+
* `seq` is set only when the step's procedure has more than one segment: a
|
|
15
|
+
* single-segment skill in the wild never carries `seq` (see `Skill.seq` and
|
|
16
|
+
* `chainOf` in ir.ts, which treats an absent `seq` as "this skill is its own
|
|
17
|
+
* one-member chain"), and giving every skill a `seq` here would desync from
|
|
18
|
+
* that. The chain id is the step's FIRST segment's id — stable, unique to
|
|
19
|
+
* this step (segment ids are unique across a store), and exactly what
|
|
20
|
+
* `chainOf` needs to find every member back by filtering the origin's skills
|
|
21
|
+
* on `seq.chain`.
|
|
22
|
+
*/
|
|
23
|
+
function toSkill(spec, step, seg, index, total, now) {
|
|
24
|
+
const skill = {
|
|
25
|
+
id: seg.id,
|
|
26
|
+
origin: spec.origin,
|
|
27
|
+
template: seg.template,
|
|
28
|
+
params: seg.params,
|
|
29
|
+
preconditions: seg.preconditions.requireText
|
|
30
|
+
? { urlPattern: seg.preconditions.urlPattern, requireText: seg.preconditions.requireText }
|
|
31
|
+
: { urlPattern: seg.preconditions.urlPattern },
|
|
32
|
+
steps: seg.steps,
|
|
33
|
+
stats: { uses: 0, successes: 0, partial: 0, created: now, failedAtStep: {}, fallthroughs: 0 },
|
|
34
|
+
status: 'validated',
|
|
35
|
+
provenance: { session: `spec:${spec.name}`, instruction: step.instruction, created: now },
|
|
36
|
+
};
|
|
37
|
+
if (seg.derived)
|
|
38
|
+
skill.derived = seg.derived;
|
|
39
|
+
// The goal and the report template it needs travel together, and both are
|
|
40
|
+
// part of the procedure rather than bookkeeping: a repair that lowered a
|
|
41
|
+
// spec, replayed it and re-emitted would otherwise drop the "already
|
|
42
|
+
// satisfied" guard on the way through.
|
|
43
|
+
if (seg.goal)
|
|
44
|
+
skill.goal = seg.goal;
|
|
45
|
+
if (seg.report)
|
|
46
|
+
skill.reportTemplate = seg.report;
|
|
47
|
+
if (total > 1)
|
|
48
|
+
skill.seq = { chain: step.segments[0].id, index, of: total };
|
|
49
|
+
return skill;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Rebuild one `FlowStep`. `skill` points at the first segment's id (index 0)
|
|
53
|
+
* — matching `Skill.seq`'s own doc: "Matching/selection always starts at
|
|
54
|
+
* index 0" — and `chainOf` walks the rest of the chain from there. A step
|
|
55
|
+
* whose spec carried no segments (no converged procedure — `flowToSpec`
|
|
56
|
+
* still exports the shape and warns) gets no `skill` at all, same as a Flow
|
|
57
|
+
* step that never had one.
|
|
58
|
+
*
|
|
59
|
+
* `recorded` and `outputEvidence` are cross-run evidence `SpecStep` never
|
|
60
|
+
* carried in the first place (ir.ts's `SpecStep` has no field for them), so
|
|
61
|
+
* they come back empty/absent rather than fabricated — an empty `recorded`
|
|
62
|
+
* is indistinguishable, to every consumer, from "this run observed nothing
|
|
63
|
+
* yet", which is the truth for a freshly staged spec.
|
|
64
|
+
*/
|
|
65
|
+
function toFlowStep(spec, step, now, skills) {
|
|
66
|
+
const segSkills = step.segments.map((seg, i) => toSkill(spec, step, seg, i, step.segments.length, now));
|
|
67
|
+
skills.push(...segSkills);
|
|
68
|
+
const flowStep = {
|
|
69
|
+
id: step.id,
|
|
70
|
+
instruction: step.instruction,
|
|
71
|
+
params: step.params,
|
|
72
|
+
outputs: step.outputs,
|
|
73
|
+
recorded: {},
|
|
74
|
+
};
|
|
75
|
+
if (segSkills.length)
|
|
76
|
+
flowStep.skill = segSkills[0].id;
|
|
77
|
+
return flowStep;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A lifted spec as the Flow + Skill[] the replay/repair code understands.
|
|
81
|
+
*
|
|
82
|
+
* Segments become skills that keep their ids, seq (chain = the flow step's
|
|
83
|
+
* first segment id, index/of from position), preconditions (no fingerprint),
|
|
84
|
+
* steps, derived, params; stats start at zero, status 'validated' (a
|
|
85
|
+
* compiled spec is by definition converged), provenance from the spec.
|
|
86
|
+
*
|
|
87
|
+
* Mirrors `flowToSpec`'s own ordering exactly (same per-step segment order,
|
|
88
|
+
* same chain-id choice) so that `flowToSpec(specToFlow(spec).flow, store)`
|
|
89
|
+
* reproduces `spec` once the returned skills are in a store `flowToSpec` can
|
|
90
|
+
* read — see `stageForReplay`, and PLAN-self-updating-spec.md's round-trip
|
|
91
|
+
* invariant.
|
|
92
|
+
*/
|
|
93
|
+
export function specToFlow(spec) {
|
|
94
|
+
const now = new Date().toISOString();
|
|
95
|
+
const skills = [];
|
|
96
|
+
const steps = spec.steps.map((step) => toFlowStep(spec, step, now, skills));
|
|
97
|
+
const flow = {
|
|
98
|
+
name: spec.name,
|
|
99
|
+
origin: spec.origin,
|
|
100
|
+
startUrl: spec.startUrl,
|
|
101
|
+
vars: spec.vars,
|
|
102
|
+
steps,
|
|
103
|
+
provenance: { session: `spec:${spec.name}`, created: now },
|
|
104
|
+
};
|
|
105
|
+
return { flow, skills };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Write the skills into a store (an isolated temp store is the normal use)
|
|
109
|
+
* and return the flow, ready for `run`.
|
|
110
|
+
*
|
|
111
|
+
* A compiled spec has no store of its own — the spec IS the store, frozen —
|
|
112
|
+
* so `repair` cannot just point the existing replay/repair machinery at
|
|
113
|
+
* `~/.sitelooper/skills` (that store may not even have this origin, or may
|
|
114
|
+
* have moved on since the spec was emitted). Staging into a scratch
|
|
115
|
+
* `SkillStore` gives that machinery the on-disk shape it already expects
|
|
116
|
+
* without teaching it a second, spec-native code path.
|
|
117
|
+
*/
|
|
118
|
+
export function stageForReplay(spec, store) {
|
|
119
|
+
const { flow, skills } = specToFlow(spec);
|
|
120
|
+
for (const skill of skills)
|
|
121
|
+
store.put(skill);
|
|
122
|
+
return flow;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=lower.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lower.js","sourceRoot":"","sources":["../../src/spec/lower.ts"],"names":[],"mappings":"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"}
|