@voltade/volty 0.1.9 → 0.1.11

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
@@ -46,7 +46,7 @@ Open Claude Code anywhere and ask in plain language. The skill you installed in
46
46
  - *"Why didn't the agent learn anything from that conversation?"*
47
47
  - *"Clean up the test conversations on prod."*
48
48
 
49
- Claude knows the safety rules: tests run against a sandboxed draft version of the agent with a synthetic contact, so nothing can ever be sent to a real contact, and Claude cleans up after itself when it's done.
49
+ Claude knows the safety rules: tests run on your workspace's Sandbox from a synthetic contact, whose replies are recorded in the thread but never sent, so nothing can reach a real contact. Claude cleans up after itself when it's done, and anything left over is removed after 24 hours.
50
50
 
51
51
  ## Upgrading
52
52
 
package/dist/volty.js CHANGED
@@ -58,8 +58,8 @@ async function fetchCatalog(ctx, env, useCache = true) {
58
58
  if (useCache && existsSync(path)) {
59
59
  try {
60
60
  const cached = JSON.parse(readFileSync(path, "utf8"));
61
- if (Date.now() - cached.at < CATALOG_TTL_MS && cached.retired)
62
- return { ops: cached.ops, retired: cached.retired };
61
+ if (Date.now() - cached.at < CATALOG_TTL_MS && cached.renamed)
62
+ return cached;
63
63
  } catch {}
64
64
  }
65
65
  const res = await apiFetch(ctx, "/api/v/_catalog");
@@ -68,11 +68,12 @@ async function fetchCatalog(ctx, env, useCache = true) {
68
68
  }
69
69
  const ops = res.body.catalog;
70
70
  const retired = res.body.retired ?? {};
71
+ const renamed = res.body.renamed ?? {};
71
72
  try {
72
73
  mkdirSync(tmpdir(), { recursive: true });
73
- writeFileSync(path, JSON.stringify({ at: Date.now(), ops, retired }));
74
+ writeFileSync(path, JSON.stringify({ at: Date.now(), ops, retired, renamed }));
74
75
  } catch {}
75
- return { ops, retired };
76
+ return { ops, retired, renamed };
76
77
  }
77
78
  async function fetchOrgs(ctx, env, useCache = true) {
78
79
  const path = join(tmpdir(), `volty-orgs-${env}.json`);
@@ -293,21 +294,21 @@ function coerce(flag, raw, prop0, UsageError) {
293
294
  return true;
294
295
  if (raw === "false")
295
296
  return false;
296
- throw new UsageError(`--${flag} expects true|false`);
297
+ throw new UsageError(`--${flag} expects true|false`, flag);
297
298
  }
298
299
  if (raw === undefined)
299
- throw new UsageError(`--${flag} requires a value`);
300
+ throw new UsageError(`--${flag} requires a value`, flag);
300
301
  if (prop.type === "number" || prop.type === "integer") {
301
302
  const n = Number(raw);
302
303
  if (Number.isNaN(n))
303
- throw new UsageError(`--${flag} expects a number`);
304
+ throw new UsageError(`--${flag} expects a number`, flag);
304
305
  return n;
305
306
  }
306
307
  if (prop.type === "object") {
307
308
  try {
308
309
  return JSON.parse(raw);
309
310
  } catch {
310
- throw new UsageError(`--${flag} expects JSON`);
311
+ throw new UsageError(`--${flag} expects JSON`, flag);
311
312
  }
312
313
  }
313
314
  return raw;
@@ -322,6 +323,16 @@ function jsonArrayValue(raw) {
322
323
  return null;
323
324
  }
324
325
  }
326
+ function arrayItems(flag, raw, prop, UsageError) {
327
+ const spread = jsonArrayValue(raw);
328
+ if (spread)
329
+ return spread;
330
+ const items = prop.items ?? {};
331
+ if (items.entityId && raw?.includes(",")) {
332
+ return raw.split(",").map((id) => id.trim()).filter((id) => id !== "");
333
+ }
334
+ return [coerce(flag, raw, items, UsageError)];
335
+ }
325
336
  function canonicalFlag(props, flag) {
326
337
  if (props[flag])
327
338
  return flag;
@@ -352,11 +363,7 @@ function parseFlagTokens(props, rest, UsageError) {
352
363
  }
353
364
  if (prop.type === "array") {
354
365
  const list = input[flag] ?? [];
355
- const spread = jsonArrayValue(raw);
356
- if (spread)
357
- list.push(...spread);
358
- else
359
- list.push(coerce(flag, raw, prop.items ?? {}, UsageError));
366
+ list.push(...arrayItems(flag, raw, prop, UsageError));
360
367
  input[flag] = list;
361
368
  } else {
362
369
  input[flag] = coerce(flag, raw, prop, UsageError);
@@ -369,11 +376,12 @@ function parseFlagTokens(props, rest, UsageError) {
369
376
  class CliUsageError extends Error {
370
377
  }
371
378
  function resolveOp(catalog, argv) {
372
- const byName = new Map(catalog.map((c) => [c.name, c]));
379
+ const byName = new Map(catalog.ops.map((c) => [c.name, c]));
373
380
  for (let n = Math.min(argv.length, 4);n >= 1; n--) {
374
- const op = byName.get(argv.slice(0, n).join(" "));
381
+ const name = argv.slice(0, n).join(" ");
382
+ const op = byName.get(name) ?? byName.get(catalog.renamed[name] ?? "");
375
383
  if (op)
376
- return { op, rest: argv.slice(n) };
384
+ return { op, rest: argv.slice(n), ...op.name !== name && { renamedFrom: name } };
377
385
  }
378
386
  return null;
379
387
  }
@@ -839,13 +847,16 @@ async function main() {
839
847
  `);
840
848
  return;
841
849
  }
842
- const resolved = resolveOp(catalog.ops, argv);
850
+ const resolved = resolveOp(catalog, argv);
843
851
  if (!resolved) {
844
852
  const retired = retiredPrefix(catalog.retired, argv);
845
853
  if (retired)
846
854
  fail(`\`${retired.name}\` is retired: ${retired.replacement}`);
847
855
  fail(`unknown operation '${argv.join(" ")}' — run \`volty help\``);
848
856
  }
857
+ if (resolved.renamedFrom)
858
+ process.stderr.write(`${resolved.renamedFrom} is now ${resolved.op.name}
859
+ `);
849
860
  if (resolved.rest.includes("--help")) {
850
861
  process.stdout.write(`${renderOpHelp(resolved.op)}
851
862
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltade/volty",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Remote CLI for the Volty platform — run operations, trigger real agent wakes, and inspect LLM I/O over an API key.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -23,6 +23,6 @@ Use the lane the behaviour was reported on. On the contact lane Volty declines t
23
23
  ## On prod
24
24
 
25
25
  - Name the org on every command (`--env prod --org <id>`, or `VOLTY_ENV` and `VOLTY_ORG` in the process), and read the stderr line the CLI prints before each command to check it names the org you meant. Act only in the org the user named.
26
- - Test through the sandbox, where every reply stays a draft. A real conversation is never a test target.
26
+ - Test through the Sandbox, where a reply to the synthetic contact is recorded as sent and never leaves. A real conversation is never a test target.
27
27
  - Run `volty debug sandbox-cleanup --all --env prod --org <id>` when you finish.
28
28
  - A `{pending:true}` result means the turn outran the wait, not that it failed: poll for it (`volty docs` names the command) instead of sending the message again.