@jclvsh/dropspace 2.50.0 → 2.51.0

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
@@ -61,6 +61,9 @@ dropspace launches --help
61
61
  | audience | follower counts + growth per connected account |
62
62
  | autopilot | status, configure, review, approve, reject, run, runs |
63
63
  | recommendations (recs) | list, apply, dismiss |
64
+ | songs | list, get, add, lyrics, rename, retry, delete |
65
+ | song-reels | make, list, get, retry, launch, delete |
66
+ | scene-clips | cut, list, get, retry, delete |
64
67
  | profile | get, update |
65
68
  | billing | portal |
66
69
 
@@ -399,6 +402,11 @@ Reddit engagement (drafted comments in other communities) is paused: its tools a
399
402
  | `get_song_reel` | One reel with its lyric lines (local package only) |
400
403
  | `launch_song_reel` | A draft launch carrying a ready reel and its lyrics as the caption (local package only) |
401
404
  | `delete_song_reel` | Delete a reel; its media library item and launch stay (local package only) |
405
+ | `cut_scene_clips` | Cut a long video (a media library video, a YouTube link, or a public video url) into 2-8 second clips at its scene changes, up to 40, into the media library (local package only) |
406
+ | `list_scene_clip_jobs` | Clip jobs, newest first, with status, clips made and added, and each clip's urls (local package only) |
407
+ | `get_scene_clip_job` | One clip job; reading a ready job adds its clips to the media library (local package only) |
408
+ | `retry_scene_clip_job` | Cut a failed or stuck job again (local package only) |
409
+ | `delete_scene_clip_job` | Delete a clip job; clips already in the media library stay (local package only) |
402
410
 
403
411
  Prompts (local package only): `instagram_audit`, `best_times_plan`, `winners_report`. Each tells the AI app which tools to call and ends with `save_report`.
404
412
 
package/dist/cli.js CHANGED
@@ -21,6 +21,7 @@ import { registerAudienceCommands } from "./commands/audience.js";
21
21
  import { registerAccountCommands } from "./commands/account.js";
22
22
  import { registerSongCommands } from "./commands/songs.js";
23
23
  import { registerSongReelCommands } from "./commands/song-reels.js";
24
+ import { registerSceneClipCommands } from "./commands/scene-clips.js";
24
25
  const program = new Command()
25
26
  .name("dropspace")
26
27
  .description("dropspace CLI - manage launches, personas, boosts, and more")
@@ -46,6 +47,7 @@ try {
46
47
  registerAccountCommands(program, client);
47
48
  registerSongCommands(program, client);
48
49
  registerSongReelCommands(program, client);
50
+ registerSceneClipCommands(program, client);
49
51
  await program.parseAsync(process.argv);
50
52
  }
