automate-it 0.2.0 → 0.3.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/ait.mjs +148 -34
- package/package.json +1 -1
package/ait.mjs
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Zero-dependency ESM script (Node 18+ or Bun). Speaks the Model Context
|
|
5
5
|
// Protocol (StreamableHTTP, stateless) to Automate It's public MCP server,
|
|
6
|
-
// authenticating with an API key (ak_*)
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
6
|
+
// authenticating with an API key (ak_*). Every command goes through MCP —
|
|
7
|
+
// there is no REST path.
|
|
8
|
+
//
|
|
9
|
+
// The command surface is a closed set: every tool the workspace MCP server
|
|
10
|
+
// exposes has exactly one named command here, and there is no generic
|
|
11
|
+
// passthrough. Adding a tool to the server means adding its command here —
|
|
12
|
+
// that's deliberate, so the skill's capabilities can be read off its docs.
|
|
13
|
+
// Scopes and workspace role still bound what runs, enforced server-side, and
|
|
14
|
+
// tools that destroy data additionally require an explicit --yes.
|
|
10
15
|
//
|
|
11
16
|
// Tool output is printed verbatim on stdout; errors go to stderr as
|
|
12
17
|
// {"error": "..."} with exit code 1.
|
|
@@ -26,12 +31,14 @@ Environment:
|
|
|
26
31
|
AUTOMATE_IT_WORKSPACE Default workspace id (else pass --workspace, else
|
|
27
32
|
auto-resolved when the key has exactly one workspace)
|
|
28
33
|
|
|
34
|
+
Destructive commands (task delete, task delete-content, task clear-content,
|
|
35
|
+
automation delete, folders delete) permanently remove data and refuse to run
|
|
36
|
+
without --yes. Only pass it when the operator asked for that deletion by name.
|
|
37
|
+
|
|
29
38
|
Discovery:
|
|
30
|
-
ait tools List every available MCP tool
|
|
31
|
-
ait call <tool> [--args '<json>'] Call any MCP tool directly (workspaceId
|
|
32
|
-
is injected automatically if omitted)
|
|
33
39
|
ait workspaces List workspaces the key can access
|
|
34
40
|
ait whoami Show the user this key acts as
|
|
41
|
+
ait integrations Platforms connected for publishing
|
|
35
42
|
|
|
36
43
|
Tasks (the review-gate loop):
|
|
37
44
|
ait task create --title <t> Create a task
|
|
@@ -64,8 +71,9 @@ Tasks (the review-gate loop):
|
|
|
64
71
|
[--media '<json array>'] [--sort-order <n>]
|
|
65
72
|
ait task update-content <taskId> <contentItemId>
|
|
66
73
|
[--body <text>] [--title <t>] Rewrite a content item in place
|
|
67
|
-
ait task delete-content <taskId> <contentItemId>
|
|
68
|
-
ait task clear-content <taskId>
|
|
74
|
+
ait task delete-content <taskId> <contentItemId> --yes
|
|
75
|
+
ait task clear-content <taskId> --yes
|
|
76
|
+
Remove every content item from a task
|
|
69
77
|
ait task complete <taskId> Finish work (status → review/approved)
|
|
70
78
|
ait task comment <taskId> --comment <text>
|
|
71
79
|
Leave a note for the human reviewer
|
|
@@ -78,7 +86,7 @@ Tasks (the review-gate loop):
|
|
|
78
86
|
later; --auto picks the next open slot
|
|
79
87
|
per the workspace's scheduling rules;
|
|
80
88
|
--clear returns it to manual
|
|
81
|
-
ait task delete <taskId>
|
|
89
|
+
ait task delete <taskId> --yes
|
|
82
90
|
|
|
83
91
|
Skills (workspace voice, formatting rules, bundled reference files):
|
|
84
92
|
ait skills list id, name, description for each skill
|
|
@@ -91,7 +99,9 @@ Automations:
|
|
|
91
99
|
ait automation get <automationId>
|
|
92
100
|
ait automation update <automationId> [--name <n>] [--instructions <text>]
|
|
93
101
|
[--output-types x,...] [--schedule <json>]
|
|
94
|
-
ait automation delete <automationId>
|
|
102
|
+
ait automation delete <automationId> --yes
|
|
103
|
+
Also deletes every task it spawned,
|
|
104
|
+
published ones included
|
|
95
105
|
ait automation run <automationId> Trigger now; returns the spawned task
|
|
96
106
|
|
|
97
107
|
Files:
|
|
@@ -100,13 +110,52 @@ Files:
|
|
|
100
110
|
ait files list [--search <s>] [--mime-type <m>] [--limit <n>]
|
|
101
111
|
ait files download-url <fileId> [--expires-in <seconds>]
|
|
102
112
|
Presigned URL — download it yourself
|
|
103
|
-
|
|
113
|
+
ait files move <fileId> [--folder <folderId>]
|
|
114
|
+
Move into a folder; omit --folder for
|
|
115
|
+
the workspace root
|
|
116
|
+
ait files copy <fileId> [--folder <folderId>]
|
|
117
|
+
Duplicate the file (new file + object)
|
|
118
|
+
|
|
119
|
+
Folders:
|
|
120
|
+
ait folders list [--parent <folderId>] [--root]
|
|
121
|
+
--root lists top-level folders only
|
|
122
|
+
ait folders create --name <n> [--parent <folderId>]
|
|
123
|
+
ait folders rename <folderId> --name <n>
|
|
124
|
+
ait folders delete <folderId> --yes Also deletes every file and nested
|
|
125
|
+
folder inside it
|
|
126
|
+
|
|
127
|
+
Composing posts:
|
|
128
|
+
ait x-limit [--text <draft>] X's limit for the connected account
|
|
129
|
+
(280 or 25000). With --text, returns the
|
|
130
|
+
draft's weighted length and whether it
|
|
131
|
+
fits — a URL counts 23, emoji 2.
|
|
132
|
+
ait shorten <url> [--title <t>] Shorten a link for a post
|
|
104
133
|
|
|
105
134
|
All workspace commands accept --workspace <id>.`;
|
|
106
135
|
|
|
107
136
|
export class CliError extends Error {}
|
|
108
137
|
|
|
109
|
-
const BOOLEAN_FLAGS = new Set(["no-review", "claim", "mine", "clear", "auto", "help"]);
|
|
138
|
+
const BOOLEAN_FLAGS = new Set(["no-review", "claim", "mine", "clear", "auto", "help", "yes", "root"]);
|
|
139
|
+
|
|
140
|
+
// MCP tools that permanently remove data, keyed to what they destroy. Keyed by
|
|
141
|
+
// tool rather than by command so the gate lives next to the thing that does
|
|
142
|
+
// the damage — a second command routing to one of these inherits it.
|
|
143
|
+
const DESTRUCTIVE_TOOLS = new Map([
|
|
144
|
+
["delete_task", "delete the task and everything attached to it"],
|
|
145
|
+
["delete_content_item", "delete the content item"],
|
|
146
|
+
["clear_task_content", "remove every content item from the task"],
|
|
147
|
+
["delete_automation", "delete the automation and every task it ever spawned, published ones included"],
|
|
148
|
+
["delete_folder", "delete the folder and every file and folder inside it"],
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
/** Destructive tools need an explicit --yes; nothing else is affected. */
|
|
152
|
+
function requireConfirmation(flags, toolName) {
|
|
153
|
+
const effect = DESTRUCTIVE_TOOLS.get(toolName);
|
|
154
|
+
if (!effect || flags.yes === true) return;
|
|
155
|
+
throw new CliError(
|
|
156
|
+
`Refusing to run without --yes: this would ${effect}. It cannot be undone — confirm with the operator, then re-run with --yes.`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
110
159
|
|
|
111
160
|
export function parseArgs(argv) {
|
|
112
161
|
const positional = [];
|
|
@@ -342,28 +391,19 @@ export async function runCommand(argv, opts = {}) {
|
|
|
342
391
|
return USAGE;
|
|
343
392
|
}
|
|
344
393
|
|
|
345
|
-
const SINGLE_WORD_COMMANDS = new Set([
|
|
394
|
+
const SINGLE_WORD_COMMANDS = new Set([
|
|
395
|
+
"workspaces",
|
|
396
|
+
"whoami",
|
|
397
|
+
"upload-url",
|
|
398
|
+
"integrations",
|
|
399
|
+
"x-limit",
|
|
400
|
+
"shorten",
|
|
401
|
+
]);
|
|
346
402
|
const command = SINGLE_WORD_COMMANDS.has(resource)
|
|
347
403
|
? resource
|
|
348
404
|
: [resource, action].filter(Boolean).join(" ");
|
|
349
405
|
|
|
350
406
|
switch (command) {
|
|
351
|
-
case "tools": {
|
|
352
|
-
const result = await mcpRequest(ctx, "tools/list", {});
|
|
353
|
-
return (result?.tools ?? [])
|
|
354
|
-
.map((t) => `${t.name} — ${(t.description || "").split("\n")[0]}`)
|
|
355
|
-
.join("\n");
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
case "call": {
|
|
359
|
-
const toolName = requirePositional([action, ...rest].filter(Boolean), "tool", "ait call <tool> [--args '<json>']");
|
|
360
|
-
const args = flags.args ? parseJsonFlag(flags.args, "args") : {};
|
|
361
|
-
if (args.workspaceId === undefined && toolName !== "list_workspaces" && toolName !== "get_clerk_user_data") {
|
|
362
|
-
args.workspaceId = await resolveWorkspace(ctx, flags);
|
|
363
|
-
}
|
|
364
|
-
return callTool(ctx, toolName, args);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
407
|
case "workspaces":
|
|
368
408
|
return callTool(ctx, "list_workspaces", {});
|
|
369
409
|
|
|
@@ -448,6 +488,7 @@ export async function runCommand(argv, opts = {}) {
|
|
|
448
488
|
delete: "delete_task",
|
|
449
489
|
};
|
|
450
490
|
const taskId = requirePositional(rest, "taskId", `ait task ${action} <taskId>`);
|
|
491
|
+
requireConfirmation(flags, toolByAction[action]);
|
|
451
492
|
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
452
493
|
return callTool(ctx, toolByAction[action], { workspaceId, taskId });
|
|
453
494
|
}
|
|
@@ -536,8 +577,9 @@ export async function runCommand(argv, opts = {}) {
|
|
|
536
577
|
case "automation get":
|
|
537
578
|
case "automation delete": {
|
|
538
579
|
const automationId = requirePositional(rest, "automationId", `ait automation ${action} <automationId>`);
|
|
539
|
-
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
540
580
|
const tool = action === "get" ? "get_automation" : "delete_automation";
|
|
581
|
+
requireConfirmation(flags, tool);
|
|
582
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
541
583
|
return callTool(ctx, tool, { workspaceId, automationId });
|
|
542
584
|
}
|
|
543
585
|
|
|
@@ -590,15 +632,17 @@ export async function runCommand(argv, opts = {}) {
|
|
|
590
632
|
}
|
|
591
633
|
|
|
592
634
|
case "task delete-content": {
|
|
593
|
-
const usage = "ait task delete-content <taskId> <contentItemId>";
|
|
635
|
+
const usage = "ait task delete-content <taskId> <contentItemId> --yes";
|
|
594
636
|
const taskId = requirePositionalAt(rest, 0, "taskId", usage);
|
|
595
637
|
const contentItemId = requirePositionalAt(rest, 1, "contentItemId", usage);
|
|
638
|
+
requireConfirmation(flags, "delete_content_item");
|
|
596
639
|
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
597
640
|
return callTool(ctx, "delete_content_item", { workspaceId, taskId, contentItemId });
|
|
598
641
|
}
|
|
599
642
|
|
|
600
643
|
case "task clear-content": {
|
|
601
|
-
const taskId = requirePositional(rest, "taskId", "ait task clear-content <taskId>");
|
|
644
|
+
const taskId = requirePositional(rest, "taskId", "ait task clear-content <taskId> --yes");
|
|
645
|
+
requireConfirmation(flags, "clear_task_content");
|
|
602
646
|
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
603
647
|
return callTool(ctx, "clear_task_content", { workspaceId, taskId });
|
|
604
648
|
}
|
|
@@ -634,8 +678,78 @@ export async function runCommand(argv, opts = {}) {
|
|
|
634
678
|
});
|
|
635
679
|
}
|
|
636
680
|
|
|
681
|
+
case "files move":
|
|
682
|
+
case "files copy": {
|
|
683
|
+
const usage = `ait files ${action} <fileId> [--folder <folderId>]`;
|
|
684
|
+
const fileId = requirePositional(rest, "fileId", usage);
|
|
685
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
686
|
+
// Omitting --folder targets the workspace root, matching the tools'
|
|
687
|
+
// own default — for `move` that relocates the file, it isn't a no-op.
|
|
688
|
+
return callTool(ctx, action === "move" ? "move_file" : "copy_file", {
|
|
689
|
+
workspaceId,
|
|
690
|
+
fileId,
|
|
691
|
+
folderId: flags.folder ?? null,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
case "folders list": {
|
|
696
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
697
|
+
return callTool(ctx, "list_folders", {
|
|
698
|
+
workspaceId,
|
|
699
|
+
...(flags.parent ? { parentId: flags.parent } : {}),
|
|
700
|
+
...(flags.root === true ? { root: true } : {}),
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
case "folders create": {
|
|
705
|
+
if (!flags.name) throw new CliError("--name is required for folders create");
|
|
706
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
707
|
+
return callTool(ctx, "create_folder", {
|
|
708
|
+
workspaceId,
|
|
709
|
+
name: flags.name,
|
|
710
|
+
...(flags.parent ? { parentId: flags.parent } : {}),
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
case "folders rename": {
|
|
715
|
+
const folderId = requirePositional(rest, "folderId", "ait folders rename <folderId> --name <n>");
|
|
716
|
+
if (!flags.name) throw new CliError("--name is required for folders rename");
|
|
717
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
718
|
+
return callTool(ctx, "rename_folder", { workspaceId, folderId, name: flags.name });
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
case "folders delete": {
|
|
722
|
+
const folderId = requirePositional(rest, "folderId", "ait folders delete <folderId> --yes");
|
|
723
|
+
requireConfirmation(flags, "delete_folder");
|
|
724
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
725
|
+
return callTool(ctx, "delete_folder", { workspaceId, folderId });
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
case "integrations": {
|
|
729
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
730
|
+
return callTool(ctx, "list_integrations", { workspaceId });
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
case "x-limit": {
|
|
734
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
735
|
+
return callTool(ctx, "get_x_character_limit", {
|
|
736
|
+
workspaceId,
|
|
737
|
+
...(flags.text !== undefined ? { text: flags.text } : {}),
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
case "shorten": {
|
|
742
|
+
const url = requirePositional([action, ...rest].filter(Boolean), "url", "ait shorten <url> [--title <t>]");
|
|
743
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
744
|
+
return callTool(ctx, "shorten_url", {
|
|
745
|
+
workspaceId,
|
|
746
|
+
url,
|
|
747
|
+
...(flags.title ? { title: flags.title } : {}),
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
637
751
|
default:
|
|
638
|
-
throw new CliError(`Unknown command "${command}". Run "ait help" for
|
|
752
|
+
throw new CliError(`Unknown command "${command}". Run "ait help" for the full command list.`);
|
|
639
753
|
}
|
|
640
754
|
}
|
|
641
755
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "automate-it",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for Automate It — AI agents create content tasks, submit them through a human review gate, and publish everywhere. Speaks the Automate It MCP server; zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|