harnessed 2.0.0 → 2.0.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.
package/dist/cli.mjs CHANGED
@@ -785,7 +785,7 @@ var init_resume = __esm({
785
785
 
786
786
  // package.json
787
787
  var package_default = {
788
- version: "2.0.0"};
788
+ version: "2.0.1"};
789
789
 
790
790
  // src/manifest/errors.ts
791
791
  function instancePathToKeyPath(instancePath) {
@@ -1566,7 +1566,7 @@ function renderHumanTable(records) {
1566
1566
  }
1567
1567
  }
1568
1568
  function pipeToJq(filterExpr, lines) {
1569
- return new Promise((resolve11, reject) => {
1569
+ return new Promise((resolve8, reject) => {
1570
1570
  const child = spawn("jq", [filterExpr], {
1571
1571
  stdio: ["pipe", "inherit", "inherit"],
1572
1572
  windowsHide: true
@@ -1575,12 +1575,12 @@ function pipeToJq(filterExpr, lines) {
1575
1575
  const e = err2;
1576
1576
  if (e.code === "ENOENT") {
1577
1577
  console.error("\u2717 jq not found in PATH \u2014 run: harnessed doctor");
1578
- resolve11(1);
1578
+ resolve8(1);
1579
1579
  } else {
1580
1580
  reject(err2);
1581
1581
  }
1582
1582
  });
1583
- child.on("close", (code) => resolve11(code ?? 0));
1583
+ child.on("close", (code) => resolve8(code ?? 0));
1584
1584
  child.stdin.write(lines.join("\n"));
1585
1585
  child.stdin.end();
1586
1586
  });
@@ -1638,19 +1638,132 @@ function registerAuditLog(program2) {
1638
1638
  }
1639
1639
  );
1640
1640
  }
1641
+ function getBackupRoot() {
1642
+ return join(homedir(), ".harnessed", "backups");
1643
+ }
1644
+ var HOME_DIR = process.env.HOME ?? process.env.USERPROFILE ?? "";
1645
+ function mirrorPath(target, scope, backupDir) {
1646
+ const root = scope === "HOME" ? HOME_DIR : ".";
1647
+ const rel = root ? relative(root, target) : target;
1648
+ if (!rel || rel.startsWith("..")) {
1649
+ const flat = createHash("sha1").update(target).digest("hex").slice(0, 16);
1650
+ return join(backupDir, scope, flat);
1651
+ }
1652
+ return join(backupDir, scope, rel);
1653
+ }
1654
+ function detectEol(buf) {
1655
+ return buf.includes("\r\n") ? "crlf" : "lf";
1656
+ }
1657
+ async function backup(plan, ctx) {
1658
+ const filename = ctx.manifest.metadata.name;
1659
+ const backupId = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
1660
+ const backupDir = join(getBackupRoot(), backupId);
1661
+ try {
1662
+ await mkdir(backupDir, { recursive: true });
1663
+ } catch (err2) {
1664
+ return {
1665
+ ok: false,
1666
+ error: {
1667
+ file: filename,
1668
+ path: "/",
1669
+ message: `failed to create backup dir ${backupDir}: ${err2.message}`,
1670
+ line: null,
1671
+ column: null,
1672
+ keyword: "backup-mkdir-failed"
1673
+ }
1674
+ };
1675
+ }
1676
+ const entries = [];
1677
+ for (const file of plan.files) {
1678
+ let buf;
1679
+ try {
1680
+ buf = await readFile(file.target);
1681
+ } catch (err2) {
1682
+ const code = err2.code;
1683
+ if (code === "ENOENT" && file.oldText === "") {
1684
+ entries.push({
1685
+ target: file.target,
1686
+ backup: "",
1687
+ // sentinel: no backup written; rollback should unlink target
1688
+ sha1: "",
1689
+ eol: "lf"
1690
+ // moot for non-existent file; default to lf
1691
+ });
1692
+ continue;
1693
+ }
1694
+ return {
1695
+ ok: false,
1696
+ error: {
1697
+ file: filename,
1698
+ path: file.target,
1699
+ message: `failed to read original file for backup: ${err2.message}`,
1700
+ line: null,
1701
+ column: null,
1702
+ keyword: "backup-read-failed"
1703
+ }
1704
+ };
1705
+ }
1706
+ const sha1 = createHash("sha1").update(buf).digest("hex");
1707
+ const eol = detectEol(buf);
1708
+ const dest = mirrorPath(file.target, file.scope, backupDir);
1709
+ try {
1710
+ await mkdir(dirname(dest), { recursive: true });
1711
+ await writeFile(dest, buf);
1712
+ } catch (err2) {
1713
+ return {
1714
+ ok: false,
1715
+ error: {
1716
+ file: filename,
1717
+ path: dest,
1718
+ message: `failed to write backup copy: ${err2.message}`,
1719
+ line: null,
1720
+ column: null,
1721
+ keyword: "backup-write-failed"
1722
+ }
1723
+ };
1724
+ }
1725
+ entries.push({ target: file.target, backup: dest, sha1, eol });
1726
+ }
1727
+ const metadata = {
1728
+ installer: filename,
1729
+ manifest: filename,
1730
+ timestamp: backupId,
1731
+ files: entries
1732
+ };
1733
+ const metadataPath = join(backupDir, "metadata.json");
1734
+ try {
1735
+ await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}
1736
+ `, "utf8");
1737
+ } catch (err2) {
1738
+ return {
1739
+ ok: false,
1740
+ error: {
1741
+ file: filename,
1742
+ path: metadataPath,
1743
+ message: `failed to write metadata.json: ${err2.message}`,
1744
+ line: null,
1745
+ column: null,
1746
+ keyword: "backup-metadata-failed"
1747
+ }
1748
+ };
1749
+ }
1750
+ return { ok: true, backupId, backupDir };
1751
+ }
1752
+
1753
+ // src/cli/backup-list.ts
1641
1754
  function registerBackupList(program2) {
1642
1755
  const backup2 = program2.command("backup").description("Backup snapshot operations");
1643
1756
  backup2.command("list").description("List backup snapshots under .harnessed-backup/").action(async () => {
1644
- const root = resolve(process.cwd(), ".harnessed-backup");
1757
+ const root = getBackupRoot();
1645
1758
  let dirs;
1646
1759
  try {
1647
1760
  dirs = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
1648
1761
  } catch {
1649
- console.log("no backups found (.harnessed-backup/ absent)");
1762
+ console.log(`no backups found (${root} absent)`);
1650
1763
  return;
1651
1764
  }
1652
1765
  if (dirs.length === 0) {
1653
- console.log("no backups found (.harnessed-backup/ empty)");
1766
+ console.log(`no backups found (${root} empty)`);
1654
1767
  return;
1655
1768
  }
1656
1769
  for (const ts of dirs) {
@@ -2721,12 +2834,12 @@ function registerGc(program2) {
2721
2834
  return;
2722
2835
  }
2723
2836
  const keepLast = Number.parseInt(opts.keepLast ?? "0", 10);
2724
- const root = resolve(process.cwd(), ".harnessed-backup");
2837
+ const root = getBackupRoot();
2725
2838
  let dirs;
2726
2839
  try {
2727
2840
  dirs = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
2728
2841
  } catch {
2729
- console.log("no backups found (.harnessed-backup/ absent) \u2014 nothing to gc");
2842
+ console.log(`no backups found (${root} absent) \u2014 nothing to gc`);
2730
2843
  return;
2731
2844
  }
2732
2845
  const cutoff = Date.now() - olderMs;
@@ -2763,114 +2876,6 @@ function registerGc(program2) {
2763
2876
  if (dryRun) console.log("\n(dry-run \u2014 re-run with --apply to actually delete)");
2764
2877
  });
2765
2878
  }
2766
- var HOME_DIR = process.env.HOME ?? process.env.USERPROFILE ?? "";
2767
- function mirrorPath(target, scope, backupDir) {
2768
- const root = scope === "HOME" ? HOME_DIR : ".";
2769
- const rel = root ? relative(root, target) : target;
2770
- if (!rel || rel.startsWith("..")) {
2771
- const flat = createHash("sha1").update(target).digest("hex").slice(0, 16);
2772
- return join(backupDir, scope, flat);
2773
- }
2774
- return join(backupDir, scope, rel);
2775
- }
2776
- function detectEol(buf) {
2777
- return buf.includes("\r\n") ? "crlf" : "lf";
2778
- }
2779
- async function backup(plan, ctx) {
2780
- const filename = ctx.manifest.metadata.name;
2781
- const backupId = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
2782
- const backupDir = join(ctx.cwd, ".harnessed-backup", backupId);
2783
- try {
2784
- await mkdir(backupDir, { recursive: true });
2785
- } catch (err2) {
2786
- return {
2787
- ok: false,
2788
- error: {
2789
- file: filename,
2790
- path: "/",
2791
- message: `failed to create backup dir ${backupDir}: ${err2.message}`,
2792
- line: null,
2793
- column: null,
2794
- keyword: "backup-mkdir-failed"
2795
- }
2796
- };
2797
- }
2798
- const entries = [];
2799
- for (const file of plan.files) {
2800
- let buf;
2801
- try {
2802
- buf = await readFile(file.target);
2803
- } catch (err2) {
2804
- const code = err2.code;
2805
- if (code === "ENOENT" && file.oldText === "") {
2806
- entries.push({
2807
- target: file.target,
2808
- backup: "",
2809
- // sentinel: no backup written; rollback should unlink target
2810
- sha1: "",
2811
- eol: "lf"
2812
- // moot for non-existent file; default to lf
2813
- });
2814
- continue;
2815
- }
2816
- return {
2817
- ok: false,
2818
- error: {
2819
- file: filename,
2820
- path: file.target,
2821
- message: `failed to read original file for backup: ${err2.message}`,
2822
- line: null,
2823
- column: null,
2824
- keyword: "backup-read-failed"
2825
- }
2826
- };
2827
- }
2828
- const sha1 = createHash("sha1").update(buf).digest("hex");
2829
- const eol = detectEol(buf);
2830
- const dest = mirrorPath(file.target, file.scope, backupDir);
2831
- try {
2832
- await mkdir(dirname(dest), { recursive: true });
2833
- await writeFile(dest, buf);
2834
- } catch (err2) {
2835
- return {
2836
- ok: false,
2837
- error: {
2838
- file: filename,
2839
- path: dest,
2840
- message: `failed to write backup copy: ${err2.message}`,
2841
- line: null,
2842
- column: null,
2843
- keyword: "backup-write-failed"
2844
- }
2845
- };
2846
- }
2847
- entries.push({ target: file.target, backup: dest, sha1, eol });
2848
- }
2849
- const metadata = {
2850
- installer: filename,
2851
- manifest: filename,
2852
- timestamp: backupId,
2853
- files: entries
2854
- };
2855
- const metadataPath = join(backupDir, "metadata.json");
2856
- try {
2857
- await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}
2858
- `, "utf8");
2859
- } catch (err2) {
2860
- return {
2861
- ok: false,
2862
- error: {
2863
- file: filename,
2864
- path: metadataPath,
2865
- message: `failed to write metadata.json: ${err2.message}`,
2866
- line: null,
2867
- column: null,
2868
- keyword: "backup-metadata-failed"
2869
- }
2870
- };
2871
- }
2872
- return { ok: true, backupId, backupDir };
2873
- }
2874
2879
  async function confirmAt(level, ctx) {
2875
2880
  if (level === "L4" && !ctx.opts.system) {
2876
2881
  if (!ctx.opts.nonInteractive) {
@@ -3152,7 +3157,7 @@ var installCcHookAdd = async (ctx) => {
3152
3157
  return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
3153
3158
  };
3154
3159
  function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
3155
- return new Promise((resolve11) => {
3160
+ return new Promise((resolve8) => {
3156
3161
  const isWin = process.platform === "win32";
3157
3162
  const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
3158
3163
  let stderr = "";
@@ -3161,15 +3166,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
3161
3166
  });
3162
3167
  const timer = setTimeout(() => {
3163
3168
  child.kill("SIGKILL");
3164
- resolve11({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
3169
+ resolve8({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
3165
3170
  }, timeoutMs);
3166
3171
  child.on("error", (e) => {
3167
3172
  clearTimeout(timer);
3168
- resolve11({ exitCode: -1, stderr: `${stderr}${e.message}` });
3173
+ resolve8({ exitCode: -1, stderr: `${stderr}${e.message}` });
3169
3174
  });
3170
3175
  child.on("close", (code) => {
3171
3176
  clearTimeout(timer);
3172
- resolve11({ exitCode: code ?? -1, stderr });
3177
+ resolve8({ exitCode: code ?? -1, stderr });
3173
3178
  });
3174
3179
  });
3175
3180
  }
@@ -3310,7 +3315,7 @@ ${newEntry}
3310
3315
  )
3311
3316
  };
3312
3317
  }
3313
- const vr = await new Promise((resolve11) => {
3318
+ const vr = await new Promise((resolve8) => {
3314
3319
  const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3315
3320
  let stderr = "";
3316
3321
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -3318,15 +3323,15 @@ ${newEntry}
3318
3323
  });
3319
3324
  const timer = setTimeout(() => {
3320
3325
  child.kill("SIGKILL");
3321
- resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
3326
+ resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
3322
3327
  }, 15e3);
3323
3328
  child.on("error", (e) => {
3324
3329
  clearTimeout(timer);
3325
- resolve11({ exitCode: -1, stderr: e.message });
3330
+ resolve8({ exitCode: -1, stderr: e.message });
3326
3331
  });
3327
3332
  child.on("close", (code) => {
3328
3333
  clearTimeout(timer);
3329
- resolve11({ exitCode: code ?? -1, stderr });
3334
+ resolve8({ exitCode: code ?? -1, stderr });
3330
3335
  });
3331
3336
  });
3332
3337
  if (vr.exitCode !== 0) {
@@ -3382,10 +3387,10 @@ async function spawnCmd(ctx, cmd, args) {
3382
3387
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
3383
3388
  stderr += chunk;
3384
3389
  });
3385
- return await new Promise((resolve11) => {
3390
+ return await new Promise((resolve8) => {
3386
3391
  const timer = setTimeout(() => {
3387
3392
  child.kill("SIGKILL");
3388
- resolve11({
3393
+ resolve8({
3389
3394
  ok: false,
3390
3395
  phase: "spawn",
3391
3396
  error: {
@@ -3400,7 +3405,7 @@ async function spawnCmd(ctx, cmd, args) {
3400
3405
  }, timeoutMs);
3401
3406
  child.on("error", (err2) => {
3402
3407
  clearTimeout(timer);
3403
- resolve11({
3408
+ resolve8({
3404
3409
  ok: false,
3405
3410
  phase: "spawn",
3406
3411
  error: {
@@ -3415,14 +3420,14 @@ async function spawnCmd(ctx, cmd, args) {
3415
3420
  });
3416
3421
  child.on("close", (code) => {
3417
3422
  clearTimeout(timer);
3418
- resolve11({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
3423
+ resolve8({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
3419
3424
  });
3420
3425
  });
3421
3426
  }
3422
3427
 
3423
3428
  // src/installers/gitCloneWithSetup.ts
3424
3429
  function gitRevParseHead(cwd, timeoutMs = 1e4) {
3425
- return new Promise((resolve11) => {
3430
+ return new Promise((resolve8) => {
3426
3431
  const isWin = process.platform === "win32";
3427
3432
  const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
3428
3433
  let stdout2 = "";
@@ -3431,15 +3436,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
3431
3436
  });
3432
3437
  const timer = setTimeout(() => {
3433
3438
  child.kill("SIGKILL");
3434
- resolve11({ sha: "", exit: -1 });
3439
+ resolve8({ sha: "", exit: -1 });
3435
3440
  }, timeoutMs);
3436
3441
  child.on("error", () => {
3437
3442
  clearTimeout(timer);
3438
- resolve11({ sha: "", exit: -1 });
3443
+ resolve8({ sha: "", exit: -1 });
3439
3444
  });
3440
3445
  child.on("close", (code) => {
3441
3446
  clearTimeout(timer);
3442
- resolve11({ sha: stdout2.trim(), exit: code ?? -1 });
3447
+ resolve8({ sha: stdout2.trim(), exit: code ?? -1 });
3443
3448
  });
3444
3449
  });
3445
3450
  }
@@ -3779,7 +3784,7 @@ ${newEntry}
3779
3784
  )
3780
3785
  };
3781
3786
  }
3782
- const vr = await new Promise((resolve11) => {
3787
+ const vr = await new Promise((resolve8) => {
3783
3788
  const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3784
3789
  let stderr = "";
3785
3790
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -3787,15 +3792,15 @@ ${newEntry}
3787
3792
  });
3788
3793
  const timer = setTimeout(() => {
3789
3794
  child.kill("SIGKILL");
3790
- resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
3795
+ resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
3791
3796
  }, 15e3);
3792
3797
  child.on("error", (e) => {
3793
3798
  clearTimeout(timer);
3794
- resolve11({ exitCode: -1, stderr: e.message });
3799
+ resolve8({ exitCode: -1, stderr: e.message });
3795
3800
  });
3796
3801
  child.on("close", (code) => {
3797
3802
  clearTimeout(timer);
3798
- resolve11({ exitCode: code ?? -1, stderr });
3803
+ resolve8({ exitCode: code ?? -1, stderr });
3799
3804
  });
3800
3805
  });
3801
3806
  if (vr.exitCode !== 0) {
@@ -3927,7 +3932,7 @@ ${newEntry}
3927
3932
  )
3928
3933
  };
3929
3934
  }
3930
- const vr = await new Promise((resolve11) => {
3935
+ const vr = await new Promise((resolve8) => {
3931
3936
  const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3932
3937
  let stderr = "";
3933
3938
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -3935,15 +3940,15 @@ ${newEntry}
3935
3940
  });
3936
3941
  const timer = setTimeout(() => {
3937
3942
  child.kill("SIGKILL");
3938
- resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
3943
+ resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
3939
3944
  }, 15e3);
3940
3945
  child.on("error", (e) => {
3941
3946
  clearTimeout(timer);
3942
- resolve11({ exitCode: -1, stderr: e.message });
3947
+ resolve8({ exitCode: -1, stderr: e.message });
3943
3948
  });
3944
3949
  child.on("close", (code) => {
3945
3950
  clearTimeout(timer);
3946
- resolve11({ exitCode: code ?? -1, stderr });
3951
+ resolve8({ exitCode: code ?? -1, stderr });
3947
3952
  });
3948
3953
  });
3949
3954
  if (vr.exitCode !== 0) {
@@ -4515,7 +4520,7 @@ function normalizeEol(buf, eol) {
4515
4520
  }
4516
4521
  function registerRollback(program2) {
4517
4522
  program2.command("rollback <timestamp>").description("Restore files from a backup snapshot (preserves original LF/CRLF)").action(async (timestamp) => {
4518
- const dir = resolve(process.cwd(), ".harnessed-backup", timestamp);
4523
+ const dir = join(getBackupRoot(), timestamp);
4519
4524
  const metaPath = join(dir, "metadata.json");
4520
4525
  let meta;
4521
4526
  try {
@@ -4958,7 +4963,7 @@ var uninstallNpmCli = async (ctx) => {
4958
4963
  const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
4959
4964
  const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
4960
4965
  const isWin = process.platform === "win32";
4961
- const result = await new Promise((resolve11) => {
4966
+ const result = await new Promise((resolve8) => {
4962
4967
  const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
4963
4968
  let stderr = "";
4964
4969
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -4966,15 +4971,15 @@ var uninstallNpmCli = async (ctx) => {
4966
4971
  });
4967
4972
  const timer = setTimeout(() => {
4968
4973
  child.kill("SIGKILL");
4969
- resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
4974
+ resolve8({ exitCode: -1, stderr: `${stderr}[timeout]` });
4970
4975
  }, 3e4);
4971
4976
  child.on("error", (e) => {
4972
4977
  clearTimeout(timer);
4973
- resolve11({ exitCode: -1, stderr: e.message });
4978
+ resolve8({ exitCode: -1, stderr: e.message });
4974
4979
  });
4975
4980
  child.on("close", (code) => {
4976
4981
  clearTimeout(timer);
4977
- resolve11({ exitCode: code ?? -1, stderr });
4982
+ resolve8({ exitCode: code ?? -1, stderr });
4978
4983
  });
4979
4984
  });
4980
4985
  if (result.exitCode !== 0) {