agentreel 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -4,6 +4,8 @@ Turn your Claude Code sessions into viral demo videos.
4
4
 
5
5
  https://github.com/user-attachments/assets/474fd85d-3b35-48f4-82b8-1b337840fb51
6
6
 
7
+ > 🔊 Turn on sound
8
+
7
9
  ## Install
8
10
 
9
11
  ```bash
package/bin/agentreel.mjs CHANGED
@@ -280,11 +280,15 @@ async function renderVideo(props, output, musicPath) {
280
280
  webpackOverride: (config) => config,
281
281
  });
282
282
 
283
- console.error(" Selecting composition...");
283
+ console.error(" Preparing renderer...");
284
284
  const composition = await selectComposition({
285
285
  serveUrl,
286
286
  id: "CastVideo",
287
287
  inputProps: props,
288
+ onBrowserDownload: () => {
289
+ console.error(" Downloading renderer (one-time, ~90MB)...");
290
+ return () => {}; // suppress progress logs
291
+ },
288
292
  });
289
293
 
290
294
  console.error(" Rendering...");
@@ -302,46 +306,18 @@ async function renderVideo(props, output, musicPath) {
302
306
 
303
307
  // ── Upload + Share ──────────────────────────────────────────
304
308
 
305
- async function uploadToStreamable(filePath) {
306
- const { FormData, File } = await import("node:buffer")
307
- .then(() => globalThis)
308
- .catch(() => globalThis);
309
-
310
- const fileBuffer = readFileSync(filePath);
311
- const fileName = basename(filePath);
312
-
313
- // Use multipart form upload via fetch
314
- const boundary = "----agentreel" + Date.now();
315
- const CRLF = "\r\n";
316
-
317
- const header = [
318
- `--${boundary}`,
319
- `Content-Disposition: form-data; name="file"; filename="${fileName}"`,
320
- "Content-Type: video/mp4",
321
- "",
322
- ].join(CRLF);
323
-
324
- const footer = `${CRLF}--${boundary}--${CRLF}`;
325
-
326
- const headerBuf = Buffer.from(header + CRLF);
327
- const footerBuf = Buffer.from(footer);
328
- const body = Buffer.concat([headerBuf, fileBuffer, footerBuf]);
329
-
330
- const resp = await fetch("https://api.streamable.com/upload", {
331
- method: "POST",
332
- headers: {
333
- "Content-Type": `multipart/form-data; boundary=${boundary}`,
334
- },
335
- body,
336
- });
337
-
338
- if (!resp.ok) {
339
- const text = await resp.text();
340
- throw new Error(`Streamable upload failed (${resp.status}): ${text}`);
341
- }
342
-
343
- const data = await resp.json();
344
- return `https://streamable.com/${data.shortcode}`;
309
+ async function uploadVideo(filePath) {
310
+ try {
311
+ const result = execFileSync("curl", [
312
+ "-s",
313
+ "-H", "Authorization: Client-ID 546c25a59c58ad7",
314
+ "-F", `video=@${filePath}`,
315
+ "https://api.imgur.com/3/upload",
316
+ ], { timeout: 120000 });
317
+ const data = JSON.parse(result.toString());
318
+ if (data.data?.link) return data.data.link;
319
+ } catch { /* fall through */ }
320
+ return null;
345
321
  }
346
322
 
347
323
  function openShareURL(videoURL, text) {
@@ -375,14 +351,25 @@ async function shareFlow(outputPath, title) {
375
351
  const shouldShare = await askYesNo("Share to Twitter? [Y/n] ");
376
352
  if (!shouldShare) return;
377
353
 
378
- console.error("Uploading to Streamable...");
379
- try {
380
- const url = await uploadToStreamable(outputPath);
381
- const text = `${title}\n\nMade with @agentreel`;
354
+ console.error("Uploading video...");
355
+ const url = await uploadVideo(outputPath);
356
+
357
+ if (url) {
358
+ const text = `${title}\n\nMade with agentreel`;
382
359
  openShareURL(url, text);
383
- } catch (err) {
384
- console.error(`Upload failed: ${err.message}`);
385
- console.error("You can manually upload the video and share it.");
360
+ } else {
361
+ // No upload worked — open Twitter with just the text, user attaches video manually
362
+ console.error("Could not auto-upload. Opening Twitter — drag your video into the tweet.");
363
+ const text = `${title}\n\nMade with agentreel`;
364
+ const tweetText = encodeURIComponent(text);
365
+ const intentURL = `https://twitter.com/intent/tweet?text=${tweetText}`;
366
+ const cmd = process.platform === "darwin" ? "open" : "xdg-open";
367
+ try {
368
+ execFileSync(cmd, [intentURL], { stdio: "ignore" });
369
+ } catch {
370
+ console.error(` Tweet link: ${intentURL}`);
371
+ }
372
+ console.error(` Video: ${resolve(outputPath)}`);
386
373
  }
387
374
  }
388
375
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentreel",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Turn Claude Code sessions into viral demo videos",
5
5
  "bin": {
6
6
  "agentreel": "./bin/agentreel.mjs"
package/src/CastVideo.tsx CHANGED
@@ -115,7 +115,7 @@ export const CastVideo: React.FC<CastProps> = ({
115
115
  letterSpacing: 2,
116
116
  }}
117
117
  >
118
- made with ♥ by agentreel
118
+ made with agentreel
119
119
  </div>
120
120
 
121
121
  <MusicTrack />
@@ -1010,7 +1010,7 @@ const EndCard: React.FC<{ text: string; url?: string }> = ({ text, url }) => {
1010
1010
  letterSpacing: 3,
1011
1011
  }}
1012
1012
  >
1013
- made with ♥ by agentreel
1013
+ made with agentreel
1014
1014
  </div>
1015
1015
  </AbsoluteFill>
1016
1016
  );