@vatio-ai/cli 0.52.0 → 0.54.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.
@@ -1,74 +1,91 @@
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
- import { askSecret, fail, paint, takeValue } from "../support.mjs";
9
+ import { askSecret, branchDefault, fail, paint, takeValue } from "../support.mjs";
11
10
 
12
- // live, preview, or a named preview like pr-42. This used to accept only the
13
- // first two, which meant a workspace could deploy to `pr-42` and then have no
14
- // way to mint the publishable token that makes it embeddable.
11
+ // live, preview, or a branch's own like fix-pagos.
15
12
  const ENVIRONMENT_NAME = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/;
16
13
  const ENVIRONMENT_MAX = 40;
17
14
 
18
- function environmentOption(args, fallback) {
19
- const value =
20
- takeValue(args, "--env") ?? takeValue(args, "--environment") ?? takeValue(args, "-e") ?? fallback;
15
+ // On a branch, its environment unless told otherwise (see branch.mjs).
16
+ function environmentOption(args, fallback, cwd) {
17
+ const given = takeValue(args, "--env") ?? takeValue(args, "--environment") ?? takeValue(args, "-e");
18
+ const value = given ?? branchDefault(cwd, fallback);
19
+ if (value === null) return null;
21
20
  if (String(value).length > ENVIRONMENT_MAX || !ENVIRONMENT_NAME.test(String(value))) {
22
- fail(`Unknown environment "${value}" (use live, preview, or a preview name like pr-42)`);
21
+ fail(`Unknown environment "${value}" (use live, preview, or a branch's environment like fix-pagos)`);
23
22
  }
24
23
  return String(value);
25
24
  }
26
25
 
26
+ // Without an environment a secret is the value everywhere. On a branch, or
27
+ // with --env, it is the value only that environment uses -- a preview pointing
28
+ // its tools at staging while live keeps production.
27
29
  export async function secrets(config, args) {
28
30
  const workspace = config.resolveWorkspaceRequired();
29
31
  const client = deployClient(config, workspace);
32
+ const environment = environmentOption(args, null, config.workspaceRoot);
33
+ const where = environment ? `${environment} on workspace ${workspace}` : `workspace ${workspace}`;
30
34
  const sub = args.shift() ?? "list";
31
35
 
32
36
  if (sub === "list") {
33
- const payload = await client.listSecrets();
37
+ const payload = await client.listSecrets({ environment });
34
38
  const keys = asArray(payload.secrets);
35
39
  if (keys.length === 0) {
36
- console.log(`No secrets on workspace ${workspace}`);
40
+ console.log(`No secrets on ${where}`);
37
41
  console.log(" Set one: vatio secrets set STRIPE_KEY sk_live_…");
38
42
  return;
39
43
  }
40
44
  // Keys only, never values. The platform does not hand a value back once it
41
45
  // is stored, and a CLI that printed them would be a CLI that leaks them
42
46
  // into a terminal scrollback.
43
- console.log(`Secrets on workspace ${workspace} (${keys.length}):`);
44
- for (const secret of keys) console.log(` ${secret.key ?? secret}`);
47
+ console.log(`Secrets on ${where} (${keys.length}):`);
48
+ for (const secret of keys) {
49
+ const scope = secret.environment ? ` (only ${secret.environment})` : "";
50
+ console.log(` ${secret.key ?? secret}${scope}`);
51
+ }
45
52
  return;
46
53
  }
47
54
  if (sub === "set") {
48
55
  const key = args.shift();
49
- if (!key) fail("Usage: vatio secrets set KEY [VALUE]");
56
+ if (!key) fail("Usage: vatio secrets set KEY [VALUE] [--env NAME]");
50
57
  // Asked for rather than typed on the command line, where it would stay in
51
58
  // shell history.
52
59
  const value = args.shift() ?? (await askSecret(`${paint("green", "?")} Paste your secret: `));
53
60
  if (value === "") fail(`No value given for ${key}`);
54
- await client.upsertSecret({ key, value });
55
- console.log(`${paint("green", "✓")} Set secret ${key} on workspace ${workspace}`);
61
+ await client.upsertSecret({ key, value, environment });
62
+ console.log(`${paint("green", "✓")} Set secret ${key} on ${where}`);
56
63
  return;
57
64
  }
58
65
  if (sub === "rm" || sub === "remove" || sub === "delete") {
59
66
  const key = args.shift();
60
- if (!key) fail("Usage: vatio secrets rm KEY");
61
- await client.deleteSecret({ key });
62
- console.log(`Removed ${key} from workspace ${workspace}`);
67
+ if (!key) fail("Usage: vatio secrets rm KEY [--env NAME]");
68
+ await client.deleteSecret({ key, environment });
69
+ console.log(`Removed ${key} from ${where}`);
63
70
  return;
64
71
  }
65
72
 
66
- fail("Usage: vatio secrets list|set KEY [VALUE]|rm KEY");
73
+ fail("Usage: vatio secrets list|set KEY [VALUE]|rm KEY [--env NAME]");
74
+ }
75
+
76
+ // Repeatable: `--origin https://a.com --origin https://b.com`.
77
+ function takeOrigins(args) {
78
+ const origins = [];
79
+ for (let value = takeValue(args, "--origin"); value !== null; value = takeValue(args, "--origin")) {
80
+ origins.push(value);
81
+ }
82
+ return origins;
67
83
  }
68
84
 
69
85
  export async function tokens(config, args) {
70
- const environment = environmentOption(args, "live");
86
+ const environment = environmentOption(args, "live", config.workspaceRoot);
71
87
  const label = takeValue(args, "--label") ?? takeValue(args, "-l");
88
+ const origins = takeOrigins(args);
72
89
  const workspace = config.resolveWorkspaceRequired();
73
90
  const client = deployClient(config, workspace);
74
91
  const sub = args.shift() ?? "list";
@@ -78,7 +95,7 @@ export async function tokens(config, args) {
78
95
  const list = asArray(payload.publishable_tokens);
79
96
  if (list.length === 0) {
80
97
  console.log(`No publishable tokens on workspace ${workspace}`);
81
- console.log(" Create one: vatio tokens create --env live");
98
+ console.log(" Create one: vatio tokens create --env live --origin https://your-site.com");
82
99
  return;
83
100
  }
84
101
  console.log(`Publishable tokens on workspace ${workspace} (${list.length}):`);
@@ -86,12 +103,14 @@ export async function tokens(config, args) {
86
103
  const parts = [token.key_prefix ?? token.prefix, token.environment];
87
104
  if (token.label) parts.push(token.label);
88
105
  console.log(` ${parts.filter(Boolean).join(" ")}`);
106
+ console.log(` origins: ${originsLine(token.allowed_origins)}`);
89
107
  }
90
108
  return;
91
109
  }
92
110
  if (sub === "create") {
93
- const payload = await client.createPublishableToken({ environment, label });
111
+ const payload = await client.createPublishableToken({ environment, label, allowedOrigins: origins });
94
112
  console.log(`Created a publishable token for ${environment} on workspace ${workspace}`);
113
+ console.log(` origins: ${originsLine(payload.allowed_origins)}`);
95
114
  console.log("");
96
115
  // Shown in full exactly once: the platform stores a digest, so this is the
97
116
  // only moment the value exists anywhere the developer can copy it.
@@ -100,6 +119,13 @@ export async function tokens(config, args) {
100
119
  console.log("Copy it now — it is not shown again.");
101
120
  return;
102
121
  }
122
+ if (sub === "origins") {
123
+ const prefix = args.shift();
124
+ if (!prefix) fail("Usage: vatio tokens origins PREFIX [URL...] (no URL clears the list)");
125
+ const payload = await client.setPublishableTokenOrigins({ prefix, allowedOrigins: args.splice(0) });
126
+ console.log(`Origins for ${prefix}: ${originsLine(payload.allowed_origins)}`);
127
+ return;
128
+ }
103
129
  if (sub === "revoke") {
104
130
  const prefix = args.shift();
105
131
  if (!prefix) fail("Usage: vatio tokens revoke PREFIX");
@@ -108,18 +134,28 @@ export async function tokens(config, args) {
108
134
  return;
109
135
  }
110
136
 
111
- fail("Usage: vatio tokens list|create [--env live|preview|NAME] [--label NAME]|revoke PREFIX");
137
+ fail(
138
+ "Usage: vatio tokens list|create [--env live|preview|NAME] [--label NAME] [--origin URL]...|" +
139
+ "origins PREFIX URL...|revoke PREFIX"
140
+ );
141
+ }
142
+
143
+ // An empty list is not "anywhere": only Vatio's own pages can use the token.
144
+ function originsLine(origins) {
145
+ const list = asArray(origins);
146
+ return list.length > 0 ? list.join(", ") : "none (add one with `vatio tokens origins PREFIX URL`)";
112
147
  }
113
148
 
114
149
  export async function widget(config, args) {
115
- const environment = environmentOption(args, "live");
150
+ const environment = environmentOption(args, "live", config.workspaceRoot);
116
151
  const workspace = config.resolveWorkspaceRequired();
117
152
  const payload = await deployClient(config, workspace).widget();
118
153
 
119
154
  console.log(JSON.stringify(payload, null, 2));
120
155
  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}`);
156
+ console.log(`The widget's look is edited in the console: ${payload.brand_url ?? "the workspace's Brand page"}`);
157
+ console.log(`Each token's allowed origins: ${payload.tokens_url ?? "the workspace's Tokens page"}`);
158
+ console.log(`A token for this environment: vatio tokens create --env ${environment} --origin https://your-site.com`);
123
159
  }
124
160
 
125
161
  function asArray(value) {
package/lib/help.mjs CHANGED
@@ -12,7 +12,10 @@ A workspace is any directory with a vatio.yml. Every command except init reads
12
12
  \`workspace:\` from the vatio.yml at or above the cwd — there is no --workspace
13
13
  flag, the directory decides.
14
14
 
15
- --env NAME targets a deployment: live, preview, or a preview name like pr-42.
15
+ --env NAME targets an environment: live, preview, or a branch's own. On a git
16
+ branch other than the default one, the branch's environment is the default
17
+ (fix/pagos -> fix-pagos): its own agent, knowledge changes and secret values,
18
+ and the one its pull request deploys to. Elsewhere commands default as listed.
16
19
 
17
20
  Getting started:
18
21
  init [SLUG] [--name NAME] Create vatio.yml here, and the remote to match
@@ -24,11 +27,15 @@ flag, the directory decides.
24
27
 
25
28
  Deploy:
26
29
  push [--env NAME] Validate the workspace, then update a preview
27
- publish [--env NAME] Promote the latest push to live. --env promotes
28
- a specific preview instead. Refused when GitHub
29
- is connected: merge to the default branch
30
- rollback Restore the previous live deployment
30
+ publish [--env NAME] Promote an environment to live, with its knowledge
31
+ changes. Without --env or a branch, the latest
32
+ push. Refused when GitHub is connected: merge
33
+ the branch's pull request instead
34
+ rollback Put live back to the previous snapshot: config and knowledge
31
35
  status Preview and live deployment state
36
+ env [NAME] This environment: token, widget snippet, link,
37
+ knowledge changes and conflicts
38
+ env list | rm NAME Every environment / remove one and what it holds
32
39
  diff [--env NAME] What this directory would change (default: preview)
33
40
  --env live answers "what would publishing change?"
34
41
  diff --stat|--name-only|--format json|--full
@@ -45,27 +52,37 @@ flag, the directory decides.
45
52
  kb [list] Bases, and whether a deployed agent reads each
46
53
  kb show NAME One base: its sites and its entries
47
54
  kb create NAME | rm NAME
48
- kb write BASE ENTRY [FILE] Write an entry, from FILE or stdin
49
- kb cat BASE ENTRY Print an entry's markdown
50
- kb rm-entry BASE ENTRY Delete one entry
55
+ kb write BASE ENTRY [FILE] Write an entry in this environment, from FILE or stdin
56
+ kb cat BASE ENTRY [--version N]
57
+ Print an entry's markdown, or one old version
58
+ kb rm-entry BASE ENTRY Delete one entry in this environment
51
59
  kb follow BASE URL Read a site into the base, nightly
52
60
  kb unfollow BASE URL | refresh BASE [URL]
53
- kb status BASE What the draft has that live does not
54
- kb publish BASE [-m NOTE] Take the draft live (preview already has it)
55
- kb history BASE | rollback BASE NUMBER
61
+ kb status BASE What this environment changed that live does not
62
+ have, and what live changed underneath
63
+ kb publish BASE Take this environment's changes live, alone
64
+ kb history BASE What reached live, newest first
65
+ (entry commands take --env; --env live writes live)
56
66
 
57
67
  Credentials and the widget:
58
- secrets list|set KEY [VALUE]|rm KEY
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
68
+ secrets list|set KEY [VALUE]|rm KEY [--env NAME]
69
+ Without VALUE, set asks for it with the input hidden.
70
+ On a branch or with --env, the value only that
71
+ environment uses; otherwise the value everywhere
72
+ tokens list|create [--env NAME] [--label NAME] [--origin URL]...|revoke PREFIX
73
+ tokens origins PREFIX URL...
74
+ The origins a token is accepted from (replaces the list)
75
+ widget [--env NAME] What the platform enforces, and the tokens there are;
76
+ the look is edited in the console
62
77
  auth --new-key Keypair that signs the JWT saying who a visitor is
63
78
 
64
- Channels (your own account serves live; the shared preview always serves preview):
79
+ Channels (your own account serves live; each test phone or account on the shared
80
+ preview reaches the environment it points at -- add and point take --env, and
81
+ default to the branch you are on):
65
82
  whatsapp [status]|connect|check|activate|deactivate|disconnect
66
- whatsapp numbers list|add PHONE|verify PHONE CODE|resend PHONE|remove PHONE
83
+ whatsapp numbers list|add PHONE|point PHONE|verify PHONE CODE|resend PHONE|remove PHONE
67
84
  instagram [status]|connect|check|disconnect
68
- instagram accounts list|add @HANDLE|verify CODE|resend ID|remove ID
85
+ instagram accounts list|add @HANDLE|point ID|verify CODE|resend ID|remove ID
69
86
 
70
87
  Everything else:
71
88
  issue "what should change"|list|show ID|comment ID "reply"|--template
package/lib/http.mjs CHANGED
@@ -37,7 +37,10 @@ function unprocessableMessage(body) {
37
37
  .map((entry) => (typeof entry === "object" ? String(entry.message ?? "") : String(entry)));
38
38
  if (errors.length > 0) return errors.join("; ");
39
39
 
40
- const message = body.error_message ?? body.error;
40
+ // `{ error: { code, message } }` is how the knowledge and token endpoints
41
+ // say it.
42
+ const error = body.error && typeof body.error === "object" && !Array.isArray(body.error) ? body.error.message : body.error;
43
+ const message = body.error_message ?? error;
41
44
  if (message != null) return Array.isArray(message) ? message.join("; ") : String(message);
42
45
 
43
46
  return "Unprocessable";
package/lib/support.mjs CHANGED
@@ -3,6 +3,8 @@
3
3
 
4
4
  import { execFileSync } from "node:child_process";
5
5
 
6
+ import { branchEnvironment } from "./branch.mjs";
7
+
6
8
  export class UserError extends Error {
7
9
  constructor(message) {
8
10
  super(message);
@@ -16,15 +18,17 @@ export function fail(message) {
16
18
  throw new UserError(message);
17
19
  }
18
20
 
19
- // `--env NAME` is the one way to say which deployment a command is about:
20
- // live, preview, or a named preview like pr-42.
21
+ // `--env NAME` is the one way to say which environment a command is about:
22
+ // live, preview, or a branch's own like fix-pagos.
21
23
  //
22
- // The default is the command's own and is passed in, because the sensible
23
- // answer differs: push writes a preview, pull brings down whatever is actually
24
- // running. `--as` was the spelling on push and publish and stays as an alias --
25
- // it is sitting in CI jobs, and there is nothing wrong with what it does.
26
- export function takeEnv(args, { fallback = null } = {}) {
27
- let environment = fallback;
24
+ // Without it, a workspace checked out on a branch other than the default one
25
+ // means that branch's environment (see branch.mjs), said once on stderr so it
26
+ // is never a surprise. Otherwise the default is the command's own and is passed
27
+ // in, because the sensible answer differs: push writes a preview, tokens are
28
+ // for live. `--as` was the spelling on push and publish and stays as an alias
29
+ // -- it is sitting in CI jobs, and there is nothing wrong with what it does.
30
+ export function takeEnv(args, { fallback = null, cwd = null } = {}) {
31
+ let environment;
28
32
  const rest = [];
29
33
 
30
34
  for (let i = 0; i < args.length; i += 1) {
@@ -39,13 +43,22 @@ export function takeEnv(args, { fallback = null } = {}) {
39
43
  continue;
40
44
  }
41
45
  i += 1;
42
- if (i >= args.length) fail(`${match[0]} needs a deployment name (live, preview, or a preview name)`);
46
+ if (i >= args.length) fail(`${match[0]} needs an environment name (live, preview, or a branch's)`);
43
47
  environment = args[i];
44
48
  }
45
49
 
46
50
  args.length = 0;
47
51
  args.push(...rest);
48
- return environment;
52
+ return environment !== undefined ? environment : branchDefault(cwd, fallback);
53
+ }
54
+
55
+ // The branch's environment when the workspace is on one, else `fallback`.
56
+ export function branchDefault(cwd, fallback) {
57
+ const fromBranch = cwd ? branchEnvironment(cwd) : null;
58
+ if (!fromBranch) return fallback;
59
+
60
+ process.stderr.write(paint("cyan", `vatio: environment ${fromBranch.environment} (branch ${fromBranch.branch})`) + "\n");
61
+ return fromBranch.environment;
49
62
  }
50
63
 
51
64
  export function takeFlag(args, name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vatio-ai/cli",
3
- "version": "0.52.0",
3
+ "version": "0.54.0",
4
4
  "description": "Vatio CLI — deploy and manage Vatio agent workspaces",
5
5
  "keywords": [
6
6
  "vatio",