@amirhosseinnateghi/vibed-mcp 0.1.3 → 0.1.5
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 +210 -53
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1248,10 +1248,10 @@ var require_decode = __commonJS({
|
|
|
1248
1248
|
this.state = EntityDecoderState.NumericDecimal;
|
|
1249
1249
|
return this.stateNumericDecimal(str2, offset);
|
|
1250
1250
|
};
|
|
1251
|
-
EntityDecoder2.prototype.addToNumericResult = function(str2, start, end,
|
|
1251
|
+
EntityDecoder2.prototype.addToNumericResult = function(str2, start, end, base2) {
|
|
1252
1252
|
if (start !== end) {
|
|
1253
1253
|
var digitCount = end - start;
|
|
1254
|
-
this.result = this.result * Math.pow(
|
|
1254
|
+
this.result = this.result * Math.pow(base2, digitCount) + parseInt(str2.substr(start, digitCount), base2);
|
|
1255
1255
|
this.consumed += digitCount;
|
|
1256
1256
|
}
|
|
1257
1257
|
};
|
|
@@ -6037,7 +6037,19 @@ var config = {
|
|
|
6037
6037
|
// Light model that auto-tags the experience category. Falls back to a keyword
|
|
6038
6038
|
// heuristic if the call fails.
|
|
6039
6039
|
BUILDER_CLASSIFIER_MODEL: strEnv("BUILDER_CLASSIFIER_MODEL", "gemini-2.5-flash"),
|
|
6040
|
-
|
|
6040
|
+
// Speech-to-text model for the builder's voice input (docs/builder-architecture.md).
|
|
6041
|
+
// Gemini 2.5 Flash transcribes the recorded clip via GapGPT's OpenAI-compatible
|
|
6042
|
+
// chat API (an `input_audio` content part) — verified to accept the exact
|
|
6043
|
+
// formats browsers produce (Chrome webm/opus, Safari mp4). It's multilingual,
|
|
6044
|
+
// so Persian and English speech both work with no per-language config.
|
|
6045
|
+
BUILDER_TRANSCRIBE_MODEL: strEnv("BUILDER_TRANSCRIBE_MODEL", "gemini-2.5-flash"),
|
|
6046
|
+
// Upload cap for a voice clip. A minute of webm/opus is ~250KB; 8MB is ample
|
|
6047
|
+
// headroom while still bounding what one request can hand the model.
|
|
6048
|
+
BUILDER_TRANSCRIBE_MAX_BYTES: intEnv("BUILDER_TRANSCRIBE_MAX_BYTES", 8388608),
|
|
6049
|
+
// Output budget per model call. 8k proved too small in prod — a rich game is
|
|
6050
|
+
// ~7-8k tokens of file alone, and a truncated file used to slip through as a
|
|
6051
|
+
// text dump. 24576 was probed OK against every catalog model on GapGPT.
|
|
6052
|
+
BUILDER_MAX_TOKENS: intEnv("BUILDER_MAX_TOKENS", 24576),
|
|
6041
6053
|
BUILDER_MAX_MESSAGES: intEnv("BUILDER_MAX_MESSAGES", 30),
|
|
6042
6054
|
// Hard cap for a single build turn (across the model + one repair). The build
|
|
6043
6055
|
// runs detached from the request, so this — not the browser — is what stops a
|
|
@@ -9779,23 +9791,23 @@ var ZodEffects = class extends ZodType {
|
|
|
9779
9791
|
}
|
|
9780
9792
|
if (effect.type === "transform") {
|
|
9781
9793
|
if (ctx.common.async === false) {
|
|
9782
|
-
const
|
|
9794
|
+
const base2 = this._def.schema._parseSync({
|
|
9783
9795
|
data: ctx.data,
|
|
9784
9796
|
path: ctx.path,
|
|
9785
9797
|
parent: ctx
|
|
9786
9798
|
});
|
|
9787
|
-
if (!isValid(
|
|
9799
|
+
if (!isValid(base2))
|
|
9788
9800
|
return INVALID;
|
|
9789
|
-
const result = effect.transform(
|
|
9801
|
+
const result = effect.transform(base2.value, checkCtx);
|
|
9790
9802
|
if (result instanceof Promise) {
|
|
9791
9803
|
throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
|
|
9792
9804
|
}
|
|
9793
9805
|
return { status: status.value, value: result };
|
|
9794
9806
|
} else {
|
|
9795
|
-
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((
|
|
9796
|
-
if (!isValid(
|
|
9807
|
+
return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base2) => {
|
|
9808
|
+
if (!isValid(base2))
|
|
9797
9809
|
return INVALID;
|
|
9798
|
-
return Promise.resolve(effect.transform(
|
|
9810
|
+
return Promise.resolve(effect.transform(base2.value, checkCtx)).then((result) => ({
|
|
9799
9811
|
status: status.value,
|
|
9800
9812
|
value: result
|
|
9801
9813
|
}));
|
|
@@ -10190,8 +10202,13 @@ var STORAGE = {
|
|
|
10190
10202
|
// 256 KB
|
|
10191
10203
|
/** Max records returnable in one query page. */
|
|
10192
10204
|
QUERY_MAX_LIMIT: intEnv("STORE_QUERY_MAX_LIMIT", 100),
|
|
10193
|
-
/**
|
|
10194
|
-
|
|
10205
|
+
/**
|
|
10206
|
+
* Platform kill-switch for anonymous shared writes. ON by default: the real
|
|
10207
|
+
* gate is per-post owner opt-in (`anon_writes` + writePolicy `anyone`), so a
|
|
10208
|
+
* post only accepts guest writes when its owner turns them on. Set the env to
|
|
10209
|
+
* `false` to disable anon writes platform-wide regardless of per-post config.
|
|
10210
|
+
*/
|
|
10211
|
+
ANON_WRITES_AVAILABLE: boolEnv("STORE_ANON_WRITES_AVAILABLE", true)
|
|
10195
10212
|
};
|
|
10196
10213
|
var COLLECTION_RE = /^[a-z0-9][a-z0-9:_-]{0,63}$/;
|
|
10197
10214
|
var collectionSchemaStore = external_exports.string().regex(COLLECTION_RE, "invalid collection name");
|
|
@@ -10301,13 +10318,31 @@ var commentSchema = external_exports.object({
|
|
|
10301
10318
|
parentId: external_exports.string().uuid().optional()
|
|
10302
10319
|
});
|
|
10303
10320
|
var eventSchema = external_exports.object({
|
|
10304
|
-
|
|
10321
|
+
// "record"/"screenshot" fire when a viewer captures the vibe (see the in-iframe
|
|
10322
|
+
// bridge capture flow, docs/architecture.md §5.4). Plain-text `viewEvents.kind`
|
|
10323
|
+
// column, so new kinds need no migration.
|
|
10324
|
+
kind: external_exports.enum(["impression", "play", "play_time", "share", "record", "screenshot"]),
|
|
10305
10325
|
ms: external_exports.number().int().nonnegative().optional()
|
|
10306
10326
|
});
|
|
10307
10327
|
var createBuildSchema = external_exports.object({
|
|
10308
10328
|
mode: external_exports.enum(["ai", "remix"]),
|
|
10309
10329
|
remixParentId: external_exports.string().uuid().optional()
|
|
10310
10330
|
});
|
|
10331
|
+
var turnAttachmentSchema = external_exports.object({
|
|
10332
|
+
kind: external_exports.enum(["image", "audio", "file", "sketch"]),
|
|
10333
|
+
key: external_exports.string().max(300).default(""),
|
|
10334
|
+
name: external_exports.string().max(200),
|
|
10335
|
+
// NUL bytes break Postgres jsonb (and mean binary-not-text) — strip defensively.
|
|
10336
|
+
text: external_exports.string().max(5e4).transform((t) => t.replace(/\u0000/g, "")).optional()
|
|
10337
|
+
});
|
|
10338
|
+
var createTurnRequestSchema = external_exports.object({
|
|
10339
|
+
clientTurnId: external_exports.string().uuid(),
|
|
10340
|
+
content: external_exports.string().max(4e3).default(""),
|
|
10341
|
+
attachments: external_exports.array(turnAttachmentSchema).max(6).optional(),
|
|
10342
|
+
model: external_exports.string().max(100).optional()
|
|
10343
|
+
}).refine((v) => v.content.trim().length > 0 || (v.attachments?.length ?? 0) > 0, {
|
|
10344
|
+
message: "Send a message or an attachment."
|
|
10345
|
+
});
|
|
10311
10346
|
var builderAttachmentSchema = external_exports.object({
|
|
10312
10347
|
kind: external_exports.enum(["image", "file", "audio"]),
|
|
10313
10348
|
name: external_exports.string().max(200),
|
|
@@ -10408,27 +10443,52 @@ var notifPrefsPatchSchema = external_exports.object({
|
|
|
10408
10443
|
groups: external_exports.record(external_exports.enum(NOTIF_PREF_GROUPS), external_exports.boolean()).optional()
|
|
10409
10444
|
});
|
|
10410
10445
|
|
|
10411
|
-
// ../shared/src/
|
|
10412
|
-
var
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
//
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
// ── Subscription (locked until billing is enabled) — premium ──────
|
|
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 },
|
|
10427
|
-
{ id: "gpt-5.2-pro", label: "GPT-5.2 Pro", tier: "subscription", note: "Premium" },
|
|
10428
|
-
{ id: "gemini-3-pro-preview", label: "Gemini 3 Pro", tier: "subscription", note: "Premium", vision: true }
|
|
10446
|
+
// ../shared/src/builderEvents.ts
|
|
10447
|
+
var BUILD_EVENT_VERSION = 1;
|
|
10448
|
+
var BUILD_PHASES = [
|
|
10449
|
+
"queued",
|
|
10450
|
+
// accepted, waiting for a worker slot
|
|
10451
|
+
"thinking",
|
|
10452
|
+
// model connected, no visible output yet (keep-alives flowing)
|
|
10453
|
+
"writing",
|
|
10454
|
+
// streaming the vibe's code/prose
|
|
10455
|
+
"validating",
|
|
10456
|
+
// sandbox pass on the produced artifact
|
|
10457
|
+
"repairing"
|
|
10458
|
+
// one automatic fix round after a validation failure
|
|
10429
10459
|
];
|
|
10430
|
-
var
|
|
10431
|
-
var
|
|
10460
|
+
var base = { v: external_exports.literal(BUILD_EVENT_VERSION), turnId: external_exports.string() };
|
|
10461
|
+
var buildEventSchema = external_exports.discriminatedUnion("type", [
|
|
10462
|
+
/** Assistant prose delta (never contains the HTML code block). */
|
|
10463
|
+
external_exports.object({ ...base, type: external_exports.literal("token"), text: external_exports.string() }),
|
|
10464
|
+
/**
|
|
10465
|
+
* Honest heartbeat, ~every 2s while the turn runs: real elapsed ms, real
|
|
10466
|
+
* output-token count, and the executor's actual phase. The UI renders this
|
|
10467
|
+
* as a live counter — a number that moves because work is happening.
|
|
10468
|
+
*/
|
|
10469
|
+
external_exports.object({
|
|
10470
|
+
...base,
|
|
10471
|
+
type: external_exports.literal("progress"),
|
|
10472
|
+
phase: external_exports.enum(BUILD_PHASES),
|
|
10473
|
+
elapsedMs: external_exports.number(),
|
|
10474
|
+
tokensOut: external_exports.number()
|
|
10475
|
+
}),
|
|
10476
|
+
/** Friendly status chip ("index.html", "Fixing a couple of things…"). */
|
|
10477
|
+
external_exports.object({ ...base, type: external_exports.literal("status"), summary: external_exports.string() }),
|
|
10478
|
+
/** First-message category classification. */
|
|
10479
|
+
external_exports.object({ ...base, type: external_exports.literal("category"), category: external_exports.string() }),
|
|
10480
|
+
/** The validated draft landed. Preview loads it from the draft URL. */
|
|
10481
|
+
external_exports.object({ ...base, type: external_exports.literal("artifact"), artifactKey: external_exports.string() }),
|
|
10482
|
+
/** Terminal: failure with taxonomy code + human message. */
|
|
10483
|
+
external_exports.object({
|
|
10484
|
+
...base,
|
|
10485
|
+
type: external_exports.literal("error"),
|
|
10486
|
+
code: external_exports.string(),
|
|
10487
|
+
message: external_exports.string()
|
|
10488
|
+
}),
|
|
10489
|
+
/** Terminal: success. */
|
|
10490
|
+
external_exports.object({ ...base, type: external_exports.literal("done") })
|
|
10491
|
+
]);
|
|
10432
10492
|
|
|
10433
10493
|
// ../shared/src/i18n.ts
|
|
10434
10494
|
var en = {
|
|
@@ -10462,6 +10522,21 @@ var en = {
|
|
|
10462
10522
|
"share.title": "Share",
|
|
10463
10523
|
"share.copyLink": "Copy link",
|
|
10464
10524
|
"share.copied": "Link copied",
|
|
10525
|
+
"capture.record": "Record",
|
|
10526
|
+
"capture.screenshot": "Screenshot",
|
|
10527
|
+
"capture.recording": "Recording",
|
|
10528
|
+
"capture.stop": "Stop",
|
|
10529
|
+
"capture.preparing": "Preparing\u2026",
|
|
10530
|
+
"capture.processing": "Finishing\u2026",
|
|
10531
|
+
"capture.preview.videoTitle": "Your clip",
|
|
10532
|
+
"capture.preview.imageTitle": "Your screenshot",
|
|
10533
|
+
"capture.save": "Save",
|
|
10534
|
+
"capture.shareVia": "Share via\u2026",
|
|
10535
|
+
"capture.retry": "Try again",
|
|
10536
|
+
"capture.error": "Capture failed \u2014 try again.",
|
|
10537
|
+
"capture.allowShare": "Allow \u201CShare this tab\u201D to record \u2014 it captures just the vibe.",
|
|
10538
|
+
"capture.pickTab": "Choose \u201CThis Tab\u201D so it records only the vibe.",
|
|
10539
|
+
"capture.unsupported": `This ${ITEM.one} can't be captured yet. Try again in a moment.`,
|
|
10465
10540
|
"create.title": "Create",
|
|
10466
10541
|
"create.buildWithAI": "Build with AI",
|
|
10467
10542
|
"create.import": "Import",
|
|
@@ -10653,6 +10728,15 @@ function validate(html, opts) {
|
|
|
10653
10728
|
errors.push({ code: "NOT_HTML", message: "Content is not parseable as HTML." });
|
|
10654
10729
|
return { ok: false, errors, warnings };
|
|
10655
10730
|
}
|
|
10731
|
+
const scriptOpens = (html.match(/<script\b/gi) ?? []).length;
|
|
10732
|
+
const scriptCloses = (html.match(/<\/script\s*>/gi) ?? []).length;
|
|
10733
|
+
if (scriptOpens > scriptCloses) {
|
|
10734
|
+
errors.push({
|
|
10735
|
+
code: "TRUNCATED_DOC",
|
|
10736
|
+
message: "The file ends mid-<script> \u2014 it looks cut off. Regenerate or paste the complete file."
|
|
10737
|
+
});
|
|
10738
|
+
return { ok: false, errors, warnings };
|
|
10739
|
+
}
|
|
10656
10740
|
if (on("frames") && root.querySelector("iframe, frame, object, embed")) {
|
|
10657
10741
|
errors.push({
|
|
10658
10742
|
code: "FRAME_TAG",
|
|
@@ -11147,7 +11231,7 @@ async function apiFetch({ apiBase, token }, method, path, body) {
|
|
|
11147
11231
|
import { homedir } from "node:os";
|
|
11148
11232
|
import { join as join2 } from "node:path";
|
|
11149
11233
|
import { mkdir, readFile as readFile2, writeFile, chmod } from "node:fs/promises";
|
|
11150
|
-
var DEFAULT_API = "https://
|
|
11234
|
+
var DEFAULT_API = "https://vibed.city";
|
|
11151
11235
|
var dir = () => join2(homedir(), ".vibed");
|
|
11152
11236
|
var file = () => join2(dir(), "config.json");
|
|
11153
11237
|
async function readFileConfig() {
|
|
@@ -11256,22 +11340,38 @@ function normalizeCategory(input) {
|
|
|
11256
11340
|
const match = CATEGORIES.find((c) => c.toLowerCase() === input.toLowerCase());
|
|
11257
11341
|
return match ?? "Other";
|
|
11258
11342
|
}
|
|
11259
|
-
async function
|
|
11260
|
-
const
|
|
11343
|
+
async function importDraft(client, html) {
|
|
11344
|
+
const r = await apiFetch(client, "POST", "/imports", { html });
|
|
11345
|
+
return { draftArtifactKey: r.draftArtifactKey, previewUrl: r.previewUrl };
|
|
11346
|
+
}
|
|
11347
|
+
async function publishDraft(client, draftArtifactKey, meta, html, projectDir) {
|
|
11261
11348
|
const exp = await apiFetch(client, "POST", "/experiences", {
|
|
11262
|
-
title: meta.title?.trim() || inferTitle(html, projectDir),
|
|
11349
|
+
title: meta.title?.trim() || (html ? inferTitle(html, projectDir ?? ".") : "Untitled"),
|
|
11263
11350
|
caption: meta.caption ?? "",
|
|
11264
11351
|
category: normalizeCategory(meta.category),
|
|
11265
11352
|
tags: meta.tags ?? [],
|
|
11266
11353
|
remixable: meta.remixable ?? true,
|
|
11267
11354
|
visibility: meta.visibility ?? "public",
|
|
11268
|
-
draftArtifactKey
|
|
11355
|
+
draftArtifactKey
|
|
11269
11356
|
});
|
|
11270
11357
|
return { id: exp.id, status: exp.status, url: `${client.apiBase}/e/${exp.id}` };
|
|
11271
11358
|
}
|
|
11359
|
+
async function publishArtifact(client, html, meta, projectDir) {
|
|
11360
|
+
const { draftArtifactKey } = await importDraft(client, html);
|
|
11361
|
+
return publishDraft(client, draftArtifactKey, meta, html, projectDir);
|
|
11362
|
+
}
|
|
11363
|
+
function experienceIdFrom(input) {
|
|
11364
|
+
const m = input.match(/\/e\/([0-9a-f-]{36})/i);
|
|
11365
|
+
return (m?.[1] ?? input.trim()).toLowerCase();
|
|
11366
|
+
}
|
|
11367
|
+
async function deleteExperience(client, idOrUrl) {
|
|
11368
|
+
const id = experienceIdFrom(idOrUrl);
|
|
11369
|
+
await apiFetch(client, "DELETE", `/experiences/${id}`);
|
|
11370
|
+
return id;
|
|
11371
|
+
}
|
|
11272
11372
|
|
|
11273
11373
|
// src/index.ts
|
|
11274
|
-
var SERVER_INFO = { name: "vibed", version: "0.1.
|
|
11374
|
+
var SERVER_INFO = { name: "vibed", version: "0.1.5" };
|
|
11275
11375
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
11276
11376
|
var CATEGORIES2 = ["Game", "Invitation", "Story", "News", "Quiz", "Tool", "Art", "Other"];
|
|
11277
11377
|
var pendingLogin = null;
|
|
@@ -11346,12 +11446,47 @@ After the user approves, call vibed_login_wait.`;
|
|
|
11346
11446
|
}
|
|
11347
11447
|
}
|
|
11348
11448
|
},
|
|
11449
|
+
{
|
|
11450
|
+
name: "vibed_draft",
|
|
11451
|
+
description: "Bundle the project and upload it as a PRIVATE draft, returning a hosted, sandboxed PREVIEW link (\u2026/p/<id>/) and a draftKey. Publishes nothing \u2014 the link is in no feed, profile, or search; only someone with it can open it. Show the preview to the user, then call vibed_publish with the returned draftKey once they approve. Requires sign-in.",
|
|
11452
|
+
inputSchema: {
|
|
11453
|
+
type: "object",
|
|
11454
|
+
properties: {
|
|
11455
|
+
path: { type: "string", description: "Project directory (default: current directory)" },
|
|
11456
|
+
entry: { type: "string" },
|
|
11457
|
+
build: { type: "boolean" },
|
|
11458
|
+
api: { type: "string", description: "API base URL (default: production)" }
|
|
11459
|
+
}
|
|
11460
|
+
},
|
|
11461
|
+
run: async (a) => {
|
|
11462
|
+
const dir2 = resolve2(str(a.path, "."));
|
|
11463
|
+
const cfg = await loadConfig({ api: str(a.api) || void 0 });
|
|
11464
|
+
if (!cfg.token) return "Not signed in. Use vibed_login first.";
|
|
11465
|
+
const result = await bundleProject(dir2, {
|
|
11466
|
+
entry: str(a.entry) || void 0,
|
|
11467
|
+
build: bool(a.build, true),
|
|
11468
|
+
onLog: (m) => process.stderr.write(m + "\n")
|
|
11469
|
+
});
|
|
11470
|
+
if (!result.ok || !result.html) {
|
|
11471
|
+
return `No preview \u2014 the project isn't vibeable yet.
|
|
11472
|
+
|
|
11473
|
+
${formatReport(result)}`;
|
|
11474
|
+
}
|
|
11475
|
+
const d = await importDraft({ apiBase: cfg.apiBase, token: cfg.token }, result.html);
|
|
11476
|
+
return `\u2713 Private preview (not published): ${d.previewUrl ?? "(no preview URL)"}
|
|
11477
|
+
Show it to the user. To publish it, call vibed_publish with draftKey: ${d.draftArtifactKey}`;
|
|
11478
|
+
}
|
|
11479
|
+
},
|
|
11349
11480
|
{
|
|
11350
11481
|
name: "vibed_publish",
|
|
11351
|
-
description: "
|
|
11482
|
+
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 category with the user first \u2014 publishing is public. Returns the published URL, or the blockers if it isn't vibeable.",
|
|
11352
11483
|
inputSchema: {
|
|
11353
11484
|
type: "object",
|
|
11354
11485
|
properties: {
|
|
11486
|
+
draftKey: {
|
|
11487
|
+
type: "string",
|
|
11488
|
+
description: "Promote this draft (from vibed_draft) instead of bundling path"
|
|
11489
|
+
},
|
|
11355
11490
|
path: { type: "string", description: "Project directory (default: current directory)" },
|
|
11356
11491
|
title: { type: "string", description: "Title (default: page <title>/<h1>/folder name)" },
|
|
11357
11492
|
category: { type: "string", enum: CATEGORIES2, description: "Experience category" },
|
|
@@ -11365,9 +11500,23 @@ After the user approves, call vibed_login_wait.`;
|
|
|
11365
11500
|
}
|
|
11366
11501
|
},
|
|
11367
11502
|
run: async (a) => {
|
|
11368
|
-
const dir2 = resolve2(str(a.path, "."));
|
|
11369
11503
|
const cfg = await loadConfig({ api: str(a.api) || void 0 });
|
|
11370
11504
|
if (!cfg.token) return "Not signed in. Use vibed_login first.";
|
|
11505
|
+
const client = { apiBase: cfg.apiBase, token: cfg.token };
|
|
11506
|
+
const meta = {
|
|
11507
|
+
title: str(a.title) || void 0,
|
|
11508
|
+
category: str(a.category) || void 0,
|
|
11509
|
+
tags: Array.isArray(a.tags) ? a.tags : void 0,
|
|
11510
|
+
caption: str(a.caption) || void 0,
|
|
11511
|
+
remixable: bool(a.remixable, true),
|
|
11512
|
+
visibility: bool(a.unlisted, false) ? "unlisted" : "public"
|
|
11513
|
+
};
|
|
11514
|
+
const draftKey = str(a.draftKey);
|
|
11515
|
+
if (draftKey) {
|
|
11516
|
+
const outcome2 = await publishDraft(client, draftKey, meta);
|
|
11517
|
+
return `\u2713 Published (${outcome2.status}): ${outcome2.url}`;
|
|
11518
|
+
}
|
|
11519
|
+
const dir2 = resolve2(str(a.path, "."));
|
|
11371
11520
|
const result = await bundleProject(dir2, {
|
|
11372
11521
|
entry: str(a.entry) || void 0,
|
|
11373
11522
|
build: bool(a.build, true),
|
|
@@ -11378,21 +11527,29 @@ After the user approves, call vibed_login_wait.`;
|
|
|
11378
11527
|
|
|
11379
11528
|
${formatReport(result)}`;
|
|
11380
11529
|
}
|
|
11381
|
-
const outcome = await publishArtifact(
|
|
11382
|
-
{ apiBase: cfg.apiBase, token: cfg.token },
|
|
11383
|
-
result.html,
|
|
11384
|
-
{
|
|
11385
|
-
title: str(a.title) || void 0,
|
|
11386
|
-
category: str(a.category) || void 0,
|
|
11387
|
-
tags: Array.isArray(a.tags) ? a.tags : void 0,
|
|
11388
|
-
caption: str(a.caption) || void 0,
|
|
11389
|
-
remixable: bool(a.remixable, true),
|
|
11390
|
-
visibility: bool(a.unlisted, false) ? "unlisted" : "public"
|
|
11391
|
-
},
|
|
11392
|
-
dir2
|
|
11393
|
-
);
|
|
11530
|
+
const outcome = await publishArtifact(client, result.html, meta, dir2);
|
|
11394
11531
|
return `\u2713 Published (${outcome.status}): ${outcome.url}`;
|
|
11395
11532
|
}
|
|
11533
|
+
},
|
|
11534
|
+
{
|
|
11535
|
+
name: "vibed_delete",
|
|
11536
|
+
description: "Delete one of your vibed posts, by experience id or a \u2026/e/<id> share URL. Owner-only (the server rejects deleting someone else's post). Destructive \u2014 confirm the specific post with the user first. Requires sign-in.",
|
|
11537
|
+
inputSchema: {
|
|
11538
|
+
type: "object",
|
|
11539
|
+
properties: {
|
|
11540
|
+
target: { type: "string", description: "Experience id or https://\u2026/e/<id> URL" },
|
|
11541
|
+
api: { type: "string", description: "API base URL (default: production)" }
|
|
11542
|
+
},
|
|
11543
|
+
required: ["target"]
|
|
11544
|
+
},
|
|
11545
|
+
run: async (a) => {
|
|
11546
|
+
const target = str(a.target);
|
|
11547
|
+
if (!target) return "Provide the post's id or its \u2026/e/<id> URL.";
|
|
11548
|
+
const cfg = await loadConfig({ api: str(a.api) || void 0 });
|
|
11549
|
+
if (!cfg.token) return "Not signed in. Use vibed_login first.";
|
|
11550
|
+
const id = await deleteExperience({ apiBase: cfg.apiBase, token: cfg.token }, target);
|
|
11551
|
+
return `\u2713 Deleted post ${id}.`;
|
|
11552
|
+
}
|
|
11396
11553
|
}
|
|
11397
11554
|
];
|
|
11398
11555
|
function write(msg) {
|
package/package.json
CHANGED