clawborrator-cli 0.0.50 → 0.0.52

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.
@@ -67615,23 +67615,30 @@ var AmbiguousError = class extends Error {
67615
67615
  }
67616
67616
  code = "CLW_AMBIGUOUS";
67617
67617
  };
67618
- async function pickCandidate(input, candidates) {
67618
+ async function pickCandidate(input, candidates, opts = {}) {
67619
67619
  if (candidates.length === 1) return candidates[0];
67620
67620
  const live = candidates.filter((c) => c.connected);
67621
- if (live.length <= 1) {
67622
- return live[0] ?? candidates[0];
67621
+ let promptSet;
67622
+ if (opts.destructive) {
67623
+ if (candidates.length <= 1) return candidates[0];
67624
+ promptSet = candidates;
67625
+ } else {
67626
+ if (live.length <= 1) return live[0] ?? candidates[0];
67627
+ promptSet = live;
67623
67628
  }
67624
67629
  if (!process.stdin.isTTY || !process.stderr.isTTY) {
67625
- throw new AmbiguousError(live, input);
67630
+ throw new AmbiguousError(promptSet, input);
67626
67631
  }
67627
67632
  process.stderr.write(`${BOLD}'${input}' is ambiguous \u2014 pick a session:${RESET}
67628
67633
  `);
67629
- for (let i = 0; i < live.length; i++) {
67630
- const c = live[i];
67634
+ for (let i = 0; i < promptSet.length; i++) {
67635
+ const c = promptSet[i];
67631
67636
  const qualified = c.routingName ? `@${c.startedByLogin}/${c.routingName.replace(/^@/, "")}` : `(no routing name)`;
67637
+ const status = c.connected ? `${BOLD}\u25CF online${RESET}` : `${DIM}\u25CB offline${RESET}`;
67632
67638
  const cwd = c.cwd ? ` ${DIM}${c.cwd}${RESET}` : "";
67633
67639
  const host = c.host ? ` ${DIM}${c.host}${RESET}` : "";
67634
- process.stderr.write(` ${BOLD}${i + 1}${RESET}. ${qualified}${host}${cwd}
67640
+ const seen = c.lastSeenAt ? ` ${DIM}last seen ${c.lastSeenAt}${RESET}` : "";
67641
+ process.stderr.write(` ${BOLD}${i + 1}${RESET}. ${qualified} ${status}${host}${cwd}${seen}
67635
67642
  `);
67636
67643
  process.stderr.write(` ${DIM}id ${c.id}${RESET}
67637
67644
  `);
@@ -67640,7 +67647,7 @@ async function pickCandidate(input, candidates) {
67640
67647
  `);
67641
67648
  const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stderr });
67642
67649
  const answer = await new Promise((resolve3) => {
67643
- rl.question(`pick [1-${live.length}]: `, resolve3);
67650
+ rl.question(`pick [1-${promptSet.length}]: `, resolve3);
67644
67651
  });
67645
67652
  rl.close();
67646
67653
  const trimmed = answer.trim().toLowerCase();
@@ -67648,10 +67655,10 @@ async function pickCandidate(input, candidates) {
67648
67655
  throw new Error("cancelled");
67649
67656
  }
67650
67657
  const idx = parseInt(trimmed, 10);
67651
- if (!Number.isInteger(idx) || idx < 1 || idx > live.length) {
67658
+ if (!Number.isInteger(idx) || idx < 1 || idx > promptSet.length) {
67652
67659
  throw new Error(`invalid selection '${answer}'`);
67653
67660
  }
67654
- return live[idx - 1];
67661
+ return promptSet[idx - 1];
67655
67662
  }
67656
67663
 
67657
67664
  // src/commands/session-attach.ts
@@ -67830,7 +67837,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
67830
67837
  const limitArg = String(opts.limit ?? "50").toLowerCase();
67831
67838
  const historyLimit = limitArg === "all" ? 5e3 : limitArg === "0" ? 0 : Math.max(0, parseInt(limitArg, 10) || 0);
67832
67839
  if (historyLimit > 0) {
67833
- const kindsParam = opts.opMessages === false ? "&kinds=event" : "";
67840
+ const kindsParam = opts.opMessages === false ? "&kinds=event,file" : "";
67834
67841
  try {
67835
67842
  const tl = await api.get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/timeline?limit=${historyLimit}${kindsParam}`);
67836
67843
  if (tl.items.length > 0) {
@@ -67843,8 +67850,11 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
67843
67850
  /* fromBacklog */
67844
67851
  true
67845
67852
  );
67846
- } else {
67853
+ } else if (item.kind === "op-message") {
67847
67854
  console.log(`${DIM2}[${shortTs(item.ts)}]${RESET2} ${GREEN}@${item.authorLogin}${RESET2} ${item.text}`);
67855
+ } else if (item.kind === "file") {
67856
+ const verb = item.action === "uploaded" ? `${GREEN}\u{1F4CE} uploaded${RESET2}` : `${RED}\u2717 deleted${RESET2}`;
67857
+ console.log(`${DIM2}[${shortTs(item.ts)}]${RESET2} ${BLUE}@${item.file.uploaderLogin}${RESET2} ${verb} ${BOLD2}${item.file.filename}${RESET2} ${DIM2}(${fmtBytes(item.file.size)} \xB7 fileId=${item.file.id})${RESET2}`);
67848
67858
  }
67849
67859
  }
67850
67860
  console.log(`${DIM2}\u2500\u2500\u2500 live \u2500\u2500\u2500${RESET2}`);
@@ -68124,6 +68134,12 @@ function printInbound(msg, myLogin) {
68124
68134
  if (!msg.connected) stopWorking();
68125
68135
  break;
68126
68136
  }
68137
+ case "file_event": {
68138
+ const verb = msg.action === "uploaded" ? `${GREEN}\u{1F4CE} uploaded${RESET2}` : `${RED}\u2717 deleted${RESET2}`;
68139
+ const f = msg.file;
68140
+ say(`${DIM2}[${ts()}]${RESET2} ${BLUE}@${f.uploaderLogin}${RESET2} ${verb} ${BOLD2}${f.filename}${RESET2} ${DIM2}(${fmtBytes(f.size)} \xB7 fileId=${f.id})${RESET2}`);
68141
+ break;
68142
+ }
68127
68143
  case "ack":
68128
68144
  break;
68129
68145
  case "error":
@@ -68135,6 +68151,12 @@ function shortTs(iso) {
68135
68151
  const d = new Date(iso);
68136
68152
  return Number.isFinite(d.getTime()) ? d.toLocaleTimeString() : iso.slice(11, 19);
68137
68153
  }
68154
+ function fmtBytes(n) {
68155
+ if (n < 1024) return `${n} B`;
68156
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
68157
+ if (n < 1024 ** 3) return `${(n / 1024 / 1024).toFixed(1)} MB`;
68158
+ return `${(n / 1024 ** 3).toFixed(2)} GB`;
68159
+ }
68138
68160
  function previewPayload(p) {
68139
68161
  const text = p?.text ?? p?.preview ?? p?.outputPreview ?? "";
68140
68162
  if (typeof text === "string" && text.length > 0) {
@@ -68315,7 +68337,7 @@ function fmtAgo(iso) {
68315
68337
  return fmtDuration(Date.now() - new Date(iso).getTime());
68316
68338
  }
68317
68339
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
68318
- async function resolveSessionId(idOrName) {
68340
+ async function resolveSessionId(idOrName, opts = {}) {
68319
68341
  if (UUID_RE.test(idOrName)) return idOrName;
68320
68342
  const needle = idOrName.startsWith("@") ? idOrName : "@" + idOrName;
68321
68343
  const slash = needle.indexOf("/");
@@ -68343,7 +68365,7 @@ async function resolveSessionId(idOrName) {
68343
68365
  throw err;
68344
68366
  }
68345
68367
  try {
68346
- const picked = await pickCandidate(idOrName, candidates);
68368
+ const picked = await pickCandidate(idOrName, candidates, { destructive: !!opts.destructive });
68347
68369
  return picked.id;
68348
68370
  } catch (e) {
68349
68371
  if (e instanceof AmbiguousError) {
@@ -68495,16 +68517,17 @@ var sessionPrune = new Command("prune").description("hard-delete duplicate sessi
68495
68517
  }
68496
68518
  if (r.dryRun) console.log("\n(--dry-run \u2014 re-run without it to apply)");
68497
68519
  });
68498
- var sessionDelete = new Command("delete").description("hard-delete a single session \u2014 cascades events / op-messages / shares. Irreversible. Use `archive` for the soft form (auto-resurrects on reconnect).").argument("<ref>", "session UUID or @routingName").option("--hard", "required: confirm you want a permanent delete (no soft form is offered without this flag)").action(async (ref, opts) => {
68520
+ var sessionDelete = new Command("delete").description("hard-delete a single session \u2014 cascades events / op-messages / shares / files (refcount-sweeps blobs). Irreversible. Use `archive` for the soft form (auto-resurrects on reconnect). Prompts if the routing name matches more than one row, even when only one is online \u2014 both are equally permanent to delete.").argument("<ref>", "session UUID or @routingName").option("--hard", "required: confirm you want a permanent delete (no soft form is offered without this flag)").action(async (ref, opts) => {
68499
68521
  if (!opts.hard) {
68500
68522
  console.error("error: hard delete requires --hard. Did you mean `claw session archive <ref>`?");
68501
68523
  process.exit(2);
68502
68524
  }
68503
- const id = await resolveSessionId(ref);
68525
+ const id = await resolveSessionId(ref, { destructive: true });
68504
68526
  const r = await api.delete(
68505
68527
  `/api/v1/sessions/${encodeURIComponent(id)}?hard=true`
68506
68528
  );
68507
- console.log(`\u2717 deleted ${r.sessionId} (events / op-messages / shares cascaded).`);
68529
+ const sweep = r.blobsSwept && r.blobsSwept > 0 ? ` \xB7 swept ${r.blobsSwept} blob${r.blobsSwept === 1 ? "" : "s"} (${r.bytesFreed ?? 0} bytes freed)` : "";
68530
+ console.log(`\u2717 deleted ${r.sessionId} (events / op-messages / shares / files cascaded)${sweep}`);
68508
68531
  });
68509
68532
  var sessionPrompt = new Command("prompt").description('send a one-shot prompt to a session\'s live Claude. Fire-and-forget \u2014 to find the eventual reply, run `claw session events <ref> --kind=chat --type=reply` (or `claw route <peer> "..."` for ask-mode that blocks for the answer).').argument("<ref>", "session UUID or @routingName").argument("<text>", "prompt text \u2014 quote multi-word").action(async (ref, text) => {
68510
68533
  const id = await resolveSessionId(ref);
@@ -68543,7 +68566,7 @@ var sessionSharesCmd = new Command("shares").description("list users granted acc
68543
68566
  console.log(` @${s.login.padEnd(20)} ${s.role.padEnd(9)} since ${s.createdAt}`);
68544
68567
  }
68545
68568
  });
68546
- function fmtBytes(n) {
68569
+ function fmtBytes2(n) {
68547
68570
  if (n < 1024) return `${n} B`;
68548
68571
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
68549
68572
  if (n < 1024 ** 3) return `${(n / 1024 / 1024).toFixed(1)} MB`;
@@ -68565,7 +68588,7 @@ var sessionFiles = new Command("files").description("list a session's file attac
68565
68588
  const ts2 = f.uploadedAt.slice(0, 19).replace("T", " ");
68566
68589
  const flag = f.deletedAt ? " [deleted]" : "";
68567
68590
  const sha = f.sha256.slice(0, 12);
68568
- console.log(`#${String(f.id).padStart(5)} ${ts2} ${fmtBytes(f.size).padStart(8)} @${f.uploaderLogin.padEnd(20)} ${f.scope.padEnd(11)} sha=${sha}\u2026 ${f.filename}${flag}`);
68591
+ console.log(`#${String(f.id).padStart(5)} ${ts2} ${fmtBytes2(f.size).padStart(8)} @${f.uploaderLogin.padEnd(20)} ${f.scope.padEnd(11)} sha=${sha}\u2026 ${f.filename}${flag}`);
68569
68592
  }
68570
68593
  });
68571
68594
  var sessionFileRm = new Command("file-rm").description("delete a file by id. Soft-deletes the row; on-disk blob is swept once no live row references its sha. Prompter+ on the file's session.").argument("<fileId>", "file id (from `claw session files` output, the # column)").action(async (fileId) => {
@@ -68583,7 +68606,7 @@ var sessionFileRm = new Command("file-rm").description("delete a file by id. Sof
68583
68606
  }
68584
68607
  const refs = r.refsRemaining ?? 0;
68585
68608
  if (r.blobDeleted) {
68586
- console.log(`\u2717 deleted file #${id} \u2014 blob swept (${fmtBytes(r.freedBytes ?? 0)} freed; was the last reference)`);
68609
+ console.log(`\u2717 deleted file #${id} \u2014 blob swept (${fmtBytes2(r.freedBytes ?? 0)} freed; was the last reference)`);
68587
68610
  } else {
68588
68611
  console.log(`\u2717 deleted file #${id} \u2014 blob retained (${refs} other live reference${refs === 1 ? "" : "s"})`);
68589
68612
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawborrator-cli",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "type": "module",
5
5
  "description": "claw — command-line client for clawborrator. Attach to remote Claude Code sessions, send prompts, resolve permission gates, route across sessions, manage public agents and webhooks. Auth via GitHub OAuth + PKCE.",
6
6
  "license": "MIT",