@wibeco/bridge 0.2.10 → 0.2.12

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.
@@ -19,15 +19,71 @@ import {
19
19
  startPresenceSession,
20
20
  stopPresenceSession,
21
21
  updatePresenceSession
22
- } from "./chunk-FIT6PBBW.js";
22
+ } from "./chunk-JXH47J5I.js";
23
23
 
24
24
  // src/cli/commands.ts
25
25
  import { access, cp, mkdir, readFile, writeFile } from "fs/promises";
26
26
  import { homedir, hostname } from "os";
27
- import { join, resolve } from "path";
27
+ import { basename, join, resolve } from "path";
28
28
  import { fileURLToPath } from "url";
29
29
  import { execFile } from "child_process";
30
30
  var ADAPTERS = ["cursor", "claude-code", "codex"];
31
+ var MAX_SCREENSHOT_BYTES = 5 * 1024 * 1024;
32
+ function screenshotMimeType(bytes) {
33
+ if ([137, 80, 78, 71, 13, 10, 26, 10].every(
34
+ (byte, index) => bytes[index] === byte
35
+ )) {
36
+ return "image/png";
37
+ }
38
+ if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
39
+ return "image/jpeg";
40
+ }
41
+ if (new TextDecoder().decode(bytes.slice(0, 4)) === "RIFF" && new TextDecoder().decode(bytes.slice(8, 12)) === "WEBP") {
42
+ return "image/webp";
43
+ }
44
+ return void 0;
45
+ }
46
+ async function uploadProgressScreenshot(screenshotPath, altText, credential, cwd) {
47
+ try {
48
+ const resolvedPath = resolve(cwd, screenshotPath);
49
+ const bytes = await readFile(resolvedPath);
50
+ if (!bytes.length || bytes.length > MAX_SCREENSHOT_BYTES) {
51
+ return {
52
+ warning: "Screenshot skipped: image must be between 1 byte and 5 MB."
53
+ };
54
+ }
55
+ const mimeType = screenshotMimeType(bytes);
56
+ if (!mimeType) {
57
+ return {
58
+ warning: "Screenshot skipped: only PNG, JPEG, and WebP are supported."
59
+ };
60
+ }
61
+ const form = new FormData();
62
+ form.set(
63
+ "screenshot",
64
+ new Blob([new Uint8Array(bytes)], { type: mimeType }),
65
+ basename(resolvedPath)
66
+ );
67
+ form.set("alt_text", altText);
68
+ const response = await fetch(`${credential.appUrl}/api/artifacts`, {
69
+ method: "POST",
70
+ headers: { authorization: `Bearer ${credential.accessToken}` },
71
+ body: form,
72
+ signal: AbortSignal.timeout(1e4)
73
+ });
74
+ const body = await response.json().catch(() => ({}));
75
+ if (!response.ok || typeof body.artifact_id !== "string") {
76
+ return {
77
+ warning: `Screenshot skipped: ${typeof body.error === "string" ? body.error : `upload failed with status ${response.status}`}.`
78
+ };
79
+ }
80
+ return { artifactId: body.artifact_id };
81
+ } catch (error) {
82
+ return {
83
+ warning: `Screenshot skipped: ${error instanceof Error ? error.message : "upload failed"}.`
84
+ };
85
+ }
86
+ }
31
87
  async function setupCommand(requestedAdapter, options = {}) {
32
88
  const cwd = options.cwd ?? process.cwd();
33
89
  const expectedRepository = options.expectedRepository ? normalizeGitHubRepository(options.expectedRepository) : void 0;
@@ -352,6 +408,10 @@ async function shareProgressCommand(options, cwd = process.cwd()) {
352
408
  if (title && title.length > 120) {
353
409
  throw new Error("Progress title must be 120 characters or fewer.");
354
410
  }
411
+ const screenshotAlt = options.screenshotAlt?.trim() || (title ? `${title} screenshot` : "Agent update screenshot");
412
+ if (screenshotAlt.length > 240) {
413
+ throw new Error("Screenshot alt text must be 240 characters or fewer.");
414
+ }
355
415
  const phases = [
356
416
  "planning",
357
417
  "implementing",
@@ -371,6 +431,12 @@ async function shareProgressCommand(options, cwd = process.cwd()) {
371
431
  projectConfig.adapter,
372
432
  cwd
373
433
  );
434
+ const screenshot = options.screenshot?.trim() ? await uploadProgressScreenshot(
435
+ options.screenshot.trim(),
436
+ screenshotAlt,
437
+ credential,
438
+ cwd
439
+ ) : {};
374
440
  const event = createHookEvent({
375
441
  source: projectConfig.adapter,
376
442
  kind: "progress.shared",
@@ -380,6 +446,7 @@ async function shareProgressCommand(options, cwd = process.cwd()) {
380
446
  ...title ? { title } : {},
381
447
  ...options.phase ? { phase: options.phase } : {},
382
448
  ...options.confidence !== void 0 ? { confidence: options.confidence } : {},
449
+ ...screenshot.artifactId ? { artifact_id: screenshot.artifactId } : {},
383
450
  ...workingTree ? {
384
451
  paths: workingTree.paths,
385
452
  lines_added: workingTree.linesAdded,
@@ -399,7 +466,7 @@ async function shareProgressCommand(options, cwd = process.cwd()) {
399
466
  }).capture(event);
400
467
  return {
401
468
  exitCode: result.error ? 1 : 0,
402
- message: result.error ? `Progress was queued; delivery failed: ${result.error}` : `Shared progress with Wibe; ${result.remaining} event(s) queued.`
469
+ message: result.error ? `Progress was queued; delivery failed: ${result.error}` : `Shared progress with Wibe; ${result.remaining} event(s) queued.${screenshot.warning ? ` ${screenshot.warning}` : ""}`
403
470
  };
404
471
  }
405
472
  async function doctorCommand(cwd = process.cwd(), requestedRepository) {
@@ -389,6 +389,7 @@ function toEnvelope(event, options) {
389
389
  paths,
390
390
  phase: event.metadata.phase,
391
391
  confidence: event.metadata.confidence,
392
+ artifact_id: typeof event.metadata.artifact_id === "string" ? event.metadata.artifact_id : void 0,
392
393
  lines_added: typeof event.metadata.lines_added === "number" ? event.metadata.lines_added : void 0,
393
394
  lines_deleted: typeof event.metadata.lines_deleted === "number" ? event.metadata.lines_deleted : void 0
394
395
  } : event.kind === "file.changed" ? {
package/dist/cli.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  setupCommand,
7
7
  shareProgressCommand,
8
8
  statusCommand
9
- } from "./chunk-ODX3HMDN.js";
9
+ } from "./chunk-7AU52DBJ.js";
10
10
  import {
11
11
  runPresenceHeartbeat
12
- } from "./chunk-FIT6PBBW.js";
12
+ } from "./chunk-JXH47J5I.js";
13
13
 
14
14
  // src/cli.ts
15
15
  var HELP = `wibe-bridge <command>
@@ -21,7 +21,7 @@ Commands:
21
21
  Use --reauthorize only to replace a rejected or revoked device token.
22
22
  status
23
23
  emit --adapter <name> --event <hook-name> (JSON payload on stdin)
24
- share-progress --summary <20-55 words> [--title <title>] [--phase <phase>] [--confidence <0-1>]
24
+ share-progress --summary <20-55 words> [--title <title>] [--phase <phase>] [--confidence <0-1>] [--screenshot <path>] [--screenshot-alt <text>]
25
25
  doctor [--repository <owner/repo>]`;
26
26
  async function main() {
27
27
  const [command, ...args] = process.argv.slice(2);
@@ -54,7 +54,9 @@ async function main() {
54
54
  summary: option(args, "--summary"),
55
55
  title: option(args, "--title"),
56
56
  phase: option(args, "--phase"),
57
- confidence: confidence === void 0 ? void 0 : Number(confidence)
57
+ confidence: confidence === void 0 ? void 0 : Number(confidence),
58
+ screenshot: option(args, "--screenshot"),
59
+ screenshotAlt: option(args, "--screenshot-alt")
58
60
  });
59
61
  } else if (command === "_presence-heartbeat") {
60
62
  parseAdapter(option(args, "--adapter"));
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  emitCommand
4
- } from "./chunk-ODX3HMDN.js";
5
- import "./chunk-FIT6PBBW.js";
4
+ } from "./chunk-7AU52DBJ.js";
5
+ import "./chunk-JXH47J5I.js";
6
6
 
7
7
  // src/codex-hook.ts
8
8
  async function main() {
package/dist/index.js CHANGED
@@ -33,7 +33,7 @@ import {
33
33
  startPresenceSession,
34
34
  stopPresenceSession,
35
35
  updatePresenceSession
36
- } from "./chunk-FIT6PBBW.js";
36
+ } from "./chunk-JXH47J5I.js";
37
37
  export {
38
38
  JsonFileOfflineQueue,
39
39
  MemoryOfflineQueue,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wibeco/bridge",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Privacy-first live activity bridge for Cursor, Claude Code, and Codex.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -7,7 +7,10 @@ alwaysApply: true
7
7
 
8
8
  - This repository is connected to Wibe project `${WIBE_PROJECT_ID}`.
9
9
  - For most tasks, publish one update only after the requested outcome is complete. Do not publish plans, routine progress, or separate updates for editing, testing, committing, or pushing.
10
- - Use `wibe_share_progress` with phase `shipped` at completion. Publish earlier only with phase `blocked` when a material blocker leaves the task unfinished.
10
+ - For text-only outcomes, use `wibe_share_progress` with phase `shipped` at completion. Publish earlier only with phase `blocked` when a material blocker leaves the task unfinished.
11
11
  - Write one self-contained paragraph of 20–55 words in past tense. Lead with what now works, then explain the user or team impact and meaningful validation. Mention important limitations when present.
12
12
  - Describe the outcome, not the agent’s process. Never include first-person narration, prompts, source code, secrets, raw commands, routine tool calls, filenames, line counts, praise, hype, or speculation; Wibe displays code and file changes separately.
13
+ - Bias frontend outcomes toward visual evidence without asking for per-task approval. For UI components, pages, styling, responsive behavior, interactions, and visual fixes, attach one screenshot when the app or preview is already runnable. Prefer a screenshot already made during visual QA; otherwise capture the clearest final state.
14
+ - Skip screenshots for backend, infrastructure, documentation, refactors, and non-visual frontend logic. Never launch or repair an app solely for Wibe, and omit the image when navigation needs manual authentication, the state contains sensitive data, visual validation is blocked, or the project has visual updates disabled.
15
+ - When a screenshot is available, do not call `wibe_share_progress`. Instead run `npx --yes --package @wibeco/bridge@latest wibe share-progress --summary "<summary>" --phase <shipped|blocked> --screenshot "<path>"` so the local bridge uploads the image and publishes the outcome together.
13
16
  - If the Wibe MCP tool is unavailable, run `npx --yes --package @wibeco/bridge@latest wibe share-progress --summary "<summary>" --phase <shipped|blocked>`.