@coderook/cli 0.31.11 → 0.31.12

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, from Claude Code.",
5
- "version": "0.31.11",
5
+ "version": "0.31.12",
6
6
  "author": {
7
7
  "name": "ACCA Gaming Productions",
8
8
  "url": "https://coderook.com"
@@ -7,6 +7,7 @@ exports.completeUpload = completeUpload;
7
7
  exports.attachToVersion = attachToVersion;
8
8
  exports.whoami = whoami;
9
9
  exports.projects = projects;
10
+ exports.projectById = projectById;
10
11
  exports.findPublicProject = findPublicProject;
11
12
  exports.findProject = findProject;
12
13
  exports.health = health;
@@ -184,6 +185,37 @@ async function projects() {
184
185
  updatedAt: String(row.updatedAt ?? row.createdAt ?? ""),
185
186
  }));
186
187
  }
188
+ /**
189
+ * One project by its id, including somebody else's public or unlisted one.
190
+ *
191
+ * The account's own list holds only projects it is in, so a folder cloned
192
+ * from somebody else's public project had no way back to it: every command
193
+ * after the clone said the project was "no longer on your account". The
194
+ * service answers this route for anybody signed in when the project is open,
195
+ * and says whether the caller is one of its people.
196
+ */
197
+ async function projectById(repositoryId) {
198
+ try {
199
+ const row = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}`);
200
+ if (!row.id)
201
+ return null;
202
+ return {
203
+ id: String(row.id),
204
+ slug: String(row.slug ?? ""),
205
+ name: String(row.displayName || row.slug || "Untitled"),
206
+ visibility: String(row.visibility ?? "public"),
207
+ defaultBranch: String(row.defaultBranch || "main"),
208
+ versionCount: Number(row.versionCount ?? 0),
209
+ fileCount: Number(row.fileCount ?? 0),
210
+ storedBytes: Number(row.storedSize ?? 0),
211
+ updatedAt: String(row.updatedAt ?? row.createdAt ?? ""),
212
+ member: row.member !== false,
213
+ };
214
+ }
215
+ catch {
216
+ return null;
217
+ }
218
+ }
187
219
  /**
188
220
  * One public project, by the two names that identify it.
189
221
  *
@@ -195,9 +195,23 @@ const folderFor = (parsed, index = 0) => node_path_1.default.resolve(parsed.posi
195
195
  async function reconcile(folder, options = {}) {
196
196
  const link = await (0, config_js_1.readLink)(folder);
197
197
  const reference = link?.slug ?? node_path_1.default.basename(folder);
198
- const project = link
198
+ let member = true;
199
+ let project = link
199
200
  ? (await (0, api_js_2.projects)()).find((candidate) => candidate.id === link.repositoryId)
200
201
  : await (0, api_js_2.findProject)(reference);
202
+ /*
203
+ Not on the account's own list, which holds only projects it is in. A
204
+ folder cloned from somebody else's public project is exactly that, and
205
+ it was told the project had gone. Asked by id instead, the service still
206
+ answers for an open project and says the caller is a visitor.
207
+ */
208
+ if (link && !project) {
209
+ const visited = await (0, api_js_2.projectById)(link.repositoryId);
210
+ if (visited) {
211
+ project = visited;
212
+ member = visited.member;
213
+ }
214
+ }
201
215
  /*
202
216
  A link that has outlived the project it points at.
203
217
 
@@ -217,12 +231,12 @@ async function reconcile(folder, options = {}) {
217
231
  }
218
232
  if (!project?.versionCount) {
219
233
  // Nothing saved on the account, so everything here is outstanding.
220
- return { link: link ?? null, baseline: null, behind: null };
234
+ return { link: link ?? null, baseline: null, behind: null, member };
221
235
  }
222
236
  const downloader = new download_js_1.Downloader(config_js_1.credentials);
223
237
  const latest = (await downloader.versions(project.id))[0];
224
238
  if (!latest)
225
- return { link: link ?? null, baseline: null, behind: null };
239
+ return { link: link ?? null, baseline: null, behind: null, member };
226
240
  /*
227
241
  A folder that already knows which version it stands on keeps it. Only a
228
242
  folder that has never been reconciled — or was linked before versions
@@ -250,6 +264,7 @@ async function reconcile(folder, options = {}) {
250
264
  */
251
265
  baseline: new Map(Object.entries(known.local ?? known.manifest)),
252
266
  behind,
267
+ member,
253
268
  };
254
269
  }
255
270
  const baseline = new Map();
@@ -271,7 +286,7 @@ async function reconcile(folder, options = {}) {
271
286
  if (!options.quiet && !link) {
272
287
  console.log(dim(`Linked this folder to ${project.slug}.`));
273
288
  }
274
- return { link: fresh, baseline, behind: null };
289
+ return { link: fresh, baseline, behind: null, member };
275
290
  }
276
291
  // ── commands ────────────────────────────────────────────────────────────────
277
292
  async function commandSignIn(parsed) {
@@ -585,7 +600,19 @@ async function commandImport(parsed) {
585
600
  async function commandSubmit(parsed) {
586
601
  const folder = folderFor(parsed);
587
602
  const message = flagText(parsed, "m", "message") ?? "";
588
- const { link, baseline, behind } = await reconcile(folder);
603
+ const { link, baseline, behind, member } = await reconcile(folder);
604
+ /*
605
+ Somebody else's project, cloned from its public page. Reading and
606
+ fetching are fine; saving into it is not this account's to do, and the
607
+ service would refuse it anyway, less clearly.
608
+ */
609
+ if (link && !member) {
610
+ console.error(red(`Access denied: you are not a collaborator on ${link.slug}.`) +
611
+ "\n" +
612
+ dim(` Ask its owner to invite you, or run ${accent("cbx unlink")} to start a project of ` +
613
+ "your own from these files."));
614
+ return 1;
615
+ }
589
616
  /*
590
617
  A first save gets a licence unless it is turned down.
591
618
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coderook/cli",
3
- "version": "0.31.11",
3
+ "version": "0.31.12",
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": "SEE LICENSE IN LICENSE.txt",
6
6
  "homepage": "https://coderook.com",