@remnic/core 9.3.599 → 9.3.601

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/index.js CHANGED
@@ -3618,10 +3618,14 @@ function hashContent3(content) {
3618
3618
  }
3619
3619
 
3620
3620
  // src/spaces/index.ts
3621
+ import { spawnSync } from "child_process";
3622
+ import crypto7 from "crypto";
3621
3623
  import fs7 from "fs";
3622
3624
  import path11 from "path";
3623
- import crypto7 from "crypto";
3624
3625
  var MANIFEST_VERSION = 1;
3626
+ var MANIFEST_LOCK_STALE_MS = 3e4;
3627
+ var MANIFEST_LOCK_TIMEOUT_MS = MANIFEST_LOCK_STALE_MS + 1e4;
3628
+ var MANIFEST_LOCK_SLEEP_MS = 20;
3625
3629
  function normalizeSpaceMemoryDir(memoryDir) {
3626
3630
  return path11.resolve(memoryDir);
3627
3631
  }
@@ -3633,24 +3637,283 @@ function getManifestPath(baseDir) {
3633
3637
  return path11.join(getSpacesDir(baseDir), "manifest.json");
3634
3638
  }
3635
3639
  function loadManifest(baseDir, memoryDirOverride) {
3640
+ if (fs7.existsSync(getManifestPath(baseDir))) {
3641
+ try {
3642
+ return readManifestUnlocked(baseDir, memoryDirOverride, { bootstrapIfMissing: false });
3643
+ } catch (error) {
3644
+ if (error.code !== "ENOENT") {
3645
+ throw error;
3646
+ }
3647
+ }
3648
+ }
3649
+ return updateManifest(baseDir, (manifest) => manifest, memoryDirOverride);
3650
+ }
3651
+ function saveManifest(manifest, baseDir) {
3652
+ withManifestLock(baseDir, () => {
3653
+ saveManifestUnlocked(manifest, baseDir);
3654
+ });
3655
+ }
3656
+ function updateManifest(baseDir, updater, memoryDirOverride) {
3657
+ return withManifestLock(baseDir, () => {
3658
+ const manifest = readManifestUnlocked(baseDir, memoryDirOverride);
3659
+ const result = updater(manifest);
3660
+ saveManifestUnlocked(manifest, baseDir);
3661
+ return result;
3662
+ });
3663
+ }
3664
+ function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
3636
3665
  const manifestPath2 = getManifestPath(baseDir);
3637
3666
  if (!fs7.existsSync(manifestPath2)) {
3667
+ if (options.bootstrapIfMissing === false) {
3668
+ const error = new Error(`Spaces manifest not found: ${manifestPath2}`);
3669
+ error.code = "ENOENT";
3670
+ throw error;
3671
+ }
3638
3672
  const personalSpace = createPersonalSpace(baseDir, memoryDirOverride);
3639
- const manifest = {
3673
+ return {
3640
3674
  activeSpaceId: personalSpace.id,
3641
3675
  spaces: [personalSpace],
3642
3676
  version: MANIFEST_VERSION
3643
3677
  };
3644
- saveManifest(manifest, baseDir);
3645
- return manifest;
3646
3678
  }
3647
3679
  const raw = JSON.parse(fs7.readFileSync(manifestPath2, "utf8"));
3648
3680
  return raw;
3649
3681
  }
