clawborrator-cli 0.0.49 → 0.0.51
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 +2 -0
- package/dist-bundled/claw.cjs +63 -3
- package/package.json +3 -6
package/README.md
CHANGED
|
@@ -77,6 +77,8 @@ claw session attach 6d04…uuid
|
|
|
77
77
|
claw session info @backend # one-shot detail dump
|
|
78
78
|
claw session events @backend --kind chat # transcript history (--limit / --after / --before)
|
|
79
79
|
claw session messages @backend # operator-to-operator chat (op-messages)
|
|
80
|
+
claw session files @backend # list file attachments on a session
|
|
81
|
+
claw session file-rm 42 # delete a file by id (refcount-sweeps blob if last ref)
|
|
80
82
|
|
|
81
83
|
claw session prompt @backend "deploy to staging" # fire-and-forget
|
|
82
84
|
claw session prompt @backend "@MRIIOT/rust-expert what is a lifetime?" # public-agent dispatch
|
package/dist-bundled/claw.cjs
CHANGED
|
@@ -67830,7 +67830,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
67830
67830
|
const limitArg = String(opts.limit ?? "50").toLowerCase();
|
|
67831
67831
|
const historyLimit = limitArg === "all" ? 5e3 : limitArg === "0" ? 0 : Math.max(0, parseInt(limitArg, 10) || 0);
|
|
67832
67832
|
if (historyLimit > 0) {
|
|
67833
|
-
const kindsParam = opts.opMessages === false ? "&kinds=event" : "";
|
|
67833
|
+
const kindsParam = opts.opMessages === false ? "&kinds=event,file" : "";
|
|
67834
67834
|
try {
|
|
67835
67835
|
const tl = await api.get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/timeline?limit=${historyLimit}${kindsParam}`);
|
|
67836
67836
|
if (tl.items.length > 0) {
|
|
@@ -67843,8 +67843,11 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
67843
67843
|
/* fromBacklog */
|
|
67844
67844
|
true
|
|
67845
67845
|
);
|
|
67846
|
-
} else {
|
|
67846
|
+
} else if (item.kind === "op-message") {
|
|
67847
67847
|
console.log(`${DIM2}[${shortTs(item.ts)}]${RESET2} ${GREEN}@${item.authorLogin}${RESET2} ${item.text}`);
|
|
67848
|
+
} else if (item.kind === "file") {
|
|
67849
|
+
const verb = item.action === "uploaded" ? `${GREEN}\u{1F4CE} uploaded${RESET2}` : `${RED}\u2717 deleted${RESET2}`;
|
|
67850
|
+
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
67851
|
}
|
|
67849
67852
|
}
|
|
67850
67853
|
console.log(`${DIM2}\u2500\u2500\u2500 live \u2500\u2500\u2500${RESET2}`);
|
|
@@ -68124,6 +68127,12 @@ function printInbound(msg, myLogin) {
|
|
|
68124
68127
|
if (!msg.connected) stopWorking();
|
|
68125
68128
|
break;
|
|
68126
68129
|
}
|
|
68130
|
+
case "file_event": {
|
|
68131
|
+
const verb = msg.action === "uploaded" ? `${GREEN}\u{1F4CE} uploaded${RESET2}` : `${RED}\u2717 deleted${RESET2}`;
|
|
68132
|
+
const f = msg.file;
|
|
68133
|
+
say(`${DIM2}[${ts()}]${RESET2} ${BLUE}@${f.uploaderLogin}${RESET2} ${verb} ${BOLD2}${f.filename}${RESET2} ${DIM2}(${fmtBytes(f.size)} \xB7 fileId=${f.id})${RESET2}`);
|
|
68134
|
+
break;
|
|
68135
|
+
}
|
|
68127
68136
|
case "ack":
|
|
68128
68137
|
break;
|
|
68129
68138
|
case "error":
|
|
@@ -68135,6 +68144,12 @@ function shortTs(iso) {
|
|
|
68135
68144
|
const d = new Date(iso);
|
|
68136
68145
|
return Number.isFinite(d.getTime()) ? d.toLocaleTimeString() : iso.slice(11, 19);
|
|
68137
68146
|
}
|
|
68147
|
+
function fmtBytes(n) {
|
|
68148
|
+
if (n < 1024) return `${n} B`;
|
|
68149
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
68150
|
+
if (n < 1024 ** 3) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
68151
|
+
return `${(n / 1024 ** 3).toFixed(2)} GB`;
|
|
68152
|
+
}
|
|
68138
68153
|
function previewPayload(p) {
|
|
68139
68154
|
const text = p?.text ?? p?.preview ?? p?.outputPreview ?? "";
|
|
68140
68155
|
if (typeof text === "string" && text.length > 0) {
|
|
@@ -68543,6 +68558,51 @@ var sessionSharesCmd = new Command("shares").description("list users granted acc
|
|
|
68543
68558
|
console.log(` @${s.login.padEnd(20)} ${s.role.padEnd(9)} since ${s.createdAt}`);
|
|
68544
68559
|
}
|
|
68545
68560
|
});
|
|
68561
|
+
function fmtBytes2(n) {
|
|
68562
|
+
if (n < 1024) return `${n} B`;
|
|
68563
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
68564
|
+
if (n < 1024 ** 3) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
68565
|
+
return `${(n / 1024 ** 3).toFixed(2)} GB`;
|
|
68566
|
+
}
|
|
68567
|
+
var sessionFiles = new Command("files").description("list a session's file attachments. Use --all to include soft-deleted rows.").argument("<ref>", "session UUID or @routingName").option("--all", "include soft-deleted rows").option("--json", "emit one JSON object per line").action(async (ref, opts) => {
|
|
68568
|
+
const id = await resolveSessionId(ref);
|
|
68569
|
+
const data = await api.get(`/api/v1/sessions/${encodeURIComponent(id)}/files`);
|
|
68570
|
+
const live = opts.all ? data.items : data.items.filter((f) => !f.deletedAt);
|
|
68571
|
+
if (live.length === 0) {
|
|
68572
|
+
if (!opts.json) console.log("no files");
|
|
68573
|
+
return;
|
|
68574
|
+
}
|
|
68575
|
+
for (const f of live) {
|
|
68576
|
+
if (opts.json) {
|
|
68577
|
+
console.log(JSON.stringify(f));
|
|
68578
|
+
continue;
|
|
68579
|
+
}
|
|
68580
|
+
const ts2 = f.uploadedAt.slice(0, 19).replace("T", " ");
|
|
68581
|
+
const flag = f.deletedAt ? " [deleted]" : "";
|
|
68582
|
+
const sha = f.sha256.slice(0, 12);
|
|
68583
|
+
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}`);
|
|
68584
|
+
}
|
|
68585
|
+
});
|
|
68586
|
+
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) => {
|
|
68587
|
+
const id = Number(fileId);
|
|
68588
|
+
if (!Number.isFinite(id) || id <= 0) {
|
|
68589
|
+
console.error("error: fileId must be a positive integer");
|
|
68590
|
+
process.exit(2);
|
|
68591
|
+
}
|
|
68592
|
+
const r = await api.delete(
|
|
68593
|
+
`/api/v1/files/${id}`
|
|
68594
|
+
);
|
|
68595
|
+
if (r.alreadyDeleted) {
|
|
68596
|
+
console.log(`(file #${id} was already deleted)`);
|
|
68597
|
+
return;
|
|
68598
|
+
}
|
|
68599
|
+
const refs = r.refsRemaining ?? 0;
|
|
68600
|
+
if (r.blobDeleted) {
|
|
68601
|
+
console.log(`\u2717 deleted file #${id} \u2014 blob swept (${fmtBytes2(r.freedBytes ?? 0)} freed; was the last reference)`);
|
|
68602
|
+
} else {
|
|
68603
|
+
console.log(`\u2717 deleted file #${id} \u2014 blob retained (${refs} other live reference${refs === 1 ? "" : "s"})`);
|
|
68604
|
+
}
|
|
68605
|
+
});
|
|
68546
68606
|
var sessionUnshareCmd = new Command("unshare").description("revoke a user's share access to a session. Owner-only.").argument("<ref>", "session UUID or @routingName").argument("<login>", "GitHub login (with or without leading @)").action(async (ref, login) => {
|
|
68547
68607
|
const id = await resolveSessionId(ref);
|
|
68548
68608
|
const cleanLogin = login.replace(/^@/, "");
|
|
@@ -68555,7 +68615,7 @@ var sessionUnshareCmd = new Command("unshare").description("revoke a user's shar
|
|
|
68555
68615
|
console.log(`\u2717 revoked @${out.login}'s access to ${out.sessionId.slice(0, 8)}\u2026`);
|
|
68556
68616
|
}
|
|
68557
68617
|
});
|
|
68558
|
-
var sessionCmd = new Command("session").description("manage Claude Code sessions registered with this hub").addCommand(sessionList).addCommand(sessionInfo).addCommand(sessionAttach).addCommand(sessionEvents).addCommand(sessionMessages).addCommand(sessionArchive).addCommand(sessionPrune).addCommand(sessionPrompt).addCommand(sessionDelete).addCommand(sessionShareCmd).addCommand(sessionSharesCmd).addCommand(sessionUnshareCmd);
|
|
68618
|
+
var sessionCmd = new Command("session").description("manage Claude Code sessions registered with this hub").addCommand(sessionList).addCommand(sessionInfo).addCommand(sessionAttach).addCommand(sessionEvents).addCommand(sessionMessages).addCommand(sessionArchive).addCommand(sessionPrune).addCommand(sessionPrompt).addCommand(sessionDelete).addCommand(sessionShareCmd).addCommand(sessionSharesCmd).addCommand(sessionUnshareCmd).addCommand(sessionFiles).addCommand(sessionFileRm);
|
|
68559
68619
|
|
|
68560
68620
|
// src/commands/token.ts
|
|
68561
68621
|
var import_node_fs2 = require("node:fs");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawborrator-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51",
|
|
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",
|
|
@@ -40,8 +40,7 @@
|
|
|
40
40
|
"bundle": "esbuild src/index.ts --bundle --platform=node --format=cjs --target=node20 --outfile=dist-bundled/claw.cjs --banner:js=\"#!/usr/bin/env node\"",
|
|
41
41
|
"dev": "tsx src/index.ts",
|
|
42
42
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
43
|
-
"prepublishOnly": "npm run bundle
|
|
44
|
-
"postpack": "node ./scripts/inject-readme.mjs --restore"
|
|
43
|
+
"prepublishOnly": "npm run bundle"
|
|
45
44
|
},
|
|
46
45
|
"devDependencies": {
|
|
47
46
|
"@clawborrator/shared": "*",
|
|
@@ -55,7 +54,5 @@
|
|
|
55
54
|
"tsx": "^4.19.1",
|
|
56
55
|
"typescript": "^5.6.2",
|
|
57
56
|
"ws": "^8.18.0"
|
|
58
|
-
}
|
|
59
|
-
"readme": "# clawborrator-cli\n\n`claw` — command-line client for [clawborrator](https://next.clawborrator.com),\na control plane for Claude Code sessions.\n\nWhat you can do with it from a terminal:\n\n- Attach to any CC session you can see (yours or shared with you), watch\n prompts and tool calls stream in, type your own prompts, approve or\n deny permission gates, chat with other operators on the side.\n- Send one-shot prompts to a session non-interactively (CI-friendly).\n- Mint channel tokens (`ck_live_…`) and drop a ready-to-use `.mcp.json`\n block so a CC running on this machine appears on the hub.\n- Route prompts across sessions (`claw route @backend \"what time is it\"`)\n with reply tracking.\n- Share sessions with teammates at viewer / prompter / approver role.\n- Publish a session as a public expert agent (`@<owner>/<slug>`,\n callable from any prompt).\n- Subscribe to webhooks for chat / permission / file / agent events.\n\nThe hub is at <https://next.clawborrator.com> by default — point at a\nself-hosted instance with `claw login --hub <url>` (persists per\nmachine).\n\n---\n\n## Install\n\n```bash\n# one-off (no global install)\nnpx clawborrator-cli@latest login\n\n# or pin globally\nnpm install -g clawborrator-cli\nclaw login\n```\n\nRequires Node 20+.\n\n---\n\n## First-time setup\n\n```bash\n# 1. Authenticate (opens a browser → GitHub → back to a localhost\n# callback that hands you a 30-day session token).\nclaw login\n\n# 2. Confirm.\nclaw whoami\n# logged in as @your-github-login\n\n# 3. (optional) Mint a channel token + drop the .mcp.json block so\n# your local Claude Code registers a session against the hub.\nclaw token mint --name \"$(hostname)\" --mcp-snippet --out .mcp.json\n# → now restart `claude code` in this directory and it'll appear in\n# `claw session list`.\n```\n\nThe session token is stored at `~/.clawborrator/config.json` (mode\n0600). To talk to a different hub, pass `--hub <url>` once on login;\nthe URL persists.\n\n---\n\n## Cheatsheet\n\n### Sessions\n\n```bash\nclaw session list # your sessions + ones shared with you\nclaw session ls --connected # only currently-online ones\n\nclaw session attach @backend # interactive TUI: live tail + send prompts + approve gates\nclaw session attach 6d04…uuid\n\nclaw session info @backend # one-shot detail dump\nclaw session events @backend --kind chat # transcript history (--limit / --after / --before)\nclaw session messages @backend # operator-to-operator chat (op-messages)\n\nclaw session prompt @backend \"deploy to staging\" # fire-and-forget\nclaw session prompt @backend \"@MRIIOT/rust-expert what is a lifetime?\" # public-agent dispatch\nclaw session prompt @backend \"@alice/frontend ...\" # cross-account peer (if shared)\n\nclaw session share @backend alice --role prompter # grant access (viewer | prompter | approver)\nclaw session shares @backend # list current shares\nclaw session unshare @backend alice # revoke\n\nclaw session archive @backend # soft-delete (auto-resurrects on next CC start in the same project)\nclaw session prune --dry-run # find duplicate-routing-name rows\nclaw session delete @backend --hard # permanent (cascades events / op-messages / shares)\n```\n\n`<ref>` in any subcommand accepts the session UUID, the `@routingName`\nfor sessions you own, or `@owner/routingName` for sessions shared with\nyou.\n\n### Cross-session routing\n\n```bash\nclaw peers # sessions reachable for routing (yours + shared)\n\nclaw route @backend \"what time is it\" # ask-mode: blocks up to 60s for the reply\nclaw route @alice/frontend \"...\" --tell # tell-mode: fire-and-forget\n\nclaw probe \"do you have a User model\" --peers @backend,@frontend # parallel fan-out\nclaw probe \"...\" # implicit: every online reachable peer\n```\n\nRouting requires prompter+ on the target. Peer's CC must be online\nand not mid-turn for someone else (driver-claim contention returns a\n409 and you retry in a moment).\n\n### Channel tokens\n\nFor your local Claude Code to register a session against the hub,\nclawborrator-mcp needs a `ck_live_…` token in the project's\n`.mcp.json`:\n\n```bash\nclaw token mint --name \"alice-laptop\" --mcp-snippet --out .mcp.json\n# → writes a ready-to-use .mcp.json\n# → prints the plaintext token to stderr ONCE — copy it if you didn't redirect\n\nclaw token list # list active tokens\nclaw token revoke <id> # revoke (cascade-archives sessions registered with it)\n```\n\n> **Windows note:** prefer `--out <path>` over PowerShell `>`\n> redirection. PowerShell's default redirection writes UTF-16 LE with\n> BOM, which CC rejects when parsing `.mcp.json`. `--out` writes UTF-8\n> without BOM.\n\n### Public expert agents\n\nA session you own can be published as `@<your-login>/<slug>`,\ncallable by any signed-in user:\n\n```bash\nclaw agents publish --session <uuid> --name \"rust expert\" --tagline \"answers Rust questions\" --published\n# → @MRIIOT/rust-expert is live; rate-limited per-user, capped at\n# 1000 queries/day by default.\n\nclaw agents list # discover everyone's published agents\nclaw agents list --mine # your own agents (any status, includes draft)\n\nclaw agents update @MRIIOT/rust-expert --budget 5000\nclaw agents update @MRIIOT/rust-expert --composable # opt out of isolation (cross-session routing tools enabled while answering)\nclaw agents update @MRIIOT/rust-expert --status draft # take it offline without unpublishing\n\nclaw agents inbound @MRIIOT/rust-expert --days 7 # who's been calling: ok / denied / latency / top askers / recent\n\nclaw agents unpublish @MRIIOT/rust-expert\n```\n\nDefault mode is `--isolated` (recommended): the agent's CC cannot\nuse cross-session routing tools while answering a public dispatch.\nUse `--composable` only for orchestrator-style agents that need to\nreach other peers.\n\nAnyone can also call your agent over the\n[A2A protocol](https://a2a-protocol.org) at\n`/api/a2a/v1/agents/<owner>/<slug>` — see the\n[A2A bridge reference](https://next.clawborrator.com/demos/a2a-docs/).\n\n### Webhooks\n\nSubscribe to events for sessions you can see:\n\n```bash\nclaw webhook add --url https://your-server/hook \\\n --events 'chat.event,permission.requested,permission.resolved'\n\nclaw webhook list\nclaw webhook test <id> # queue a synthetic webhook.test delivery\nclaw webhook rm <id>\n```\n\nThe signing secret is shown ONCE on `add`. HMAC-SHA256 in the\n`X-Clawborrator-Signature: t=…,v1=…` header (Stripe-style).\nVerification recipes for Node + Python and the full event catalog\nlive at the\n[webhooks reference](https://next.clawborrator.com/demos/webhooks/).\n\n### Auth\n\n```bash\nclaw login # browser-based GitHub OAuth (PKCE)\nclaw login --hub https://your-hub.example.com # point at a different hub; persists\nclaw whoami\nclaw logout # revokes the session token + clears local config\n```\n\n---\n\n## `claw session attach` — the TUI\n\nThis is the killer feature. Three or more humans attached to the\nsame Claude Code session, seeing each other's prompts in real time,\nracing on tool-permission approvals, talking to each other in a side\nchannel that doesn't pollute Claude's context.\n\n```\n$ claw session attach @backend\n\n attached to @backend (cwd /home/alice/repo, role: prompter, alice (you), bob)\n ───────────────────────────────────────────────────────────────────────────\n [10:42] @bob › deploy to staging\n [10:42] claude I'll start by running the test suite first…\n [10:42] → Bash npm run test\n ↳ permission gate: /y to approve, /n to deny (alice can decide)\n /y\n [10:42] ✓ Bash (allowed by @alice)\n [10:43] ✓ Bash (npm run test exited 0)\n [10:43] claude Tests pass. Running deploy now…\n ───────────────────────────────────────────────────────────────────────────\n > _ [/help for commands]\n```\n\nIn-TUI commands:\n\n| | |\n|---|---|\n| `<text>` | send a prompt to the attached session |\n| `@peer <text>` | route to a peer session and wait for the reply |\n| `/op <text>` | operator-to-operator chat (visible to peers, not to Claude) |\n| `/y` `/n` | approve / deny the most recent permission gate |\n| `/peers` | show currently-reachable peer sessions |\n| `/help` | full list |\n| `Ctrl-C` | detach (the session keeps running) |\n\n---\n\n## Configuration\n\n| Path | Contents |\n|---|---|\n| `~/.clawborrator/config.json` | hub URL + session token (mode 0600) |\n| `$CLAWBORRATOR_HUB` | env override for hub URL (one-shot, doesn't persist) |\n\n---\n\n## Troubleshooting\n\n**\"can't connect to localhost:8787\" after login.** You're on a\npre-0.0.45 install pointed at the dev default. Upgrade to\n`@latest` and re-run `claw login`; the new default points at\n`https://next.clawborrator.com`. Or pass `--hub` explicitly.\n\n**OAuth callback hangs / \"state is required\" in the browser.** On\nWindows the callback URL contains `&` which CMD's `start` builtin\ntreats as a separator. Upgrade to a recent version of the CLI\n(0.0.41+); we now quote the URL through `windowsVerbatimArguments`.\n\n**`.mcp.json` written via `>` doesn't work.** PowerShell encodes\nredirected output as UTF-16 LE with BOM. CC's MCP parser doesn't\nhandle the BOM. Use `claw token mint … --mcp-snippet --out .mcp.json`\ninstead — that writes UTF-8 without BOM directly to disk.\n\n**`claw session attach` shows \"auth failed\".** Your session token\nexpired (30-day hard cap, no refresh). Run `claw login` again and\nre-attach.\n\n**Cross-session routing returns \"is offline\" or \"is processing a\nturn for another user\".** The peer's CC needs to be online (channel\nWS open) and not mid-turn for someone else. Wait or pick a different\npeer.\n\n---\n\n## Where to look next\n\n- **Hub home & demos:** <https://next.clawborrator.com/>\n- **REST API (OpenAPI):** <https://next.clawborrator.com/docs>\n- **WebSocket (AsyncAPI):** <https://next.clawborrator.com/ws-docs>\n- **Webhook reference:** <https://next.clawborrator.com/demos/webhooks/>\n- **A2A bridge reference:** <https://next.clawborrator.com/demos/a2a-docs/>\n- **Source / issues / contributing:** <https://github.com/clawborrator/hub_v1>\n\nThe CLI source is in `cli/` of the same repo; it's a thin shell over\nthe REST + WS surface, so anything `claw` does you can do directly\nfrom any HTTP client.\n\n---\n\n## License\n\nMIT.\n",
|
|
60
|
-
"readmeFilename": "README.md"
|
|
57
|
+
}
|
|
61
58
|
}
|