@wenathlan/extension 1.1.50 → 1.1.51
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 +6 -6
- package/dist/controlflow.d.ts +220 -0
- package/dist/controlflow.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2929 -2106
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +7 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +1 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +22 -5
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +154 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/workflow.d.ts +12 -3
- package/dist/workflow.d.ts.map +1 -1
- package/extension/dist/background.js +964 -38
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +8 -3
- package/extension/dist/pagebridge.js.map +2 -2
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +6 -5
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.js +110 -3
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -2191,6 +2191,26 @@ var sessionmemory = class {
|
|
|
2191
2191
|
async getsteptemplates() {
|
|
2192
2192
|
return await this.adapter.get("steptemplates") ?? [];
|
|
2193
2193
|
}
|
|
2194
|
+
/** Records one control flow decision of a run — a branch choice with its reason, the loop counters of an iteration trail, the retry attempts with their backoff durations, the timeout aborts with the exceeded budget, the join record with its strategy and conflicts or the catch handler execution — so the audit trail keeps every control flow turn. */
|
|
2195
|
+
async addcontroldecision(runid, decision) {
|
|
2196
|
+
const decisions = await this.listcontroldecisions(runid);
|
|
2197
|
+
await this.adapter.set(`controldecisions${runid}`, [...decisions, { ...decision, runid }]);
|
|
2198
|
+
}
|
|
2199
|
+
/** Returns every stored control flow decision of one run, oldest first. */
|
|
2200
|
+
async listcontroldecisions(runid) {
|
|
2201
|
+
return await this.adapter.get(`controldecisions${runid}`) ?? [];
|
|
2202
|
+
}
|
|
2203
|
+
/** Returns the past branch decisions of one workflow across every stored run, oldest first, so review can compare branch paths over time. */
|
|
2204
|
+
async getbranchhistory(workflowid) {
|
|
2205
|
+
const runs = await this.listworkflowruns();
|
|
2206
|
+
const ordered = [...runs].reverse().filter((run) => run.workflowid === workflowid);
|
|
2207
|
+
const history2 = [];
|
|
2208
|
+
for (const run of ordered) {
|
|
2209
|
+
const decisions = await this.listcontroldecisions(run.id);
|
|
2210
|
+
for (const decision of decisions) if (decision.kind === "branch" && decision.branch !== void 0) history2.push(decision.branch);
|
|
2211
|
+
}
|
|
2212
|
+
return history2;
|
|
2213
|
+
}
|
|
2194
2214
|
};
|
|
2195
2215
|
function mediakindof(record2) {
|
|
2196
2216
|
if ("pages" in record2) return "pdf";
|
|
@@ -3177,6 +3197,678 @@ function teardowncdpsession(input) {
|
|
|
3177
3197
|
};
|
|
3178
3198
|
}
|
|
3179
3199
|
|
|
3200
|
+
// controlflow.ts
|
|
3201
|
+
var controlflowkinds = ["condition", "branch", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"];
|
|
3202
|
+
var defaultloopbound = 1e3;
|
|
3203
|
+
function iscontrolflowkind(kind) {
|
|
3204
|
+
return controlflowkinds.includes(kind);
|
|
3205
|
+
}
|
|
3206
|
+
var cancellederror = class extends Error {
|
|
3207
|
+
constructor(message) {
|
|
3208
|
+
super(message);
|
|
3209
|
+
this.name = "cancellederror";
|
|
3210
|
+
}
|
|
3211
|
+
};
|
|
3212
|
+
function controlname(value) {
|
|
3213
|
+
return typeof value === "string" && /^[a-z][a-z0-9]*$/.test(value) ? value : void 0;
|
|
3214
|
+
}
|
|
3215
|
+
function controlstepslist(value) {
|
|
3216
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
3217
|
+
const steps = [];
|
|
3218
|
+
for (const entry of value) {
|
|
3219
|
+
const parsed = workflowstepof(entry);
|
|
3220
|
+
if (!parsed) return void 0;
|
|
3221
|
+
steps.push(parsed);
|
|
3222
|
+
}
|
|
3223
|
+
return steps;
|
|
3224
|
+
}
|
|
3225
|
+
function controlbound(value) {
|
|
3226
|
+
if (value === void 0) return void 0;
|
|
3227
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
3228
|
+
}
|
|
3229
|
+
function conditionof(value) {
|
|
3230
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3231
|
+
const candidate = value;
|
|
3232
|
+
const expression = expressionof(candidate.expression);
|
|
3233
|
+
if (!expression) return void 0;
|
|
3234
|
+
if (expression.resultkind !== "boolean") return void 0;
|
|
3235
|
+
return { expression };
|
|
3236
|
+
}
|
|
3237
|
+
function elseof(value) {
|
|
3238
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3239
|
+
const candidate = value;
|
|
3240
|
+
const name = controlname(candidate.name);
|
|
3241
|
+
if (!name) return void 0;
|
|
3242
|
+
if (candidate.when !== void 0) return void 0;
|
|
3243
|
+
if (!Array.isArray(candidate.steps)) return void 0;
|
|
3244
|
+
const steps = [];
|
|
3245
|
+
for (const entry of candidate.steps) {
|
|
3246
|
+
const parsed = workflowstepof(entry);
|
|
3247
|
+
if (!parsed) return void 0;
|
|
3248
|
+
steps.push(parsed);
|
|
3249
|
+
}
|
|
3250
|
+
return { name, steps };
|
|
3251
|
+
}
|
|
3252
|
+
function branchof(value) {
|
|
3253
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3254
|
+
const candidate = value;
|
|
3255
|
+
if (!Array.isArray(candidate.paths) || candidate.paths.length === 0) return void 0;
|
|
3256
|
+
const paths = [];
|
|
3257
|
+
for (const entry of candidate.paths) {
|
|
3258
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return void 0;
|
|
3259
|
+
const path = entry;
|
|
3260
|
+
const name = controlname(path.name);
|
|
3261
|
+
if (!name) return void 0;
|
|
3262
|
+
const when = path.when === void 0 ? void 0 : expressionof(path.when);
|
|
3263
|
+
if (path.when !== void 0 && when === void 0) return void 0;
|
|
3264
|
+
if (when !== void 0 && when.resultkind !== "boolean") return void 0;
|
|
3265
|
+
const steps = controlstepslist(path.steps);
|
|
3266
|
+
if (!steps) return void 0;
|
|
3267
|
+
paths.push({ name, ...when !== void 0 ? { when } : {}, steps });
|
|
3268
|
+
}
|
|
3269
|
+
const names = paths.map((path) => path.name);
|
|
3270
|
+
if (new Set(names).size !== names.length) return void 0;
|
|
3271
|
+
const elsepath = elseof(candidate.else);
|
|
3272
|
+
if (!elsepath) return void 0;
|
|
3273
|
+
if (names.includes(elsepath.name)) return void 0;
|
|
3274
|
+
return { paths, else: elsepath };
|
|
3275
|
+
}
|
|
3276
|
+
function loopof(value) {
|
|
3277
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3278
|
+
const candidate = value;
|
|
3279
|
+
const list = controlname(candidate.list);
|
|
3280
|
+
const item = controlname(candidate.item);
|
|
3281
|
+
const index = controlname(candidate.index);
|
|
3282
|
+
if (!list || !item || !index) return void 0;
|
|
3283
|
+
if (item === list || index === list || item === index) return void 0;
|
|
3284
|
+
const bound = controlbound(candidate.bound);
|
|
3285
|
+
if (candidate.bound !== void 0 && bound === void 0) return void 0;
|
|
3286
|
+
const steps = controlstepslist(candidate.steps);
|
|
3287
|
+
if (!steps) return void 0;
|
|
3288
|
+
return { list, item, index, ...bound !== void 0 ? { bound } : {}, steps };
|
|
3289
|
+
}
|
|
3290
|
+
function repeatuntilof(value) {
|
|
3291
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3292
|
+
const candidate = value;
|
|
3293
|
+
const until = expressionof(candidate.until);
|
|
3294
|
+
if (!until || until.resultkind !== "boolean") return void 0;
|
|
3295
|
+
const bound = controlbound(candidate.bound);
|
|
3296
|
+
if (candidate.bound !== void 0 && bound === void 0) return void 0;
|
|
3297
|
+
const steps = controlstepslist(candidate.steps);
|
|
3298
|
+
if (!steps) return void 0;
|
|
3299
|
+
return { until, ...bound !== void 0 ? { bound } : {}, steps };
|
|
3300
|
+
}
|
|
3301
|
+
function whileof(value) {
|
|
3302
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3303
|
+
const candidate = value;
|
|
3304
|
+
const condition = expressionof(candidate.while);
|
|
3305
|
+
if (!condition || condition.resultkind !== "boolean") return void 0;
|
|
3306
|
+
const bound = controlbound(candidate.bound);
|
|
3307
|
+
if (bound === void 0) return void 0;
|
|
3308
|
+
const steps = controlstepslist(candidate.steps);
|
|
3309
|
+
if (!steps) return void 0;
|
|
3310
|
+
return { while: condition, bound, steps };
|
|
3311
|
+
}
|
|
3312
|
+
function foreachof(value) {
|
|
3313
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3314
|
+
const candidate = value;
|
|
3315
|
+
if (typeof candidate.selector !== "string" || !candidate.selector.trim()) return void 0;
|
|
3316
|
+
const item = controlname(candidate.item);
|
|
3317
|
+
const index = controlname(candidate.index);
|
|
3318
|
+
if (!item || !index || item === index) return void 0;
|
|
3319
|
+
const steps = controlstepslist(candidate.steps);
|
|
3320
|
+
if (!steps) return void 0;
|
|
3321
|
+
return { selector: candidate.selector, item, index, steps };
|
|
3322
|
+
}
|
|
3323
|
+
function parallelof(value) {
|
|
3324
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3325
|
+
const candidate = value;
|
|
3326
|
+
if (!Array.isArray(candidate.branches) || candidate.branches.length === 0) return void 0;
|
|
3327
|
+
const branches = [];
|
|
3328
|
+
for (const entry of candidate.branches) {
|
|
3329
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return void 0;
|
|
3330
|
+
const branch = entry;
|
|
3331
|
+
const id = controlname(branch.id);
|
|
3332
|
+
if (!id) return void 0;
|
|
3333
|
+
const steps = controlstepslist(branch.steps);
|
|
3334
|
+
if (!steps) return void 0;
|
|
3335
|
+
branches.push({ id, steps });
|
|
3336
|
+
}
|
|
3337
|
+
if (new Set(branches.map((branch) => branch.id)).size !== branches.length) return void 0;
|
|
3338
|
+
const join = candidate.join && typeof candidate.join === "object" && !Array.isArray(candidate.join) ? candidate.join : void 0;
|
|
3339
|
+
if (!join) return void 0;
|
|
3340
|
+
if (join.strategy !== "first" && join.strategy !== "last" && join.strategy !== "fail") return void 0;
|
|
3341
|
+
if (join.onfail !== "cancel" && join.onfail !== "continue") return void 0;
|
|
3342
|
+
return { branches, join: { strategy: join.strategy, onfail: join.onfail } };
|
|
3343
|
+
}
|
|
3344
|
+
function tryof(value) {
|
|
3345
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
3346
|
+
const candidate = value;
|
|
3347
|
+
const steps = controlstepslist(candidate.steps);
|
|
3348
|
+
if (!steps) return void 0;
|
|
3349
|
+
const catchcandidate = candidate.catch && typeof candidate.catch === "object" && !Array.isArray(candidate.catch) ? candidate.catch : void 0;
|
|
3350
|
+
if (!catchcandidate) return void 0;
|
|
3351
|
+
const catchsteps = controlstepslist(catchcandidate.steps);
|
|
3352
|
+
if (!catchsteps) return void 0;
|
|
3353
|
+
if (catchcandidate.rerun !== void 0 && typeof catchcandidate.rerun !== "boolean") return void 0;
|
|
3354
|
+
const catchvalue = { steps: catchsteps, ...catchcandidate.rerun === true ? { rerun: true } : {} };
|
|
3355
|
+
let retry;
|
|
3356
|
+
if (candidate.retry !== void 0) {
|
|
3357
|
+
const retrycandidate = candidate.retry && typeof candidate.retry === "object" && !Array.isArray(candidate.retry) ? candidate.retry : void 0;
|
|
3358
|
+
if (!retrycandidate) return void 0;
|
|
3359
|
+
if (typeof retrycandidate.attempts !== "number" || !Number.isInteger(retrycandidate.attempts) || retrycandidate.attempts < 1) return void 0;
|
|
3360
|
+
const backoff = retrycandidate.backoff && typeof retrycandidate.backoff === "object" && !Array.isArray(retrycandidate.backoff) ? retrycandidate.backoff : void 0;
|
|
3361
|
+
if (!backoff) return void 0;
|
|
3362
|
+
if (backoff.shape !== "fixed" && backoff.shape !== "exponential") return void 0;
|
|
3363
|
+
if (typeof backoff.base !== "number" || !Number.isFinite(backoff.base) || backoff.base < 0) return void 0;
|
|
3364
|
+
if (typeof backoff.jitter !== "number" || !Number.isFinite(backoff.jitter) || backoff.jitter < 0) return void 0;
|
|
3365
|
+
if (!Array.isArray(retrycandidate.retryable) || !retrycandidate.retryable.every((entry) => typeof entry === "string" && entry.trim())) return void 0;
|
|
3366
|
+
retry = { attempts: retrycandidate.attempts, backoff: { shape: backoff.shape, base: backoff.base, jitter: backoff.jitter }, retryable: retrycandidate.retryable };
|
|
3367
|
+
}
|
|
3368
|
+
let timeout;
|
|
3369
|
+
if (candidate.timeout !== void 0) {
|
|
3370
|
+
const timeoutcandidate = candidate.timeout && typeof candidate.timeout === "object" && !Array.isArray(candidate.timeout) ? candidate.timeout : void 0;
|
|
3371
|
+
if (!timeoutcandidate) return void 0;
|
|
3372
|
+
const stepms = timeoutcandidate.stepms === void 0 ? void 0 : typeof timeoutcandidate.stepms === "number" && Number.isFinite(timeoutcandidate.stepms) && timeoutcandidate.stepms > 0 ? timeoutcandidate.stepms : void 0;
|
|
3373
|
+
const runms = timeoutcandidate.runms === void 0 ? void 0 : typeof timeoutcandidate.runms === "number" && Number.isFinite(timeoutcandidate.runms) && timeoutcandidate.runms > 0 ? timeoutcandidate.runms : void 0;
|
|
3374
|
+
if (stepms === void 0 && runms === void 0) return void 0;
|
|
3375
|
+
if (timeoutcandidate.stepms !== void 0 && stepms === void 0) return void 0;
|
|
3376
|
+
if (timeoutcandidate.runms !== void 0 && runms === void 0) return void 0;
|
|
3377
|
+
timeout = { ...stepms !== void 0 ? { stepms } : {}, ...runms !== void 0 ? { runms } : {} };
|
|
3378
|
+
}
|
|
3379
|
+
return { steps, catch: catchvalue, ...retry !== void 0 ? { retry } : {}, ...timeout !== void 0 ? { timeout } : {} };
|
|
3380
|
+
}
|
|
3381
|
+
function controloptions(step) {
|
|
3382
|
+
if (step.options === void 0) throw new Error(`The ${step.kind} step needs its reviewed control payload in options.`);
|
|
3383
|
+
let parsed;
|
|
3384
|
+
try {
|
|
3385
|
+
parsed = JSON.parse(step.options);
|
|
3386
|
+
} catch {
|
|
3387
|
+
throw new Error(`The ${step.kind} control payload must be a JSON object.`);
|
|
3388
|
+
}
|
|
3389
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`The ${step.kind} control payload must be a JSON object.`);
|
|
3390
|
+
return parsed;
|
|
3391
|
+
}
|
|
3392
|
+
function validatecontrolpayload(step) {
|
|
3393
|
+
if (!iscontrolflowkind(step.kind)) return;
|
|
3394
|
+
const payload = controloptions(step);
|
|
3395
|
+
if (step.kind === "condition" && conditionof(payload.condition) === void 0) throw new Error("The condition step needs a reviewed boolean expression in its options.");
|
|
3396
|
+
if (step.kind === "branch" && branchof(payload.branch) === void 0) throw new Error("The branch step needs reviewed unique paths with boolean match expressions and an else path in its options.");
|
|
3397
|
+
if (step.kind === "loop" && loopof(payload.loop) === void 0) throw new Error("The loop step needs a reviewed list variable, distinct item and index variables, an optional positive safety bound and a non-empty body in its options.");
|
|
3398
|
+
if (step.kind === "repeatuntil" && repeatuntilof(payload.repeatuntil) === void 0) throw new Error("The repeat until step needs a reviewed convergence expression, an optional positive safety bound and a non-empty body in its options.");
|
|
3399
|
+
if (step.kind === "whileloop" && whileof(payload.while) === void 0) throw new Error("The while step needs a reviewed condition, a mandatory positive safety bound and a non-empty body in its options.");
|
|
3400
|
+
if (step.kind === "foreach" && foreachof(payload.foreach) === void 0) throw new Error("The foreach step needs a reviewed non-empty selector, distinct item and index variables and a non-empty body in its options.");
|
|
3401
|
+
if (step.kind === "parallel" && parallelof(payload.parallel) === void 0) throw new Error("The parallel step needs uniquely identified branches with bodies and a join policy of the first, last or fail strategy with cancel or continue on branch failure in its options.");
|
|
3402
|
+
if (step.kind === "trycatch" && tryof(payload.try) === void 0) throw new Error("The try step needs a fragile body, a catch handler and optional retry and timeout policies in its options.");
|
|
3403
|
+
}
|
|
3404
|
+
function controlsteps(step) {
|
|
3405
|
+
if (!iscontrolflowkind(step.kind)) return [];
|
|
3406
|
+
let payload;
|
|
3407
|
+
try {
|
|
3408
|
+
payload = controloptions(step);
|
|
3409
|
+
} catch {
|
|
3410
|
+
return [];
|
|
3411
|
+
}
|
|
3412
|
+
const children = [];
|
|
3413
|
+
const collect = (steps) => {
|
|
3414
|
+
for (const child of steps) {
|
|
3415
|
+
children.push(child);
|
|
3416
|
+
collect(controlsteps(child));
|
|
3417
|
+
}
|
|
3418
|
+
};
|
|
3419
|
+
if (step.kind === "condition") return children;
|
|
3420
|
+
if (step.kind === "branch") {
|
|
3421
|
+
const branch = branchof(payload.branch);
|
|
3422
|
+
if (!branch) return children;
|
|
3423
|
+
for (const path of branch.paths) collect(path.steps);
|
|
3424
|
+
collect(branch.else.steps);
|
|
3425
|
+
return children;
|
|
3426
|
+
}
|
|
3427
|
+
if (step.kind === "loop") {
|
|
3428
|
+
const loop = loopof(payload.loop);
|
|
3429
|
+
if (loop) collect(loop.steps);
|
|
3430
|
+
return children;
|
|
3431
|
+
}
|
|
3432
|
+
if (step.kind === "repeatuntil") {
|
|
3433
|
+
const repeat = repeatuntilof(payload.repeatuntil);
|
|
3434
|
+
if (repeat) collect(repeat.steps);
|
|
3435
|
+
return children;
|
|
3436
|
+
}
|
|
3437
|
+
if (step.kind === "whileloop") {
|
|
3438
|
+
const condition = whileof(payload.while);
|
|
3439
|
+
if (condition) collect(condition.steps);
|
|
3440
|
+
return children;
|
|
3441
|
+
}
|
|
3442
|
+
if (step.kind === "foreach") {
|
|
3443
|
+
const foreach = foreachof(payload.foreach);
|
|
3444
|
+
if (foreach) collect(foreach.steps);
|
|
3445
|
+
return children;
|
|
3446
|
+
}
|
|
3447
|
+
if (step.kind === "parallel") {
|
|
3448
|
+
const parallel = parallelof(payload.parallel);
|
|
3449
|
+
if (parallel) for (const branch of parallel.branches) collect(branch.steps);
|
|
3450
|
+
return children;
|
|
3451
|
+
}
|
|
3452
|
+
const fragile = tryof(payload.try);
|
|
3453
|
+
if (fragile) {
|
|
3454
|
+
collect(fragile.steps);
|
|
3455
|
+
collect(fragile.catch.steps);
|
|
3456
|
+
}
|
|
3457
|
+
return children;
|
|
3458
|
+
}
|
|
3459
|
+
function controlsummary(step) {
|
|
3460
|
+
if (!iscontrolflowkind(step.kind)) return void 0;
|
|
3461
|
+
let payload;
|
|
3462
|
+
try {
|
|
3463
|
+
payload = controloptions(step);
|
|
3464
|
+
} catch {
|
|
3465
|
+
return { kind: step.kind };
|
|
3466
|
+
}
|
|
3467
|
+
if (step.kind === "condition") {
|
|
3468
|
+
const condition = conditionof(payload.condition);
|
|
3469
|
+
return { kind: step.kind, ...condition ? { expression: `${condition.expression.operator} into ${condition.expression.result}` } : {} };
|
|
3470
|
+
}
|
|
3471
|
+
if (step.kind === "branch") {
|
|
3472
|
+
const branch = branchof(payload.branch);
|
|
3473
|
+
return { kind: step.kind, ...branch ? { paths: branch.paths.map((path) => path.name), elsepath: branch.else.name } : {} };
|
|
3474
|
+
}
|
|
3475
|
+
if (step.kind === "loop") {
|
|
3476
|
+
const loop = loopof(payload.loop);
|
|
3477
|
+
return { kind: step.kind, ...loop ? { list: loop.list, item: loop.item, index: loop.index, ...loop.bound !== void 0 ? { bound: loop.bound } : { bound: defaultloopbound } } : {} };
|
|
3478
|
+
}
|
|
3479
|
+
if (step.kind === "repeatuntil") {
|
|
3480
|
+
const repeat = repeatuntilof(payload.repeatuntil);
|
|
3481
|
+
return { kind: step.kind, ...repeat ? { bound: repeat.bound ?? defaultloopbound } : {} };
|
|
3482
|
+
}
|
|
3483
|
+
if (step.kind === "whileloop") {
|
|
3484
|
+
const condition = whileof(payload.while);
|
|
3485
|
+
return { kind: step.kind, ...condition ? { bound: condition.bound } : {} };
|
|
3486
|
+
}
|
|
3487
|
+
if (step.kind === "foreach") {
|
|
3488
|
+
const foreach = foreachof(payload.foreach);
|
|
3489
|
+
return { kind: step.kind, ...foreach ? { selector: foreach.selector, item: foreach.item, index: foreach.index } : {} };
|
|
3490
|
+
}
|
|
3491
|
+
if (step.kind === "parallel") {
|
|
3492
|
+
const parallel = parallelof(payload.parallel);
|
|
3493
|
+
return { kind: step.kind, ...parallel ? { branches: parallel.branches.map((branch) => branch.id), strategy: parallel.join.strategy, onfail: parallel.join.onfail } : {} };
|
|
3494
|
+
}
|
|
3495
|
+
const fragile = tryof(payload.try);
|
|
3496
|
+
return { kind: step.kind, ...fragile ? { ...fragile.retry !== void 0 ? { attempts: fragile.retry.attempts, backoff: `${fragile.retry.backoff.shape} base ${fragile.retry.backoff.base} jitter ${fragile.retry.backoff.jitter}` } : {}, ...fragile.catch.rerun === true ? { rerun: true } : {}, ...fragile.timeout?.stepms !== void 0 ? { stepms: fragile.timeout.stepms } : {}, ...fragile.timeout?.runms !== void 0 ? { runms: fragile.timeout.runms } : {} } : {} };
|
|
3497
|
+
}
|
|
3498
|
+
function evaluatecondition(condition, scopes) {
|
|
3499
|
+
const value = expressioneval(condition.expression, scopes);
|
|
3500
|
+
if (typeof value !== "boolean") throw new Error("The condition expression must resolve to a boolean.");
|
|
3501
|
+
return value;
|
|
3502
|
+
}
|
|
3503
|
+
function choosebranch(input) {
|
|
3504
|
+
let scopes = input.scopes;
|
|
3505
|
+
if (input.pagestate !== void 0) {
|
|
3506
|
+
const parent = scopes.length > 0 ? scopes[scopes.length - 1].name : void 0;
|
|
3507
|
+
scopes = pushscope(scopes, `pagestate${input.stepid}`, parent);
|
|
3508
|
+
if (input.pagestate.url !== void 0) scopes = setvariable(scopes, "pageurl", "string", input.pagestate.url, input.now);
|
|
3509
|
+
if (input.pagestate.title !== void 0) scopes = setvariable(scopes, "pagetitle", "string", input.pagestate.title, input.now);
|
|
3510
|
+
if (input.pagestate.ready !== void 0) scopes = setvariable(scopes, "pageready", "boolean", input.pagestate.ready, input.now);
|
|
3511
|
+
}
|
|
3512
|
+
for (const path of input.branch.paths) {
|
|
3513
|
+
if (path.when === void 0) return { outcome: { stepid: input.stepid, path: path.name, reason: `The path ${path.name} matches unconditionally.`, at: input.now }, steps: path.steps };
|
|
3514
|
+
const value = expressioneval(path.when, scopes);
|
|
3515
|
+
if (typeof value !== "boolean") throw new Error(`The branch path ${path.name} needs a boolean expression.`);
|
|
3516
|
+
if (value) return { outcome: { stepid: input.stepid, path: path.name, reason: `The condition of the path ${path.name} holds.`, at: input.now }, steps: path.steps };
|
|
3517
|
+
}
|
|
3518
|
+
return { outcome: { stepid: input.stepid, path: input.branch.else.name, reason: "No path condition held and the else path ran.", at: input.now }, steps: input.branch.else.steps };
|
|
3519
|
+
}
|
|
3520
|
+
async function runbody(input) {
|
|
3521
|
+
let scopes = input.scopes;
|
|
3522
|
+
const outputs = { ...input.outputs };
|
|
3523
|
+
const log = [];
|
|
3524
|
+
for (const child of input.steps) {
|
|
3525
|
+
if (iscontrolflowkind(child.kind)) {
|
|
3526
|
+
const result = await runcontrolstep({ step: child, scopes, outputs, execute: input.execute, now: input.now, ...input.path !== void 0 ? { path: `${input.path}.${child.id}` } : {} });
|
|
3527
|
+
scopes = result.scopes;
|
|
3528
|
+
log.push(...result.log);
|
|
3529
|
+
outputs[child.id] = { stepid: child.id, ok: result.output.ok, summary: result.output.summary, ...result.output.details !== void 0 ? { details: result.output.details } : {}, at: input.now };
|
|
3530
|
+
if (!result.output.ok) return { ok: false, scopes, log, outputs, failure: result.output };
|
|
3531
|
+
continue;
|
|
3532
|
+
}
|
|
3533
|
+
const executed = await runstep({ step: child, scopes, outputs, execute: input.execute, now: input.now });
|
|
3534
|
+
scopes = executed.scopes;
|
|
3535
|
+
if (executed.childlog !== void 0) log.push(...executed.childlog);
|
|
3536
|
+
log.push(executed.log);
|
|
3537
|
+
outputs[child.id] = { stepid: child.id, ok: executed.output.ok, summary: executed.output.summary, ...executed.output.details !== void 0 ? { details: executed.output.details } : {}, at: input.now };
|
|
3538
|
+
if (!executed.output.ok) return { ok: false, scopes, log, outputs, failure: executed.output };
|
|
3539
|
+
}
|
|
3540
|
+
return { ok: true, scopes, log, outputs };
|
|
3541
|
+
}
|
|
3542
|
+
function deepcopy(value) {
|
|
3543
|
+
if (Array.isArray(value)) return value.map(deepcopy);
|
|
3544
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, deepcopy(entry)]));
|
|
3545
|
+
return value;
|
|
3546
|
+
}
|
|
3547
|
+
function iterationentry(step, iteration, total, ok, now) {
|
|
3548
|
+
return { stepid: step.id, label: `${step.label} iteration ${iteration + 1}`, state: ok ? "done" : "failed", startedat: now, duration: 0, summary: `Iteration ${iteration + 1} of ${total}.`, details: { iteration, total } };
|
|
3549
|
+
}
|
|
3550
|
+
async function runloop(input) {
|
|
3551
|
+
const list = resolvevariable(input.scopes, input.loop.list);
|
|
3552
|
+
if (!list) throw new Error(`The loop references the undefined list variable ${input.loop.list}.`);
|
|
3553
|
+
if (list.kind !== "list") throw new Error(`The loop variable ${input.loop.list} is not a list.`);
|
|
3554
|
+
const items = list.value;
|
|
3555
|
+
const bound = input.loop.bound ?? defaultloopbound;
|
|
3556
|
+
const loops = [];
|
|
3557
|
+
const decision = { runid: "", stepid: input.step.id, kind: "loop", at: input.now, loops };
|
|
3558
|
+
if (items.length > bound) {
|
|
3559
|
+
return { ok: false, scopes: input.scopes, log: [], summary: `The loop list holds ${items.length} items and exceeds the reviewed safety bound of ${bound} iterations; nothing ran.`, decision };
|
|
3560
|
+
}
|
|
3561
|
+
let scopes = input.scopes;
|
|
3562
|
+
const log = [];
|
|
3563
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
3564
|
+
scopes = setvariable(scopes, input.loop.item, "string", deepcopy(items[index]), input.now);
|
|
3565
|
+
scopes = setvariable(scopes, input.loop.index, "number", index, input.now);
|
|
3566
|
+
const body = await runbody({ step: input.step, scopes, outputs: input.outputs, execute: input.execute, now: input.now, steps: input.loop.steps, path: `${input.path ?? input.step.id}[${index}]` });
|
|
3567
|
+
scopes = body.scopes;
|
|
3568
|
+
const ok = body.ok;
|
|
3569
|
+
loops.push({ stepid: input.step.id, path: `${input.path ?? input.step.id}[${index}]`, iteration: index, ok, at: input.now });
|
|
3570
|
+
log.push(...body.log, iterationentry(input.step, index, items.length, ok, input.now));
|
|
3571
|
+
if (!ok) return { ok: false, scopes, log, summary: `The loop failed at iteration ${index + 1} of ${items.length}: ${body.failure?.summary ?? "the body step failed."}`, decision };
|
|
3572
|
+
}
|
|
3573
|
+
return { ok: true, scopes, log, summary: `The loop ran ${items.length} iteration${items.length === 1 ? "" : "s"} over ${input.loop.list} inside the reviewed safety bound of ${bound}.`, decision };
|
|
3574
|
+
}
|
|
3575
|
+
async function runrepeatuntil(input) {
|
|
3576
|
+
const bound = input.repeat.bound ?? defaultloopbound;
|
|
3577
|
+
let scopes = input.scopes;
|
|
3578
|
+
const log = [];
|
|
3579
|
+
const loops = [];
|
|
3580
|
+
const decision = { runid: "", stepid: input.step.id, kind: "loop", at: input.now, loops };
|
|
3581
|
+
for (let iteration = 0; iteration < bound; iteration += 1) {
|
|
3582
|
+
const body = await runbody({ step: input.step, scopes, outputs: input.outputs, execute: input.execute, now: input.now, steps: input.repeat.steps, path: `${input.path ?? input.step.id}[${iteration}]` });
|
|
3583
|
+
scopes = body.scopes;
|
|
3584
|
+
log.push(...body.log);
|
|
3585
|
+
const converged = evaluatecondition({ expression: input.repeat.until }, scopes);
|
|
3586
|
+
loops.push({ stepid: input.step.id, path: `${input.path ?? input.step.id}[${iteration}]`, iteration, ok: body.ok, at: input.now });
|
|
3587
|
+
if (!body.ok) return { ok: false, scopes, log, summary: `The repeat until failed at iteration ${iteration + 1}: ${body.failure?.summary ?? "the body step failed."}`, decision };
|
|
3588
|
+
log.push(iterationentry(input.step, iteration, bound, true, input.now));
|
|
3589
|
+
if (converged) return { ok: true, scopes, log, summary: `The repeat until converged after ${iteration + 1} iteration${iteration === 0 ? "" : "s"} inside the reviewed safety bound of ${bound}.`, decision };
|
|
3590
|
+
}
|
|
3591
|
+
return { ok: false, scopes, log, summary: `The repeat until never converged within the reviewed safety bound of ${bound} iterations.`, decision };
|
|
3592
|
+
}
|
|
3593
|
+
async function runwhile(input) {
|
|
3594
|
+
let scopes = input.scopes;
|
|
3595
|
+
const log = [];
|
|
3596
|
+
const loops = [];
|
|
3597
|
+
const decision = { runid: "", stepid: input.step.id, kind: "loop", at: input.now, loops };
|
|
3598
|
+
for (let iteration = 0; iteration < input.condition.bound; iteration += 1) {
|
|
3599
|
+
if (!evaluatecondition({ expression: input.condition.while }, scopes)) {
|
|
3600
|
+
return { ok: true, scopes, log, summary: `The while loop ended after ${iteration} iteration${iteration === 1 ? "" : "s"} because its condition stopped holding inside the reviewed safety bound of ${input.condition.bound}.`, decision };
|
|
3601
|
+
}
|
|
3602
|
+
const body = await runbody({ step: input.step, scopes, outputs: input.outputs, execute: input.execute, now: input.now, steps: input.condition.steps, path: `${input.path ?? input.step.id}[${iteration}]` });
|
|
3603
|
+
scopes = body.scopes;
|
|
3604
|
+
log.push(...body.log);
|
|
3605
|
+
loops.push({ stepid: input.step.id, path: `${input.path ?? input.step.id}[${iteration}]`, iteration, ok: body.ok, at: input.now });
|
|
3606
|
+
if (!body.ok) return { ok: false, scopes, log, summary: `The while loop failed at iteration ${iteration + 1}: ${body.failure?.summary ?? "the body step failed."}`, decision };
|
|
3607
|
+
log.push(iterationentry(input.step, iteration, input.condition.bound, true, input.now));
|
|
3608
|
+
}
|
|
3609
|
+
if (evaluatecondition({ expression: input.condition.while }, scopes)) {
|
|
3610
|
+
return { ok: false, scopes, log, summary: `The while loop hit its reviewed safety bound of ${input.condition.bound} iterations while its condition still held; the overflow is reported instead of looping forever.`, decision };
|
|
3611
|
+
}
|
|
3612
|
+
return { ok: true, scopes, log, summary: `The while loop ended after ${input.condition.bound} iteration${input.condition.bound === 1 ? "" : "s"} inside the reviewed safety bound.`, decision };
|
|
3613
|
+
}
|
|
3614
|
+
async function runforeach(input) {
|
|
3615
|
+
if (!input.resolveelements) throw new Error("The foreach step needs the element resolver of the executor seam.");
|
|
3616
|
+
const elements = await input.resolveelements(input.foreach.selector);
|
|
3617
|
+
const loops = [];
|
|
3618
|
+
const decision = { runid: "", stepid: input.step.id, kind: "loop", at: input.now, loops };
|
|
3619
|
+
if (elements.length === 0) return { ok: true, scopes: input.scopes, log: [], summary: `The selector ${input.foreach.selector} matched no element and the foreach ran zero iterations.`, decision };
|
|
3620
|
+
let scopes = input.scopes;
|
|
3621
|
+
const log = [];
|
|
3622
|
+
for (let index = 0; index < elements.length; index += 1) {
|
|
3623
|
+
scopes = setvariable(scopes, input.foreach.item, "element", deepcopy(elements[index]), input.now);
|
|
3624
|
+
scopes = setvariable(scopes, input.foreach.index, "number", index, input.now);
|
|
3625
|
+
const body = await runbody({ step: input.step, scopes, outputs: input.outputs, execute: input.execute, now: input.now, steps: input.foreach.steps, path: `${input.path ?? input.step.id}[${index}]` });
|
|
3626
|
+
scopes = body.scopes;
|
|
3627
|
+
const ok = body.ok;
|
|
3628
|
+
loops.push({ stepid: input.step.id, path: `${input.path ?? input.step.id}[${index}]`, iteration: index, ok, at: input.now });
|
|
3629
|
+
log.push(...body.log, iterationentry(input.step, index, elements.length, ok, input.now));
|
|
3630
|
+
if (!ok) return { ok: false, scopes, log, summary: `The foreach failed at iteration ${index + 1} of ${elements.length}: ${body.failure?.summary ?? "the body step failed."}`, decision };
|
|
3631
|
+
}
|
|
3632
|
+
return { ok: true, scopes, log, summary: `The foreach ran ${elements.length} iteration${elements.length === 1 ? "" : "s"} over the elements of ${input.foreach.selector}.`, decision };
|
|
3633
|
+
}
|
|
3634
|
+
function joinbranches(input) {
|
|
3635
|
+
const contributing = input.branches.filter((branch) => !branch.cancelled);
|
|
3636
|
+
const byname = /* @__PURE__ */ new Map();
|
|
3637
|
+
for (const branch of contributing) for (const variable of branch.variables) {
|
|
3638
|
+
const entries = byname.get(variable.name) ?? [];
|
|
3639
|
+
entries.push({ order: branch.order, value: variable });
|
|
3640
|
+
byname.set(variable.name, entries);
|
|
3641
|
+
}
|
|
3642
|
+
const conflicts = [...byname.entries()].filter(([, entries]) => entries.length > 1).map(([name]) => name);
|
|
3643
|
+
const record2 = { stepid: input.stepid, strategy: input.strategy, conflicts, merged: [], at: input.now };
|
|
3644
|
+
if (conflicts.length > 0 && input.strategy === "fail") {
|
|
3645
|
+
return { ok: false, conflicts, merged: [], record: record2, summary: `The join refused the conflicting writes of ${conflicts.join(", ")} under the fail strategy.` };
|
|
3646
|
+
}
|
|
3647
|
+
const merged = [];
|
|
3648
|
+
for (const [name, entries] of byname) {
|
|
3649
|
+
void name;
|
|
3650
|
+
const winner = input.strategy === "first" ? entries.reduce((left, right) => left.order <= right.order ? left : right) : entries.reduce((left, right) => left.order >= right.order ? left : right);
|
|
3651
|
+
merged.push({ ...winner.value, setat: input.now });
|
|
3652
|
+
}
|
|
3653
|
+
record2.merged = merged.map((variable) => variable.name);
|
|
3654
|
+
return { ok: true, conflicts, merged, record: record2, summary: `The join merged ${merged.length} variable${merged.length === 1 ? "" : "s"} under the ${input.strategy} strategy${conflicts.length > 0 ? ` with the conflicts ${conflicts.join(", ")} resolved by the strategy` : " with no conflict"}.` };
|
|
3655
|
+
}
|
|
3656
|
+
async function runparallel(input) {
|
|
3657
|
+
let finished = 0;
|
|
3658
|
+
let firstfailure = Number.POSITIVE_INFINITY;
|
|
3659
|
+
const log = [];
|
|
3660
|
+
const launches = input.parallel.branches.map((branch, order) => (async () => {
|
|
3661
|
+
const parent = input.scopes.length > 0 ? input.scopes[input.scopes.length - 1].name : void 0;
|
|
3662
|
+
const isolated = pushscope(input.scopes, `branch${branch.id}`, parent);
|
|
3663
|
+
const body = await runbody({ step: input.step, scopes: isolated, outputs: input.outputs, execute: input.execute, now: input.now, steps: branch.steps });
|
|
3664
|
+
const finishedat = finished;
|
|
3665
|
+
finished += 1;
|
|
3666
|
+
if (!body.ok && finishedat < firstfailure) firstfailure = finishedat;
|
|
3667
|
+
const scope = body.scopes[body.scopes.length - 1];
|
|
3668
|
+
return { branch, order, finishedat, ok: body.ok, scopes: body.scopes, variables: scope.name === `branch${branch.id}` ? scope.variables : [], log: body.log, failure: body.failure };
|
|
3669
|
+
})());
|
|
3670
|
+
const settled = await Promise.all(launches);
|
|
3671
|
+
for (const entry of settled) log.push(...entry.log);
|
|
3672
|
+
const cancelmode = input.parallel.join.onfail === "cancel";
|
|
3673
|
+
const outcomes = settled.map((entry) => ({ branchid: entry.branch.id, ok: entry.ok, summary: entry.ok ? `The branch ${entry.branch.id} completed.` : entry.failure?.summary ?? `The branch ${entry.branch.id} failed.`, ...cancelmode && firstfailure !== Number.POSITIVE_INFINITY && entry.finishedat > firstfailure ? { cancelled: true } : {} }));
|
|
3674
|
+
const join = joinbranches({ stepid: input.step.id, branches: settled.map((entry, order) => ({ id: entry.branch.id, order, ok: entry.ok, cancelled: outcomes[order]?.cancelled === true, variables: entry.variables })), strategy: input.parallel.join.strategy, now: input.now });
|
|
3675
|
+
const decision = { runid: "", stepid: input.step.id, kind: "join", at: input.now, join: join.record, branches: outcomes };
|
|
3676
|
+
if (!join.ok) return { ok: false, scopes: input.scopes, log, summary: join.summary, decision };
|
|
3677
|
+
let scopes = input.scopes;
|
|
3678
|
+
for (const variable of join.merged) scopes = setvariable(scopes, variable.name, variable.kind, variable.value, input.now);
|
|
3679
|
+
const failedbranches = settled.filter((entry, order) => !entry.ok && outcomes[order]?.cancelled !== true).map((entry) => entry.branch.id);
|
|
3680
|
+
if (cancelmode && failedbranches.length > 0) {
|
|
3681
|
+
return { ok: false, scopes, log, summary: `The parallel block failed on branch ${failedbranches.join(", ")} and the join policy cancelled the siblings still running; ${join.summary}`, decision };
|
|
3682
|
+
}
|
|
3683
|
+
return { ok: true, scopes, log, summary: `The parallel block ran ${input.parallel.branches.length} concurrent branch${input.parallel.branches.length === 1 ? "" : "es"}; ${join.summary}`, decision };
|
|
3684
|
+
}
|
|
3685
|
+
function errorclassof(output) {
|
|
3686
|
+
const errorclass = output.details?.errorclass;
|
|
3687
|
+
return typeof errorclass === "string" && errorclass.trim() ? errorclass : "stepfailed";
|
|
3688
|
+
}
|
|
3689
|
+
function backoffdelay(policy, attempt, seed) {
|
|
3690
|
+
const base = policy.backoff.shape === "exponential" ? policy.backoff.base * 2 ** (attempt - 1) : policy.backoff.base;
|
|
3691
|
+
if (policy.backoff.jitter <= 0) return Math.max(0, base);
|
|
3692
|
+
return Math.max(0, base - policy.backoff.jitter / 2 + seededrandom(seed + attempt) * policy.backoff.jitter);
|
|
3693
|
+
}
|
|
3694
|
+
function waitsome(milliseconds) {
|
|
3695
|
+
return new Promise((resolve) => setTimeout(resolve, Math.max(0, milliseconds)));
|
|
3696
|
+
}
|
|
3697
|
+
async function applyretry(input) {
|
|
3698
|
+
const seed = input.seed ?? 0;
|
|
3699
|
+
let value = await input.run();
|
|
3700
|
+
const attempts = [];
|
|
3701
|
+
let attempt = 1;
|
|
3702
|
+
while (!value.ok && attempt < input.policy.attempts) {
|
|
3703
|
+
const errorclass = input.errorclass(value);
|
|
3704
|
+
if (!input.policy.retryable.includes(errorclass)) break;
|
|
3705
|
+
const delay = backoffdelay(input.policy, attempt, seed);
|
|
3706
|
+
attempts.push({ stepid: input.stepid, attempt: attempt + 1, delay, errorclass, at: input.now });
|
|
3707
|
+
if (delay > 0) await waitsome(delay);
|
|
3708
|
+
attempt += 1;
|
|
3709
|
+
value = await input.run();
|
|
3710
|
+
}
|
|
3711
|
+
return { value, attempts, exhausted: !value.ok && attempts.length > 0 && attempt >= input.policy.attempts };
|
|
3712
|
+
}
|
|
3713
|
+
async function applytimeout(input) {
|
|
3714
|
+
let timer;
|
|
3715
|
+
const guard = new Promise((resolve) => {
|
|
3716
|
+
timer = setTimeout(() => resolve("cancelled"), Math.max(0, input.budgetms));
|
|
3717
|
+
});
|
|
3718
|
+
const raced = await Promise.race([input.run().then((value) => ({ kind: "done", value })), guard.then((marker) => ({ kind: "cancelled", marker }))]);
|
|
3719
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
3720
|
+
if (raced.kind === "done") return { aborted: false, value: raced.value };
|
|
3721
|
+
return { aborted: true, output: { ok: false, summary: `The ${input.stepid} step exceeded its reviewed budget of ${input.budgetms} milliseconds and was cancelled.`, details: { errorclass: "timeout", budget: input.budgetms, cancelled: true } }, abort: { stepid: input.stepid, budget: input.budgetms, scope: "step", at: Date.now() } };
|
|
3722
|
+
}
|
|
3723
|
+
async function applyruntimeout(input) {
|
|
3724
|
+
let timer;
|
|
3725
|
+
const guard = new Promise((resolve) => {
|
|
3726
|
+
timer = setTimeout(() => resolve("cancelled"), Math.max(0, input.budgetms));
|
|
3727
|
+
});
|
|
3728
|
+
const raced = await Promise.race([input.run().then((value) => ({ kind: "done", value })), guard.then((marker) => ({ kind: "cancelled", marker }))]);
|
|
3729
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
3730
|
+
if (raced.kind === "done") return { cancelled: false, value: raced.value };
|
|
3731
|
+
return { cancelled: true, error: new cancellederror(`The run exceeded its reviewed budget of ${input.budgetms} milliseconds and was cancelled.`) };
|
|
3732
|
+
}
|
|
3733
|
+
async function runcatch(input) {
|
|
3734
|
+
const body = await runbody({ step: { id: "catch", kind: "trycatch", label: "catch handler" }, scopes: input.scopes, outputs: input.outputs, execute: input.execute, now: input.now, steps: input.handler.steps });
|
|
3735
|
+
return { ok: body.ok, scopes: body.scopes, log: body.log };
|
|
3736
|
+
}
|
|
3737
|
+
async function runtry(input) {
|
|
3738
|
+
const timeouts = [];
|
|
3739
|
+
const retries = [];
|
|
3740
|
+
const runonce = async (child, scopes) => {
|
|
3741
|
+
if (iscontrolflowkind(child.kind)) {
|
|
3742
|
+
const result = await runcontrolstep({ step: child, scopes, outputs: input.outputs, execute: input.execute, now: input.now });
|
|
3743
|
+
return { ok: result.output.ok, scopes: result.scopes, log: result.log, output: result.output };
|
|
3744
|
+
}
|
|
3745
|
+
const executed = await runstep({ step: child, scopes, outputs: input.outputs, execute: input.execute, now: input.now });
|
|
3746
|
+
return { ok: executed.output.ok, scopes: executed.scopes, log: [...executed.childlog ?? [], executed.log], output: executed.output };
|
|
3747
|
+
};
|
|
3748
|
+
const runchild = async (child, scopes) => {
|
|
3749
|
+
const attempt = async () => {
|
|
3750
|
+
if (input.fragile.retry === void 0) return await runonce(child, scopes);
|
|
3751
|
+
const retried = await applyretry({ stepid: child.id, policy: input.fragile.retry, run: () => runonce(child, scopes), errorclass: (value) => errorclassof(value.output), now: input.now, seed: seedof(child.id) });
|
|
3752
|
+
retries.push(...retried.attempts);
|
|
3753
|
+
return retried.value;
|
|
3754
|
+
};
|
|
3755
|
+
if (input.fragile.timeout?.stepms === void 0) return await attempt();
|
|
3756
|
+
const guarded = await applytimeout({ stepid: child.id, budgetms: input.fragile.timeout.stepms, run: attempt });
|
|
3757
|
+
if (!guarded.aborted) return guarded.value;
|
|
3758
|
+
if (guarded.abort) timeouts.push(guarded.abort);
|
|
3759
|
+
return { ok: false, scopes, log: [], output: guarded.output };
|
|
3760
|
+
};
|
|
3761
|
+
const runbodyof = async (scopes) => {
|
|
3762
|
+
let current = scopes;
|
|
3763
|
+
const log = [];
|
|
3764
|
+
for (const child of input.fragile.steps) {
|
|
3765
|
+
const executed = await runchild(child, current);
|
|
3766
|
+
current = executed.scopes;
|
|
3767
|
+
log.push(...executed.log);
|
|
3768
|
+
if (!executed.ok) return { ok: false, scopes: current, log, failure: executed.output };
|
|
3769
|
+
}
|
|
3770
|
+
return { ok: true, scopes: current, log };
|
|
3771
|
+
};
|
|
3772
|
+
let body;
|
|
3773
|
+
if (input.fragile.timeout?.runms !== void 0) {
|
|
3774
|
+
const guarded = await applytimeout({ stepid: input.step.id, budgetms: input.fragile.timeout.runms, run: () => runbodyof(input.scopes) });
|
|
3775
|
+
if (guarded.aborted) {
|
|
3776
|
+
if (guarded.abort) timeouts.push({ ...guarded.abort, scope: "run" });
|
|
3777
|
+
body = { ok: false, scopes: input.scopes, log: [], failure: guarded.output ?? { ok: false, summary: "The try block exceeded its reviewed run budget and was cancelled.", details: { errorclass: "timeout", cancelled: true } } };
|
|
3778
|
+
} else {
|
|
3779
|
+
body = guarded.value;
|
|
3780
|
+
}
|
|
3781
|
+
} else {
|
|
3782
|
+
body = await runbodyof(input.scopes);
|
|
3783
|
+
}
|
|
3784
|
+
if (body.ok) {
|
|
3785
|
+
const summary = `The try block completed its ${input.fragile.steps.length} step${input.fragile.steps.length === 1 ? "" : "s"}${retries.length > 0 ? ` after ${retries.length} retry attempt${retries.length === 1 ? "" : "s"}` : ""}.`;
|
|
3786
|
+
if (retries.length === 0 && timeouts.length === 0) return { ok: true, scopes: body.scopes, log: body.log, summary };
|
|
3787
|
+
return { ok: true, scopes: body.scopes, log: body.log, summary, decision: { runid: "", stepid: input.step.id, kind: "retry", at: input.now, ...retries.length > 0 ? { retries } : {}, ...timeouts.length > 0 ? { timeouts } : {} } };
|
|
3788
|
+
}
|
|
3789
|
+
const handler = await runcatch({ handler: input.fragile.catch, scopes: body.scopes, outputs: input.outputs, execute: input.execute, now: input.now });
|
|
3790
|
+
const errorclass = errorclassof(body.failure ?? { ok: false, summary: "" });
|
|
3791
|
+
const decision = { runid: "", stepid: input.step.id, kind: "catch", at: input.now, ...retries.length > 0 ? { retries } : {}, ...timeouts.length > 0 ? { timeouts } : {}, catch: { errorclass, message: body.failure?.summary ?? "The fragile body step failed.", rerun: input.fragile.catch.rerun === true } };
|
|
3792
|
+
if (!handler.ok) return { ok: false, scopes: handler.scopes, log: [...body.log, ...handler.log], summary: `The catch handler of the try block failed after the ${errorclass} failure.`, decision };
|
|
3793
|
+
if (input.fragile.catch.rerun === true) {
|
|
3794
|
+
const rerun = await runbodyof(handler.scopes);
|
|
3795
|
+
if (rerun.ok) return { ok: true, scopes: rerun.scopes, log: [...body.log, ...handler.log, ...rerun.log], summary: `The catch handler ran after the ${errorclass} failure and the rerun of the try body succeeded.`, decision };
|
|
3796
|
+
return { ok: false, scopes: rerun.scopes, log: [...body.log, ...handler.log, ...rerun.log], summary: `The catch handler ran and the rerun of the try body failed again with ${errorclassof(rerun.failure ?? { ok: false, summary: "" })}.`, decision };
|
|
3797
|
+
}
|
|
3798
|
+
return { ok: true, scopes: handler.scopes, log: [...body.log, ...handler.log], summary: `The catch handler ran ${input.fragile.catch.steps.length} step${input.fragile.catch.steps.length === 1 ? "" : "s"} after the ${errorclass} failure.`, decision };
|
|
3799
|
+
}
|
|
3800
|
+
function seedof(text2) {
|
|
3801
|
+
let hash = 2166136261;
|
|
3802
|
+
for (let index = 0; index < text2.length; index += 1) {
|
|
3803
|
+
hash ^= text2.charCodeAt(index);
|
|
3804
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
3805
|
+
}
|
|
3806
|
+
return hash >>> 0;
|
|
3807
|
+
}
|
|
3808
|
+
async function runcontrolstep(input) {
|
|
3809
|
+
const payload = controloptions(input.step);
|
|
3810
|
+
const base = { step: input.step, scopes: input.scopes, outputs: input.outputs, execute: input.execute, now: input.now, ...input.path !== void 0 ? { path: input.path } : {} };
|
|
3811
|
+
let result;
|
|
3812
|
+
switch (input.step.kind) {
|
|
3813
|
+
case "condition": {
|
|
3814
|
+
const condition = conditionof(payload.condition);
|
|
3815
|
+
if (!condition) throw new Error("The condition step needs a reviewed boolean expression in its options.");
|
|
3816
|
+
const value = evaluatecondition(condition, input.scopes);
|
|
3817
|
+
const scopes = setvariable(input.scopes, condition.expression.result, condition.expression.resultkind, value, input.now);
|
|
3818
|
+
const summary = `The condition ${condition.expression.result} ${value ? "holds" : "does not hold"} over the extracted values.`;
|
|
3819
|
+
return { scopes, log: [], output: { ok: true, summary, details: { condition: { result: condition.expression.result, value } } } };
|
|
3820
|
+
}
|
|
3821
|
+
case "branch": {
|
|
3822
|
+
const branch = branchof(payload.branch);
|
|
3823
|
+
if (!branch) throw new Error("The branch step needs reviewed unique paths with boolean match expressions and an else path in its options.");
|
|
3824
|
+
const chosen = choosebranch({ stepid: input.step.id, branch, scopes: input.scopes, ...input.pagestate !== void 0 ? { pagestate: input.pagestate } : {}, now: input.now });
|
|
3825
|
+
const body = await runbody({ ...base, steps: chosen.steps });
|
|
3826
|
+
result = body.ok ? { ok: true, scopes: body.scopes, log: body.log, summary: `The branch chose the path ${chosen.outcome.path}: ${chosen.outcome.reason}`, decision: { runid: input.runid ?? "", stepid: input.step.id, kind: "branch", at: input.now, branch: chosen.outcome } } : { ok: false, scopes: body.scopes, log: body.log, summary: `The branch chose the path ${chosen.outcome.path} and its body failed: ${body.failure?.summary ?? "the body step failed."}`, decision: { runid: input.runid ?? "", stepid: input.step.id, kind: "branch", at: input.now, branch: chosen.outcome } };
|
|
3827
|
+
break;
|
|
3828
|
+
}
|
|
3829
|
+
case "loop": {
|
|
3830
|
+
const loop = loopof(payload.loop);
|
|
3831
|
+
if (!loop) throw new Error("The loop step needs a reviewed list variable, distinct item and index variables, an optional positive safety bound and a non-empty body in its options.");
|
|
3832
|
+
result = await runloop({ ...base, loop });
|
|
3833
|
+
break;
|
|
3834
|
+
}
|
|
3835
|
+
case "repeatuntil": {
|
|
3836
|
+
const repeat = repeatuntilof(payload.repeatuntil);
|
|
3837
|
+
if (!repeat) throw new Error("The repeat until step needs a reviewed convergence expression, an optional positive safety bound and a non-empty body in its options.");
|
|
3838
|
+
result = await runrepeatuntil({ ...base, repeat });
|
|
3839
|
+
break;
|
|
3840
|
+
}
|
|
3841
|
+
case "whileloop": {
|
|
3842
|
+
const condition = whileof(payload.while);
|
|
3843
|
+
if (!condition) throw new Error("The while step needs a reviewed condition, a mandatory positive safety bound and a non-empty body in its options.");
|
|
3844
|
+
result = await runwhile({ ...base, condition });
|
|
3845
|
+
break;
|
|
3846
|
+
}
|
|
3847
|
+
case "foreach": {
|
|
3848
|
+
const foreach = foreachof(payload.foreach);
|
|
3849
|
+
if (!foreach) throw new Error("The foreach step needs a reviewed non-empty selector, distinct item and index variables and a non-empty body in its options.");
|
|
3850
|
+
result = await runforeach({ ...base, foreach, ...input.resolveelements !== void 0 ? { resolveelements: input.resolveelements } : {} });
|
|
3851
|
+
break;
|
|
3852
|
+
}
|
|
3853
|
+
case "parallel": {
|
|
3854
|
+
const parallel = parallelof(payload.parallel);
|
|
3855
|
+
if (!parallel) throw new Error("The parallel step needs uniquely identified branches with bodies and a join policy of the first, last or fail strategy with cancel or continue on branch failure in its options.");
|
|
3856
|
+
result = await runparallel({ ...base, parallel });
|
|
3857
|
+
break;
|
|
3858
|
+
}
|
|
3859
|
+
case "trycatch": {
|
|
3860
|
+
const fragile = tryof(payload.try);
|
|
3861
|
+
if (!fragile) throw new Error("The try step needs a fragile body, a catch handler and optional retry and timeout policies in its options.");
|
|
3862
|
+
result = await runtry({ ...base, fragile });
|
|
3863
|
+
break;
|
|
3864
|
+
}
|
|
3865
|
+
default:
|
|
3866
|
+
throw new Error(`The ${input.step.kind} step is not a control flow kind.`);
|
|
3867
|
+
}
|
|
3868
|
+
if (result.decision !== void 0 && input.runid !== void 0) result.decision.runid = input.runid;
|
|
3869
|
+
return { scopes: result.scopes, log: result.log, output: { ok: result.ok, summary: result.summary, ...result.decision !== void 0 ? { details: { control: result.decision } } : {} } };
|
|
3870
|
+
}
|
|
3871
|
+
|
|
3180
3872
|
// workflow.ts
|
|
3181
3873
|
var workflowkinds = ["composeworkflow", "savetemplate", "runworkflow", "dryrun", "delay", "waitelement", "compute", "extractvars"];
|
|
3182
3874
|
function workflowstepof(value) {
|
|
@@ -3324,12 +4016,19 @@ function composeworkflow(input) {
|
|
|
3324
4016
|
const steps = expandblocks(input.steps, blocks);
|
|
3325
4017
|
for (const step of steps) {
|
|
3326
4018
|
if (input.kindallowed && !input.kindallowed(step.kind)) throw new Error(`The workflow step kind ${step.kind} is not a reviewed action kind.`);
|
|
4019
|
+
if (iscontrolflowkind(step.kind)) {
|
|
4020
|
+
validatecontrolpayload(step);
|
|
4021
|
+
for (const child of controlsteps(step)) {
|
|
4022
|
+
if (input.kindallowed && !input.kindallowed(child.kind)) throw new Error(`The workflow step kind ${child.kind} inside the control payload of ${step.id} is not a reviewed action kind.`);
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
3327
4025
|
if (step.bindings) for (const binding of step.bindings) {
|
|
3328
4026
|
if (!steps.some((other) => other.id === binding.stepid)) throw new Error(`The binding of ${binding.variable} references the unknown step ${binding.stepid}.`);
|
|
3329
4027
|
}
|
|
3330
4028
|
}
|
|
3331
4029
|
const riskof = input.riskof ?? (() => "sensitive");
|
|
3332
|
-
const
|
|
4030
|
+
const gradedkinds = steps.flatMap((step) => [step.kind, ...controlsteps(step).map((child) => child.kind)]);
|
|
4031
|
+
const risk = gradedkinds.some((kind) => riskof(kind) === "sensitive") ? "sensitive" : gradedkinds.some((kind) => riskof(kind) === "interaction") ? "interaction" : "read";
|
|
3333
4032
|
const record2 = { id: input.id ?? crypto.randomUUID(), name: input.name, version: input.version, origins: [...new Set(origins)], steps, blocks, risk, createdat: input.now };
|
|
3334
4033
|
return deepfreeze(record2);
|
|
3335
4034
|
}
|
|
@@ -3610,21 +4309,24 @@ async function runstep(input) {
|
|
|
3610
4309
|
}
|
|
3611
4310
|
stepvalue = interpolated.text;
|
|
3612
4311
|
}
|
|
3613
|
-
const
|
|
4312
|
+
const controlled = iscontrolflowkind(input.step.kind);
|
|
4313
|
+
const target = !controlled && input.step.target !== void 0 ? interpolate(input.step.target, scopes) : void 0;
|
|
3614
4314
|
if (target) consumed.push(...target.consumed);
|
|
3615
|
-
const value = stepvalue !== void 0 ? interpolate(stepvalue, scopes) : void 0;
|
|
4315
|
+
const value = !controlled && stepvalue !== void 0 ? interpolate(stepvalue, scopes) : void 0;
|
|
3616
4316
|
if (value) consumed.push(...value.consumed);
|
|
3617
|
-
const options = input.step.options !== void 0 ? interpolate(input.step.options, scopes) : void 0;
|
|
4317
|
+
const options = !controlled && input.step.options !== void 0 ? interpolate(input.step.options, scopes) : void 0;
|
|
3618
4318
|
if (options) consumed.push(...options.consumed);
|
|
3619
4319
|
const dispatchable = { ...input.step, ...target !== void 0 ? { target: target.text } : {}, ...value !== void 0 ? { value: value.text } : {}, ...options !== void 0 ? { options: options.text } : {} };
|
|
3620
|
-
const output = await input.execute(dispatchable, { scopes, ...input.block !== void 0 ? { block: input.block } : {} });
|
|
4320
|
+
const output = await input.execute(dispatchable, { scopes, outputs: input.outputs, ...input.block !== void 0 ? { block: input.block } : {} });
|
|
4321
|
+
if (output.scopes !== void 0) scopes = output.scopes;
|
|
4322
|
+
const childlog = output.log;
|
|
3621
4323
|
if (input.step.bindings) {
|
|
3622
4324
|
const bound = bindvariables(scopes, input.step.bindings, { ...input.outputs, [input.step.id]: { stepid: input.step.id, ok: output.ok, summary: output.summary, ...output.details !== void 0 ? { details: output.details } : {}, at: input.now } }, input.now);
|
|
3623
4325
|
scopes = bound.scopes;
|
|
3624
4326
|
produced = [.../* @__PURE__ */ new Set([...produced, ...bound.produced])];
|
|
3625
4327
|
}
|
|
3626
4328
|
const duration = Date.now() - startedat;
|
|
3627
|
-
return { scopes, log: runlogof(input.step, output.ok ? "done" : "failed", startedat, duration, output.summary, { ...input.block !== void 0 ? { block: input.block } : {}, ...consumed.length > 0 ? { consumed } : {}, ...produced.length > 0 ? { produced } : {}, ...output.details !== void 0 ? { details: output.details } : {}, ...output.ok ? { checkpoint: true } : {} }), output };
|
|
4329
|
+
return { scopes, log: runlogof(input.step, output.ok ? "done" : "failed", startedat, duration, output.summary, { ...input.block !== void 0 ? { block: input.block } : {}, ...consumed.length > 0 ? { consumed } : {}, ...produced.length > 0 ? { produced } : {}, ...output.details !== void 0 ? { details: output.details } : {}, ...output.ok ? { checkpoint: true } : {} }), ...childlog !== void 0 ? { childlog } : {}, output };
|
|
3628
4330
|
} catch (error) {
|
|
3629
4331
|
const duration = Date.now() - startedat;
|
|
3630
4332
|
const summary = error instanceof Error ? error.message : String(error);
|
|
@@ -3656,6 +4358,7 @@ async function runworkflow(input) {
|
|
|
3656
4358
|
}
|
|
3657
4359
|
const executed = await runstep({ step, scopes, outputs, execute: input.execute, now: Date.now(), ...step.block !== void 0 ? { block: step.block } : {} });
|
|
3658
4360
|
scopes = executed.scopes;
|
|
4361
|
+
if (executed.childlog !== void 0) log.push(...executed.childlog);
|
|
3659
4362
|
log.push(executed.log);
|
|
3660
4363
|
outputs[step.id] = { stepid: step.id, ok: executed.output.ok, summary: executed.output.summary, ...executed.output.details !== void 0 ? { details: executed.output.details } : {}, at: Date.now() };
|
|
3661
4364
|
if (!executed.output.ok) {
|
|
@@ -3916,8 +4619,8 @@ function consolediff(input) {
|
|
|
3916
4619
|
|
|
3917
4620
|
// policy.ts
|
|
3918
4621
|
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "restoresession", "exportsessions", "importsessions", "runworkflow"]);
|
|
3919
|
-
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr"]);
|
|
3920
|
-
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts", "persiststate", "capturesession", "namedsessions", "diffsessions", "searchsessions", "composeworkflow", "savetemplate", "dryrun", "delay", "waitelement", "compute", "extractvars"]);
|
|
4622
|
+
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"]);
|
|
4623
|
+
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts", "persiststate", "capturesession", "namedsessions", "diffsessions", "searchsessions", "composeworkflow", "savetemplate", "dryrun", "delay", "waitelement", "compute", "extractvars", "condition", "branch"]);
|
|
3921
4624
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
3922
4625
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
3923
4626
|
var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
|
|
@@ -3938,7 +4641,7 @@ var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "w
|
|
|
3938
4641
|
var profileractions = /* @__PURE__ */ new Set(["measureflow", "heapshot", "trackmemory", "profilecpu", "watchshifts", "traceload", "annotatetrace", "replaytrace", "capturesourcemaps"]);
|
|
3939
4642
|
var emulationactions = /* @__PURE__ */ new Set(["emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "blackboxscripts"]);
|
|
3940
4643
|
var sessionactions = /* @__PURE__ */ new Set(["persiststate", "capturesession", "restoresession", "namedsessions", "diffsessions", "searchsessions", "exportsessions", "importsessions"]);
|
|
3941
|
-
var workflowactions = /* @__PURE__ */ new Set(["composeworkflow", "savetemplate", "runworkflow", "dryrun", "delay", "waitelement", "compute", "extractvars"]);
|
|
4644
|
+
var workflowactions = /* @__PURE__ */ new Set(["composeworkflow", "savetemplate", "runworkflow", "dryrun", "delay", "waitelement", "compute", "extractvars", "condition", "branch", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"]);
|
|
3942
4645
|
var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
|
|
3943
4646
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
3944
4647
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
@@ -5583,6 +6286,68 @@ function validateworkflowgrammar(step, options) {
|
|
|
5583
6286
|
if (typeof options.text !== "string") return { allowed: false, reason: "The variable extraction needs the reviewed text the regex rule applies to." };
|
|
5584
6287
|
return { allowed: true };
|
|
5585
6288
|
}
|
|
6289
|
+
if (kind === "condition") {
|
|
6290
|
+
const condition = conditionof(options.condition);
|
|
6291
|
+
if (!condition) return { allowed: false, reason: "The condition step needs a reviewed boolean expression in its options." };
|
|
6292
|
+
const operatorcheck = validatexpressionoperators(condition.expression);
|
|
6293
|
+
if (!operatorcheck.allowed) return operatorcheck;
|
|
6294
|
+
return { allowed: true };
|
|
6295
|
+
}
|
|
6296
|
+
if (kind === "branch") {
|
|
6297
|
+
const branch = branchof(options.branch);
|
|
6298
|
+
if (!branch) return { allowed: false, reason: "The branch step needs reviewed unique paths with boolean match expressions and an else path in its options so every branch terminates." };
|
|
6299
|
+
for (const path of [...branch.paths, branch.else]) {
|
|
6300
|
+
if (path.when === void 0) continue;
|
|
6301
|
+
const operatorcheck = validatexpressionoperators(path.when);
|
|
6302
|
+
if (!operatorcheck.allowed) return operatorcheck;
|
|
6303
|
+
}
|
|
6304
|
+
return controlchildkinds(step);
|
|
6305
|
+
}
|
|
6306
|
+
if (kind === "loop") {
|
|
6307
|
+
const loop = loopof(options.loop);
|
|
6308
|
+
if (!loop) return { allowed: false, reason: "The loop step needs a reviewed list variable, distinct item and index variables, an optional positive safety bound and a non-empty body in its options; an absent bound keeps the documented default." };
|
|
6309
|
+
return controlchildkinds(step);
|
|
6310
|
+
}
|
|
6311
|
+
if (kind === "repeatuntil") {
|
|
6312
|
+
const repeat = repeatuntilof(options.repeatuntil);
|
|
6313
|
+
if (!repeat) return { allowed: false, reason: "The repeat until step needs a reviewed convergence expression, an optional positive safety bound and a non-empty body in its options." };
|
|
6314
|
+
const operatorcheck = validatexpressionoperators(repeat.until);
|
|
6315
|
+
if (!operatorcheck.allowed) return operatorcheck;
|
|
6316
|
+
return controlchildkinds(step);
|
|
6317
|
+
}
|
|
6318
|
+
if (kind === "whileloop") {
|
|
6319
|
+
const condition = whileof(options.while);
|
|
6320
|
+
if (!condition) return { allowed: false, reason: "The while step needs a reviewed condition, a mandatory positive safety bound and a non-empty body in its options; a while loop without a safety bound is refused." };
|
|
6321
|
+
const operatorcheck = validatexpressionoperators(condition.while);
|
|
6322
|
+
if (!operatorcheck.allowed) return operatorcheck;
|
|
6323
|
+
return controlchildkinds(step);
|
|
6324
|
+
}
|
|
6325
|
+
if (kind === "foreach") {
|
|
6326
|
+
const foreach = foreachof(options.foreach);
|
|
6327
|
+
if (!foreach) return { allowed: false, reason: "The foreach step needs a reviewed non-empty selector, distinct item and index variables and a non-empty body in its options." };
|
|
6328
|
+
return controlchildkinds(step);
|
|
6329
|
+
}
|
|
6330
|
+
if (kind === "parallel") {
|
|
6331
|
+
const parallel = parallelof(options.parallel);
|
|
6332
|
+
if (!parallel) return { allowed: false, reason: "The parallel step needs uniquely identified branches with bodies and a join policy of the first, last or fail strategy with cancel or continue on branch failure in its options." };
|
|
6333
|
+
return controlchildkinds(step);
|
|
6334
|
+
}
|
|
6335
|
+
if (kind === "trycatch") {
|
|
6336
|
+
const fragile = tryof(options.try);
|
|
6337
|
+
if (!fragile) return { allowed: false, reason: "The try step needs a fragile body, a catch handler and optional retry and timeout policies in its options: attempts stay user configured with no code ceiling, backoff is fixed or exponential and budgets are positive." };
|
|
6338
|
+
return controlchildkinds(step);
|
|
6339
|
+
}
|
|
6340
|
+
return { allowed: true };
|
|
6341
|
+
}
|
|
6342
|
+
function controlchildkinds(step) {
|
|
6343
|
+
const children = controlsteps({ id: step.id, kind: step.kind, label: step.summary, ...step.options !== void 0 ? { options: step.options } : {} });
|
|
6344
|
+
for (const child of children) {
|
|
6345
|
+
try {
|
|
6346
|
+
actionrisk(child.kind);
|
|
6347
|
+
} catch {
|
|
6348
|
+
return { allowed: false, reason: `The ${child.kind} step inside the control payload of the ${step.kind} step is not a reviewed action kind.` };
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
5586
6351
|
return { allowed: true };
|
|
5587
6352
|
}
|
|
5588
6353
|
function validateregexrule(pattern) {
|
|
@@ -5644,6 +6409,20 @@ function workflowgate(input) {
|
|
|
5644
6409
|
return { allowed: true };
|
|
5645
6410
|
}
|
|
5646
6411
|
function dryrunprojection(step) {
|
|
6412
|
+
if (iscontrolflowkind(step.kind)) {
|
|
6413
|
+
for (const child of controlsteps(step)) {
|
|
6414
|
+
const childrisk = resolvedrisk({ id: child.id, kind: child.kind, summary: child.label, risk: "read", ...child.target !== void 0 ? { target: child.target } : {}, ...child.value !== void 0 ? { value: child.value } : {}, ...child.options !== void 0 ? { options: child.options } : {} });
|
|
6415
|
+
if (childrisk !== "read") return void 0;
|
|
6416
|
+
}
|
|
6417
|
+
if (step.kind === "condition") return "The condition step would evaluate its reviewed expression over the extracted values with no page side effect.";
|
|
6418
|
+
if (step.kind === "branch") return "The branch step would choose one reviewed path by page state and only the chosen path would run.";
|
|
6419
|
+
if (step.kind === "loop") return "The loop step would iterate its reviewed list binding the item and index variables per iteration inside the safety bound.";
|
|
6420
|
+
if (step.kind === "repeatuntil") return "The repeat until step would rerun its body until the convergence expression holds inside the safety bound.";
|
|
6421
|
+
if (step.kind === "whileloop") return "The while step would loop while its condition holds inside the reviewed safety bound.";
|
|
6422
|
+
if (step.kind === "foreach") return "The foreach step would iterate the elements of its reviewed selector binding the item and index variables per iteration.";
|
|
6423
|
+
if (step.kind === "parallel") return "The parallel step would run its branches concurrently and join their outcomes under the reviewed strategy.";
|
|
6424
|
+
return "The try step would run its fragile body and only the catch handler on failure.";
|
|
6425
|
+
}
|
|
5647
6426
|
const risk = resolvedrisk({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, ...step.options !== void 0 ? { options: step.options } : {} });
|
|
5648
6427
|
if (risk !== "read") return void 0;
|
|
5649
6428
|
if (step.kind === "delay") return `The delay step would sleep its reviewed base inside the jitter window.`;
|
|
@@ -6422,30 +7201,30 @@ function canexecute(input) {
|
|
|
6422
7201
|
if (iscontrolkind(input.step.kind)) {
|
|
6423
7202
|
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
6424
7203
|
if (!controlgate.allowed) return controlgate;
|
|
6425
|
-
let
|
|
7204
|
+
let controloptions2 = {};
|
|
6426
7205
|
try {
|
|
6427
|
-
|
|
7206
|
+
controloptions2 = parseoptions(input.step);
|
|
6428
7207
|
} catch {
|
|
6429
|
-
|
|
7208
|
+
controloptions2 = {};
|
|
6430
7209
|
}
|
|
6431
7210
|
if (input.step.kind === "blockrequest") {
|
|
6432
7211
|
const blockgatecheck = blockgate(input.session, input.step, now);
|
|
6433
7212
|
if (!blockgatecheck.allowed) return blockgatecheck;
|
|
6434
|
-
const rule = blockruleof(
|
|
7213
|
+
const rule = blockruleof(controloptions2.block);
|
|
6435
7214
|
if (rule) {
|
|
6436
7215
|
const blockorigin = origincheck(input.session, rule.urlpattern);
|
|
6437
7216
|
if (!blockorigin.allowed) return blockorigin;
|
|
6438
7217
|
}
|
|
6439
7218
|
}
|
|
6440
7219
|
if (input.step.kind === "mockresponse" || input.step.kind === "rewriteheaders") {
|
|
6441
|
-
const patterns = input.step.kind === "mockresponse" ? [mockspecof(
|
|
7220
|
+
const patterns = input.step.kind === "mockresponse" ? [mockspecof(controloptions2.mock)?.urlpattern ?? ""] : Array.isArray(controloptions2.rules) ? controloptions2.rules.map((item) => item && typeof item === "object" && !Array.isArray(item) ? String(item.urlpattern ?? "") : "") : [];
|
|
6442
7221
|
for (const pattern of patterns) {
|
|
6443
7222
|
const patterngate = origincheck(input.session, pattern);
|
|
6444
7223
|
if (!patterngate.allowed) return patterngate;
|
|
6445
7224
|
}
|
|
6446
7225
|
}
|
|
6447
7226
|
if (input.step.kind === "setcookies" || input.step.kind === "readcookies" || input.step.kind === "clearcookies") {
|
|
6448
|
-
const domain = typeof
|
|
7227
|
+
const domain = typeof controloptions2.domain === "string" && controloptions2.domain.trim() ? controloptions2.domain : Array.isArray(controloptions2.cookies) ? String(controloptions2.cookies[0]?.domain ?? "") : "";
|
|
6449
7228
|
if (!domain) return { allowed: false, reason: "A reviewed cookie domain is required before cookie control runs." };
|
|
6450
7229
|
const cookiegatecheck = cookiegate(input.session, domain, now);
|
|
6451
7230
|
if (!cookiegatecheck.allowed) return cookiegatecheck;
|
|
@@ -6677,13 +7456,13 @@ function recordsession(progress, planid, stepid, entry, now) {
|
|
|
6677
7456
|
}
|
|
6678
7457
|
function recordworkflow(progress, planid, stepid, entry, now) {
|
|
6679
7458
|
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
6680
|
-
const counts = `${entry.executed !== void 0 ? `${entry.executed} executed step${entry.executed === 1 ? "" : "s"}, ` : ""}${entry.refused !== void 0 ? `${entry.refused} refused step${entry.refused === 1 ? "" : "s"}, ` : ""}${entry.total !== void 0 ? `${entry.total} total step${entry.total === 1 ? "" : "s"}, ` : ""}`.replace(/, $/, "");
|
|
7459
|
+
const counts = `${entry.executed !== void 0 ? `${entry.executed} executed step${entry.executed === 1 ? "" : "s"}, ` : ""}${entry.refused !== void 0 ? `${entry.refused} refused step${entry.refused === 1 ? "" : "s"}, ` : ""}${entry.total !== void 0 ? `${entry.total} total step${entry.total === 1 ? "" : "s"}, ` : ""}${entry.iterations !== void 0 ? `${entry.iterations} iteration${entry.iterations === 1 ? "" : "s"}, ` : ""}${entry.denominator !== void 0 ? `denominator ${entry.denominator}, ` : ""}`.replace(/, $/, "");
|
|
6681
7460
|
const outcome = { stepid, ok: true, summary: `${entry.detail}${counts.length > 0 ? ` with ${counts}` : ""}.`, details: { workflow: entry }, at: now };
|
|
6682
7461
|
return recordoutcome(base, planid, outcome, now);
|
|
6683
7462
|
}
|
|
6684
7463
|
|
|
6685
7464
|
// version.ts
|
|
6686
|
-
var packageversion = "1.1.
|
|
7465
|
+
var packageversion = "1.1.51";
|
|
6687
7466
|
|
|
6688
7467
|
// types.ts
|
|
6689
7468
|
var protocolversion = packageversion;
|
|
@@ -6983,7 +7762,7 @@ function parseproposal(value, origin, grants) {
|
|
|
6983
7762
|
}
|
|
6984
7763
|
function workflowoutcome(input) {
|
|
6985
7764
|
const selected = input.stepid !== void 0 ? input.entries.filter((entry) => entry.stepid === input.stepid) : input.entries;
|
|
6986
|
-
const steps = selected.map((entry) => ({ stepid: entry.stepid, label: entry.label, state: entry.state, duration: entry.duration, summary: entry.summary, ...entry.block !== void 0 ? { block: entry.block } : {}, ...entry.produced !== void 0 ? { produced: entry.produced } : {}, ...entry.consumed !== void 0 ? { consumed: entry.consumed } : {}, ...entry.checkpoint === true ? { checkpoint: true } : {} }));
|
|
7765
|
+
const steps = selected.map((entry) => ({ stepid: entry.stepid, label: entry.label, state: entry.state, duration: entry.duration, summary: entry.summary, ...entry.block !== void 0 ? { block: entry.block } : {}, ...entry.produced !== void 0 ? { produced: entry.produced } : {}, ...entry.consumed !== void 0 ? { consumed: entry.consumed } : {}, ...entry.checkpoint === true ? { checkpoint: true } : {}, ...entry.details !== void 0 && entry.details.control !== void 0 ? { control: entry.details.control } : {} }));
|
|
6987
7766
|
return { version: protocolversion, runid: input.run.id, workflowid: input.run.workflowid, state: input.run.state, ...input.run.dryrun === true ? { dryrun: true } : {}, steps };
|
|
6988
7767
|
}
|
|
6989
7768
|
function stepof(kind, candidate, index) {
|
|
@@ -7001,7 +7780,7 @@ function requestbody(input) {
|
|
|
7001
7780
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
7002
7781
|
}
|
|
7003
7782
|
function outcomeresponse(input) {
|
|
7004
|
-
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {}, ...input.session ? { session: input.session } : {}, ...input.workflow ? { workflow: { runid: input.workflow.runid, state: input.workflow.state, ...input.workflow.dryrun === true ? { dryrun: true } : {}, produced: input.workflow.produced, consumed: input.workflow.consumed } } : {} });
|
|
7783
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {}, ...input.session ? { session: input.session } : {}, ...input.workflow ? { workflow: { runid: input.workflow.runid, state: input.workflow.state, ...input.workflow.dryrun === true ? { dryrun: true } : {}, produced: input.workflow.produced, consumed: input.workflow.consumed, ...input.workflow.timeout !== void 0 ? { timeout: input.workflow.timeout } : {}, ...input.workflow.retry !== void 0 ? { retry: input.workflow.retry } : {} } } : {} });
|
|
7005
7784
|
}
|
|
7006
7785
|
function mapresponse(input) {
|
|
7007
7786
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -7136,7 +7915,7 @@ function sessionreport(input) {
|
|
|
7136
7915
|
return { version: protocolversion, records: input.records, events: input.events, folders: input.folders, diffs: input.diffs, ...input.auto !== void 0 ? { auto: input.auto } : {}, ...input.crashed === true ? { crashed: true } : {} };
|
|
7137
7916
|
}
|
|
7138
7917
|
function workflowreport(input) {
|
|
7139
|
-
return { version: protocolversion, workflows: input.workflows, runs: input.runs, templates: input.templates, log: input.log ?? [], scopes: input.scopes ?? [], provenance: input.provenance ?? [] };
|
|
7918
|
+
return { version: protocolversion, workflows: input.workflows, runs: input.runs, templates: input.templates, log: input.log ?? [], scopes: input.scopes ?? [], provenance: input.provenance ?? [], control: input.control ?? [] };
|
|
7140
7919
|
}
|
|
7141
7920
|
|
|
7142
7921
|
// capture.ts
|
|
@@ -8866,7 +9645,7 @@ function stepoptions2(step) {
|
|
|
8866
9645
|
}
|
|
8867
9646
|
async function refreshcapabilities() {
|
|
8868
9647
|
const report = await readcapabilities();
|
|
8869
|
-
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds, ...cdpkinds], profile: [...profilerkinds], emulation: [...emulationkinds], sessions: [...sessionkinds], workflow: [...workflowkinds] };
|
|
9648
|
+
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds, ...cdpkinds], profile: [...profilerkinds], emulation: [...emulationkinds], sessions: [...sessionkinds], workflow: [...workflowkinds, ...controlflowkinds] };
|
|
8870
9649
|
await memory.setcapabilities(withmedia);
|
|
8871
9650
|
return withmedia;
|
|
8872
9651
|
}
|
|
@@ -11566,7 +12345,7 @@ async function executehttpstep(step, session, plan, tabid2, origin) {
|
|
|
11566
12345
|
var activesockets = /* @__PURE__ */ new Map();
|
|
11567
12346
|
var channelbuses = /* @__PURE__ */ new Map();
|
|
11568
12347
|
var netpoll = 100;
|
|
11569
|
-
function
|
|
12348
|
+
function waitsome2(milliseconds) {
|
|
11570
12349
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, milliseconds)));
|
|
11571
12350
|
}
|
|
11572
12351
|
async function queueinboundmessage(channelid, payload) {
|
|
@@ -11654,7 +12433,7 @@ async function runsubscription(subscription, controller) {
|
|
|
11654
12433
|
}
|
|
11655
12434
|
if (controller.signal.aborted) break;
|
|
11656
12435
|
if (subscription.lifetime !== void 0 && Date.now() - startedat >= subscription.lifetime) break;
|
|
11657
|
-
await
|
|
12436
|
+
await waitsome2(netpoll * 10);
|
|
11658
12437
|
}
|
|
11659
12438
|
const closed = { ...current, state: "closed", closedat: Date.now() };
|
|
11660
12439
|
await memory.setsubscription(closed);
|
|
@@ -11748,7 +12527,7 @@ async function executesocketstep(step, session, plan, tabid2, origin) {
|
|
|
11748
12527
|
const queued = await memory.getmessages(channelid);
|
|
11749
12528
|
matched = queued.filter((envelope) => matchmessage(filter, envelope)).slice(0, filter.limit ?? (matched.length || void 0));
|
|
11750
12529
|
if (matched.length >= (filter.limit ?? 1) || Date.now() >= deadline) break;
|
|
11751
|
-
await
|
|
12530
|
+
await waitsome2(netpoll);
|
|
11752
12531
|
}
|
|
11753
12532
|
await memory.drainmessages(matched.map((envelope) => ({ channelid: envelope.channelid, sequence: envelope.sequence })));
|
|
11754
12533
|
await audit("socket", `The message wait on channel ${channelid} matched ${matched.length} envelope${matched.length === 1 ? "" : "s"} of the reviewed filter within the ${budget} millisecond budget; payload values stay out of the audit trail.`, extra);
|
|
@@ -11809,7 +12588,7 @@ async function executesocketstep(step, session, plan, tabid2, origin) {
|
|
|
11809
12588
|
break;
|
|
11810
12589
|
}
|
|
11811
12590
|
stopcursor = decision.cursor;
|
|
11812
|
-
await
|
|
12591
|
+
await waitsome2(decision.next?.wait ?? cursor.interval);
|
|
11813
12592
|
next = { url: decision.next?.url ?? next.url, ...decision.next?.body !== void 0 ? { body: decision.next.body } : {} };
|
|
11814
12593
|
}
|
|
11815
12594
|
} finally {
|
|
@@ -11831,7 +12610,7 @@ async function executenetwatchstep(step, session, plan, tabid2, origin) {
|
|
|
11831
12610
|
const limit = typeof options.limit === "number" && Number.isInteger(options.limit) && options.limit >= 1 ? options.limit : void 0;
|
|
11832
12611
|
const before = await bridgecall(tabid2, "resourcerecords");
|
|
11833
12612
|
const known = new Set(resourcefacts(before ?? []).map((fact) => `${fact.url}@${fact.start}`));
|
|
11834
|
-
if (window2 > 0) await
|
|
12613
|
+
if (window2 > 0) await waitsome2(window2);
|
|
11835
12614
|
const after = await bridgecall(tabid2, "resourcerecords");
|
|
11836
12615
|
const fresh = resourcefacts(after ?? []).filter((fact) => !known.has(`${fact.url}@${fact.start}`));
|
|
11837
12616
|
const chosen = limit !== void 0 ? fresh.slice(0, limit) : fresh;
|
|
@@ -13342,7 +14121,7 @@ async function sleepreviewed(sampled, stepid) {
|
|
|
13342
14121
|
} catch {
|
|
13343
14122
|
}
|
|
13344
14123
|
}
|
|
13345
|
-
await
|
|
14124
|
+
await waitsome2(sampled);
|
|
13346
14125
|
return "timer";
|
|
13347
14126
|
}
|
|
13348
14127
|
async function executewaitelement(step, tabid2) {
|
|
@@ -13360,7 +14139,7 @@ async function executewaitelement(step, tabid2) {
|
|
|
13360
14139
|
const probe = await bridgecall(tabid2, "elementrect", wait.selector).catch(() => void 0);
|
|
13361
14140
|
if (probe?.ok) return { ok: true, summary: `Selector ${wait.selector} appeared after ${Date.now() - startedat} milliseconds of polling every ${wait.poll} milliseconds.`, details: { selector: wait.selector, waited: Date.now() - startedat, poll: wait.poll } };
|
|
13362
14141
|
if (singlepass || Date.now() >= deadline) break;
|
|
13363
|
-
await
|
|
14142
|
+
await waitsome2(pass + 1 < plan.probes ? wait.poll : plan.lastwait);
|
|
13364
14143
|
}
|
|
13365
14144
|
return { ok: false, summary: `Selector ${wait.selector} did not appear within the reviewed timeout of ${wait.timeout} milliseconds.`, details: { selector: wait.selector, waited: Date.now() - startedat } };
|
|
13366
14145
|
}
|
|
@@ -13475,13 +14254,26 @@ async function executeworkflowrun(step, session, plan, tabid2, origin, dry) {
|
|
|
13475
14254
|
activeworkflowruns.set(run.id, guards);
|
|
13476
14255
|
await audit("workflow", `Started the run ${run.id} of the workflow ${record2.name} version ${record2.version} with ${record2.steps.length} reviewed steps; every step passes the session, plan review and origin gates.`, { sessionid: session.id, planid: plan.id, stepid: step.id });
|
|
13477
14256
|
const context = { session, plan, tabid: tabid2, origin };
|
|
13478
|
-
const
|
|
14257
|
+
const executesteprouted = async (workflowstep, stepcontext) => {
|
|
13479
14258
|
if (guards.cancelled) return { ok: false, summary: `The run was cancelled before the ${workflowstep.label} step dispatched.` };
|
|
13480
|
-
return await dispatchworkflowstep(workflowstep, context);
|
|
14259
|
+
if (!iscontrolflowkind(workflowstep.kind)) return await dispatchworkflowstep(workflowstep, context);
|
|
14260
|
+
const controlled = await runcontrolstep({
|
|
14261
|
+
step: workflowstep,
|
|
14262
|
+
scopes: stepcontext.scopes,
|
|
14263
|
+
outputs: stepcontext.outputs ?? {},
|
|
14264
|
+
execute: async (dispatched, inner) => await executesteprouted(dispatched, inner),
|
|
14265
|
+
now: Date.now(),
|
|
14266
|
+
runid: run.id,
|
|
14267
|
+
pagestate: await pagestateof(tabid2),
|
|
14268
|
+
resolveelements: async (selector) => await resolveelements(tabid2, selector)
|
|
14269
|
+
});
|
|
14270
|
+
return { ...controlled.output, scopes: controlled.scopes, log: controlled.log };
|
|
13481
14271
|
};
|
|
14272
|
+
const execute = async (workflowstep, stepcontext) => await executesteprouted(workflowstep, stepcontext);
|
|
13482
14273
|
let result;
|
|
14274
|
+
const runbudget = options.timeout && typeof options.timeout === "object" && !Array.isArray(options.timeout) && typeof options.timeout.runms === "number" && Number.isFinite(options.timeout.runms) && options.timeout.runms > 0 ? options.timeout.runms : void 0;
|
|
13483
14275
|
try {
|
|
13484
|
-
|
|
14276
|
+
const launch = () => runworkflow({
|
|
13485
14277
|
record: record2,
|
|
13486
14278
|
run,
|
|
13487
14279
|
scopes: runscopes(options.variables),
|
|
@@ -13497,6 +14289,18 @@ async function executeworkflowrun(step, session, plan, tabid2, origin, dry) {
|
|
|
13497
14289
|
await refreshbadge();
|
|
13498
14290
|
}
|
|
13499
14291
|
});
|
|
14292
|
+
if (runbudget === void 0) {
|
|
14293
|
+
result = await launch();
|
|
14294
|
+
} else {
|
|
14295
|
+
const raced = await applyruntimeout({ budgetms: runbudget, run: launch });
|
|
14296
|
+
if (raced.cancelled) {
|
|
14297
|
+
guards.cancelled = true;
|
|
14298
|
+
const aborted = await storetimeoutabort(run, step, raced.error.message, runbudget);
|
|
14299
|
+
result = { run: aborted.run, scopes: [], log: aborted.log, outputs: {} };
|
|
14300
|
+
} else {
|
|
14301
|
+
result = raced.value;
|
|
14302
|
+
}
|
|
14303
|
+
}
|
|
13500
14304
|
} finally {
|
|
13501
14305
|
activeworkflowruns.delete(run.id);
|
|
13502
14306
|
}
|
|
@@ -13506,12 +14310,59 @@ async function executeworkflowrun(step, session, plan, tabid2, origin, dry) {
|
|
|
13506
14310
|
if (!stored.some((candidate) => candidate.stepid === entry.stepid && candidate.startedat === entry.startedat)) await memory.addrunlogentry(run.id, entry);
|
|
13507
14311
|
}
|
|
13508
14312
|
await memory.setrunscopes(run.id, result.scopes);
|
|
14313
|
+
const decisions = [];
|
|
14314
|
+
for (const entry of result.log) {
|
|
14315
|
+
const control = entry.details?.control;
|
|
14316
|
+
if (control && typeof control === "object" && !Array.isArray(control)) {
|
|
14317
|
+
const decision = control;
|
|
14318
|
+
decisions.push(decision);
|
|
14319
|
+
await memory.addcontroldecision(run.id, decision);
|
|
14320
|
+
}
|
|
14321
|
+
}
|
|
14322
|
+
for (const decision of decisions) await auditcontroldecision(run.id, decision, session.id, plan.id);
|
|
14323
|
+
const loopbounds = record2.steps.flatMap((controlstep) => {
|
|
14324
|
+
const summary = controlsummary(controlstep);
|
|
14325
|
+
return summary !== void 0 && summary.bound !== void 0 && (controlstep.kind === "loop" || controlstep.kind === "repeatuntil" || controlstep.kind === "whileloop") ? [summary.bound] : [];
|
|
14326
|
+
});
|
|
14327
|
+
if (loopbounds.length > 0) {
|
|
14328
|
+
const iterations = decisions.filter((decision) => decision.kind === "loop").reduce((total, decision) => total + (decision.loops?.length ?? 0), 0);
|
|
14329
|
+
const denominator = loopbounds.reduce((total, bound) => total + bound, 0);
|
|
14330
|
+
await memory.setprogress(recordworkflow(await memory.getprogress(), plan.id, step.id, { family: "control", detail: `The control flow steps of the workflow ${record2.name} ran their loops toward the reviewed bounds`, runid: run.id, iterations, denominator }, Date.now()));
|
|
14331
|
+
}
|
|
13509
14332
|
const executed = result.run.cursor;
|
|
13510
14333
|
await memory.setprogress(recordworkflow(await memory.getprogress(), plan.id, step.id, { family: "run", detail: `Ran the workflow ${record2.name}`, runid: run.id, executed, total: record2.steps.length }, Date.now()));
|
|
13511
14334
|
await audit("workflow", `The run ${run.id} of the workflow ${record2.name} ended ${result.run.state} after ${executed} of ${record2.steps.length} steps${result.run.failreason !== void 0 ? ` with the failure ${result.run.failreason}` : ""}.`, { sessionid: session.id, planid: plan.id, stepid: step.id });
|
|
13512
14335
|
await refreshbadge();
|
|
13513
14336
|
return { ok: result.run.state === "done", summary: `The workflow run ended ${result.run.state} after ${executed} of ${record2.steps.length} steps.`, details: { runid: run.id, state: result.run.state, executed, total: record2.steps.length, ...result.run.failreason !== void 0 ? { failreason: result.run.failreason } : {} } };
|
|
13514
14337
|
}
|
|
14338
|
+
async function pagestateof(tabid2) {
|
|
14339
|
+
const tab = await chrome.tabs.get(tabid2).catch(() => void 0);
|
|
14340
|
+
if (!tab) return {};
|
|
14341
|
+
return { ...tab.url !== void 0 ? { url: tab.url } : {}, ...tab.title !== void 0 ? { title: tab.title } : {}, ...tab.status !== void 0 ? { ready: tab.status === "complete" } : {} };
|
|
14342
|
+
}
|
|
14343
|
+
async function resolveelements(tabid2, selector) {
|
|
14344
|
+
const resolved = await bridgecall(tabid2, "queryelements", selector).catch(() => void 0);
|
|
14345
|
+
if (!resolved?.ok) return [];
|
|
14346
|
+
return resolved.selectors ?? [];
|
|
14347
|
+
}
|
|
14348
|
+
async function storetimeoutabort(run, step, message, budget) {
|
|
14349
|
+
const at = Date.now();
|
|
14350
|
+
const aborted = { ...run, state: "failed", endedat: at, failreason: message };
|
|
14351
|
+
const entry = { stepid: step.id, label: `Run budget of ${step.id}`, state: "failed", startedat: at, duration: 0, summary: message, details: { errorclass: "timeout", budget, cancelled: true, control: { runid: run.id, stepid: step.id, kind: "timeout", at, timeouts: [{ stepid: step.id, budget, scope: "run", at }] } } };
|
|
14352
|
+
const decision = entry.details?.control;
|
|
14353
|
+
await memory.addcontroldecision(run.id, decision);
|
|
14354
|
+
await memory.addrunlogentry(run.id, entry);
|
|
14355
|
+
await audit("workflow", `The run ${run.id} exceeded its reviewed budget of ${budget} milliseconds and was cancelled with the cancelled error class.`, { planid: step.id });
|
|
14356
|
+
return { run: aborted, log: [entry] };
|
|
14357
|
+
}
|
|
14358
|
+
async function auditcontroldecision(runid, decision, sessionid, planid) {
|
|
14359
|
+
if (decision.kind === "branch" && decision.branch !== void 0) await audit("workflow", `The branch step ${decision.stepid} of the run ${runid} chose the path ${decision.branch.path}: ${decision.branch.reason}`, { sessionid, planid });
|
|
14360
|
+
else if (decision.kind === "loop" && decision.loops !== void 0) await audit("workflow", `The loop step ${decision.stepid} of the run ${runid} recorded ${decision.loops.length} iteration counter${decision.loops.length === 1 ? "" : "s"} with the paths ${decision.loops.map((counter) => counter.path).join(", ")}.`, { sessionid, planid });
|
|
14361
|
+
else if (decision.kind === "join" && decision.join !== void 0) await audit("workflow", `The join of the parallel step ${decision.stepid} of the run ${runid} merged ${decision.join.merged.length} variable${decision.join.merged.length === 1 ? "" : "s"} under the ${decision.join.strategy} strategy${decision.join.conflicts.length > 0 ? ` with the conflicts ${decision.join.conflicts.join(", ")}` : " with no conflict"}.`, { sessionid, planid });
|
|
14362
|
+
else if (decision.kind === "catch" && decision.catch !== void 0) await audit("workflow", `The catch handler of the try step ${decision.stepid} of the run ${runid} ran after the ${decision.catch.errorclass} failure${decision.catch.rerun ? " and reran the fragile body" : ""}.`, { sessionid, planid });
|
|
14363
|
+
else if (decision.kind === "retry" && decision.retries !== void 0) await audit("workflow", `The retry policy of the try step ${decision.stepid} of the run ${runid} ran ${decision.retries.length} retry attempt${decision.retries.length === 1 ? "" : "s"} with the backoff durations ${decision.retries.map((attempt) => `${attempt.delay} ms`).join(", ")}.`, { sessionid, planid });
|
|
14364
|
+
else if (decision.kind === "timeout" && decision.timeouts !== void 0) await audit("workflow", `The timeout policy of the try step ${decision.stepid} of the run ${runid} aborted ${decision.timeouts.length} step${decision.timeouts.length === 1 ? "" : "s"} that exceeded their reviewed budgets.`, { sessionid, planid });
|
|
14365
|
+
}
|
|
13515
14366
|
async function executestep(stepid) {
|
|
13516
14367
|
const session = await memory.getsession();
|
|
13517
14368
|
const plan = await memory.getplan();
|
|
@@ -13845,7 +14696,7 @@ async function handlerequest(message, sender) {
|
|
|
13845
14696
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
13846
14697
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
13847
14698
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
13848
|
-
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
14699
|
+
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
13849
14700
|
}
|
|
13850
14701
|
case "capabilities":
|
|
13851
14702
|
return refreshcapabilities();
|
|
@@ -15016,7 +15867,49 @@ async function handlerequest(message, sender) {
|
|
|
15016
15867
|
const inputreview = message;
|
|
15017
15868
|
const record2 = await memory.getworkflowrecord(inputreview.workflowid?.trim() ?? "");
|
|
15018
15869
|
if (!record2) throw new Error(`No composed workflow matches ${inputreview.workflowid ?? ""}.`);
|
|
15019
|
-
return { record: record2, steps: record2.steps.map((entry) => ({ id: entry.id, kind: entry.kind, label: entry.label, ...entry.target !== void 0 ? { target: entry.target } : {}, ...entry.value !== void 0 ? { value: entry.value } : {}, ...entry.block !== void 0 ? { block: entry.block } : {}, ...entry.bindings !== void 0 ? { bindings: entry.bindings } : {}, ...entry.expression !== void 0 ? { expression: entry.expression } : {}, ...entry.extract !== void 0 ? { extract: entry.extract } : {} })), blocks: record2.blocks, risk: record2.risk, origins: record2.origins };
|
|
15870
|
+
return { record: record2, steps: record2.steps.map((entry) => ({ id: entry.id, kind: entry.kind, label: entry.label, ...entry.target !== void 0 ? { target: entry.target } : {}, ...entry.value !== void 0 ? { value: entry.value } : {}, ...entry.block !== void 0 ? { block: entry.block } : {}, ...entry.bindings !== void 0 ? { bindings: entry.bindings } : {}, ...entry.expression !== void 0 ? { expression: entry.expression } : {}, ...entry.extract !== void 0 ? { extract: entry.extract } : {}, ...controlsummary(entry) !== void 0 ? { control: controlsummary(entry) } : {} })), blocks: record2.blocks, risk: record2.risk, origins: record2.origins };
|
|
15871
|
+
}
|
|
15872
|
+
case "setloopbound": {
|
|
15873
|
+
const inputbound = message;
|
|
15874
|
+
const session = await memory.getsession();
|
|
15875
|
+
if (!session || session.stoppedat || session.expiresat <= Date.now()) throw new Error("Editing a loop safety bound needs an active browser session behind the consent gates.");
|
|
15876
|
+
const record2 = await memory.getworkflowrecord(inputbound.workflowid?.trim() ?? "");
|
|
15877
|
+
if (!record2) throw new Error(`No composed workflow matches ${inputbound.workflowid ?? ""}.`);
|
|
15878
|
+
if (typeof inputbound.bound !== "number" || !Number.isInteger(inputbound.bound) || inputbound.bound < 1) throw new Error("The loop safety bound must be a positive integer with no code ceiling.");
|
|
15879
|
+
const target = record2.steps.find((entry) => entry.id === (inputbound.stepid ?? ""));
|
|
15880
|
+
if (!target || target.kind !== "loop" && target.kind !== "repeatuntil") throw new Error(`No loop or repeat until step of the workflow matches ${inputbound.stepid ?? ""}.`);
|
|
15881
|
+
let payload;
|
|
15882
|
+
try {
|
|
15883
|
+
payload = JSON.parse(target.options ?? "{}");
|
|
15884
|
+
} catch {
|
|
15885
|
+
payload = {};
|
|
15886
|
+
}
|
|
15887
|
+
const key = target.kind === "loop" ? "loop" : "repeatuntil";
|
|
15888
|
+
const body = payload[key] && typeof payload[key] === "object" && !Array.isArray(payload[key]) ? payload[key] : {};
|
|
15889
|
+
body.bound = inputbound.bound;
|
|
15890
|
+
payload[key] = body;
|
|
15891
|
+
const restep = { ...target, options: JSON.stringify(payload) };
|
|
15892
|
+
const recomposed = composeworkflow({
|
|
15893
|
+
id: record2.id,
|
|
15894
|
+
name: record2.name,
|
|
15895
|
+
version: record2.version + 1,
|
|
15896
|
+
origins: [...record2.origins],
|
|
15897
|
+
steps: record2.steps.map((entry) => entry.id === target.id ? restep : entry),
|
|
15898
|
+
blocks: [...record2.blocks],
|
|
15899
|
+
now: Date.now(),
|
|
15900
|
+
kindallowed: (kind) => {
|
|
15901
|
+
try {
|
|
15902
|
+
actionrisk(kind);
|
|
15903
|
+
return true;
|
|
15904
|
+
} catch {
|
|
15905
|
+
return false;
|
|
15906
|
+
}
|
|
15907
|
+
},
|
|
15908
|
+
riskof: (kind) => actionrisk(kind)
|
|
15909
|
+
});
|
|
15910
|
+
await memory.addworkflowrecord(recomposed);
|
|
15911
|
+
await audit("workflow", `The user edited the loop safety bound of the step ${target.id} of the workflow ${record2.name} to ${inputbound.bound} iterations; version ${recomposed.version} was recomposed and the older version survives for the audit trail.`, { sessionid: session.id });
|
|
15912
|
+
return { workflowid: recomposed.id, stepid: target.id, bound: inputbound.bound, version: recomposed.version };
|
|
15020
15913
|
}
|
|
15021
15914
|
case "approveworkflowrun": {
|
|
15022
15915
|
const inputapprove = message;
|
|
@@ -15039,8 +15932,24 @@ async function handlerequest(message, sender) {
|
|
|
15039
15932
|
const step = record2.steps.find((entry) => entry.id === (inputsingle.stepid ?? ""));
|
|
15040
15933
|
if (!step) throw new Error(`No step of the workflow matches ${inputsingle.stepid ?? ""}.`);
|
|
15041
15934
|
const { tab, origin } = await activecontext();
|
|
15042
|
-
const
|
|
15935
|
+
const executestepsingle = async (dispatched, stepcontext) => {
|
|
15936
|
+
if (iscontrolflowkind(dispatched.kind)) {
|
|
15937
|
+
const controlled = await runcontrolstep({ step: dispatched, scopes: stepcontext.scopes, outputs: stepcontext.outputs ?? {}, execute: executestepsingle, now: Date.now(), runid: stored.run.id, pagestate: await pagestateof(tab.id), resolveelements: async (selector) => await resolveelements(tab.id, selector) });
|
|
15938
|
+
return { ...controlled.output, scopes: controlled.scopes, log: controlled.log };
|
|
15939
|
+
}
|
|
15940
|
+
return await dispatchworkflowstep(dispatched, { session, plan, tabid: tab.id, origin });
|
|
15941
|
+
};
|
|
15942
|
+
const executed = await runstep({ step, scopes: await memory.getrunscopes(stored.run.id), outputs: {}, execute: executestepsingle, now: Date.now(), ...step.block !== void 0 ? { block: step.block } : {} });
|
|
15043
15943
|
await memory.addrunlogentry(stored.run.id, executed.log);
|
|
15944
|
+
if (executed.childlog !== void 0) for (const entry of executed.childlog) await memory.addrunlogentry(stored.run.id, entry);
|
|
15945
|
+
for (const entry of [executed.log, ...executed.childlog ?? []]) {
|
|
15946
|
+
const control = entry.details?.control;
|
|
15947
|
+
if (control && typeof control === "object" && !Array.isArray(control)) {
|
|
15948
|
+
const decision = control;
|
|
15949
|
+
await memory.addcontroldecision(stored.run.id, decision);
|
|
15950
|
+
await auditcontroldecision(stored.run.id, decision, session.id, plan.id);
|
|
15951
|
+
}
|
|
15952
|
+
}
|
|
15044
15953
|
await memory.setrunscopes(stored.run.id, executed.scopes);
|
|
15045
15954
|
const stepindex = record2.steps.findIndex((entry) => entry.id === step.id);
|
|
15046
15955
|
const isthenext = stepindex === stored.run.cursor && executed.output.ok;
|
|
@@ -15073,15 +15982,32 @@ async function handlerequest(message, sender) {
|
|
|
15073
15982
|
const scopes = await memory.getrunscopes(stored.run.id);
|
|
15074
15983
|
const log = await memory.getrunlog(stored.run.id);
|
|
15075
15984
|
const { tab, origin } = await activecontext();
|
|
15076
|
-
|
|
15985
|
+
let storedentries = log.length;
|
|
15986
|
+
const executeresume = async (dispatched, stepcontext) => {
|
|
15987
|
+
if (iscontrolflowkind(dispatched.kind)) {
|
|
15988
|
+
const controlled = await runcontrolstep({ step: dispatched, scopes: stepcontext.scopes, outputs: stepcontext.outputs ?? {}, execute: executeresume, now: Date.now(), runid: stored.run.id, pagestate: await pagestateof(tab.id), resolveelements: async (selector) => await resolveelements(tab.id, selector) });
|
|
15989
|
+
return { ...controlled.output, scopes: controlled.scopes, log: controlled.log };
|
|
15990
|
+
}
|
|
15991
|
+
return await dispatchworkflowstep(dispatched, { session, plan, tabid: tab.id, origin });
|
|
15992
|
+
};
|
|
15993
|
+
const resumed = await runworkflow({ record: record2, run: stored.run, scopes, log, execute: executeresume, now: Date.now(), gates: { sessionactive: Boolean(session && !session.stoppedat && !session.pausedat && session.expiresat > Date.now()), planapproved: plan.state === "approved", origingranted: (workfloworigin) => origingranted(session, workfloworigin) }, oncheckpoint: async (state) => {
|
|
15077
15994
|
await memory.setworkflowrun(state.run);
|
|
15078
|
-
const
|
|
15079
|
-
|
|
15995
|
+
for (const entry of state.log.slice(storedentries)) await memory.addrunlogentry(state.run.id, entry);
|
|
15996
|
+
storedentries = state.log.length;
|
|
15080
15997
|
await memory.setrunscopes(state.run.id, state.scopes);
|
|
15081
15998
|
} });
|
|
15999
|
+
for (const entry of resumed.log.slice(storedentries)) await memory.addrunlogentry(stored.run.id, entry);
|
|
16000
|
+
for (const entry of resumed.log.slice(log.length)) {
|
|
16001
|
+
const control = entry.details?.control;
|
|
16002
|
+
if (control && typeof control === "object" && !Array.isArray(control)) {
|
|
16003
|
+
const decision = control;
|
|
16004
|
+
await memory.addcontroldecision(stored.run.id, decision);
|
|
16005
|
+
await auditcontroldecision(stored.run.id, decision, session.id, plan.id);
|
|
16006
|
+
}
|
|
16007
|
+
}
|
|
15082
16008
|
await memory.setworkflowrun(resumed.run);
|
|
15083
16009
|
await memory.setrunscopes(stored.run.id, resumed.scopes);
|
|
15084
|
-
await audit("workflow", `Resumed the workflow run ${stored.run.id} from the checkpoint at step cursor ${stored.run.cursor}; the run ended ${resumed.run.state} at cursor ${resumed.run.cursor}.`, { sessionid: session.id, planid: plan.id });
|
|
16010
|
+
await audit("workflow", `Resumed the workflow run ${stored.run.id} from the checkpoint at step cursor ${stored.run.cursor}; the run ended ${resumed.run.state} at cursor ${resumed.run.cursor} with ${resumed.log.length - log.length} new runlog entries.`, { sessionid: session.id, planid: plan.id });
|
|
15085
16011
|
await refreshbadge();
|
|
15086
16012
|
return { runid: resumed.run.id, state: resumed.run.state, cursor: resumed.run.cursor };
|
|
15087
16013
|
}
|