3650
- function saveManifest(manifest, baseDir) {
3682
+ function saveManifestUnlocked(manifest, baseDir) {
3651
3683
  const manifestPath2 = getManifestPath(baseDir);
3652
- fs7.mkdirSync(path11.dirname(manifestPath2), { recursive: true });
3653
- fs7.writeFileSync(manifestPath2, JSON.stringify(manifest, null, 2) + "\n");
3684
+ const manifestDir2 = path11.dirname(manifestPath2);
3685
+ fs7.mkdirSync(manifestDir2, { recursive: true });
3686
+ const tempPath = path11.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto7.randomUUID()}.tmp`);
3687
+ try {
3688
+ fs7.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
3689
+ `, { flag: "wx" });
3690
+ fs7.renameSync(tempPath, manifestPath2);
3691
+ } catch (error) {
3692
+ try {
3693
+ fs7.rmSync(tempPath, { force: true });
3694
+ } catch {
3695
+ }
3696
+ throw error;
3697
+ }
3698
+ }
3699
+ function withManifestLock(baseDir, operation) {
3700
+ const lockDir = `${getManifestPath(baseDir)}.lock`;
3701
+ fs7.mkdirSync(path11.dirname(lockDir), { recursive: true });
3702
+ const lockOwner = acquireManifestLock(lockDir);
3703
+ try {
3704
+ return operation();
3705
+ } finally {
3706
+ releaseManifestLock(lockDir, lockOwner);
3707
+ }
3708
+ }
3709
+ function acquireManifestLock(lockDir) {
3710
+ const deadline = Date.now() + MANIFEST_LOCK_TIMEOUT_MS;
3711
+ const owner = createManifestLockOwner();
3712
+ const reclaimDir = getManifestLockReclaimDir(lockDir);
3713
+ while (true) {
3714
+ if (fs7.existsSync(reclaimDir)) {
3715
+ removeStaleManifestReclaimLock(reclaimDir);
3716
+ }
3717
+ if (fs7.existsSync(reclaimDir)) {
3718
+ if (Date.now() >= deadline) {
3719
+ throw new Error(`Timed out waiting for spaces manifest reclaim lock: ${reclaimDir}`);
3720
+ }
3721
+ sleepSync(MANIFEST_LOCK_SLEEP_MS);
3722
+ continue;
3723
+ }
3724
+ try {
3725
+ fs7.mkdirSync(lockDir, { recursive: false });
3726
+ try {
3727
+ fs7.writeFileSync(path11.join(lockDir, "owner"), `${owner}
3728
+ `, { flag: "wx" });
3729
+ } catch (error) {
3730
+ fs7.rmSync(lockDir, { recursive: true, force: true });
3731
+ throw error;
3732
+ }
3733
+ if (fs7.existsSync(reclaimDir)) {
3734
+ releaseManifestLock(lockDir, owner);
3735
+ if (Date.now() >= deadline) {
3736
+ throw new Error(`Timed out waiting for spaces manifest reclaim lock: ${reclaimDir}`);
3737
+ }
3738
+ sleepSync(MANIFEST_LOCK_SLEEP_MS);
3739
+ continue;
3740
+ }
3741
+ return owner;
3742
+ } catch (error) {
3743
+ const code = error.code;
3744
+ if (code !== "EEXIST") {
3745
+ throw error;
3746
+ }
3747
+ removeStaleManifestLock(lockDir);
3748
+ if (Date.now() >= deadline) {
3749
+ throw new Error(`Timed out waiting for spaces manifest lock: ${lockDir}`);
3750
+ }
3751
+ sleepSync(MANIFEST_LOCK_SLEEP_MS);
3752
+ }
3753
+ }
3754
+ }
3755
+ function releaseManifestLock(lockDir, owner) {
3756
+ try {
3757
+ const ownerPath = path11.join(lockDir, "owner");
3758
+ if (fs7.readFileSync(ownerPath, "utf8").trim() === owner) {
3759
+ fs7.rmSync(lockDir, { recursive: true, force: true });
3760
+ }
3761
+ } catch (error) {
3762
+ if (error.code !== "ENOENT") {
3763
+ throw error;
3764
+ }
3765
+ }
3766
+ }
3767
+ function removeStaleManifestLock(lockDir) {
3768
+ const reclaimDir = getManifestLockReclaimDir(lockDir);
3769
+ const reclaimOwner = createManifestLockOwner();
3770
+ try {
3771
+ fs7.mkdirSync(reclaimDir, { recursive: false });
3772
+ try {
3773
+ fs7.writeFileSync(path11.join(reclaimDir, "owner"), `${reclaimOwner}
3774
+ `, { flag: "wx" });
3775
+ } catch (error) {
3776
+ fs7.rmSync(reclaimDir, { recursive: true, force: true });
3777
+ throw error;
3778
+ }
3779
+ } catch (error) {
3780
+ const code = error.code;
3781
+ if (code === "EEXIST") {
3782
+ return;
3783
+ }
3784
+ throw error;
3785
+ }
3786
+ try {
3787
+ const snapshot = readManifestLockSnapshot(lockDir);
3788
+ if (!snapshot || Date.now() - snapshot.mtimeMs <= MANIFEST_LOCK_STALE_MS) {
3789
+ return;
3790
+ }
3791
+ if (isManifestLockOwnerActive(snapshot.owner)) {
3792
+ return;
3793
+ }
3794
+ const tombstoneDir = `${lockDir}.stale.${process.pid}.${crypto7.randomUUID()}`;
3795
+ try {
3796
+ fs7.renameSync(lockDir, tombstoneDir);
3797
+ } catch (error) {
3798
+ if (error.code !== "ENOENT") {
3799
+ throw error;
3800
+ }
3801
+ return;
3802
+ }
3803
+ fs7.rmSync(tombstoneDir, { recursive: true, force: true });
3804
+ } finally {
3805
+ fs7.rmSync(reclaimDir, { recursive: true, force: true });
3806
+ }
3807
+ }
3808
+ function getManifestLockReclaimDir(lockDir) {
3809
+ return `${lockDir}.reclaim`;
3810
+ }
3811
+ function createManifestLockOwner() {
3812
+ return JSON.stringify({
3813
+ pid: process.pid,
3814
+ startKey: readProcessStartKey(process.pid),
3815
+ token: crypto7.randomUUID()
3816
+ });
3817
+ }
3818
+ function removeStaleManifestReclaimLock(reclaimDir) {
3819
+ const snapshot = readManifestLockSnapshot(reclaimDir);
3820
+ if (!snapshot || Date.now() - snapshot.mtimeMs <= MANIFEST_LOCK_STALE_MS) {
3821
+ return;
3822
+ }
3823
+ if (isManifestLockOwnerActive(snapshot.owner)) {
3824
+ return;
3825
+ }
3826
+ const tombstoneDir = `${reclaimDir}.stale.${process.pid}.${crypto7.randomUUID()}`;
3827
+ try {
3828
+ fs7.renameSync(reclaimDir, tombstoneDir);
3829
+ } catch (error) {
3830
+ if (error.code !== "ENOENT") {
3831
+ throw error;
3832
+ }
3833
+ return;
3834
+ }
3835
+ fs7.rmSync(tombstoneDir, { recursive: true, force: true });
3836
+ }
3837
+ function readManifestLockSnapshot(lockDir) {
3838
+ let stat;
3839
+ try {
3840
+ stat = fs7.statSync(lockDir);
3841
+ } catch (error) {
3842
+ if (error.code === "ENOENT") {
3843
+ return void 0;
3844
+ }
3845
+ throw error;
3846
+ }
3847
+ try {
3848
+ const owner = fs7.readFileSync(path11.join(lockDir, "owner"), "utf8").trim();
3849
+ return { mtimeMs: stat.mtimeMs, owner };
3850
+ } catch (error) {
3851
+ if (error.code === "ENOENT") {
3852
+ return { mtimeMs: stat.mtimeMs };
3853
+ }
3854
+ throw error;
3855
+ }
3856
+ }
3857
+ function isManifestLockOwnerActive(owner) {
3858
+ if (!owner) {
3859
+ return false;
3860
+ }
3861
+ const parsed = parseManifestLockOwner(owner);
3862
+ if (!parsed) {
3863
+ return false;
3864
+ }
3865
+ const currentStartKey = readProcessStartKey(parsed.pid);
3866
+ if (currentStartKey && parsed.startKey) {
3867
+ return currentStartKey === parsed.startKey;
3868
+ }
3869
+ return isProcessAlive(parsed.pid);
3870
+ }
3871
+ function parseManifestLockOwner(owner) {
3872
+ try {
3873
+ const parsed = JSON.parse(owner);
3874
+ const pid = typeof parsed.pid === "number" ? parsed.pid : Number.NaN;
3875
+ if (Number.isInteger(pid) && pid > 0) {
3876
+ return {
3877
+ pid,
3878
+ startKey: typeof parsed.startKey === "string" && parsed.startKey.length > 0 ? parsed.startKey : void 0
3879
+ };
3880
+ }
3881
+ } catch {
3882
+ }
3883
+ const legacyPid = Number(owner.split(":", 1)[0]);
3884
+ return Number.isInteger(legacyPid) && legacyPid > 0 ? { pid: legacyPid } : void 0;
3885
+ }
3886
+ function readProcessStartKey(pid) {
3887
+ if (!Number.isInteger(pid) || pid <= 0) {
3888
+ return void 0;
3889
+ }
3890
+ const result = spawnSync("ps", ["-p", String(pid), "-o", "lstart="], {
3891
+ encoding: "utf8",
3892
+ stdio: ["ignore", "pipe", "ignore"]
3893
+ });
3894
+ if (result.error || result.status !== 0 || typeof result.stdout !== "string") {
3895
+ return void 0;
3896
+ }
3897
+ const startKey = result.stdout.trim().replace(/\s+/g, " ");
3898
+ return startKey.length > 0 ? startKey : void 0;
3899
+ }
3900
+ function isProcessAlive(pid) {
3901
+ if (pid === process.pid) {
3902
+ return true;
3903
+ }
3904
+ try {
3905
+ process.kill(pid, 0);
3906
+ return true;
3907
+ } catch (error) {
3908
+ const code = error.code;
3909
+ if (code === "ESRCH") {
3910
+ return false;
3911
+ }
3912
+ return true;
3913
+ }
3914
+ }
3915
+ function sleepSync(ms) {
3916
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
3654
3917
  }
