@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.
@@ -4,10 +4,13 @@
4
4
  // vatio.yml is a list of *names*, and a push neither fills nor empties one.
5
5
  // That is why this is a command and not part of the manifest.
6
6
  //
7
- // Every write goes to the base's draft, which preview answers from. Live
8
- // answers from the last published snapshot and changes only on `publish` --
9
- // or `rollback`, which points it at an earlier one. So: write, try it on
10
- // preview, publish.
7
+ // The entries are live. Reads and writes here are as one environment sees
8
+ // them: on a branch, the branch's own (see branch.mjs), which keeps the
9
+ // entries it changed on top of live and answers from them in its preview;
10
+ // otherwise `preview`; `--env live` writes live directly, the way a supervisor
11
+ // in the inbox does. `vatio publish`, or merging the branch's pull request,
12
+ // takes the changes live with the agent -- `vatio kb publish` takes them alone.
13
+ // So: write, try it on the branch's preview, publish.
11
14
  //
12
15
  // A base holds entries, and an entry is markdown. Either you wrote it, or a
13
16
  // site wrote it from a page it read — and a site is one URL or one pattern,
@@ -16,32 +19,48 @@
16
19
  import { readFileSync, statSync } from "node:fs";
17
20
 
18
21
  import { deployClient } from "./deploy.mjs";
19
- import { fail } from "../support.mjs";
22
+ import { fail, takeEnv, takeValue } from "../support.mjs";
23
+
24
+ // Only what reads or writes entries takes an environment; bases and sites
25
+ // belong to the workspace.
26
+ const ENVIRONMENT_SCOPED = new Set(["list", "show", "write", "cat", "rm-entry", "status", "publish"]);
20
27
 
21
28
  export async function kb(config, args) {
22
29
  const workspace = config.resolveWorkspaceRequired();
23
30
  const client = deployClient(config, workspace);
24
31
  const sub = args.shift() ?? "list";
32
+ const env = ENVIRONMENT_SCOPED.has(sub) ? takeEnv(args, { fallback: null, cwd: config.workspaceRoot }) : null;
33
+ const ctx = { client, workspace, env };
25
34
 
26
35
  switch (sub) {
27
- case "list": return await list(client, workspace);
28
- case "show": return await show(client, workspace, args.shift());
36
+ case "list": return await list(ctx);
37
+ case "show": return await show(ctx, args.shift());
29
38
  case "create": return await create(client, workspace, args.splice(0));
30
39
  case "rm": return await remove(client, workspace, args.shift());
31
- case "write": return await write(client, workspace, args);
32
- case "cat": return await cat(client, args.shift(), args.shift());
33
- case "rm-entry": return await removeEntry(client, workspace, args.shift(), args.shift());
40
+ case "write": return await write(ctx, args);
41
+ case "cat": return await cat(ctx, args);
42
+ case "rm-entry": return await removeEntry(ctx, args.shift(), args.shift());
34
43
  case "follow": return await follow(client, workspace, args.shift(), args.shift());
35
44
  case "unfollow": return await unfollow(client, workspace, args.shift(), args.shift());
36
45
  case "refresh": return await refresh(client, workspace, args.shift(), args.shift());
37
- case "status": return await status(client, workspace, args.shift());
38
- case "publish": return await publish(client, workspace, args);
46
+ case "status": return await status(ctx, args.shift());
47
+ case "publish": return await publish(ctx, args.shift());
39
48
  case "history": return await history(client, workspace, args.shift());
40
- case "rollback": return await rollback(client, workspace, args.shift(), args.shift());
49
+ case "rollback":
50
+ return fail(
51
+ "`vatio kb rollback` was removed. Restore one entry instead: `vatio kb history BASE` lists what " +
52
+ "reached live, `vatio kb cat BASE ENTRY --version N > old.md` prints one, and " +
53
+ "`vatio kb write BASE ENTRY old.md --env live` brings it back."
54
+ );
41
55
  default: fail(`Unknown kb subcommand: ${sub}\n\n${usage()}`);
42
56
  }
43
57
  }
44
58
 
