@vatio-ai/cli 0.47.0 → 0.48.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 +59 -16
- package/lib/commands/chat.mjs +72 -4
- package/lib/commands/deploy.mjs +5 -2
- package/lib/commands/mcp.mjs +5 -3
- package/lib/help.mjs +4 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -1,44 +1,87 @@
|
|
|
1
1
|
# @vatio-ai/cli
|
|
2
2
|
|
|
3
|
-
The CLI for [Vatio](https://vatio.ai) —
|
|
3
|
+
The CLI for [Vatio](https://vatio.ai) — a production runtime for
|
|
4
|
+
customer-facing AI agents. Describe an agent in one `vatio.yml` and deploy it to
|
|
5
|
+
web chat, WhatsApp and Instagram; Vatio runs the conversation, the contacts, the
|
|
6
|
+
identity, the safeguards and the handoff to a human.
|
|
4
7
|
|
|
5
8
|
```bash
|
|
6
9
|
npx @vatio-ai/cli init my-agent
|
|
7
10
|
npx @vatio-ai/cli push
|
|
8
11
|
```
|
|
9
12
|
|
|
10
|
-
No runtime to install first: if you have Node, you have this.
|
|
13
|
+
No runtime to install first: if you have Node 20 or newer, you have this.
|
|
11
14
|
|
|
12
15
|
The package is `@vatio-ai/cli`; the command it installs is `vatio`. So
|
|
13
16
|
`npm install -g @vatio-ai/cli` gives you `vatio push`, and `npx @vatio-ai/cli push`
|
|
14
17
|
is the same thing without installing anything.
|
|
15
18
|
|
|
19
|
+
## If you are a coding agent
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
vatio mcp # MCP over stdio
|
|
23
|
+
vatio docs # the whole developer contract as markdown, live from Vatio
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`vatio mcp` is the short path: it hands you the contract and the deploy commands
|
|
27
|
+
over MCP, with no browser step. Failing that, `vatio docs` prints the same
|
|
28
|
+
contract that [docs.vatio.ai/docs.md](https://docs.vatio.ai/docs.md) serves, and
|
|
29
|
+
`vatio docs --save` writes it next to the workspace.
|
|
30
|
+
|
|
31
|
+
Don't guess `vatio.yml` keys. Unknown root keys fail validation, and the rules
|
|
32
|
+
that judge a workspace live on the server — so the printed contract is the
|
|
33
|
+
current one by construction.
|
|
34
|
+
|
|
16
35
|
## What a workspace is
|
|
17
36
|
|
|
18
|
-
A directory with a `vatio.yml` in it
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
A directory with a `vatio.yml` in it:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
support-agent/
|
|
41
|
+
vatio.yml # required: the agent, its tools, its knowledge
|
|
42
|
+
tools/*.yml # one HTTP request each, described in YAML
|
|
43
|
+
identity.pub # public key that verifies the JWT saying who a visitor is
|
|
44
|
+
```
|
|
21
45
|
|
|
22
|
-
Nothing constrains where that directory lives —
|
|
23
|
-
repository of the backend it calls, and every
|
|
24
|
-
walking up from the current directory. There
|
|
46
|
+
Only create the files you need. Nothing constrains where that directory lives —
|
|
47
|
+
an agent can sit inside the repository of the backend it calls, and every
|
|
48
|
+
command except `init` finds it by walking up from the current directory. There
|
|
49
|
+
is no `--workspace` flag.
|
|
25
50
|
|
|
26
51
|
## Commands
|
|
27
52
|
|
|
28
53
|
```
|
|
29
|
-
init [SLUG]
|
|
30
|
-
login
|
|
54
|
+
init [SLUG] Create vatio.yml here, and the remote to match
|
|
55
|
+
login | logout Authorize this machine in a browser, or forget it
|
|
56
|
+
doctor Node, config, workspace and token status
|
|
57
|
+
|
|
31
58
|
push [--env NAME] Validate the workspace, then update a preview
|
|
32
|
-
publish [--env NAME] Promote
|
|
59
|
+
publish [--env NAME] Promote the latest push to live, or the named preview
|
|
33
60
|
diff [--env NAME] What this directory would change
|
|
34
61
|
status Preview and live deployment state
|
|
35
62
|
rollback Restore the previous live deployment
|
|
36
63
|
tools check Validate the workspace against Vatio's contracts
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
|
|
65
|
+
chat "message" [--env NAME] Talk to your own agent; the token is the identity
|
|
66
|
+
chat transcript|debug|reset|destroy
|
|
67
|
+
chat show CHAT_ID [--json] Any chat in the workspace, with its deployment and tool calls
|
|
68
|
+
|
|
69
|
+
kb list|show|create|rm Knowledge bases
|
|
70
|
+
kb write|cat|rm-entry Entries, from a file or stdin
|
|
71
|
+
kb follow|unfollow|refresh Read a site into a base, nightly
|
|
72
|
+
|
|
73
|
+
secrets list|set|rm Credentials your tools read
|
|
74
|
+
tokens list|create|revoke Publishable tokens for the widget and the SDK
|
|
75
|
+
widget [--env NAME] What the platform enforces, and the tokens there are
|
|
76
|
+
auth --new-key Keypair that signs the visitor's JWT
|
|
77
|
+
|
|
78
|
+
whatsapp ... Shared preview number, or your own business number
|
|
79
|
+
instagram ... Shared sandbox, or your own account
|
|
80
|
+
|
|
81
|
+
docs [--save [PATH]] The whole developer contract
|
|
82
|
+
mcp Speak MCP over stdio, for a coding agent
|
|
83
|
+
issue "what should change" Send a change request, workspace attached
|
|
84
|
+
config show|set|unset base_url and token, per developer
|
|
42
85
|
```
|
|
43
86
|
|
|
44
87
|
`--env NAME` targets a deployment: `live`, `preview`, or a named preview like
|
package/lib/commands/chat.mjs
CHANGED
|
@@ -10,11 +10,11 @@ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
|
|
12
12
|
import { ApiClient } from "../api-client.mjs";
|
|
13
|
-
import { fail, sleep, takeEnv, takeValue } from "../support.mjs";
|
|
13
|
+
import { fail, sleep, takeEnv, takeFlag, takeValue } from "../support.mjs";
|
|
14
14
|
|
|
15
15
|
export const STATE_FILE = ".vatio-chat.json";
|
|
16
16
|
|
|
17
|
-
const SUBCOMMANDS = new Set(["transcript", "debug", "reset", "destroy", "delete", "help"]);
|
|
17
|
+
const SUBCOMMANDS = new Set(["show", "transcript", "debug", "reset", "destroy", "delete", "help"]);
|
|
18
18
|
const REMOVED_FLAGS = ["--channel", "--from", "--as", "--sandbox-url"];
|
|
19
19
|
|
|
20
20
|
export async function chat(config, args) {
|
|
@@ -30,14 +30,17 @@ export async function chat(config, args) {
|
|
|
30
30
|
|
|
31
31
|
const environment = takeEnv(args, { fallback: "preview" });
|
|
32
32
|
const timeout = Number(takeValue(args, "--timeout") ?? 120);
|
|
33
|
-
const
|
|
33
|
+
const lastValue = takeValue(args, "--last");
|
|
34
|
+
const last = Number(lastValue ?? 10);
|
|
35
|
+
const json = takeFlag(args, "--json");
|
|
34
36
|
const apiUrl = takeValue(args, "--api-url");
|
|
35
37
|
const token = takeValue(args, "--token");
|
|
36
38
|
|
|
37
|
-
const options = { environment, timeout, last, apiUrl, token };
|
|
39
|
+
const options = { environment, timeout, last, lastGiven: lastValue != null, json, apiUrl, token };
|
|
38
40
|
const sub = SUBCOMMANDS.has(args[0]) ? args.shift() : null;
|
|
39
41
|
|
|
40
42
|
if (sub === "help") return console.log(chatHelp());
|
|
43
|
+
if (sub === "show") return await showChat(config, options, args);
|
|
41
44
|
if (sub === "transcript") return await transcript(config, options);
|
|
42
45
|
if (sub === "debug") return await debugChat(config, options);
|
|
43
46
|
if (sub === "reset") return await resetChat(config, options);
|
|
@@ -54,6 +57,7 @@ function chatHelp() {
|
|
|
54
57
|
vatio chat "message" [--env NAME] [--timeout N]
|
|
55
58
|
vatio chat transcript [--last N]
|
|
56
59
|
vatio chat debug [--last N]
|
|
60
|
+
vatio chat show CHAT_ID [--last N] [--json]
|
|
57
61
|
vatio chat reset
|
|
58
62
|
vatio chat destroy CHAT_ID
|
|
59
63
|
|
|
@@ -61,6 +65,10 @@ function chatHelp() {
|
|
|
61
65
|
name like pr-42. A chat against live is a real conversation and shows up in
|
|
62
66
|
your inbox.
|
|
63
67
|
|
|
68
|
+
\`show\` reads any chat in the workspace by id -- one a visitor had on the web,
|
|
69
|
+
WhatsApp or Instagram, not only yours -- with the environment and deployment
|
|
70
|
+
that answered it, and every tool call.
|
|
71
|
+
|
|
64
72
|
The open chat per deployment is remembered in ${STATE_FILE}, so a second
|
|
65
73
|
\`vatio chat\` continues the first. \`reset\` starts over as a visitor the
|
|
66
74
|
workspace has never met.`;
|
|
@@ -200,6 +208,66 @@ async function debugChat(config, options) {
|
|
|
200
208
|
}
|
|
201
209
|
}
|
|
202
210
|
|
|
211
|
+
// Every message, not the first page: a chat worth reading by id is usually one
|
|
212
|
+
// that went wrong somewhere in the middle.
|
|
213
|
+
async function allMessages(api, chatId) {
|
|
214
|
+
const messages = [];
|
|
215
|
+
let after = null;
|
|
216
|
+
for (;;) {
|
|
217
|
+
const payload = await api.chatMessages({ chatId, after, limit: 200 });
|
|
218
|
+
const page = Array.isArray(payload.data) ? payload.data : [];
|
|
219
|
+
messages.push(...page);
|
|
220
|
+
if (page.length < 200) return messages;
|
|
221
|
+
after = page[page.length - 1].id;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function showChat(config, options, args) {
|
|
226
|
+
const chatId = args.shift();
|
|
227
|
+
if (!chatId || !/^\d+$/.test(chatId)) fail("Usage: vatio chat show CHAT_ID [--last N] [--json]");
|
|
228
|
+
|
|
229
|
+
const api = client(config, options);
|
|
230
|
+
const chat = await api.showChat({ chatId });
|
|
231
|
+
let messages = await allMessages(api, chatId);
|
|
232
|
+
if (options.lastGiven) messages = messages.slice(-options.last);
|
|
233
|
+
|
|
234
|
+
if (options.json) return console.log(JSON.stringify({ chat, messages }, null, 2));
|
|
235
|
+
|
|
236
|
+
const header = [
|
|
237
|
+
`Chat ${chat.chat_id}`,
|
|
238
|
+
chat.channel,
|
|
239
|
+
chat.environment,
|
|
240
|
+
chat.deployment_id && `deployment #${chat.deployment_id}`,
|
|
241
|
+
`agent ${chat.agent_slug}`
|
|
242
|
+
];
|
|
243
|
+
console.log(header.filter(Boolean).join(" · "));
|
|
244
|
+
console.log(`Started ${chat.created_at}, last activity ${chat.updated_at}`);
|
|
245
|
+
if (chat.identity) console.log(`Identity: ${JSON.stringify(chat.identity)}`);
|
|
246
|
+
console.log("");
|
|
247
|
+
|
|
248
|
+
for (const message of messages) {
|
|
249
|
+
const content = String(message.content ?? "").trim();
|
|
250
|
+
if (message.role === "system") {
|
|
251
|
+
console.log(`[system] ${content.length} characters of prompt (--json prints it)`);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (message.role === "tool") {
|
|
255
|
+
console.log(` [tool result] ${truncate(content, 400)}`);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const flags = message.discarded ? " (discarded)" : "";
|
|
259
|
+
if (content !== "" || flags) console.log(`[${message.role}]${flags} ${content}`);
|
|
260
|
+
for (const call of Array.isArray(message.tool_calls) ? message.tool_calls : []) {
|
|
261
|
+
console.log(` -> ${call.name}(${truncate(JSON.stringify(call.arguments ?? {}), 400)})`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (messages.length === 0) console.log("(no messages)");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function truncate(text, max) {
|
|
268
|
+
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
269
|
+
}
|
|
270
|
+
|
|
203
271
|
async function resetChat(config, options) {
|
|
204
272
|
const apiUrl = config.resolveApiUrl({ explicit: options.apiUrl });
|
|
205
273
|
const payload = await openChat(config, options, {
|
package/lib/commands/deploy.mjs
CHANGED
|
@@ -145,14 +145,17 @@ export async function push(config, args) {
|
|
|
145
145
|
|
|
146
146
|
export async function publish(config, args) {
|
|
147
147
|
// Here `--env` names the preview being promoted, not a destination: the
|
|
148
|
-
// destination of a publish is always live.
|
|
148
|
+
// destination of a publish is always live. Without it the platform promotes
|
|
149
|
+
// the most recent push, whichever preview it landed on.
|
|
149
150
|
const environment = takeEnv(args, { fallback: null });
|
|
150
151
|
const workspace = config.resolveWorkspaceRequired();
|
|
151
152
|
const payload = await deployClient(config, workspace).publish({
|
|
152
153
|
createdBy: process.env.USER ?? null,
|
|
153
154
|
environment
|
|
154
155
|
});
|
|
155
|
-
|
|
156
|
+
const from = asArray(payload.published_from);
|
|
157
|
+
const source = from.length > 0 ? ` from ${from.join(", ")}` : "";
|
|
158
|
+
console.log(`Published live deployment #${payload.deployment_id}${source} (git_sha=${JSON.stringify(payload.git_sha ?? null)})`);
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
export async function rollback(config) {
|
package/lib/commands/mcp.mjs
CHANGED
|
@@ -67,7 +67,8 @@ const COMMANDS = {
|
|
|
67
67
|
publish: {
|
|
68
68
|
usage: "[--env NAME]",
|
|
69
69
|
summary:
|
|
70
|
-
"Promote
|
|
70
|
+
"Promote the latest push to live (or the preview `--env` names), which is what customers " +
|
|
71
|
+
"see. Ask the developer first: this is " +
|
|
71
72
|
"the one step that changes what real visitors get.",
|
|
72
73
|
annotations: { readOnlyHint: false, destructiveHint: true }
|
|
73
74
|
},
|
|
@@ -77,11 +78,12 @@ const COMMANDS = {
|
|
|
77
78
|
annotations: { readOnlyHint: false, destructiveHint: true }
|
|
78
79
|
},
|
|
79
80
|
chat: {
|
|
80
|
-
usage: '"message" | transcript | debug | reset [--env NAME]',
|
|
81
|
+
usage: '"message" | transcript | debug | reset [--env NAME] | show CHAT_ID [--json]',
|
|
81
82
|
summary:
|
|
82
83
|
"Talk to the deployed agent as the developer holding the token, and read the transcript. " +
|
|
83
84
|
"`debug` shows the developer view, including tool calls. `--env live` is a real conversation " +
|
|
84
|
-
"that appears in the inbox."
|
|
85
|
+
"that appears in the inbox. `show CHAT_ID` reads any chat in the workspace — a visitor's on " +
|
|
86
|
+
"the web or WhatsApp too — with the environment and deployment that answered it.",
|
|
85
87
|
annotations: { readOnlyHint: false }
|
|
86
88
|
},
|
|
87
89
|
secrets: {
|
package/lib/help.mjs
CHANGED
|
@@ -24,7 +24,8 @@ flag, the directory decides.
|
|
|
24
24
|
|
|
25
25
|
Deploy:
|
|
26
26
|
push [--env NAME] Validate the workspace, then update a preview
|
|
27
|
-
publish [--env NAME] Promote
|
|
27
|
+
publish [--env NAME] Promote the latest push to live. --env promotes
|
|
28
|
+
a specific preview instead
|
|
28
29
|
rollback Restore the previous live deployment
|
|
29
30
|
status Preview and live deployment state
|
|
30
31
|
diff [--env NAME] What this directory would change (default: preview)
|
|
@@ -35,6 +36,8 @@ flag, the directory decides.
|
|
|
35
36
|
Talk to your agent (as yourself — the token is the identity):
|
|
36
37
|
chat "message" [--env NAME] preview by default; --env live is real
|
|
37
38
|
chat transcript|debug|reset [--last N]
|
|
39
|
+
chat show CHAT_ID [--json] Any chat in the workspace (web, WhatsApp, ...) with
|
|
40
|
+
its channel, environment, deployment and tool calls
|
|
38
41
|
chat destroy CHAT_ID
|
|
39
42
|
|
|
40
43
|
Knowledge:
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vatio-ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "Vatio CLI — deploy and manage Vatio agent workspaces",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"vatio",
|
|
7
|
+
"agent",
|
|
8
|
+
"ai",
|
|
9
|
+
"cli",
|
|
10
|
+
"deploy"
|
|
11
|
+
],
|
|
6
12
|
"homepage": "https://vatio.ai",
|
|
7
13
|
"bugs": "https://docs.vatio.ai",
|
|
8
14
|
"license": "SEE LICENSE IN LICENSE.txt",
|