github TypeScript analyzed 348bde5

liha-app/live-preview

github

A review session your agent can join. Share a build at a stable URL; reviewers click the element they mean; an agent reads the source behind it, resizes their preview, and replies in the thread — over WebMCP, MCP and a JSON CLI.

maintainer
liha-app
licence
MIT
first seen
2026-08-31
last seen
2026-09-18
releases · 30d
0
short id

Drift inferred · capture-to-capture

No drift recorded — single capability capture; advisories appear once its surface changes.

capabilities 8 tools
transport stdio counts 8 tools · 0 res · 0 prompts permission surface via code analysis

tools

  • create_preview
  • get_comment
  • get_preview_info
  • list_comments
  • list_versions
  • reply_to_comment
  • resolve_comment
  • update_preview
skills & danger signals github-tarball
prompt-surface shipped agent-instruction files + hidden-content / dangerous-code findings — quoted from the analyzed source

analyzed commit 348bde5 · analyzer v33 · 19h ago

danger signals5

code evidence vHEAD · github-tarball
evidence-backed findings quoted directly from the published source artifact — not inferred

code files: 185

filesystem 23

  • fs (weak) liha-app-live-preview-348bde5/apps/api/test/content-origin.test.ts :1 import { readFileSync } from 'node:fs';
  • fs (weak) liha-app-live-preview-348bde5/apps/api/test/harness.ts :1 import { readFileSync, readdirSync } from 'node:fs';
  • fs (weak) liha-app-live-preview-348bde5/apps/web/src/lib/unseen.test.ts :1 import { readFileSync } from 'node:fs';
  • fs (weak) liha-app-live-preview-348bde5/apps/web/src/og.test.ts :1 import { readFileSync } from 'node:fs';
  • fs (weak) liha-app-live-preview-348bde5/apps/web/src/styles.test.ts :1 import { readFileSync } from 'node:fs';
  • fs (weak) liha-app-live-preview-348bde5/packages/cli/src/cli.test.ts :1 import { readFileSync } from 'node:fs';
  • fs liha-app-live-preview-348bde5/packages/cli/src/client.ts :1 import { readFile } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/packages/cli/src/files.ts :1 import { readdir, stat } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/packages/cli/src/project.ts :2 import { existsSync } from 'node:fs';
  • fs liha-app-live-preview-348bde5/packages/mcp/src/api.ts :15 * cast is safe: `fs.readFile` never returns a SharedArrayBuffer-backed buffer.
  • fs liha-app-live-preview-348bde5/packages/mcp/src/credentials.ts :3 import { mkdir, readFile, writeFile } from 'node:fs/promises';
  • fs (weak) liha-app-live-preview-348bde5/packages/mcp/src/server.test.ts :1 import { readFileSync } from 'node:fs';