59
+ // `preview` when the platform chose, for messages.
60
+ function envName(env) {
61
+ return env ?? "preview";
62
+ }
63
+
45
64
  function usage() {
46
65
  return `usage:
47
66
  vatio kb List knowledge bases
@@ -49,19 +68,21 @@ function usage() {
49
68
  vatio kb create NAME [NAME...] Create one base, or several
50
69
  vatio kb rm NAME Delete a base (must be unreferenced)
51
70
  vatio kb write BASE ENTRY [FILE] Write an entry, from FILE or stdin
52
- vatio kb cat BASE ENTRY Print an entry's markdown
71
+ vatio kb cat BASE ENTRY [--version N] Print an entry's markdown, or one of its versions
53
72
  vatio kb rm-entry BASE ENTRY Delete one entry
54
73
  vatio kb follow BASE URL Read a site into the base, nightly
55
74
  vatio kb unfollow BASE URL Stop reading it, and drop its entries
56
75
  vatio kb refresh BASE [URL] Read the sites again now
57
- vatio kb status BASE What the draft has that live does not
58
- vatio kb publish BASE [-m NOTE] Take the draft live
59
- vatio kb history BASE Every publication, newest first
60
- vatio kb rollback BASE NUMBER Point live back at publication NUMBER`;
76
+ vatio kb status BASE What this environment changed that live does not have
77
+ vatio kb publish BASE Take this environment's changes live, without the agent
78
+ vatio kb history BASE What reached live, newest first
79
+
80
+ Entry commands, status and publish take --env NAME; on a branch they default
81
+ to the branch's environment, otherwise to preview. --env live writes live.`;
61
82
  }
62
83
 
