mindwire 0.1.32 → 0.1.33

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.
@@ -79,7 +79,7 @@ function computerPairingURI(invitation) {
79
79
  }
80
80
 
81
81
  // src/version.ts
82
- var SDK_VERSION = "0.1.32" ;
82
+ var SDK_VERSION = "0.1.33" ;
83
83
  function versionAtLeast(actual, desired) {
84
84
  if (actual === desired) return true;
85
85
  if (!actual || !/^\d+\.\d+\.\d+$/.test(actual) || !/^\d+\.\d+\.\d+$/.test(desired)) return false;
@@ -665,6 +665,171 @@ var Run = class _Run {
665
665
  }
666
666
  };
667
667
 
668
+ // src/project-sync.ts
669
+ var ProjectSyncError = class extends MindwireError {
670
+ constructor(operation) {
671
+ super(operation.error ?? (operation.status === "conflicts" ? "Changes conflict. Both workspace copies and their checkpoints are preserved." : `Project synchronization ${operation.status}.`));
672
+ this.operation = operation;
673
+ this.name = "ProjectSyncError";
674
+ }
675
+ operation;
676
+ };
677
+ var active = (o) => ["queued", "exporting", "preparing", "applying"].includes(o.status);
678
+ var prefix = "/workspace/sync";
679
+ function delay2(ms, signal) {
680
+ signal?.throwIfAborted();
681
+ return new Promise((resolve, reject) => {
682
+ const done = () => {
683
+ signal?.removeEventListener("abort", abort);
684
+ resolve();
685
+ };
686
+ const timer = setTimeout(done, ms);
687
+ const abort = () => {
688
+ clearTimeout(timer);
689
+ signal?.removeEventListener("abort", abort);
690
+ reject(signal?.reason);
691
+ };
692
+ signal?.addEventListener("abort", abort, { once: true });
693
+ });
694
+ }
695
+ var ProjectSyncApi = class {
696
+ constructor(mw) {
697
+ this.mw = mw;
698
+ }
699
+ mw;
700
+ flights = /* @__PURE__ */ new Map();
701
+ export(request, options = {}) {
702
+ return this.mw.http.request("POST", `${prefix}/exports`, { body: request, ...options });
703
+ }
704
+ import(request, options = {}) {
705
+ return this.mw.http.request("POST", `${prefix}/imports`, { body: request, ...options });
706
+ }
707
+ async list(options = {}) {
708
+ const response = await this.mw.http.request("GET", `${prefix}/operations`, options);
709
+ return response.operations;
710
+ }
711
+ get(id, options = {}) {
712
+ return this.mw.http.request("GET", `${prefix}/operations/${encodeURIComponent(id)}`, options);
713
+ }
714
+ retry(id, options = {}) {
715
+ return this.mw.http.request("POST", `${prefix}/operations/${encodeURIComponent(id)}/retry`, options);
716
+ }
717
+ cancel(id) {
718
+ return this.mw.http.request("POST", `${prefix}/operations/${encodeURIComponent(id)}/cancel`);
719
+ }
720
+ checkpoint(id, options = {}) {
721
+ return this.mw.http.request("GET", `${prefix}/checkpoints/${encodeURIComponent(id)}`, options);
722
+ }
723
+ acceptCheckpoint(checkpoint, options = {}) {
724
+ return this.mw.http.request("PUT", `${prefix}/checkpoints/${encodeURIComponent(checkpoint.id)}`, { body: checkpoint, ...options });
725
+ }
726
+ objects(id, cursor = 0, options = {}) {
727
+ return this.mw.http.request("GET", `${prefix}/checkpoints/${encodeURIComponent(id)}/objects`, { query: { cursor }, ...options });
728
+ }
729
+ async missing(objects, options = {}) {
730
+ const response = await this.mw.http.request("POST", `${prefix}/objects/missing`, { body: { objects }, ...options });
731
+ return response.objects;
732
+ }
733
+ /** Base64 chunk, at most 256 KiB before encoding. */
734
+ async getObject(id, options = {}) {
735
+ const response = await this.mw.http.request("GET", `${prefix}/objects/${encodeURIComponent(id)}`, options);
736
+ return response.data;
737
+ }
738
+ async putObject(id, data, options = {}) {
739
+ await this.mw.http.request("PUT", `${prefix}/objects/${encodeURIComponent(id)}`, { body: { data }, ...options });
740
+ }
741
+ async wait(id, options = {}) {
742
+ for (; ; ) {
743
+ const o = await this.get(id, { signal: options.signal });
744
+ options.onUpdate?.(o);
745
+ if (!active(o)) return o;
746
+ await delay2(500, options.signal);
747
+ }
748
+ }
749
+ /** Repeating this relay skips acknowledged checkpoints and verified chunks.
750
+ * Aborting detaches the relay; daemon export/apply operations remain durable.
751
+ */
752
+ async relayCheckpoint(target, id, options = {}) {
753
+ const visited = /* @__PURE__ */ new Set();
754
+ const visit = async (checkpointId) => {
755
+ options.signal?.throwIfAborted();
756
+ if (visited.has(checkpointId)) return;
757
+ visited.add(checkpointId);
758
+ try {
759
+ await target.checkpoint(checkpointId, options);
760
+ return;
761
+ } catch (error) {
762
+ if (!(error instanceof ApiError) || error.status !== 404) throw error;
763
+ }
764
+ const checkpoint = await this.checkpoint(checkpointId, options);
765
+ for (const parent of checkpoint.parents) await visit(parent);
766
+ let cursor = 0;
767
+ do {
768
+ const page = await this.objects(checkpointId, cursor, options);
769
+ const missing = await target.missing(page.objects, options);
770
+ for (const object of missing) {
771
+ const data = await this.getObject(object, options);
772
+ await target.putObject(object, data, options);
773
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
774
+ options.onBytes?.(Math.floor(data.length * 3 / 4) - padding);
775
+ }
776
+ cursor = page.next ?? 0;
777
+ } while (cursor);
778
+ await target.acceptCheckpoint(checkpoint, options);
779
+ };
780
+ await visit(id);
781
+ }
782
+ /** Safe X → Y and Y → X. Persist request.id until acknowledged. A conflict
783
+ * throws ProjectSyncError carrying the complete conflict/recovery operation.
784
+ */
785
+ switchTo(target, request, options = {}) {
786
+ const intent = JSON.stringify(request);
787
+ const old = this.flights.get(request.id);
788
+ if (old) {
789
+ if (old.intent !== intent || old.target !== target) return Promise.reject(new MindwireError("This sync ID already belongs to a different request or destination."));
790
+ return old.result;
791
+ }
792
+ const work = async () => {
793
+ if (target === this || target.mw.http === this.mw.http) throw new MindwireError("Choose another workspace for this project.");
794
+ let accepted;
795
+ try {
796
+ accepted = await target.get(`${request.id}-import`, options);
797
+ } catch (error) {
798
+ if (!(error instanceof ApiError) || error.status !== 404) throw error;
799
+ }
800
+ if (accepted) {
801
+ if ((accepted.path ?? "") !== (request.path ?? "")) throw new MindwireError("This sync ID belongs to a different destination folder.");
802
+ const operation = await target.wait(accepted.id, {
803
+ signal: options.signal,
804
+ onUpdate: (operation2) => options.onProgress?.({ phase: "apply", transferredBytes: 0, operation: operation2 })
805
+ });
806
+ if (operation.status !== "succeeded") throw new ProjectSyncError(operation);
807
+ return operation;
808
+ }
809
+ const health = await Promise.all([this.mw.health(), target.mw.health()]);
810
+ if (health.some((h) => (h.projectSyncVersion ?? 0) < 1)) throw new MindwireError("Update Mindwire on both workspaces to switch this project.");
811
+ let bytes = 0;
812
+ const emit2 = (phase, operation) => options.onProgress?.({ phase, transferredBytes: bytes, operation });
813
+ emit2("checkpoint");
814
+ let o = await this.export({ id: `${request.id}-export`, projectId: request.projectId }, options);
815
+ o = await this.wait(o.id, { signal: options.signal, onUpdate: (value) => emit2("checkpoint", value) });
816
+ if (o.status !== "succeeded" || !o.resultCheckpointId) throw new ProjectSyncError(o);
817
+ emit2("transfer");
818
+ await this.relayCheckpoint(target, o.resultCheckpointId, { signal: options.signal, onBytes: (n) => {
819
+ bytes += n;
820
+ emit2("transfer");
821
+ } });
822
+ o = await target.import({ id: `${request.id}-import`, checkpointId: o.resultCheckpointId, path: request.path }, options);
823
+ o = await target.wait(o.id, { signal: options.signal, onUpdate: (value) => emit2("apply", value) });
824
+ if (o.status !== "succeeded") throw new ProjectSyncError(o);
825
+ return o;
826
+ };
827
+ const result = work().finally(() => this.flights.delete(request.id));
828
+ this.flights.set(request.id, { intent, target, result });
829
+ return result;
830
+ }
831
+ };
832
+
668
833
  // src/workspace.ts
