vslides 1.0.9 → 1.0.11
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/cli.js +116 -26
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3126,8 +3126,8 @@ function clearCLIAuth() {
|
|
|
3126
3126
|
if ((0, import_node_fs.existsSync)(AUTH_FILE)) {
|
|
3127
3127
|
try {
|
|
3128
3128
|
(0, import_node_fs.writeFileSync)(AUTH_FILE, "{}");
|
|
3129
|
-
const { unlinkSync } = require("node:fs");
|
|
3130
|
-
|
|
3129
|
+
const { unlinkSync: unlinkSync2 } = require("node:fs");
|
|
3130
|
+
unlinkSync2(AUTH_FILE);
|
|
3131
3131
|
} catch {
|
|
3132
3132
|
}
|
|
3133
3133
|
}
|
|
@@ -3204,6 +3204,18 @@ async function getShare(slug, token) {
|
|
|
3204
3204
|
async function getJoin(code) {
|
|
3205
3205
|
return request(`/api/share/${code}`);
|
|
3206
3206
|
}
|
|
3207
|
+
async function authenticateToShare(code, cliAuthToken) {
|
|
3208
|
+
return request(`/api/share/${code}/authenticate`, {
|
|
3209
|
+
method: "POST",
|
|
3210
|
+
headers: {
|
|
3211
|
+
"X-CLI-Auth-Token": cliAuthToken,
|
|
3212
|
+
"Content-Type": "application/json"
|
|
3213
|
+
},
|
|
3214
|
+
body: JSON.stringify({}),
|
|
3215
|
+
skipAuthCheck: true
|
|
3216
|
+
// Don't clear auth on failure (might be session issue, not auth issue)
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3207
3219
|
async function getSlides(slug, token) {
|
|
3208
3220
|
return request("/api/slides", {
|
|
3209
3221
|
headers: {
|
|
@@ -3354,10 +3366,13 @@ async function reconnectSession(slug, cliAuthToken) {
|
|
|
3354
3366
|
// src/lib/config.ts
|
|
3355
3367
|
var import_node_fs2 = require("node:fs");
|
|
3356
3368
|
var import_node_path2 = require("node:path");
|
|
3369
|
+
var import_node_os2 = require("node:os");
|
|
3357
3370
|
var CONFIG_FILE = ".vslides.json";
|
|
3358
3371
|
var GUIDE_FILE = ".vslides-guide.md";
|
|
3359
3372
|
var SLIDES_FILE = "slides.md";
|
|
3360
3373
|
var UPSTREAM_FILE = "upstream.md";
|
|
3374
|
+
var VSLIDES_DIR = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".vslides");
|
|
3375
|
+
var PENDING_LOGIN_FILE = (0, import_node_path2.join)(VSLIDES_DIR, "pending-login.json");
|
|
3361
3376
|
var GUIDE_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
3362
3377
|
function getConfigPath() {
|
|
3363
3378
|
return (0, import_node_path2.join)(process.cwd(), CONFIG_FILE);
|
|
@@ -3452,6 +3467,34 @@ function requireToken() {
|
|
|
3452
3467
|
}
|
|
3453
3468
|
return { config, token: config.token };
|
|
3454
3469
|
}
|
|
3470
|
+
function ensureVslidesDir() {
|
|
3471
|
+
if (!(0, import_node_fs2.existsSync)(VSLIDES_DIR)) {
|
|
3472
|
+
(0, import_node_fs2.mkdirSync)(VSLIDES_DIR, { mode: 448 });
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
function readPendingLogin() {
|
|
3476
|
+
if (!(0, import_node_fs2.existsSync)(PENDING_LOGIN_FILE)) {
|
|
3477
|
+
return null;
|
|
3478
|
+
}
|
|
3479
|
+
try {
|
|
3480
|
+
const content = (0, import_node_fs2.readFileSync)(PENDING_LOGIN_FILE, "utf-8");
|
|
3481
|
+
return JSON.parse(content);
|
|
3482
|
+
} catch {
|
|
3483
|
+
return null;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
function writePendingLogin(pending) {
|
|
3487
|
+
ensureVslidesDir();
|
|
3488
|
+
(0, import_node_fs2.writeFileSync)(PENDING_LOGIN_FILE, JSON.stringify(pending, null, 2) + "\n");
|
|
3489
|
+
}
|
|
3490
|
+
function clearPendingLogin() {
|
|
3491
|
+
if ((0, import_node_fs2.existsSync)(PENDING_LOGIN_FILE)) {
|
|
3492
|
+
try {
|
|
3493
|
+
(0, import_node_fs2.unlinkSync)(PENDING_LOGIN_FILE);
|
|
3494
|
+
} catch {
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3455
3498
|
|
|
3456
3499
|
// src/lib/output.ts
|
|
3457
3500
|
function success(message) {
|
|
@@ -3663,6 +3706,29 @@ async function join3(urlOrCode) {
|
|
|
3663
3706
|
code = match[1];
|
|
3664
3707
|
}
|
|
3665
3708
|
}
|
|
3709
|
+
const cachedAuth = getCachedAuth();
|
|
3710
|
+
if (cachedAuth && isAuthValid(cachedAuth)) {
|
|
3711
|
+
const authResult = await authenticateToShare(code, cachedAuth.token);
|
|
3712
|
+
if (authResult.ok) {
|
|
3713
|
+
const { slug: slug2, pollSecret: pollSecret2, token, previewUrl, email } = authResult.data;
|
|
3714
|
+
writeConfig({
|
|
3715
|
+
slug: slug2,
|
|
3716
|
+
pollSecret: pollSecret2,
|
|
3717
|
+
token,
|
|
3718
|
+
previewUrl: previewUrl || `${getBaseUrl()}/slides/${slug2}`
|
|
3719
|
+
});
|
|
3720
|
+
success(`Joined session: ${slug2}`);
|
|
3721
|
+
url("USER", email);
|
|
3722
|
+
url("PREVIEW_URL", previewUrl || `${getBaseUrl()}/slides/${slug2}`);
|
|
3723
|
+
newline();
|
|
3724
|
+
info("You are authenticated. Run: vslides get");
|
|
3725
|
+
return;
|
|
3726
|
+
}
|
|
3727
|
+
if (authResult.status !== 400 && authResult.status !== 401 && authResult.status !== 403) {
|
|
3728
|
+
error(`Failed to join session: ${JSON.stringify(authResult.data)}`);
|
|
3729
|
+
process.exit(ExitCode.NetworkError);
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3666
3732
|
const result = await getJoin(code);
|
|
3667
3733
|
if (!result.ok) {
|
|
3668
3734
|
error(`Failed to join session: ${JSON.stringify(result.data)}`);
|
|
@@ -3674,12 +3740,12 @@ async function join3(urlOrCode) {
|
|
|
3674
3740
|
pollSecret,
|
|
3675
3741
|
previewUrl: `${getBaseUrl()}/slides/${slug}`
|
|
3676
3742
|
});
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3743
|
+
success(`Joined session: ${slug}`);
|
|
3744
|
+
url("VERIFY_URL", joinUrl);
|
|
3745
|
+
newline();
|
|
3746
|
+
info("ACTION_REQUIRED: Open the URL above in your browser to sign in");
|
|
3747
|
+
info("After signing in, run: vslides check --wait");
|
|
3748
|
+
info("Then run: vslides get");
|
|
3683
3749
|
}
|
|
3684
3750
|
|
|
3685
3751
|
// src/commands/share.ts
|
|
@@ -3690,14 +3756,14 @@ async function share() {
|
|
|
3690
3756
|
error(`Failed to get share info: ${JSON.stringify(result.data)}`);
|
|
3691
3757
|
process.exit(ExitCode.NetworkError);
|
|
3692
3758
|
}
|
|
3693
|
-
const { joinUrl } = result.data;
|
|
3694
|
-
|
|
3759
|
+
const { joinUrl, code } = result.data;
|
|
3760
|
+
info("Share with your co-editor:");
|
|
3695
3761
|
newline();
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
info("
|
|
3699
|
-
|
|
3700
|
-
info(
|
|
3762
|
+
url("LINK", joinUrl);
|
|
3763
|
+
newline();
|
|
3764
|
+
info("OR if they use Claude Code, tell them to paste this:");
|
|
3765
|
+
newline();
|
|
3766
|
+
info(` /vslides join ${code}`);
|
|
3701
3767
|
}
|
|
3702
3768
|
|
|
3703
3769
|
// src/commands/list.ts
|
|
@@ -4159,11 +4225,15 @@ async function upload(file) {
|
|
|
4159
4225
|
error(`Failed to upload: ${JSON.stringify(result.data)}`);
|
|
4160
4226
|
process.exit(ExitCode.NetworkError);
|
|
4161
4227
|
}
|
|
4162
|
-
|
|
4228
|
+
success(`Uploaded: ${result.data.filename}`);
|
|
4163
4229
|
info(`PATH: ${result.data.path}`);
|
|
4164
4230
|
newline();
|
|
4165
|
-
info("Use in slides
|
|
4231
|
+
info("Use in slides frontmatter:");
|
|
4166
4232
|
info(` image: ${result.data.path}`);
|
|
4233
|
+
info(` grayscale: 0 # for full color (default is grayscale)`);
|
|
4234
|
+
newline();
|
|
4235
|
+
info("For avatar images in 1-title layout:");
|
|
4236
|
+
info(` subtitle1Image: ${result.data.path}`);
|
|
4167
4237
|
}
|
|
4168
4238
|
|
|
4169
4239
|
// src/commands/export.ts
|
|
@@ -4251,7 +4321,7 @@ var POLL_TIMEOUT2 = 12e4;
|
|
|
4251
4321
|
function sleep3(ms) {
|
|
4252
4322
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4253
4323
|
}
|
|
4254
|
-
async function login() {
|
|
4324
|
+
async function login(options = {}) {
|
|
4255
4325
|
const cachedAuth = getCachedAuth();
|
|
4256
4326
|
if (cachedAuth && isAuthValid(cachedAuth)) {
|
|
4257
4327
|
const daysLeft = Math.ceil((cachedAuth.expiresAt - Date.now()) / (24 * 60 * 60 * 1e3));
|
|
@@ -4259,6 +4329,11 @@ async function login() {
|
|
|
4259
4329
|
info("Run `vslides logout` to sign out first.");
|
|
4260
4330
|
return;
|
|
4261
4331
|
}
|
|
4332
|
+
const pendingLogin = readPendingLogin();
|
|
4333
|
+
if (pendingLogin && options.wait) {
|
|
4334
|
+
await pollForAuth(pendingLogin.slug, pendingLogin.pollSecret);
|
|
4335
|
+
return;
|
|
4336
|
+
}
|
|
4262
4337
|
const createResult = await createSession();
|
|
4263
4338
|
if (!createResult.ok) {
|
|
4264
4339
|
error(`Failed to create session: ${JSON.stringify(createResult.data)}`);
|
|
@@ -4266,11 +4341,22 @@ async function login() {
|
|
|
4266
4341
|
}
|
|
4267
4342
|
const { slug, pollSecret, verifyUrl } = createResult.data;
|
|
4268
4343
|
const authUrl = `${verifyUrl}?cliAuth=true`;
|
|
4269
|
-
|
|
4270
|
-
url("
|
|
4344
|
+
writePendingLogin({ slug, pollSecret, authUrl });
|
|
4345
|
+
url("VERIFY_URL", authUrl);
|
|
4271
4346
|
newline();
|
|
4272
|
-
info("
|
|
4347
|
+
info("ACTION_REQUIRED: Open the URL above in your browser to sign in");
|
|
4348
|
+
if (options.wait) {
|
|
4349
|
+
info("Waiting for authentication (2 minute timeout)...");
|
|
4350
|
+
newline();
|
|
4351
|
+
await pollForAuth(slug, pollSecret);
|
|
4352
|
+
} else {
|
|
4353
|
+
newline();
|
|
4354
|
+
info("Run `vslides login --wait` to wait for authentication to complete");
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
async function pollForAuth(slug, pollSecret) {
|
|
4273
4358
|
const startTime = Date.now();
|
|
4359
|
+
let pollCount = 0;
|
|
4274
4360
|
while (true) {
|
|
4275
4361
|
const result = await getSession(slug, pollSecret);
|
|
4276
4362
|
if (!result.ok) {
|
|
@@ -4282,21 +4368,25 @@ async function login() {
|
|
|
4282
4368
|
const validateResult = await validateCLIAuth(cliAuthToken);
|
|
4283
4369
|
if (validateResult.ok && validateResult.data.valid && validateResult.data.email && validateResult.data.expiresAt) {
|
|
4284
4370
|
saveCLIAuth(cliAuthToken, validateResult.data.email, validateResult.data.expiresAt);
|
|
4285
|
-
|
|
4371
|
+
clearPendingLogin();
|
|
4286
4372
|
success(`Logged in as ${validateResult.data.email} (valid for 7 days)`);
|
|
4287
4373
|
process.exit(ExitCode.Success);
|
|
4288
4374
|
}
|
|
4289
4375
|
}
|
|
4290
4376
|
if (Date.now() - startTime > POLL_TIMEOUT2) {
|
|
4291
|
-
newline();
|
|
4292
4377
|
error("Timeout waiting for authentication");
|
|
4293
4378
|
instructions([
|
|
4294
|
-
"Open the
|
|
4379
|
+
"Open the VERIFY_URL in your browser",
|
|
4295
4380
|
"Sign in with your @vercel.com account",
|
|
4296
|
-
"Run `vslides login` again"
|
|
4381
|
+
"Run `vslides login --wait` again"
|
|
4297
4382
|
]);
|
|
4298
4383
|
process.exit(ExitCode.Conflict);
|
|
4299
4384
|
}
|
|
4385
|
+
pollCount++;
|
|
4386
|
+
if (pollCount % 10 === 0) {
|
|
4387
|
+
const secondsLeft = Math.ceil((POLL_TIMEOUT2 - (Date.now() - startTime)) / 1e3);
|
|
4388
|
+
info(`Still waiting... (${secondsLeft}s remaining)`);
|
|
4389
|
+
}
|
|
4300
4390
|
await sleep3(POLL_INTERVAL2);
|
|
4301
4391
|
}
|
|
4302
4392
|
}
|
|
@@ -4361,7 +4451,7 @@ function wrapCommand(fn) {
|
|
|
4361
4451
|
};
|
|
4362
4452
|
}
|
|
4363
4453
|
program.name("vslides").description("CLI for Vercel Slides API").version("1.0.0");
|
|
4364
|
-
program.command("login").description("Authenticate with Vercel (valid for 7 days)").action(wrapCommand(login));
|
|
4454
|
+
program.command("login").description("Authenticate with Vercel (valid for 7 days)").option("--wait", "Wait for authentication to complete (2 min timeout)").action(wrapCommand((options) => login(options)));
|
|
4365
4455
|
program.command("logout").description("Sign out and revoke credentials").action(wrapCommand(logout));
|
|
4366
4456
|
program.command("whoami").description("Show current authentication status").option("--validate", "Validate token with server").action(wrapCommand((options) => whoami(options)));
|
|
4367
4457
|
program.command("init").description("Create a new session").action(wrapCommand(init));
|