@voltade/volty 0.1.31 → 0.1.33

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/dist/volty.js CHANGED
@@ -300,6 +300,9 @@ async function logout(config, env) {
300
300
  // src/dispatch.ts
301
301
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
302
302
 
303
+ // ../shared/ops/dry-run.ts
304
+ var DRY_RUN_HEADER = "x-volty-dry-run";
305
+
303
306
  // ../shared/ops/flag-parse.ts
304
307
  function acceptsNull(prop) {
305
308
  if (prop.type === "null")
@@ -434,6 +437,21 @@ function resolveOp(catalog, argv) {
434
437
  }
435
438
  return null;
436
439
  }
440
+ function takeBool(argv, name) {
441
+ const rest = argv.filter((t) => t !== name);
442
+ return { present: rest.length !== argv.length, rest };
443
+ }
444
+ function takeConfirm(op, argv) {
445
+ if (op.annotations.destructiveHint !== true)
446
+ return { rest: argv, dryRun: false };
447
+ const confirm = takeBool(argv, "--confirm");
448
+ if (confirm.present)
449
+ return { rest: confirm.rest, dryRun: false };
450
+ if (op.dryRun !== true) {
451
+ throw new CliUsageError(`\`${op.name}\` is destructive: run it again with --confirm to do it`);
452
+ }
453
+ return { rest: confirm.rest, dryRun: true };
454
+ }
437
455
  function parseFlags(op, rest) {
438
456
  const props = op.input.properties ?? {};
439
457
  const parsed = parseFlagTokens(props, rest, CliUsageError);
@@ -472,8 +490,9 @@ function readBase64File(flag, path) {
472
490
  throw new CliUsageError(`--${flag}: cannot read ${path} (${err.code ?? "error"})`);
473
491
  }
474
492
  }
475
- async function runOp(ctx, op, input) {
476
- const res = await apiFetch(ctx, pathForOp(op.name), { method: "POST", body: input });
493
+ async function runOp(ctx, op, input, dryRun = false) {
494
+ const headers = dryRun ? { [DRY_RUN_HEADER]: "1" } : undefined;
495
+ const res = await apiFetch(ctx, pathForOp(op.name), { method: "POST", body: input, headers });
477
496
  return { status: res.status, ok: res.ok, body: res.body };
478
497
  }
479
498
  function saveOutFile(body, path) {
@@ -1177,10 +1196,6 @@ function takeValue(argv, name) {
1177
1196
  }
1178
1197
  return { value, rest };
1179
1198
  }
1180
- function takeBool(argv, name) {
1181
- const rest = argv.filter((t) => t !== name);
1182
- return { present: rest.length !== argv.length, rest };
1183
- }
1184
1199
  async function handleAuth(rest, config, env, orgOverride) {
1185
1200
  const sub = rest[0];
1186
1201
  if (sub === "login") {
@@ -1593,18 +1608,24 @@ async function main() {
1593
1608
  if (out.rest.length !== resolved.rest.length && !out.value)
1594
1609
  fail("--out needs a path, as in --out=./logo.png");
1595
1610
  let parsed;
1611
+ let dryRun = false;
1596
1612
  try {
1597
- parsed = parseFlags(resolved.op, out.rest);
1613
+ const confirm = takeConfirm(resolved.op, out.rest);
1614
+ dryRun = confirm.dryRun;
1615
+ parsed = parseFlags(resolved.op, confirm.rest);
1598
1616
  } catch (err) {
1599
1617
  if (err instanceof CliUsageError)
1600
1618
  fail(err.message);
1601
1619
  throw err;
1602
1620
  }
1603
- const result = await runOp(ctx, resolved.op, parsed.input);
1621
+ const result = await runOp(ctx, resolved.op, parsed.input, dryRun);
1604
1622
  if (!result.ok)
1605
1623
  failResult(resolved.op.name, result.body, result.status, parsed.json);
1606
1624
  if (out.value === undefined) {
1607
1625
  process.stdout.write(`${renderResult(result.body, parsed.json)}
1626
+ `);
1627
+ if (dryRun && !parsed.json)
1628
+ process.stdout.write(`Dry run: run it again with --confirm to do it.
1608
1629
  `);
1609
1630
  return;
1610
1631
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltade/volty",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
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",
@@ -26,6 +26,7 @@ Use the lane the behaviour was reported on. On the contact lane Volty declines t
26
26
  ## On prod
27
27
 
28
28
  - A key reaches one org and calls operations only; `--org` gets each org you belong to its own key the first time. A call a key cannot make is refused with a sentence naming why; report that sentence instead of working around it. `volty keys create <name>` mints a narrower named key for a script (`volty docs` has the flags).
29
+ - An op that deletes for good runs only with `--confirm`. Run it without first: the CLI prints the dry run the server answers and changes nothing. Show that plan to the user and add `--confirm` only once they agree. Sending a broadcast is never done by key; the refusal names the Broadcasts page, so pass it on.
29
30
  - 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.
30
31
  - 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.
31
32
  - Run `volty debug sandbox-cleanup --all --env prod --org <id>` when you finish.