@coderook/cli 0.42.4 → 0.42.6
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.6",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ACCA Gaming Productions",
|
|
8
8
|
"url": "https://coderook.com"
|
package/dist/cli/src/cli.js
CHANGED
|
@@ -1541,12 +1541,26 @@ async function commandGet(parsed) {
|
|
|
1541
1541
|
would bury the only copy.
|
|
1542
1542
|
*/
|
|
1543
1543
|
if (resuming) {
|
|
1544
|
+
/*
|
|
1545
|
+
Only a file this fetch rewrites can be buried by finishing it: one
|
|
1546
|
+
whose incoming copy differs from what was placed here. An edit to a
|
|
1547
|
+
file the version left alone is kept, exactly as an ordinary fetch
|
|
1548
|
+
keeps it, so it is no reason to stop. It used to be: completing in
|
|
1549
|
+
"replace" mode rewrote every file, so any edit at all, anywhere,
|
|
1550
|
+
refused the resume (2026-11-06, two page edits blocked a catch-up).
|
|
1551
|
+
*/
|
|
1544
1552
|
const notYetArrived = await (0, worktree_js_1.changedFiles)(folder, rules, incoming);
|
|
1545
1553
|
const changedInTheGap = notYetArrived.filter((file) => !file.deleted &&
|
|
1546
1554
|
incoming.has(file.path) &&
|
|
1555
|
+
incoming.get(file.path) !== link.local[file.path] &&
|
|
1547
1556
|
edited.some((one) => one.path === file.path));
|
|
1548
1557
|
if (!changedInTheGap.length) {
|
|
1549
|
-
|
|
1558
|
+
/*
|
|
1559
|
+
Reconciling finishes the job: every file the version changed is
|
|
1560
|
+
placed (the half the interrupted run already placed are placed
|
|
1561
|
+
again, harmlessly), and what it left alone stays as it is.
|
|
1562
|
+
*/
|
|
1563
|
+
return fetchInto(link.repositoryId, folder, link.slug, link.local ?? null, "reconcile", await (0, track_commands_js_1.trackFor)(folder));
|
|
1550
1564
|
}
|
|
1551
1565
|
console.error(red(`${changedInTheGap.length} file${changedInTheGap.length === 1 ? "" : "s"} ` +
|
|
1552
1566
|
`changed while the interrupted fetch was stopped:`));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.commandWorkspace = commandWorkspace;
|
|
4
|
+
exports.deployLanded = deployLanded;
|
|
4
5
|
exports.commandWorkspaceMcp = commandWorkspaceMcp;
|
|
5
6
|
const promises_1 = require("node:fs/promises");
|
|
6
7
|
const node_readline_1 = require("node:readline");
|
|
@@ -154,13 +155,80 @@ async function commandWorkspace(parsed) {
|
|
|
154
155
|
else if (operation !== "show")
|
|
155
156
|
throw new Error(`Unknown workspace operation ${operation}`);
|
|
156
157
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
let result;
|
|
159
|
+
try {
|
|
160
|
+
result = await (0, api_js_1.callApi)(path, {
|
|
161
|
+
method,
|
|
162
|
+
...(body === undefined ? {} : { body }),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
/*
|
|
167
|
+
Saves land one at a time, each on top of whatever landed before, so the
|
|
168
|
+
version a save made can differ from the draft that was checked: other
|
|
169
|
+
people's work landed in between. That version is the one to check and
|
|
170
|
+
ship, so check it where it is and deploy it, rather than refusing.
|
|
171
|
+
*/
|
|
172
|
+
if (operation === "deploy" && error instanceof api_js_1.ServiceError && error.code === "workspace_saved_content_changed") {
|
|
173
|
+
result = await deployLanded(root, body);
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
161
178
|
console.log(JSON.stringify(result, null, 2));
|
|
162
179
|
return 0;
|
|
163
180
|
}
|
|
181
|
+
const say = (line) => process.stderr.write(line + "\n");
|
|
182
|
+
const FINISHED = new Set(["passed", "failed", "cancelled", "canceled", "timed_out", "error", "skipped"]);
|
|
183
|
+
/** Check a saved version where it landed, in an unedited draft on it, then deploy it. */
|
|
184
|
+
async function deployLanded(root, body, options = {}) {
|
|
185
|
+
const api = options.api ?? api_js_1.callApi;
|
|
186
|
+
const wait = options.wait ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
187
|
+
const saved = String(body.savedVersionId ?? "");
|
|
188
|
+
const recipe = String(body.recipe ?? "");
|
|
189
|
+
say(`That version merged onto the line after this draft was checked. Checking the version itself, then deploying it.`);
|
|
190
|
+
const opened = await api(`${root}/workspaces`, {
|
|
191
|
+
method: "POST",
|
|
192
|
+
body: { name: `Deploy ${saved.slice(0, 8)} (${recipe})`, operationKey: `deploy-landed-${saved}-${recipe}`, baseVersionId: saved },
|
|
193
|
+
});
|
|
194
|
+
const ws = `${root}/workspaces/${opened.workspace.id}`;
|
|
195
|
+
if (opened.workspace.revision !== 0)
|
|
196
|
+
throw new Error(`Draft ${opened.workspace.id} was edited; open a fresh one on the saved version.`);
|
|
197
|
+
const shown = await api(ws);
|
|
198
|
+
const required = shown.recipes?.find((r) => r.name === recipe)?.required_recipes ?? [];
|
|
199
|
+
const deadline = Date.now() + (options.deadlineMs ?? 45 * 60_000);
|
|
200
|
+
for (const name of required) {
|
|
201
|
+
const started = await api(`${ws}/runs`, {
|
|
202
|
+
method: "POST",
|
|
203
|
+
body: { expectedRevision: 0, operationKey: `check-${saved}-${name}`, recipe: name },
|
|
204
|
+
});
|
|
205
|
+
say(` ${name}: run #${started.number ?? started.runId} on the saved version…`);
|
|
206
|
+
for (;;) {
|
|
207
|
+
/* The job endpoint answers { run: { status, number, … }, logs, artifacts }. */
|
|
208
|
+
const answer = await api(`${ws}/runs/${started.runId}`);
|
|
209
|
+
const run = answer.run ?? { status: "unknown" };
|
|
210
|
+
if (run.status === "passed") {
|
|
211
|
+
say(` ${name}: passed`);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
if (FINISHED.has(run.status) || run.status === "unknown")
|
|
215
|
+
throw new Error(`${name} ${run.status} on the saved version (run #${run.number ?? started.runId}); not deploying.`);
|
|
216
|
+
if (Date.now() > deadline)
|
|
217
|
+
throw new Error(`${name} has not finished; deploy again later with the same command.`);
|
|
218
|
+
await wait(15_000);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return api(`${ws}/runs`, {
|
|
222
|
+
method: "POST",
|
|
223
|
+
body: {
|
|
224
|
+
expectedRevision: 0,
|
|
225
|
+
operationKey: `${String(body.operationKey ?? "deploy")}-landed`,
|
|
226
|
+
recipe,
|
|
227
|
+
target: body.target,
|
|
228
|
+
savedVersionId: saved,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
164
232
|
/** Explicit remote/write-capable alternative; the original cbx mcp stays read-only. */
|
|
165
233
|
async function commandWorkspaceMcp() {
|
|
166
234
|
const lines = (0, node_readline_1.createInterface)({ input: process.stdin, crlfDelay: Infinity });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderook/cli",
|
|
3
|
-
"version": "0.42.
|
|
3
|
+
"version": "0.42.6",
|
|
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",
|