clawborrator-cli 0.0.22 → 0.0.24
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 +161 -60
- package/package.json +1 -1
package/dist-bundled/claw.cjs
CHANGED
|
@@ -6844,7 +6844,7 @@ var whoamiCmd = new Command("whoami").description("show the currently authentica
|
|
|
6844
6844
|
});
|
|
6845
6845
|
|
|
6846
6846
|
// src/commands/session-attach.ts
|
|
6847
|
-
var
|
|
6847
|
+
var import_node_readline2 = require("node:readline");
|
|
6848
6848
|
|
|
6849
6849
|
// ../node_modules/ws/wrapper.mjs
|
|
6850
6850
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -6857,13 +6857,65 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
6857
6857
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
6858
6858
|
var wrapper_default = import_websocket.default;
|
|
6859
6859
|
|
|
6860
|
-
// src/
|
|
6860
|
+
// src/util/disambiguate.ts
|
|
6861
|
+
var import_node_readline = require("node:readline");
|
|
6861
6862
|
var RESET = "\x1B[0m";
|
|
6862
6863
|
var DIM = "\x1B[2m";
|
|
6864
|
+
var BOLD = "\x1B[1m";
|
|
6865
|
+
var AmbiguousError = class extends Error {
|
|
6866
|
+
constructor(candidates, input) {
|
|
6867
|
+
super(`ambiguous reference '${input}' \u2014 multiple online sessions match`);
|
|
6868
|
+
this.candidates = candidates;
|
|
6869
|
+
this.input = input;
|
|
6870
|
+
}
|
|
6871
|
+
code = "CLW_AMBIGUOUS";
|
|
6872
|
+
};
|
|
6873
|
+
async function pickCandidate(input, candidates) {
|
|
6874
|
+
if (candidates.length === 1) return candidates[0];
|
|
6875
|
+
const live = candidates.filter((c) => c.connected);
|
|
6876
|
+
if (live.length <= 1) {
|
|
6877
|
+
return live[0] ?? candidates[0];
|
|
6878
|
+
}
|
|
6879
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
6880
|
+
throw new AmbiguousError(live, input);
|
|
6881
|
+
}
|
|
6882
|
+
process.stderr.write(`${BOLD}'${input}' is ambiguous \u2014 pick a session:${RESET}
|
|
6883
|
+
`);
|
|
6884
|
+
for (let i = 0; i < live.length; i++) {
|
|
6885
|
+
const c = live[i];
|
|
6886
|
+
const qualified = c.routingName ? `@${c.startedByLogin}/${c.routingName.replace(/^@/, "")}` : `(no routing name)`;
|
|
6887
|
+
const cwd = c.cwd ? ` ${DIM}${c.cwd}${RESET}` : "";
|
|
6888
|
+
const host = c.host ? ` ${DIM}${c.host}${RESET}` : "";
|
|
6889
|
+
process.stderr.write(` ${BOLD}${i + 1}${RESET}. ${qualified}${host}${cwd}
|
|
6890
|
+
`);
|
|
6891
|
+
process.stderr.write(` ${DIM}id ${c.id}${RESET}
|
|
6892
|
+
`);
|
|
6893
|
+
}
|
|
6894
|
+
process.stderr.write(` ${BOLD}q${RESET}. cancel
|
|
6895
|
+
`);
|
|
6896
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stderr });
|
|
6897
|
+
const answer = await new Promise((resolve3) => {
|
|
6898
|
+
rl.question(`pick [1-${live.length}]: `, resolve3);
|
|
6899
|
+
});
|
|
6900
|
+
rl.close();
|
|
6901
|
+
const trimmed = answer.trim().toLowerCase();
|
|
6902
|
+
if (trimmed === "q" || trimmed === "quit" || trimmed === "") {
|
|
6903
|
+
throw new Error("cancelled");
|
|
6904
|
+
}
|
|
6905
|
+
const idx = parseInt(trimmed, 10);
|
|
6906
|
+
if (!Number.isInteger(idx) || idx < 1 || idx > live.length) {
|
|
6907
|
+
throw new Error(`invalid selection '${answer}'`);
|
|
6908
|
+
}
|
|
6909
|
+
return live[idx - 1];
|
|
6910
|
+
}
|
|
6911
|
+
|
|
6912
|
+
// src/commands/session-attach.ts
|
|
6913
|
+
var RESET2 = "\x1B[0m";
|
|
6914
|
+
var DIM2 = "\x1B[2m";
|
|
6863
6915
|
var AMBER = "\x1B[33m";
|
|
6864
6916
|
var BLUE = "\x1B[36m";
|
|
6865
6917
|
var RED = "\x1B[31m";
|
|
6866
|
-
var
|
|
6918
|
+
var BOLD2 = "\x1B[1m";
|
|
6867
6919
|
var GREEN = "\x1B[32m";
|
|
6868
6920
|
function ts() {
|
|
6869
6921
|
return (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
@@ -6894,15 +6946,32 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
6894
6946
|
slug = needle;
|
|
6895
6947
|
}
|
|
6896
6948
|
const data = await api.get("/api/v1/sessions");
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6949
|
+
let candidates = data.items.filter((s) => s.routingName === slug);
|
|
6950
|
+
if (ownerLogin !== null) {
|
|
6951
|
+
candidates = candidates.filter((s) => s.startedByLogin === ownerLogin);
|
|
6952
|
+
} else {
|
|
6953
|
+
const mine = candidates.filter((s) => s.role === "owner");
|
|
6954
|
+
if (mine.length > 0) candidates = mine;
|
|
6955
|
+
}
|
|
6956
|
+
if (candidates.length === 0) {
|
|
6901
6957
|
const label = ownerLogin ? `@${ownerLogin}/${slug.slice(1)}` : slug;
|
|
6902
6958
|
console.error(`error: no session with routing name ${label} (run \`claw session list\` to see what's available)`);
|
|
6903
6959
|
process.exit(2);
|
|
6904
6960
|
}
|
|
6905
|
-
|
|
6961
|
+
try {
|
|
6962
|
+
const picked = await pickCandidate(ref, candidates);
|
|
6963
|
+
sessionId = picked.id;
|
|
6964
|
+
} catch (e) {
|
|
6965
|
+
if (e instanceof AmbiguousError) {
|
|
6966
|
+
console.error(`error: '${ref}' is ambiguous \u2014 multiple online sessions match. Re-run with a UUID or qualified form (e.g. @owner/slug). Candidates:`);
|
|
6967
|
+
for (const c of e.candidates) {
|
|
6968
|
+
console.error(` ${c.id} @${c.startedByLogin}/${(c.routingName ?? "").replace(/^@/, "")} ${c.cwd ?? ""}`);
|
|
6969
|
+
}
|
|
6970
|
+
process.exit(2);
|
|
6971
|
+
}
|
|
6972
|
+
console.error(`error: ${e?.message ?? String(e)}`);
|
|
6973
|
+
process.exit(2);
|
|
6974
|
+
}
|
|
6906
6975
|
}
|
|
6907
6976
|
const wsUrl = cfg.hubUrl.replace(/^http/i, "ws") + "/cli";
|
|
6908
6977
|
const ws = new wrapper_default(wsUrl, {
|
|
@@ -6911,7 +6980,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
6911
6980
|
let mySubscription = false;
|
|
6912
6981
|
const pendingPerms = [];
|
|
6913
6982
|
ws.on("open", () => {
|
|
6914
|
-
console.log(`${
|
|
6983
|
+
console.log(`${DIM2}[${ts()}]${RESET2} connected to ${cfg.hubUrl}`);
|
|
6915
6984
|
const sub = { type: "subscribe", sessionId };
|
|
6916
6985
|
ws.send(JSON.stringify(sub));
|
|
6917
6986
|
});
|
|
@@ -6931,22 +7000,22 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
6931
7000
|
printInbound(msg, myLogin);
|
|
6932
7001
|
if (msg.type === "subscribed") {
|
|
6933
7002
|
mySubscription = true;
|
|
6934
|
-
console.log(`${
|
|
7003
|
+
console.log(`${DIM2}attached as ${BOLD2}${msg.role}${RESET2}${DIM2}. type for prompt \xB7 @other <text> to route \xB7 /m <text> for op-msg \xB7 /y /n on permissions \xB7 /q to quit${RESET2}`);
|
|
6935
7004
|
}
|
|
6936
7005
|
});
|
|
6937
7006
|
ws.on("close", (code, reason) => {
|
|
6938
|
-
console.log(`${
|
|
7007
|
+
console.log(`${DIM2}[${ts()}] disconnected (${code}${reason ? ": " + reason.toString() : ""})${RESET2}`);
|
|
6939
7008
|
process.exit(0);
|
|
6940
7009
|
});
|
|
6941
7010
|
ws.on("error", (err) => {
|
|
6942
|
-
console.error(`${RED}ws error: ${err.message}${
|
|
7011
|
+
console.error(`${RED}ws error: ${err.message}${RESET2}`);
|
|
6943
7012
|
});
|
|
6944
|
-
const rl = (0,
|
|
7013
|
+
const rl = (0, import_node_readline2.createInterface)({ input: process.stdin, terminal: false });
|
|
6945
7014
|
rl.on("line", (raw) => {
|
|
6946
7015
|
const text = raw.trim();
|
|
6947
7016
|
if (!text) return;
|
|
6948
7017
|
if (!mySubscription) {
|
|
6949
|
-
console.log(`${
|
|
7018
|
+
console.log(`${DIM2}(not subscribed yet \u2014 waiting...)${RESET2}`);
|
|
6950
7019
|
return;
|
|
6951
7020
|
}
|
|
6952
7021
|
if (text === "/q" || text === "/quit") {
|
|
@@ -6956,7 +7025,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
6956
7025
|
if (text === "/y" || text === "/yes" || text === "/n" || text === "/no") {
|
|
6957
7026
|
const pending = pendingPerms[pendingPerms.length - 1];
|
|
6958
7027
|
if (!pending) {
|
|
6959
|
-
console.log(`${
|
|
7028
|
+
console.log(`${DIM2}(no pending permission to act on)${RESET2}`);
|
|
6960
7029
|
return;
|
|
6961
7030
|
}
|
|
6962
7031
|
const decision = text === "/y" || text === "/yes" ? "allow" : "deny";
|
|
@@ -6973,7 +7042,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
6973
7042
|
if (text === "/m" || text.startsWith("/m ")) {
|
|
6974
7043
|
const opText = text.slice(2).trim();
|
|
6975
7044
|
if (!opText) {
|
|
6976
|
-
console.log(`${
|
|
7045
|
+
console.log(`${DIM2}usage: /m <text> (sends as op-message; bare text is a prompt)${RESET2}`);
|
|
6977
7046
|
return;
|
|
6978
7047
|
}
|
|
6979
7048
|
const out2 = { type: "op_message", sessionId, text: opText };
|
|
@@ -6983,16 +7052,16 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
6983
7052
|
if (text === "/p" || text.startsWith("/p ")) {
|
|
6984
7053
|
const promptText = text.slice(2).trim();
|
|
6985
7054
|
if (!promptText) {
|
|
6986
|
-
console.log(`${
|
|
7055
|
+
console.log(`${DIM2}usage: /p <text> (or just type \u2014 bare text is a prompt now)${RESET2}`);
|
|
6987
7056
|
return;
|
|
6988
7057
|
}
|
|
6989
7058
|
const out2 = { type: "prompt", sessionId, text: promptText };
|
|
6990
7059
|
ws.send(JSON.stringify(out2));
|
|
6991
|
-
console.log(`${
|
|
7060
|
+
console.log(`${DIM2}[${ts()}]${RESET2} ${AMBER}\u2192 prompt sent${RESET2} ${promptText}`);
|
|
6992
7061
|
return;
|
|
6993
7062
|
}
|
|
6994
7063
|
if (text.startsWith("/")) {
|
|
6995
|
-
console.log(`${
|
|
7064
|
+
console.log(`${DIM2}unknown slash-command: ${text} (try /m /y /n /q)${RESET2}`);
|
|
6996
7065
|
return;
|
|
6997
7066
|
}
|
|
6998
7067
|
const xMatch = /^(@[A-Za-z0-9._\-/]+)\s+([\s\S]+)$/.exec(text);
|
|
@@ -7000,7 +7069,7 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
7000
7069
|
const targetRef = xMatch[1];
|
|
7001
7070
|
const promptText = xMatch[2].trim();
|
|
7002
7071
|
if (!promptText) {
|
|
7003
|
-
console.log(`${
|
|
7072
|
+
console.log(`${DIM2}usage: ${targetRef} <prompt>${RESET2}`);
|
|
7004
7073
|
return;
|
|
7005
7074
|
}
|
|
7006
7075
|
(async () => {
|
|
@@ -7016,13 +7085,26 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
7016
7085
|
}
|
|
7017
7086
|
try {
|
|
7018
7087
|
const data = await api.get("/api/v1/sessions");
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7088
|
+
let candidates = data.items.filter((s) => s.routingName === slug);
|
|
7089
|
+
if (ownerLogin !== null) {
|
|
7090
|
+
candidates = candidates.filter((s) => s.startedByLogin === ownerLogin);
|
|
7091
|
+
} else {
|
|
7092
|
+
const mine = candidates.filter((s) => s.role === "owner");
|
|
7093
|
+
if (mine.length > 0) candidates = mine;
|
|
7094
|
+
}
|
|
7095
|
+
if (candidates.length === 0) {
|
|
7096
|
+
console.error(`${RED}error: no session ${targetRef} (try \`claw session list\` in another terminal)${RESET2}`);
|
|
7097
|
+
return;
|
|
7098
|
+
}
|
|
7099
|
+
const liveMatches = candidates.filter((c) => c.connected);
|
|
7100
|
+
if (liveMatches.length > 1) {
|
|
7101
|
+
console.error(`${RED}error: '${targetRef}' is ambiguous \u2014 multiple online matches. Use the qualified form:${RESET2}`);
|
|
7102
|
+
for (const c of liveMatches) {
|
|
7103
|
+
console.error(` ${DIM2}${c.id.slice(0, 8)}\u2026${RESET2} @${c.startedByLogin}/${(c.routingName ?? "").replace(/^@/, "")} ${DIM2}${c.cwd ?? ""}${RESET2}`);
|
|
7104
|
+
}
|
|
7024
7105
|
return;
|
|
7025
7106
|
}
|
|
7107
|
+
const match = candidates[0];
|
|
7026
7108
|
const out2 = {
|
|
7027
7109
|
type: "prompt",
|
|
7028
7110
|
sessionId: match.id,
|
|
@@ -7034,16 +7116,16 @@ var sessionAttach = new Command("attach").description("open a TUI on a session \
|
|
|
7034
7116
|
sourceSessionId: sessionId
|
|
7035
7117
|
};
|
|
7036
7118
|
ws.send(JSON.stringify(out2));
|
|
7037
|
-
console.log(`${
|
|
7119
|
+
console.log(`${DIM2}[${ts()}]${RESET2} ${AMBER}\u2192 prompt \u2192 ${targetRef}${RESET2} ${promptText}`);
|
|
7038
7120
|
} catch (e) {
|
|
7039
|
-
console.error(`${RED}error: ${e?.message ?? String(e)}${
|
|
7121
|
+
console.error(`${RED}error: ${e?.message ?? String(e)}${RESET2}`);
|
|
7040
7122
|
}
|
|
7041
7123
|
})();
|
|
7042
7124
|
return;
|
|
7043
7125
|
}
|
|
7044
7126
|
const out = { type: "prompt", sessionId, text };
|
|
7045
7127
|
ws.send(JSON.stringify(out));
|
|
7046
|
-
console.log(`${
|
|
7128
|
+
console.log(`${DIM2}[${ts()}]${RESET2} ${AMBER}\u2192 prompt${RESET2} ${text}`);
|
|
7047
7129
|
});
|
|
7048
7130
|
process.on("SIGINT", () => {
|
|
7049
7131
|
ws.close(1e3, "sigint");
|
|
@@ -7058,26 +7140,26 @@ function printInbound(msg, myLogin) {
|
|
|
7058
7140
|
break;
|
|
7059
7141
|
}
|
|
7060
7142
|
case "op_message": {
|
|
7061
|
-
console.log(`${
|
|
7143
|
+
console.log(`${DIM2}[${shortTs(msg.ts)}]${RESET2} ${GREEN}@${msg.authorLogin}${RESET2} ${msg.text}`);
|
|
7062
7144
|
break;
|
|
7063
7145
|
}
|
|
7064
7146
|
case "permission_request": {
|
|
7065
|
-
console.log(`${RED}[!] ${shortTs(msg.ts)}${
|
|
7147
|
+
console.log(`${RED}[!] ${shortTs(msg.ts)}${RESET2} approval needed: ${BOLD2}${msg.tool}${RESET2} \u2014 ${msg.inputPreview}`);
|
|
7066
7148
|
break;
|
|
7067
7149
|
}
|
|
7068
7150
|
case "permission_resolved": {
|
|
7069
|
-
const dec = msg.decision === "allow" ? `${GREEN}allowed${
|
|
7070
|
-
console.log(`${
|
|
7151
|
+
const dec = msg.decision === "allow" ? `${GREEN}allowed${RESET2}` : msg.decision === "deny" ? `${RED}denied${RESET2}` : `${DIM2}expired${RESET2}`;
|
|
7152
|
+
console.log(`${DIM2}[${ts()}]${RESET2} permission ${msg.requestId} ${dec} by @${msg.resolverLogin ?? "?"}`);
|
|
7071
7153
|
break;
|
|
7072
7154
|
}
|
|
7073
7155
|
case "presence": {
|
|
7074
|
-
console.log(`${
|
|
7156
|
+
console.log(`${DIM2}[${ts()}] presence: ${msg.attached.map((l) => "@" + l).join(", ") || "(empty)"}${RESET2}`);
|
|
7075
7157
|
break;
|
|
7076
7158
|
}
|
|
7077
7159
|
case "ack":
|
|
7078
7160
|
break;
|
|
7079
7161
|
case "error":
|
|
7080
|
-
console.error(`${RED}error (${msg.code}): ${msg.message}${
|
|
7162
|
+
console.error(`${RED}error (${msg.code}): ${msg.message}${RESET2}`);
|
|
7081
7163
|
break;
|
|
7082
7164
|
}
|
|
7083
7165
|
}
|
|
@@ -7100,58 +7182,58 @@ function renderEvent(ev, myLogin) {
|
|
|
7100
7182
|
const text = String(p.text ?? p.prompt ?? "").trim() || JSON.stringify(p).slice(0, 200);
|
|
7101
7183
|
const source = String(p.source ?? "cli");
|
|
7102
7184
|
if (source === "operator" && myLogin && p.authorLogin === myLogin) return;
|
|
7103
|
-
const label = source === "operator" ? `${
|
|
7104
|
-
console.log(`${
|
|
7185
|
+
const label = source === "operator" ? `${BOLD2}@${String(p.authorLogin ?? "remote")} \u203A${RESET2}` : `${BOLD2}(cli) \u203A${RESET2}`;
|
|
7186
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${label} ${text}`);
|
|
7105
7187
|
return;
|
|
7106
7188
|
}
|
|
7107
7189
|
if (ev.type === "assistant_text") {
|
|
7108
7190
|
const text = String(p.text ?? "").trim();
|
|
7109
7191
|
const isPlaceholder = !!p.placeholder;
|
|
7110
|
-
const tag = isPlaceholder ? `${
|
|
7111
|
-
console.log(`${
|
|
7192
|
+
const tag = isPlaceholder ? `${DIM2}claude \xB7 thinking${RESET2}` : `${AMBER}claude${RESET2}`;
|
|
7193
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${tag} ${isPlaceholder ? DIM2 + text + RESET2 : text}`);
|
|
7112
7194
|
return;
|
|
7113
7195
|
}
|
|
7114
7196
|
if (ev.type === "reply") {
|
|
7115
7197
|
const text = String(p.text ?? "").trim();
|
|
7116
7198
|
if (p.source === "peer-reply" && p.peerLogin) {
|
|
7117
|
-
console.log(`${
|
|
7199
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${AMBER}${String(p.peerLogin)} answered${RESET2} ${text}`);
|
|
7118
7200
|
return;
|
|
7119
7201
|
}
|
|
7120
|
-
const tag = p.chatId ? `${AMBER}claude${
|
|
7121
|
-
console.log(`${
|
|
7202
|
+
const tag = p.chatId ? `${AMBER}claude${RESET2} ${DIM2}(reply to ${String(p.chatId).slice(0, 8)})${RESET2}` : `${AMBER}claude${RESET2}`;
|
|
7203
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${tag} ${text}`);
|
|
7122
7204
|
return;
|
|
7123
7205
|
}
|
|
7124
|
-
console.log(`${
|
|
7206
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${AMBER}[chat:${ev.type}]${RESET2} ${previewPayload(p)}`);
|
|
7125
7207
|
return;
|
|
7126
7208
|
}
|
|
7127
7209
|
switch (ev.type) {
|
|
7128
7210
|
case "PreToolUse": {
|
|
7129
7211
|
const tool = String(p.tool_name ?? p.toolName ?? p.tool ?? "?");
|
|
7130
7212
|
const input = renderToolInput(p);
|
|
7131
|
-
console.log(`${
|
|
7213
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${BLUE}\u2192 ${tool}${RESET2} ${DIM2}${input}${RESET2}`);
|
|
7132
7214
|
return;
|
|
7133
7215
|
}
|
|
7134
7216
|
case "PostToolUse": {
|
|
7135
7217
|
const tool = String(p.tool_name ?? p.toolName ?? p.tool ?? "?");
|
|
7136
7218
|
const out = stringifyToolResponse(p.tool_response ?? p.toolResponse ?? p.outputPreview ?? p.output);
|
|
7137
|
-
const ok = p.ok === false ? `${RED}\u2717${
|
|
7138
|
-
console.log(`${
|
|
7219
|
+
const ok = p.ok === false ? `${RED}\u2717${RESET2}` : `${BLUE}\u2713${RESET2}`;
|
|
7220
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${ok} ${tool}${out ? " " + DIM2 + truncate(out, 200) + RESET2 : ""}`);
|
|
7139
7221
|
return;
|
|
7140
7222
|
}
|
|
7141
7223
|
case "PostToolUseFailure": {
|
|
7142
7224
|
const tool = String(p.tool_name ?? p.toolName ?? p.tool ?? "?");
|
|
7143
7225
|
const err = stringifyToolResponse(p.error ?? p.message ?? p.tool_response);
|
|
7144
|
-
console.log(`${
|
|
7226
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${RED}\u2717 ${tool}${RESET2} ${RED}${truncate(err, 200)}${RESET2}`);
|
|
7145
7227
|
return;
|
|
7146
7228
|
}
|
|
7147
7229
|
case "Stop":
|
|
7148
|
-
console.log(`${
|
|
7230
|
+
console.log(`${DIM2}[${ts2}] \u2014 turn end \u2014${RESET2}`);
|
|
7149
7231
|
return;
|
|
7150
7232
|
case "SessionStart":
|
|
7151
|
-
console.log(`${
|
|
7233
|
+
console.log(`${DIM2}[${ts2}] \u25B8 session start${RESET2}`);
|
|
7152
7234
|
return;
|
|
7153
7235
|
case "SessionEnd":
|
|
7154
|
-
console.log(`${
|
|
7236
|
+
console.log(`${DIM2}[${ts2}] \u25C2 session end${RESET2}`);
|
|
7155
7237
|
return;
|
|
7156
7238
|
case "TaskCreated":
|
|
7157
7239
|
case "SubagentStart":
|
|
@@ -7159,16 +7241,16 @@ function renderEvent(ev, myLogin) {
|
|
|
7159
7241
|
case "TaskCompleted": {
|
|
7160
7242
|
const which = ev.type;
|
|
7161
7243
|
const desc = String(p.description ?? p.agentType ?? "");
|
|
7162
|
-
console.log(`${
|
|
7244
|
+
console.log(`${DIM2}[${ts2}] ${BLUE}\u21AA ${which}${RESET2}${desc ? " " + desc : ""}`);
|
|
7163
7245
|
return;
|
|
7164
7246
|
}
|
|
7165
7247
|
case "Notification": {
|
|
7166
7248
|
const msg = String(p.message ?? p.text ?? "").trim();
|
|
7167
|
-
console.log(`${
|
|
7249
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${BOLD2}\u{1F514}${RESET2} ${msg || JSON.stringify(p).slice(0, 200)}`);
|
|
7168
7250
|
return;
|
|
7169
7251
|
}
|
|
7170
7252
|
default:
|
|
7171
|
-
console.log(`${
|
|
7253
|
+
console.log(`${DIM2}[${ts2}]${RESET2} ${BLUE}[${ev.type}]${RESET2} ${previewPayload(p)}`);
|
|
7172
7254
|
}
|
|
7173
7255
|
}
|
|
7174
7256
|
function stringifyToolResponse(v) {
|
|
@@ -7235,17 +7317,34 @@ async function resolveSessionId(idOrName) {
|
|
|
7235
7317
|
slug = needle;
|
|
7236
7318
|
}
|
|
7237
7319
|
const data = await api.get("/api/v1/sessions");
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7320
|
+
let candidates = data.items.filter((s) => s.routingName === slug);
|
|
7321
|
+
if (ownerLogin !== null) {
|
|
7322
|
+
candidates = candidates.filter((s) => s.startedByLogin === ownerLogin);
|
|
7323
|
+
} else {
|
|
7324
|
+
const mine = candidates.filter((s) => s.role === "owner");
|
|
7325
|
+
if (mine.length > 0) candidates = mine;
|
|
7326
|
+
}
|
|
7327
|
+
if (candidates.length === 0) {
|
|
7242
7328
|
const err = new Error(
|
|
7243
7329
|
ownerLogin ? `no session @${ownerLogin}/${slug.slice(1)} (run \`claw session list\`)` : `no session with routing name ${slug} (run \`claw session list\`)`
|
|
7244
7330
|
);
|
|
7245
7331
|
err.code = "CLW_NO_ROUTING_MATCH";
|
|
7246
7332
|
throw err;
|
|
7247
7333
|
}
|
|
7248
|
-
|
|
7334
|
+
try {
|
|
7335
|
+
const picked = await pickCandidate(idOrName, candidates);
|
|
7336
|
+
return picked.id;
|
|
7337
|
+
} catch (e) {
|
|
7338
|
+
if (e instanceof AmbiguousError) {
|
|
7339
|
+
const err = new Error(
|
|
7340
|
+
`ambiguous reference '${idOrName}' \u2014 multiple online sessions match. Re-run with a UUID or qualified form. Candidates:
|
|
7341
|
+
` + e.candidates.map((c) => ` ${c.id} @${c.startedByLogin}/${(c.routingName ?? "").replace(/^@/, "")} ${c.cwd ?? ""}`).join("\n")
|
|
7342
|
+
);
|
|
7343
|
+
err.code = "CLW_AMBIGUOUS";
|
|
7344
|
+
throw err;
|
|
7345
|
+
}
|
|
7346
|
+
throw e;
|
|
7347
|
+
}
|
|
7249
7348
|
}
|
|
7250
7349
|
var sessionList = new Command("list").alias("ls").description("list sessions you can see").option("--connected", "only sessions whose channel WS is currently open").option("--all", "include archived sessions").action(async (opts) => {
|
|
7251
7350
|
const qs = new URLSearchParams();
|
|
@@ -7564,7 +7663,7 @@ function fmtAgo2(iso) {
|
|
|
7564
7663
|
}
|
|
7565
7664
|
|
|
7566
7665
|
// src/commands/peers.ts
|
|
7567
|
-
var peersCmd = new Command("peers").description("list your sessions reachable for cross-session routing").action(async () => {
|
|
7666
|
+
var peersCmd = new Command("peers").description("list your sessions reachable for cross-session routing \u2014 own + shared").action(async () => {
|
|
7568
7667
|
const data = await api.get("/api/v1/peers");
|
|
7569
7668
|
if (data.peers.length === 0) {
|
|
7570
7669
|
console.log("no peers (no sessions registered yet)");
|
|
@@ -7573,7 +7672,9 @@ var peersCmd = new Command("peers").description("list your sessions reachable fo
|
|
|
7573
7672
|
for (const p of data.peers) {
|
|
7574
7673
|
const dot = p.online ? "\u25CF" : "\u25CB";
|
|
7575
7674
|
const where = p.cwd ? ` ${p.cwd}` : "";
|
|
7576
|
-
|
|
7675
|
+
const qualifiedName = `@${p.ownerLogin}/${p.routingName.replace(/^@/, "")}`;
|
|
7676
|
+
const tag = p.mine ? "" : " (shared)";
|
|
7677
|
+
console.log(`${dot} ${qualifiedName.padEnd(28)} ${p.online ? "online " : "offline"}${tag}${where}`);
|
|
7577
7678
|
}
|
|
7578
7679
|
});
|
|
7579
7680
|
var routeCmd = new Command("route").description("send a one-shot prompt to a peer session; ask mode (default) blocks for the reply, tell mode is fire-and-forget").argument("<peer>", "routingName (e.g. @foo, foo, @owner/foo)").argument("<prompt>", "text to send (quote it to keep spaces)").option("--mode <mode>", "ask | tell", "ask").action(async (peer, prompt, opts) => {
|
|
@@ -7662,7 +7763,7 @@ var webhookCmd = new Command("webhook").description("manage webhook subscription
|
|
|
7662
7763
|
|
|
7663
7764
|
// src/index.ts
|
|
7664
7765
|
var program2 = new Command();
|
|
7665
|
-
program2.name("claw").description("clawborrator CLI \u2014 control your Claude Code sessions from the terminal").version("0.0.
|
|
7766
|
+
program2.name("claw").description("clawborrator CLI \u2014 control your Claude Code sessions from the terminal").version("0.0.24");
|
|
7666
7767
|
program2.addCommand(loginCmd);
|
|
7667
7768
|
program2.addCommand(logoutCmd);
|
|
7668
7769
|
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.24",
|
|
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",
|