@qawolf/cli 1.23.0 → 1.25.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.
@@ -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, {
@@ -6102,6 +6305,9 @@ var ZodEmail = /* @__PURE__ */ $constructor("ZodEmail", (inst, def) => {
6102
6305
  $ZodEmail.init(inst, def);
6103
6306
  ZodStringFormat.init(inst, def);
6104
6307
  });
6308
+ function email2(params) {
6309
+ return _email(ZodEmail, params);
6310
+ }
6105
6311
  var ZodGUID = /* @__PURE__ */ $constructor("ZodGUID", (inst, def) => {
6106
6312
  $ZodGUID.init(inst, def);
6107
6313
  ZodStringFormat.init(inst, def);
@@ -6165,6 +6371,9 @@ var ZodBase64 = /* @__PURE__ */ $constructor("ZodBase64", (inst, def) => {
6165
6371
  $ZodBase64.init(inst, def);
6166
6372
  ZodStringFormat.init(inst, def);
6167
6373
  });
6374
+ function base642(params) {
6375
+ return _base64(ZodBase64, params);
6376
+ }
6168
6377
  var ZodBase64URL = /* @__PURE__ */ $constructor("ZodBase64URL", (inst, def) => {
6169
6378
  $ZodBase64URL.init(inst, def);
6170
6379
  ZodStringFormat.init(inst, def);
@@ -6747,6 +6956,44 @@ var agentReplySchema = object({
6747
6956
  text: string2().describe("What the AI said.")
6748
6957
  });
6749
6958
 
6959
+ // node_modules/@qawolf/api-contracts/dist/v1/resource.js
6960
+ function resource(fields, { urlFieldDescription = "Absolute URL." } = {}) {
6961
+ return object({
6962
+ ...fields,
6963
+ url: url().describe(urlFieldDescription)
6964
+ });
6965
+ }
6966
+
6967
+ // node_modules/@qawolf/api-contracts/dist/v1/email/resource.js
6968
+ var emailDirectionSchema = _enum(["inbound", "outbound"]);
6969
+ var namedAddressSchema = object({
6970
+ address: string2().describe("The email address."),
6971
+ name: string2().optional().describe("The display name, when the message carried one.")
6972
+ });
6973
+ var emailUrlFieldDescription = "Absolute URL of the workspace. The inbox opens from the app header.";
6974
+ var makeEmailSummaryFields = () => ({
6975
+ createdAt: exports_iso.datetime({ offset: true }).describe("When QA Wolf received or sent the email."),
6976
+ direction: emailDirectionSchema.describe("inbound for mail the workspace received, outbound for mail it sent."),
6977
+ from: namedAddressSchema,
6978
+ id: string2().min(1).describe("The id of the email. Pass it to email.get to read the body."),
6979
+ subject: string2().optional(),
6980
+ to: array(namedAddressSchema)
6981
+ });
6982
+ var makeEmailResourceSchema = () => resource({
6983
+ ...makeEmailSummaryFields(),
6984
+ attachments: array(object({
6985
+ fileName: string2(),
6986
+ index: number2().int().min(0).describe("The position of the attachment in the email, from 0. Pass it to email.getAttachment when file names repeat."),
6987
+ type: string2().optional().describe("The MIME type.")
6988
+ })).optional().describe("Attachment names, positions, and types. Read the content with email.getAttachment."),
6989
+ bcc: array(namedAddressSchema).optional(),
6990
+ cc: array(namedAddressSchema).optional(),
6991
+ html: string2().optional().describe("The HTML body."),
6992
+ links: array(string2()).describe("The links of the email, in order: HTML anchors, then images, then bare URLs of the plain text body. Follow one to confirm a sign-up or reset a password."),
6993
+ replyTo: array(namedAddressSchema).optional(),
6994
+ text: string2().optional().describe("The plain text body.")
6995
+ }, { urlFieldDescription: emailUrlFieldDescription });
6996
+
6750
6997
  // node_modules/@qawolf/api-contracts/dist/v1/identity/organization.js
6751
6998
  var identityOrganization = object({
6752
6999
  id: string2(),
@@ -6789,6 +7036,10 @@ var identityResponse = union([
6789
7036
  user: never().optional()
6790
7037
  }).transform(({ organization }) => ({ organization }))
6791
7038
  ]);
7039
+ // node_modules/@qawolf/api-contracts/dist/v1/identity/organizations.js
7040
+ var identityOrganizationsResponse = object({
7041
+ organizations: array(identityOrganization)
7042
+ });
6792
7043
  // node_modules/@qawolf/api-contracts/dist/v1/ids.js
6793
7044
  var defaultIdSchemas = {
6794
7045
  aiTask: string2().min(1).describe("The id of the AI task."),
@@ -6802,14 +7053,6 @@ var defaultIdSchemas = {
6802
7053
  workspace: string2().min(1).describe("The id of the workspace.")
6803
7054
  };
6804
7055
 
6805
- // node_modules/@qawolf/api-contracts/dist/v1/resource.js
6806
- function resource(fields, { urlFieldDescription = "Absolute URL." } = {}) {
6807
- return object({
6808
- ...fields,
6809
- url: url().describe(urlFieldDescription)
6810
- });
6811
- }
6812
-
6813
7056
  // node_modules/@qawolf/api-contracts/dist/v1/run/flowSelection.js
6814
7057
  var makeFlowSelectionSchema = (ids) => object({
6815
7058
  flowIds: array(ids.flow).default([]),
@@ -7344,6 +7587,131 @@ var makeAutomateContract = (ids) => {
7344
7587
  };
7345
7588
  };
7346
7589
 
7590
+ // node_modules/@qawolf/api-contracts/dist/v1/pagination.js
7591
+ var makePaginationInputFields = (limits) => ({
7592
+ cursor: string2().min(1).optional().describe("Opaque cursor copied from the previous response's nextCursor. Omit to fetch the first page."),
7593
+ limit: number2().int().min(1).max(limits.maxLimit).default(limits.defaultLimit).describe(`Maximum rows per page. Defaults to ${limits.defaultLimit}, capped at ${limits.maxLimit}.`)
7594
+ });
7595
+ var nextCursorSchema = string2().min(1).optional().describe("Repeat the request with this value as cursor to fetch the next page. Absent when there are no further results.");
7596
+
7597
+ // node_modules/@qawolf/api-contracts/dist/v1/email/find.js
7598
+ var makeFindEmailsContract = (ids) => {
7599
+ const input = object({
7600
+ address: email2().optional().describe("Only emails delivered to this exact address (inbound) or sent from it (outbound). Mail to a plus-suffixed address such as inbox+abc@example.com is found by that full address, not by inbox@example.com."),
7601
+ createdAfter: exports_iso.datetime({ offset: true }).optional().describe("Only include emails received or sent after this instant, e.g. since a flow submitted a sign-up form."),
7602
+ direction: emailDirectionSchema.default("inbound").describe("inbound lists the mail the workspace received: its inbox. outbound lists the mail it sent."),
7603
+ workspaceId: ids.workspace.optional().describe("The workspace whose emails to list. Required when authenticating with an organization or user API key."),
7604
+ ...makePaginationInputFields({ defaultLimit: 20, maxLimit: 50 })
7605
+ });
7606
+ const output = object({
7607
+ emails: array(resource(makeEmailSummaryFields(), {
7608
+ urlFieldDescription: emailUrlFieldDescription
7609
+ })).describe("The workspace's emails, newest first. Bodies are available via email.get."),
7610
+ nextCursor: nextCursorSchema
7611
+ });
7612
+ return {
7613
+ description: "List the workspace's inbox, or its sent mail, newest first. Read a message body with email.get.",
7614
+ input,
7615
+ kind: "read",
7616
+ name: "email.find",
7617
+ output
7618
+ };
7619
+ };
7620
+
7621
+ // node_modules/@qawolf/api-contracts/dist/v1/email/get.js
7622
+ var makeGetEmailContract = (ids) => {
7623
+ const input = object({
7624
+ id: string2().min(1).describe("The id of the email, from email.find."),
7625
+ workspaceId: ids.workspace.optional().describe("The workspace the email belongs to. Required when authenticating with an organization or user API key.")
7626
+ });
7627
+ const output = makeEmailResourceSchema();
7628
+ return {
7629
+ description: "Read one email of the workspace, with its plain text and HTML bodies. Use it to pull a sign-in code or a verification link out of a message.",
7630
+ input,
7631
+ kind: "read",
7632
+ name: "email.get",
7633
+ output
7634
+ };
7635
+ };
7636
+
7637
+ // node_modules/@qawolf/api-contracts/dist/v1/email/getAttachment.js
7638
+ var makeGetEmailAttachmentContract = (ids) => {
7639
+ const input = object({
7640
+ fileName: string2().min(1).optional().describe("The attachment's file name, as email.get lists it. The first attachment with that name is returned."),
7641
+ id: string2().min(1).describe("The id of the email, from email.find."),
7642
+ index: number2().int().min(0).optional().describe("The attachment's position in the email, from 0, as email.get lists it. Use it when file names repeat."),
7643
+ workspaceId: ids.workspace.optional().describe("The workspace the email belongs to. Required when authenticating with an organization or user API key.")
7644
+ }).refine((input) => input.fileName === undefined !== (input.index === undefined), { message: "Provide fileName or index, not both." });
7645
+ const output = resource({
7646
+ contentBase64: string2().describe("The file content, base64 encoded."),
7647
+ fileName: string2(),
7648
+ index: number2().int().min(0).describe("The position of the attachment in the email, from 0."),
7649
+ type: string2().optional().describe("The MIME type.")
7650
+ }, { urlFieldDescription: emailUrlFieldDescription });
7651
+ return {
7652
+ description: "Read one attachment of a workspace email as base64 content, by file name or by position. email.get lists both.",
7653
+ input,
7654
+ kind: "read",
7655
+ name: "email.getAttachment",
7656
+ output
7657
+ };
7658
+ };
7659
+
7660
+ // node_modules/@qawolf/api-contracts/dist/v1/email/listAddresses.js
7661
+ var makeListEmailAddressesContract = (ids) => {
7662
+ const input = object({
7663
+ workspaceId: ids.workspace.optional().describe("The workspace whose inbox addresses to list. Required when authenticating with an organization or user API key."),
7664
+ ...makePaginationInputFields({ defaultLimit: 20, maxLimit: 50 })
7665
+ });
7666
+ const output = object({
7667
+ addresses: array(resource({
7668
+ address: string2().describe("The inbox address. Mail sent to any plus-suffixed form, such as inbox+abc@example.com, lands in the same inbox."),
7669
+ isDefault: boolean2().describe("Whether this is the workspace's default inbox address.")
7670
+ }, { urlFieldDescription: emailUrlFieldDescription })).describe("The workspace's permanent inbox addresses, alphabetical."),
7671
+ nextCursor: nextCursorSchema
7672
+ });
7673
+ return {
7674
+ description: "List the workspace's inbox addresses, alphabetical. A flow can sign up with a plus-suffixed form of any of them, and email.find reads what arrives.",
7675
+ input,
7676
+ kind: "read",
7677
+ name: "email.listAddresses",
7678
+ output
7679
+ };
7680
+ };
7681
+
7682
+ // node_modules/@qawolf/api-contracts/dist/v1/email/send.js
7683
+ var recipientsSchema = array(email2()).max(50);
7684
+ var maxAttachments = 10;
7685
+ var maxEmailAttachmentsBase64Length = 30 * 1024 * 1024;
7686
+ var attachmentsBase64Length = (attachments) => Object.values(attachments).reduce((total, content) => total + content.length, 0);
7687
+ var makeSendEmailContract = (ids) => {
7688
+ const input = object({
7689
+ attachments: record(string2().min(1), base642()).refine((attachments) => Object.keys(attachments).length <= maxAttachments, { message: `Send at most ${maxAttachments} attachments.` }).refine((attachments) => attachmentsBase64Length(attachments) <= maxEmailAttachmentsBase64Length, { message: "Send at most 30 MB of attachments." }).optional().describe("Files to attach, as file name to base64 content, at most 30 MB in total. The MIME type comes from the file name's extension."),
7690
+ bcc: recipientsSchema.optional(),
7691
+ cc: recipientsSchema.optional(),
7692
+ from: email2().optional().describe("The workspace inbox address to send from, or a plus-suffixed form of one. Defaults to the workspace's default inbox address. See email.listAddresses."),
7693
+ html: string2().optional().describe("The HTML body."),
7694
+ inReplyTo: string2().min(1).optional().describe("The id of an email of the workspace to reply to, from email.find. The reply threads under that message."),
7695
+ replyTo: recipientsSchema.optional().describe("Where replies should go instead of the from address."),
7696
+ subject: string2().min(1),
7697
+ text: string2().optional().describe("The plain text body."),
7698
+ to: recipientsSchema.min(1),
7699
+ workspaceId: ids.workspace.optional().describe("The workspace to send from. Required when authenticating with an organization or user API key.")
7700
+ }).refine((email) => email.html || email.text, {
7701
+ message: "Provide text, html, or both."
7702
+ });
7703
+ const output = resource(makeEmailSummaryFields(), {
7704
+ urlFieldDescription: emailUrlFieldDescription
7705
+ });
7706
+ return {
7707
+ description: "Send an email from one of the workspace's inbox addresses, for example to exercise a flow that reacts to incoming mail. Returns the sent email; read it back with email.get.",
7708
+ input,
7709
+ kind: "write",
7710
+ name: "email.send",
7711
+ output
7712
+ };
7713
+ };
7714
+
7347
7715
  // node_modules/@qawolf/api-contracts/dist/v1/environment/deleteVariable.js
7348
7716
  var makeDeleteEnvironmentVariableContract = (ids) => {
7349
7717
  const input = object({
@@ -7362,13 +7730,6 @@ var makeDeleteEnvironmentVariableContract = (ids) => {
7362
7730
  };
7363
7731
  };
7364
7732
 
7365
- // node_modules/@qawolf/api-contracts/dist/v1/pagination.js
7366
- var makePaginationInputFields = (limits) => ({
7367
- cursor: string2().min(1).optional().describe("Opaque cursor copied from the previous response's nextCursor. Omit to fetch the first page."),
7368
- limit: number2().int().min(1).max(limits.maxLimit).default(limits.defaultLimit).describe(`Maximum rows per page. Defaults to ${limits.defaultLimit}, capped at ${limits.maxLimit}.`)
7369
- });
7370
- var nextCursorSchema = string2().min(1).optional().describe("Repeat the request with this value as cursor to fetch the next page. Absent when there are no further results.");
7371
-
7372
7733
  // node_modules/@qawolf/api-contracts/dist/v1/environment/resource.js
7373
7734
  var makeEnvironmentResourceSchema = (ids) => resource({
7374
7735
  alias: string2().optional().describe("The environment's alias: a team-unique slug that team API keys can use in place of the id. Terminated environments have none."),
@@ -7540,15 +7901,27 @@ var makeCreateTagContract = (ids) => {
7540
7901
  };
7541
7902
  };
7542
7903
 
7904
+ // node_modules/@qawolf/api-contracts/dist/v1/flow/skippedFlows.js
7905
+ var makeSkippedFlowsSchema = (ids, { description, reason }) => array(object({
7906
+ flowId: ids.flow,
7907
+ name: string2().describe("The name of the flow."),
7908
+ reason
7909
+ })).describe(description);
7910
+
7543
7911
  // node_modules/@qawolf/api-contracts/dist/v1/flow/addTag.js
7544
7912
  var makeAddTagToFlowsContract = (ids) => {
7545
7913
  const input = object({
7546
7914
  flowIds: array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("The flows that receive the tag."),
7547
7915
  tagName: string2().trim().min(1).describe("The name of an existing tag on the caller's team.")
7548
7916
  });
7549
- const output = makeTagResourceSchema();
7917
+ const output = makeTagResourceSchema().extend({
7918
+ skippedFlows: makeSkippedFlowsSchema(ids, {
7919
+ description: "Requested flows that already carried the tag, so nothing changed for them.",
7920
+ reason: _enum(["already-tagged"]).describe('Why the flow was skipped: "already-tagged" means the flow already carried the tag.')
7921
+ })
7922
+ });
7550
7923
  return {
7551
- description: "Assign an existing tag to the selected flows. Create tags with tag.create.",
7924
+ description: "Assign an existing tag to the selected flows. Create tags with tag.create. Flows that already carry the tag are reported in skippedFlows.",
7552
7925
  input,
7553
7926
  kind: "write",
7554
7927
  name: "flow.addTag",
@@ -7588,6 +7961,27 @@ var makeListFlowsContract = (ids) => {
7588
7961
  };
7589
7962
  };
7590
7963
 
7964
+ // node_modules/@qawolf/api-contracts/dist/v1/flow/removeTag.js
7965
+ var makeRemoveTagFromFlowsContract = (ids) => {
7966
+ const input = object({
7967
+ flowIds: array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("The flows that lose the tag."),
7968
+ tagName: string2().trim().min(1).describe("The name of an existing tag on the caller's team.")
7969
+ });
7970
+ const output = makeTagResourceSchema().extend({
7971
+ skippedFlows: makeSkippedFlowsSchema(ids, {
7972
+ description: "Requested flows that did not carry the tag, so nothing changed for them.",
7973
+ reason: _enum(["not-tagged"]).describe('Why the flow was skipped: "not-tagged" means the flow did not carry the tag.')
7974
+ })
7975
+ });
7976
+ return {
7977
+ description: "Remove a tag from the selected flows. Succeeds whether or not each flow carried the tag; the flows that did not are reported in skippedFlows.",
7978
+ input,
7979
+ kind: "write",
7980
+ name: "flow.removeTag",
7981
+ output
7982
+ };
7983
+ };
7984
+
7591
7985
  // node_modules/@qawolf/api-contracts/dist/v1/flow/update.js
7592
7986
  var makeUpdateFlowContract = (ids) => {
7593
7987
  const input = object({
@@ -7732,6 +8126,24 @@ var makeCreateIssueContract = (ids) => {
7732
8126
  };
7733
8127
  };
7734
8128
 
8129
+ // node_modules/@qawolf/api-contracts/dist/v1/issue/removeFlows.js
8130
+ var makeRemoveFlowsFromIssueContract = (ids) => {
8131
+ const input = object({
8132
+ flowIds: array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("The flows the coverage request no longer covers."),
8133
+ issueId: ids.issue
8134
+ });
8135
+ const output = object({
8136
+ issue: makeIssueResourceSchema(ids)
8137
+ });
8138
+ return {
8139
+ description: "Remove flows from a coverage request owned by the caller's team. " + "Flows the request does not cover are left alone. " + "Bug and maintenance reports link to flows through the runs that reproduce them, so their flows cannot be set directly.",
8140
+ input,
8141
+ kind: "write",
8142
+ name: "issue.removeFlows",
8143
+ output
8144
+ };
8145
+ };
8146
+
7735
8147
  // node_modules/@qawolf/api-contracts/dist/v1/issue/update.js
7736
8148
  var makeUpdateIssueContract = (ids) => {
7737
8149
  const descriptionSchema = string2().describe("The issue description as plain text; each line becomes a paragraph. Markdown is not parsed.");
@@ -8058,6 +8470,22 @@ var makeTerminateRunnerContract = () => {
8058
8470
  };
8059
8471
  };
8060
8472
 
8473
+ // node_modules/@qawolf/api-contracts/dist/v1/runner/list.js
8474
+ var makeListRunnersContract = () => {
8475
+ const input = object({});
8476
+ const output = object({
8477
+ outcome: literal("success"),
8478
+ 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.")
8479
+ });
8480
+ return {
8481
+ description: "List the runners running on the caller's team right now. Takes no arguments: the API key already names the team. 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.",
8482
+ input,
8483
+ kind: "read",
8484
+ name: "runner.list",
8485
+ output
8486
+ };
8487
+ };
8488
+
8061
8489
  // node_modules/@qawolf/api-contracts/dist/v1/runner/promoteSnapshot.js
8062
8490
  var maxSnapshotPathLength = 1024;
8063
8491
  var makePromoteSnapshotOnRunnerContract = () => {
@@ -8253,6 +8681,13 @@ var makeContractsV1 = (ids) => {
8253
8681
  send: makeAgentSendContract(resolvedIds)
8254
8682
  },
8255
8683
  automate: makeAutomateContract(resolvedIds),
8684
+ email: {
8685
+ find: makeFindEmailsContract(resolvedIds),
8686
+ get: makeGetEmailContract(resolvedIds),
8687
+ getAttachment: makeGetEmailAttachmentContract(resolvedIds),
8688
+ listAddresses: makeListEmailAddressesContract(resolvedIds),
8689
+ send: makeSendEmailContract(resolvedIds)
8690
+ },
8256
8691
  environment: {
8257
8692
  create: makeCreateEnvironmentContract(resolvedIds),
8258
8693
  deleteVariable: makeDeleteEnvironmentVariableContract(resolvedIds),
@@ -8266,6 +8701,7 @@ var makeContractsV1 = (ids) => {
8266
8701
  flow: {
8267
8702
  addTag: makeAddTagToFlowsContract(resolvedIds),
8268
8703
  list: makeListFlowsContract(resolvedIds),
8704
+ removeTag: makeRemoveTagFromFlowsContract(resolvedIds),
8269
8705
  update: makeUpdateFlowContract(resolvedIds)
8270
8706
  },
8271
8707
  issue: {
@@ -8273,6 +8709,7 @@ var makeContractsV1 = (ids) => {
8273
8709
  create: makeCreateIssueContract(resolvedIds),
8274
8710
  find: makeFindIssuesContract(resolvedIds),
8275
8711
  get: makeGetIssueContract(resolvedIds),
8712
+ removeFlows: makeRemoveFlowsFromIssueContract(resolvedIds),
8276
8713
  update: makeUpdateIssueContract(resolvedIds)
8277
8714
  },
8278
8715
  run: {
@@ -8290,6 +8727,7 @@ var makeContractsV1 = (ids) => {
8290
8727
  inspect: makeInspectOnRunnerContract(),
8291
8728
  inspectMobile: makeInspectMobileOnRunnerContract(),
8292
8729
  launch: makeLaunchRunnerContract(),
8730
+ list: makeListRunnersContract(),
8293
8731
  performAction: makePerformActionOnRunnerContract(),
8294
8732
  promoteSnapshot: makePromoteSnapshotOnRunnerContract(),
8295
8733
  readJournal: makeReadRunnerJournalContract(),
@@ -8306,18 +8744,9 @@ var makeContractsV1 = (ids) => {
8306
8744
  };
8307
8745
  var publicContractsV1 = makeContractsV1(defaultIdSchemas);
8308
8746
 
8309
- // src/core/batchMap.ts
8310
- async function* batchMap(items, fn, size) {
8311
- if (size < 1) {
8312
- throw new Error("batchMap size must be greater than 0");
8313
- }
8314
- for (let i = 0;i < items.length; i += size) {
8315
- const batch = items.slice(i, i + size);
8316
- const results = await Promise.all(batch.map(fn));
8317
- for (const result of results) {
8318
- yield result;
8319
- }
8320
- }
8747
+ // src/core/pluralize.ts
8748
+ function pluralize(count, singular, plural = `${singular}s`) {
8749
+ return `${String(count)} ${count === 1 ? singular : plural}`;
8321
8750
  }
8322
8751
 
8323
8752
  // src/core/formatSeconds.ts
@@ -8325,6 +8754,72 @@ function formatSeconds(ms) {
8325
8754
  return `${Math.round(ms / 1000)}s`;
8326
8755
  }
8327
8756
 
8757
+ // src/core/messages/authErrors.ts
8758
+ var authErrorMessages = {
8759
+ identity: {
8760
+ invalidOrUnauthorized: "API key is invalid or unauthorized",
8761
+ unexpectedFormat: "Could not verify API key: unexpected response format",
8762
+ couldNotVerify: (detail, status) => `Could not verify API key: ${detail || `HTTP ${status}`}`,
8763
+ couldNotVerifyNetwork: (cause) => `Could not verify API key: ${cause}`,
8764
+ timedOut: (timeoutMs) => `Could not verify API key: the QA Wolf API did not answer within ${formatSeconds(timeoutMs)}.`
8765
+ },
8766
+ request: {
8767
+ rejected401: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 401). Check your API key.`,
8768
+ rejected402: (noun) => `QA Wolf API refused the${noun ? ` ${noun}` : ""} request (HTTP 402): billing prevented it.`,
8769
+ rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
8770
+ notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
8771
+ failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
8772
+ networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
8773
+ timedOut: (timeoutMs, noun) => `The QA Wolf API${noun ? ` ${noun}` : ""} request timed out after ${formatSeconds(timeoutMs)}. The work may still be finishing on the platform.`,
8774
+ unexpectedResponse: (noun) => `Unexpected${noun ? ` ${noun}` : ""} response from the QA Wolf API.`
8775
+ },
8776
+ workos: {
8777
+ unexpectedResponse: "WorkOS returned an unexpected response",
8778
+ unexpectedResponseWithStatus: (status) => `WorkOS returned an unexpected response (HTTP ${status})`,
8779
+ unreachable: (detail) => `Could not reach WorkOS: ${detail}`,
8780
+ redirected: "WorkOS answered with a redirect, which the CLI does not follow for a sign-in request",
8781
+ noClientForSession: "This session names no WorkOS client"
8782
+ },
8783
+ bundle: {
8784
+ linkExpired: "The flow bundle download link has expired. Please run `qawolf flows pull` again to refresh.",
8785
+ failedWithStatus: (status) => `Could not download the flow bundle (HTTP ${status}).`,
8786
+ networkUnreachable: "Could not reach the flow bundle storage. Check your network connection and try again.",
8787
+ timedOut: (timeoutMs) => `Downloading the flow bundle stalled — no data arrived for ${formatSeconds(timeoutMs)}. Please try again.`,
8788
+ malformed: "The flow bundle download was malformed. Please run `qawolf flows pull` again."
8789
+ }
8790
+ };
8791
+
8792
+ // src/core/messages/whoami.ts
8793
+ var whoamiMessages = {
8794
+ source: (source) => `Source: ${source}`,
8795
+ authFailed: (source, error) => `Authentication failed (source: ${source}): ${error}`,
8796
+ authenticatedAs: (teamName, source) => `Authenticated as ${teamName} (source: ${source})`,
8797
+ teamNote: (input) => [
8798
+ `Workspace: ${input.team.name}`,
8799
+ `ID: ${input.team.id}`,
8800
+ input.team.slug ? `Slug: ${input.team.slug}` : undefined,
8801
+ input.teamUrl ? `URL: ${input.teamUrl}` : undefined,
8802
+ `Source: ${input.source}`
8803
+ ].filter((line) => Boolean(line)).join(`
8804
+ `),
8805
+ organizationNote: (input) => [
8806
+ `Organization: ${input.organization.name}`,
8807
+ `ID: ${input.organization.id}`,
8808
+ `Source: ${input.source}`
8809
+ ].join(`
8810
+ `),
8811
+ activeWorkspace: (workspaceId, found) => found ? `${found.workspace} (${found.organization})` : `${workspaceId} — no longer in reach; run 'qawolf auth switch'`,
8812
+ userNote: (input) => [
8813
+ `User: ${input.user.email}`,
8814
+ `ID: ${input.user.id}`,
8815
+ `Organization: ${input.organization.name}`,
8816
+ input.activeWorkspace ? `Workspace: ${input.activeWorkspace}` : undefined,
8817
+ input.organizations.length > 0 ? `Workspaces: ${input.organizations.map((o) => `${o.name}: ${o.workspaces.map((w) => w.name).join(", ")}`).join(" | ")}` : undefined,
8818
+ `Source: ${input.source}`
8819
+ ].filter((line) => Boolean(line)).join(`
8820
+ `)
8821
+ };
8822
+
8328
8823
  // src/core/messages/auth.ts
8329
8824
  var authMessages = {
8330
8825
  title: "QA Wolf Authentication",
@@ -8342,7 +8837,47 @@ var authMessages = {
8342
8837
  whoamiFailed: "Not authenticated",
8343
8838
  login: {
8344
8839
  nonInteractive: "auth login requires an interactive terminal. Set the QAWOLF_API_KEY environment variable for CI authentication.",
8345
- reAuthPrompt: "You are already authenticated. Re-authenticate?"
8840
+ reAuthPrompt: "You are already authenticated. Re-authenticate?",
8841
+ chooseMethod: "How do you want to sign in?",
8842
+ methodBrowser: "Browser",
8843
+ methodBrowserHint: "Sign in with your QA Wolf account",
8844
+ methodApiKey: "API key",
8845
+ methodApiKeyHint: "Paste a team key — needed for flows pull and flows run",
8846
+ apiKeyPrecedence: {
8847
+ 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.",
8848
+ 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."
8849
+ }
8850
+ },
8851
+ device: {
8852
+ unavailable: "This QA Wolf deployment does not offer browser sign-in. Run 'qawolf auth login' again and choose 'API key'.",
8853
+ configUnreachable: "Could not ask this QA Wolf deployment whether it offers browser sign-in. Check your connection, then try again.",
8854
+ confirmCode: (userCode) => `Your code is ${userCode}`,
8855
+ visitUrl: (url) => `Confirm it at ${url}`,
8856
+ visitUrlPlain: (url) => `Or go to ${url} and enter the code`,
8857
+ openFailed: (url) => `Could not open a browser automatically. Open ${url} yourself to continue.`,
8858
+ waiting: "Waiting for you to finish in the browser",
8859
+ signedIn: (email) => `Signed in as ${email}.`,
8860
+ failed: {
8861
+ "access-denied": "The sign-in request was rejected.",
8862
+ expired: "The sign-in request expired. Run 'qawolf auth login' to retry.",
8863
+ timeout: "The sign-in request timed out. Run 'qawolf auth login' to retry.",
8864
+ network: "Could not reach WorkOS to complete sign-in.",
8865
+ unavailable: "Could not start browser sign-in.",
8866
+ cancelled: "Sign-in cancelled."
8867
+ }
8868
+ },
8869
+ workspace: {
8870
+ chooseOrganization: "Which organization do you want to work in?",
8871
+ choose: "Which workspace do you want to use?",
8872
+ workspaceCount: (count) => pluralize(count, "workspace"),
8873
+ working: (organization, workspace) => `Working in ${workspace} (${organization}).`,
8874
+ none: "This account reaches no organizations yet.",
8875
+ cancelled: "Workspace not changed.",
8876
+ notSignedIn: "Workspace switching needs a browser sign-in. Run 'qawolf auth login' and choose Browser.",
8877
+ sessionExpired: "Your session could not be renewed. Run 'qawolf auth login' to sign in again.",
8878
+ nonInteractive: "auth switch needs an interactive terminal, or set QAWOLF_WORKSPACE to name a workspace.",
8879
+ sessionChanged: "The stored sign-in changed while you made your choice. Run the command again.",
8880
+ saveFailed: (detail) => `Could not save the workspace choice: ${detail}`
8346
8881
  },
8347
8882
  logout: {
8348
8883
  title: "Log Out",
@@ -8354,58 +8889,8 @@ var authMessages = {
8354
8889
  success: "Logged out successfully.",
8355
8890
  cancelled: "Logout cancelled."
8356
8891
  },
8357
- errors: {
8358
- identity: {
8359
- invalidOrUnauthorized: "API key is invalid or unauthorized",
8360
- unexpectedFormat: "Could not verify API key: unexpected response format",
8361
- couldNotVerify: (detail, status) => `Could not verify API key: ${detail || `HTTP ${status}`}`,
8362
- couldNotVerifyNetwork: (cause) => `Could not verify API key: ${cause}`,
8363
- timedOut: (timeoutMs) => `Could not verify API key: the QA Wolf API did not answer within ${formatSeconds(timeoutMs)}.`
8364
- },
8365
- request: {
8366
- rejected401: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 401). Check your API key.`,
8367
- rejected402: (noun) => `QA Wolf API refused the${noun ? ` ${noun}` : ""} request (HTTP 402): billing prevented it.`,
8368
- rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
8369
- notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
8370
- failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
8371
- networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
8372
- timedOut: (timeoutMs, noun) => `The QA Wolf API${noun ? ` ${noun}` : ""} request timed out after ${formatSeconds(timeoutMs)}. The work may still be finishing on the platform.`,
8373
- unexpectedResponse: (noun) => `Unexpected${noun ? ` ${noun}` : ""} response from the QA Wolf API.`
8374
- },
8375
- bundle: {
8376
- linkExpired: "The flow bundle download link has expired. Please run `qawolf flows pull` again to refresh.",
8377
- failedWithStatus: (status) => `Could not download the flow bundle (HTTP ${status}).`,
8378
- networkUnreachable: "Could not reach the flow bundle storage. Check your network connection and try again.",
8379
- timedOut: (timeoutMs) => `Downloading the flow bundle stalled — no data arrived for ${formatSeconds(timeoutMs)}. Please try again.`,
8380
- malformed: "The flow bundle download was malformed. Please run `qawolf flows pull` again."
8381
- }
8382
- },
8383
- whoami: {
8384
- source: (source) => `Source: ${source}`,
8385
- authFailed: (source, error) => `Authentication failed (source: ${source}): ${error}`,
8386
- authenticatedAs: (teamName, source) => `Authenticated as ${teamName} (source: ${source})`,
8387
- teamNote: (input) => [
8388
- `Team: ${input.team.name}`,
8389
- `ID: ${input.team.id}`,
8390
- input.team.slug ? `Slug: ${input.team.slug}` : undefined,
8391
- input.teamUrl ? `URL: ${input.teamUrl}` : undefined,
8392
- `Source: ${input.source}`
8393
- ].filter((line) => Boolean(line)).join(`
8394
- `),
8395
- organizationNote: (input) => [
8396
- `Organization: ${input.organization.name}`,
8397
- `ID: ${input.organization.id}`,
8398
- `Source: ${input.source}`
8399
- ].join(`
8400
- `),
8401
- userNote: (input) => [
8402
- `User: ${input.user.email}`,
8403
- `ID: ${input.user.id}`,
8404
- `Organization: ${input.organization.name}`,
8405
- `Source: ${input.source}`
8406
- ].join(`
8407
- `)
8408
- }
8892
+ errors: authErrorMessages,
8893
+ whoami: whoamiMessages
8409
8894
  };
8410
8895
  // src/core/messages/toolNotFound.ts
8411
8896
  var installRemedy = "Run `qawolf install` to install the runtime dependencies.";
@@ -8421,11 +8906,6 @@ ${installRemedy}`;
8421
8906
  function packageLoadFailed(pkg, envDir, detail) {
8422
8907
  return toolNotRunnable(`Could not load ${pkg} from ${envDir}`, detail);
8423
8908
  }
8424
- // src/core/pluralize.ts
8425
- function pluralize(count, singular, plural = `${singular}s`) {
8426
- return `${String(count)} ${count === 1 ? singular : plural}`;
8427
- }
8428
-
8429
8909
  // src/core/messages/flows.ts
8430
8910
  var flowsMessages = {
8431
8911
  title: "Flows",
@@ -8628,7 +9108,8 @@ var lifecycleMessages = {
8628
9108
 
8629
9109
  // src/core/messages/interactiveRunner/list.ts
8630
9110
  var listMessages = {
8631
- noRunners: "This directory has no runner running. Launch one with qawolf runner launch, or pass --runner to address a runner started elsewhere.",
9111
+ noRunners: "Your team has no runner running. Launch one with qawolf runner launch.",
9112
+ noRunnersHere: "This directory has no runner running. Launch one with qawolf runner launch, or drop --here to see the whole team's runners.",
8632
9113
  runnerCount: (count) => count === 1 ? "1 runner" : `${String(count)} runners`,
8633
9114
  title: "Runners"
8634
9115
  };
@@ -8776,50 +9257,37 @@ var runnerCallOptions = { timeoutMs: 90000 };
8776
9257
  var runnerIdEnvironmentVariable = "QAWOLF_RUNNER_ID";
8777
9258
 
8778
9259
  // src/domains/interactiveRunner/list.ts
8779
- var probeBatchSize = 8;
8780
- async function readCandidates(deps) {
8781
- const held = await deps.store.readRunners();
8782
- const storedDefault = await deps.store.readDefaultRunnerId();
8783
- const environmentValue = deps.env[runnerIdEnvironmentVariable]?.trim();
8784
- const fromEnvironment = environmentValue ? environmentValue : undefined;
8785
- const candidates = [...held];
8786
- for (const id of [storedDefault, fromEnvironment]) {
8787
- if (id !== undefined && !candidates.some((runner) => runner.id === id)) {
8788
- candidates.push({ id });
8789
- }
8790
- }
8791
- return { candidates, defaultRunnerId: fromEnvironment ?? storedDefault };
9260
+ function rank(item) {
9261
+ if (item.isDefault)
9262
+ return 0;
9263
+ return item.launchedHere ? 1 : 2;
8792
9264
  }
8793
- async function probeRunner(ctx, runner) {
8794
- const result = await ctx.platformClient.callPublicApi(publicContractsV1.runner.get, { id: runner.id }, runnerCallOptions);
8795
- if (!result.ok)
8796
- return { ...failureFields(result), ok: false };
8797
- return { ok: true, runner, running: result.value.running };
9265
+ async function readDefaultRunnerId(deps) {
9266
+ const environmentValue = deps.env[runnerIdEnvironmentVariable]?.trim();
9267
+ if (environmentValue)
9268
+ return environmentValue;
9269
+ return deps.store.readDefaultRunnerId();
8798
9270
  }
8799
9271
  async function listRunners(ctx, deps) {
8800
- const { candidates, defaultRunnerId } = await readCandidates(deps);
8801
- const running = [];
8802
- const gone = [];
8803
- for await (const probe of batchMap(candidates, (runner) => probeRunner(ctx, runner), probeBatchSize)) {
8804
- if (!probe.ok)
8805
- return { ...failureFields(probe), ok: false };
8806
- if (probe.running)
8807
- running.push(probe.runner);
8808
- else
8809
- gone.push(probe.runner.id);
8810
- }
9272
+ const listed = await ctx.platformClient.callPublicApi(publicContractsV1.runner.list, {}, runnerCallOptions);
9273
+ if (!listed.ok)
9274
+ return { ...failureFields(listed), ok: false };
9275
+ const defaultRunnerId = await readDefaultRunnerId(deps);
9276
+ const runningIds = new Set(listed.value.runners.map((runner) => runner.id));
9277
+ const held = await deps.store.readRunners();
9278
+ const gone = held.filter((runner) => !runningIds.has(runner.id)).map((runner) => runner.id);
8811
9279
  await deps.store.dropRunners(gone).catch(() => {
8812
9280
  return;
8813
9281
  });
9282
+ const heldIds = new Set(held.map((runner) => runner.id));
9283
+ const items = listed.value.runners.map((runner) => ({
9284
+ id: runner.id,
9285
+ isDefault: runner.id === defaultRunnerId,
9286
+ launchedHere: heldIds.has(runner.id),
9287
+ runnerName: runner.runnerName
9288
+ }));
8814
9289
  return {
8815
- items: [
8816
- ...running.filter((runner) => runner.id === defaultRunnerId),
8817
- ...running.filter((runner) => runner.id !== defaultRunnerId)
8818
- ].map((runner) => ({
8819
- id: runner.id,
8820
- isDefault: runner.id === defaultRunnerId,
8821
- runnerName: runner.runnerName
8822
- })),
9290
+ items: items.sort((left, right) => rank(left) - rank(right) || left.id.localeCompare(right.id)),
8823
9291
  ok: true
8824
9292
  };
8825
9293
  }
@@ -10160,6 +10628,176 @@ function makeDefaultFs() {
10160
10628
  };
10161
10629
  }
10162
10630
 
10631
+ // src/core/publicApi/flagKind.ts
10632
+ function flagKind(schema) {
10633
+ if (schema.type === "string")
10634
+ return { ok: true, kind: "string" };
10635
+ if (schema.type === "number" || schema.type === "integer") {
10636
+ return { ok: true, kind: "number" };
10637
+ }
10638
+ if (schema.type === "boolean")
10639
+ return { ok: true, kind: "boolean" };
10640
+ if (schema.type === "array") {
10641
+ if (schema.items?.type !== "string") {
10642
+ return {
10643
+ ok: false,
10644
+ reason: "only string arrays can be expressed as flags"
10645
+ };
10646
+ }
10647
+ return { ok: true, kind: "string-array" };
10648
+ }
10649
+ if (schema.type === "object") {
10650
+ if (schema.properties) {
10651
+ return {
10652
+ ok: false,
10653
+ reason: "nested objects cannot be expressed as flags"
10654
+ };
10655
+ }
10656
+ if (typeof schema.additionalProperties === "object" && schema.additionalProperties.type === "string") {
10657
+ return { ok: true, kind: "key-value-record" };
10658
+ }
10659
+ return { ok: false, reason: "nested objects cannot be expressed as flags" };
10660
+ }
10661
+ return {
10662
+ ok: false,
10663
+ reason: `type ${schema.type ?? "unknown"} cannot be expressed as a flag`
10664
+ };
10665
+ }
10666
+
10667
+ // src/core/publicApi/objectShape.ts
10668
+ var asObjectShape = (schema) => schema.type === "object" && schema.properties ? { properties: schema.properties, required: new Set(schema.required) } : undefined;
10669
+ function mergeIntersection(members) {
10670
+ const shape = { properties: {}, required: new Set };
10671
+ for (const member of members) {
10672
+ const memberShape = asObjectShape(member);
10673
+ if (!memberShape) {
10674
+ return {
10675
+ ok: false,
10676
+ field: "",
10677
+ reason: "intersection members must be object schemas"
10678
+ };
10679
+ }
10680
+ for (const [field, fieldSchema] of Object.entries(memberShape.properties)) {
10681
+ if (field in shape.properties) {
10682
+ return {
10683
+ ok: false,
10684
+ field,
10685
+ reason: "field appears in multiple intersection members"
10686
+ };
10687
+ }
10688
+ shape.properties[field] = fieldSchema;
10689
+ if (memberShape.required.has(field))
10690
+ shape.required.add(field);
10691
+ }
10692
+ }
10693
+ return { ok: true, shape };
10694
+ }
10695
+ function mergeUnion(branches) {
10696
+ const shapes = [];
10697
+ for (const branch of branches) {
10698
+ const branchShape = asObjectShape(branch);
10699
+ if (!branchShape) {
10700
+ return {
10701
+ ok: false,
10702
+ field: "",
10703
+ reason: "union branches must be object schemas"
10704
+ };
10705
+ }
10706
+ shapes.push(branchShape);
10707
+ }
10708
+ const shape = { properties: {}, required: new Set };
10709
+ for (const branchShape of shapes) {
10710
+ for (const [field, fieldSchema] of Object.entries(branchShape.properties)) {
10711
+ const existing = shape.properties[field];
10712
+ if (!existing) {
10713
+ shape.properties[field] = { ...fieldSchema };
10714
+ continue;
10715
+ }
10716
+ const kinds = [flagKind(existing), flagKind(fieldSchema)];
10717
+ if (kinds[0]?.ok && kinds[1]?.ok) {
10718
+ if (kinds[0].kind !== kinds[1].kind) {
10719
+ return {
10720
+ ok: false,
10721
+ field,
10722
+ reason: "field has conflicting types across union branches"
10723
+ };
10724
+ }
10725
+ } else if (JSON.stringify({ ...existing, description: undefined }) !== JSON.stringify({ ...fieldSchema, description: undefined })) {
10726
+ return {
10727
+ ok: false,
10728
+ field,
10729
+ reason: "field has conflicting object schemas across union branches"
10730
+ };
10731
+ }
10732
+ if (!existing.description && fieldSchema.description) {
10733
+ existing.description = fieldSchema.description;
10734
+ }
10735
+ }
10736
+ }
10737
+ const discriminator = Object.keys(shape.properties).find((field) => {
10738
+ const values = shapes.map((branch) => branch.properties[field]?.const);
10739
+ return values.every((value) => typeof value === "string") && new Set(values).size === shapes.length && shapes.every((branch) => branch.required.has(field));
10740
+ });
10741
+ for (const [field, fieldSchema] of Object.entries(shape.properties)) {
10742
+ const occurrences = shapes.filter((branch) => (field in branch.properties));
10743
+ if (occurrences.length === shapes.length && occurrences.every((branch) => branch.required.has(field))) {
10744
+ shape.required.add(field);
10745
+ }
10746
+ if (field === discriminator) {
10747
+ const values = shapes.map((branch) => branch.properties[field]?.const);
10748
+ fieldSchema.description = `One of: ${values.join(", ")}`;
10749
+ }
10750
+ }
10751
+ return { ok: true, shape };
10752
+ }
10753
+ function toObjectShape(schema) {
10754
+ if (schema.allOf)
10755
+ return mergeIntersection(schema.allOf);
10756
+ const branches = schema.oneOf ?? schema.anyOf;
10757
+ if (branches)
10758
+ return mergeUnion(branches);
10759
+ const shape = asObjectShape(schema);
10760
+ if (shape)
10761
+ return { ok: true, shape };
10762
+ return {
10763
+ ok: false,
10764
+ field: "",
10765
+ reason: "contract input must be an object schema"
10766
+ };
10767
+ }
10768
+
10769
+ // src/shell/platform/applyWorkspaceId.ts
10770
+ var acceptsWorkspace = new WeakMap;
10771
+ function takesWorkspaceId(inputSchema) {
10772
+ const cached = acceptsWorkspace.get(inputSchema);
10773
+ if (cached !== undefined)
10774
+ return cached;
10775
+ let accepted = false;
10776
+ try {
10777
+ const jsonSchema = toJSONSchema(inputSchema, {
10778
+ io: "input"
10779
+ });
10780
+ const shape = toObjectShape(jsonSchema);
10781
+ accepted = shape.ok && "workspaceId" in shape.shape.properties;
10782
+ } catch {
10783
+ accepted = false;
10784
+ }
10785
+ acceptsWorkspace.set(inputSchema, accepted);
10786
+ return accepted;
10787
+ }
10788
+ function applyWorkspaceId(inputSchema, input, workspaceId) {
10789
+ if (!workspaceId)
10790
+ return input;
10791
+ if (typeof input !== "object" || input === null)
10792
+ return input;
10793
+ const existing = input.workspaceId;
10794
+ if (existing !== undefined)
10795
+ return input;
10796
+ if (!takesWorkspaceId(inputSchema))
10797
+ return input;
10798
+ return { ...input, workspaceId };
10799
+ }
10800
+
10163
10801
  // src/shell/platform/parseErrorBody.ts
10164
10802
  var defaultMaxLength = 1024;
10165
10803
  function asRecord(value) {
@@ -10292,7 +10930,7 @@ function callPublicApi(trpc, contract, input, options) {
10292
10930
  }
10293
10931
  function makeCallPublicApiMethod(trpc, deps, readBackoffMs) {
10294
10932
  return async (contract, input, options) => requestWithRetry({
10295
- call: () => callPublicApi(trpc, contract, input, options),
10933
+ call: () => callPublicApi(trpc, contract, applyWorkspaceId(contract.input, input, deps.workspaceId), options),
10296
10934
  backoffMs: contract.kind === "read" ? readBackoffMs : [],
10297
10935
  describe: (err) => describeRequestError(err, deps.baseUrl, contract.name),
10298
10936
  sleep: deps.sleep
@@ -11292,10 +11930,16 @@ async function downloadBundle(signedUrl, deps) {
11292
11930
  return { ok: true, value: { tmpArchive } };
11293
11931
  }
11294
11932
 
11295
- // src/shell/platform/getIdentity.ts
11933
+ // src/shell/platform/organizations.ts
11934
+ function parseOrganizationsResponse(json) {
11935
+ const parsed = identityOrganizationsResponse.safeParse(json);
11936
+ return parsed.success ? parsed.data.organizations : undefined;
11937
+ }
11938
+
11939
+ // src/shell/platform/getAccessibleOrganizations.ts
11296
11940
  var timeoutMs = 1e4;
11297
- async function getIdentity(apiKey, deps) {
11298
- const url = `${deps.baseUrl}/api/v0/identity`;
11941
+ async function getAccessibleOrganizations(apiKey, deps) {
11942
+ const url = `${deps.baseUrl}/api/v0/identity/organizations`;
11299
11943
  let response;
11300
11944
  try {
11301
11945
  response = await deps.fetch(url, {
@@ -11306,6 +11950,51 @@ async function getIdentity(apiKey, deps) {
11306
11950
  if (isTimeoutError(error)) {
11307
11951
  return { ok: false, error: { kind: "timeout", timeoutMs } };
11308
11952
  }
11953
+ return { ok: false, error: { kind: "network", cause: toError(error) } };
11954
+ }
11955
+ if (!response.ok) {
11956
+ const body = await response.text().catch(() => "");
11957
+ return {
11958
+ ok: false,
11959
+ error: { kind: "http", status: response.status, body }
11960
+ };
11961
+ }
11962
+ let json;
11963
+ try {
11964
+ json = await response.json();
11965
+ } catch (error) {
11966
+ if (isTimeoutError(error)) {
11967
+ return { ok: false, error: { kind: "timeout", timeoutMs } };
11968
+ }
11969
+ json = undefined;
11970
+ }
11971
+ const organizations = parseOrganizationsResponse(json);
11972
+ if (!organizations) {
11973
+ return {
11974
+ ok: false,
11975
+ error: {
11976
+ kind: "parse",
11977
+ cause: Error("organizations response did not match the contract")
11978
+ }
11979
+ };
11980
+ }
11981
+ return { ok: true, data: organizations };
11982
+ }
11983
+
11984
+ // src/shell/platform/getIdentity.ts
11985
+ var timeoutMs2 = 1e4;
11986
+ async function getIdentity(apiKey, deps) {
11987
+ const url = `${deps.baseUrl}/api/v0/identity`;
11988
+ let response;
11989
+ try {
11990
+ response = await deps.fetch(url, {
11991
+ headers: { Authorization: `Bearer ${apiKey}` },
11992
+ signal: AbortSignal.timeout(timeoutMs2)
11993
+ });
11994
+ } catch (error) {
11995
+ if (isTimeoutError(error)) {
11996
+ return { ok: false, error: { kind: "timeout", timeoutMs: timeoutMs2 } };
11997
+ }
11309
11998
  return {
11310
11999
  ok: false,
11311
12000
  error: { kind: "network", cause: toError(error) }
@@ -11323,7 +12012,7 @@ async function getIdentity(apiKey, deps) {
11323
12012
  json = await response.json();
11324
12013
  } catch (error) {
11325
12014
  if (isTimeoutError(error)) {
11326
- return { ok: false, error: { kind: "timeout", timeoutMs } };
12015
+ return { ok: false, error: { kind: "timeout", timeoutMs: timeoutMs2 } };
11327
12016
  }
11328
12017
  json = undefined;
11329
12018
  }
@@ -11334,6 +12023,24 @@ async function getIdentity(apiKey, deps) {
11334
12023
  return { ok: true, data: parsed.data };
11335
12024
  }
11336
12025
 
12026
+ // src/shell/platform/identityMethods.ts
12027
+ function createIdentityMethods(apiKey, deps, backoffMs) {
12028
+ return {
12029
+ getIdentity: () => requestWithRetry({
12030
+ call: () => getIdentity(apiKey, deps),
12031
+ backoffMs,
12032
+ describe: describeIdentityError,
12033
+ sleep: deps.sleep
12034
+ }),
12035
+ getAccessibleOrganizations: () => requestWithRetry({
12036
+ call: () => getAccessibleOrganizations(apiKey, deps),
12037
+ backoffMs,
12038
+ describe: describeIdentityError,
12039
+ sleep: deps.sleep
12040
+ })
12041
+ };
12042
+ }
12043
+
11337
12044
  // src/shell/platform/types.ts
11338
12045
  var environmentListItemSchema = object({
11339
12046
  envId: string2(),
@@ -11633,14 +12340,7 @@ function createPlatformClient(apiKey, deps) {
11633
12340
  return { ok: true, value: { signedUrl: result.value.url } };
11634
12341
  }
11635
12342
  return {
11636
- async getIdentity() {
11637
- return requestWithRetry({
11638
- call: () => getIdentity(apiKey, deps),
11639
- backoffMs: requestBackoffMs2,
11640
- describe: describeIdentityError,
11641
- sleep: deps.sleep
11642
- });
11643
- },
12343
+ ...createIdentityMethods(apiKey, deps, requestBackoffMs2),
11644
12344
  getFlowsBundleUrl: getFlowsBundleUrlImpl,
11645
12345
  callPublicApi: makeCallPublicApiMethod(trpc, deps, requestBackoffMs2),
11646
12346
  async getEnvVars(envId) {
@@ -12394,4 +13094,4 @@ export {
12394
13094
  createRunnerSdk
12395
13095
  };
12396
13096
 
12397
- //# debugId=57D8825DED309B1264756E2164756E21
13097
+ //# debugId=DD6DEC33C9C6693F64756E2164756E21