instabox 0.8.6 → 0.8.8

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
- 8e1a441e30f6fc303e34894cac5e605aa6a5584237016ff16afdf048c0633edd instabox-darwin-amd64
2
- cd76819b5dd37ed75443e3b2edfa22562e05b2668fac55f1e2bbe5363d4769d5 instabox-darwin-arm64
3
- 96629ac77edac211123cc3797078a9bbadad456dc7f40974bbab7ff9912b8dc8 instabox-linux-amd64
4
- 1fa96b63ff89633bc05020fc076730064df3a08952f0c208d7e459a4459dd9a3 instabox-linux-arm64
5
- 3fdc46ad1ab72885c4dc074297d868f15b69c9f3d364c77b2bd47c2a0478851a instabox-windows-amd64.exe
6
- b61fb3956c7f6c7962650e0ef7ecc9b7183d6be015f0b6ef787da43def5794c9 instabox-windows-arm64.exe
1
+ 68aed6ff418946b2fabdbe2b2404191d6d2caef666ea22fc938d75067cfe4c06 instabox-darwin-amd64
2
+ 373cb6dd024d07ab2aeac6665cc9a187bfc3bb3e05bdfabe3b6ffd85e28ba69c instabox-darwin-arm64
3
+ 54f4408ce12596e52d953fb9638cfd6d90dd88ac89a6f4da833e6f02cf3df131 instabox-linux-amd64
4
+ cba38617e9f98bccff21f5bf2e9bf05d7259c9d1b27c090eb76935515b8f2fa8 instabox-linux-arm64
5
+ edbf876282c1901e798d77297ca8b43465a8bdacf64b16f2e66ab2fc6efb2c0d instabox-windows-amd64.exe
6
+ b04f7dbf884bab7a0e817fd7f5eb9fc5becc1a17fde61278eff38c8c4b53eca6 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.6",
3
+ "version": "0.8.8",
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) {