@vatio-ai/cli 0.52.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 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 Publishable tokens for the widget and the SDK
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
 
@@ -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
  }
@@ -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 -- the push a few
110
- // lines down applies `business.name` from the manifest anyway.
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) {
@@ -106,17 +106,18 @@ const COMMANDS = {
106
106
  annotations: { readOnlyHint: false, openWorldHint: true }
107
107
  },
108
108
  tokens: {
109
- usage: "list | create [--env NAME] [--label NAME] | revoke PREFIX",
109
+ usage: "list | create [--env NAME] [--label NAME] [--origin URL]... | origins PREFIX URL... | revoke PREFIX",
110
110
  summary:
111
111
  "Publishable tokens for the widget and anything built on the SDK. A created token is shown in " +
112
- "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.",
113
114
  annotations: { readOnlyHint: false }
114
115
  },
115
116
  widget: {
116
117
  usage: "[--env NAME]",
117
118
  summary:
118
- "What the platform will actually enforce for the widget, the origin allowlist, and the tokens " +
119
- "that exist. Read-only: vatio.yml owns every field, so push is what changes it.",
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.",
120
121
  annotations: { readOnlyHint: true }
121
122
  },
122
123
  whatsapp: {
@@ -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, name));
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; a display
125
- // name is whatever the developer typed, so it goes through the YAML emitter.
126
- function starterManifest(slug, name) {
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, and the widget
5
- // screen is a read of what the platform will actually enforce. None of them
6
- // belong in a file that gets committed, which is why none of them are in
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("Usage: vatio tokens list|create [--env live|preview|NAME] [--label NAME]|revoke PREFIX");
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("The widget's look comes from vatio.yml's `widget:` block — edit it and push.");
122
- console.log(`A token for this environment: vatio tokens create --env ${environment}`);
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
@@ -57,8 +57,11 @@ flag, the directory decides.
57
57
  Credentials and the widget:
58
58
  secrets list|set KEY [VALUE]|rm KEY
59
59
  Without VALUE, set asks for it with the input hidden
60
- tokens list|create [--env NAME] [--label NAME]|revoke PREFIX
61
- widget [--env NAME] What the platform enforces, and the tokens there are
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
62
65
  auth --new-key Keypair that signs the JWT saying who a visitor is
63
66
 
64
67
  Channels (your own account serves live; the shared preview always serves preview):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vatio-ai/cli",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "description": "Vatio CLI — deploy and manage Vatio agent workspaces",
5
5
  "keywords": [
6
6
  "vatio",