instabox 0.8.5 → 0.8.7

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.
package/cli/SHA256SUMS CHANGED
@@ -1,6 +1,6 @@
1
- bb0a348729f8f2c8cda23b77293e8969ddaf83670e28a6bdf7b3ec0c3fb96a9c instabox-darwin-amd64
2
- fdd071db9d874768f2d5224ae96c734364d7405d80388e7cb667b158953a6172 instabox-darwin-arm64
3
- 9c24a0c66325038f0ad48bbcf1d9ed2549bb240c05d949dc147cdc79540bdd66 instabox-linux-amd64
4
- ce9c867bd3704513c71d3f4c48f0aa5619dd7e0bbc3059c28e744850303709ea instabox-linux-arm64
5
- 7fa9be21247482f1b2a757d353f9ca7aa99dadd03ffc8ad5e6ade6cd393ca551 instabox-windows-amd64.exe
6
- f8cc9f6cbe9c8a9f6f7899188651f135d3f52d25d805ddca3b009a393c5c6443 instabox-windows-arm64.exe
1
+ 27fa581ad33ebabce3229045ebb12d9a760be0ccd363139a57b580d7aa3dac0a instabox-darwin-amd64
2
+ 0c5620128fa44dbda291bdafbfa2178b909d8457a5ad0751a428044e6adc25e5 instabox-darwin-arm64
3
+ c47e1086ee1e532340b95ff116cc58b31e8a1bddb8ab7e7aaf76454e6f8d5ab5 instabox-linux-amd64
4
+ 32c8096ecb7a782f2a51341b1b75b65b305c773597982976fb1888215fe773b0 instabox-linux-arm64
5
+ 940bed7319b187ff873a939d25738e02e44f5f5e71948cf901be5f5d05e0295e instabox-windows-amd64.exe
6
+ 8a6ec5b8daf403a969dc89d87f4180f276cdfbeee335c8469f466fdade9fec61 instabox-windows-arm64.exe
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instabox",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "A cloud box for your coding agent, the instabox CLI and its agent skills in one command: npx instabox@latest",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,6 +38,7 @@ Before starting, tell the user plainly:
38
38
  Run with **bash**, anywhere inside the repo or one of its worktrees:
39
39
 
40
40
  ```bash
41
+ export MSYS2_ARG_CONV_EXCL=/data/ # Git Bash on Windows: keep box paths as they are
41
42
  instabox whoami # signed in? otherwise: instabox login
42
43
  S=~/.claude/skills/instabox-move-repo/scripts # or ~/.agents/skills/instabox-move-repo/scripts: wherever this file is
43
44
  W=$(mktemp -d)
@@ -58,7 +59,7 @@ Show the user the plan in short: the worktrees with their sizes and state, the t
58
59
  SKIP=() # one --skip-worktree per worktree the user unchecked: SKIP+=(--skip-worktree /path/to/it)
59
60
  KEEP=() # one --keep per excluded name the user wants copied anyway: KEEP+=(--keep build)
60
61
  node "$S/repo-files.mjs" "${SKIP[@]}" "${KEEP[@]}" --json > "$W/plan.json"
61
- DEST=$(jq -r .main.box "$W/plan.json")
62
+ DEST=$(jq -r .main.box "$W/plan.json" | tr -d '\r') # tr: Windows jq.exe ends lines in CRLF
62
63
  jq -r '.totals' "$W/plan.json" # sendBytes must fit well inside /data: instabox usage
63
64
  ```
64
65
 
@@ -81,7 +82,7 @@ The main checkout first (it carries `.git`), then each worktree, each as its own
81
82
  ```bash
82
83
  export COPYFILE_DISABLE=1 # macOS tar: no ._ AppleDouble files
83
84
  X='tar -xzf - --warning=no-unknown-keyword' # the box runs GNU tar
84
- jq -r '.main, (.worktrees[] | select(.move)) | [.src, .box] | @tsv' "$W/plan.json" |
85
+ jq -r '.main, (.worktrees[] | select(.move)) | [.src, .box] | @tsv' "$W/plan.json" | tr -d '\r' |
85
86
  while IFS=$'\t' read -r src box; do
86
87
  echo "== $src -> $box"
87
88
  node "$S/repo-files.mjs" --list0 "$src" "${SKIP[@]}" "${KEEP[@]}" </dev/null |
@@ -93,7 +94,7 @@ done
93
94
  Then point the worktrees at their new places (each worktree's `.git` file and `.git/worktrees/<id>/gitdir` still name the local paths; `git worktree repair` with the new paths rewrites both sides):
94
95
 
95
96
  ```bash
