@qawolf/cli 1.24.0 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2116 -678
- package/dist/runner-sdk.js +724 -146
- package/package.json +2 -2
- package/skills/qawolf-cli/SKILL.md +6 -3
package/dist/runner-sdk.js
CHANGED
|
@@ -5494,9 +5494,34 @@ var bigintProcessor = (_schema, ctx, _json, _params) => {
|
|
|
5494
5494
|
throw new Error("BigInt cannot be represented in JSON Schema");
|
|
5495
5495
|
}
|
|
5496
5496
|
};
|
|
5497
|
+
var symbolProcessor = (_schema, ctx, _json, _params) => {
|
|
5498
|
+
if (ctx.unrepresentable === "throw") {
|
|
5499
|
+
throw new Error("Symbols cannot be represented in JSON Schema");
|
|
5500
|
+
}
|
|
5501
|
+
};
|
|
5502
|
+
var nullProcessor = (_schema, ctx, json, _params) => {
|
|
5503
|
+
if (ctx.target === "openapi-3.0") {
|
|
5504
|
+
json.type = "string";
|
|
5505
|
+
json.nullable = true;
|
|
5506
|
+
json.enum = [null];
|
|
5507
|
+
} else {
|
|
5508
|
+
json.type = "null";
|
|
5509
|
+
}
|
|
5510
|
+
};
|
|
5511
|
+
var undefinedProcessor = (_schema, ctx, _json, _params) => {
|
|
5512
|
+
if (ctx.unrepresentable === "throw") {
|
|
5513
|
+
throw new Error("Undefined cannot be represented in JSON Schema");
|
|
5514
|
+
}
|
|
5515
|
+
};
|
|
5516
|
+
var voidProcessor = (_schema, ctx, _json, _params) => {
|
|
5517
|
+
if (ctx.unrepresentable === "throw") {
|
|
5518
|
+
throw new Error("Void cannot be represented in JSON Schema");
|
|
5519
|
+
}
|
|
5520
|
+
};
|
|
5497
5521
|
var neverProcessor = (_schema, _ctx, json, _params) => {
|
|
5498
5522
|
json.not = {};
|
|
5499
5523
|
};
|
|
5524
|
+
var anyProcessor = (_schema, _ctx, _json, _params) => {};
|
|
5500
5525
|
var unknownProcessor = (_schema, _ctx, _json, _params) => {};
|
|
5501
5526
|
var dateProcessor = (_schema, ctx, _json, _params) => {
|
|
5502
5527
|
if (ctx.unrepresentable === "throw") {
|
|
@@ -5550,16 +5575,71 @@ var literalProcessor = (schema, ctx, json, _params) => {
|
|
|
5550
5575
|
json.enum = vals;
|
|
5551
5576
|
}
|
|
5552
5577
|
};
|
|
5578
|
+
var nanProcessor = (_schema, ctx, _json, _params) => {
|
|
5579
|
+
if (ctx.unrepresentable === "throw") {
|
|
5580
|
+
throw new Error("NaN cannot be represented in JSON Schema");
|
|
5581
|
+
}
|
|
5582
|
+
};
|
|
5583
|
+
var templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
|
5584
|
+
const _json = json;
|
|
5585
|
+
const pattern = schema._zod.pattern;
|
|
5586
|
+
if (!pattern)
|
|
5587
|
+
throw new Error("Pattern not found in template literal");
|
|
5588
|
+
_json.type = "string";
|
|
5589
|
+
_json.pattern = pattern.source;
|
|
5590
|
+
};
|
|
5591
|
+
var fileProcessor = (schema, _ctx, json, _params) => {
|
|
5592
|
+
const _json = json;
|
|
5593
|
+
const file = {
|
|
5594
|
+
type: "string",
|
|
5595
|
+
format: "binary",
|
|
5596
|
+
contentEncoding: "binary"
|
|
5597
|
+
};
|
|
5598
|
+
const { minimum, maximum, mime } = schema._zod.bag;
|
|
5599
|
+
if (minimum !== undefined)
|
|
5600
|
+
file.minLength = minimum;
|
|
5601
|
+
if (maximum !== undefined)
|
|
5602
|
+
file.maxLength = maximum;
|
|
5603
|
+
if (mime) {
|
|
5604
|
+
if (mime.length === 1) {
|
|
5605
|
+
file.contentMediaType = mime[0];
|
|
5606
|
+
Object.assign(_json, file);
|
|
5607
|
+
} else {
|
|
5608
|
+
Object.assign(_json, file);
|
|
5609
|
+
_json.anyOf = mime.map((m) => ({ contentMediaType: m }));
|
|
5610
|
+
}
|
|
5611
|
+
} else {
|
|
5612
|
+
Object.assign(_json, file);
|
|
5613
|
+
}
|
|
5614
|
+
};
|
|
5615
|
+
var successProcessor = (_schema, _ctx, json, _params) => {
|
|
5616
|
+
json.type = "boolean";
|
|
5617
|
+
};
|
|
5553
5618
|
var customProcessor = (_schema, ctx, _json, _params) => {
|
|
5554
5619
|
if (ctx.unrepresentable === "throw") {
|
|
5555
5620
|
throw new Error("Custom types cannot be represented in JSON Schema");
|
|
5556
5621
|
}
|
|
5557
5622
|
};
|
|
5623
|
+
var functionProcessor = (_schema, ctx, _json, _params) => {
|
|
5624
|
+
if (ctx.unrepresentable === "throw") {
|
|
5625
|
+
throw new Error("Function types cannot be represented in JSON Schema");
|
|
5626
|
+
}
|
|
5627
|
+
};
|
|
5558
5628
|
var transformProcessor = (_schema, ctx, _json, _params) => {
|
|
5559
5629
|
if (ctx.unrepresentable === "throw") {
|
|
5560
5630
|
throw new Error("Transforms cannot be represented in JSON Schema");
|
|
5561
5631
|
}
|
|
5562
5632
|
};
|
|
5633
|
+
var mapProcessor = (_schema, ctx, _json, _params) => {
|
|
5634
|
+
if (ctx.unrepresentable === "throw") {
|
|
5635
|
+
throw new Error("Map cannot be represented in JSON Schema");
|
|
5636
|
+
}
|
|
5637
|
+
};
|
|
5638
|
+
var setProcessor = (_schema, ctx, _json, _params) => {
|
|
5639
|
+
if (ctx.unrepresentable === "throw") {
|
|
5640
|
+
throw new Error("Set cannot be represented in JSON Schema");
|
|
5641
|
+
}
|
|
5642
|
+
};
|
|
5563
5643
|
var arrayProcessor = (schema, ctx, _json, params) => {
|
|
5564
5644
|
const json = _json;
|
|
5565
5645
|
const def = schema._zod.def;
|
|
@@ -5640,6 +5720,48 @@ var intersectionProcessor = (schema, ctx, json, params) => {
|
|
|
5640
5720
|
];
|
|
5641
5721
|
json.allOf = allOf;
|
|
5642
5722
|
};
|
|
5723
|
+
var tupleProcessor = (schema, ctx, _json, params) => {
|
|
5724
|
+
const json = _json;
|
|
5725
|
+
const def = schema._zod.def;
|
|
5726
|
+
json.type = "array";
|
|
5727
|
+
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
5728
|
+
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
5729
|
+
const prefixItems = def.items.map((x, i) => process2(x, ctx, {
|
|
5730
|
+
...params,
|
|
5731
|
+
path: [...params.path, prefixPath, i]
|
|
5732
|
+
}));
|
|
5733
|
+
const rest = def.rest ? process2(def.rest, ctx, {
|
|
5734
|
+
...params,
|
|
5735
|
+
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
5736
|
+
}) : null;
|
|
5737
|
+
if (ctx.target === "draft-2020-12") {
|
|
5738
|
+
json.prefixItems = prefixItems;
|
|
5739
|
+
if (rest) {
|
|
5740
|
+
json.items = rest;
|
|
5741
|
+
}
|
|
5742
|
+
} else if (ctx.target === "openapi-3.0") {
|
|
5743
|
+
json.items = {
|
|
5744
|
+
anyOf: prefixItems
|
|
5745
|
+
};
|
|
5746
|
+
if (rest) {
|
|
5747
|
+
json.items.anyOf.push(rest);
|
|
5748
|
+
}
|
|
5749
|
+
json.minItems = prefixItems.length;
|
|
5750
|
+
if (!rest) {
|
|
5751
|
+
json.maxItems = prefixItems.length;
|
|
5752
|
+
}
|
|
5753
|
+
} else {
|
|
5754
|
+
json.items = prefixItems;
|
|
5755
|
+
if (rest) {
|
|
5756
|
+
json.additionalItems = rest;
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5759
|
+
const { minimum, maximum } = schema._zod.bag;
|
|
5760
|
+
if (typeof minimum === "number")
|
|
5761
|
+
json.minItems = minimum;
|
|
5762
|
+
if (typeof maximum === "number")
|
|
5763
|
+
json.maxItems = maximum;
|
|
5764
|
+
};
|
|
5643
5765
|
var recordProcessor = (schema, ctx, _json, params) => {
|
|
5644
5766
|
const json = _json;
|
|
5645
5767
|
const def = schema._zod.def;
|
|
@@ -5736,6 +5858,12 @@ var readonlyProcessor = (schema, ctx, json, params) => {
|
|
|
5736
5858
|
seen.ref = def.innerType;
|
|
5737
5859
|
json.readOnly = true;
|
|
5738
5860
|
};
|
|
5861
|
+
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
5862
|
+
const def = schema._zod.def;
|
|
5863
|
+
process2(def.innerType, ctx, params);
|
|
5864
|
+
const seen = ctx.seen.get(schema);
|
|
5865
|
+
seen.ref = def.innerType;
|
|
5866
|
+
};
|
|
5739
5867
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
5740
5868
|
const def = schema._zod.def;
|
|
5741
5869
|
process2(def.innerType, ctx, params);
|
|
@@ -5748,6 +5876,81 @@ var lazyProcessor = (schema, ctx, _json, params) => {
|
|
|
5748
5876
|
const seen = ctx.seen.get(schema);
|
|
5749
5877
|
seen.ref = innerType;
|
|
5750
5878
|
};
|
|
5879
|
+
var allProcessors = {
|
|
5880
|
+
string: stringProcessor,
|
|
5881
|
+
number: numberProcessor,
|
|
5882
|
+
boolean: booleanProcessor,
|
|
5883
|
+
bigint: bigintProcessor,
|
|
5884
|
+
symbol: symbolProcessor,
|
|
5885
|
+
null: nullProcessor,
|
|
5886
|
+
undefined: undefinedProcessor,
|
|
5887
|
+
void: voidProcessor,
|
|
5888
|
+
never: neverProcessor,
|
|
5889
|
+
any: anyProcessor,
|
|
5890
|
+
unknown: unknownProcessor,
|
|
5891
|
+
date: dateProcessor,
|
|
5892
|
+
enum: enumProcessor,
|
|
5893
|
+
literal: literalProcessor,
|
|
5894
|
+
nan: nanProcessor,
|
|
5895
|
+
template_literal: templateLiteralProcessor,
|
|
5896
|
+
file: fileProcessor,
|
|
5897
|
+
success: successProcessor,
|
|
5898
|
+
custom: customProcessor,
|
|
5899
|
+
function: functionProcessor,
|
|
5900
|
+
transform: transformProcessor,
|
|
5901
|
+
map: mapProcessor,
|
|
5902
|
+
set: setProcessor,
|
|
5903
|
+
array: arrayProcessor,
|
|
5904
|
+
object: objectProcessor,
|
|
5905
|
+
union: unionProcessor,
|
|
5906
|
+
intersection: intersectionProcessor,
|
|
5907
|
+
tuple: tupleProcessor,
|
|
5908
|
+
record: recordProcessor,
|
|
5909
|
+
nullable: nullableProcessor,
|
|
5910
|
+
nonoptional: nonoptionalProcessor,
|
|
5911
|
+
default: defaultProcessor,
|
|
5912
|
+
prefault: prefaultProcessor,
|
|
5913
|
+
catch: catchProcessor,
|
|
5914
|
+
pipe: pipeProcessor,
|
|
5915
|
+
readonly: readonlyProcessor,
|
|
5916
|
+
promise: promiseProcessor,
|
|
5917
|
+
optional: optionalProcessor,
|
|
5918
|
+
lazy: lazyProcessor
|
|
5919
|
+
};
|
|
5920
|
+
function toJSONSchema(input, params) {
|
|
5921
|
+
if ("_idmap" in input) {
|
|
5922
|
+
const registry = input;
|
|
5923
|
+
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
5924
|
+
const defs = {};
|
|
5925
|
+
for (const entry of registry._idmap.entries()) {
|
|
5926
|
+
const [_, schema] = entry;
|
|
5927
|
+
process2(schema, ctx);
|
|
5928
|
+
}
|
|
5929
|
+
const schemas = {};
|
|
5930
|
+
const external = {
|
|
5931
|
+
registry,
|
|
5932
|
+
uri: params?.uri,
|
|
5933
|
+
defs
|
|
5934
|
+
};
|
|
5935
|
+
ctx.external = external;
|
|
5936
|
+
for (const entry of registry._idmap.entries()) {
|
|
5937
|
+
const [key, schema] = entry;
|
|
5938
|
+
extractDefs(ctx, schema);
|
|
5939
|
+
schemas[key] = finalize(ctx, schema);
|
|
5940
|
+
}
|
|
5941
|
+
if (Object.keys(defs).length > 0) {
|
|
5942
|
+
const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
|
|
5943
|
+
schemas.__shared = {
|
|
5944
|
+
[defsSegment]: defs
|
|
5945
|
+
};
|
|
5946
|
+
}
|
|
5947
|
+
return { schemas };
|
|
5948
|
+
}
|
|
5949
|
+
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
5950
|
+
process2(input, ctx);
|
|
5951
|
+
extractDefs(ctx, input);
|
|
5952
|
+
return finalize(ctx, input);
|
|
5953
|
+
}
|
|
5751
5954
|
// node_modules/zod/v4/classic/iso.js
|
|
5752
5955
|
var exports_iso = {};
|
|
5753
5956
|
__export(exports_iso, {
|
|
@@ -6790,6 +6993,10 @@ var makeEmailResourceSchema = () => resource({
|
|
|
6790
6993
|
replyTo: array(namedAddressSchema).optional(),
|
|
6791
6994
|
text: string2().optional().describe("The plain text body.")
|
|
6792
6995
|
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
6996
|
+
var makeEmailAddressResourceSchema = () => resource({
|
|
6997
|
+
address: string2().describe("The inbox address. Mail sent to any plus-suffixed form, such as inbox+abc@example.com, lands in the same inbox."),
|
|
6998
|
+
isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
|
|
6999
|
+
}, { urlFieldDescription: emailUrlFieldDescription });
|
|
6793
7000
|
|
|
6794
7001
|
// node_modules/@qawolf/api-contracts/dist/v1/identity/organization.js
|
|
6795
7002
|
var identityOrganization = object({
|
|
@@ -6833,6 +7040,10 @@ var identityResponse = union([
|
|
|
6833
7040
|
user: never().optional()
|
|
6834
7041
|
}).transform(({ organization }) => ({ organization }))
|
|
6835
7042
|
]);
|
|
7043
|
+
// node_modules/@qawolf/api-contracts/dist/v1/identity/organizations.js
|
|
7044
|
+
var identityOrganizationsResponse = object({
|
|
7045
|
+
organizations: array(identityOrganization)
|
|
7046
|
+
});
|
|
6836
7047
|
// node_modules/@qawolf/api-contracts/dist/v1/ids.js
|
|
6837
7048
|
var defaultIdSchemas = {
|
|
6838
7049
|
aiTask: string2().min(1).describe("The id of the AI task."),
|
|
@@ -7010,6 +7221,8 @@ var runnerNameSchema = _enum([
|
|
|
7010
7221
|
"android",
|
|
7011
7222
|
"ios"
|
|
7012
7223
|
]);
|
|
7224
|
+
var runnerWorkspaceRequirement = "Required when the credential is not bound to a single workspace, such as a user API key or an OAuth connection whose organization owns several.";
|
|
7225
|
+
var runnerWorkspaceIdDescription = `The workspace the runner belongs to. ${runnerWorkspaceRequirement}`;
|
|
7013
7226
|
var runnerIdSchema = string2().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "A runner id may contain only lowercase letters, digits and dashes, and must start and end with a letter or digit.");
|
|
7014
7227
|
var makeRunnerSchema = () => object({
|
|
7015
7228
|
gpuAccelerated: boolean2().describe("Whether the runner was placed on GPU hardware. Chosen by QA Wolf from the team's configuration and what the fleet can provide, not requested."),
|
|
@@ -7038,10 +7251,11 @@ var inspectRequestSchema = discriminatedUnion("what", [
|
|
|
7038
7251
|
what: literal("variable")
|
|
7039
7252
|
})
|
|
7040
7253
|
]);
|
|
7041
|
-
var makeInspectOnRunnerContract = () => {
|
|
7254
|
+
var makeInspectOnRunnerContract = (ids) => {
|
|
7042
7255
|
const input = object({
|
|
7043
7256
|
id: runnerIdSchema.describe("Id of the runner to inspect."),
|
|
7044
|
-
request: inspectRequestSchema
|
|
7257
|
+
request: inspectRequestSchema,
|
|
7258
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
7045
7259
|
});
|
|
7046
7260
|
const output = discriminatedUnion("outcome", [
|
|
7047
7261
|
object({
|
|
@@ -7189,10 +7403,11 @@ var inspectMobileAnswerSchema = discriminatedUnion("what", [
|
|
|
7189
7403
|
what: literal("elements")
|
|
7190
7404
|
})
|
|
7191
7405
|
]);
|
|
7192
|
-
var makeInspectMobileOnRunnerContract = () => {
|
|
7406
|
+
var makeInspectMobileOnRunnerContract = (ids) => {
|
|
7193
7407
|
const input = object({
|
|
7194
7408
|
id: runnerIdSchema.describe("Id of the runner to inspect."),
|
|
7195
|
-
request: inspectMobileRequestSchema
|
|
7409
|
+
request: inspectMobileRequestSchema,
|
|
7410
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
7196
7411
|
});
|
|
7197
7412
|
const output = discriminatedUnion("outcome", [
|
|
7198
7413
|
inspectMobileAnswerSchema,
|
|
@@ -7278,20 +7493,26 @@ var screenFailureReasons = [
|
|
|
7278
7493
|
var screenNotReadyDescription = "`screen-not-ready` if the runner has a screen that cannot serve this instant. Retry in a second or two: the desktop restarts when a run changes the display size, and it serves one see-or-act request at a time, so a screenshot or action already in flight is the usual reason. Do not submit a run to clear this — a run may restart the display and discard what is on it.";
|
|
7279
7494
|
var screenNeedsARunDescription = "`screen-needs-a-run` if the runner's virtual desktop has never started. Waiting will not change this and retrying is pointless — call `runner.runFlow` with a flow that opens a browser, then ask for the screen again. On a runner image with a browser that has never run anything, any `runner.performAction` also starts the browser itself. Evaluating a snippet does not start the desktop.";
|
|
7280
7495
|
var runnerHasNoScreenDescription = "`runner-has-no-screen` if this runner is not one that runs a browser on a virtual desktop. Nothing about it can be seen or driven, and retrying will never help — launch a `playwright` runner instead.";
|
|
7496
|
+
var screenshotImageSchema = string2().min(1).describe("The screenshot, as a base64-encoded JPEG. Decode it and write the bytes to a `.jpg` file — writing this string to the file leaves you with base64 text rather than an image.");
|
|
7281
7497
|
|
|
7282
7498
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/performAction.js
|
|
7283
7499
|
var unreachableDescription = "`runner-unreachable` if the runner could not be reached: it may still be starting, it may have terminated after inactivity, or it may have stopped answering mid-action. This does not mean the action was not performed — take a screenshot before repeating it.";
|
|
7284
7500
|
var notSupportedOnMobileDescription = "`action-not-supported-on-mobile` if the runner is a mobile device and this action has no touchscreen equivalent: `double_click`, `scroll`, `move`, `keypress` and `navigate`, and a `click` whose `button` is not `left`, are all pointer-device concepts a touchscreen has nothing to offer for. `click` taps, `drag` swipes between its path's first and last point, and `type` types into whatever the last tap focused.";
|
|
7501
|
+
var withScreenshotDescription = "With `withScreenshot: true`, the answer also carries `imageJpegBase64`: a screenshot taken after the action, once the screen has changed from just before it or half a second has passed, whichever comes first. One call instead of `runner.performAction` followed by `runner.takeScreenshot`, with no fixed wait in between. Anything animating on the page counts as a change. The action itself is performed exactly as without the option; if the screen could serve no frame after it — a display restart, or a `navigate` on a desktop that is still starting — the action's result comes back without `imageJpegBase64`, so take a screenshot separately rather than repeating the action. On a mobile runner the screenshot is the device's own, taken right after a performed action.";
|
|
7285
7502
|
var maxActionErrorMessageLength = 1000;
|
|
7286
|
-
var makePerformActionOnRunnerContract = () => {
|
|
7503
|
+
var makePerformActionOnRunnerContract = (ids) => {
|
|
7287
7504
|
const input = object({
|
|
7288
7505
|
action: browserActionSchema.describe("The action to perform. Coordinates are pixels on the screenshot's own coordinate space."),
|
|
7289
|
-
id: runnerIdSchema.describe("Id of the runner to act on.")
|
|
7506
|
+
id: runnerIdSchema.describe("Id of the runner to act on."),
|
|
7507
|
+
withScreenshot: boolean2().optional().describe("Also answer with a screenshot taken after the action, once the screen has changed or half a second has passed. Saves the separate `runner.takeScreenshot` call and its fixed wait."),
|
|
7508
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
7290
7509
|
});
|
|
7510
|
+
const screenshotAfterAction = screenshotImageSchema.optional();
|
|
7291
7511
|
const failure = discriminatedUnion("failureReason", [
|
|
7292
7512
|
object({
|
|
7293
7513
|
errorMessage: string2().max(maxActionErrorMessageLength).describe("What stopped the action from taking effect."),
|
|
7294
7514
|
failureReason: literal("action-failed"),
|
|
7515
|
+
imageJpegBase64: screenshotAfterAction,
|
|
7295
7516
|
outcome: literal("failure")
|
|
7296
7517
|
}),
|
|
7297
7518
|
makeRunnerFailureSchema([
|
|
@@ -7301,11 +7522,14 @@ var makePerformActionOnRunnerContract = () => {
|
|
|
7301
7522
|
])
|
|
7302
7523
|
]);
|
|
7303
7524
|
const output = discriminatedUnion("outcome", [
|
|
7304
|
-
object({
|
|
7525
|
+
object({
|
|
7526
|
+
imageJpegBase64: screenshotAfterAction,
|
|
7527
|
+
outcome: literal("success")
|
|
7528
|
+
}),
|
|
7305
7529
|
failure
|
|
7306
7530
|
]);
|
|
7307
7531
|
return {
|
|
7308
|
-
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. One action per request, and the runner serves one at a time. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). A success means the action took effect. \`action-failed\`, with a reason, if it reached the runner and did not take effect. On a runner image with a browser, the first action on a runner that has never run anything starts its browser and waits for it, so it can take up to a minute to answer — no \`runner.runFlow\` is needed before acting; if the browser is still starting when the wait runs out, the answer is \`screen-not-ready\` and retrying converges. On a mobile runner, the same \`screen-needs-a-run\` and \`screen-not-ready\` outcomes mean no Appium session has started yet, or it did not answer this instant; \`runner.runFlow\` is what starts one, same as a browser. \`screen-needs-a-run\` if the browser could not be started that way — usually a runner whose runs all finished without starting its desktop; call \`runner.runFlow\` with a flow that opens a browser. ${screenNotReadyDescription} A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${runnerHasNoScreenDescription} ${notSupportedOnMobileDescription} ${unreachableDescription}`,
|
|
7532
|
+
description: `Perform one raw browser action on an interactive runner: click, double_click, move, drag, scroll, keypress, type, or navigate. Coordinates are whole pixels on the runner's virtual desktop, in the same space as \`runner.takeScreenshot\`. One action per request, and the runner serves one at a time. The action shapes follow the computer-use vocabulary, minus \`screenshot\` (use \`runner.takeScreenshot\`) and \`wait\` (delay on the caller's side). A success means the action took effect. \`action-failed\`, with a reason, if it reached the runner and did not take effect. On a runner image with a browser, the first action on a runner that has never run anything starts its browser and waits for it, so it can take up to a minute to answer — no \`runner.runFlow\` is needed before acting; if the browser is still starting when the wait runs out, the answer is \`screen-not-ready\` and retrying converges. On a mobile runner, the same \`screen-needs-a-run\` and \`screen-not-ready\` outcomes mean no Appium session has started yet, or it did not answer this instant; \`runner.runFlow\` is what starts one, same as a browser. \`screen-needs-a-run\` if the browser could not be started that way — usually a runner whose runs all finished without starting its desktop; call \`runner.runFlow\` with a flow that opens a browser. ${screenNotReadyDescription} A \`navigate\` does not go through the screen, so a screen that is not ready does not stop it. ${runnerHasNoScreenDescription} ${notSupportedOnMobileDescription} ${withScreenshotDescription} ${unreachableDescription}`,
|
|
7309
7533
|
input,
|
|
7310
7534
|
kind: "write",
|
|
7311
7535
|
name: "runner.performAction",
|
|
@@ -7331,9 +7555,11 @@ var makeAgentGetContract = (ids) => {
|
|
|
7331
7555
|
replies: array(agentReplySchema).describe("Everything the AI has said so far, oldest first. A reply carrying choices is a question the work is blocked on."),
|
|
7332
7556
|
sessionId: ids.chatSession,
|
|
7333
7557
|
status: agentSessionStatusSchema
|
|
7334
|
-
}, {
|
|
7558
|
+
}, {
|
|
7559
|
+
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
7560
|
+
});
|
|
7335
7561
|
return {
|
|
7336
|
-
description: '
|
|
7562
|
+
description: 'Monitor a QA Wolf AI session by reading its status and replies. After agent.send, share the returned session URL before monitoring. Wait 30 to 60 seconds between checks; do not call this in a tight loop. Replies accumulate, so compare them with what you have already seen. Continue monitoring silently when the status and replies are unchanged; do not narrate waiting, announce the next check, or ask whether to keep monitoring. Report only substantive new progress, questions, blockers, or the final outcome. A status of "waiting-for-you" means the last reply is a question the work is blocked on, and answering it with agent.send is what unblocks it. Surface an explicit request for user input even if the status still says "working". Include the session URL when reporting a blocker or final outcome. On "completed", stop status checks and verify the requested result before claiming success. For new flows, validation, publication in the target environment, and readiness are separate checks; a Git push or final reply does not prove the flow is active. If every requested result is verified but status remains "working", report the mismatch and stop monitoring. Stop on "failed" or "cancelled" and report any confirmed partial result.',
|
|
7337
7563
|
input,
|
|
7338
7564
|
kind: "read",
|
|
7339
7565
|
name: "agent.get",
|
|
@@ -7352,9 +7578,11 @@ var makeAgentSendContract = (ids) => {
|
|
|
7352
7578
|
const output = resource({
|
|
7353
7579
|
sessionId: ids.chatSession.describe("Follow the work by passing this to agent.get."),
|
|
7354
7580
|
status: agentSessionStatusSchema
|
|
7355
|
-
}, {
|
|
7581
|
+
}, {
|
|
7582
|
+
urlFieldDescription: "Absolute URL of the live session in the QA Wolf app."
|
|
7583
|
+
});
|
|
7356
7584
|
return {
|
|
7357
|
-
description: "
|
|
7585
|
+
description: "Start or continue work with the QA Wolf AI and return a live session URL to share with the user. Use it to cover a user journey, investigate a failing run, or fix a broken flow. This is the one verb that starts work from nothing: every other write acts on a flow, run or issue that already exists. Returns sessionId, status, and url as soon as the request is accepted; work can take minutes to tens of minutes. After each send, make the next action a normal user-visible assistant message containing the exact returned url, before any tool call or wait. Tool output and internal reasoning do not count as sharing the link. Do not run a timer or monitoring call alongside this send. Acceptance does not mean the work is complete. Then monitor the session with agent.get, reporting new progress, blockers, and the final outcome rather than unchanged status. Send here again to answer a question or add context to the same session.",
|
|
7358
7586
|
input,
|
|
7359
7587
|
kind: "write",
|
|
7360
7588
|
name: "agent.send",
|
|
@@ -7457,10 +7685,7 @@ var makeListEmailAddressesContract = (ids) => {
|
|
|
7457
7685
|
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 50 })
|
|
7458
7686
|
});
|
|
7459
7687
|
const output = object({
|
|
7460
|
-
addresses: array(
|
|
7461
|
-
address: string2().describe("The inbox address. Mail sent to any plus-suffixed form, such as inbox+abc@example.com, lands in the same inbox."),
|
|
7462
|
-
isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
|
|
7463
|
-
}, { urlFieldDescription: emailUrlFieldDescription })).describe("The workspace's permanent inbox addresses, alphabetical."),
|
|
7688
|
+
addresses: array(makeEmailAddressResourceSchema()).describe("The workspace's permanent inbox addresses, alphabetical."),
|
|
7464
7689
|
nextCursor: nextCursorSchema
|
|
7465
7690
|
});
|
|
7466
7691
|
return {
|
|
@@ -7472,6 +7697,28 @@ var makeListEmailAddressesContract = (ids) => {
|
|
|
7472
7697
|
};
|
|
7473
7698
|
};
|
|
7474
7699
|
|
|
7700
|
+
// node_modules/@qawolf/api-contracts/dist/v1/email/registerAddress.js
|
|
7701
|
+
var hasNoPlusSuffix = (address) => {
|
|
7702
|
+
const [localPart] = address.split("@");
|
|
7703
|
+
return localPart !== undefined && !localPart.includes("+");
|
|
7704
|
+
};
|
|
7705
|
+
var makeRegisterEmailAddressContract = (ids) => {
|
|
7706
|
+
const input = object({
|
|
7707
|
+
address: email2().refine(hasNoPlusSuffix, {
|
|
7708
|
+
message: "Register the base address. Mail to a plus-suffixed form lands in the base inbox."
|
|
7709
|
+
}).describe("The inbox address to register. Its domain must be one QA Wolf serves for the workspace."),
|
|
7710
|
+
workspaceId: ids.workspace.optional().describe("The workspace to register the address in. Required when authenticating with an organization or user API key.")
|
|
7711
|
+
});
|
|
7712
|
+
const output = makeEmailAddressResourceSchema();
|
|
7713
|
+
return {
|
|
7714
|
+
description: "Register an inbox address for the workspace. Registering an address the workspace already has changes nothing. A refusal names the domains the workspace can use.",
|
|
7715
|
+
input,
|
|
7716
|
+
kind: "write",
|
|
7717
|
+
name: "email.registerAddress",
|
|
7718
|
+
output
|
|
7719
|
+
};
|
|
7720
|
+
};
|
|
7721
|
+
|
|
7475
7722
|
// node_modules/@qawolf/api-contracts/dist/v1/email/send.js
|
|
7476
7723
|
var recipientsSchema = array(email2()).max(50);
|
|
7477
7724
|
var maxAttachments = 10;
|
|
@@ -8112,14 +8359,31 @@ var makeReattemptRunContract = (ids) => {
|
|
|
8112
8359
|
};
|
|
8113
8360
|
};
|
|
8114
8361
|
|
|
8362
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/stop.js
|
|
8363
|
+
var makeStopRunContract = (ids) => {
|
|
8364
|
+
const input = object({ runId: ids.run });
|
|
8365
|
+
const output = resource({
|
|
8366
|
+
runId: ids.run,
|
|
8367
|
+
status: _enum(["accepted", "already-finished", "execution-not-found"]).describe('"accepted" means stopping was requested; poll run.get for the final result. "already-finished" means the run has already finished and its result is unchanged. "execution-not-found" means the run exists but no execution was found to stop; it may still be starting or may no longer be available. No stop request was accepted in that case.')
|
|
8368
|
+
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
8369
|
+
return {
|
|
8370
|
+
description: "Stop a run, including its queued flows and automatic retries. Stopping is asynchronous. Repeated requests are safe, and finished runs keep their results. A run that is still being created returns not found; retry once run.get returns the run. If run.get returns a different runId, use that ID. Poll run.get for results.",
|
|
8371
|
+
input,
|
|
8372
|
+
kind: "write",
|
|
8373
|
+
name: "run.stop",
|
|
8374
|
+
output
|
|
8375
|
+
};
|
|
8376
|
+
};
|
|
8377
|
+
|
|
8115
8378
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/evaluateSnippet.js
|
|
8116
8379
|
var maxSnippetCodeLength = 64 * 1024;
|
|
8117
|
-
var makeEvaluateSnippetOnRunnerContract = () => {
|
|
8380
|
+
var makeEvaluateSnippetOnRunnerContract = (ids) => {
|
|
8118
8381
|
const input = object({
|
|
8119
8382
|
code: string2().min(1).max(maxSnippetCodeLength).describe("The code to evaluate against the runner's live page, in the scope of `filePath` when one is given."),
|
|
8120
8383
|
filePath: runFilePathSchema.optional().describe("Path of the file the snippet is evaluated in the scope of. Omit to evaluate a snippet that imports nothing of yours."),
|
|
8121
8384
|
files: runFilesSchema.optional().describe("The file named by `filePath` and everything it imports. A runner holds no copy of your project, so a snippet's scope has to travel with it."),
|
|
8122
|
-
id: runnerIdSchema.describe("Id of the runner to evaluate the snippet on.")
|
|
8385
|
+
id: runnerIdSchema.describe("Id of the runner to evaluate the snippet on."),
|
|
8386
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8123
8387
|
}).refine((request) => request.filePath === undefined || Object.hasOwn(request.files ?? {}, request.filePath), {
|
|
8124
8388
|
error: "A snippet naming a file must carry that file in files.",
|
|
8125
8389
|
path: ["files"]
|
|
@@ -8145,9 +8409,10 @@ var makeEvaluateSnippetOnRunnerContract = () => {
|
|
|
8145
8409
|
};
|
|
8146
8410
|
|
|
8147
8411
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/get.js
|
|
8148
|
-
var makeGetRunnerContract = () => {
|
|
8412
|
+
var makeGetRunnerContract = (ids) => {
|
|
8149
8413
|
const input = object({
|
|
8150
|
-
id: runnerIdSchema.describe("Id of the runner to look up.")
|
|
8414
|
+
id: runnerIdSchema.describe("Id of the runner to look up."),
|
|
8415
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8151
8416
|
});
|
|
8152
8417
|
const output = object({
|
|
8153
8418
|
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
@@ -8165,10 +8430,11 @@ var makeGetRunnerContract = () => {
|
|
|
8165
8430
|
|
|
8166
8431
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/highlightSelector.js
|
|
8167
8432
|
var maxHighlightSelectorLength = 2000;
|
|
8168
|
-
var makeHighlightSelectorOnRunnerContract = () => {
|
|
8433
|
+
var makeHighlightSelectorOnRunnerContract = (ids) => {
|
|
8169
8434
|
const input = object({
|
|
8170
8435
|
id: runnerIdSchema.describe("Id of the runner to highlight on."),
|
|
8171
|
-
selector: string2().max(maxHighlightSelectorLength).describe("The selector to highlight. An empty string clears whatever is highlighted.")
|
|
8436
|
+
selector: string2().max(maxHighlightSelectorLength).describe("The selector to highlight. An empty string clears whatever is highlighted."),
|
|
8437
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8172
8438
|
});
|
|
8173
8439
|
const output = discriminatedUnion("outcome", [
|
|
8174
8440
|
object({
|
|
@@ -8203,12 +8469,13 @@ var makeHighlightSelectorOnRunnerContract = () => {
|
|
|
8203
8469
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/importPackage.js
|
|
8204
8470
|
var maxPackageNameLength = 214;
|
|
8205
8471
|
var maxPackageVersionLength = 256;
|
|
8206
|
-
var makeImportPackageOnRunnerContract = () => {
|
|
8472
|
+
var makeImportPackageOnRunnerContract = (ids) => {
|
|
8207
8473
|
const input = object({
|
|
8208
8474
|
id: runnerIdSchema.describe("Id of the runner to install into."),
|
|
8209
8475
|
npmDependencies: record(string2(), string2()).describe("The run's current npm dependencies, so the install resolves against them."),
|
|
8210
8476
|
packageName: string2().min(1).max(maxPackageNameLength),
|
|
8211
|
-
packageVersion: string2().min(1).max(maxPackageVersionLength)
|
|
8477
|
+
packageVersion: string2().min(1).max(maxPackageVersionLength),
|
|
8478
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8212
8479
|
});
|
|
8213
8480
|
const output = discriminatedUnion("outcome", [
|
|
8214
8481
|
object({ outcome: literal("success") }),
|
|
@@ -8228,10 +8495,11 @@ var makeImportPackageOnRunnerContract = () => {
|
|
|
8228
8495
|
};
|
|
8229
8496
|
|
|
8230
8497
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/index.js
|
|
8231
|
-
var makeLaunchRunnerContract = () => {
|
|
8498
|
+
var makeLaunchRunnerContract = (ids) => {
|
|
8232
8499
|
const input = object({
|
|
8233
8500
|
id: runnerIdSchema.describe("Id to launch the runner under, chosen by the caller. Launching the same id again returns the runner already running under it."),
|
|
8234
|
-
runnerName: runnerNameSchema.optional().describe("The runner family to launch. Defaults to the standard web family, playwright.")
|
|
8501
|
+
runnerName: runnerNameSchema.optional().describe("The runner family to launch. Defaults to the standard web family, playwright."),
|
|
8502
|
+
workspaceId: ids.workspace.optional().describe(`The workspace to launch the runner in. ${runnerWorkspaceRequirement}`)
|
|
8235
8503
|
});
|
|
8236
8504
|
const output = makeRunnerSchema().extend({
|
|
8237
8505
|
alreadyRunning: boolean2().describe("True when a runner was already running under this id and was returned unchanged, so no second runner was started and nothing new is billed. Not an error either way."),
|
|
@@ -8245,9 +8513,10 @@ var makeLaunchRunnerContract = () => {
|
|
|
8245
8513
|
output
|
|
8246
8514
|
};
|
|
8247
8515
|
};
|
|
8248
|
-
var makeTerminateRunnerContract = () => {
|
|
8516
|
+
var makeTerminateRunnerContract = (ids) => {
|
|
8249
8517
|
const input = object({
|
|
8250
|
-
id: runnerIdSchema.describe("Id of the runner to terminate.")
|
|
8518
|
+
id: runnerIdSchema.describe("Id of the runner to terminate."),
|
|
8519
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8251
8520
|
});
|
|
8252
8521
|
const output = object({
|
|
8253
8522
|
id: runnerIdSchema.describe("Id of the runner the call addressed."),
|
|
@@ -8264,14 +8533,16 @@ var makeTerminateRunnerContract = () => {
|
|
|
8264
8533
|
};
|
|
8265
8534
|
|
|
8266
8535
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/list.js
|
|
8267
|
-
var makeListRunnersContract = () => {
|
|
8268
|
-
const input = object({
|
|
8536
|
+
var makeListRunnersContract = (ids) => {
|
|
8537
|
+
const input = object({
|
|
8538
|
+
workspaceId: ids.workspace.optional().describe(`The workspace whose runners to list. ${runnerWorkspaceRequirement}`)
|
|
8539
|
+
});
|
|
8269
8540
|
const output = object({
|
|
8270
8541
|
outcome: literal("success"),
|
|
8271
8542
|
runners: array(makeRunnerSchema()).describe("The runners running right now, in no particular order. Each entry carries the same fields a launch reports, so any of them can be addressed like a runner you launched yourself.")
|
|
8272
8543
|
});
|
|
8273
8544
|
return {
|
|
8274
|
-
description: "List the runners running on the caller's team right now.
|
|
8545
|
+
description: "List the runners running on the caller's team right now. A team API key already names the team and sends nothing; a credential bound to no single workspace names one in `workspaceId`. A runner that was terminated, or that terminated on its own after inactivity, is not listed, because the run system keeps no record of a runner once it is gone. This is a lookup: it never starts a runner, and it does not reset a runner's inactivity clock the way reading its journal does. It is how to find a runner launched from another machine or in an earlier session, when the id it was launched under is no longer at hand.",
|
|
8275
8546
|
input,
|
|
8276
8547
|
kind: "read",
|
|
8277
8548
|
name: "runner.list",
|
|
@@ -8281,11 +8552,12 @@ var makeListRunnersContract = () => {
|
|
|
8281
8552
|
|
|
8282
8553
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/promoteSnapshot.js
|
|
8283
8554
|
var maxSnapshotPathLength = 1024;
|
|
8284
|
-
var makePromoteSnapshotOnRunnerContract = () => {
|
|
8555
|
+
var makePromoteSnapshotOnRunnerContract = (ids) => {
|
|
8285
8556
|
const input = object({
|
|
8286
8557
|
baselinePath: string2().min(1).max(maxSnapshotPathLength).describe("The baseline to replace, as named in the image diff."),
|
|
8287
8558
|
id: runnerIdSchema.describe("Id of the runner holding the screenshot."),
|
|
8288
|
-
screenshotPath: string2().min(1).max(maxSnapshotPathLength).describe("The screenshot to promote, as named in the image diff.")
|
|
8559
|
+
screenshotPath: string2().min(1).max(maxSnapshotPathLength).describe("The screenshot to promote, as named in the image diff."),
|
|
8560
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8289
8561
|
});
|
|
8290
8562
|
const output = discriminatedUnion("outcome", [
|
|
8291
8563
|
object({ outcome: literal("success") }),
|
|
@@ -8308,10 +8580,11 @@ var makePromoteSnapshotOnRunnerContract = () => {
|
|
|
8308
8580
|
};
|
|
8309
8581
|
|
|
8310
8582
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/readJournal.js
|
|
8311
|
-
var makeReadRunnerJournalContract = () => {
|
|
8583
|
+
var makeReadRunnerJournalContract = (ids) => {
|
|
8312
8584
|
const input = readJournalRequestSchema.extend({
|
|
8313
8585
|
id: runnerIdSchema.describe("Id of the runner to read the journal of."),
|
|
8314
|
-
stream: journalStreamSchema.describe(`The stream to read. QA Wolf writes ${knownJournalStreams.join(", ")}; an unwritten stream reads as empty rather than as an error.`)
|
|
8586
|
+
stream: journalStreamSchema.describe(`The stream to read. QA Wolf writes ${knownJournalStreams.join(", ")}; an unwritten stream reads as empty rather than as an error.`),
|
|
8587
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8315
8588
|
});
|
|
8316
8589
|
const output = discriminatedUnion("outcome", [
|
|
8317
8590
|
readJournalResponseSchema.extend({ outcome: literal("success") }),
|
|
@@ -8346,7 +8619,8 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
8346
8619
|
files: runFilesSchema.describe("Every file the run needs keyed by its path — the flow file, everything it imports, package.json and tsconfig.json. A runner holds no copy of your project, so what runs is exactly what is sent here."),
|
|
8347
8620
|
id: runnerIdSchema.describe("Id of the runner to run the flow on."),
|
|
8348
8621
|
selection: runSelectionSchema.optional().describe("Run only these lines, inside the page the runner is already on, instead of running the whole flow from a fresh browser. Omit to run the whole entry point."),
|
|
8349
|
-
unchangedFiles: unchangedFilesSchema.optional().describe("Files this runner already holds from an earlier run, keyed by path with a hex SHA-256 of the content it should hold. Send this to make `files` only what changed. The runner refuses with `needs-full-sync` if it holds none of a referenced path, so it never runs something other than what was asked for. Omit it to send every file.")
|
|
8622
|
+
unchangedFiles: unchangedFilesSchema.optional().describe("Files this runner already holds from an earlier run, keyed by path with a hex SHA-256 of the content it should hold. Send this to make `files` only what changed. The runner refuses with `needs-full-sync` if it holds none of a referenced path, so it never runs something other than what was asked for. Omit it to send every file."),
|
|
8623
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8350
8624
|
}).refine((request) => request.env === undefined || request.environmentId === undefined, {
|
|
8351
8625
|
error: "Send env or environmentId, not both. An environment's variables are taken as they are stored, so there is no order in which the two would combine.",
|
|
8352
8626
|
path: ["environmentId"]
|
|
@@ -8398,9 +8672,10 @@ var makeRunFlowOnRunnerContract = (ids) => {
|
|
|
8398
8672
|
};
|
|
8399
8673
|
|
|
8400
8674
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/stopRun.js
|
|
8401
|
-
var makeStopRunOnRunnerContract = () => {
|
|
8675
|
+
var makeStopRunOnRunnerContract = (ids) => {
|
|
8402
8676
|
const input = object({
|
|
8403
|
-
id: runnerIdSchema.describe("Id of the runner to stop the run on.")
|
|
8677
|
+
id: runnerIdSchema.describe("Id of the runner to stop the run on."),
|
|
8678
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8404
8679
|
});
|
|
8405
8680
|
const output = discriminatedUnion("outcome", [
|
|
8406
8681
|
object({
|
|
@@ -8419,13 +8694,14 @@ var makeStopRunOnRunnerContract = () => {
|
|
|
8419
8694
|
};
|
|
8420
8695
|
|
|
8421
8696
|
// node_modules/@qawolf/api-contracts/dist/v1/runner/takeScreenshot.js
|
|
8422
|
-
var makeTakeScreenshotOnRunnerContract = () => {
|
|
8697
|
+
var makeTakeScreenshotOnRunnerContract = (ids) => {
|
|
8423
8698
|
const input = object({
|
|
8424
|
-
id: runnerIdSchema.describe("Id of the runner to take a screenshot of.")
|
|
8699
|
+
id: runnerIdSchema.describe("Id of the runner to take a screenshot of."),
|
|
8700
|
+
workspaceId: ids.workspace.optional().describe(runnerWorkspaceIdDescription)
|
|
8425
8701
|
});
|
|
8426
8702
|
const output = discriminatedUnion("outcome", [
|
|
8427
8703
|
object({
|
|
8428
|
-
imageJpegBase64:
|
|
8704
|
+
imageJpegBase64: screenshotImageSchema,
|
|
8429
8705
|
outcome: literal("success")
|
|
8430
8706
|
}),
|
|
8431
8707
|
makeRunnerFailureSchema([
|
|
@@ -8479,6 +8755,7 @@ var makeContractsV1 = (ids) => {
|
|
|
8479
8755
|
get: makeGetEmailContract(resolvedIds),
|
|
8480
8756
|
getAttachment: makeGetEmailAttachmentContract(resolvedIds),
|
|
8481
8757
|
listAddresses: makeListEmailAddressesContract(resolvedIds),
|
|
8758
|
+
registerAddress: makeRegisterEmailAddressContract(resolvedIds),
|
|
8482
8759
|
send: makeSendEmailContract(resolvedIds)
|
|
8483
8760
|
},
|
|
8484
8761
|
environment: {
|
|
@@ -8510,24 +8787,25 @@ var makeContractsV1 = (ids) => {
|
|
|
8510
8787
|
diagnose: makeDiagnoseRunContract(resolvedIds),
|
|
8511
8788
|
find: makeFindRunsContract(resolvedIds),
|
|
8512
8789
|
get: makeGetRunContract(resolvedIds),
|
|
8513
|
-
reattempt: makeReattemptRunContract(resolvedIds)
|
|
8790
|
+
reattempt: makeReattemptRunContract(resolvedIds),
|
|
8791
|
+
stop: makeStopRunContract(resolvedIds)
|
|
8514
8792
|
},
|
|
8515
8793
|
runner: {
|
|
8516
|
-
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(),
|
|
8517
|
-
get: makeGetRunnerContract(),
|
|
8518
|
-
highlightSelector: makeHighlightSelectorOnRunnerContract(),
|
|
8519
|
-
importPackage: makeImportPackageOnRunnerContract(),
|
|
8520
|
-
inspect: makeInspectOnRunnerContract(),
|
|
8521
|
-
inspectMobile: makeInspectMobileOnRunnerContract(),
|
|
8522
|
-
launch: makeLaunchRunnerContract(),
|
|
8523
|
-
list: makeListRunnersContract(),
|
|
8524
|
-
performAction: makePerformActionOnRunnerContract(),
|
|
8525
|
-
promoteSnapshot: makePromoteSnapshotOnRunnerContract(),
|
|
8526
|
-
readJournal: makeReadRunnerJournalContract(),
|
|
8794
|
+
evaluateSnippet: makeEvaluateSnippetOnRunnerContract(resolvedIds),
|
|
8795
|
+
get: makeGetRunnerContract(resolvedIds),
|
|
8796
|
+
highlightSelector: makeHighlightSelectorOnRunnerContract(resolvedIds),
|
|
8797
|
+
importPackage: makeImportPackageOnRunnerContract(resolvedIds),
|
|
8798
|
+
inspect: makeInspectOnRunnerContract(resolvedIds),
|
|
8799
|
+
inspectMobile: makeInspectMobileOnRunnerContract(resolvedIds),
|
|
8800
|
+
launch: makeLaunchRunnerContract(resolvedIds),
|
|
8801
|
+
list: makeListRunnersContract(resolvedIds),
|
|
8802
|
+
performAction: makePerformActionOnRunnerContract(resolvedIds),
|
|
8803
|
+
promoteSnapshot: makePromoteSnapshotOnRunnerContract(resolvedIds),
|
|
8804
|
+
readJournal: makeReadRunnerJournalContract(resolvedIds),
|
|
8527
8805
|
runFlow: makeRunFlowOnRunnerContract(resolvedIds),
|
|
8528
|
-
stopRun: makeStopRunOnRunnerContract(),
|
|
8529
|
-
takeScreenshot: makeTakeScreenshotOnRunnerContract(),
|
|
8530
|
-
terminate: makeTerminateRunnerContract()
|
|
8806
|
+
stopRun: makeStopRunOnRunnerContract(resolvedIds),
|
|
8807
|
+
takeScreenshot: makeTakeScreenshotOnRunnerContract(resolvedIds),
|
|
8808
|
+
terminate: makeTerminateRunnerContract(resolvedIds)
|
|
8531
8809
|
},
|
|
8532
8810
|
tag: {
|
|
8533
8811
|
create: makeCreateTagContract(resolvedIds),
|
|
@@ -8537,11 +8815,82 @@ var makeContractsV1 = (ids) => {
|
|
|
8537
8815
|
};
|
|
8538
8816
|
var publicContractsV1 = makeContractsV1(defaultIdSchemas);
|
|
8539
8817
|
|
|
8818
|
+
// src/core/pluralize.ts
|
|
8819
|
+
function pluralize(count, singular, plural = `${singular}s`) {
|
|
8820
|
+
return `${String(count)} ${count === 1 ? singular : plural}`;
|
|
8821
|
+
}
|
|
8822
|
+
|
|
8540
8823
|
// src/core/formatSeconds.ts
|
|
8541
8824
|
function formatSeconds(ms) {
|
|
8542
8825
|
return `${Math.round(ms / 1000)}s`;
|
|
8543
8826
|
}
|
|
8544
8827
|
|
|
8828
|
+
// src/core/messages/authErrors.ts
|
|
8829
|
+
var authErrorMessages = {
|
|
8830
|
+
identity: {
|
|
8831
|
+
invalidOrUnauthorized: "API key is invalid or unauthorized",
|
|
8832
|
+
unexpectedFormat: "Could not verify API key: unexpected response format",
|
|
8833
|
+
couldNotVerify: (detail, status) => `Could not verify API key: ${detail || `HTTP ${status}`}`,
|
|
8834
|
+
couldNotVerifyNetwork: (cause) => `Could not verify API key: ${cause}`,
|
|
8835
|
+
timedOut: (timeoutMs) => `Could not verify API key: the QA Wolf API did not answer within ${formatSeconds(timeoutMs)}.`
|
|
8836
|
+
},
|
|
8837
|
+
request: {
|
|
8838
|
+
rejected401: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 401). Check your API key.`,
|
|
8839
|
+
rejected402: (noun) => `QA Wolf API refused the${noun ? ` ${noun}` : ""} request (HTTP 402): billing prevented it.`,
|
|
8840
|
+
rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
|
|
8841
|
+
notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
|
|
8842
|
+
failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
|
|
8843
|
+
networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
|
|
8844
|
+
timedOut: (timeoutMs, noun) => `The QA Wolf API${noun ? ` ${noun}` : ""} request timed out after ${formatSeconds(timeoutMs)}. The work may still be finishing on the platform.`,
|
|
8845
|
+
unexpectedResponse: (noun) => `Unexpected${noun ? ` ${noun}` : ""} response from the QA Wolf API.`
|
|
8846
|
+
},
|
|
8847
|
+
workos: {
|
|
8848
|
+
unexpectedResponse: "WorkOS returned an unexpected response",
|
|
8849
|
+
unexpectedResponseWithStatus: (status) => `WorkOS returned an unexpected response (HTTP ${status})`,
|
|
8850
|
+
unreachable: (detail) => `Could not reach WorkOS: ${detail}`,
|
|
8851
|
+
redirected: "WorkOS answered with a redirect, which the CLI does not follow for a sign-in request",
|
|
8852
|
+
noClientForSession: "This session names no WorkOS client"
|
|
8853
|
+
},
|
|
8854
|
+
bundle: {
|
|
8855
|
+
linkExpired: "The flow bundle download link has expired. Please run `qawolf flows pull` again to refresh.",
|
|
8856
|
+
failedWithStatus: (status) => `Could not download the flow bundle (HTTP ${status}).`,
|
|
8857
|
+
networkUnreachable: "Could not reach the flow bundle storage. Check your network connection and try again.",
|
|
8858
|
+
timedOut: (timeoutMs) => `Downloading the flow bundle stalled — no data arrived for ${formatSeconds(timeoutMs)}. Please try again.`,
|
|
8859
|
+
malformed: "The flow bundle download was malformed. Please run `qawolf flows pull` again."
|
|
8860
|
+
}
|
|
8861
|
+
};
|
|
8862
|
+
|
|
8863
|
+
// src/core/messages/whoami.ts
|
|
8864
|
+
var whoamiMessages = {
|
|
8865
|
+
source: (source) => `Source: ${source}`,
|
|
8866
|
+
authFailed: (source, error) => `Authentication failed (source: ${source}): ${error}`,
|
|
8867
|
+
authenticatedAs: (teamName, source) => `Authenticated as ${teamName} (source: ${source})`,
|
|
8868
|
+
teamNote: (input) => [
|
|
8869
|
+
`Workspace: ${input.team.name}`,
|
|
8870
|
+
`ID: ${input.team.id}`,
|
|
8871
|
+
input.team.slug ? `Slug: ${input.team.slug}` : undefined,
|
|
8872
|
+
input.teamUrl ? `URL: ${input.teamUrl}` : undefined,
|
|
8873
|
+
`Source: ${input.source}`
|
|
8874
|
+
].filter((line) => Boolean(line)).join(`
|
|
8875
|
+
`),
|
|
8876
|
+
organizationNote: (input) => [
|
|
8877
|
+
`Organization: ${input.organization.name}`,
|
|
8878
|
+
`ID: ${input.organization.id}`,
|
|
8879
|
+
`Source: ${input.source}`
|
|
8880
|
+
].join(`
|
|
8881
|
+
`),
|
|
8882
|
+
activeWorkspace: (workspaceId, found) => found ? `${found.workspace} (${found.organization})` : `${workspaceId} — no longer in reach; run 'qawolf auth switch'`,
|
|
8883
|
+
userNote: (input) => [
|
|
8884
|
+
`User: ${input.user.email}`,
|
|
8885
|
+
`ID: ${input.user.id}`,
|
|
8886
|
+
`Organization: ${input.organization.name}`,
|
|
8887
|
+
input.activeWorkspace ? `Workspace: ${input.activeWorkspace}` : undefined,
|
|
8888
|
+
input.organizations.length > 0 ? `Workspaces: ${input.organizations.map((o) => `${o.name}: ${o.workspaces.map((w) => w.name).join(", ")}`).join(" | ")}` : undefined,
|
|
8889
|
+
`Source: ${input.source}`
|
|
8890
|
+
].filter((line) => Boolean(line)).join(`
|
|
8891
|
+
`)
|
|
8892
|
+
};
|
|
8893
|
+
|
|
8545
8894
|
// src/core/messages/auth.ts
|
|
8546
8895
|
var authMessages = {
|
|
8547
8896
|
title: "QA Wolf Authentication",
|
|
@@ -8559,7 +8908,47 @@ var authMessages = {
|
|
|
8559
8908
|
whoamiFailed: "Not authenticated",
|
|
8560
8909
|
login: {
|
|
8561
8910
|
nonInteractive: "auth login requires an interactive terminal. Set the QAWOLF_API_KEY environment variable for CI authentication.",
|
|
8562
|
-
reAuthPrompt: "You are already authenticated. Re-authenticate?"
|
|
8911
|
+
reAuthPrompt: "You are already authenticated. Re-authenticate?",
|
|
8912
|
+
chooseMethod: "How do you want to sign in?",
|
|
8913
|
+
methodBrowser: "Browser",
|
|
8914
|
+
methodBrowserHint: "Sign in with your QA Wolf account",
|
|
8915
|
+
methodApiKey: "API key",
|
|
8916
|
+
methodApiKeyHint: "Paste a team key — needed for flows pull and flows run",
|
|
8917
|
+
apiKeyPrecedence: {
|
|
8918
|
+
env: "QAWOLF_API_KEY is set, and an API key takes precedence over a browser session. Commands continue to use that key until you unset the variable.",
|
|
8919
|
+
stored: "A stored API key takes precedence over a browser session. Commands continue to use that key until you run 'qawolf auth logout' and sign in again."
|
|
8920
|
+
}
|
|
8921
|
+
},
|
|
8922
|
+
device: {
|
|
8923
|
+
unavailable: "This QA Wolf deployment does not offer browser sign-in. Run 'qawolf auth login' again and choose 'API key'.",
|
|
8924
|
+
configUnreachable: "Could not ask this QA Wolf deployment whether it offers browser sign-in. Check your connection, then try again.",
|
|
8925
|
+
confirmCode: (userCode) => `Your code is ${userCode}`,
|
|
8926
|
+
visitUrl: (url) => `Confirm it at ${url}`,
|
|
8927
|
+
visitUrlPlain: (url) => `Or go to ${url} and enter the code`,
|
|
8928
|
+
openFailed: (url) => `Could not open a browser automatically. Open ${url} yourself to continue.`,
|
|
8929
|
+
waiting: "Waiting for you to finish in the browser",
|
|
8930
|
+
signedIn: (email) => `Signed in as ${email}.`,
|
|
8931
|
+
failed: {
|
|
8932
|
+
"access-denied": "The sign-in request was rejected.",
|
|
8933
|
+
expired: "The sign-in request expired. Run 'qawolf auth login' to retry.",
|
|
8934
|
+
timeout: "The sign-in request timed out. Run 'qawolf auth login' to retry.",
|
|
8935
|
+
network: "Could not reach WorkOS to complete sign-in.",
|
|
8936
|
+
unavailable: "Could not start browser sign-in.",
|
|
8937
|
+
cancelled: "Sign-in cancelled."
|
|
8938
|
+
}
|
|
8939
|
+
},
|
|
8940
|
+
workspace: {
|
|
8941
|
+
chooseOrganization: "Which organization do you want to work in?",
|
|
8942
|
+
choose: "Which workspace do you want to use?",
|
|
8943
|
+
workspaceCount: (count) => pluralize(count, "workspace"),
|
|
8944
|
+
working: (organization, workspace) => `Working in ${workspace} (${organization}).`,
|
|
8945
|
+
none: "This account reaches no organizations yet.",
|
|
8946
|
+
cancelled: "Workspace not changed.",
|
|
8947
|
+
notSignedIn: "Workspace switching needs a browser sign-in. Run 'qawolf auth login' and choose Browser.",
|
|
8948
|
+
sessionExpired: "Your session could not be renewed. Run 'qawolf auth login' to sign in again.",
|
|
8949
|
+
nonInteractive: "auth switch needs an interactive terminal, or set QAWOLF_WORKSPACE to name a workspace.",
|
|
8950
|
+
sessionChanged: "The stored sign-in changed while you made your choice. Run the command again.",
|
|
8951
|
+
saveFailed: (detail) => `Could not save the workspace choice: ${detail}`
|
|
8563
8952
|
},
|
|
8564
8953
|
logout: {
|
|
8565
8954
|
title: "Log Out",
|
|
@@ -8571,58 +8960,8 @@ var authMessages = {
|
|
|
8571
8960
|
success: "Logged out successfully.",
|
|
8572
8961
|
cancelled: "Logout cancelled."
|
|
8573
8962
|
},
|
|
8574
|
-
errors:
|
|
8575
|
-
|
|
8576
|
-
invalidOrUnauthorized: "API key is invalid or unauthorized",
|
|
8577
|
-
unexpectedFormat: "Could not verify API key: unexpected response format",
|
|
8578
|
-
couldNotVerify: (detail, status) => `Could not verify API key: ${detail || `HTTP ${status}`}`,
|
|
8579
|
-
couldNotVerifyNetwork: (cause) => `Could not verify API key: ${cause}`,
|
|
8580
|
-
timedOut: (timeoutMs) => `Could not verify API key: the QA Wolf API did not answer within ${formatSeconds(timeoutMs)}.`
|
|
8581
|
-
},
|
|
8582
|
-
request: {
|
|
8583
|
-
rejected401: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 401). Check your API key.`,
|
|
8584
|
-
rejected402: (noun) => `QA Wolf API refused the${noun ? ` ${noun}` : ""} request (HTTP 402): billing prevented it.`,
|
|
8585
|
-
rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
|
|
8586
|
-
notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
|
|
8587
|
-
failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
|
|
8588
|
-
networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
|
|
8589
|
-
timedOut: (timeoutMs, noun) => `The QA Wolf API${noun ? ` ${noun}` : ""} request timed out after ${formatSeconds(timeoutMs)}. The work may still be finishing on the platform.`,
|
|
8590
|
-
unexpectedResponse: (noun) => `Unexpected${noun ? ` ${noun}` : ""} response from the QA Wolf API.`
|
|
8591
|
-
},
|
|
8592
|
-
bundle: {
|
|
8593
|
-
linkExpired: "The flow bundle download link has expired. Please run `qawolf flows pull` again to refresh.",
|
|
8594
|
-
failedWithStatus: (status) => `Could not download the flow bundle (HTTP ${status}).`,
|
|
8595
|
-
networkUnreachable: "Could not reach the flow bundle storage. Check your network connection and try again.",
|
|
8596
|
-
timedOut: (timeoutMs) => `Downloading the flow bundle stalled — no data arrived for ${formatSeconds(timeoutMs)}. Please try again.`,
|
|
8597
|
-
malformed: "The flow bundle download was malformed. Please run `qawolf flows pull` again."
|
|
8598
|
-
}
|
|
8599
|
-
},
|
|
8600
|
-
whoami: {
|
|
8601
|
-
source: (source) => `Source: ${source}`,
|
|
8602
|
-
authFailed: (source, error) => `Authentication failed (source: ${source}): ${error}`,
|
|
8603
|
-
authenticatedAs: (teamName, source) => `Authenticated as ${teamName} (source: ${source})`,
|
|
8604
|
-
teamNote: (input) => [
|
|
8605
|
-
`Team: ${input.team.name}`,
|
|
8606
|
-
`ID: ${input.team.id}`,
|
|
8607
|
-
input.team.slug ? `Slug: ${input.team.slug}` : undefined,
|
|
8608
|
-
input.teamUrl ? `URL: ${input.teamUrl}` : undefined,
|
|
8609
|
-
`Source: ${input.source}`
|
|
8610
|
-
].filter((line) => Boolean(line)).join(`
|
|
8611
|
-
`),
|
|
8612
|
-
organizationNote: (input) => [
|
|
8613
|
-
`Organization: ${input.organization.name}`,
|
|
8614
|
-
`ID: ${input.organization.id}`,
|
|
8615
|
-
`Source: ${input.source}`
|
|
8616
|
-
].join(`
|
|
8617
|
-
`),
|
|
8618
|
-
userNote: (input) => [
|
|
8619
|
-
`User: ${input.user.email}`,
|
|
8620
|
-
`ID: ${input.user.id}`,
|
|
8621
|
-
`Organization: ${input.organization.name}`,
|
|
8622
|
-
`Source: ${input.source}`
|
|
8623
|
-
].join(`
|
|
8624
|
-
`)
|
|
8625
|
-
}
|
|
8963
|
+
errors: authErrorMessages,
|
|
8964
|
+
whoami: whoamiMessages
|
|
8626
8965
|
};
|
|
8627
8966
|
// src/core/messages/toolNotFound.ts
|
|
8628
8967
|
var installRemedy = "Run `qawolf install` to install the runtime dependencies.";
|
|
@@ -8638,11 +8977,6 @@ ${installRemedy}`;
|
|
|
8638
8977
|
function packageLoadFailed(pkg, envDir, detail) {
|
|
8639
8978
|
return toolNotRunnable(`Could not load ${pkg} from ${envDir}`, detail);
|
|
8640
8979
|
}
|
|
8641
|
-
// src/core/pluralize.ts
|
|
8642
|
-
function pluralize(count, singular, plural = `${singular}s`) {
|
|
8643
|
-
return `${String(count)} ${count === 1 ? singular : plural}`;
|
|
8644
|
-
}
|
|
8645
|
-
|
|
8646
8980
|
// src/core/messages/flows.ts
|
|
8647
8981
|
var flowsMessages = {
|
|
8648
8982
|
title: "Flows",
|
|
@@ -10365,6 +10699,176 @@ function makeDefaultFs() {
|
|
|
10365
10699
|
};
|
|
10366
10700
|
}
|
|
10367
10701
|
|
|
10702
|
+
// src/core/publicApi/flagKind.ts
|
|
10703
|
+
function flagKind(schema) {
|
|
10704
|
+
if (schema.type === "string")
|
|
10705
|
+
return { ok: true, kind: "string" };
|
|
10706
|
+
if (schema.type === "number" || schema.type === "integer") {
|
|
10707
|
+
return { ok: true, kind: "number" };
|
|
10708
|
+
}
|
|
10709
|
+
if (schema.type === "boolean")
|
|
10710
|
+
return { ok: true, kind: "boolean" };
|
|
10711
|
+
if (schema.type === "array") {
|
|
10712
|
+
if (schema.items?.type !== "string") {
|
|
10713
|
+
return {
|
|
10714
|
+
ok: false,
|
|
10715
|
+
reason: "only string arrays can be expressed as flags"
|
|
10716
|
+
};
|
|
10717
|
+
}
|
|
10718
|
+
return { ok: true, kind: "string-array" };
|
|
10719
|
+
}
|
|
10720
|
+
if (schema.type === "object") {
|
|
10721
|
+
if (schema.properties) {
|
|
10722
|
+
return {
|
|
10723
|
+
ok: false,
|
|
10724
|
+
reason: "nested objects cannot be expressed as flags"
|
|
10725
|
+
};
|
|
10726
|
+
}
|
|
10727
|
+
if (typeof schema.additionalProperties === "object" && schema.additionalProperties.type === "string") {
|
|
10728
|
+
return { ok: true, kind: "key-value-record" };
|
|
10729
|
+
}
|
|
10730
|
+
return { ok: false, reason: "nested objects cannot be expressed as flags" };
|
|
10731
|
+
}
|
|
10732
|
+
return {
|
|
10733
|
+
ok: false,
|
|
10734
|
+
reason: `type ${schema.type ?? "unknown"} cannot be expressed as a flag`
|
|
10735
|
+
};
|
|
10736
|
+
}
|
|
10737
|
+
|
|
10738
|
+
// src/core/publicApi/objectShape.ts
|
|
10739
|
+
var asObjectShape = (schema) => schema.type === "object" && schema.properties ? { properties: schema.properties, required: new Set(schema.required) } : undefined;
|
|
10740
|
+
function mergeIntersection(members) {
|
|
10741
|
+
const shape = { properties: {}, required: new Set };
|
|
10742
|
+
for (const member of members) {
|
|
10743
|
+
const memberShape = asObjectShape(member);
|
|
10744
|
+
if (!memberShape) {
|
|
10745
|
+
return {
|
|
10746
|
+
ok: false,
|
|
10747
|
+
field: "",
|
|
10748
|
+
reason: "intersection members must be object schemas"
|
|
10749
|
+
};
|
|
10750
|
+
}
|
|
10751
|
+
for (const [field, fieldSchema] of Object.entries(memberShape.properties)) {
|
|
10752
|
+
if (field in shape.properties) {
|
|
10753
|
+
return {
|
|
10754
|
+
ok: false,
|
|
10755
|
+
field,
|
|
10756
|
+
reason: "field appears in multiple intersection members"
|
|
10757
|
+
};
|
|
10758
|
+
}
|
|
10759
|
+
shape.properties[field] = fieldSchema;
|
|
10760
|
+
if (memberShape.required.has(field))
|
|
10761
|
+
shape.required.add(field);
|
|
10762
|
+
}
|
|
10763
|
+
}
|
|
10764
|
+
return { ok: true, shape };
|
|
10765
|
+
}
|
|
10766
|
+
function mergeUnion(branches) {
|
|
10767
|
+
const shapes = [];
|
|
10768
|
+
for (const branch of branches) {
|
|
10769
|
+
const branchShape = asObjectShape(branch);
|
|
10770
|
+
if (!branchShape) {
|
|
10771
|
+
return {
|
|
10772
|
+
ok: false,
|
|
10773
|
+
field: "",
|
|
10774
|
+
reason: "union branches must be object schemas"
|
|
10775
|
+
};
|
|
10776
|
+
}
|
|
10777
|
+
shapes.push(branchShape);
|
|
10778
|
+
}
|
|
10779
|
+
const shape = { properties: {}, required: new Set };
|
|
10780
|
+
for (const branchShape of shapes) {
|
|
10781
|
+
for (const [field, fieldSchema] of Object.entries(branchShape.properties)) {
|
|
10782
|
+
const existing = shape.properties[field];
|
|
10783
|
+
if (!existing) {
|
|
10784
|
+
shape.properties[field] = { ...fieldSchema };
|
|
10785
|
+
continue;
|
|
10786
|
+
}
|
|
10787
|
+
const kinds = [flagKind(existing), flagKind(fieldSchema)];
|
|
10788
|
+
if (kinds[0]?.ok && kinds[1]?.ok) {
|
|
10789
|
+
if (kinds[0].kind !== kinds[1].kind) {
|
|
10790
|
+
return {
|
|
10791
|
+
ok: false,
|
|
10792
|
+
field,
|
|
10793
|
+
reason: "field has conflicting types across union branches"
|
|
10794
|
+
};
|
|
10795
|
+
}
|
|
10796
|
+
} else if (JSON.stringify({ ...existing, description: undefined }) !== JSON.stringify({ ...fieldSchema, description: undefined })) {
|
|
10797
|
+
return {
|
|
10798
|
+
ok: false,
|
|
10799
|
+
field,
|
|
10800
|
+
reason: "field has conflicting object schemas across union branches"
|
|
10801
|
+
};
|
|
10802
|
+
}
|
|
10803
|
+
if (!existing.description && fieldSchema.description) {
|
|
10804
|
+
existing.description = fieldSchema.description;
|
|
10805
|
+
}
|
|
10806
|
+
}
|
|
10807
|
+
}
|
|
10808
|
+
const discriminator = Object.keys(shape.properties).find((field) => {
|
|
10809
|
+
const values = shapes.map((branch) => branch.properties[field]?.const);
|
|
10810
|
+
return values.every((value) => typeof value === "string") && new Set(values).size === shapes.length && shapes.every((branch) => branch.required.has(field));
|
|
10811
|
+
});
|
|
10812
|
+
for (const [field, fieldSchema] of Object.entries(shape.properties)) {
|
|
10813
|
+
const occurrences = shapes.filter((branch) => (field in branch.properties));
|
|
10814
|
+
if (occurrences.length === shapes.length && occurrences.every((branch) => branch.required.has(field))) {
|
|
10815
|
+
shape.required.add(field);
|
|
10816
|
+
}
|
|
10817
|
+
if (field === discriminator) {
|
|
10818
|
+
const values = shapes.map((branch) => branch.properties[field]?.const);
|
|
10819
|
+
fieldSchema.description = `One of: ${values.join(", ")}`;
|
|
10820
|
+
}
|
|
10821
|
+
}
|
|
10822
|
+
return { ok: true, shape };
|
|
10823
|
+
}
|
|
10824
|
+
function toObjectShape(schema) {
|
|
10825
|
+
if (schema.allOf)
|
|
10826
|
+
return mergeIntersection(schema.allOf);
|
|
10827
|
+
const branches = schema.oneOf ?? schema.anyOf;
|
|
10828
|
+
if (branches)
|
|
10829
|
+
return mergeUnion(branches);
|
|
10830
|
+
const shape = asObjectShape(schema);
|
|
10831
|
+
if (shape)
|
|
10832
|
+
return { ok: true, shape };
|
|
10833
|
+
return {
|
|
10834
|
+
ok: false,
|
|
10835
|
+
field: "",
|
|
10836
|
+
reason: "contract input must be an object schema"
|
|
10837
|
+
};
|
|
10838
|
+
}
|
|
10839
|
+
|
|
10840
|
+
// src/shell/platform/applyWorkspaceId.ts
|
|
10841
|
+
var acceptsWorkspace = new WeakMap;
|
|
10842
|
+
function takesWorkspaceId(inputSchema) {
|
|
10843
|
+
const cached = acceptsWorkspace.get(inputSchema);
|
|
10844
|
+
if (cached !== undefined)
|
|
10845
|
+
return cached;
|
|
10846
|
+
let accepted = false;
|
|
10847
|
+
try {
|
|
10848
|
+
const jsonSchema = toJSONSchema(inputSchema, {
|
|
10849
|
+
io: "input"
|
|
10850
|
+
});
|
|
10851
|
+
const shape = toObjectShape(jsonSchema);
|
|
10852
|
+
accepted = shape.ok && "workspaceId" in shape.shape.properties;
|
|
10853
|
+
} catch {
|
|
10854
|
+
accepted = false;
|
|
10855
|
+
}
|
|
10856
|
+
acceptsWorkspace.set(inputSchema, accepted);
|
|
10857
|
+
return accepted;
|
|
10858
|
+
}
|
|
10859
|
+
function applyWorkspaceId(inputSchema, input, workspaceId) {
|
|
10860
|
+
if (!workspaceId)
|
|
10861
|
+
return input;
|
|
10862
|
+
if (typeof input !== "object" || input === null)
|
|
10863
|
+
return input;
|
|
10864
|
+
const existing = input.workspaceId;
|
|
10865
|
+
if (existing !== undefined)
|
|
10866
|
+
return input;
|
|
10867
|
+
if (!takesWorkspaceId(inputSchema))
|
|
10868
|
+
return input;
|
|
10869
|
+
return { ...input, workspaceId };
|
|
10870
|
+
}
|
|
10871
|
+
|
|
10368
10872
|
// src/shell/platform/parseErrorBody.ts
|
|
10369
10873
|
var defaultMaxLength = 1024;
|
|
10370
10874
|
function asRecord(value) {
|
|
@@ -10497,7 +11001,7 @@ function callPublicApi(trpc, contract, input, options) {
|
|
|
10497
11001
|
}
|
|
10498
11002
|
function makeCallPublicApiMethod(trpc, deps, readBackoffMs) {
|
|
10499
11003
|
return async (contract, input, options) => requestWithRetry({
|
|
10500
|
-
call: () => callPublicApi(trpc, contract, input, options),
|
|
11004
|
+
call: () => callPublicApi(trpc, contract, applyWorkspaceId(contract.input, input, deps.workspaceId), options),
|
|
10501
11005
|
backoffMs: contract.kind === "read" ? readBackoffMs : [],
|
|
10502
11006
|
describe: (err) => describeRequestError(err, deps.baseUrl, contract.name),
|
|
10503
11007
|
sleep: deps.sleep
|
|
@@ -11497,10 +12001,16 @@ async function downloadBundle(signedUrl, deps) {
|
|
|
11497
12001
|
return { ok: true, value: { tmpArchive } };
|
|
11498
12002
|
}
|
|
11499
12003
|
|
|
11500
|
-
// src/shell/platform/
|
|
12004
|
+
// src/shell/platform/organizations.ts
|
|
12005
|
+
function parseOrganizationsResponse(json) {
|
|
12006
|
+
const parsed = identityOrganizationsResponse.safeParse(json);
|
|
12007
|
+
return parsed.success ? parsed.data.organizations : undefined;
|
|
12008
|
+
}
|
|
12009
|
+
|
|
12010
|
+
// src/shell/platform/getAccessibleOrganizations.ts
|
|
11501
12011
|
var timeoutMs = 1e4;
|
|
11502
|
-
async function
|
|
11503
|
-
const url = `${deps.baseUrl}/api/v0/identity`;
|
|
12012
|
+
async function getAccessibleOrganizations(apiKey, deps) {
|
|
12013
|
+
const url = `${deps.baseUrl}/api/v0/identity/organizations`;
|
|
11504
12014
|
let response;
|
|
11505
12015
|
try {
|
|
11506
12016
|
response = await deps.fetch(url, {
|
|
@@ -11511,6 +12021,51 @@ async function getIdentity(apiKey, deps) {
|
|
|
11511
12021
|
if (isTimeoutError(error)) {
|
|
11512
12022
|
return { ok: false, error: { kind: "timeout", timeoutMs } };
|
|
11513
12023
|
}
|
|
12024
|
+
return { ok: false, error: { kind: "network", cause: toError(error) } };
|
|
12025
|
+
}
|
|
12026
|
+
if (!response.ok) {
|
|
12027
|
+
const body = await response.text().catch(() => "");
|
|
12028
|
+
return {
|
|
12029
|
+
ok: false,
|
|
12030
|
+
error: { kind: "http", status: response.status, body }
|
|
12031
|
+
};
|
|
12032
|
+
}
|
|
12033
|
+
let json;
|
|
12034
|
+
try {
|
|
12035
|
+
json = await response.json();
|
|
12036
|
+
} catch (error) {
|
|
12037
|
+
if (isTimeoutError(error)) {
|
|
12038
|
+
return { ok: false, error: { kind: "timeout", timeoutMs } };
|
|
12039
|
+
}
|
|
12040
|
+
json = undefined;
|
|
12041
|
+
}
|
|
12042
|
+
const organizations = parseOrganizationsResponse(json);
|
|
12043
|
+
if (!organizations) {
|
|
12044
|
+
return {
|
|
12045
|
+
ok: false,
|
|
12046
|
+
error: {
|
|
12047
|
+
kind: "parse",
|
|
12048
|
+
cause: Error("organizations response did not match the contract")
|
|
12049
|
+
}
|
|
12050
|
+
};
|
|
12051
|
+
}
|
|
12052
|
+
return { ok: true, data: organizations };
|
|
12053
|
+
}
|
|
12054
|
+
|
|
12055
|
+
// src/shell/platform/getIdentity.ts
|
|
12056
|
+
var timeoutMs2 = 1e4;
|
|
12057
|
+
async function getIdentity(apiKey, deps) {
|
|
12058
|
+
const url = `${deps.baseUrl}/api/v0/identity`;
|
|
12059
|
+
let response;
|
|
12060
|
+
try {
|
|
12061
|
+
response = await deps.fetch(url, {
|
|
12062
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
12063
|
+
signal: AbortSignal.timeout(timeoutMs2)
|
|
12064
|
+
});
|
|
12065
|
+
} catch (error) {
|
|
12066
|
+
if (isTimeoutError(error)) {
|
|
12067
|
+
return { ok: false, error: { kind: "timeout", timeoutMs: timeoutMs2 } };
|
|
12068
|
+
}
|
|
11514
12069
|
return {
|
|
11515
12070
|
ok: false,
|
|
11516
12071
|
error: { kind: "network", cause: toError(error) }
|
|
@@ -11528,7 +12083,7 @@ async function getIdentity(apiKey, deps) {
|
|
|
11528
12083
|
json = await response.json();
|
|
11529
12084
|
} catch (error) {
|
|
11530
12085
|
if (isTimeoutError(error)) {
|
|
11531
|
-
return { ok: false, error: { kind: "timeout", timeoutMs } };
|
|
12086
|
+
return { ok: false, error: { kind: "timeout", timeoutMs: timeoutMs2 } };
|
|
11532
12087
|
}
|
|
11533
12088
|
json = undefined;
|
|
11534
12089
|
}
|
|
@@ -11539,6 +12094,24 @@ async function getIdentity(apiKey, deps) {
|
|
|
11539
12094
|
return { ok: true, data: parsed.data };
|
|
11540
12095
|
}
|
|
11541
12096
|
|
|
12097
|
+
// src/shell/platform/identityMethods.ts
|
|
12098
|
+
function createIdentityMethods(apiKey, deps, backoffMs) {
|
|
12099
|
+
return {
|
|
12100
|
+
getIdentity: () => requestWithRetry({
|
|
12101
|
+
call: () => getIdentity(apiKey, deps),
|
|
12102
|
+
backoffMs,
|
|
12103
|
+
describe: describeIdentityError,
|
|
12104
|
+
sleep: deps.sleep
|
|
12105
|
+
}),
|
|
12106
|
+
getAccessibleOrganizations: () => requestWithRetry({
|
|
12107
|
+
call: () => getAccessibleOrganizations(apiKey, deps),
|
|
12108
|
+
backoffMs,
|
|
12109
|
+
describe: describeIdentityError,
|
|
12110
|
+
sleep: deps.sleep
|
|
12111
|
+
})
|
|
12112
|
+
};
|
|
12113
|
+
}
|
|
12114
|
+
|
|
11542
12115
|
// src/shell/platform/types.ts
|
|
11543
12116
|
var environmentListItemSchema = object({
|
|
11544
12117
|
envId: string2(),
|
|
@@ -11821,6 +12394,34 @@ async function writeTeamStorageAssets(args, deps) {
|
|
|
11821
12394
|
}
|
|
11822
12395
|
}
|
|
11823
12396
|
|
|
12397
|
+
// src/shell/platform/teamStorageMethods.ts
|
|
12398
|
+
function createTeamStorageMethods(trpc, deps, fs, getIdentity) {
|
|
12399
|
+
async function list() {
|
|
12400
|
+
if (deps.workspaceId !== undefined) {
|
|
12401
|
+
return listTeamStorageFiles(trpc, { teamId: deps.workspaceId }, deps);
|
|
12402
|
+
}
|
|
12403
|
+
const identity = await getIdentity();
|
|
12404
|
+
if (!identity.ok)
|
|
12405
|
+
return identity;
|
|
12406
|
+
if (!("team" in identity.value)) {
|
|
12407
|
+
return {
|
|
12408
|
+
ok: false,
|
|
12409
|
+
error: flowsMessages.pull.teamStorageRequiresTeamKey
|
|
12410
|
+
};
|
|
12411
|
+
}
|
|
12412
|
+
return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
|
|
12413
|
+
}
|
|
12414
|
+
return {
|
|
12415
|
+
listTeamStorageFiles: list,
|
|
12416
|
+
async syncTeamStorageAssets(assetsAbs, opts) {
|
|
12417
|
+
const files = await list();
|
|
12418
|
+
if (!files.ok)
|
|
12419
|
+
return files;
|
|
12420
|
+
return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
|
|
12421
|
+
}
|
|
12422
|
+
};
|
|
12423
|
+
}
|
|
12424
|
+
|
|
11824
12425
|
// src/shell/platform/createPlatformClient.ts
|
|
11825
12426
|
var requestBackoffMs2 = [500, 1500];
|
|
11826
12427
|
function createPlatformClient(apiKey, deps) {
|
|
@@ -11837,15 +12438,10 @@ function createPlatformClient(apiKey, deps) {
|
|
|
11837
12438
|
return result;
|
|
11838
12439
|
return { ok: true, value: { signedUrl: result.value.url } };
|
|
11839
12440
|
}
|
|
12441
|
+
const identityMethods = createIdentityMethods(apiKey, deps, requestBackoffMs2);
|
|
11840
12442
|
return {
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
call: () => getIdentity(apiKey, deps),
|
|
11844
|
-
backoffMs: requestBackoffMs2,
|
|
11845
|
-
describe: describeIdentityError,
|
|
11846
|
-
sleep: deps.sleep
|
|
11847
|
-
});
|
|
11848
|
-
},
|
|
12443
|
+
...identityMethods,
|
|
12444
|
+
...createTeamStorageMethods(trpc, deps, fs, identityMethods.getIdentity),
|
|
11849
12445
|
getFlowsBundleUrl: getFlowsBundleUrlImpl,
|
|
11850
12446
|
callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
|
|
11851
12447
|
async getEnvVars(envId) {
|
|
@@ -11859,24 +12455,6 @@ function createPlatformClient(apiKey, deps) {
|
|
|
11859
12455
|
return result;
|
|
11860
12456
|
return { ok: true, value: result.value.environmentVariables };
|
|
11861
12457
|
},
|
|
11862
|
-
async listTeamStorageFiles() {
|
|
11863
|
-
const identity = await this.getIdentity();
|
|
11864
|
-
if (!identity.ok)
|
|
11865
|
-
return identity;
|
|
11866
|
-
if (!("team" in identity.value)) {
|
|
11867
|
-
return {
|
|
11868
|
-
ok: false,
|
|
11869
|
-
error: flowsMessages.pull.teamStorageRequiresTeamKey
|
|
11870
|
-
};
|
|
11871
|
-
}
|
|
11872
|
-
return listTeamStorageFiles(trpc, { teamId: identity.value.team.id }, deps);
|
|
11873
|
-
},
|
|
11874
|
-
async syncTeamStorageAssets(assetsAbs, opts) {
|
|
11875
|
-
const files = await this.listTeamStorageFiles();
|
|
11876
|
-
if (!files.ok)
|
|
11877
|
-
return files;
|
|
11878
|
-
return downloadTeamStorageAssets({ assetsAbs, files: files.value }, { fetch: deps.fetch, fs, onProgress: opts?.onProgress });
|
|
11879
|
-
},
|
|
11880
12458
|
async downloadBundle(envId) {
|
|
11881
12459
|
const urlResult = await getFlowsBundleUrlImpl(envId);
|
|
11882
12460
|
if (!urlResult.ok)
|
|
@@ -12599,4 +13177,4 @@ export {
|
|
|
12599
13177
|
createRunnerSdk
|
|
12600
13178
|
};
|
|
12601
13179
|
|
|
12602
|
-
//# debugId=
|
|
13180
|
+
//# debugId=1D5281D81CC3C81164756E2164756E21
|