@vatio-ai/cli 0.51.0 → 0.53.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 +1 -1
- package/lib/api-client.mjs +6 -1
- package/lib/commands/deploy.mjs +15 -3
- package/lib/commands/mcp.mjs +7 -5
- package/lib/commands/misc.mjs +7 -9
- package/lib/commands/workspace-admin.mjs +37 -9
- package/lib/help.mjs +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ kb write|cat|rm-entry Entries, from a file or stdin
|
|
|
71
71
|
kb follow|unfollow|refresh Read a site into a base, nightly
|
|
72
72
|
|
|
73
73
|
secrets list|set|rm Credentials your tools read
|
|
74
|
-
tokens list|create|revoke
|
|
74
|
+
tokens list|create|origins|revoke Publishable tokens and the origins each accepts
|
|
75
75
|
widget [--env NAME] What the platform enforces, and the tokens there are
|
|
76
76
|
auth --new-key Keypair that signs the visitor's JWT
|
|
77
77
|
|
package/lib/api-client.mjs
CHANGED
|
@@ -151,12 +151,17 @@ export class ApiClient extends HttpClient {
|
|
|
151
151
|
return this.get("/publishable_tokens");
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
createPublishableToken({ environment, label = null }) {
|
|
154
|
+
createPublishableToken({ environment, label = null, allowedOrigins = [] }) {
|
|
155
155
|
const body = { environment };
|
|
156
156
|
if (String(label ?? "").trim() !== "") body.label = String(label).trim();
|
|
157
|
+
if (allowedOrigins.length > 0) body.allowed_origins = allowedOrigins;
|
|
157
158
|
return this.post("/publishable_tokens", body);
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
setPublishableTokenOrigins({ prefix, allowedOrigins }) {
|
|
162
|
+
return this.patch(`/publishable_tokens/${encode(prefix)}`, { allowed_origins: allowedOrigins });
|
|
163
|
+
}
|
|
164
|
+
|
|
160
165
|
revokePublishableToken({ prefix }) {
|
|
161
166
|
return this.delete(`/publishable_tokens/${encode(prefix)}`);
|
|
162
167
|
}
|
package/lib/commands/deploy.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { buildBundle, BundleError } from "../bundle.mjs";
|
|
|
18
18
|
import { ApiClient, CliClient } from "../api-client.mjs";
|
|
19
19
|
import { NotFoundError } from "../http.mjs";
|
|
20
20
|
import { deviceLogin } from "./auth.mjs";
|
|
21
|
-
import { detectGitSha, fail, takeEnv, takeFlag, takeValue } from "../support.mjs";
|
|
21
|
+
import { detectGitSha, fail, paint, takeEnv, takeFlag, takeValue } from "../support.mjs";
|
|
22
22
|
|
|
23
23
|
// The first command that makes something exist on a server, so the first one
|
|
24
24
|
// that needs to know whose it is.
|
|
@@ -106,8 +106,8 @@ export async function push(config, args) {
|
|
|
106
106
|
await ensureAuthorized(config);
|
|
107
107
|
|
|
108
108
|
// The remote has to exist before the workspace can be checked: the slug is in
|
|
109
|
-
// the URL of the call that checks it. It is created unnamed
|
|
110
|
-
//
|
|
109
|
+
// the URL of the call that checks it. It is created unnamed; the display name
|
|
110
|
+
// is a workspace setting edited in the console, not part of a push.
|
|
111
111
|
const clients = workspacesClient(config);
|
|
112
112
|
const created = !(await clients.workspaceExists(workspace));
|
|
113
113
|
if (created) {
|
|
@@ -141,6 +141,16 @@ export async function push(config, args) {
|
|
|
141
141
|
console.log(hint.message);
|
|
142
142
|
console.log(` ${hint.url}`);
|
|
143
143
|
}
|
|
144
|
+
printNotices(payload);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Something the platform wants the developer to know about the command they
|
|
148
|
+
// just ran -- a deprecation, usually. On stderr so scripts reading stdout keep
|
|
149
|
+
// working.
|
|
150
|
+
function printNotices(payload) {
|
|
151
|
+
for (const notice of asArray(payload.notices)) {
|
|
152
|
+
process.stderr.write(`\n${paint("yellow", `Notice: ${notice}`)}\n`);
|
|
153
|
+
}
|
|
144
154
|
}
|
|
145
155
|
|
|
146
156
|
export async function publish(config, args) {
|
|
@@ -156,12 +166,14 @@ export async function publish(config, args) {
|
|
|
156
166
|
const from = asArray(payload.published_from);
|
|
157
167
|
const source = from.length > 0 ? ` from ${from.join(", ")}` : "";
|
|
158
168
|
console.log(`Published live deployment #${payload.deployment_id}${source} (git_sha=${JSON.stringify(payload.git_sha ?? null)})`);
|
|
169
|
+
printNotices(payload);
|
|
159
170
|
}
|
|
160
171
|
|
|
161
172
|
export async function rollback(config) {
|
|
162
173
|
const workspace = config.resolveWorkspaceRequired();
|
|
163
174
|
const payload = await deployClient(config, workspace).rollback({ createdBy: process.env.USER ?? null });
|
|
164
175
|
console.log(`Rolled back to deployment #${payload.deployment_id} (from #${payload.rolled_back_from_id})`);
|
|
176
|
+
printNotices(payload);
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
export async function status(config) {
|
package/lib/commands/mcp.mjs
CHANGED
|
@@ -69,7 +69,8 @@ const COMMANDS = {
|
|
|
69
69
|
summary:
|
|
70
70
|
"Promote the latest push to live (or the preview `--env` names), which is what customers " +
|
|
71
71
|
"see. Ask the developer first: this is " +
|
|
72
|
-
"the one step that changes what real visitors get."
|
|
72
|
+
"the one step that changes what real visitors get. Refused when the workspace is connected " +
|
|
73
|
+
"to GitHub: there, live changes only by merging to the default branch.",
|
|
73
74
|
annotations: { readOnlyHint: false, destructiveHint: true }
|
|
74
75
|
},
|
|
75
76
|
rollback: {
|
|
@@ -105,17 +106,18 @@ const COMMANDS = {
|
|
|
105
106
|
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
106
107
|
},
|
|
107
108
|
tokens: {
|
|
108
|
-
usage: "list | create [--env NAME] [--label NAME] | revoke PREFIX",
|
|
109
|
+
usage: "list | create [--env NAME] [--label NAME] [--origin URL]... | origins PREFIX URL... | revoke PREFIX",
|
|
109
110
|
summary:
|
|
110
111
|
"Publishable tokens for the widget and anything built on the SDK. A created token is shown in " +
|
|
111
|
-
"full exactly once."
|
|
112
|
+
"full exactly once. Each token works only from its own allowed origins (--origin, repeatable, or " +
|
|
113
|
+
"`origins PREFIX URL...` to replace the list); the install snippet's token is edited in the console.",
|
|
112
114
|
annotations: { readOnlyHint: false }
|
|
113
115
|
},
|
|
114
116
|
widget: {
|
|
115
117
|
usage: "[--env NAME]",
|
|
116
118
|
summary:
|
|
117
|
-
"What the platform will actually enforce for the widget, the
|
|
118
|
-
"that exist. Read-only:
|
|
119
|
+
"What the platform will actually enforce for the widget, the origins its tokens accept, and the " +
|
|
120
|
+
"tokens that exist. Read-only: the look is edited on the console's Brand page, whose URL it prints.",
|
|
119
121
|
annotations: { readOnlyHint: true }
|
|
120
122
|
},
|
|
121
123
|
whatsapp: {
|
package/lib/commands/misc.mjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { existsSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { stringify as stringifyYaml } from "yaml";
|
|
6
5
|
|
|
7
6
|
import { KEYS, SLUG_FORMAT } from "../config.mjs";
|
|
8
7
|
import { MANIFEST_FILE, manifestPath } from "../workspace.mjs";
|
|
@@ -110,10 +109,13 @@ export async function init(config, args) {
|
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
const manifest = join(target, MANIFEST_FILE);
|
|
113
|
-
writeFileSync(manifest, starterManifest(slug
|
|
112
|
+
writeFileSync(manifest, starterManifest(slug));
|
|
114
113
|
|
|
115
114
|
console.log(`Wrote ${manifest}`);
|
|
116
115
|
if (!authorized) console.log(`Local only for now — \`vatio push\` is what creates ${slug} on the platform.`);
|
|
116
|
+
if (!authorized && String(name ?? "").trim() !== "") {
|
|
117
|
+
console.log("The display name is set in the console (the workspace's Brand page) once it exists.");
|
|
118
|
+
}
|
|
117
119
|
console.log("");
|
|
118
120
|
console.log("Next:");
|
|
119
121
|
console.log(` edit ${MANIFEST_FILE}, then \`vatio push\` and \`vatio publish\``);
|
|
@@ -121,14 +123,10 @@ export async function init(config, args) {
|
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
// The smallest file that deploys: which workspace, and what the agent does.
|
|
124
|
-
// The slug is already validated as a slug, so it needs no quoting
|
|
125
|
-
// name is
|
|
126
|
-
function starterManifest(slug
|
|
126
|
+
// The slug is already validated as a slug, so it needs no quoting. The display
|
|
127
|
+
// name is not here: it is a workspace setting, edited in the console.
|
|
128
|
+
function starterManifest(slug) {
|
|
127
129
|
const lines = ["# Which Vatio workspace this directory deploys to.", `workspace: ${slug}`, ""];
|
|
128
|
-
if (String(name ?? "").trim() !== "") {
|
|
129
|
-
lines.push(stringifyYaml({ business: { name: String(name).trim() } }).trimEnd());
|
|
130
|
-
lines.push("");
|
|
131
|
-
}
|
|
132
130
|
lines.push("agent:");
|
|
133
131
|
lines.push(" instructions: |");
|
|
134
132
|
lines.push(" Describe what this agent does and how it should answer.");
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// secrets / tokens / widget — the three things a workspace has that its
|
|
2
2
|
// manifest deliberately does not carry.
|
|
3
3
|
//
|
|
4
|
-
// A secret is a credential, a publishable token is a credential
|
|
5
|
-
// screen is a read of what the platform
|
|
6
|
-
//
|
|
7
|
-
// vatio.yml and all of them are here.
|
|
4
|
+
// A secret is a credential, a publishable token is a credential (with the
|
|
5
|
+
// origins it accepts), and the widget screen is a read of what the platform
|
|
6
|
+
// will actually enforce, edited in the console. None of them are in vatio.yml.
|
|
8
7
|
|
|
9
8
|
import { deployClient } from "./deploy.mjs";
|
|
10
9
|
import { askSecret, fail, paint, takeValue } from "../support.mjs";
|
|
@@ -66,9 +65,19 @@ export async function secrets(config, args) {
|
|
|
66
65
|
fail("Usage: vatio secrets list|set KEY [VALUE]|rm KEY");
|
|
67
66
|
}
|
|
68
67
|
|
|
68
|
+
// Repeatable: `--origin https://a.com --origin https://b.com`.
|
|
69
|
+
function takeOrigins(args) {
|
|
70
|
+
const origins = [];
|
|
71
|
+
for (let value = takeValue(args, "--origin"); value !== null; value = takeValue(args, "--origin")) {
|
|
72
|
+
origins.push(value);
|
|
73
|
+
}
|
|
74
|
+
return origins;
|
|
75
|
+
}
|
|
76
|
+
|
|
69
77
|
export async function tokens(config, args) {
|
|
70
78
|
const environment = environmentOption(args, "live");
|
|
71
79
|
const label = takeValue(args, "--label") ?? takeValue(args, "-l");
|
|
80
|
+
const origins = takeOrigins(args);
|
|
72
81
|
const workspace = config.resolveWorkspaceRequired();
|
|
73
82
|
const client = deployClient(config, workspace);
|
|
74
83
|
const sub = args.shift() ?? "list";
|
|
@@ -78,7 +87,7 @@ export async function tokens(config, args) {
|
|
|
78
87
|
const list = asArray(payload.publishable_tokens);
|
|
79
88
|
if (list.length === 0) {
|
|
80
89
|
console.log(`No publishable tokens on workspace ${workspace}`);
|
|
81
|
-
console.log(" Create one: vatio tokens create --env live");
|
|
90
|
+
console.log(" Create one: vatio tokens create --env live --origin https://your-site.com");
|
|
82
91
|
return;
|
|
83
92
|
}
|
|
84
93
|
console.log(`Publishable tokens on workspace ${workspace} (${list.length}):`);
|
|
@@ -86,12 +95,14 @@ export async function tokens(config, args) {
|
|
|
86
95
|
const parts = [token.key_prefix ?? token.prefix, token.environment];
|
|
87
96
|
if (token.label) parts.push(token.label);
|
|
88
97
|
console.log(` ${parts.filter(Boolean).join(" ")}`);
|
|
98
|
+
console.log(` origins: ${originsLine(token.allowed_origins)}`);
|
|
89
99
|
}
|
|
90
100
|
return;
|
|
91
101
|
}
|
|
92
102
|
if (sub === "create") {
|
|
93
|
-
const payload = await client.createPublishableToken({ environment, label });
|
|
103
|
+
const payload = await client.createPublishableToken({ environment, label, allowedOrigins: origins });
|
|
94
104
|
console.log(`Created a publishable token for ${environment} on workspace ${workspace}`);
|
|
105
|
+
console.log(` origins: ${originsLine(payload.allowed_origins)}`);
|
|
95
106
|
console.log("");
|
|
96
107
|
// Shown in full exactly once: the platform stores a digest, so this is the
|
|
97
108
|
// only moment the value exists anywhere the developer can copy it.
|
|
@@ -100,6 +111,13 @@ export async function tokens(config, args) {
|
|
|
100
111
|
console.log("Copy it now — it is not shown again.");
|
|
101
112
|
return;
|
|
102
113
|
}
|
|
114
|
+
if (sub === "origins") {
|
|
115
|
+
const prefix = args.shift();
|
|
116
|
+
if (!prefix) fail("Usage: vatio tokens origins PREFIX [URL...] (no URL clears the list)");
|
|
117
|
+
const payload = await client.setPublishableTokenOrigins({ prefix, allowedOrigins: args.splice(0) });
|
|
118
|
+
console.log(`Origins for ${prefix}: ${originsLine(payload.allowed_origins)}`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
103
121
|
if (sub === "revoke") {
|
|
104
122
|
const prefix = args.shift();
|
|
105
123
|
if (!prefix) fail("Usage: vatio tokens revoke PREFIX");
|
|
@@ -108,7 +126,16 @@ export async function tokens(config, args) {
|
|
|
108
126
|
return;
|
|
109
127
|
}
|
|
110
128
|
|
|
111
|
-
fail(
|
|
129
|
+
fail(
|
|
130
|
+
"Usage: vatio tokens list|create [--env live|preview|NAME] [--label NAME] [--origin URL]...|" +
|
|
131
|
+
"origins PREFIX URL...|revoke PREFIX"
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// An empty list is not "anywhere": only Vatio's own pages can use the token.
|
|
136
|
+
function originsLine(origins) {
|
|
137
|
+
const list = asArray(origins);
|
|
138
|
+
return list.length > 0 ? list.join(", ") : "none (add one with `vatio tokens origins PREFIX URL`)";
|
|
112
139
|
}
|
|
113
140
|
|
|
114
141
|
export async function widget(config, args) {
|
|
@@ -118,8 +145,9 @@ export async function widget(config, args) {
|
|
|
118
145
|
|
|
119
146
|
console.log(JSON.stringify(payload, null, 2));
|
|
120
147
|
console.log("");
|
|
121
|
-
console.log(
|
|
122
|
-
console.log(`
|
|
148
|
+
console.log(`The widget's look is edited in the console: ${payload.brand_url ?? "the workspace's Brand page"}`);
|
|
149
|
+
console.log(`Each token's allowed origins: ${payload.tokens_url ?? "the workspace's Tokens page"}`);
|
|
150
|
+
console.log(`A token for this environment: vatio tokens create --env ${environment} --origin https://your-site.com`);
|
|
123
151
|
}
|
|
124
152
|
|
|
125
153
|
function asArray(value) {
|
package/lib/help.mjs
CHANGED
|
@@ -25,7 +25,8 @@ flag, the directory decides.
|
|
|
25
25
|
Deploy:
|
|
26
26
|
push [--env NAME] Validate the workspace, then update a preview
|
|
27
27
|
publish [--env NAME] Promote the latest push to live. --env promotes
|
|
28
|
-
a specific preview instead
|
|
28
|
+
a specific preview instead. Refused when GitHub
|
|
29
|
+
is connected: merge to the default branch
|
|
29
30
|
rollback Restore the previous live deployment
|
|
30
31
|
status Preview and live deployment state
|
|
31
32
|
diff [--env NAME] What this directory would change (default: preview)
|
|
@@ -56,8 +57,11 @@ flag, the directory decides.
|
|
|
56
57
|
Credentials and the widget:
|
|
57
58
|
secrets list|set KEY [VALUE]|rm KEY
|
|
58
59
|
Without VALUE, set asks for it with the input hidden
|
|
59
|
-
tokens list|create [--env NAME] [--label NAME]
|
|
60
|
-
|
|
60
|
+
tokens list|create [--env NAME] [--label NAME] [--origin URL]...|revoke PREFIX
|
|
61
|
+
tokens origins PREFIX URL...
|
|
62
|
+
The origins a token is accepted from (replaces the list)
|
|
63
|
+
widget [--env NAME] What the platform enforces, and the tokens there are;
|
|
64
|
+
the look is edited in the console
|
|
61
65
|
auth --new-key Keypair that signs the JWT saying who a visitor is
|
|
62
66
|
|
|
63
67
|
Channels (your own account serves live; the shared preview always serves preview):
|