96
- WTS=$(jq -r '[.worktrees[] | select(.move) | .box] | join(" ")' "$W/plan.json")
97
+ WTS=$(jq -r '[.worktrees[] | select(.move) | .box] | join(" ")' "$W/plan.json" | tr -d '\r')
97
98
  [ -z "$WTS" ] || instabox ssh "$BOX" -- "cd '$DEST' && git worktree repair $WTS"
98
99
  ```
99
100
 
@@ -102,7 +103,7 @@ Box paths contain only letters, digits and `._-/`, so they are safe unquoted the
102
103
  Check that every checkout has the same state, and the same worktrees:
103
104
 
104
105
  ```bash
105
- jq -r '.main, (.worktrees[] | select(.move)) | [.src, .box] | @tsv' "$W/plan.json" |
106
+ jq -r '.main, (.worktrees[] | select(.move)) | [.src, .box] | @tsv' "$W/plan.json" | tr -d '\r' |
106
107
  while IFS=$'\t' read -r src box; do
107
108
  echo "== $box"
108
109
  diff <(cd "$src" && { git status --porcelain | sort; git rev-parse HEAD; }) \
@@ -119,7 +120,7 @@ The only allowed differences are `<` lines starting with `??` for what the plan
119
120
  H=$(mktemp -d)
120
121
  node "$S/history.mjs" --plan "$W/plan.json" --out "$H" > "$W/history.json" # --no-claude / --no-codex to skip one
121
122
  jq '{claude: .claude | {sessions, bytes, recent}, codex: .codex | {sessions, bytes, recent}}' "$W/history.json" # null: skipped
122
- tar -C "$H" -czf - $(jq -r '.entries[]' "$W/history.json") | instabox ssh "$BOX" -- "$X --no-overwrite-dir -C ~"
123
+ tar -C "$H" -czf - $(jq -r '.entries[]' "$W/history.json" | tr -d '\r') | instabox ssh "$BOX" -- "$X --no-overwrite-dir -C ~"
123
124
  instabox ssh "$BOX" -- 'bash ~/.cache/instabox-move-repo/apply-history.sh'
124
125
  rm -rf "$H"