669
834
  var ProjectOperationsApi = class {
670
835
  constructor(mw) {
@@ -736,6 +901,7 @@ var WorkspaceCollection = class {
736
901
  var WorkspaceApi = class {
737
902
  constructor(mw) {
738
903
  this.mw = mw;
904
+ this.sync = new ProjectSyncApi(mw);
739
905
  this.git = new GitAccessApi(mw);
740
906
  this.operations = new ProjectOperationsApi(mw);
741
907
  this.agents = new WorkspaceCollection(mw, "agents");
@@ -743,6 +909,7 @@ var WorkspaceApi = class {
743
909
  this.chats = new WorkspaceCollection(mw, "chats");
744
910
  }
745
911
  mw;
912
+ sync;
746
913
  git;
747
914
  operations;
748
915
  agents;
@@ -2022,7 +2189,7 @@ async function ensureComputer(directory, cliPath, patch = {}, onProgress, option
2022
2189
  let controller = await readJSON(path.join(directory, "computer-controller.json"));
2023
2190
  if (existing && controller && !compatible(controller) && await processStateAlive(controller)) {
2024
2191
  onProgress?.("Refreshing the background connection service\u2026");
2025
- const { handoffController } = await import('./controller-handoff-7S6SKFHX.js');
2192
+ const { handoffController } = await import('./controller-handoff-NFDOLPJL.js');
2026
2193
  await handoffController(directory, controller, config, await existing.computer.info());
2027
2194
  controller = void 0;
2028
2195
  }
@@ -2056,7 +2223,7 @@ async function ensureComputer(directory, cliPath, patch = {}, onProgress, option
2056
2223
  }
2057
2224
  }
2058
2225
  async function superviseComputer(directory) {
2059
- const controller = await import('./controller-CHJ5MMZS.js');
2226
+ const controller = await import('./controller-QBC6L7AT.js');
2060
2227
  await controller.superviseComputer(directory);
2061
2228
  }
2062
2229
  async function stopComputer(directory, force = false) {
@@ -2081,5 +2248,5 @@ async function stopComputer(directory, force = false) {
2081
2248
  }
2082
2249
 
2083
2250
  export { CONTROLLER_PROTOCOL, SDK_VERSION, acquireProcessLock, adoptProcess, computerClient, computerPairingQRData, computerPairingURI, currentProcessIdentity, defaultStateDirectory, directRoutes, ensureComputer, ensureDaemonBinary, ownChild, processIdentity, processStateAlive, readJSON, stopComputer, superviseComputer, writeJSON };
2084
- //# sourceMappingURL=chunk-5X5PAFM7.js.map
2085
- //# sourceMappingURL=chunk-5X5PAFM7.js.map
2251
+ //# sourceMappingURL=chunk-H2I3VRBS.js.map
2252
+ //# sourceMappingURL=chunk-H2I3VRBS.js.map