clawborrator-cli 0.0.38 → 0.0.39
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-bundled/claw.cjs +163 -164
- package/package.json +1 -1
package/dist-bundled/claw.cjs
CHANGED
|
@@ -5226,7 +5226,7 @@ var require_websocket = __commonJS({
|
|
|
5226
5226
|
var http = require("http");
|
|
5227
5227
|
var net = require("net");
|
|
5228
5228
|
var tls = require("tls");
|
|
5229
|
-
var { randomBytes, createHash } = require("crypto");
|
|
5229
|
+
var { randomBytes: randomBytes2, createHash: createHash2 } = require("crypto");
|
|
5230
5230
|
var { Duplex, Readable } = require("stream");
|
|
5231
5231
|
var { URL: URL2 } = require("url");
|
|
5232
5232
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -5756,7 +5756,7 @@ var require_websocket = __commonJS({
|
|
|
5756
5756
|
}
|
|
5757
5757
|
}
|
|
5758
5758
|
const defaultPort = isSecure ? 443 : 80;
|
|
5759
|
-
const key =
|
|
5759
|
+
const key = randomBytes2(16).toString("base64");
|
|
5760
5760
|
const request2 = isSecure ? https.request : http.request;
|
|
5761
5761
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
5762
5762
|
let perMessageDeflate;
|
|
@@ -5886,7 +5886,7 @@ var require_websocket = __commonJS({
|
|
|
5886
5886
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
5887
5887
|
return;
|
|
5888
5888
|
}
|
|
5889
|
-
const digest =
|
|
5889
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
5890
5890
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
5891
5891
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
5892
5892
|
return;
|
|
@@ -6253,7 +6253,7 @@ var require_websocket_server = __commonJS({
|
|
|
6253
6253
|
var EventEmitter = require("events");
|
|
6254
6254
|
var http = require("http");
|
|
6255
6255
|
var { Duplex } = require("stream");
|
|
6256
|
-
var { createHash } = require("crypto");
|
|
6256
|
+
var { createHash: createHash2 } = require("crypto");
|
|
6257
6257
|
var extension2 = require_extension();
|
|
6258
6258
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
6259
6259
|
var subprotocol2 = require_subprotocol();
|
|
@@ -6554,7 +6554,7 @@ var require_websocket_server = __commonJS({
|
|
|
6554
6554
|
);
|
|
6555
6555
|
}
|
|
6556
6556
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
6557
|
-
const digest =
|
|
6557
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
6558
6558
|
const headers = [
|
|
6559
6559
|
"HTTP/1.1 101 Switching Protocols",
|
|
6560
6560
|
"Upgrade: websocket",
|
|
@@ -63997,6 +63997,11 @@ var {
|
|
|
63997
63997
|
Help
|
|
63998
63998
|
} = import_index.default;
|
|
63999
63999
|
|
|
64000
|
+
// src/commands/login.ts
|
|
64001
|
+
var import_node_http = require("node:http");
|
|
64002
|
+
var import_node_crypto = require("node:crypto");
|
|
64003
|
+
var import_node_child_process = require("node:child_process");
|
|
64004
|
+
|
|
64000
64005
|
// src/config.ts
|
|
64001
64006
|
var import_node_os = require("node:os");
|
|
64002
64007
|
var import_node_path = require("node:path");
|
|
@@ -64005,7 +64010,7 @@ var CONFIG_DIR = (0, import_node_path.resolve)((0, import_node_os.homedir)(), ".
|
|
|
64005
64010
|
var CONFIG_PATH = (0, import_node_path.resolve)(CONFIG_DIR, "config.json");
|
|
64006
64011
|
var DEFAULTS = {
|
|
64007
64012
|
hubUrl: process.env.CLAWBORRATOR_HUB ?? "http://localhost:8787",
|
|
64008
|
-
|
|
64013
|
+
sessionToken: null
|
|
64009
64014
|
};
|
|
64010
64015
|
function loadConfig() {
|
|
64011
64016
|
if (!(0, import_node_fs.existsSync)(CONFIG_PATH)) return { ...DEFAULTS };
|
|
@@ -64014,7 +64019,11 @@ function loadConfig() {
|
|
|
64014
64019
|
const parsed = JSON.parse(raw);
|
|
64015
64020
|
return {
|
|
64016
64021
|
hubUrl: parsed.hubUrl?.trim() || DEFAULTS.hubUrl,
|
|
64017
|
-
pat
|
|
64022
|
+
// Forward-migrate a legacy `pat` field. The token itself is now
|
|
64023
|
+
// dead on the server side, but carrying it forward avoids loud
|
|
64024
|
+
// "config corrupt" errors — the next API call will 401 cleanly
|
|
64025
|
+
// and prompt re-login.
|
|
64026
|
+
sessionToken: parsed.sessionToken ?? parsed.pat ?? null
|
|
64018
64027
|
};
|
|
64019
64028
|
} catch {
|
|
64020
64029
|
return { ...DEFAULTS };
|
|
@@ -64030,9 +64039,9 @@ function saveConfig(cfg) {
|
|
|
64030
64039
|
} catch {
|
|
64031
64040
|
}
|
|
64032
64041
|
}
|
|
64033
|
-
function
|
|
64042
|
+
function clearSession() {
|
|
64034
64043
|
const cfg = loadConfig();
|
|
64035
|
-
saveConfig({ ...cfg,
|
|
64044
|
+
saveConfig({ ...cfg, sessionToken: null });
|
|
64036
64045
|
}
|
|
64037
64046
|
|
|
64038
64047
|
// src/client/api.ts
|
|
@@ -64047,10 +64056,10 @@ var ApiError = class extends Error {
|
|
|
64047
64056
|
async function request(method, path, body, opts = {}) {
|
|
64048
64057
|
const cfg = loadConfig();
|
|
64049
64058
|
const hubUrl = (opts.hubUrl ?? cfg.hubUrl).replace(/\/$/, "");
|
|
64050
|
-
const
|
|
64059
|
+
const token = opts.token === void 0 ? cfg.sessionToken : opts.token;
|
|
64051
64060
|
const headers = {};
|
|
64052
64061
|
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
64053
|
-
if (
|
|
64062
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
64054
64063
|
const res = await fetch(`${hubUrl}${path}`, {
|
|
64055
64064
|
method,
|
|
64056
64065
|
headers,
|
|
@@ -64082,61 +64091,113 @@ var api = {
|
|
|
64082
64091
|
};
|
|
64083
64092
|
|
|
64084
64093
|
// src/commands/login.ts
|
|
64085
|
-
|
|
64086
|
-
|
|
64087
|
-
|
|
64088
|
-
|
|
64089
|
-
|
|
64094
|
+
function base64url(buf) {
|
|
64095
|
+
return buf.toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
64096
|
+
}
|
|
64097
|
+
function openBrowser(url) {
|
|
64098
|
+
const platform2 = process.platform;
|
|
64099
|
+
const cmd = platform2 === "win32" ? "cmd" : platform2 === "darwin" ? "open" : "xdg-open";
|
|
64100
|
+
const args = platform2 === "win32" ? ["/c", "start", '""', url] : [url];
|
|
64101
|
+
try {
|
|
64102
|
+
(0, import_node_child_process.spawn)(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
64103
|
+
} catch {
|
|
64104
|
+
}
|
|
64105
|
+
}
|
|
64106
|
+
function awaitCallback(server) {
|
|
64107
|
+
return new Promise((resolve3, reject) => {
|
|
64108
|
+
server.on("request", (req, res) => {
|
|
64109
|
+
const u = new URL(req.url ?? "/", "http://localhost");
|
|
64110
|
+
if (u.pathname !== "/callback") {
|
|
64111
|
+
res.statusCode = 404;
|
|
64112
|
+
res.end("not found");
|
|
64113
|
+
return;
|
|
64114
|
+
}
|
|
64115
|
+
const code = u.searchParams.get("code") ?? "";
|
|
64116
|
+
const state = u.searchParams.get("state") ?? "";
|
|
64117
|
+
const error = u.searchParams.get("error");
|
|
64118
|
+
if (error) {
|
|
64119
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
64120
|
+
res.end(`<html><body><h2>login failed: ${error}</h2><p>you can close this tab.</p></body></html>`);
|
|
64121
|
+
reject(new Error(`oauth error: ${error}`));
|
|
64122
|
+
return;
|
|
64123
|
+
}
|
|
64124
|
+
if (!code || !state) {
|
|
64125
|
+
res.statusCode = 400;
|
|
64126
|
+
res.end("missing code or state");
|
|
64127
|
+
reject(new Error("callback missing code or state"));
|
|
64128
|
+
return;
|
|
64129
|
+
}
|
|
64130
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
64131
|
+
res.end("<html><body><h2>logged in</h2><p>you can close this tab and return to the terminal.</p><script>setTimeout(() => window.close(), 1500);</script></body></html>");
|
|
64132
|
+
resolve3({ code, state });
|
|
64133
|
+
});
|
|
64134
|
+
server.on("error", reject);
|
|
64135
|
+
});
|
|
64136
|
+
}
|
|
64137
|
+
async function browserOAuthFlow(hubUrl) {
|
|
64138
|
+
const verifier = base64url((0, import_node_crypto.randomBytes)(32));
|
|
64139
|
+
const challenge = base64url((0, import_node_crypto.createHash)("sha256").update(verifier).digest());
|
|
64140
|
+
const state = base64url((0, import_node_crypto.randomBytes)(16));
|
|
64141
|
+
const server = (0, import_node_http.createServer)();
|
|
64142
|
+
await new Promise((resolve3, reject) => {
|
|
64143
|
+
server.once("error", reject);
|
|
64144
|
+
server.listen(0, "127.0.0.1", () => resolve3());
|
|
64145
|
+
});
|
|
64146
|
+
const port = server.address().port;
|
|
64147
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
64148
|
+
const startUrl = new URL("/api/v1/auth/oauth/start", hubUrl);
|
|
64149
|
+
startUrl.searchParams.set("redirect_uri", redirectUri);
|
|
64150
|
+
startUrl.searchParams.set("state", state);
|
|
64151
|
+
startUrl.searchParams.set("code_challenge", challenge);
|
|
64152
|
+
startUrl.searchParams.set("code_challenge_method", "S256");
|
|
64153
|
+
console.log("opening browser to authenticate with GitHub\u2026");
|
|
64154
|
+
console.log(` if it doesn't open automatically, paste this URL into a browser:`);
|
|
64155
|
+
console.log(` ${startUrl}`);
|
|
64090
64156
|
console.log("");
|
|
64091
|
-
|
|
64092
|
-
let
|
|
64093
|
-
|
|
64094
|
-
|
|
64095
|
-
|
|
64096
|
-
|
|
64097
|
-
|
|
64098
|
-
|
|
64099
|
-
|
|
64100
|
-
|
|
64101
|
-
|
|
64102
|
-
|
|
64103
|
-
|
|
64104
|
-
|
|
64105
|
-
|
|
64106
|
-
|
|
64107
|
-
|
|
64108
|
-
|
|
64109
|
-
|
|
64110
|
-
|
|
64111
|
-
|
|
64112
|
-
|
|
64113
|
-
|
|
64114
|
-
|
|
64115
|
-
throw new Error("authorization denied");
|
|
64116
|
-
}
|
|
64117
|
-
if (poll.status === "granted" && poll.user && poll.pat) {
|
|
64118
|
-
console.log("");
|
|
64119
|
-
return { user: poll.user, pat: poll.pat };
|
|
64157
|
+
openBrowser(startUrl.toString());
|
|
64158
|
+
let cb;
|
|
64159
|
+
try {
|
|
64160
|
+
cb = await Promise.race([
|
|
64161
|
+
awaitCallback(server),
|
|
64162
|
+
new Promise(
|
|
64163
|
+
(_, reject) => setTimeout(() => reject(new Error("login timed out after 5 minutes")), 5 * 60 * 1e3)
|
|
64164
|
+
)
|
|
64165
|
+
]);
|
|
64166
|
+
} finally {
|
|
64167
|
+
server.close();
|
|
64168
|
+
}
|
|
64169
|
+
if (cb.state !== state) throw new Error("state mismatch \u2014 possible CSRF; aborting");
|
|
64170
|
+
const res = await fetch(hubUrl.replace(/\/$/, "") + "/api/v1/auth/oauth/token", {
|
|
64171
|
+
method: "POST",
|
|
64172
|
+
headers: { "Content-Type": "application/json", "User-Agent": "clawborrator-cli" },
|
|
64173
|
+
body: JSON.stringify({ code: cb.code, code_verifier: verifier })
|
|
64174
|
+
});
|
|
64175
|
+
if (!res.ok) {
|
|
64176
|
+
const text = await res.text().catch(() => "");
|
|
64177
|
+
let parsed = null;
|
|
64178
|
+
try {
|
|
64179
|
+
parsed = JSON.parse(text);
|
|
64180
|
+
} catch {
|
|
64120
64181
|
}
|
|
64121
|
-
|
|
64122
|
-
throw new Error(`unexpected poll status: ${poll.status}`);
|
|
64182
|
+
throw new ApiError(res.status, parsed?.error ?? `http_${res.status}`, parsed?.message ?? parsed?.error ?? text);
|
|
64123
64183
|
}
|
|
64184
|
+
return res.json();
|
|
64124
64185
|
}
|
|
64125
|
-
var loginCmd = new Command("login").description("authenticate against a hub_v1 instance via GitHub
|
|
64186
|
+
var loginCmd = new Command("login").description("authenticate against a hub_v1 instance via GitHub OAuth (browser callback)").option("--hub <url>", "hub URL to use (defaults to config or http://localhost:8787)").action(async (opts) => {
|
|
64126
64187
|
const cfg = loadConfig();
|
|
64127
64188
|
const hubUrl = opts.hub ?? cfg.hubUrl;
|
|
64128
64189
|
try {
|
|
64129
|
-
const { user,
|
|
64130
|
-
saveConfig({ hubUrl,
|
|
64190
|
+
const { user, session } = await browserOAuthFlow(hubUrl);
|
|
64191
|
+
saveConfig({ hubUrl, sessionToken: session.token });
|
|
64131
64192
|
console.log(`logged in as @${user.githubLogin}`);
|
|
64132
|
-
console.log(`hub:
|
|
64133
|
-
console.log(`
|
|
64193
|
+
console.log(`hub: ${hubUrl}`);
|
|
64194
|
+
console.log(`session: ${session.token.slice(0, 16)}\u2026 (stored in ~/.clawborrator/config.json)`);
|
|
64195
|
+
console.log(`expires at: ${session.expiresAt}`);
|
|
64134
64196
|
} catch (e) {
|
|
64135
64197
|
if (e instanceof ApiError && e.status === 503) {
|
|
64136
64198
|
console.error("error: this hub does not have GitHub OAuth configured");
|
|
64137
|
-
console.error(" hub operator must register a GitHub OAuth App
|
|
64138
|
-
console.error(" and
|
|
64139
|
-
console.error(" see hub_v1/docs/SETUP.md for step-by-step");
|
|
64199
|
+
console.error(" hub operator must register a GitHub OAuth App, set GITHUB_CLIENT_ID +");
|
|
64200
|
+
console.error(" GITHUB_CLIENT_SECRET, and add the callback URL.");
|
|
64140
64201
|
process.exit(2);
|
|
64141
64202
|
}
|
|
64142
64203
|
throw e;
|
|
@@ -64144,34 +64205,27 @@ var loginCmd = new Command("login").description("authenticate against a hub_v1 i
|
|
|
64144
64205
|
});
|
|
64145
64206
|
|
|
64146
64207
|
// src/commands/logout.ts
|
|
64147
|
-
var logoutCmd = new Command("logout").description("forget the locally-stored
|
|
64208
|
+
var logoutCmd = new Command("logout").description("forget the locally-stored session token").option("--keep-server", "do not revoke the session server-side (default: revoke)").action(async (opts) => {
|
|
64148
64209
|
const cfg = loadConfig();
|
|
64149
|
-
if (!cfg.
|
|
64210
|
+
if (!cfg.sessionToken) {
|
|
64150
64211
|
console.log("not logged in; nothing to do");
|
|
64151
64212
|
return;
|
|
64152
64213
|
}
|
|
64153
64214
|
if (!opts.keepServer) {
|
|
64154
64215
|
try {
|
|
64155
|
-
|
|
64156
|
-
const list3 = await api.get(
|
|
64157
|
-
"/api/v1/tokens?kind=pat"
|
|
64158
|
-
);
|
|
64159
|
-
const target = list3.items.find((t) => cfg.pat.startsWith(t.prefix) && !t.revokedAt);
|
|
64160
|
-
if (target) {
|
|
64161
|
-
await api.delete(`/api/v1/tokens/${target.id}`);
|
|
64162
|
-
}
|
|
64216
|
+
await api.post("/api/v1/auth/logout");
|
|
64163
64217
|
} catch (e) {
|
|
64164
64218
|
console.warn(`warning: server-side revoke failed: ${e?.message ?? e}`);
|
|
64165
64219
|
}
|
|
64166
64220
|
}
|
|
64167
|
-
|
|
64168
|
-
console.log("logged out (
|
|
64221
|
+
clearSession();
|
|
64222
|
+
console.log("logged out (session cleared from local config)");
|
|
64169
64223
|
});
|
|
64170
64224
|
|
|
64171
64225
|
// src/commands/whoami.ts
|
|
64172
64226
|
var whoamiCmd = new Command("whoami").description("show the currently authenticated user").action(async () => {
|
|
64173
64227
|
const cfg = loadConfig();
|
|
64174
|
-
if (!cfg.
|
|
64228
|
+
if (!cfg.sessionToken) {
|
|
64175
64229
|
console.log("not logged in \u2014 run: claw login");
|
|
64176
64230
|
return;
|
|
64177
64231
|
}
|
|
@@ -64181,7 +64235,7 @@ var whoamiCmd = new Command("whoami").description("show the currently authentica
|
|
|
64181
64235
|
console.log(`hub: ${cfg.hubUrl}`);
|
|
64182
64236
|
} catch (e) {
|
|
64183
64237
|
if (e instanceof ApiError && e.status === 401) {
|
|
64184
|
-
console.error("error:
|
|
64238
|
+
console.error("error: session rejected by hub. Run: claw login");
|
|
64185
64239
|
process.exit(2);
|
|
64186
64240
|
}
|
|
64187
64241
|
throw e;
|
|
@@ -67702,7 +67756,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
67702
67756
|
if (opts.debug) debugMode = true;
|
|
67703
67757
|
if (opts.status === false) statusEnabled = false;
|
|
67704
67758
|
const cfg = loadConfig();
|
|
67705
|
-
if (!cfg.
|
|
67759
|
+
if (!cfg.sessionToken) {
|
|
67706
67760
|
console.error("error: not logged in. run `claw login`.");
|
|
67707
67761
|
process.exit(2);
|
|
67708
67762
|
}
|
|
@@ -67790,7 +67844,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
67790
67844
|
let reconnectAttempt = 0;
|
|
67791
67845
|
let reconnectTimer = null;
|
|
67792
67846
|
function connect() {
|
|
67793
|
-
ws = new wrapper_default(wsUrl, { headers: { Authorization: `Bearer ${cfg.
|
|
67847
|
+
ws = new wrapper_default(wsUrl, { headers: { Authorization: `Bearer ${cfg.sessionToken}` } });
|
|
67794
67848
|
ws.on("open", () => {
|
|
67795
67849
|
if (reconnectAttempt > 0) {
|
|
67796
67850
|
say(`${DIM2}[${ts()}]${RESET2} ${AMBER}reconnected${RESET2} to ${cfg.hubUrl}`);
|
|
@@ -68488,112 +68542,57 @@ var sessionCmd = new Command("session").description("manage Claude Code sessions
|
|
|
68488
68542
|
// src/commands/token.ts
|
|
68489
68543
|
var import_node_fs2 = require("node:fs");
|
|
68490
68544
|
var import_node_path2 = require("node:path");
|
|
68491
|
-
|
|
68492
|
-
|
|
68493
|
-
|
|
68494
|
-
|
|
68495
|
-
|
|
68496
|
-
|
|
68497
|
-
"
|
|
68498
|
-
|
|
68499
|
-
|
|
68500
|
-
|
|
68501
|
-
|
|
68502
|
-
|
|
68503
|
-
|
|
68504
|
-
|
|
68505
|
-
|
|
68506
|
-
|
|
68507
|
-
|
|
68508
|
-
|
|
68509
|
-
|
|
68510
|
-
|
|
68511
|
-
|
|
68512
|
-
|
|
68513
|
-
|
|
68514
|
-
|
|
68515
|
-
|
|
68516
|
-
"op-messages:write",
|
|
68517
|
-
"prompts:write",
|
|
68518
|
-
"webhooks:manage",
|
|
68519
|
-
"tokens:manage"
|
|
68520
|
-
];
|
|
68521
|
-
|
|
68522
|
-
// src/commands/token.ts
|
|
68523
|
-
var tokenMint = new Command("mint").description("create a new token").requiredOption("--kind <kind>", "channel | pat").requiredOption("--name <name>", 'human-readable label (e.g. "alice-laptop")').option("--scopes <csv>", "comma-separated scopes (PAT only); default = sensible CLI set").option("--app <name>", "pin the PAT to a specific app (display only)").option("--mcp-snippet", "after minting a channel token, also produce a ready-to-use .mcp.json block. By default writes to stdout (prose to stderr); pair with --out=<path> to write the file directly (recommended on Windows \u2014 `>` redirection in PowerShell encodes as UTF-16 w/ BOM, which CC rejects).").option("--out <path>", "when used with --mcp-snippet, write the JSON to <path> (UTF-8, no BOM) instead of stdout. Pass `.mcp.json` for the canonical project location.").action(async (opts) => {
|
|
68524
|
-
if (opts.kind === "channel") {
|
|
68525
|
-
const out = await api.post("/api/v1/tokens/channel", { name: opts.name });
|
|
68526
|
-
const proseToStderr = opts.mcpSnippet && !opts.out;
|
|
68527
|
-
const prose = proseToStderr ? console.error : console.log;
|
|
68528
|
-
prose(`\u2713 channel token minted: ${out.name}`);
|
|
68529
|
-
prose(` ${out.token}`);
|
|
68530
|
-
prose(" (shown ONCE \u2014 store it now)");
|
|
68531
|
-
if (opts.mcpSnippet) {
|
|
68532
|
-
const cfg = loadConfig();
|
|
68533
|
-
const wsUrl = cfg.hubUrl.replace(/^http(s?):\/\//, "ws$1://");
|
|
68534
|
-
const json = JSON.stringify({
|
|
68535
|
-
mcpServers: {
|
|
68536
|
-
clawborrator: {
|
|
68537
|
-
command: "npx",
|
|
68538
|
-
args: ["-y", "clawborrator-mcp"],
|
|
68539
|
-
env: {
|
|
68540
|
-
CLAWBORRATOR_HUB_URL: wsUrl,
|
|
68541
|
-
CLAWBORRATOR_TOKEN: out.token
|
|
68542
|
-
}
|
|
68543
|
-
}
|
|
68544
|
-
}
|
|
68545
|
-
}, null, 2) + "\n";
|
|
68546
|
-
if (opts.out) {
|
|
68547
|
-
const target = (0, import_node_path2.resolve)(opts.out);
|
|
68548
|
-
(0, import_node_fs2.writeFileSync)(target, json, "utf8");
|
|
68549
|
-
prose("");
|
|
68550
|
-
prose(`\u2713 wrote ${target}`);
|
|
68551
|
-
} else {
|
|
68552
|
-
process.stdout.write(json);
|
|
68553
|
-
}
|
|
68545
|
+
var tokenMint = new Command("mint").description("create a new channel token").requiredOption("--name <name>", 'human-readable label (e.g. "alice-laptop")').option("--mcp-snippet", "after minting, also produce a ready-to-use .mcp.json block. By default writes to stdout (prose to stderr); pair with --out=<path> to write the file directly (recommended on Windows \u2014 `>` redirection in PowerShell encodes as UTF-16 w/ BOM, which CC rejects).").option("--out <path>", "when used with --mcp-snippet, write the JSON to <path> (UTF-8, no BOM) instead of stdout. Pass `.mcp.json` for the canonical project location.").action(async (opts) => {
|
|
68546
|
+
const out = await api.post("/api/v1/tokens/channel", { name: opts.name });
|
|
68547
|
+
const proseToStderr = opts.mcpSnippet && !opts.out;
|
|
68548
|
+
const prose = proseToStderr ? console.error : console.log;
|
|
68549
|
+
prose(`\u2713 channel token minted: ${out.name}`);
|
|
68550
|
+
prose(` ${out.token}`);
|
|
68551
|
+
prose(" (shown ONCE \u2014 store it now)");
|
|
68552
|
+
if (opts.mcpSnippet) {
|
|
68553
|
+
const cfg = loadConfig();
|
|
68554
|
+
const wsUrl = cfg.hubUrl.replace(/^http(s?):\/\//, "ws$1://");
|
|
68555
|
+
const json = JSON.stringify({
|
|
68556
|
+
mcpServers: {
|
|
68557
|
+
clawborrator: {
|
|
68558
|
+
command: "npx",
|
|
68559
|
+
args: ["-y", "clawborrator-mcp"],
|
|
68560
|
+
env: {
|
|
68561
|
+
CLAWBORRATOR_HUB_URL: wsUrl,
|
|
68562
|
+
CLAWBORRATOR_TOKEN: out.token
|
|
68563
|
+
}
|
|
68564
|
+
}
|
|
68565
|
+
}
|
|
68566
|
+
}, null, 2) + "\n";
|
|
68567
|
+
if (opts.out) {
|
|
68568
|
+
const target = (0, import_node_path2.resolve)(opts.out);
|
|
68569
|
+
(0, import_node_fs2.writeFileSync)(target, json, "utf8");
|
|
68554
68570
|
prose("");
|
|
68555
|
-
prose(
|
|
68556
|
-
|
|
68571
|
+
prose(`\u2713 wrote ${target}`);
|
|
68572
|
+
} else {
|
|
68573
|
+
process.stdout.write(json);
|
|
68557
68574
|
}
|
|
68558
|
-
|
|
68559
|
-
|
|
68560
|
-
|
|
68561
|
-
const scopes = opts.scopes ? opts.scopes.split(",").map((s) => s.trim()).filter(Boolean) : CLI_DEFAULT_SCOPES;
|
|
68562
|
-
const out = await api.post("/api/v1/tokens/pat", {
|
|
68563
|
-
name: opts.name,
|
|
68564
|
-
scopes,
|
|
68565
|
-
appName: opts.app ?? null
|
|
68566
|
-
});
|
|
68567
|
-
console.log(`\u2713 PAT minted: ${out.name}`);
|
|
68568
|
-
console.log(` ${out.token}`);
|
|
68569
|
-
console.log(` scopes: ${out.scopes.join(", ")}`);
|
|
68570
|
-
console.log(" (shown ONCE \u2014 store it now)");
|
|
68571
|
-
return;
|
|
68575
|
+
prose("");
|
|
68576
|
+
prose(" next: launch CC with the clawborrator channel enabled \u2014");
|
|
68577
|
+
prose(" claude --dangerously-load-development-channels server:clawborrator");
|
|
68572
68578
|
}
|
|
68573
|
-
console.error(`error: --kind must be channel or pat (got ${opts.kind})`);
|
|
68574
|
-
process.exit(2);
|
|
68575
68579
|
});
|
|
68576
|
-
var tokenList = new Command("list").alias("ls").description("list
|
|
68577
|
-
const
|
|
68578
|
-
const data = await api.get(`/api/v1/tokens${qs}`);
|
|
68580
|
+
var tokenList = new Command("list").alias("ls").description("list channel tokens for the current user").action(async () => {
|
|
68581
|
+
const data = await api.get("/api/v1/tokens");
|
|
68579
68582
|
if (data.items.length === 0) {
|
|
68580
|
-
console.log("no active tokens");
|
|
68583
|
+
console.log("no active channel tokens");
|
|
68581
68584
|
return;
|
|
68582
68585
|
}
|
|
68583
68586
|
for (const t of data.items) {
|
|
68584
68587
|
const used = t.lastUsedAt ? `last used ${fmtAgo2(t.lastUsedAt)}` : "never used";
|
|
68585
|
-
|
|
68586
|
-
console.log(`${t.id.toString().padStart(3)} ${t.kind.padEnd(7)} ${t.prefix}\u2026 ${t.name.padEnd(28)} ${used}${meta}`);
|
|
68588
|
+
console.log(`${t.id.toString().padStart(3)} ${t.prefix}\u2026 ${t.name.padEnd(28)} ${used}`);
|
|
68587
68589
|
}
|
|
68588
68590
|
});
|
|
68589
|
-
var tokenRevoke = new Command("revoke").description("revoke a token by id").argument("<id>", "token id (from `claw token list`)").action(async (id) => {
|
|
68591
|
+
var tokenRevoke = new Command("revoke").description("revoke a channel token by id").argument("<id>", "token id (from `claw token list`)").action(async (id) => {
|
|
68590
68592
|
await api.delete(`/api/v1/tokens/${encodeURIComponent(id)}`);
|
|
68591
68593
|
console.log(`\u2713 token ${id} revoked`);
|
|
68592
68594
|
});
|
|
68593
|
-
var
|
|
68594
|
-
for (const s of ALL_SCOPES) console.log(s);
|
|
68595
|
-
});
|
|
68596
|
-
var tokenCmd = new Command("token").description("mint, list, and revoke channel tokens + PATs").addCommand(tokenMint).addCommand(tokenList).addCommand(tokenRevoke).addCommand(tokenScopes);
|
|
68595
|
+
var tokenCmd = new Command("token").description("mint, list, and revoke channel tokens").addCommand(tokenMint).addCommand(tokenList).addCommand(tokenRevoke);
|
|
68597
68596
|
function fmtAgo2(iso) {
|
|
68598
68597
|
const ms = Date.now() - new Date(iso).getTime();
|
|
68599
68598
|
if (ms < 6e4) return Math.max(1, Math.floor(ms / 1e3)) + "s ago";
|
|
@@ -68703,7 +68702,7 @@ var webhookCmd = new Command("webhook").description("manage webhook subscription
|
|
|
68703
68702
|
|
|
68704
68703
|
// src/index.ts
|
|
68705
68704
|
var program2 = new Command();
|
|
68706
|
-
program2.name("claw").description("clawborrator CLI \u2014 control your Claude Code sessions from the terminal").version("0.0.
|
|
68705
|
+
program2.name("claw").description("clawborrator CLI \u2014 control your Claude Code sessions from the terminal").version("0.0.39");
|
|
68707
68706
|
program2.addCommand(loginCmd);
|
|
68708
68707
|
program2.addCommand(logoutCmd);
|
|
68709
68708
|
program2.addCommand(whoamiCmd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawborrator-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.39",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "claw — command-line client for clawborrator hub_v1. Manages PATs, channel tokens, sessions, cross-session routing, and webhooks; ships an inline TUI for live multi-operator session attach.",
|
|
6
6
|
"license": "MIT",
|