125
126
  ```
@@ -21,7 +21,7 @@
21
21
  // difference is `??` lines for what was left out. File contents are never read, only names and sizes.
22
22
  import { spawnSync } from 'node:child_process';
23
23
  import { lstatSync, readdirSync, readFileSync, realpathSync } from 'node:fs';
24
- import { basename, dirname, join, relative, resolve } from 'node:path';
24
+ import { basename, dirname, join, relative, resolve, sep } from 'node:path';
25
25
  import { fileURLToPath } from 'node:url';
26
26
 
27
27
  // Directory names that hold build output, dependencies or caches: regenerated on the box.
@@ -161,10 +161,10 @@ function treeBytes(p, skip = new Set()) {
161
161
  return { bytes, files };
162
162
  }
163
163
 
164
- // Every path under dir (directories too, for tar --no-recursion), relative to base, sorted.
165
- function walk(dir, base, skip, out = []) {
164
+ // Archive names are joined with '/' whatever the host separator.
165
+ function walk(dir, rel, skip, out = []) {
166
166
  if (skip.has(dir)) return out;
167
- out.push(relative(base, dir));
167
+ out.push(rel);
168
168
  let names = [];
169
169
  try {
170
170
  names = readdirSync(dir).sort();
@@ -173,8 +173,8 @@ function walk(dir, base, skip, out = []) {
173
173
  const p = join(dir, n);
174
174
  const st = lstat(p);
175
175
  if (!st || skip.has(p)) continue;
176
- if (st.isDirectory()) walk(p, base, skip, out);
177
- else out.push(relative(base, p));
176
+ if (st.isDirectory()) walk(p, `${rel}/${n}`, skip, out);
177
+ else out.push(`${rel}/${n}`);
178
178
  }
179
179
  return out;
180
180
  }
@@ -217,7 +217,8 @@ const real = (p) => {
217
217
  return resolve(p);
218
218
  }
219
219
  };
220
- const inside = (p, dir) => p === dir || p.startsWith(dir + '/');
220
+ // s: sep for native paths, '/' for git's.
221
+ const inside = (p, dir, s = sep) => p === dir || p.startsWith(dir + s);
221
222
 
222
223
  // `git worktree list --porcelain -z`, parsed. The first entry is the main checkout.
223
224
  export function listWorktrees(cwd) {
@@ -258,14 +259,14 @@ function checkoutFiles(top, { keep, nested = [] }) {
258
259
  }
259
260
  const trackedFiles = files.length;
260
261
  const tracked = new Set(files);
261
- const nestedRel = nested.map((p) => relative(top, p));
262
+ const nestedRel = nested.map((p) => relative(top, p).split(sep).join('/'));
262
263
 
263
264
  let untrackedBytes = 0;
264
265
  let untrackedFiles = 0;
265
266
  const groups = new Map();
266
267
  for (const f of split0(git(['ls-files', '-z', '--others', '--exclude-standard'], top))) {
267
268
  const bare = f.replace(/\/$/, '');
268
- if (tracked.has(f) || nestedRel.some((n) => inside(bare, n))) continue;
269
+ if (tracked.has(f) || nestedRel.some((n) => inside(bare, n, '/'))) continue;
269
270
  const st = lstat(join(top, bare));
270
271
  if (!st) continue;
271
272
  const isDir = st.isDirectory();
@@ -286,7 +287,7 @@ function checkoutFiles(top, { keep, nested = [] }) {
286
287
  // .env files git ignores (names only): they stay behind too, and the user should know.
287
288
  const ignoredEnv = split0(git(['ls-files', '-z', '--others', '--ignored', '--exclude-standard', '--directory'], top))
288
289
  .filter((f) => !f.endsWith('/') && isEnvName(f))
289
- .filter((f) => !nestedRel.some((n) => inside(f, n)));
290
+ .filter((f) => !nestedRel.some((n) => inside(f, n, '/')));
290
291
  const left = [...groups.entries()]
291
292
  .map(([path, g]) => ({ path, ...g }))
292
293
  .sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path));
@@ -377,7 +378,7 @@ export function planRepo(cwd = process.cwd(), { dest, keep = [], skip = [], lite
377
378
  }
378
379
  // Box paths are safe in a single-quoted shell word: [A-Za-z0-9._-/] only.
379
380
  let box = inside(src, mainSrc)
380
- ? `${dest}/${relative(mainSrc, src).split('/').map(safeName).join('/')}`
381
+ ? `${dest}/${relative(mainSrc, src).split(sep).map(safeName).join('/')}`
381
382
  : `${dest}.worktrees/${safeName(basename(src))}`;
382
383
  for (let i = 2, b = box; used.has(box); i++) box = `${b}-${i}`;
383
384
  entry.box = box;
@@ -456,7 +457,7 @@ export function listFor(path, { keep = [], skip = [] } = {}) {
456
457
  if (!isMain) return ['.git', ...files];
457
458
  // The admin dirs of worktrees that stay behind (unchecked, prunable, missing) stay too.
458
459
  const skipAdmin = new Set(plan.worktrees.filter((x) => !x.move && x.adminDir).map((x) => x.adminDir));
459
- return [...walk(real(join(top, '.git')), top, skipAdmin), ...files];
460
+ return [...walk(real(join(top, '.git')), '.git', skipAdmin), ...files];
460
461
  }
461
462
 
462
463
  function describe(w) {