@coderook/cli 0.42.0 → 0.42.1
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.1",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ACCA Gaming Productions",
|
|
8
8
|
"url": "https://coderook.com"
|
|
@@ -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.1",
|
|
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",
|