51
53
  catch (error) {
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ import type { DropspaceClient } from "../client.js";
3
+ export declare function registerSceneClipCommands(program: Command, client: DropspaceClient): void;
@@ -0,0 +1,79 @@
1
+ import { formatOutput, handleError, getFormat } from "../cli-utils.js";
2
+ export function registerSceneClipCommands(program, client) {
3
+ const clips = program
4
+ .command("scene-clips")
5
+ .description("cut a long video into short clips for your media library (pro and premium)");
6
+ clips
7
+ .command("cut")
8
+ .description("start a job: give one of --media, --link, or --url")
9
+ .option("--media <id>", "a video in your media library")
10
+ .option("--link <url>", "a YouTube link (up to 30 minutes)")
11
+ .option("--url <url>", "a public video file url (up to 512 MB)")
12
+ .action(async (opts) => {
13
+ try {
14
+ const body = {};
15
+ if (opts.media)
16
+ body.media_library_id = opts.media;
17
+ if (opts.link)
18
+ body.link_url = opts.link;
19
+ if (opts.url)
20
+ body.video_url = opts.url;
21
+ formatOutput(await client.post("/scene-clips", body), getFormat(program.opts()));
22
+ }
23
+ catch (error) {
24
+ handleError(error);
25
+ }
26
+ });
27
+ clips
28
+ .command("list")
29
+ .description("your clip jobs, newest first")
30
+ .option("--page <n>", "page number")
31
+ .option("--page-size <n>", "jobs per page (max 100)")
32
+ .action(async (opts) => {
33
+ try {
34
+ const params = {};
35
+ if (opts.page)
36
+ params.page = opts.page;
37
+ if (opts.pageSize)
38
+ params.page_size = opts.pageSize;
39
+ formatOutput(await client.get("/scene-clips", params), getFormat(program.opts()));
40
+ }
41
+ catch (error) {
42
+ handleError(error);
43
+ }
44
+ });
45
+ clips
46
+ .command("get <id>")
47
+ .description("one job; reading a ready job adds its clips to your media library")
48
+ .action(async (id) => {
49
+ try {
50
+ formatOutput(await client.get(`/scene-clips/${id}`), getFormat(program.opts()));
51
+ }
52
+ catch (error) {
53
+ handleError(error);
54
+ }
55
+ });
56
+ clips
57
+ .command("retry <id>")
58
+ .description("cut a failed (or stuck) job again")
59
+ .action(async (id) => {
60
+ try {
61
+ formatOutput(await client.post(`/scene-clips/${id}/retry`), getFormat(program.opts()));
62
+ }
63
+ catch (error) {
64
+ handleError(error);
65
+ }
66
+ });
67
+ clips
68
+ .command("delete <id>")
69
+ .description("delete a job; clips already in your media library stay")
70
+ .action(async (id) => {
71
+ try {
72
+ await client.delete(`/scene-clips/${id}`);
73
+ formatOutput({ id, deleted: true }, getFormat(program.opts()));
74
+ }
75
+ catch (error) {
76
+ handleError(error);
77
+ }
78
+ });
79
+ }
package/dist/server.js CHANGED
@@ -21,6 +21,7 @@ import { registerTrialReelTools } from "./tools/trial-reels.js";
21
21
  import { registerReportTools, registerWorkflowPrompts } from "./tools/reports.js";
22
22
  import { registerSongTools } from "./tools/songs.js";
23
23
  import { registerSongReelTools } from "./tools/song-reels.js";
24
+ import { registerSceneClipTools } from "./tools/scene-clips.js";
24
25
  /** Drops tools whose feature is paused before the policy registrar sees them (see PAUSED_TOOLS). */
25
26
  function skipPausedTools(registrar) {
26
27
  return {
@@ -54,6 +55,7 @@ export function createDropspaceServer({ client, version, profile }) {
54
55
  registerReportTools(registrar, client);
55
56
  registerSongTools(registrar, client);
56
57
  registerSongReelTools(registrar, client);
58
+ registerSceneClipTools(registrar, client);
57
59
  // Prompts are not tools, so the policy registrar does not filter them: the hosted profile
58
60
  // (frozen for the Meta review) gets none.
59
61
  if (profile === "full")
@@ -148,6 +148,12 @@ const FULL_TOOL_POLICY = {
148
148
  get_song_reel: localOnly("media", "read"),
149
149
  launch_song_reel: localOnly("media", "write"),
150
150
  delete_song_reel: localOnly("media", "write"),
151
+ // scene clips: a job spends server minutes and adds up to 40 items to the media library
152
+ cut_scene_clips: localOnly("media", "write"),
153
+ list_scene_clip_jobs: localOnly("media", "read"),
154
+ get_scene_clip_job: localOnly("media", "read"),
155
+ retry_scene_clip_job: localOnly("media", "write"),
156
+ delete_scene_clip_job: localOnly("media", "write"),
151
157
  get_usage: hosted("usage", "read"),
152
158
  get_changelog: hosted("usage", "read"),
153
159
  get_dropspace_status: hosted("dropspace", "read"),
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { DropspaceClient } from "../client.js";
3
+ /** Scene clips: a long video cut into short clips for the media library (pro and premium). Local package only. */
4
+ export declare function registerSceneClipTools(server: McpServer, client: DropspaceClient): void;
@@ -0,0 +1,66 @@
1
+ import { z } from "zod";
2
+ function text(result) {
3
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4
+ }
5
+ function fail(error) {
6
+ return {
7
+ content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
8
+ isError: true,
9
+ };
10
+ }
11
+ /** Scene clips: a long video cut into short clips for the media library (pro and premium). Local package only. */
12
+ export function registerSceneClipTools(server, client) {
13
+ server.tool("cut_scene_clips", "cut a long video into 2 to 8 second clips at its scene changes, up to 40 a job, each with a thumbnail, for the user's media library (footage for make_song_reel and autopilot). give exactly one source: media_library_id (a video from list_media_library), link_url (a YouTube link, up to 30 minutes), or video_url (a public mp4 or mov url, up to 512 MB). only the first 15 minutes are scanned. the work runs on the server: the job comes back pending; read it with get_scene_clip_job until ready (the clips are added to the media library then, source scene) or failed (error). pro and premium; at most 10 jobs a day and one running at a time.", {
14
+ media_library_id: z.string().uuid().optional().describe("a video in the media library"),
15
+ link_url: z.string().url().optional().describe("a YouTube link"),
16
+ video_url: z.string().url().optional().describe("a public video file url"),
17
+ }, async (body) => {
18
+ try {
19
+ return text(await client.post("/scene-clips", body));
20
+ }
21
+ catch (error) {
22
+ return fail(error);
23
+ }
24
+ });
25
+ server.tool("list_scene_clip_jobs", "the user's clip jobs, newest first: id, source (library, link, url), source_title, status (pending, running, ready, or failed with error), clips_made, clips_added (in the media library), clips (start, end, video_url, thumbnail_url, media_library_id), and a note when clips are waiting for room in the library.", {
26
+ page: z.number().int().min(1).optional(),
27
+ page_size: z.number().int().min(1).max(100).optional(),
28
+ }, async ({ page, page_size }) => {
29
+ try {
30
+ const params = {};
31
+ if (page)
32
+ params.page = String(page);
33
+ if (page_size)
34
+ params.page_size = String(page_size);
35
+ return text(await client.get("/scene-clips", params));
36
+ }
37
+ catch (error) {
38
+ return fail(error);
39
+ }
40
+ });
41
+ server.tool("get_scene_clip_job", "one clip job. reading a ready job adds its clips to the media library (as many as fit under the 500-item cap; the rest on a later read).", { job_id: z.string().uuid() }, async ({ job_id }) => {
42
+ try {
43
+ return text(await client.get(`/scene-clips/${job_id}`));
44
+ }
45
+ catch (error) {
46
+ return fail(error);
47
+ }
48
+ });
49
+ server.tool("retry_scene_clip_job", "cut a failed (or stuck) clip job again. three attempts in all.", { job_id: z.string().uuid() }, async ({ job_id }) => {
50
+ try {
51
+ return text(await client.post(`/scene-clips/${job_id}/retry`));
52
+ }
53
+ catch (error) {
54
+ return fail(error);
55
+ }
56
+ });
57
+ server.tool("delete_scene_clip_job", "delete a clip job. clips already in the media library stay (delete_media_library_item removes one).", { job_id: z.string().uuid() }, async ({ job_id }) => {
58
+ try {
59
+ await client.delete(`/scene-clips/${job_id}`);
60
+ return text({ id: job_id, deleted: true });
61
+ }
62
+ catch (error) {
63
+ return fail(error);
64
+ }
65
+ });
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jclvsh/dropspace",
3
- "version": "2.50.0",
3
+ "version": "2.51.0",
4
4
  "description": "MCP server and CLI for the Dropspace API",
5
5
  "repository": {
6
6
  "type": "git",