show 11 more
  • fs liha-app-live-preview-348bde5/packages/mcp/src/workspace.ts :1 import { existsSync } from 'node:fs';
  • fs (weak) liha-app-live-preview-348bde5/scripts/deploy.mjs :24 import fs from 'node:fs';
  • fs (weak) liha-app-live-preview-348bde5/scripts/make-og.mjs :17 import { stat } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/video/tools/build-subtitles-ja.ts :3 import { mkdir, readFile, writeFile } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/video/tools/build-subtitles.ts :3 import { mkdir, readFile, writeFile } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/video/tools/generate-narration-ja.mjs :3 import { mkdtemp, mkdir, readFile, rm } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/video/tools/generate-narration.ts :3 import { mkdir, mkdtemp, readFile, rename, rm, unlink, writeFile } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/video/tools/record.mjs :13 import { readFileSync, mkdirSync } from 'node:fs';
  • fs liha-app-live-preview-348bde5/video/tools/scene-02-publish.mjs :1 import { cp, mkdtemp, rm } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/video/tools/scene-05-same-url.mjs :1 import { cp, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
  • fs liha-app-live-preview-348bde5/video/tools/scene-06-product.mjs :2 import { rm } from 'node:fs/promises';

shell / exec 8

  • shell liha-app-live-preview-348bde5/packages/cli/src/project.ts :1 import { spawn } from 'node:child_process';
  • shell (weak) liha-app-live-preview-348bde5/scripts/lib/cloudflare.mjs :3 import { spawn } from 'node:child_process';
  • shell liha-app-live-preview-348bde5/video/tools/generate-narration-ja.mjs :6 import { spawnSync } from 'node:child_process';
  • shell liha-app-live-preview-348bde5/video/tools/generate-narration.ts :4 import { spawnSync } from 'node:child_process';
  • shell liha-app-live-preview-348bde5/video/tools/record.mjs :15 import { spawnSync } from 'node:child_process';
  • shell liha-app-live-preview-348bde5/video/tools/scene-02-publish.mjs :4 import { spawnSync } from 'node:child_process';
  • shell liha-app-live-preview-348bde5/video/tools/scene-05-same-url.mjs :4 import { spawnSync } from 'node:child_process';
  • shell liha-app-live-preview-348bde5/video/tools/terminal-capture.mjs :2 import { spawn, spawnSync } from 'node:child_process';

network 40

  • net liha-app-live-preview-348bde5/apps/api/src/app.ts :1447 const asset = await env.ASSETS.fetch(
  • net liha-app-live-preview-348bde5/apps/api/src/content.ts :27 // The app needs to read these bytes with fetch(): pdf.js renders a PDF from
  • net liha-app-live-preview-348bde5/apps/api/src/env.ts :37 ASSETS?: { fetch(request: Request): Promise<Response> };
  • net liha-app-live-preview-348bde5/apps/api/src/google.ts :159 const response = await fetch(TOKEN, {
  • net liha-app-live-preview-348bde5/apps/api/src/index.ts :9 async fetch(request: Request, env: Env, ctx: DeferredWork): Promise<Response> {
  • net liha-app-live-preview-348bde5/apps/api/src/notification-site.ts :199 const response = await fetch(API + path, {
  • net liha-app-live-preview-348bde5/apps/api/src/push.ts :98 const response = await fetch(endpoint, {
  • net liha-app-live-preview-348bde5/apps/api/src/url-import.ts :51 const response = await fetcher.fetch(
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/accounts.test.ts :40 await server.fetch(path, {
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/comment-idempotency.test.ts :16 const response = await app.fetch('/api/previews', {
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/content-origin.test.ts :205 const response = await server.fetch('/api/previews', {
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/flow.test.ts :23 const response = await server.fetch('/api/previews', {
show 28 more
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/harness.ts :99 fetch(path: string, init?: RequestInit): Promise<Response>;
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/limits.test.ts :13 return server.fetch('/api/previews', {
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/notify.test.ts :38 server.fetch('/api/push/subscribe', {
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/retention.test.ts :58 expect((await server.fetch(`/api/previews/${preview.slug}`)).status).toBe(200);
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/review-host.test.ts :42 const response = await server.fetch('/api/previews', {
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/security.test.ts :14 const response = await server.fetch('/api/previews', {
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/threads.test.ts :9 const response = await server.fetch('/api/previews', { method: 'POST', ...uploadBody([PAGE]) });
  • net (weak) liha-app-live-preview-348bde5/apps/api/test/url-import.test.ts :158 const anyone = await server.fetch(`/api/previews/${created.preview.slug}/versions/from-url`, {
  • net (weak) liha-app-live-preview-348bde5/apps/web/e2e/a11y.spec.ts :28 const response = await fetch(`${API}/api/previews`, {
  • net (weak) liha-app-live-preview-348bde5/apps/web/e2e/account.spec.ts :143 const response = await fetch(`${API}/api/previews/${slug}/comments`, {
  • net (weak) liha-app-live-preview-348bde5/apps/web/e2e/artifact-types.spec.ts :36 const response = await fetch(`${API}/api/previews`, {
  • net (weak) liha-app-live-preview-348bde5/apps/web/e2e/comments.spec.ts :29 const response = await fetch(`${API}/api/previews`, {
  • net (weak) liha-app-live-preview-348bde5/apps/web/e2e/focus.spec.ts :17 await fetch(`${API}/api/previews`, { method: 'POST', headers: asNewClient(), body: form })
  • net (weak) liha-app-live-preview-348bde5/apps/web/e2e/i18n.spec.ts :25 const response = await fetch(`${API}/api/previews`, {
  • net (weak) liha-app-live-preview-348bde5/apps/web/e2e/review-loop.spec.ts :33 const response = await fetch(`${API}/api/previews`, {
  • net liha-app-live-preview-348bde5/apps/web/src/components/PdfStage.tsx :60 const response = await fetch(version.contentUrl ?? '');
  • net liha-app-live-preview-348bde5/apps/web/src/lib/api.ts :65 response = await fetch(`${API_URL}${path}`, {
  • net liha-app-live-preview-348bde5/apps/web/src/routes/PreviewRoute.tsx :480 const response = await fetch(target);
  • net (weak) liha-app-live-preview-348bde5/packages/cli/src/cli.test.ts :146 const content = await fetch(`${server.url}/content/${data.slug as string}/1/index.html`).then(
  • net liha-app-live-preview-348bde5/packages/cli/src/client.ts :68 response = await fetch(`${this.options.apiUrl}${path}`, {
  • net liha-app-live-preview-348bde5/packages/mcp/src/api.ts :56 const response = await fetch(`${this.options.apiUrl}${path}`, {
  • net (weak) liha-app-live-preview-348bde5/packages/mcp/src/server.test.ts :108 const posted = (await fetch(`${api.url}/api/previews/${slug}/comments`, {
  • net (weak) liha-app-live-preview-348bde5/scripts/deploy.mjs :734 const response = await fetch(`https://${config.apiHost}/api/health`);
  • net (weak) liha-app-live-preview-348bde5/scripts/lib/cloudflare.mjs :84 const response = await fetch(`https://api.cloudflare.com/client/v4${endpoint}`, {
  • net (weak) liha-app-live-preview-348bde5/scripts/verify-deployment.mjs :127 if (!url.hostname.endsWith('.localhost')) return fetch(url, init);
  • net (weak) liha-app-live-preview-348bde5/video/tools/fixture.mjs :62 const response = await fetch(`${API}/api/previews`, {
  • net liha-app-live-preview-348bde5/video/tools/generate-narration.ts :337 response = await fetch('https://generativelanguage.googleapis.com/v1beta/interactions', {
  • net liha-app-live-preview-348bde5/video/tools/scene-06-product.mjs :20 const response = await fetch(`${apiUrl}${path}`, {

secrets 6

  • secrets (weak) liha-app-live-preview-348bde5/packages/cli/src/cli.test.ts :69 delete process.env.LIHA_OWNER_TOKEN;
  • secrets liha-app-live-preview-348bde5/packages/cli/src/context.ts :81 flagString(args, 'token') ?? process.env.LIHA_OWNER_TOKEN ?? credential?.ownerToken;
  • secrets (weak) liha-app-live-preview-348bde5/packages/mcp/src/server.test.ts :53 delete process.env.LIHA_OWNER_TOKEN;
  • secrets liha-app-live-preview-348bde5/packages/mcp/src/server.ts :104 ownerToken: credential?.ownerToken ?? process.env.LIHA_OWNER_TOKEN,
  • secrets (weak) liha-app-live-preview-348bde5/scripts/deploy.mjs :196 let token = process.env.CLOUDFLARE_API_TOKEN?.trim();
  • secrets liha-app-live-preview-348bde5/video/tools/generate-narration.ts :397 const apiKey = process.env.GEMINI_API_KEY;

tool registrations 8

  • get_preview_info liha-app-live-preview-348bde5/packages/mcp/src/server.ts :126
  • list_comments liha-app-live-preview-348bde5/packages/mcp/src/server.ts :157
  • get_comment liha-app-live-preview-348bde5/packages/mcp/src/server.ts :183
  • list_versions liha-app-live-preview-348bde5/packages/mcp/src/server.ts :214
  • create_preview liha-app-live-preview-348bde5/packages/mcp/src/server.ts :242
  • update_preview liha-app-live-preview-348bde5/packages/mcp/src/server.ts :295
  • reply_to_comment liha-app-live-preview-348bde5/packages/mcp/src/server.ts :331
  • resolve_comment liha-app-live-preview-348bde5/packages/mcp/src/server.ts :375

declared dependencies 3

  • playwright@1.62.1
  • prettier@^3.4.2
  • typescript@^5.7.3

perm:untrusted 1

  • untrusted (weak) liha-app-live-preview-348bde5/packages/cli/src/cli.test.ts :146 const content = await fetch(`${server.url}/content/${data.slug as string}/1/index.html`).then(