63
- async function list(client, workspace) {
64
- const bases = asArray((await client.knowledgeBases()).data);
84
+ async function list({ client, workspace, env }) {
85
+ const bases = asArray((await client.knowledgeBases({ environment: env })).data);
65
86
  if (bases.length === 0) {
66
87
  console.log(`No knowledge bases on workspace ${workspace}`);
67
88
  console.log("Create one with `vatio kb create docs`, then add `knowledge: [docs]` to vatio.yml");
@@ -70,9 +91,10 @@ async function list(client, workspace) {
70
91
 
71
92
  console.log(`Knowledge bases on workspace ${workspace} (${bases.length}):`);
72
93
  for (const row of bases) {
94
+ const pending = row.pending_changes ? ` ${row.pending_changes} unpublished in ${envName(env)}` : "";
73
95
  console.log(
74
96
  ` ${row.name} entries=${row.entries_count} ready=${row.answerable_count} ` +
75
- `sites=${row.sites_count}${referenceNote(row)}`
97
+ `sites=${row.sites_count}${pending}${referenceNote(row)}`
76
98
  );
77
99
  }
78
100
  }
@@ -84,15 +106,17 @@ function referenceNote(row) {
84
106
  return row.referenced ? "" : " (not referenced by any deployed agent)";
85
107
  }
86
108
 
87
- async function show(client, workspace, name) {
109
+ async function show({ client, workspace, env }, name) {
88
110
  if (!name) fail("usage: vatio kb show NAME");
89
111
 
90
- const base = await client.knowledgeBase(name);
112
+ const base = await client.knowledgeBase(name, { environment: env });
91
113
  console.log(
92
114
  `${base.name} on workspace ${workspace}: entries=${base.entries_count} ` +
93
115
  `ready=${base.answerable_count}${referenceNote(base)}`
94
116
  );
95
- console.log(` ${liveNote(base)}`);
117
+ if (base.pending_changes) {
118
+ console.log(` ${base.pending_changes} change(s) in ${envName(env)} that live does not have — \`vatio kb status ${base.name}\``);
119
+ }
96
120
 
97
121
  const sites = asArray(base.sites);
98
122
  if (sites.length > 0) {
@@ -116,23 +140,15 @@ async function show(client, workspace, name) {
116
140
  return;
117
141
  }
118
142
 
119
- console.log("\n entries:");
143
+ console.log(`\n entries, as ${envName(env)} sees them:`);
120
144
  for (const entry of entries) {
121
145
  const origin = entry.written ? "written here" : entry.url;
122
146
  const version = entry.version ? ` v${entry.version}` : "";
123
- console.log(` ${entry.name}${version} ${entry.state ?? "?"} ${origin}`);
147
+ const unpublished = entry.published === false ? " (not live yet)" : "";
148
+ console.log(` ${entry.name}${version} ${entry.state ?? "?"} ${origin}${unpublished}`);
124
149
  }
125
150
  }
126
151
 
127
- // Where live stands, and whether the draft has moved past it.
128
- function liveNote(base) {
129
- const live = base.live_snapshot ? `live=publication #${base.live_snapshot}` : "live=never published";
130
- const pending = base.pending_changes
131
- ? ` ${base.pending_changes} unpublished change${base.pending_changes === 1 ? "" : "s"} — \`vatio kb publish ${base.name}\``
132
- : "";
133
- return `${live}${pending}`;
134
- }
135
-
136
152
  // Several names, because the moment this command is most often run is a push
137
153
  // that just refused for naming bases that do not exist yet -- and that push
138
154
  // names all of them at once. One base per invocation turned a first deploy
@@ -177,7 +193,7 @@ async function remove(client, workspace, name) {
177
193
  // vatio kb cat docs horarios > horarios.md
178
194
  // $EDITOR horarios.md
179
195
  // vatio kb write docs horarios horarios.md
180
- async function write(client, workspace, args) {
196
+ async function write({ client, workspace, env }, args) {
181
197
  const [base, name, file] = args;
182
198
  if (!base || !name) fail("usage: vatio kb write BASE ENTRY [FILE] (or pipe the markdown in)");
183
199
  if (file && !isFile(file)) fail(`No such file: ${file}`);
@@ -187,27 +203,33 @@ async function write(client, workspace, args) {
187
203
  fail("Nothing to write: pass a FILE, or pipe the markdown in on stdin.");
188
204
  }
189
205
 
190
- const row = await client.writeKnowledgeEntry({ base, name, content });
206
+ const row = await client.writeKnowledgeEntry({ base, name, content, environment: env });
191
207
  const version = row.version ? ` (v${row.version})` : "";
192
- console.log(`Wrote ${row.name}${version} to the draft of ${base} on workspace ${workspace}`);
193
- console.log(`Preview answers with it now; \`vatio kb publish ${base}\` takes it live.`);
208
+ if (env === "live") {
209
+ console.log(`Wrote ${row.name}${version} to ${base} on workspace ${workspace}. Live answers with it now.`);
210
+ return;
211
+ }
212
+ console.log(`Wrote ${row.name}${version} to ${base} in ${envName(env)} on workspace ${workspace}`);
213
+ console.log(`${envName(env)} answers with it now; \`vatio publish\` takes it live with the agent.`);
194
214
  }
195
215
 
196
216
  // Prints exactly what the platform holds, so a diff against the file you
197
217
  // pushed is a real answer to "is what I sent what it has".
198
- async function cat(client, base, name) {
199
- if (!base || !name) fail("usage: vatio kb cat BASE ENTRY");
218
+ async function cat({ client, env }, args) {
219
+ const version = takeValue(args, "--version");
220
+ const [base, name] = args;
221
+ if (!base || !name) fail("usage: vatio kb cat BASE ENTRY [--version N]");
200
222
 
201
- const row = await client.knowledgeEntry({ base, name });
223
+ const row = await client.knowledgeEntry({ base, name, environment: env, version });
202
224
  const content = row.content ?? "";
203
225
  process.stdout.write(content.endsWith("\n") ? content : `${content}\n`);
204
226
  }
205
227
 
206
- async function removeEntry(client, workspace, base, name) {
228
+ async function removeEntry({ client, workspace, env }, base, name) {
207
229
  if (!base || !name) fail("usage: vatio kb rm-entry BASE ENTRY");
208
230
 
209
- await client.deleteKnowledgeEntry({ base, name });
210
- console.log(`Deleted entry ${name} from ${base} on workspace ${workspace}`);
231
+ await client.deleteKnowledgeEntry({ base, name, environment: env });
232
+ console.log(`Deleted entry ${name} from ${base} in ${envName(env)} on workspace ${workspace}`);
211
233
  }
212
234
 
213
235
  // One URL, and the shape of it is the whole configuration. A URL is that URL;
@@ -246,65 +268,61 @@ async function refresh(client, workspace, base, url) {
246
268
  console.log(`Runs in the background — check progress with \`vatio kb show ${base}\``);
247
269
  }
248
270
 
249
- // The draft against live, entry by entry -- what `publish` would do.
250
- async function status(client, workspace, base) {
271
+ // What this environment changed against live, entry by entry -- what
272
+ // publishing it would do, and which entries live changed underneath.
273
+ async function status({ client, workspace, env }, base) {
251
274
  if (!base) fail("usage: vatio kb status BASE");
252
275
 
253
- const changes = asArray((await client.knowledgeChanges(base)).data);
276
+ const payload = await client.knowledgeChanges(base, { environment: env });
277
+ const changes = asArray(payload.data);
254
278
  if (changes.length === 0) {
255
- console.log(`${base} on workspace ${workspace}: everything is published`);
279
+ console.log(`${base} in ${envName(env)} on workspace ${workspace}: nothing that live does not have`);
256
280
  return;
257
281
  }
258
282
 
259
- console.log(`${base} on workspace ${workspace}: ${changes.length} unpublished change${changes.length === 1 ? "" : "s"}`);
283
+ console.log(`${base} in ${envName(env)} on workspace ${workspace}: ${changes.length} change${changes.length === 1 ? "" : "s"} live does not have`);
260
284
  for (const change of changes) {
261
285
  const from = change.live_version ? `v${change.live_version}` : "—";
262
- const to = change.draft_version ? `v${change.draft_version}` : "—";
263
- console.log(` ${change.change.padEnd(8)} ${change.name} ${from} → ${to}`);
286
+ const to = change.version ?? change.draft_version;
287
+ const conflict = change.conflict ? " CONFLICT: live changed it after you did" : "";
288
+ console.log(` ${change.change.padEnd(8)} ${change.name} ${from} → v${to}${conflict}`);
289
+ }
290
+ if (changes.some((change) => change.conflict)) {
291
+ console.log(`\nRead each conflicting entry from live (\`vatio kb cat ${base} ENTRY --env live\`), write your change on top, and it clears.`);
292
+ if (payload.review_url) console.log(`Or resolve them side by side: ${payload.review_url}`);
293
+ } else {
294
+ console.log("\n`vatio publish` takes them live with the agent; `vatio kb publish` takes them alone.");
264
295
  }
265
- console.log(`\nPublish with \`vatio kb publish ${base} -m "what changed"\``);
266
296
  }
267
297
 
268
- async function publish(client, workspace, args) {
269
- const base = args.shift();
270
- if (!base) fail("usage: vatio kb publish BASE [-m NOTE]");
298
+ async function publish({ client, workspace, env }, base) {
299
+ if (!base) fail("usage: vatio kb publish BASE [--env NAME]");
271
300
 
272
- let note = "";
273
- const flag = args.findIndex((arg) => arg === "-m" || arg === "--note");
274
- if (flag !== -1) note = args[flag + 1] ?? "";
275
-
276
- const snapshot = await client.publishKnowledgeBase({ base, note });
277
- console.log(`Published ${base} on workspace ${workspace} as #${snapshot.number} (${snapshot.entries} entries)`);
278
- console.log(`Live answers from it now. Undo with \`vatio kb rollback ${base} NUMBER\` — see \`vatio kb history ${base}\`.`);
301
+ const result = await client.publishKnowledgeBase({ base, environment: env });
302
+ const names = asArray(result.published);
303
+ console.log(`Published ${names.length} change${names.length === 1 ? "" : "s"} to ${base} from ${envName(env)} on workspace ${workspace}: ${names.join(", ")}`);
304
+ console.log("Live answers from them now.");
279
305
  }
280
306
 
307
+ // Every version that reached live, newest first. Going back is writing one
308
+ // of them again.
281
309
  async function history(client, workspace, base) {
282
310
  if (!base) fail("usage: vatio kb history BASE");
283
311
 
284
- const snapshots = asArray((await client.knowledgeSnapshots(base)).data);
285
- if (snapshots.length === 0) {
286
- console.log(`${base} on workspace ${workspace} was never published — \`vatio kb publish ${base}\``);
312
+ const versions = asArray((await client.knowledgeHistory(base)).data);
313
+ if (versions.length === 0) {
314
+ console.log(`Nothing in ${base} on workspace ${workspace} has reached live yet`);
287
315
  return;
288
316
  }
289
317
 
290
- console.log(`Publications of ${base} on workspace ${workspace}:`);
291
- for (const snapshot of snapshots) {
292
- const live = snapshot.live ? " ← live" : "";
293
- const who = snapshot.author ? ` ${snapshot.author}` : "";
294
- const note = snapshot.note ? ` ${snapshot.note}` : "";
295
- console.log(` #${snapshot.number} ${snapshot.created_at}${who} entries=${snapshot.entries}${note}${live}`);
318
+ console.log(`What reached live in ${base} on workspace ${workspace}:`);
319
+ for (const row of versions) {
320
+ const who = row.author ? ` ${row.author}` : "";
321
+ const what = row.deleted ? " deleted" : "";
322
+ console.log(` ${row.published_at} ${row.name} v${row.version} ${row.source}${who}${what}`);
296
323
  }
297
324
  }
298
325
 
299
- // Only the pointer moves: the draft, and every later publication, stay.
300
- async function rollback(client, workspace, base, number) {
301
- if (!base || !number || !/^\d+$/.test(number)) fail("usage: vatio kb rollback BASE NUMBER");
302
-
303
- await client.rollbackKnowledgeBase({ base, number });
304
- console.log(`Live for ${base} on workspace ${workspace} is publication #${number} again`);
305
- console.log("The draft is untouched; publish again whenever it is ready.");
306
- }
307
-
308
326
  // Blocking on purpose: `vatio kb write docs refunds < refunds.md` should behave
309
327
  // like every other filter, and a terminal with nobody piping into it returns ""
310
328
  // rather than hanging on a read that will never end.
@@ -60,22 +60,31 @@ const COMMANDS = {
60
60
  push: {
61
61
  usage: "[--env NAME]",
62
62
  summary:
63
- "Validate the workspace and update a preview deployment. Never touches live. `--env NAME` " +
64
- "lands it on its own named preview (one per pull request) instead of the default one.",
63
+ "Validate the workspace and update a preview deployment. Never touches live. On a git branch " +
64
+ "other than the default one it lands on that branch's own environment (fix/pagos -> fix-pagos), " +
65
+ "the same one the branch's pull request deploys to; elsewhere on `preview`.",
65
66
  annotations: { readOnlyHint: false }
66
67
  },
67
68
  publish: {
68
69
  usage: "[--env NAME]",
69
70
  summary:
70
- "Promote the latest push to live (or the preview `--env` names), which is what customers " +
71
- "see. Ask the developer first: this is " +
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.",
71
+ "Promote an environment to live, with the knowledge changes made in it — what customers see. " +
72
+ "Ask the developer first: this is the one step that changes what real visitors get. Refused " +
73
+ "when the workspace is connected to GitHub: there, live changes only by merging the branch's " +
74
+ "pull request. Refused too when live changed an entry this environment also changed.",
74
75
  annotations: { readOnlyHint: false, destructiveHint: true }
75
76
  },
77
+ env: {
78
+ usage: "[NAME] | list | rm NAME",
79
+ summary:
80
+ "The environment this workspace is working in — the branch's own on a branch — with its " +
81
+ "publishable token, widget snippet, share link, the knowledge entries it changed and any that " +
82
+ "live changed underneath. Use its token to point an app being developed at this environment.",
83
+ annotations: { readOnlyHint: true }
84
+ },
76
85
  rollback: {
77
86
  usage: "",
78
- summary: "Restore the previous live deployment. Same weight as publish — ask first.",
87
+ summary: "Put live back to the previous snapshot — its configuration and its knowledge together. Same weight as publish — ask first.",
79
88
  annotations: { readOnlyHint: false, destructiveHint: true }
80
89
  },
81
90
  chat: {
@@ -88,35 +97,39 @@ const COMMANDS = {
88
97
  annotations: { readOnlyHint: false }
89
98
  },
90
99
  secrets: {
91
- usage: "list | set KEY VALUE | rm KEY",
100
+ usage: "list | set KEY VALUE | rm KEY [--env NAME]",
92
101
  summary:
93
- "Credentials the workspace's JS tools read as ctx.env. Values are write-only — listing shows " +
94
- "keys, never values.",
102
+ "Credentials the workspace's tools read as $env.KEY. Values are write-only — listing shows " +
103
+ "keys, never values. On a branch (or with --env) a value applies to that environment only, " +
104
+ "replacing the one everywhere else: point a branch's tools at staging without touching live.",
95
105
  annotations: { readOnlyHint: false }
96
106
  },
97
107
  kb: {
98
108
  usage:
99
- "[list] | show NAME | create NAME | write BASE ENTRY [FILE] | cat BASE ENTRY | rm-entry BASE ENTRY | " +
100
- "follow BASE URL | unfollow BASE URL | refresh BASE [URL] | status BASE | publish BASE [-m NOTE] | " +
101
- "history BASE | rollback BASE NUMBER",
109
+ "[list] | show NAME | create NAME | write BASE ENTRY [FILE] | cat BASE ENTRY [--version N] | " +
110
+ "rm-entry BASE ENTRY | follow BASE URL | unfollow BASE URL | refresh BASE [URL] | status BASE | " +
111
+ "publish BASE | history BASE (entry commands take --env NAME)",
102
112
  summary:
103
113
  "Knowledge bases, which belong to the workspace rather than to a deployment: `knowledge:` in " +
104
- "vatio.yml is a list of names, and a push neither fills nor empties one. Writes go to a draft that " +
105
- "preview answers from; live changes only on publish.",
114
+ "vatio.yml is a list of names, and a push neither fills nor empties one. The entries are live; " +
115
+ "writes on a branch are changes only that branch's preview answers from, and they go live with " +
116
+ "vatio_publish or the merge of its pull request. `status` names entries live changed underneath: " +
117
+ "read those from live (`cat --env live`), write your change on top, and the conflict clears.",
106
118
  annotations: { readOnlyHint: false, openWorldHint: true }
107
119
  },
108
120
  tokens: {
109
- usage: "list | create [--env NAME] [--label NAME] | revoke PREFIX",
121
+ usage: "list | create [--env NAME] [--label NAME] [--origin URL]... | origins PREFIX URL... | revoke PREFIX",
110
122
  summary:
111
123
  "Publishable tokens for the widget and anything built on the SDK. A created token is shown in " +
112
- "full exactly once.",
124
+ "full exactly once. Each token works only from its own allowed origins (--origin, repeatable, or " +
125
+ "`origins PREFIX URL...` to replace the list); the install snippet's token is edited in the console.",
113
126
  annotations: { readOnlyHint: false }
114
127
  },
115
128
  widget: {
116
129
  usage: "[--env NAME]",
117
130
  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.",
131
+ "What the platform will actually enforce for the widget, the origins its tokens accept, and the " +
132
+ "tokens that exist. Read-only: the look is edited on the console's Brand page, whose URL it prints.",
120
133
  annotations: { readOnlyHint: true }
121
134
  },
122
135
  whatsapp: {
@@ -124,7 +137,7 @@ const COMMANDS = {
124
137
  summary:
125
138
  "The workspace's own WhatsApp number, which serves live: whether it is connected, whether Meta " +
126
139
  "delivers to it, and whether it is answering — three things that fail separately. `numbers` is " +
127
- "the other thing: the shared WhatsApp preview, test phones that reach preview and need no Meta " +
140
+ "the other thing: the shared WhatsApp preview, test phones that reach the environment they point at (`point PHONE --env NAME`) and need no Meta " +
128
141
  "account. A code goes to the handset, so a human has to read it back to you.",
129
142
  annotations: { readOnlyHint: false, openWorldHint: true }
130
143
  },
@@ -154,6 +167,7 @@ const WITHHELD = {
154
167
  const BLOCKED_SUBCOMMANDS = {
155
168
  chat: { destroy: "destroys a conversation; ask the developer" },
156
169
  kb: { rm: "deletes an indexed base; ask the developer" },
170
+ env: { rm: "removes an environment with its unpublished knowledge changes; ask the developer" },
157
171
  whatsapp: { connect: "takes Meta credentials the developer holds", disconnect: "ask the developer" },
158
172
  instagram: { connect: "needs Meta's consent screen in a browser", disconnect: "ask the developer" }
159
173
  };
@@ -217,8 +231,13 @@ function initializeResult(params) {
217
231
  "Start with vatio_docs: it is the contract for vatio.yml, tools, auth, visitor identity and",
218
232
  "knowledge, fetched live from the platform, so it is what the platform actually accepts.",
219
233
  "",
220
- "The loop is: edit files, vatio_push (preview only), vatio_chat to read the agent's answer,",
221
- "vatio_publish when the developer says so. Nothing a customer sees changes until publish.",
234
+ "Work on a git branch (a worktree is ideal): every command then uses that branch's own",
235
+ "environment — its agent, its knowledge changes, its secret values — and nothing you do there",
236
+ "reaches live or another branch. vatio_env shows which one and the token to point an app at it.",
237
+ "",
238
+ "The loop is: edit files and knowledge (vatio_kb write), vatio_push, vatio_chat to read the",
239
+ "agent's answer, then publish when the developer says so — by merging the branch's pull request",
240
+ "when GitHub is connected, vatio_publish otherwise. The prompt and the knowledge go live together.",
222
241
  "",
223
242
  "Anything that needs a browser or a person — login, issue, instagram connect — is not here;",
224
243
  "ask the developer to run it in their terminal."
@@ -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.");