automate-it 0.2.0 → 0.4.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 +152 -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,55 @@ 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 limits [--platform <p>] Text limits for every platform, or one.
|
|
129
|
+
[--text <draft>] With --text, returns the draft's length
|
|
130
|
+
under that platform's counting rule and
|
|
131
|
+
whether it fits. Platforms count
|
|
132
|
+
differently: X counts a URL as 23 and
|
|
133
|
+
emoji as 2, Threads counts emoji as
|
|
134
|
+
UTF-8 bytes, Bluesky counts graphemes.
|
|
135
|
+
ait shorten <url> [--title <t>] Shorten a link for a post
|
|
104
136
|
|
|
105
137
|
All workspace commands accept --workspace <id>.`;
|
|
106
138
|
|
|
107
139
|
export class CliError extends Error {}
|
|
108
140
|
|
|
109
|
-
const BOOLEAN_FLAGS = new Set(["no-review", "claim", "mine", "clear", "auto", "help"]);
|
|
141
|
+
const BOOLEAN_FLAGS = new Set(["no-review", "claim", "mine", "clear", "auto", "help", "yes", "root"]);
|
|
142
|
+
|
|
143
|
+
// MCP tools that permanently remove data, keyed to what they destroy. Keyed by
|
|
144
|
+
// tool rather than by command so the gate lives next to the thing that does
|
|
145
|
+
// the damage — a second command routing to one of these inherits it.
|
|
146
|
+
const DESTRUCTIVE_TOOLS = new Map([
|
|
147
|
+
["delete_task", "delete the task and everything attached to it"],
|
|
148
|
+
["delete_content_item", "delete the content item"],
|
|
149
|
+
["clear_task_content", "remove every content item from the task"],
|
|
150
|
+
["delete_automation", "delete the automation and every task it ever spawned, published ones included"],
|
|
151
|
+
["delete_folder", "delete the folder and every file and folder inside it"],
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
/** Destructive tools need an explicit --yes; nothing else is affected. */
|
|
155
|
+
function requireConfirmation(flags, toolName) {
|
|
156
|
+
const effect = DESTRUCTIVE_TOOLS.get(toolName);
|
|
157
|
+
if (!effect || flags.yes === true) return;
|
|
158
|
+
throw new CliError(
|
|
159
|
+
`Refusing to run without --yes: this would ${effect}. It cannot be undone — confirm with the operator, then re-run with --yes.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
110
162
|
|
|
111
163
|
export function parseArgs(argv) {
|
|
112
164
|
const positional = [];
|
|
@@ -342,28 +394,19 @@ export async function runCommand(argv, opts = {}) {
|
|
|
342
394
|
return USAGE;
|
|
343
395
|
}
|
|
344
396
|
|
|
345
|
-
const SINGLE_WORD_COMMANDS = new Set([
|
|
397
|
+
const SINGLE_WORD_COMMANDS = new Set([
|
|
398
|
+
"workspaces",
|
|
399
|
+
"whoami",
|
|
400
|
+
"upload-url",
|
|
401
|
+
"integrations",
|
|
402
|
+
"limits",
|
|
403
|
+
"shorten",
|
|
404
|
+
]);
|
|
346
405
|
const command = SINGLE_WORD_COMMANDS.has(resource)
|
|
347
406
|
? resource
|
|
348
407
|
: [resource, action].filter(Boolean).join(" ");
|
|
349
408
|
|
|
350
409
|
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
410
|
case "workspaces":
|
|
368
411
|
return callTool(ctx, "list_workspaces", {});
|
|
369
412
|
|
|
@@ -448,6 +491,7 @@ export async function runCommand(argv, opts = {}) {
|
|
|
448
491
|
delete: "delete_task",
|
|
449
492
|
};
|
|
450
493
|
const taskId = requirePositional(rest, "taskId", `ait task ${action} <taskId>`);
|
|
494
|
+
requireConfirmation(flags, toolByAction[action]);
|
|
451
495
|
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
452
496
|
return callTool(ctx, toolByAction[action], { workspaceId, taskId });
|
|
453
497
|
}
|
|
@@ -536,8 +580,9 @@ export async function runCommand(argv, opts = {}) {
|
|
|
536
580
|
case "automation get":
|
|
537
581
|
case "automation delete": {
|
|
538
582
|
const automationId = requirePositional(rest, "automationId", `ait automation ${action} <automationId>`);
|
|
539
|
-
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
540
583
|
const tool = action === "get" ? "get_automation" : "delete_automation";
|
|
584
|
+
requireConfirmation(flags, tool);
|
|
585
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
541
586
|
return callTool(ctx, tool, { workspaceId, automationId });
|
|
542
587
|
}
|
|
543
588
|
|
|
@@ -590,15 +635,17 @@ export async function runCommand(argv, opts = {}) {
|
|
|
590
635
|
}
|
|
591
636
|
|
|
592
637
|
case "task delete-content": {
|
|
593
|
-
const usage = "ait task delete-content <taskId> <contentItemId>";
|
|
638
|
+
const usage = "ait task delete-content <taskId> <contentItemId> --yes";
|
|
594
639
|
const taskId = requirePositionalAt(rest, 0, "taskId", usage);
|
|
595
640
|
const contentItemId = requirePositionalAt(rest, 1, "contentItemId", usage);
|
|
641
|
+
requireConfirmation(flags, "delete_content_item");
|
|
596
642
|
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
597
643
|
return callTool(ctx, "delete_content_item", { workspaceId, taskId, contentItemId });
|
|
598
644
|
}
|
|
599
645
|
|
|
600
646
|
case "task clear-content": {
|
|
601
|
-
const taskId = requirePositional(rest, "taskId", "ait task clear-content <taskId>");
|
|
647
|
+
const taskId = requirePositional(rest, "taskId", "ait task clear-content <taskId> --yes");
|
|
648
|
+
requireConfirmation(flags, "clear_task_content");
|
|
602
649
|
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
603
650
|
return callTool(ctx, "clear_task_content", { workspaceId, taskId });
|
|
604
651
|
}
|
|
@@ -634,8 +681,79 @@ export async function runCommand(argv, opts = {}) {
|
|
|
634
681
|
});
|
|
635
682
|
}
|
|
636
683
|
|
|
684
|
+
case "files move":
|
|
685
|
+
case "files copy": {
|
|
686
|
+
const usage = `ait files ${action} <fileId> [--folder <folderId>]`;
|
|
687
|
+
const fileId = requirePositional(rest, "fileId", usage);
|
|
688
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
689
|
+
// Omitting --folder targets the workspace root, matching the tools'
|
|
690
|
+
// own default — for `move` that relocates the file, it isn't a no-op.
|
|
691
|
+
return callTool(ctx, action === "move" ? "move_file" : "copy_file", {
|
|
692
|
+
workspaceId,
|
|
693
|
+
fileId,
|
|
694
|
+
folderId: flags.folder ?? null,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
case "folders list": {
|
|
699
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
700
|
+
return callTool(ctx, "list_folders", {
|
|
701
|
+
workspaceId,
|
|
702
|
+
...(flags.parent ? { parentId: flags.parent } : {}),
|
|
703
|
+
...(flags.root === true ? { root: true } : {}),
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
case "folders create": {
|
|
708
|
+
if (!flags.name) throw new CliError("--name is required for folders create");
|
|
709
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
710
|
+
return callTool(ctx, "create_folder", {
|
|
711
|
+
workspaceId,
|
|
712
|
+
name: flags.name,
|
|
713
|
+
...(flags.parent ? { parentId: flags.parent } : {}),
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
case "folders rename": {
|
|
718
|
+
const folderId = requirePositional(rest, "folderId", "ait folders rename <folderId> --name <n>");
|
|
719
|
+
if (!flags.name) throw new CliError("--name is required for folders rename");
|
|
720
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
721
|
+
return callTool(ctx, "rename_folder", { workspaceId, folderId, name: flags.name });
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
case "folders delete": {
|
|
725
|
+
const folderId = requirePositional(rest, "folderId", "ait folders delete <folderId> --yes");
|
|
726
|
+
requireConfirmation(flags, "delete_folder");
|
|
727
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
728
|
+
return callTool(ctx, "delete_folder", { workspaceId, folderId });
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
case "integrations": {
|
|
732
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
733
|
+
return callTool(ctx, "list_integrations", { workspaceId });
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
case "limits": {
|
|
737
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
738
|
+
return callTool(ctx, "get_platform_limit", {
|
|
739
|
+
workspaceId,
|
|
740
|
+
...(flags.platform !== undefined ? { platform: flags.platform } : {}),
|
|
741
|
+
...(flags.text !== undefined ? { text: flags.text } : {}),
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
case "shorten": {
|
|
746
|
+
const url = requirePositional([action, ...rest].filter(Boolean), "url", "ait shorten <url> [--title <t>]");
|
|
747
|
+
const workspaceId = await resolveWorkspace(ctx, flags);
|
|
748
|
+
return callTool(ctx, "shorten_url", {
|
|
749
|
+
workspaceId,
|
|
750
|
+
url,
|
|
751
|
+
...(flags.title ? { title: flags.title } : {}),
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
637
755
|
default:
|
|
638
|
-
throw new CliError(`Unknown command "${command}". Run "ait help" for
|
|
756
|
+
throw new CliError(`Unknown command "${command}". Run "ait help" for the full command list.`);
|
|
639
757
|
}
|
|
640
758
|
}
|
|
641
759
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "automate-it",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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": {
|