@coderook/cli 0.42.0 → 0.42.2
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.
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "coderook",
|
|
3
3
|
"displayName": "CodeRook",
|
|
4
4
|
"description": "Save, browse and restore whole-snapshot versions of a project on CodeRook, with cbx or plain git, from Claude Code.",
|
|
5
|
-
"version": "0.42.
|
|
5
|
+
"version": "0.42.2",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ACCA Gaming Productions",
|
|
8
8
|
"url": "https://coderook.com"
|
package/dist/cli/src/cli.js
CHANGED
|
@@ -103,9 +103,22 @@ function bytes(value) {
|
|
|
103
103
|
/** One line that rewrites itself, so progress does not scroll the terminal. */
|
|
104
104
|
function progressLine() {
|
|
105
105
|
let last = 0;
|
|
106
|
+
let lastPlain = 0;
|
|
106
107
|
return (text) => {
|
|
107
|
-
|
|
108
|
+
/*
|
|
109
|
+
Without a terminal (a script, a bot, a CI log) the redrawn bar cannot
|
|
110
|
+
work, and this used to print nothing at all: a 1.79 GB clone ran for
|
|
111
|
+
minutes in silence and was taken for a hang (2026-11-05). So a plain
|
|
112
|
+
line instead, at most every ten seconds.
|
|
113
|
+
*/
|
|
114
|
+
if (!node_process_1.default.stdout.isTTY) {
|
|
115
|
+
const now = Date.now();
|
|
116
|
+
if (now - lastPlain < 10_000)
|
|
117
|
+
return;
|
|
118
|
+
lastPlain = now;
|
|
119
|
+
node_process_1.default.stdout.write(`${text.replace(/\[[0-9;]*m/g, "").replace(/[█░▏▎▍▌▋▊▉]+/g, "").replace(/\s+/g, " ").trim()}\n`);
|
|
108
120
|
return;
|
|
121
|
+
}
|
|
109
122
|
/*
|
|
110
123
|
Measured without the colour codes. Padding to the raw length of a string
|
|
111
124
|
carrying escape sequences overshoots by however many invisible
|
|
@@ -145,6 +145,26 @@ function key() {
|
|
|
145
145
|
process.stdin.on("keypress", handler);
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
|
+
/*
|
|
149
|
+
A key, or nothing within `ms`. The views wait on this so they can look for
|
|
150
|
+
new messages while somebody is reading, rather than only when a key is
|
|
151
|
+
pressed: a thread that only updates on a keystroke reads as a dead one.
|
|
152
|
+
*/
|
|
153
|
+
function keyWithin(ms) {
|
|
154
|
+
return new Promise((resolve) => {
|
|
155
|
+
const handler = (sequence, info) => {
|
|
156
|
+
clearTimeout(timer);
|
|
157
|
+
process.stdin.off("keypress", handler);
|
|
158
|
+
resolve({ name: info?.name ?? sequence, sequence, ctrl: Boolean(info?.ctrl) });
|
|
159
|
+
};
|
|
160
|
+
const timer = setTimeout(() => {
|
|
161
|
+
process.stdin.off("keypress", handler);
|
|
162
|
+
resolve(null);
|
|
163
|
+
}, ms);
|
|
164
|
+
process.stdin.on("keypress", handler);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const REFRESH_MS = 5000;
|
|
148
168
|
function raw(on) {
|
|
149
169
|
if (process.stdin.isTTY)
|
|
150
170
|
process.stdin.setRawMode(on);
|
|
@@ -187,7 +207,19 @@ async function interactiveWorkspaces(parsed) {
|
|
|
187
207
|
});
|
|
188
208
|
console.log((0, colour_js_1.dim)(`\n${shown.length} open${filter ? ` matching "${filter}"` : ""} · ${listed.archived} archived (cbx workspace list <project> shows them)`));
|
|
189
209
|
raw(true);
|
|
190
|
-
|
|
210
|
+
/* Redrawn only when something on it changed: new messages, or a workspace opened or closed. */
|
|
211
|
+
const drawn = JSON.stringify(listed.workspaces.map((w) => [w.id, w.unread, w.activeAt]));
|
|
212
|
+
let pressed = null;
|
|
213
|
+
while (!pressed) {
|
|
214
|
+
pressed = await keyWithin(REFRESH_MS);
|
|
215
|
+
if (pressed)
|
|
216
|
+
break;
|
|
217
|
+
const now = await (0, api_js_1.callApi)(`/v1/account/workspace-threads${join(asQuery(name))}`).catch(() => null);
|
|
218
|
+
if (now && JSON.stringify(now.workspaces.map((w) => [w.id, w.unread, w.activeAt])) !== drawn)
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
if (!pressed)
|
|
222
|
+
continue;
|
|
191
223
|
if (pressed.ctrl && pressed.name === "c")
|
|
192
224
|
return 0;
|
|
193
225
|
if (pressed.name === "q" || pressed.name === "escape")
|
|
@@ -217,7 +249,12 @@ async function openWorkspace(listed, name) {
|
|
|
217
249
|
let notice = "";
|
|
218
250
|
for (;;) {
|
|
219
251
|
const shown = await (0, api_js_1.callApi)(root);
|
|
220
|
-
|
|
252
|
+
/* Every page, oldest first, so the newest messages are the ones shown (a page holds 50). */
|
|
253
|
+
let thread = await (0, api_js_1.callApi)(`${root}/messages${join(asQuery(name))}`);
|
|
254
|
+
for (let more = 0; thread.next && more < 20; more += 1) {
|
|
255
|
+
const page = await (0, api_js_1.callApi)(`${root}/messages${join(asQuery(name), `after=${encodeURIComponent(thread.next)}`)}`);
|
|
256
|
+
thread = { ...page, messages: [...thread.messages, ...page.messages] };
|
|
257
|
+
}
|
|
221
258
|
const w = shown.workspace;
|
|
222
259
|
clear();
|
|
223
260
|
console.log(`${(0, colour_js_1.bold)(w.name)} ${(0, colour_js_1.dim)(`${listed.project} · ${w.track} · revision ${w.revision}${w.archived ? " · archived" : ""}`)}`);
|
|
@@ -240,7 +277,19 @@ async function openWorkspace(listed, name) {
|
|
|
240
277
|
console.log(`\n${notice}`);
|
|
241
278
|
notice = "";
|
|
242
279
|
raw(true);
|
|
243
|
-
|
|
280
|
+
/* New messages appear on their own: a light check every few seconds, redrawn only when there is something new. */
|
|
281
|
+
const newestAt = thread.messages[thread.messages.length - 1]?.createdAt ?? null;
|
|
282
|
+
let pressed = null;
|
|
283
|
+
while (!pressed) {
|
|
284
|
+
pressed = await keyWithin(REFRESH_MS);
|
|
285
|
+
if (pressed)
|
|
286
|
+
break;
|
|
287
|
+
const peek = await (0, api_js_1.callApi)(`${root}/messages${join(asQuery(name), "peek=true", newestAt ? `after=${encodeURIComponent(newestAt)}` : "")}`).catch(() => null);
|
|
288
|
+
if (peek && peek.messages.length)
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
if (!pressed)
|
|
292
|
+
continue;
|
|
244
293
|
if (pressed.ctrl && pressed.name === "c")
|
|
245
294
|
return true;
|
|
246
295
|
if (pressed.name === "q" || pressed.name === "escape")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderook/cli",
|
|
3
|
-
"version": "0.42.
|
|
3
|
+
"version": "0.42.2",
|
|
4
4
|
"description": "The command line for CodeRook (coderook.com): version control and project hosting for projects of any size. Every save is a compressed snapshot, private or public, never used to train AI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://coderook.com",
|