@amirhosseinnateghi/vibed-mcp 0.1.2 → 0.1.3

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/index.js +342 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5986,6 +5986,18 @@ import { dirname, extname, isAbsolute, join, resolve } from "node:path";
5986
5986
  // ../sandbox/src/index.ts
5987
5987
  var import_node_html_parser = __toESM(require_dist(), 1);
5988
5988
 
5989
+ // ../shared/src/terms.ts
5990
+ var ITEM = {
5991
+ /** lowercase singular — "vibe" */
5992
+ one: "vibe",
5993
+ /** lowercase plural — "vibes" */
5994
+ many: "vibes",
5995
+ /** Capitalized singular — "Vibe" */
5996
+ One: "Vibe",
5997
+ /** Capitalized plural — "Vibes" */
5998
+ Many: "Vibes"
5999
+ };
6000
+
5989
6001
  // ../shared/src/constants.ts
5990
6002
  var CATEGORIES = [
5991
6003
  "Game",
@@ -5998,10 +6010,13 @@ var CATEGORIES = [
5998
6010
  "Other"
5999
6011
  ];
6000
6012
  var VISIBILITY = ["public", "unlisted"];
6013
+ var COVER_MODES = ["image", "gif"];
6001
6014
  var EXPERIENCE_SOURCE = ["builder", "import", "remix"];
6015
+ var PUSH_PROVIDERS = ["webpush", "fcm", "apns"];
6016
+ var DEVICE_PLATFORMS = ["web", "ios", "android"];
6002
6017
  var config = {
6003
- ARTIFACT_MAX_BYTES: intEnv("ARTIFACT_MAX_BYTES", 3145728),
6004
- // 3 MB
6018
+ ARTIFACT_MAX_BYTES: intEnv("ARTIFACT_MAX_BYTES", 8388608),
6019
+ // 8 MB — headroom for an embedded audio track
6005
6020
  ARTIFACT_ALLOWED_ORIGINS: strEnv(
6006
6021
  "ARTIFACT_ALLOWED_ORIGINS",
6007
6022
  "fonts.googleapis.com,fonts.gstatic.com,cdnjs.cloudflare.com,cdn.jsdelivr.net,unpkg.com"
@@ -6024,6 +6039,19 @@ var config = {
6024
6039
  BUILDER_CLASSIFIER_MODEL: strEnv("BUILDER_CLASSIFIER_MODEL", "gemini-2.5-flash"),
6025
6040
  BUILDER_MAX_TOKENS: intEnv("BUILDER_MAX_TOKENS", 8192),
6026
6041
  BUILDER_MAX_MESSAGES: intEnv("BUILDER_MAX_MESSAGES", 30),
6042
+ // Hard cap for a single build turn (across the model + one repair). The build
6043
+ // runs detached from the request, so this — not the browser — is what stops a
6044
+ // runaway/abandoned turn. The provider's own 90s per-stall timeout still applies.
6045
+ // Never cut a LIVE generation — these two guards only catch dead work:
6046
+ // stall = total silence (not even a keep-alive byte) long enough to mean the
6047
+ // connection is dead (TCP half-open never errors on its own); the turn cap is
6048
+ // a platform-cost safety ceiling for the whole turn incl. one repair pass.
6049
+ BUILDER_STALL_TIMEOUT_MS: intEnv("BUILDER_STALL_TIMEOUT_MS", 3e5),
6050
+ BUILDER_TURN_TIMEOUT_MS: intEnv("BUILDER_TURN_TIMEOUT_MS", 9e5),
6051
+ // Once a build has run this long, the UI nudges the user to go explore, and on
6052
+ // completion the server drops a notification (§7.1) — so a slow build never
6053
+ // traps them staring at a spinner.
6054
+ BUILDER_SLOW_MS: intEnv("BUILDER_SLOW_MS", 12e3),
6027
6055
  DEFAULT_LOCALE: strEnv("DEFAULT_LOCALE", "en")
6028
6056
  };
6029
6057
  function strEnv(key, fallback) {
@@ -6046,6 +6074,31 @@ function boolEnv(key, fallback) {
6046
6074
  return v === "true" || v === "1";
6047
6075
  }
6048
6076
 
6077
+ // ../shared/src/notifications.ts
6078
+ var NOTIF_VERB = {
6079
+ like_milestone: `liked your ${ITEM.one}`,
6080
+ comment: `commented on your ${ITEM.one}`,
6081
+ reply: "replied to your comment",
6082
+ follow: "started following you",
6083
+ mention: "mentioned you",
6084
+ remix: `remixed your ${ITEM.one}`
6085
+ };
6086
+ var SYSTEM_NOTIF = {
6087
+ build_ready: { icon: "\u2728", text: "Your build is ready \u2014 tap to see it", tone: "info" },
6088
+ build_failed: { icon: "\u26A0\uFE0F", text: "A build didn't finish \u2014 tap to try again", tone: "error" },
6089
+ // Moderation reject (spec §9): the vibe never went live (or was pulled), so
6090
+ // the inbox row must carry everything — title + reason arrive in the payload.
6091
+ post_rejected: { icon: "\u{1F6AB}", text: `Your ${ITEM.one} didn't pass moderation`, tone: "error" }
6092
+ };
6093
+ var NOTIF_PREF_GROUPS = ["follow", "comments", "likes", "remixes", "builder"];
6094
+ var NOTIF_GROUP_LABEL = {
6095
+ follow: { title: "New followers", hint: "when someone follows you" },
6096
+ comments: { title: "Comments", hint: "comments, replies & mentions" },
6097
+ likes: { title: "Likes", hint: `when your ${ITEM.many} hit like milestones` },
6098
+ remixes: { title: "Remixes", hint: `when someone remixes your ${ITEM.one}` },
6099
+ builder: { title: "Builder", hint: "when a build finishes or fails" }
6100
+ };
6101
+
6049
6102
  // ../shared/src/policy.ts
6050
6103
  function boolEnv2(key, fallback) {
6051
6104
  const v = typeof process !== "undefined" ? process.env[key] : void 0;
@@ -10118,10 +10171,102 @@ var coerce = {
10118
10171
  };
10119
10172
  var NEVER = INVALID;
10120
10173
 
10174
+ // ../shared/src/storage.ts
10175
+ var STORAGE = {
10176
+ /** Total shared bytes per post (values, serialized). */
10177
+ SHARED_MAX_BYTES: intEnv("STORE_SHARED_MAX_BYTES", 1048576),
10178
+ // 1 MB
10179
+ /** Total shared records (rows) per post. */
10180
+ SHARED_MAX_RECORDS: intEnv("STORE_SHARED_MAX_RECORDS", 5e3),
10181
+ /** Serialized size cap for a single value. */
10182
+ VALUE_MAX_BYTES: intEnv("STORE_VALUE_MAX_BYTES", 16384),
10183
+ // 16 KB
10184
+ /** Records a single author may hold in one post (anti-flood / anti-grief). */
10185
+ AUTHOR_MAX_RECORDS: intEnv("STORE_AUTHOR_MAX_RECORDS", 100),
10186
+ /** Distinct collections per post. */
10187
+ MAX_COLLECTIONS: intEnv("STORE_MAX_COLLECTIONS", 32),
10188
+ /** Local-tier budget per (post, identity) — enforced by the host broker. */
10189
+ LOCAL_MAX_BYTES: intEnv("STORE_LOCAL_MAX_BYTES", 262144),
10190
+ // 256 KB
10191
+ /** Max records returnable in one query page. */
10192
+ QUERY_MAX_LIMIT: intEnv("STORE_QUERY_MAX_LIMIT", 100),
10193
+ /** Whether anonymous (logged-out) shared writes may be enabled per post. */
10194
+ ANON_WRITES_AVAILABLE: boolEnv("STORE_ANON_WRITES_AVAILABLE", false)
10195
+ };
10196
+ var COLLECTION_RE = /^[a-z0-9][a-z0-9:_-]{0,63}$/;
10197
+ var collectionSchemaStore = external_exports.string().regex(COLLECTION_RE, "invalid collection name");
10198
+ var STORE_KEY_RE = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,127}$/;
10199
+ var RESERVED_STORE_KEYS = ["submit", "rank", "increment"];
10200
+ var storeKeySchema = external_exports.string().regex(STORE_KEY_RE, "invalid key").refine((k) => !RESERVED_STORE_KEYS.includes(k), {
10201
+ message: "reserved key"
10202
+ });
10203
+ var STORE_WRITE_POLICIES = ["anyone", "logged_in", "owner_only"];
10204
+ var STORE_SORTS = ["num", "-num", "created", "-created"];
10205
+ var storeValueSchema = external_exports.unknown().refine(
10206
+ (v) => {
10207
+ try {
10208
+ const s = JSON.stringify(v);
10209
+ return s !== void 0 && byteLength(s) <= STORAGE.VALUE_MAX_BYTES;
10210
+ } catch {
10211
+ return false;
10212
+ }
10213
+ },
10214
+ { message: `value must be JSON-serializable and <= ${STORAGE.VALUE_MAX_BYTES} bytes` }
10215
+ );
10216
+ var storePutSchema = external_exports.object({
10217
+ value: storeValueSchema,
10218
+ /** Optimistic concurrency: fail with `conflict` unless current version matches. */
10219
+ ifVersion: external_exports.number().int().positive().optional()
10220
+ });
10221
+ var storeAppendSchema = external_exports.object({
10222
+ value: storeValueSchema,
10223
+ /** Optional numeric field to index (sorting/leaderboards). */
10224
+ num: external_exports.number().finite().optional()
10225
+ });
10226
+ var storeIncrementSchema = external_exports.object({
10227
+ delta: external_exports.number().int().min(-1e6).max(1e6).default(1)
10228
+ });
10229
+ var storeSubmitSchema = external_exports.object({
10230
+ /** The number that ranks this author (mapped into `num`). */
10231
+ score: external_exports.number().finite(),
10232
+ /** Conflict rule when the author already has a row. */
10233
+ keep: external_exports.enum(["max", "min", "last"]).default("max"),
10234
+ /** Small display payload stored alongside (same value-size gate). */
10235
+ meta: storeValueSchema.optional()
10236
+ });
10237
+ var storeQuerySchema = external_exports.object({
10238
+ sort: external_exports.enum(STORE_SORTS).default("-created"),
10239
+ limit: external_exports.coerce.number().int().min(1).max(STORAGE.QUERY_MAX_LIMIT).default(30),
10240
+ cursor: external_exports.string().max(200).optional(),
10241
+ /** Only the caller's records. */
10242
+ mine: external_exports.coerce.boolean().default(false)
10243
+ });
10244
+ var storeAdminSchema = external_exports.discriminatedUnion("action", [
10245
+ external_exports.object({ action: external_exports.literal("freeze") }),
10246
+ external_exports.object({ action: external_exports.literal("unfreeze") }),
10247
+ external_exports.object({ action: external_exports.literal("set_policy"), writePolicy: external_exports.enum(STORE_WRITE_POLICIES) }),
10248
+ external_exports.object({ action: external_exports.literal("set_anon_writes"), anonWrites: external_exports.boolean() }),
10249
+ external_exports.object({ action: external_exports.literal("reset"), collection: collectionSchemaStore.optional() })
10250
+ ]);
10251
+ var storageOptInSchema = external_exports.object({
10252
+ local: external_exports.boolean().default(false),
10253
+ shared: external_exports.boolean().default(false)
10254
+ });
10255
+ function byteLength(s) {
10256
+ if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(s).length;
10257
+ let n = 0;
10258
+ for (let i = 0; i < s.length; i++) {
10259
+ const c = s.codePointAt(i);
10260
+ n += c <= 127 ? 1 : c <= 2047 ? 2 : c <= 65535 ? 3 : (i++, 4);
10261
+ }
10262
+ return n;
10263
+ }
10264
+
10121
10265
  // ../shared/src/schemas.ts
10122
10266
  var handleSchema = external_exports.string().regex(/^[a-z0-9_.]{3,24}$/, "3\u201324 chars: a\u2013z, 0\u20139, _ or .");
10123
10267
  var categorySchema = external_exports.enum(CATEGORIES);
10124
10268
  var visibilitySchema = external_exports.enum(VISIBILITY);
10269
+ var coverModeSchema = external_exports.enum(COVER_MODES);
10125
10270
  var sourceSchema = external_exports.enum(EXPERIENCE_SOURCE);
10126
10271
  var tagsSchema = external_exports.array(external_exports.string().min(1).max(40)).max(10).transform(
10127
10272
  (tags) => Array.from(
@@ -10135,8 +10280,12 @@ var publishSchema = external_exports.object({
10135
10280
  tags: tagsSchema,
10136
10281
  remixable: external_exports.boolean().default(true),
10137
10282
  visibility: visibilitySchema.default("public"),
10283
+ coverMode: coverModeSchema.default("gif"),
10138
10284
  draftArtifactKey: external_exports.string().min(1),
10139
- buildSessionId: external_exports.string().uuid().optional()
10285
+ buildSessionId: external_exports.string().uuid().optional(),
10286
+ /** Storage opt-in override; when omitted, publish auto-detects from the source
10287
+ * (docs/post-storage.md §9 — default off, enabled only when actually used). */
10288
+ storage: storageOptInSchema.optional()
10140
10289
  });
10141
10290
  var patchExperienceSchema = external_exports.object({
10142
10291
  title: external_exports.string().min(1).max(120).optional(),
@@ -10144,7 +10293,8 @@ var patchExperienceSchema = external_exports.object({
10144
10293
  tags: tagsSchema.optional(),
10145
10294
  category: categorySchema.optional(),
10146
10295
  remixable: external_exports.boolean().optional(),
10147
- visibility: visibilitySchema.optional()
10296
+ visibility: visibilitySchema.optional(),
10297
+ coverMode: coverModeSchema.optional()
10148
10298
  });
10149
10299
  var commentSchema = external_exports.object({
10150
10300
  body: external_exports.string().min(1).max(500),
@@ -10159,10 +10309,10 @@ var createBuildSchema = external_exports.object({
10159
10309
  remixParentId: external_exports.string().uuid().optional()
10160
10310
  });
10161
10311
  var builderAttachmentSchema = external_exports.object({
10162
- kind: external_exports.enum(["image", "file"]),
10312
+ kind: external_exports.enum(["image", "file", "audio"]),
10163
10313
  name: external_exports.string().max(200),
10164
- dataUrl: external_exports.string().max(8e6).optional(),
10165
- // ~6MB image as base64 data URL
10314
+ // base64 data URL for image/audio (~8.7MB raw fits, covering a short song).
10315
+ dataUrl: external_exports.string().max(12e6).optional(),
10166
10316
  text: external_exports.string().max(5e4).optional()
10167
10317
  });
10168
10318
  var builderMessageSchema = external_exports.object({
@@ -10194,10 +10344,21 @@ var reportSchema = external_exports.object({
10194
10344
  var patchMeSchema = external_exports.object({
10195
10345
  displayName: external_exports.string().min(1).max(60).optional(),
10196
10346
  bio: external_exports.string().max(300).optional(),
10197
- avatar: external_exports.string().url().optional(),
10347
+ // A stored image URL, or "" to remove the photo and fall back to the color avatar.
10348
+ avatar: external_exports.string().url().or(external_exports.literal("")).optional(),
10349
+ // Fallback avatar colour (used when there's no photo). Hex like #6C4CF1.
10350
+ avatarBg: external_exports.string().regex(/^#[0-9a-fA-F]{6}$/, "hex colour, e.g. #6C4CF1").optional(),
10198
10351
  handle: handleSchema.optional()
10199
10352
  // once
10200
10353
  });
10354
+ var waitlistSchema = external_exports.object({
10355
+ email: external_exports.string().trim().email().max(200),
10356
+ note: external_exports.string().trim().max(500).optional()
10357
+ });
10358
+ var waitlistReviewSchema = external_exports.object({
10359
+ requestId: external_exports.string().uuid(),
10360
+ action: external_exports.enum(["approve", "reject"])
10361
+ });
10201
10362
  var aiConnectionSchema = external_exports.object({
10202
10363
  provider: external_exports.enum(AI_PROVIDERS),
10203
10364
  apiKey: external_exports.string().trim().min(12).max(300)
@@ -10211,28 +10372,185 @@ var feedQuerySchema = external_exports.object({
10211
10372
  var searchQuerySchema = external_exports.object({
10212
10373
  q: external_exports.string().default(""),
10213
10374
  tab: external_exports.enum(["experiences", "tags", "creators"]).default("experiences"),
10214
- remixableOnly: external_exports.coerce.boolean().default(false),
10375
+ // Query params arrive as strings; z.coerce.boolean() is just Boolean(x), so
10376
+ // "false"/"0" would (wrongly) become true and the filter could never be turned
10377
+ // off. Parse the string explicitly: only "true"/"1" (or a real boolean) is true.
10378
+ remixableOnly: external_exports.union([external_exports.boolean(), external_exports.string()]).optional().transform((v) => v === true || v === "true" || v === "1"),
10215
10379
  cursor: external_exports.string().optional(),
10216
10380
  limit: external_exports.coerce.number().int().min(1).max(30).default(15)
10217
10381
  });
10382
+ var registerPushDeviceSchema = external_exports.object({
10383
+ provider: external_exports.enum(PUSH_PROVIDERS),
10384
+ token: external_exports.string().trim().min(16).max(4096),
10385
+ keys: external_exports.record(external_exports.string().max(512)).default({}),
10386
+ platform: external_exports.enum(DEVICE_PLATFORMS).default("web"),
10387
+ userAgent: external_exports.string().trim().max(400).default("")
10388
+ }).superRefine((v, ctx) => {
10389
+ if (v.provider === "webpush" && (!v.keys.p256dh || !v.keys.auth)) {
10390
+ ctx.addIssue({
10391
+ code: external_exports.ZodIssueCode.custom,
10392
+ path: ["keys"],
10393
+ message: "webpush requires keys.p256dh and keys.auth"
10394
+ });
10395
+ }
10396
+ });
10397
+ var unregisterPushDeviceSchema = external_exports.object({
10398
+ token: external_exports.string().trim().min(16).max(4096)
10399
+ });
10400
+ var notificationsQuerySchema = external_exports.object({
10401
+ filter: external_exports.enum(["all", "mentions", "remixes"]).default("all"),
10402
+ // Keyset cursor: the `createdAt` ISO timestamp of the last item seen.
10403
+ cursor: external_exports.string().datetime({ offset: true }).optional(),
10404
+ limit: external_exports.coerce.number().int().min(1).max(80).default(30)
10405
+ });
10406
+ var notifPrefsPatchSchema = external_exports.object({
10407
+ enabled: external_exports.boolean().optional(),
10408
+ groups: external_exports.record(external_exports.enum(NOTIF_PREF_GROUPS), external_exports.boolean()).optional()
10409
+ });
10218
10410
 
10219
10411
  // ../shared/src/models.ts
10220
10412
  var BUILDER_MODELS = [
10221
- // ── Free (selectable now) — good quality, reasonable price ─────────
10222
- { id: "claude-sonnet-4-20250514", label: "Claude Sonnet 4", tier: "free", note: "Best for polished HTML", vision: true },
10223
- { id: "gemini-2.5-flash", label: "Gemini 2.5 Flash", tier: "free", note: "Fast \xB7 great UI \xB7 cheap", vision: true },
10224
- { id: "gpt-4o-mini", label: "GPT-4o mini", tier: "free", note: "Cheap & reliable", vision: true },
10225
- { id: "gpt-5-mini", label: "GPT-5 mini", tier: "free", note: "Newer, capable", vision: true },
10226
- { id: "gpt-5.1-codex", label: "GPT-5.1 Codex", tier: "free", note: "Code specialist" },
10413
+ // ── Free (selectable now) — ONLY genuinely strong builders ─────────
10414
+ // Curated 2026-07: every id verified against GapGPT with a real full-HTML
10415
+ // build (not a ping). Weak/cheap models were removed on purpose a bad
10416
+ // first build costs more in user trust than the tokens save.
10417
+ { id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", tier: "free", note: "Best for polished vibes \xB7 fast", vision: true },
10418
+ { id: "claude-sonnet-4-20250514", label: "Claude Sonnet 4", tier: "free", note: "Reliable all-rounder", vision: true },
10419
+ { id: "gpt-5.2", label: "GPT-5.2", tier: "free", note: "Strong generalist", vision: true },
10420
+ { id: "gpt-5.3-codex", label: "GPT-5.3 Codex", tier: "free", note: "Code specialist" },
10421
+ { id: "gemini-2.5-pro", label: "Gemini 2.5 Pro", tier: "free", note: "Great UI sense", vision: true },
10227
10422
  { id: "deepseek-v4-pro", label: "DeepSeek V4 Pro", tier: "free", note: "Strong coder \xB7 great value" },
10228
- { id: "deepseek-v4-flash", label: "DeepSeek V4 Flash", tier: "free", note: "Fast \xB7 very cheap" },
10229
- { id: "gapgpt-qwen-3.6", label: "Qwen 3.6", tier: "free", note: "Good value" },
10423
+ { id: "qwen3-coder-480b-a35b-instruct", label: "Qwen3 Coder 480B", tier: "free", note: "Fast, capable coder" },
10230
10424
  // ── Subscription (locked until billing is enabled) — premium ──────
10231
- { id: "claude-opus-4-8", label: "Claude Opus 4.8", tier: "subscription", note: "Top quality", vision: true },
10425
+ // Opus 4.8 is owner-only early access: locked for everyone, live for the owner.
10426
+ { id: "claude-opus-4-8", label: "Claude Opus 4.8", tier: "subscription", note: "Top quality", vision: true, ownerOnly: true },
10232
10427
  { id: "gpt-5.2-pro", label: "GPT-5.2 Pro", tier: "subscription", note: "Premium" },
10233
10428
  { id: "gemini-3-pro-preview", label: "Gemini 3 Pro", tier: "subscription", note: "Premium", vision: true }
10234
10429
  ];
10235
10430
  var FREE_IDS = new Set(BUILDER_MODELS.filter((m) => m.tier === "free").map((m) => m.id));
10431
+ var OWNER_MODEL_IDS = new Set(BUILDER_MODELS.filter((m) => m.ownerOnly).map((m) => m.id));
10432
+
10433
+ // ../shared/src/i18n.ts
10434
+ var en = {
10435
+ "nav.home": "Home",
10436
+ "nav.search": "Search",
10437
+ "nav.create": "Create",
10438
+ "nav.notifications": "Notifications",
10439
+ "nav.profile": "Profile",
10440
+ "feed.explore": "Explore",
10441
+ "feed.following": "Following",
10442
+ "feed.tapToInteract": "Tap to interact",
10443
+ "feed.loading": `Loading ${ITEM.one}`,
10444
+ "feed.exit": "Exit",
10445
+ "feed.remixedFrom": "Remixed from @{handle}",
10446
+ "feed.remixedFromRemoved": `Remixed from a removed ${ITEM.one}`,
10447
+ "feed.newVersionOf": "New version of {title}",
10448
+ "feed.end.title": "You're all caught up",
10449
+ "feed.end.body": `No new ${ITEM.many} right now \u2014 remix one you loved, or check back soon.`,
10450
+ "feed.end.remix": "Find something to remix",
10451
+ "feed.end.top": "Back to top",
10452
+ "rail.like": "Like",
10453
+ "rail.comment": "Comment",
10454
+ "rail.remix": "Remix",
10455
+ "rail.save": "Save",
10456
+ "rail.share": "Share",
10457
+ "comments.title": "Comments",
10458
+ "comments.placeholder": "Add a comment\u2026",
10459
+ "comments.reply": "Reply",
10460
+ "comments.empty": "No comments yet \u2014 be the first.",
10461
+ "comments.post": "Post",
10462
+ "share.title": "Share",
10463
+ "share.copyLink": "Copy link",
10464
+ "share.copied": "Link copied",
10465
+ "create.title": "Create",
10466
+ "create.buildWithAI": "Build with AI",
10467
+ "create.import": "Import",
10468
+ "create.remix": "Remix something",
10469
+ "create.prompt.placeholder": "Describe what you want to build\u2026",
10470
+ "create.prompt.starters": "Start from a template",
10471
+ "builder.publish": "Publish",
10472
+ "builder.thinking": "Building\u2026",
10473
+ "builder.updated": "Updated \u2713",
10474
+ "builder.placeholder": "Ask for a change\u2026",
10475
+ "builder.mockNotice": "Preview builder \u2014 AI generation is mocked for now.",
10476
+ "import.title": "Import",
10477
+ "import.paste": "Paste HTML, or drop an .html file",
10478
+ "import.validate": "Validate",
10479
+ "import.warnings": "Warnings",
10480
+ "publish.title": "Publish",
10481
+ "publish.postTitle": "Title",
10482
+ "publish.caption": "Caption",
10483
+ "publish.category": "Category",
10484
+ "publish.remixable": "Allow remixes",
10485
+ "publish.public": "Public",
10486
+ "publish.unlisted": "Unlisted",
10487
+ "publish.cta": "Publish",
10488
+ "publish.remixCredit": "Remixing @{handle} \u2014 {title}",
10489
+ "publish.toast.published": "Published! \u{1F389}",
10490
+ "publish.toast.remix": "Remix published \u{1F501} @{handle} was credited",
10491
+ "remix.confirmTitle": "Remixing @{handle}'s {title}",
10492
+ "remix.confirmBody": `They'll be credited on your ${ITEM.one}. Attribution can't be removed.`,
10493
+ "remix.start": "Start remixing",
10494
+ "remix.galleryTitle": "Remixes",
10495
+ "remix.sortPopular": "Popular",
10496
+ "remix.sortRecent": "Recent",
10497
+ "remix.count": "{n} remixes",
10498
+ "remix.identicalWarn": "This looks identical to the original \u2014 publish anyway?",
10499
+ "search.placeholder": `Search ${ITEM.many}, tags, creators`,
10500
+ "search.tab.experiences": ITEM.Many,
10501
+ "search.tab.tags": "Hashtags",
10502
+ "search.tab.creators": "Creators",
10503
+ "search.remixableOnly": "Remixable only",
10504
+ "search.trending": "Trending",
10505
+ "profile.tab.experiences": ITEM.Many,
10506
+ "profile.tab.remixes": "Remixes",
10507
+ "profile.tab.saved": "Saved",
10508
+ "profile.followers": "followers",
10509
+ "profile.following": "following",
10510
+ "profile.likes": "likes",
10511
+ "profile.follow": "Follow",
10512
+ "profile.unfollow": "Following",
10513
+ "profile.edit": "Edit profile",
10514
+ "notifs.all": "All",
10515
+ "notifs.mentions": "Mentions",
10516
+ "notifs.remixes": "Remixes",
10517
+ "notifs.remixedYou": `@{handle} remixed your ${ITEM.one}`,
10518
+ "notifs.liked": `@{handle} liked your ${ITEM.one}`,
10519
+ "notifs.commented": "@{handle} commented",
10520
+ "notifs.followed": "@{handle} started following you",
10521
+ "notifs.empty": "Nothing here yet.",
10522
+ "collections.title": "Saved",
10523
+ "collections.new": "New collection",
10524
+ "analytics.title": "Analytics",
10525
+ "analytics.views": "Views",
10526
+ "analytics.plays": "Plays",
10527
+ "analytics.playRate": "Play rate",
10528
+ "analytics.avgTime": "Avg. time",
10529
+ "analytics.likes": "Likes",
10530
+ "analytics.comments": "Comments",
10531
+ "analytics.shares": "Shares",
10532
+ "analytics.saves": "Saves",
10533
+ "analytics.remixes": "Remixes",
10534
+ "post.options": `${ITEM.One} options`,
10535
+ "post.analytics": "View analytics",
10536
+ "post.edit": "Edit caption",
10537
+ "post.delete": `Delete ${ITEM.one}`,
10538
+ "post.deleteConfirm": `Delete this ${ITEM.one}? This can't be undone.`,
10539
+ "post.captionPlaceholder": "Write a caption\u2026",
10540
+ "auth.signIn": "Sign in",
10541
+ "auth.google": "Continue with Google",
10542
+ "auth.github": "Continue with GitHub",
10543
+ "auth.dev": "Dev login",
10544
+ "auth.needAccount": "Sign in to like, comment, and remix.",
10545
+ "common.cancel": "Cancel",
10546
+ "common.save": "Save",
10547
+ "common.back": "Back",
10548
+ "common.retry": "Retry",
10549
+ "common.loading": "Loading\u2026",
10550
+ "common.report": "Report",
10551
+ "common.delete": "Delete",
10552
+ "common.views": "{n} views"
10553
+ };
10236
10554
 
10237
10555
  // ../shared/src/tokens.ts
10238
10556
  var colors = {
@@ -10419,6 +10737,12 @@ function validate(html, opts) {
10419
10737
  }
10420
10738
  }
10421
10739
  }
10740
+ if (/(localStorage|sessionStorage|indexedDB|document\s*\.\s*cookie)/.test(html)) {
10741
+ warnings.push({
10742
+ code: "BROWSER_STORAGE",
10743
+ message: "Uses browser storage (localStorage/sessionStorage/indexedDB/cookies), which throws inside the vibed sandbox. Port persistence to window.vibed.storage (local per-viewer, shared collective)."
10744
+ });
10745
+ }
10422
10746
  const lower = html.toLowerCase();
10423
10747
  const hasCredField = root.querySelector('input[type="email"], input[type="text"], input[type="tel"]') != null && /(password|passcode|otp|verify|sign\s*in|log\s*in|account)/i.test(html);
10424
10748
  if (hasCredField) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amirhosseinnateghi/vibed-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "vibed MCP server — make it vibed from any MCP client (Codex, Cursor, Gemini, …): check, login, and publish tools",
5
5
  "license": "MIT",
6
6
  "type": "module",