cortex-sync 0.4.12 → 0.4.16

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.
Files changed (2) hide show
  1. package/dist/cli.js +37 -5
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -321,7 +321,8 @@ var GitHubBackend = class {
321
321
  apiBase;
322
322
  headers;
323
323
  url(path) {
324
- return `${this.apiBase}/contents/${path}`;
324
+ const encoded = path.split("/").map(encodeURIComponent).join("/");
325
+ return `${this.apiBase}/contents/${encoded}`;
325
326
  }
326
327
  async getSha(path) {
327
328
  const res = await fetch(this.url(path), { headers: this.headers });
@@ -927,6 +928,7 @@ async function pullCommand(opts = {}) {
927
928
  if (mappingsDirty) await saveMappings(mappings);
928
929
  }
929
930
  async function* gen() {
931
+ let downloaded = 0;
930
932
  for (const path of toPull) {
931
933
  const blob = await backend.read("files/" + path);
932
934
  const content = decrypt(blob, derived);
@@ -950,11 +952,16 @@ async function pullCommand(opts = {}) {
950
952
  if (newEncodedDir !== oldEncodedDir) {
951
953
  relativePath = `projects/${newEncodedDir}/${m[3]}`;
952
954
  }
955
+ downloaded++;
956
+ process.stdout.write(`\r Downloading\u2026 ${downloaded}/${toPull.length} files`);
953
957
  yield { relativePath, content: remappedContent };
954
958
  } else {
959
+ downloaded++;
960
+ process.stdout.write(`\r Downloading\u2026 ${downloaded}/${toPull.length} files`);
955
961
  yield { relativePath: path, content };
956
962
  }
957
963
  }
964
+ if (toPull.length > 0) process.stdout.write("\n");
958
965
  }
959
966
  await adapter.putFiles(gen());
960
967
  await saveManifest(MANIFEST_PATH, remote);
@@ -1064,6 +1071,11 @@ function identifyProject(dir) {
1064
1071
  }
1065
1072
 
1066
1073
  // src/commands/sync.ts
1074
+ function isSafeGitHubPath(path) {
1075
+ return path.split("/").every(
1076
+ (c) => c.length > 0 && c !== "." && c !== ".." && c !== ".git" && !/[\x00-\x1f\x7f]/.test(c)
1077
+ );
1078
+ }
1067
1079
  async function syncCommand(opts = {}) {
1068
1080
  const config = await loadConfig();
1069
1081
  const passphrase = await readPassphrase();
@@ -1072,7 +1084,7 @@ async function syncCommand(opts = {}) {
1072
1084
  const backend = resolveBackend(config, { target: opts.target });
1073
1085
  console.log(`Sync target: ${backend.name}${opts.target ? ` (${opts.target})` : ""}
1074
1086
  `);
1075
- console.log("Reading local files\u2026");
1087
+ process.stdout.write("Reading local files\u2026");
1076
1088
  const local = emptyManifest("claude-code");
1077
1089
  const contents = /* @__PURE__ */ new Map();
1078
1090
  for await (const f of adapter.getFiles()) {
@@ -1082,8 +1094,10 @@ async function syncCommand(opts = {}) {
1082
1094
  size: f.content.length,
1083
1095
  encryptedSize: 0
1084
1096
  };
1097
+ process.stdout.write(`\r Reading\u2026 ${contents.size} files`);
1085
1098
  }
1086
- console.log(` ${contents.size} files`);
1099
+ process.stdout.write(`\r ${contents.size} files read${" ".repeat(20)}
1100
+ `);
1087
1101
  local.projects = {};
1088
1102
  const seenDirs = /* @__PURE__ */ new Set();
1089
1103
  for (const [path, content] of contents) {
@@ -1122,11 +1136,29 @@ async function syncCommand(opts = {}) {
1122
1136
  console.log(
1123
1137
  `Diff \u2014 added: ${diff.added.length}, modified: ${diff.modified.length}, removed: ${diff.removed.length}, unchanged: ${diff.unchanged.length}`
1124
1138
  );
1125
- for (const path of [...diff.added, ...diff.modified]) {
1139
+ const toUpload = [...diff.added, ...diff.modified];
1140
+ let uploaded = 0;
1141
+ let skipped = 0;
1142
+ for (const path of toUpload) {
1143
+ if (!isSafeGitHubPath(path)) {
1144
+ skipped++;
1145
+ continue;
1146
+ }
1126
1147
  const content = contents.get(path);
1127
1148
  const enc = encrypt(content, derived);
1128
1149
  local.files[path].encryptedSize = enc.length;
1129
- await backend.write("files/" + path, enc);
1150
+ try {
1151
+ await backend.write("files/" + path, enc);
1152
+ } catch (e) {
1153
+ process.stdout.write("\n");
1154
+ throw new Error(`Upload failed for "${path}": ${e.message}`);
1155
+ }
1156
+ uploaded++;
1157
+ process.stdout.write(`\r Uploading\u2026 ${uploaded}/${toUpload.length - skipped} files`);
1158
+ }
1159
+ if (toUpload.length > 0) process.stdout.write("\n");
1160
+ if (skipped > 0) {
1161
+ console.warn(` \u26A0 Skipped ${skipped} file(s) with paths incompatible with GitHub (control chars, .git, etc.)`);
1130
1162
  }
1131
1163
  for (const path of diff.removed) {
1132
1164
  await backend.remove("files/" + path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortex-sync",
3
- "version": "0.4.12",
3
+ "version": "0.4.16",
4
4
  "description": "Sync Claude Code sessions between machines with automatic path remapping and skill conversion",
5
5
  "license": "AGPL-3.0",
6
6
  "type": "module",