@yoqa/cli 0.3.8 → 0.3.10

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.
Files changed (2) hide show
  1. package/dist/main.js +307 -1
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -6184,6 +6184,75 @@ var ensureRuntimeResponseSchema = exports_external.object({
6184
6184
  installed: exports_external.array(setupPlatformResponseSchema),
6185
6185
  message: exports_external.string().min(1)
6186
6186
  });
6187
+ var serverKindSchema = exports_external.union([
6188
+ exports_external.literal("appium"),
6189
+ exports_external.literal("runner"),
6190
+ exports_external.literal("device-session")
6191
+ ]);
6192
+ var serverOwnershipSchema = exports_external.union([
6193
+ exports_external.literal("managed"),
6194
+ exports_external.literal("foreign"),
6195
+ exports_external.literal("self")
6196
+ ]);
6197
+ var serverActionSchema = exports_external.union([exports_external.literal("stop"), exports_external.literal("restart")]);
6198
+ var serverEntrySchema = exports_external.object({
6199
+ id: exports_external.string().min(1),
6200
+ kind: serverKindSchema,
6201
+ ownership: serverOwnershipSchema,
6202
+ label: exports_external.string().min(1),
6203
+ status: exports_external.string().min(1),
6204
+ pid: exports_external.number().int().positive().optional(),
6205
+ port: exports_external.number().int().positive().optional(),
6206
+ deviceId: exports_external.string().min(1).optional(),
6207
+ platform: devicePlatformSchema.optional(),
6208
+ startedAt: exports_external.number().nonnegative().optional(),
6209
+ actions: exports_external.array(serverActionSchema)
6210
+ });
6211
+ var listServersResponseSchema = exports_external.object({
6212
+ servers: exports_external.array(serverEntrySchema)
6213
+ });
6214
+ var serverMutationResponseSchema = exports_external.object({
6215
+ ok: exports_external.literal(true),
6216
+ message: exports_external.string().min(1),
6217
+ servers: exports_external.array(serverEntrySchema)
6218
+ });
6219
+ var doctorCheckStatusSchema = exports_external.union([
6220
+ exports_external.literal("pass"),
6221
+ exports_external.literal("fail"),
6222
+ exports_external.literal("warn")
6223
+ ]);
6224
+ var doctorRepairIdSchema = exports_external.union([
6225
+ exports_external.literal("ensure-runtime"),
6226
+ exports_external.literal("stop-foreign-appium"),
6227
+ exports_external.literal("disconnect-session")
6228
+ ]);
6229
+ var doctorCheckSchema = exports_external.object({
6230
+ id: exports_external.string().min(1),
6231
+ label: exports_external.string().min(1),
6232
+ status: doctorCheckStatusSchema,
6233
+ detail: exports_external.string().optional(),
6234
+ fixHint: exports_external.string().optional()
6235
+ });
6236
+ var doctorStepSchema = exports_external.object({
6237
+ severity: exports_external.union([exports_external.literal("error"), exports_external.literal("warn"), exports_external.literal("info")]),
6238
+ title: exports_external.string().min(1),
6239
+ detail: exports_external.string().min(1),
6240
+ repair: doctorRepairIdSchema.optional()
6241
+ });
6242
+ var doctorReportSchema = exports_external.object({
6243
+ ok: exports_external.boolean(),
6244
+ checks: exports_external.array(doctorCheckSchema),
6245
+ servers: exports_external.array(serverEntrySchema),
6246
+ steps: exports_external.array(doctorStepSchema)
6247
+ });
6248
+ var doctorRepairRequestSchema = exports_external.object({
6249
+ repairs: exports_external.array(doctorRepairIdSchema).min(1)
6250
+ });
6251
+ var doctorRepairResponseSchema = exports_external.object({
6252
+ ok: exports_external.literal(true),
6253
+ message: exports_external.string().min(1),
6254
+ report: doctorReportSchema
6255
+ });
6187
6256
  var capabilitySchema = exports_external.object({
6188
6257
  id: exports_external.string().min(1),
6189
6258
  key: exports_external.string(),
@@ -6384,6 +6453,22 @@ var aiProviderSchema = exports_external.object({
6384
6453
  var listProvidersResponseSchema = exports_external.object({
6385
6454
  providers: exports_external.array(aiProviderSchema)
6386
6455
  });
6456
+ var providerDriverCapabilitiesSchema = exports_external.object({
6457
+ vision: exports_external.boolean()
6458
+ });
6459
+ var providerDriverCatalogEntrySchema = exports_external.object({
6460
+ kind: providerKindSchema,
6461
+ label: exports_external.string().min(1),
6462
+ description: exports_external.string().nullable(),
6463
+ authModes: exports_external.array(providerAuthModeSchema).min(1),
6464
+ defaultBinary: exports_external.string().nullable(),
6465
+ envHints: exports_external.array(exports_external.string()),
6466
+ loginInstructions: exports_external.string().nullable(),
6467
+ capabilities: providerDriverCapabilitiesSchema
6468
+ });
6469
+ var listProviderCatalogResponseSchema = exports_external.object({
6470
+ drivers: exports_external.array(providerDriverCatalogEntrySchema)
6471
+ });
6387
6472
  var createProviderRequestSchema = exports_external.object({
6388
6473
  kind: providerKindSchema,
6389
6474
  authMode: providerAuthModeSchema.optional(),
@@ -7396,6 +7481,73 @@ class RunnerClient {
7396
7481
  }
7397
7482
  return ensureRuntimeResponseSchema.parse(json);
7398
7483
  }
7484
+ async listServers(options = {}) {
7485
+ const response = await this.fetchImpl(`${this.baseUrl}/servers`, {
7486
+ signal: options.signal
7487
+ });
7488
+ const json = await response.json().catch(() => null);
7489
+ if (!response.ok) {
7490
+ throw new Error(errorMessageFromBody(json, `List servers failed: HTTP ${response.status}`));
7491
+ }
7492
+ return listServersResponseSchema.parse(json);
7493
+ }
7494
+ async stopAllServers(options = {}) {
7495
+ const response = await this.fetchImpl(`${this.baseUrl}/servers/stop-all`, {
7496
+ method: "POST",
7497
+ signal: options.signal
7498
+ });
7499
+ const json = await response.json().catch(() => null);
7500
+ if (!response.ok) {
7501
+ throw new Error(errorMessageFromBody(json, `Stop all servers failed: HTTP ${response.status}`));
7502
+ }
7503
+ return serverMutationResponseSchema.parse(json);
7504
+ }
7505
+ async stopServer(id, options = {}) {
7506
+ const response = await this.fetchImpl(`${this.baseUrl}/servers/${encodeURIComponent(id)}/stop`, {
7507
+ method: "POST",
7508
+ signal: options.signal
7509
+ });
7510
+ const json = await response.json().catch(() => null);
7511
+ if (!response.ok) {
7512
+ throw new Error(errorMessageFromBody(json, `Stop server failed: HTTP ${response.status}`));
7513
+ }
7514
+ return serverMutationResponseSchema.parse(json);
7515
+ }
7516
+ async restartServer(id, options = {}) {
7517
+ const response = await this.fetchImpl(`${this.baseUrl}/servers/${encodeURIComponent(id)}/restart`, {
7518
+ method: "POST",
7519
+ signal: options.signal
7520
+ });
7521
+ const json = await response.json().catch(() => null);
7522
+ if (!response.ok) {
7523
+ throw new Error(errorMessageFromBody(json, `Restart server failed: HTTP ${response.status}`));
7524
+ }
7525
+ return serverMutationResponseSchema.parse(json);
7526
+ }
7527
+ async getDoctorReport(options = {}) {
7528
+ const response = await this.fetchImpl(`${this.baseUrl}/doctor`, {
7529
+ signal: options.signal
7530
+ });
7531
+ const json = await response.json().catch(() => null);
7532
+ if (!response.ok) {
7533
+ throw new Error(errorMessageFromBody(json, `Doctor failed: HTTP ${response.status}`));
7534
+ }
7535
+ return doctorReportSchema.parse(json);
7536
+ }
7537
+ async repairDoctor(request, options = {}) {
7538
+ const body = doctorRepairRequestSchema.parse(request);
7539
+ const response = await this.fetchImpl(`${this.baseUrl}/doctor/repair`, {
7540
+ method: "POST",
7541
+ headers: { "Content-Type": "application/json" },
7542
+ body: JSON.stringify(body),
7543
+ signal: options.signal
7544
+ });
7545
+ const json = await response.json().catch(() => null);
7546
+ if (!response.ok) {
7547
+ throw new Error(errorMessageFromBody(json, `Doctor repair failed: HTTP ${response.status}`));
7548
+ }
7549
+ return doctorRepairResponseSchema.parse(json);
7550
+ }
7399
7551
  async listApps() {
7400
7552
  const json = await this.requestJson("/apps", undefined, "List apps failed");
7401
7553
  return listAppsResponseSchema.parse(json).apps;
@@ -7499,6 +7651,10 @@ class RunnerClient {
7499
7651
  const json = await this.requestJson("/providers", undefined, "List providers failed");
7500
7652
  return listProvidersResponseSchema.parse(json).providers;
7501
7653
  }
7654
+ async listProviderCatalog() {
7655
+ const json = await this.requestJson("/providers/catalog", undefined, "List provider catalog failed");
7656
+ return listProviderCatalogResponseSchema.parse(json).drivers;
7657
+ }
7502
7658
  async getProvider(providerId) {
7503
7659
  const json = await this.requestJson(`/providers/${encodeURIComponent(providerId)}`, undefined, "Get provider failed");
7504
7660
  return aiProviderSchema.parse(json);
@@ -7694,7 +7850,7 @@ var {
7694
7850
  // package.json
7695
7851
  var package_default = {
7696
7852
  name: "@yoqa/cli",
7697
- version: "0.3.8",
7853
+ version: "0.3.10",
7698
7854
  description: "Yoqa CLI — drive local mobile QA from the terminal or CI (talks to the Yoqa runner over HTTP)",
7699
7855
  license: "UNLICENSED",
7700
7856
  type: "module",
@@ -8036,6 +8192,156 @@ runtime.command("ensure").description("Install Appium + both platform drivers if
8036
8192
  fail("runtime ensure", error);
8037
8193
  }
8038
8194
  });
8195
+ var servers = program2.command("servers").description("List or control local Appium, runner, and device sessions").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").action(async (options) => {
8196
+ try {
8197
+ const body = await client(options.baseUrl).listServers();
8198
+ if (options.json) {
8199
+ console.log(JSON.stringify(body, null, 2));
8200
+ return;
8201
+ }
8202
+ if (body.servers.length === 0) {
8203
+ console.log("No servers running");
8204
+ return;
8205
+ }
8206
+ for (const entry of body.servers) {
8207
+ const meta = [
8208
+ entry.kind,
8209
+ entry.ownership,
8210
+ entry.status,
8211
+ entry.port ? `:${entry.port}` : null,
8212
+ entry.pid ? `pid ${entry.pid}` : null,
8213
+ entry.actions.length > 0 ? `actions=${entry.actions.join(",")}` : "actions=none"
8214
+ ].filter(Boolean).join(" · ");
8215
+ console.log(`${entry.id} ${entry.label} ${meta}`);
8216
+ }
8217
+ } catch (error) {
8218
+ fail("servers", error);
8219
+ }
8220
+ });
8221
+ servers.command("list").description("List running servers").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").action(async (options) => {
8222
+ try {
8223
+ const body = await client(options.baseUrl).listServers();
8224
+ if (options.json) {
8225
+ console.log(JSON.stringify(body, null, 2));
8226
+ return;
8227
+ }
8228
+ if (body.servers.length === 0) {
8229
+ console.log("No servers running");
8230
+ return;
8231
+ }
8232
+ for (const entry of body.servers) {
8233
+ const meta = [
8234
+ entry.kind,
8235
+ entry.ownership,
8236
+ entry.status,
8237
+ entry.port ? `:${entry.port}` : null,
8238
+ entry.pid ? `pid ${entry.pid}` : null,
8239
+ entry.actions.length > 0 ? `actions=${entry.actions.join(",")}` : "actions=none"
8240
+ ].filter(Boolean).join(" · ");
8241
+ console.log(`${entry.id} ${entry.label} ${meta}`);
8242
+ }
8243
+ } catch (error) {
8244
+ fail("servers list", error);
8245
+ }
8246
+ });
8247
+ servers.command("stop-all").description("Stop Appium processes and disconnect the device session (does not stop the runner)").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").action(async (options) => {
8248
+ try {
8249
+ const body = await client(options.baseUrl).stopAllServers();
8250
+ if (options.json) {
8251
+ console.log(JSON.stringify(body, null, 2));
8252
+ return;
8253
+ }
8254
+ console.log(body.message);
8255
+ } catch (error) {
8256
+ fail("servers stop-all", error);
8257
+ }
8258
+ });
8259
+ servers.command("stop").description("Stop one server by id").argument("<id>", "Server id from yoqa servers list").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").action(async (id, options) => {
8260
+ try {
8261
+ if (id === "runner-self") {
8262
+ throw new Error("Stopping the runner over HTTP is not supported. Quit the desktop app or kill the process on the runner port.");
8263
+ }
8264
+ const body = await client(options.baseUrl).stopServer(id);
8265
+ if (options.json) {
8266
+ console.log(JSON.stringify(body, null, 2));
8267
+ return;
8268
+ }
8269
+ console.log(body.message);
8270
+ } catch (error) {
8271
+ fail("servers stop", error);
8272
+ }
8273
+ });
8274
+ servers.command("restart").description("Restart one server by id").argument("<id>", "Server id from yoqa servers list").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").action(async (id, options) => {
8275
+ try {
8276
+ if (id === "runner-self") {
8277
+ throw new Error("Restarting the runner over HTTP is not supported while it is serving requests. Prefer the desktop Servers panel.");
8278
+ }
8279
+ const body = await client(options.baseUrl).restartServer(id);
8280
+ if (options.json) {
8281
+ console.log(JSON.stringify(body, null, 2));
8282
+ return;
8283
+ }
8284
+ console.log(body.message);
8285
+ } catch (error) {
8286
+ fail("servers restart", error);
8287
+ }
8288
+ });
8289
+ program2.command("doctor").description("Diagnose local tooling, drivers, and leftover Appium processes").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("--fix", "Apply safe repairs (ensure runtime, stop foreign Appium, disconnect session)").action(async (options) => {
8290
+ try {
8291
+ const c = client(options.baseUrl);
8292
+ if (options.fix) {
8293
+ const report2 = await c.getDoctorReport();
8294
+ const repairs = [
8295
+ ...new Set(report2.steps.map((step) => step.repair).filter((id) => Boolean(id)))
8296
+ ];
8297
+ if (repairs.length === 0) {
8298
+ if (options.json) {
8299
+ console.log(JSON.stringify({ ok: true, message: "No safe repairs", report: report2 }, null, 2));
8300
+ } else {
8301
+ console.log("No safe repairs to apply");
8302
+ }
8303
+ if (!report2.ok)
8304
+ process.exitCode = 1;
8305
+ return;
8306
+ }
8307
+ const result = await c.repairDoctor({ repairs });
8308
+ if (options.json) {
8309
+ console.log(JSON.stringify(result, null, 2));
8310
+ } else {
8311
+ console.log(result.message);
8312
+ console.log(result.report.ok ? "doctor: ok" : "doctor: issues remain");
8313
+ for (const check of result.report.checks) {
8314
+ console.log(` [${check.status}] ${check.label}${check.detail ? ` — ${check.detail}` : ""}`);
8315
+ }
8316
+ for (const step of result.report.steps) {
8317
+ console.log(` → [${step.severity}] ${step.title}: ${step.detail}`);
8318
+ }
8319
+ }
8320
+ if (!result.report.ok)
8321
+ process.exitCode = 1;
8322
+ return;
8323
+ }
8324
+ const report = await c.getDoctorReport();
8325
+ if (options.json) {
8326
+ console.log(JSON.stringify(report, null, 2));
8327
+ } else {
8328
+ console.log(report.ok ? "doctor: ok" : "doctor: issues found");
8329
+ for (const check of report.checks) {
8330
+ console.log(` [${check.status}] ${check.label}${check.detail ? ` — ${check.detail}` : ""}`);
8331
+ }
8332
+ if (report.steps.length > 0) {
8333
+ console.log("steps:");
8334
+ for (const step of report.steps) {
8335
+ console.log(` → [${step.severity}] ${step.title}: ${step.detail}`);
8336
+ }
8337
+ }
8338
+ }
8339
+ if (!report.ok)
8340
+ process.exitCode = 1;
8341
+ } catch (error) {
8342
+ fail("doctor", error);
8343
+ }
8344
+ });
8039
8345
  var assertCmd = program2.command("assert").description("Assert text visibility on the active device");
8040
8346
  for (const kind of ["visible", "not-visible"]) {
8041
8347
  assertCmd.command(kind).description(kind === "visible" ? "Fail unless matching text appears in the cleaned screen tree" : "Fail if matching text is still present in the cleaned screen tree").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").requiredOption("-t, --text <text>", "Text / label substring to match").option("--timeout <seconds>", "Seconds to wait before failing", (v) => Number(v), 5).action(async (options) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoqa/cli",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "Yoqa CLI — drive local mobile QA from the terminal or CI (talks to the Yoqa runner over HTTP)",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",