@amirhosseinnateghi/vibed-mcp 0.1.6 → 0.1.8
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.
- package/dist/index.js +141 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6009,6 +6009,25 @@ var CATEGORIES = [
|
|
|
6009
6009
|
"Art",
|
|
6010
6010
|
"Other"
|
|
6011
6011
|
];
|
|
6012
|
+
var TAGS = [
|
|
6013
|
+
"game",
|
|
6014
|
+
"puzzle",
|
|
6015
|
+
"quiz",
|
|
6016
|
+
"simulation",
|
|
6017
|
+
"music",
|
|
6018
|
+
"art",
|
|
6019
|
+
"animation",
|
|
6020
|
+
"story",
|
|
6021
|
+
"invitation",
|
|
6022
|
+
"love",
|
|
6023
|
+
"education",
|
|
6024
|
+
"science",
|
|
6025
|
+
"news",
|
|
6026
|
+
"tool",
|
|
6027
|
+
"productivity"
|
|
6028
|
+
];
|
|
6029
|
+
var MAX_TAGS = 5;
|
|
6030
|
+
var FEED_TAG_CHIPS = ["All", ...TAGS];
|
|
6012
6031
|
var VISIBILITY = ["public", "unlisted"];
|
|
6013
6032
|
var COVER_MODES = ["image", "gif"];
|
|
6014
6033
|
var EXPERIENCE_SOURCE = ["builder", "import", "remix"];
|
|
@@ -10285,15 +10304,22 @@ var categorySchema = external_exports.enum(CATEGORIES);
|
|
|
10285
10304
|
var visibilitySchema = external_exports.enum(VISIBILITY);
|
|
10286
10305
|
var coverModeSchema = external_exports.enum(COVER_MODES);
|
|
10287
10306
|
var sourceSchema = external_exports.enum(EXPERIENCE_SOURCE);
|
|
10288
|
-
var
|
|
10307
|
+
var KNOWN_TAGS = new Set(TAGS);
|
|
10308
|
+
var tagsSchema = external_exports.array(external_exports.string().max(40)).transform(
|
|
10289
10309
|
(tags) => Array.from(
|
|
10290
|
-
new Set(
|
|
10291
|
-
|
|
10310
|
+
new Set(
|
|
10311
|
+
tags.map((t) => t.replace(/^#/, "").trim().toLowerCase()).filter((t) => KNOWN_TAGS.has(t))
|
|
10312
|
+
)
|
|
10313
|
+
).slice(0, MAX_TAGS)
|
|
10292
10314
|
).default([]);
|
|
10293
10315
|
var publishSchema = external_exports.object({
|
|
10294
10316
|
title: external_exports.string().min(1).max(120),
|
|
10317
|
+
// Caption was removed from the publish UI; kept optional so old clients / the
|
|
10318
|
+
// CLI don't break and existing captions stay searchable.
|
|
10295
10319
|
caption: external_exports.string().max(2e3).default(""),
|
|
10296
|
-
|
|
10320
|
+
// Legacy: category is no longer chosen in the UI. The connector CLI may still
|
|
10321
|
+
// send it; publishExperience folds it into `tags` and leaves the column null.
|
|
10322
|
+
category: categorySchema.optional(),
|
|
10297
10323
|
tags: tagsSchema,
|
|
10298
10324
|
remixable: external_exports.boolean().default(true),
|
|
10299
10325
|
visibility: visibilitySchema.default("public"),
|
|
@@ -10406,7 +10432,8 @@ var aiConnectionSchema = external_exports.object({
|
|
|
10406
10432
|
});
|
|
10407
10433
|
var feedQuerySchema = external_exports.object({
|
|
10408
10434
|
tab: external_exports.enum(["explore", "following"]).default("explore"),
|
|
10409
|
-
category
|
|
10435
|
+
// Filter the explore feed to a single tag (replaces the old `category` param).
|
|
10436
|
+
tag: external_exports.string().optional(),
|
|
10410
10437
|
cursor: external_exports.string().optional(),
|
|
10411
10438
|
limit: external_exports.coerce.number().int().min(1).max(30).default(10)
|
|
10412
10439
|
});
|
|
@@ -11191,9 +11218,94 @@ function formatReport(result) {
|
|
|
11191
11218
|
out.push(` \u2022 \u2026and ${result.inlineWarnings.length - 12} more`);
|
|
11192
11219
|
}
|
|
11193
11220
|
}
|
|
11221
|
+
const usesBrowserStorage = [...result.warnings.map((w) => w.message), ...result.inlineWarnings].some(
|
|
11222
|
+
(m) => /BROWSER_STORAGE|localStorage|sessionStorage|indexedDB/i.test(m)
|
|
11223
|
+
);
|
|
11224
|
+
if (usesBrowserStorage) {
|
|
11225
|
+
out.push(
|
|
11226
|
+
"",
|
|
11227
|
+
"\u2139 Uses browser storage (blocked in the sandbox). Port it to `window.vibed.storage` \u2014 run `vibed storage` (or the vibed_storage_guide MCP tool) for the local + shared SDK."
|
|
11228
|
+
);
|
|
11229
|
+
}
|
|
11194
11230
|
return out.join("\n");
|
|
11195
11231
|
}
|
|
11196
11232
|
|
|
11233
|
+
// ../cli/src/storageGuide.ts
|
|
11234
|
+
var STORAGE_GUIDE = `# vibed storage \u2014 add persistence to a vibe (\`window.vibed.storage\`)
|
|
11235
|
+
|
|
11236
|
+
A vibe is ONE static sandboxed HTML file with no backend. \`localStorage\`,
|
|
11237
|
+
\`sessionStorage\`, \`indexedDB\`, and cookies are BLOCKED \u2014 they throw in the
|
|
11238
|
+
sandbox (\`vibed check\` warns \`BROWSER_STORAGE\`). For anything you'd persist,
|
|
11239
|
+
use the injected \`window.vibed.storage\` SDK instead. The platform is the
|
|
11240
|
+
backend; you write zero server code.
|
|
11241
|
+
|
|
11242
|
+
## Two rules that apply to EVERY call
|
|
11243
|
+
1. Every call returns a Promise that can reject \u2014 always guard it. When the vibe
|
|
11244
|
+
runs OUTSIDE vibed (e.g. a local file, your \`vibed draft\`/\`preview\`), calls
|
|
11245
|
+
reject with \`{ code: "no_host" }\`. The vibe must stay playable standalone, so
|
|
11246
|
+
treat missing storage as "no saved data yet", never a crash.
|
|
11247
|
+
2. WRITES require the viewer to be signed in. If not, they reject with
|
|
11248
|
+
\`{ code: "unauthenticated" }\` \u2014 catch it and show a gentle "sign in to save"
|
|
11249
|
+
nudge. READS of shared data work for everyone.
|
|
11250
|
+
|
|
11251
|
+
Publishing auto-detects SDK usage and turns storage on for the post \u2014 no flags.
|
|
11252
|
+
|
|
11253
|
+
## Tier 1 \u2014 \`vibed.storage.local\` (LOCAL: private, per-viewer, per-post)
|
|
11254
|
+
Each viewer gets their own private bucket for THIS post. Nobody else can read it.
|
|
11255
|
+
Use for: this player's high score, their settings, "resume where I left off".
|
|
11256
|
+
\`\`\`js
|
|
11257
|
+
await vibed.storage.local.set("best", 8200);
|
|
11258
|
+
const best = await vibed.storage.local.get("best"); // null if never set
|
|
11259
|
+
await vibed.storage.local.delete("best");
|
|
11260
|
+
\`\`\`
|
|
11261
|
+
|
|
11262
|
+
## Tier 2 \u2014 \`vibed.storage.shared\` (GLOBAL: one collective store for the whole post)
|
|
11263
|
+
Data every viewer of the post shares \u2014 leaderboards, votes, guestbooks, shared
|
|
11264
|
+
state. Organized into named "collections" (like tables) you invent.
|
|
11265
|
+
\`\`\`js
|
|
11266
|
+
// key/value doc with optimistic concurrency
|
|
11267
|
+
await vibed.storage.shared.set("state", "doc", { phase: "open" }); // \u2192 { value, version, ... }
|
|
11268
|
+
await vibed.storage.shared.set("state", "doc", next, { ifVersion: 3 }); // rejects { code: "conflict" }
|
|
11269
|
+
const doc = await vibed.storage.shared.get("state", "doc"); // null if missing
|
|
11270
|
+
await vibed.storage.shared.delete("state", "doc");
|
|
11271
|
+
|
|
11272
|
+
// append-only list + query (guestbook, bets, submissions)
|
|
11273
|
+
await vibed.storage.shared.append("bets", { pick: "Argentina", stake: 50 });
|
|
11274
|
+
const mine = await vibed.storage.shared.query("bets", { mine: true });
|
|
11275
|
+
const feed = await vibed.storage.shared.query("bets", { sort: "-created", limit: 30 });
|
|
11276
|
+
|
|
11277
|
+
// counter (poll / reactions)
|
|
11278
|
+
const { num } = await vibed.storage.shared.increment("votes", "argentina", 1);
|
|
11279
|
+
|
|
11280
|
+
// leaderboard: submit a score (keep the best), read the top, get MY rank
|
|
11281
|
+
await vibed.storage.shared.submit("leaderboard", { score, keep: "max", meta: { emoji: "\u{1F427}" } });
|
|
11282
|
+
const top = await vibed.storage.shared.query("leaderboard", { sort: "-num", limit: 10 });
|
|
11283
|
+
const rank = await vibed.storage.shared.rank("leaderboard"); // { rank, of, score } | null
|
|
11284
|
+
|
|
11285
|
+
// live updates (polling now): re-renders when the collection changes; returns an unsubscribe fn
|
|
11286
|
+
const off = vibed.storage.shared.subscribe("leaderboard", (r) => render(r.rows), { intervalMs: 4000 });
|
|
11287
|
+
\`\`\`
|
|
11288
|
+
|
|
11289
|
+
## Who is watching
|
|
11290
|
+
\`\`\`js
|
|
11291
|
+
const me = await vibed.storage.identity(); // { id, signedIn, name? } \u2014 stable per viewer; identify entries
|
|
11292
|
+
const m = await vibed.storage.meta(); // { postId, ... } \u2014 this post's context
|
|
11293
|
+
\`\`\`
|
|
11294
|
+
|
|
11295
|
+
## Limits (free tier \u2014 design within these)
|
|
11296
|
+
- Each value must be JSON-serializable and under the per-value byte cap
|
|
11297
|
+
(rejects \`{ code: "too_large" }\`); \`invalid\` if it can't be JSON-stringified.
|
|
11298
|
+
- shared collections are for structured game/app state, NOT open chat/DM/messaging.
|
|
11299
|
+
|
|
11300
|
+
## Porting a project that used browser storage (the \`BROWSER_STORAGE\` warning)
|
|
11301
|
+
1. Find every \`localStorage\`/\`sessionStorage\`/\`indexedDB\`/cookie use.
|
|
11302
|
+
2. Per-viewer state (settings, progress, personal best) \u2192 \`vibed.storage.local\`.
|
|
11303
|
+
3. Anything others should see (scores to compare, shared counters, submissions)
|
|
11304
|
+
\u2192 the matching \`vibed.storage.shared\` primitive above.
|
|
11305
|
+
4. Make every call \`await\` + \`try/catch\`, with a standalone fallback (no_host).
|
|
11306
|
+
5. Re-run \`vibed check\` \u2014 the \`BROWSER_STORAGE\` warning should be gone.
|
|
11307
|
+
`;
|
|
11308
|
+
|
|
11197
11309
|
// ../cli/src/login.ts
|
|
11198
11310
|
import { spawn } from "node:child_process";
|
|
11199
11311
|
import { platform } from "node:os";
|
|
@@ -11350,10 +11462,14 @@ function inferTitle(html, projectDir) {
|
|
|
11350
11462
|
const dir2 = basename(projectDir).replace(/[-_]+/g, " ").trim();
|
|
11351
11463
|
return (dir2 || "Untitled").slice(0, 120);
|
|
11352
11464
|
}
|
|
11353
|
-
function
|
|
11354
|
-
|
|
11355
|
-
const
|
|
11356
|
-
|
|
11465
|
+
function normalizeTags(tags, category) {
|
|
11466
|
+
const known = new Set(TAGS);
|
|
11467
|
+
const out = [];
|
|
11468
|
+
for (const t of [...tags ?? [], ...category ? [category] : []]) {
|
|
11469
|
+
const s = t.replace(/^#/, "").trim().toLowerCase();
|
|
11470
|
+
if (known.has(s) && !out.includes(s)) out.push(s);
|
|
11471
|
+
}
|
|
11472
|
+
return out.slice(0, MAX_TAGS);
|
|
11357
11473
|
}
|
|
11358
11474
|
async function importDraft(client, html) {
|
|
11359
11475
|
const r = await apiFetch(client, "POST", "/imports", { html });
|
|
@@ -11362,9 +11478,7 @@ async function importDraft(client, html) {
|
|
|
11362
11478
|
async function publishDraft(client, draftArtifactKey, meta, html, projectDir) {
|
|
11363
11479
|
const exp = await apiFetch(client, "POST", "/experiences", {
|
|
11364
11480
|
title: meta.title?.trim() || (html ? inferTitle(html, projectDir ?? ".") : "Untitled"),
|
|
11365
|
-
|
|
11366
|
-
category: normalizeCategory(meta.category),
|
|
11367
|
-
tags: meta.tags ?? [],
|
|
11481
|
+
tags: normalizeTags(meta.tags, meta.category),
|
|
11368
11482
|
remixable: meta.remixable ?? true,
|
|
11369
11483
|
visibility: meta.visibility ?? "public",
|
|
11370
11484
|
draftArtifactKey
|
|
@@ -11386,16 +11500,21 @@ async function deleteExperience(client, idOrUrl) {
|
|
|
11386
11500
|
}
|
|
11387
11501
|
|
|
11388
11502
|
// src/index.ts
|
|
11389
|
-
var SERVER_INFO = { name: "vibed", version: "0.1.
|
|
11503
|
+
var SERVER_INFO = { name: "vibed", version: "0.1.8" };
|
|
11390
11504
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
11391
|
-
var CATEGORIES2 = ["Game", "Invitation", "Story", "News", "Quiz", "Tool", "Art", "Other"];
|
|
11392
11505
|
var pendingLogin = null;
|
|
11393
11506
|
var str = (v, d = "") => typeof v === "string" ? v : d;
|
|
11394
11507
|
var bool = (v, d) => typeof v === "boolean" ? v : d;
|
|
11395
11508
|
var TOOLS = [
|
|
11509
|
+
{
|
|
11510
|
+
name: "vibed_storage_guide",
|
|
11511
|
+
description: "How to add PERSISTENCE to a vibe: leaderboards, votes/polls, saved progress/high scores, guestbooks, or any shared state. A vibe is one static sandboxed file \u2014 localStorage/sessionStorage/indexedDB/cookies and backend calls are BLOCKED \u2014 so use the injected window.vibed.storage SDK instead. Returns the full primer for both tiers: `local` (private, per-viewer) and `shared` (collective, per-post). Call this BEFORE writing any save/leaderboard/multiplayer feature, or when vibed_check warns BROWSER_STORAGE.",
|
|
11512
|
+
inputSchema: { type: "object", properties: {} },
|
|
11513
|
+
run: async () => STORAGE_GUIDE
|
|
11514
|
+
},
|
|
11396
11515
|
{
|
|
11397
11516
|
name: "vibed_check",
|
|
11398
|
-
description: "Bundle the project into a single HTML file and validate it against vibed's sandbox rules (no network/auth). Returns whether it's publishable and, if not, the blockers to fix. Run this before vibed_publish.",
|
|
11517
|
+
description: "Bundle the project into a single HTML file and validate it against vibed's sandbox rules (no network/auth). Returns whether it's publishable and, if not, the blockers to fix. Run this before vibed_publish. If it warns BROWSER_STORAGE (localStorage etc. throw in the sandbox), call vibed_storage_guide and port to window.vibed.storage.",
|
|
11399
11518
|
inputSchema: {
|
|
11400
11519
|
type: "object",
|
|
11401
11520
|
properties: {
|
|
@@ -11494,7 +11613,7 @@ Show it to the user. To publish it, call vibed_publish with draftKey: ${d.draftA
|
|
|
11494
11613
|
},
|
|
11495
11614
|
{
|
|
11496
11615
|
name: "vibed_publish",
|
|
11497
|
-
description: "Publish a vibed Experience. Pass draftKey (from vibed_draft) to promote an already-previewed draft \u2014 the recommended flow \u2014 or pass path to bundle + publish in one shot. Requires sign-in (vibed_whoami / vibed_login). Confirm the title and
|
|
11616
|
+
description: "Publish a vibed Experience. Pass draftKey (from vibed_draft) to promote an already-previewed draft \u2014 the recommended flow \u2014 or pass path to bundle + publish in one shot. Requires sign-in (vibed_whoami / vibed_login). Confirm the title and tags with the user first \u2014 publishing is public. Returns the published URL, or the blockers if it isn't vibeable.",
|
|
11498
11617
|
inputSchema: {
|
|
11499
11618
|
type: "object",
|
|
11500
11619
|
properties: {
|
|
@@ -11504,9 +11623,12 @@ Show it to the user. To publish it, call vibed_publish with draftKey: ${d.draftA
|
|
|
11504
11623
|
},
|
|
11505
11624
|
path: { type: "string", description: "Project directory (default: current directory)" },
|
|
11506
11625
|
title: { type: "string", description: "Title (default: page <title>/<h1>/folder name)" },
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11626
|
+
tags: {
|
|
11627
|
+
type: "array",
|
|
11628
|
+
items: { type: "string", enum: TAGS },
|
|
11629
|
+
maxItems: MAX_TAGS,
|
|
11630
|
+
description: `Up to ${MAX_TAGS} tags from: ${TAGS.join(", ")}`
|
|
11631
|
+
},
|
|
11510
11632
|
unlisted: { type: "boolean", description: "Publish unlisted instead of public" },
|
|
11511
11633
|
remixable: { type: "boolean", description: "Allow others to remix (default true)" },
|
|
11512
11634
|
entry: { type: "string" },
|
|
@@ -11520,9 +11642,7 @@ Show it to the user. To publish it, call vibed_publish with draftKey: ${d.draftA
|
|
|
11520
11642
|
const client = { apiBase: cfg.apiBase, token: cfg.token };
|
|
11521
11643
|
const meta = {
|
|
11522
11644
|
title: str(a.title) || void 0,
|
|
11523
|
-
category: str(a.category) || void 0,
|
|
11524
11645
|
tags: Array.isArray(a.tags) ? a.tags : void 0,
|
|
11525
|
-
caption: str(a.caption) || void 0,
|
|
11526
11646
|
remixable: bool(a.remixable, true),
|
|
11527
11647
|
visibility: bool(a.unlisted, false) ? "unlisted" : "public"
|
|
11528
11648
|
};
|
package/package.json
CHANGED