vibeview 0.23.0 → 0.24.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.
@@ -197,7 +197,7 @@ var ApiClient = class {
197
197
  async listApps() {
198
198
  return this.requestRaw("GET", "/apps/");
199
199
  }
200
- async uploadApp(filePath, name) {
200
+ async uploadApp(filePath, name, note) {
201
201
  const fileBuffer = readFileSync2(filePath);
202
202
  const blob = new Blob([fileBuffer]);
203
203
  const formData = new FormData();
@@ -205,8 +205,15 @@ var ApiClient = class {
205
205
  if (name) {
206
206
  formData.append("name", name);
207
207
  }
208
+ if (note) {
209
+ formData.append("note", note);
210
+ }
208
211
  return this.requestFormData("POST", "/apps/upload", formData);
209
212
  }
213
+ /** PATCH a build's note (null clears it). */
214
+ async updateBuildNote(appPublicId, buildId, note) {
215
+ return this.requestRaw("PATCH", `/apps/${appPublicId}/builds/${buildId}`, { note });
216
+ }
210
217
  async uploadBuild(appPublicId, filePath, version) {
211
218
  const fileBuffer = readFileSync2(filePath);
212
219
  const blob = new Blob([fileBuffer]);
@@ -613,9 +620,15 @@ function sanitizeDownloadFilename(raw) {
613
620
 
614
621
  // src/lib/spinner.ts
615
622
  import ora from "ora";
616
- function createSpinner(text) {
623
+ function createSpinner(text, opts = {}) {
617
624
  const isCI = process.env.CI === "true" || process.env.CI === "1" || !process.stdout.isTTY;
618
- return ora({ text, isEnabled: !isCI, stream: process.stderr, discardStdin: false });
625
+ return ora({
626
+ text,
627
+ isEnabled: !isCI,
628
+ stream: process.stderr,
629
+ discardStdin: false,
630
+ isSilent: opts.silent === true
631
+ });
619
632
  }
620
633
 
621
634
  // src/lib/types.ts
@@ -636,16 +649,26 @@ async function uploadAppBuild(filePath, options) {
636
649
  }
637
650
  const { token, baseUrl } = resolveAuth(options);
638
651
  const client = new ApiClient(baseUrl, token);
639
- const result = await client.uploadApp(filePath, options.name);
652
+ const note = options.note?.trim() || void 0;
653
+ const result = await client.uploadApp(filePath, options.name, note);
654
+ if (note && result.deduplicated && result.build_id != null) {
655
+ await client.updateBuildNote(result.public_id, result.build_id, note);
656
+ }
640
657
  return { result, baseUrl };
641
658
  }
659
+ function describeBuild(result) {
660
+ if (result.build_id == null) return null;
661
+ const version = result.version ? `v${result.version}${result.build_number ? ` (${result.build_number})` : ""}` : "";
662
+ const dedup = result.deduplicated ? chalk.dim(" identical build already uploaded") : "";
663
+ return `Build ${chalk.cyan(String(result.build_id))}${version ? ` ${version}` : ""}${dedup}`;
664
+ }
642
665
  async function uploadApp(filePath, options) {
643
666
  if (!existsSync(filePath)) {
644
667
  console.error(chalk.red(`Error: File not found: ${filePath}`));
645
668
  process.exit(EXIT_INFRA_ERROR);
646
669
  }
647
670
  const { baseUrl } = resolveAuth(options);
648
- const spinner = createSpinner("Uploading app...");
671
+ const spinner = createSpinner("Uploading app...", { silent: options.output === "json" });
649
672
  spinner.start();
650
673
  try {
651
674
  const { result } = await uploadAppBuild(filePath, options);
@@ -662,6 +685,11 @@ async function uploadApp(filePath, options) {
662
685
  package_name: result.package_name,
663
686
  platform: result.platform,
664
687
  is_new: isNew,
688
+ build_id: result.build_id ?? null,
689
+ version: result.version ?? null,
690
+ build_number: result.build_number ?? null,
691
+ deduplicated: result.deduplicated === true,
692
+ note: options.note?.trim() || null,
665
693
  url: `${baseUrl}/apps/${result.public_id}`,
666
694
  warnings: result.warnings ?? []
667
695
  },
@@ -675,6 +703,8 @@ async function uploadApp(filePath, options) {
675
703
  ${chalk.cyan(result.public_id)} ${result.name} ${chalk.dim(`[${result.platform}]`)}`
676
704
  );
677
705
  console.log(` ${chalk.dim(result.package_name)}`);
706
+ const build = describeBuild(result);
707
+ if (build) console.log(` ${build}`);
678
708
  console.log(chalk.dim(` ${baseUrl}/apps/${result.public_id}
679
709
  `));
680
710
  }
@@ -683,7 +713,9 @@ async function uploadApp(filePath, options) {
683
713
  \u26A0 ${w}`));
684
714
  }
685
715
  } catch (err) {
686
- spinner.fail(err instanceof Error ? err.message : "Upload failed");
716
+ const message = err instanceof Error ? err.message : "Upload failed";
717
+ spinner.fail(message);
718
+ if (options.output === "json") console.error(chalk.red(`Error: ${message}`));
687
719
  process.exit(EXIT_INFRA_ERROR);
688
720
  }
689
721
  }
@@ -700,5 +732,6 @@ export {
700
732
  EXIT_INFRA_ERROR,
701
733
  UsageError,
702
734
  uploadAppBuild,
735
+ describeBuild,
703
736
  uploadApp
704
737
  };
@@ -6,7 +6,7 @@ import {
6
6
  loadConfig,
7
7
  resolveAuth,
8
8
  uploadAppBuild
9
- } from "./chunk-PSFXVUHG.js";
9
+ } from "./chunk-2WNJYNDW.js";
10
10
 
11
11
  // src/lib/dev-state.ts
12
12
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
@@ -1646,6 +1646,26 @@ function decodeFrame(data) {
1646
1646
  return { type, streamId: data.readUInt32BE(1), payload: data.subarray(HEADER_LEN) };
1647
1647
  }
1648
1648
 
1649
+ // src/lib/redact.ts
1650
+ var SECRET_PARAMS = /* @__PURE__ */ new Set([
1651
+ "token",
1652
+ "access_token",
1653
+ "refresh_token",
1654
+ "api_key",
1655
+ "apikey",
1656
+ "key",
1657
+ "secret",
1658
+ "signature",
1659
+ "sig",
1660
+ "jwt"
1661
+ ]);
1662
+ function redactSecrets(text) {
1663
+ return text.replace(
1664
+ /\b([A-Za-z_]+)=([^&\s"'#]+)/g,
1665
+ (match, name) => SECRET_PARAMS.has(name.toLowerCase()) ? `${name}=[redacted]` : match
1666
+ );
1667
+ }
1668
+
1649
1669
  // src/lib/tunnel-client.ts
1650
1670
  var FATAL_CLOSE_CODES = /* @__PURE__ */ new Set([4008, 4009]);
1651
1671
  var MAX_BACKOFF_MS = 8e3;
@@ -1665,7 +1685,7 @@ var TunnelClient = class {
1665
1685
  url;
1666
1686
  connCount = 0;
1667
1687
  debug(msg) {
1668
- this.opts.onDebug?.(msg);
1688
+ this.opts.onDebug?.(redactSecrets(msg));
1669
1689
  }
1670
1690
  start() {
1671
1691
  this.connect();
@@ -3412,9 +3432,9 @@ function deviceStateLine(r) {
3412
3432
  const parts = [];
3413
3433
  if (r.action === "rotated") {
3414
3434
  if (typeof r.device_orientation === "string") {
3415
- let o = `orientation: ${r.device_orientation}`;
3435
+ let o = `device held ${r.device_orientation}`;
3416
3436
  if (typeof r.screen_orientation === "string" && r.screen_orientation !== r.device_orientation) {
3417
- o += ` (screen shows ${r.screen_orientation})`;
3437
+ o += ` (picture on the lit screen: ${r.screen_orientation})`;
3418
3438
  }
3419
3439
  parts.push(o);
3420
3440
  }
package/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  readDevState,
20
20
  readMetroState,
21
21
  runAgentVerb
22
- } from "./chunk-E2LZSLRF.js";
22
+ } from "./chunk-MVNMGB6I.js";
23
23
  import {
24
24
  ApiClient,
25
25
  EXIT_INFRA_ERROR,
@@ -32,7 +32,7 @@ import {
32
32
  resolveAuth,
33
33
  saveConfig,
34
34
  uploadApp
35
- } from "./chunk-PSFXVUHG.js";
35
+ } from "./chunk-2WNJYNDW.js";
36
36
 
37
37
  // src/cli.ts
38
38
  import { createRequire } from "module";
@@ -3229,7 +3229,7 @@ program.command("list-devices").description("List devices registered with the pl
3229
3229
  ).action((opts) => listDevicesCommand(opts));
3230
3230
  program.command("upload-app <file>").description(
3231
3231
  "Upload an app build (zipped simulator .app for iOS/tvOS, .apk for Android/Android TV, or a Roku channel .zip)"
3232
- ).option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--name <name>", "App name (auto-detected if not provided)").action((file, opts) => uploadApp(file, opts));
3232
+ ).option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--name <name>", "App name (auto-detected if not provided)").option("--note <text>", "Note stored on the uploaded build (shown in the dashboard)").action((file, opts) => uploadApp(file, opts));
3233
3233
  program.command("create-test [name]").description("Create a standalone AI test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--app <app>", "App ID (public_id) or package name (requires --platform)").option("--platform <platform>", "Platform (ios, android) when using package name for --app").option("--steps <json>", "Test steps as inline JSON array").option("--steps-file <path>", "Path to JSON file containing test steps").option("--context <text>", "Additional context for AI test execution").option("--test-name <name>", "Test case name (alternative to positional arg)").action((name, opts) => createTest({ ...opts, testName: name || opts.testName || "CLI Test" }));
3234
3234
  program.command("login").description("Log in to VibeView (interactive)").option("--base-url <url>", "VibeView server URL (defaults to https://vibeview.io)").action((opts) => login(opts));
3235
3235
  program.command("logout").description("Revoke the saved login token and remove it from ~/.vibeview/config.json").action(() => logout());
@@ -3393,7 +3393,7 @@ program.command("dev-reload").description(
3393
3393
  program.command("mcp").description(
3394
3394
  "Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
3395
3395
  ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
3396
- const { runMcpServer } = await import("./server-XMSBLIWY.js");
3396
+ const { runMcpServer } = await import("./server-KIJCEN6T.js");
3397
3397
  await runMcpServer(opts);
3398
3398
  });
3399
3399
  program.command("agent-setup").description(
@@ -16,11 +16,11 @@ import {
16
16
  readDevState,
17
17
  renderEnvelope,
18
18
  startDevSession
19
- } from "./chunk-E2LZSLRF.js";
19
+ } from "./chunk-MVNMGB6I.js";
20
20
  import {
21
21
  ApiClient,
22
22
  resolveAuth
23
- } from "./chunk-PSFXVUHG.js";
23
+ } from "./chunk-2WNJYNDW.js";
24
24
 
25
25
  // src/mcp/server.ts
26
26
  import { writeFileSync } from "fs";
@@ -301,7 +301,7 @@ async function handleUploadApp(args, deps) {
301
301
  }
302
302
  const name = typeof args.name === "string" && args.name ? args.name : void 0;
303
303
  const upload = deps.uploadApp ?? (async (f, n) => {
304
- const { uploadAppBuild } = await import("./upload-app-ZAKAOJKE.js");
304
+ const { uploadAppBuild } = await import("./upload-app-BXXKH65S.js");
305
305
  const { result: result2 } = await uploadAppBuild(f, { ...deps.auth, name: n });
306
306
  return result2;
307
307
  });
@@ -319,11 +319,17 @@ async function handleUploadApp(args, deps) {
319
319
  package_name: result.package_name,
320
320
  platform: result.platform,
321
321
  is_new: isNew,
322
+ build_id: result.build_id ?? null,
323
+ version: result.version ?? null,
324
+ build_number: result.build_number ?? null,
322
325
  warnings
323
326
  };
324
327
  const lines = [
325
328
  `${isNew ? "New app created" : "Build added to existing app"}: ${result.name}`,
326
329
  `app_id: ${result.public_id} [${result.platform}] ${result.package_name}`,
330
+ ...result.build_id != null ? [
331
+ `build_id: ${result.build_id}${result.version ? ` version ${result.version}${result.build_number ? ` (${result.build_number})` : ""}` : ""}`
332
+ ] : [],
327
333
  ...warnings.map((w) => `warning: ${w}`)
328
334
  ];
329
335
  return textResult(lines.join("\n"), payload);
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ describeBuild,
3
4
  uploadApp,
4
5
  uploadAppBuild
5
- } from "./chunk-PSFXVUHG.js";
6
+ } from "./chunk-2WNJYNDW.js";
6
7
  export {
8
+ describeBuild,
7
9
  uploadApp,
8
10
  uploadAppBuild
9
11
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeview",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "VibeView CLI -- run mobile tests from CI/CD pipelines",
5
5
  "mcpName": "io.github.vibeview/vibeview",
6
6
  "type": "module",