3655
3918
  function createPersonalSpace(baseDir, memoryDirOverride) {
3656
3919
  const homeDir = baseDir ?? resolveHomeDir();
@@ -3681,84 +3944,91 @@ function getActiveSpace(baseDir) {
3681
3944
  return space;
3682
3945
  }
3683
3946
  function createSpace(options) {
3684
- const manifest = loadManifest(options.baseDir);
3685
3947
  const id = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
3686
- if (manifest.spaces.some((s) => s.id === id)) {
3687
- throw new Error(`Space "${id}" already exists`);
3688
- }
3689
- if (options.parentSpaceId && !manifest.spaces.some((s) => s.id === options.parentSpaceId)) {
3690
- throw new Error(`Parent space "${options.parentSpaceId}" not found`);
3691
- }
3692
3948
  const now = (/* @__PURE__ */ new Date()).toISOString();
3693
3949
  const memoryDir = normalizeSpaceMemoryDir(
3694
- options.memoryDir ?? path11.join(
3695
- getSpacesDir(options.baseDir),
3950
+ options.memoryDir ?? path11.join(getSpacesDir(options.baseDir), id, "memory")
3951
+ );
3952
+ const space = updateManifest(options.baseDir, (manifest) => {
3953
+ if (manifest.spaces.some((s) => s.id === id)) {
3954
+ throw new Error(`Space "${id}" already exists`);
3955
+ }
3956
+ if (options.parentSpaceId && !manifest.spaces.some((s) => s.id === options.parentSpaceId)) {
3957
+ throw new Error(`Parent space "${options.parentSpaceId}" not found`);
3958
+ }
3959
+ const created = {
3696
3960
  id,
3697
- "memory"
3698
- )
3961
+ name: options.name,
3962
+ kind: options.kind,
3963
+ description: options.description,
3964
+ memoryDir,
3965
+ createdAt: now,
3966
+ updatedAt: now,
3967
+ owner: readEnvVar("USER"),
3968
+ parentSpaceId: options.parentSpaceId
3969
+ };
3970
+ fs7.mkdirSync(memoryDir, { recursive: true });
3971
+ manifest.spaces.push(created);
3972
+ manifest.updatedAt = now;
3973
+ return created;
3974
+ });
3975
+ appendAudit(
3976
+ {
3977
+ action: "space.create",
3978
+ sourceSpaceId: id,
3979
+ details: `Created ${options.kind} space "${options.name}"`
3980
+ },
3981
+ options.baseDir
3699
3982
  );
3700
- const space = {
3701
- id,
3702
- name: options.name,
3703
- kind: options.kind,
3704
- description: options.description,
3705
- memoryDir,
3706
- createdAt: now,
3707
- updatedAt: now,
3708
- owner: readEnvVar("USER"),
3709
- parentSpaceId: options.parentSpaceId
3710
- };
3711
- fs7.mkdirSync(memoryDir, { recursive: true });
3712
- manifest.spaces.push(space);
3713
- manifest.updatedAt = now;
3714
- saveManifest(manifest, options.baseDir);
3715
- appendAudit({
3716
- action: "space.create",
3717
- sourceSpaceId: id,
3718
- details: `Created ${options.kind} space "${options.name}"`
3719
- }, options.baseDir);
3720
3983
  return space;
3721
3984
  }
3722
3985
  function deleteSpace(spaceId, baseDir) {
3723
- const manifest = loadManifest(baseDir);
3724
3986
  if (spaceId === "personal") {
3725
3987
  throw new Error("Cannot delete the personal space");
3726
3988
  }
3727
- const idx = manifest.spaces.findIndex((s) => s.id === spaceId);
3728
- if (idx === -1) throw new Error(`Space "${spaceId}" not found`);
3729
- if (manifest.activeSpaceId === spaceId) {
3730
- manifest.activeSpaceId = "personal";
3731
- }
3732
- for (const space of manifest.spaces) {
3733
- if (space.parentSpaceId === spaceId) {
3734
- space.parentSpaceId = void 0;
3989
+ updateManifest(baseDir, (manifest) => {
3990
+ const idx = manifest.spaces.findIndex((s) => s.id === spaceId);
3991
+ if (idx === -1) throw new Error(`Space "${spaceId}" not found`);
3992
+ if (manifest.activeSpaceId === spaceId) {
3993
+ manifest.activeSpaceId = "personal";
3735
3994
  }
3736
- }
3737
- manifest.spaces.splice(idx, 1);
3738
- saveManifest(manifest, baseDir);
3739
- appendAudit({
3740
- action: "space.delete",
3741
- sourceSpaceId: spaceId,
3742
- details: `Deleted space "${spaceId}"`
3743
- }, baseDir);
3995
+ for (const space of manifest.spaces) {
3996
+ if (space.parentSpaceId === spaceId) {
3997
+ space.parentSpaceId = void 0;
3998
+ }
3999
+ }
4000
+ manifest.spaces.splice(idx, 1);
4001
+ });
4002
+ appendAudit(
4003
+ {
4004
+ action: "space.delete",
4005
+ sourceSpaceId: spaceId,
4006
+ details: `Deleted space "${spaceId}"`
4007
+ },
4008
+ baseDir
4009
+ );
3744
4010
  }
3745
4011
  function switchSpace(spaceId, baseDir) {
3746
- const manifest = loadManifest(baseDir);
3747
- const space = manifest.spaces.find((s) => s.id === spaceId);
3748
- if (!space) throw new Error(`Space "${spaceId}" not found`);
3749
- const previousId = manifest.activeSpaceId;
3750
- manifest.activeSpaceId = spaceId;
3751
- saveManifest(manifest, baseDir);
3752
- appendAudit({
3753
- action: "space.switch",
3754
- sourceSpaceId: previousId,
3755
- targetSpaceId: spaceId,
3756
- details: `Switched from "${previousId}" to "${spaceId}"`
3757
- }, baseDir);
4012
+ const { previousId, spaceName } = updateManifest(baseDir, (manifest) => {
4013
+ const space = manifest.spaces.find((s) => s.id === spaceId);
4014
+ if (!space) throw new Error(`Space "${spaceId}" not found`);
4015
+ const previousId2 = manifest.activeSpaceId;
4016
+ manifest.activeSpaceId = spaceId;
4017
+ return { previousId: previousId2, spaceName: space.name };
4018
+ });
4019
+ appendAudit(
4020
+ {
4021
+ action: "space.switch",
4022
+ sourceSpaceId: previousId,
4023
+ targetSpaceId: spaceId,
4024
+ details: `Switched from "${previousId}" to "${spaceId}"`
4025
+ },
4026
+ baseDir
4027
+ );
3758
4028
  return {
3759
4029
  previousSpaceId: previousId,
3760
4030
  currentSpaceId: spaceId,
3761
- message: `Switched to "${space.name}"`
4031
+ message: `Switched to "${spaceName}"`
3762
4032
  };
3763
4033
  }
3764
4034
  function pushToSpace(sourceSpaceId, targetSpaceId, options) {
@@ -3772,12 +4042,15 @@ function pushToSpace(sourceSpaceId, targetSpaceId, options) {
3772
4042
  filterIds: options?.memoryIds,
3773
4043
  force: options?.force
3774
4044
  });
3775
- appendAudit({
3776
- action: "space.push",
3777
- sourceSpaceId,
3778
- targetSpaceId,
3779
- details: `Pushed ${result.merged} memories, ${result.conflicts.length} conflicts`
3780
- }, options?.baseDir);
4045
+ appendAudit(
4046
+ {
4047
+ action: "space.push",
4048
+ sourceSpaceId,
4049
+ targetSpaceId,
4050
+ details: `Pushed ${result.merged} memories, ${result.conflicts.length} conflicts`
4051
+ },
4052
+ options?.baseDir
4053
+ );
3781
4054
  return {
3782
4055
  sourceSpaceId,
3783
4056
  targetSpaceId,
@@ -3797,12 +4070,15 @@ function pullFromSpace(sourceSpaceId, targetSpaceId, options) {
3797
4070
  filterIds: options?.memoryIds,
3798
4071
  force: options?.force
3799
4072
  });
3800
- appendAudit({
3801
- action: "space.pull",
3802
- sourceSpaceId,
3803
- targetSpaceId,
3804
- details: `Pulled ${result.merged} memories, ${result.conflicts.length} conflicts`
3805
- }, options?.baseDir);
4073
+ appendAudit(
4074
+ {
4075
+ action: "space.pull",
4076
+ sourceSpaceId,
4077
+ targetSpaceId,
4078
+ details: `Pulled ${result.merged} memories, ${result.conflicts.length} conflicts`
4079
+ },
4080
+ options?.baseDir
4081
+ );
3806
4082
  return {
3807
4083
  sourceSpaceId,
3808
4084
  targetSpaceId,
@@ -3812,22 +4088,26 @@ function pullFromSpace(sourceSpaceId, targetSpaceId, options) {
3812
4088
  };
3813
4089
  }
3814
4090
  function shareSpace(spaceId, members, baseDir) {
3815
- const manifest = loadManifest(baseDir);
3816
- const space = manifest.spaces.find((s) => s.id === spaceId);
3817
- if (!space) throw new Error(`Space "${spaceId}" not found`);
3818
- if (space.kind === "personal") throw new Error("Cannot share personal space");
3819
- space.members = [.../* @__PURE__ */ new Set([...space.members ?? [], ...members])];
3820
- space.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3821
- saveManifest(manifest, baseDir);
3822
- appendAudit({
3823
- action: "space.share",
3824
- sourceSpaceId: spaceId,
3825
- details: `Shared with: ${members.join(", ")}`
3826
- }, baseDir);
4091
+ const spaceName = updateManifest(baseDir, (manifest) => {
4092
+ const space = manifest.spaces.find((s) => s.id === spaceId);
4093
+ if (!space) throw new Error(`Space "${spaceId}" not found`);
4094
+ if (space.kind === "personal") throw new Error("Cannot share personal space");
4095
+ space.members = [.../* @__PURE__ */ new Set([...space.members ?? [], ...members])];
4096
+ space.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
4097
+ return space.name;
4098
+ });
4099
+ appendAudit(
4100
+ {
4101
+ action: "space.share",
4102
+ sourceSpaceId: spaceId,
4103
+ details: `Shared with: ${members.join(", ")}`
4104
+ },
4105
+ baseDir
4106
+ );
3827
4107
  return {
3828
4108
  spaceId,
3829
4109
  sharedWith: members,
3830
- message: `Shared "${space.name}" with ${members.length} member(s)`
4110
+ message: `Shared "${spaceName}" with ${members.length} member(s)`
3831
4111
  };
3832
4112
  }
3833
4113
  function promoteSpace(sourceSpaceId, targetSpaceId, options) {
@@ -3846,12 +4126,15 @@ function promoteSpace(sourceSpaceId, targetSpaceId, options) {
3846
4126
  filterIds: options?.memoryIds,
3847
4127
  force: options?.forceOverwrite !== void 0 ? options.forceOverwrite : options?.force ?? false
3848
4128
  });
3849
- appendAudit({
3850
- action: "space.promote",
3851
- sourceSpaceId,
3852
- targetSpaceId,
3853
- details: `Promoted ${result.merged} memories from "${source.name}" to "${target.name}"`
3854
- }, options?.baseDir);
4129
+ appendAudit(
4130
+ {
4131
+ action: "space.promote",
4132
+ sourceSpaceId,
4133
+ targetSpaceId,
4134
+ details: `Promoted ${result.merged} memories from "${source.name}" to "${target.name}"`
4135
+ },
4136
+ options?.baseDir
4137
+ );
3855
4138
  return {
3856
4139
  sourceSpaceId,
3857
4140
  targetSpaceId,
@@ -3870,12 +4153,15 @@ function mergeSpaces(sourceSpaceId, targetSpaceId, options) {
3870
4153
  const result = copyMemories(source.memoryDir, target.memoryDir, {
3871
4154
  force: options?.force
3872
4155
  });
3873
- appendAudit({
3874
- action: "space.merge",
3875
- sourceSpaceId,
3876
- targetSpaceId,
3877
- details: `Merged: ${result.merged} merged, ${result.conflicts.length} conflicts, ${result.skipped} skipped`
3878
- }, options?.baseDir);
4156
+ appendAudit(
4157
+ {
4158
+ action: "space.merge",
4159
+ sourceSpaceId,
4160
+ targetSpaceId,
4161
+ details: `Merged: ${result.merged} merged, ${result.conflicts.length} conflicts, ${result.skipped} skipped`
4162
+ },
4163
+ options?.baseDir
4164
+ );
3879
4165
  return {
3880
4166
  ...result,
3881
4167
  durationMs: Date.now() - startTime
@@ -3895,7 +4181,8 @@ function appendAudit(entry, baseDir) {
3895
4181
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3896
4182
  ...entry
3897
4183
  };
3898
- fs7.appendFileSync(auditPath, JSON.stringify(full) + "\n");
4184
+ fs7.appendFileSync(auditPath, `${JSON.stringify(full)}
4185
+ `);
3899
4186
  }
3900
4187
  function copyMemories(sourceDir, targetDir, options) {
3901
4188
  let merged = 0;