@coderook/cli 0.33.0 → 0.33.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, from Claude Code.",
5
- "version": "0.33.0",
5
+ "version": "0.33.1",
6
6
  "author": {
7
7
  "name": "ACCA Gaming Productions",
8
8
  "url": "https://coderook.com"
@@ -899,7 +899,7 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
899
899
  console.log("");
900
900
  }
901
901
  const line = progressLine();
902
- const uploader = new upload_js_1.Uploader(config_js_1.credentials);
902
+ let uploader = new upload_js_1.Uploader(config_js_1.credentials);
903
903
  /*
904
904
  Built by the shared description, so this and the git remote helper cannot
905
905
  drift. They already had: the helper listed deletions in `deletions` alone
@@ -1001,71 +1001,99 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
1001
1001
  }
1002
1002
  }
1003
1003
  let result;
1004
- try {
1005
- const plan = await uploader.plan(uploadRequest, (progress) => {
1006
- line(` ${track(progress.percent)} ${String(progress.percent).padStart(3)}% ` +
1007
- `${"plan".padEnd(7)} ${progress.files}/${progress.totalFiles} ` +
1008
- dim(progress.path.slice(-40)));
1009
- });
1010
- done(line);
1011
- console.log(`${accent("Upload plan")} ${bytes(plan.sourceBytes)} selected → ` +
1012
- `${bytes(plan.compactedBytes)} compacted; ${bytes(plan.chargeableBytes)} new storage.`);
1013
- console.log(plan.allowance.exempt
1014
- ? dim("Annual plan: the monthly staged upload allowance does not apply.")
1015
- : dim(`Monthly stage ${plan.allowance.stage}: ${plan.allowance.unlockedPercent}% unlocked, ` +
1016
- `${bytes(plan.allowance.remainingBytes)} remains after this reservation.`));
1017
- if (dryRun) {
1018
- await uploader.cancelPlan(plan, true);
1019
- const going = files.filter((file) => !file.deleted);
1020
- const removing = files.filter((file) => file.deleted);
1021
- console.log(`${going.length} file${going.length === 1 ? "" : "s"} would be sent or reused:`);
1022
- for (const file of going)
1023
- console.log(` ${file.path}`);
1024
- if (removing.length) {
1025
- console.log(`${removing.length} file${removing.length === 1 ? "" : "s"} would be removed from the save:`);
1026
- for (const file of removing)
1027
- console.log(` ${red("deleted")} ${file.path}`);
1004
+ /*
1005
+ How long to wait before picking a dropped upload up again by itself.
1006
+
1007
+ Each request is already retried for a few seconds, but a Wi-Fi drop or a
1008
+ router reconnecting outlasts that, and the upload ended with "run the same
1009
+ command again" for somebody to notice and act on. Running it again was
1010
+ always safe and cheap (pieces already stored are recognised and skipped,
1011
+ and the save itself is named so it can never be made twice), so the
1012
+ command now does it itself, a few times, before giving up.
1013
+ */
1014
+ const resumeAfter = [10, 30, 60];
1015
+ for (let resumed = 0;; resumed += 1) {
1016
+ try {
1017
+ const plan = await uploader.plan(uploadRequest, (progress) => {
1018
+ line(` ${track(progress.percent)} ${String(progress.percent).padStart(3)}% ` +
1019
+ `${"plan".padEnd(7)} ${progress.files}/${progress.totalFiles} ` +
1020
+ dim(progress.path.slice(-40)));
1021
+ });
1022
+ done(line);
1023
+ console.log(`${accent("Upload plan")} ${bytes(plan.sourceBytes)} selected → ` +
1024
+ `${bytes(plan.compactedBytes)} compacted; ${bytes(plan.chargeableBytes)} new storage.`);
1025
+ console.log(plan.allowance.exempt
1026
+ ? dim("Annual plan: the monthly staged upload allowance does not apply.")
1027
+ : dim(`Monthly stage ${plan.allowance.stage}: ${plan.allowance.unlockedPercent}% unlocked, ` +
1028
+ `${bytes(plan.allowance.remainingBytes)} remains after this reservation.`));
1029
+ if (dryRun) {
1030
+ await uploader.cancelPlan(plan, true);
1031
+ const going = files.filter((file) => !file.deleted);
1032
+ const removing = files.filter((file) => file.deleted);
1033
+ console.log(`${going.length} file${going.length === 1 ? "" : "s"} would be sent or reused:`);
1034
+ for (const file of going)
1035
+ console.log(` ${file.path}`);
1036
+ if (removing.length) {
1037
+ console.log(`${removing.length} file${removing.length === 1 ? "" : "s"} would be removed from the save:`);
1038
+ for (const file of removing)
1039
+ console.log(` ${red("deleted")} ${file.path}`);
1040
+ }
1041
+ noteKeptDeletions(folder, baseline, hasFlag(parsed, "sync"));
1042
+ console.log("Nothing was uploaded and no Version was created.");
1043
+ return 0;
1028
1044
  }
1029
- noteKeptDeletions(folder, baseline, hasFlag(parsed, "sync"));
1030
- console.log("Nothing was uploaded and no Version was created.");
1031
- return 0;
1032
- }
1033
- result = await uploader.execute(uploadRequest, plan, (progress) => {
1034
- line(` ${track(progress.percent)} ${String(progress.percent).padStart(3)}% ` +
1035
- `${progress.stage.padEnd(7)} ${progress.files}/${progress.totalFiles} ` +
1036
- dim(progress.path.slice(-40)));
1037
- });
1038
- }
1039
- catch (error) {
1040
- done(line);
1041
- /*
1042
- Classified by the shared description rather than by a private regular
1043
- expression, so a push and a submit disagree about nothing — including
1044
- what counts as "the connection failed" and what that means for whether
1045
- the work was saved.
1046
- */
1047
- const failure = (0, publish_js_1.classifyPublishFailure)(error);
1048
- const text = (0, network_js_1.describeError)(error);
1049
- if (failure?.kind === "interrupted") {
1050
- console.error(red(`
1051
- The connection failed: ${text}`));
1052
- console.error(`Your work may already have been saved. Run the same command again —` +
1053
- ` it will not create a second version.`);
1054
- return 1;
1045
+ result = await uploader.execute(uploadRequest, plan, (progress) => {
1046
+ line(` ${track(progress.percent)} ${String(progress.percent).padStart(3)}% ` +
1047
+ `${progress.stage.padEnd(7)} ${progress.files}/${progress.totalFiles} ` +
1048
+ dim(progress.path.slice(-40)));
1049
+ });
1050
+ break;
1055
1051
  }
1056
- if (failure?.kind === "conflict") {
1052
+ catch (error) {
1053
+ done(line);
1057
1054
  /*
1058
- Somebody else saved to this project first. Everything that did not
1059
- overlap has already been combined by the service; what is left is a
1060
- genuine decision, so the answer is to bring their work down and look
1061
- at it rather than to try again harder.
1055
+ Classified by the shared description rather than by a private regular
1056
+ expression, so a push and a submit disagree about nothing — including
1057
+ what counts as "the connection failed" and what that means for whether
1058
+ the work was saved.
1062
1059
  */
1063
- console.error(red(error instanceof Error ? error.message : String(error)));
1064
- console.error(`\nRun ${accent("cbx get")} to bring the latest version down, then` +
1065
- ` submit again. Nothing was changed on your account.`);
1066
- return 1;
1060
+ const failure = (0, publish_js_1.classifyPublishFailure)(error);
1061
+ const text = (0, network_js_1.describeError)(error);
1062
+ if (failure?.kind === "interrupted" && !dryRun && resumed < resumeAfter.length) {
1063
+ const wait = resumeAfter[resumed];
1064
+ console.error(dim(`The connection dropped (${text})`));
1065
+ console.error(dim(`Picking the upload up again in ${wait}s (try ${resumed + 2} of ${resumeAfter.length + 1}).` +
1066
+ ` Pieces already sent are kept; only the rest goes.`));
1067
+ /*
1068
+ The failed attempt's other lanes are still retrying; stopped first, so
1069
+ they do not carry on sending beside the attempt that replaces them.
1070
+ */
1071
+ uploader.cancel();
1072
+ await (0, network_js_1.pause)(wait * 1000);
1073
+ uploader = new upload_js_1.Uploader(config_js_1.credentials);
1074
+ continue;
1075
+ }
1076
+ if (failure?.kind === "interrupted") {
1077
+ console.error(red(`
1078
+ The connection failed: ${text}`));
1079
+ console.error(`Your work may already have been saved. Run the same command again —` +
1080
+ ` it will not create a second version.`);
1081
+ return 1;
1082
+ }
1083
+ if (failure?.kind === "conflict") {
1084
+ /*
1085
+ Somebody else saved to this project first. Everything that did not
1086
+ overlap has already been combined by the service; what is left is a
1087
+ genuine decision, so the answer is to bring their work down and look
1088
+ at it rather than to try again harder.
1089
+ */
1090
+ console.error(red(error instanceof Error ? error.message : String(error)));
1091
+ console.error(`\nRun ${accent("cbx get")} to bring the latest version down, then` +
1092
+ ` submit again. Nothing was changed on your account.`);
1093
+ return 1;
1094
+ }
1095
+ throw error;
1067
1096
  }
1068
- throw error;
1069
1097
  }
1070
1098
  done(line);
1071
1099
  if (result.mergeTrack) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coderook/cli",
3
- "version": "0.33.0",
3
+ "version": "0.33.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",