riftri 0.6.2 → 0.6.3

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/lib/client.d.ts CHANGED
@@ -5,6 +5,9 @@ export const EXIT_OPERATIONAL: 1;
5
5
  export const EXIT_USAGE: 2;
6
6
  export const EXIT_POLICY: 3;
7
7
 
8
+ /** Resolve and validate the installed native executable, honoring RIFTRI_BINARY. Does not spawn. */
9
+ export function resolveBinary(): string;
10
+
8
11
  /** One versioned failure receipt, as emitted by `--json-errors`. */
9
12
  export interface FailureReceipt {
10
13
  schemaVersion: number;
@@ -37,7 +40,9 @@ export class RiftriError extends Error {
37
40
  /** The process was killed rather than exiting on its own. */
38
41
  readonly wasSignalled: boolean;
39
42
  readonly receipt: FailureReceipt | null;
40
- /** Riftri declined before touching anything; falling back is safe. */
43
+ /** Parsed stdout on a nonzero exit (for example a failed post-checkout hook), or null. Validate before use. */
44
+ readonly report: unknown;
45
+ /** A native policy receipt confirms no mutation; an exit code alone is insufficient. */
41
46
  readonly isPolicyRefusal: boolean;
42
47
  /** The request was malformed; never retry it unchanged. */
43
48
  readonly isUsageError: boolean;
@@ -110,6 +115,28 @@ export interface WorktreeInventory {
110
115
  native_path_encoding: string;
111
116
  }
112
117
 
118
+ export interface WorktreeOwner {
119
+ schema_version: 1;
120
+ /** Null only when no registered state claims the path. Errors reject. */
121
+ state_directory: string | null;
122
+ state_directory_native_hex: string | null;
123
+ native_path_encoding: string;
124
+ }
125
+
126
+ export interface WorktreeInspectionReport {
127
+ schema_version: 1;
128
+ native_path_encoding: string;
129
+ worktrees: Array<{
130
+ path: string;
131
+ path_native_hex: string;
132
+ state_directory: string | null;
133
+ state_directory_native_hex: string | null;
134
+ backend: string | null;
135
+ /** Null unless the managed backend needs a mount. Inspection never repairs. */
136
+ mount_status: "active" | "recovery-required" | "different-namespace" | "foreign" | "unavailable" | null;
137
+ }>;
138
+ }
139
+
113
140
  export interface StateDiagnosticIssue {
114
141
  path: string;
115
142
  path_native_hex: string;
@@ -239,6 +266,10 @@ export class Riftri {
239
266
 
240
267
  readonly worktree: {
241
268
  add(destination: string, options?: AddOptions): Promise<AddReport>;
269
+ /** Advisory ownership lookup without disk accounting; mutations revalidate. */
270
+ owner(destination: string): Promise<WorktreeOwner>;
271
+ /** One read-only discovery pass; paths retain the requested order. */
272
+ inspect(destinations: readonly string[]): Promise<WorktreeInspectionReport>;
242
273
  list(options: { allStates: true }): Promise<AllStatesWorktreeInventory>;
243
274
  list(options?: { allStates?: false }): Promise<WorktreeInventory>;
244
275
  /** Narrow schema_version when allStates is a runtime boolean. */
package/lib/client.js CHANGED
@@ -29,7 +29,7 @@ const REPORTING = new Set([
29
29
  ]);
30
30
 
31
31
  class RiftriError extends Error {
32
- constructor(exitCode, receipt, stderr, signal = null) {
32
+ constructor(exitCode, receipt, stderr, signal = null, report = null) {
33
33
  super(
34
34
  // JSON is not a guarantee of a valid receipt. Coercing an object here
35
35
  // can throw inside the child close handler instead of rejecting run().
@@ -43,6 +43,7 @@ class RiftriError extends Error {
43
43
  this.exitCode = exitCode;
44
44
  this.signal = signal;
45
45
  this.receipt = receipt ?? null;
46
+ this.report = report;
46
47
  }
47
48
 
48
49
  /** The process was killed rather than exiting on its own. */
@@ -52,7 +53,12 @@ class RiftriError extends Error {
52
53
 
53
54
  /** Riftri declined before touching anything. Falling back is safe. */
54
55
  get isPolicyRefusal() {
55
- return this.exitCode === EXIT_POLICY;
56
+ // Hooks and `exec` children can return 3 after performing work. Only a
57
+ // native refusal receipt proves the operation stopped before mutation.
58
+ return this.exitCode === EXIT_POLICY && this.signal === null && this.report === null &&
59
+ this.receipt?.schemaVersion === 1 && this.receipt?.outcome === "failed" &&
60
+ this.receipt?.category === "policy" && this.receipt?.cleanup === "not-needed" &&
61
+ typeof this.receipt?.code === "string";
56
62
  }
57
63
 
58
64
  /** The request itself was malformed. Never retry unchanged. */
@@ -120,6 +126,8 @@ class Riftri {
120
126
  this.worktree = {
121
127
  add: this.#worktreeAdd.bind(this),
122
128
  list: this.#worktreeList.bind(this),
129
+ owner: this.#worktreeOwner.bind(this),
130
+ inspect: this.#worktreeInspect.bind(this),
123
131
  remove: this.#worktreeRemove.bind(this),
124
132
  move: this.#worktreeMove.bind(this),
125
133
  compact: this.#worktreeCompact.bind(this),
@@ -212,7 +220,18 @@ class Riftri {
212
220
  // Plain text, as from a binary that predates usage receipts.
213
221
  }
214
222
  }
215
- reject(new RiftriError(exitCode, receipt, stderr));
223
+ let report = null;
224
+ if (json && stdout.trim()) {
225
+ // A failed post-checkout hook leaves a created worktree in place.
226
+ // Preserve that report even though the hook's status rejects run().
227
+ try {
228
+ report = JSON.parse(stdout);
229
+ } catch {
230
+ // Malformed output cannot prove that a fallback is safe either.
231
+ }
232
+ receipt = null;
233
+ }
234
+ reject(new RiftriError(exitCode, receipt, stderr, null, report));
216
235
  });
217
236
  });
218
237
  }
@@ -300,6 +319,15 @@ class Riftri {
300
319
  return this.run(["worktree", "list", ...flagsFor(options), ...state]);
301
320
  }
302
321
 
322
+ #worktreeOwner(destination) {
323
+ // Ownership always consults every registered state, not this.stateDir.
324
+ return this.run(["worktree", "owner", "--", destination]);
325
+ }
326
+
327
+ #worktreeInspect(destinations) {
328
+ return this.run(["worktree", "inspect", "--", ...destinations]);
329
+ }
330
+
303
331
  #worktreeRemove(destination, { force = false } = {}) {
304
332
  const args = ["worktree", "remove"];
305
333
  if (force) args.push("--force", "--yes");
@@ -322,6 +350,7 @@ class Riftri {
322
350
  module.exports = {
323
351
  Riftri,
324
352
  RiftriError,
353
+ resolveBinary,
325
354
  EXIT_SUCCESS,
326
355
  EXIT_OPERATIONAL,
327
356
  EXIT_USAGE,
package/lib/platform.js CHANGED
@@ -75,6 +75,17 @@ function assertUsableBinary(candidate, source) {
75
75
  return candidate;
76
76
  }
77
77
 
78
+ function unpackedElectronBinary(candidate) {
79
+ if (!process.versions.electron) return candidate;
80
+ const segments = candidate.split(path.sep);
81
+ const archive = segments.findLastIndex((segment) => segment.endsWith(".asar"));
82
+ if (archive < 0) return candidate;
83
+ // Electron can read an archived executable, but the OS cannot spawn it.
84
+ // Validate the physical file below instead of relying on Electron's virtual fs.
85
+ segments[archive] += ".unpacked";
86
+ return segments.join(path.sep);
87
+ }
88
+
78
89
  function launcherVersion() {
79
90
  for (const candidate of [
80
91
  path.join(__dirname, "..", "package.json"),
@@ -141,7 +152,7 @@ function resolveBinary(options = {}) {
141
152
  );
142
153
 
143
154
  const candidate = path.join(path.dirname(packageJson), "bin", binaryName(platform));
144
- return assertUsableBinary(candidate, packageName);
155
+ return assertUsableBinary(unpackedElectronBinary(candidate), packageName);
145
156
  }
146
157
 
147
158
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "riftri",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Experimental lightweight Git workspaces for parallel development",
5
5
  "license": "MIT",
6
6
  "author": "Assistant UI contributors",
@@ -22,14 +22,14 @@
22
22
  "typescript": "5.9.3"
23
23
  },
24
24
  "optionalDependencies": {
25
- "riftri-darwin-arm64": "0.6.2",
26
- "riftri-darwin-x64": "0.6.2",
27
- "riftri-linux-arm64-gnu": "0.6.2",
28
- "riftri-linux-arm64-musl": "0.6.2",
29
- "riftri-linux-x64-gnu": "0.6.2",
30
- "riftri-linux-x64-musl": "0.6.2",
31
- "riftri-win32-arm64": "0.6.2",
32
- "riftri-win32-x64": "0.6.2"
25
+ "riftri-darwin-arm64": "0.6.3",
26
+ "riftri-darwin-x64": "0.6.3",
27
+ "riftri-linux-arm64-gnu": "0.6.3",
28
+ "riftri-linux-arm64-musl": "0.6.3",
29
+ "riftri-linux-x64-gnu": "0.6.3",
30
+ "riftri-linux-x64-musl": "0.6.3",
31
+ "riftri-win32-arm64": "0.6.3",
32
+ "riftri-win32-x64": "0.6.3"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",