riftri 0.3.5
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/LICENSE +21 -0
- package/README.md +220 -0
- package/bin/riftri.js +77 -0
- package/lib/platform.js +134 -0
- package/lib/signals.js +27 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Riftri contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Riftri
|
|
2
|
+
|
|
3
|
+
**Lightweight Git workspaces for parallel development.**
|
|
4
|
+
|
|
5
|
+
Riftri creates real Git worktrees without eagerly storing another full physical
|
|
6
|
+
copy of every unchanged project file. It is designed for developers and coding
|
|
7
|
+
agents working on several tasks at once.
|
|
8
|
+
|
|
9
|
+
[](https://github.com/assistant-ui/riftri/actions/workflows/ci.yml)
|
|
10
|
+
|
|
11
|
+
> [!WARNING]
|
|
12
|
+
> Riftri is experimental, pre-release software. Keep important work committed or
|
|
13
|
+
> backed up. Optimized worktrees need APFS on macOS, Btrfs or reflink-enabled
|
|
14
|
+
> XFS or OverlayFS on Linux, or ReFS on Windows — see
|
|
15
|
+
> [what works today](#what-works-today).
|
|
16
|
+
|
|
17
|
+
## Why Riftri?
|
|
18
|
+
|
|
19
|
+
Git linked worktrees share repository history, but each worktree normally gets
|
|
20
|
+
its own complete checkout. That can consume significant disk space when several
|
|
21
|
+
developers or agents work on a large repository in parallel.
|
|
22
|
+
|
|
23
|
+
Riftri keeps normal Git behavior while making those checkouts lightweight:
|
|
24
|
+
|
|
25
|
+
- Every workspace is a real Git linked worktree.
|
|
26
|
+
- Unchanged files share native copy-on-write storage blocks.
|
|
27
|
+
- Changes remain private to each worktree.
|
|
28
|
+
- Editors, build tools, and agents use ordinary files and Git commands.
|
|
29
|
+
- Interrupted creation and cleanup operations can be recovered safely.
|
|
30
|
+
|
|
31
|
+
Riftri does not replace Git, manage branches, or sit between applications and
|
|
32
|
+
the filesystem. For how it compares to plain `git worktree`, reference clones,
|
|
33
|
+
manual reflink copies, and per-agent containers, see the
|
|
34
|
+
[comparison with alternatives](docs/comparison.md).
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
macOS and Linux:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
curl -fsSL https://riftri.dev/install.sh | bash
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Windows PowerShell:
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
$Installer = Join-Path $env:TEMP 'riftri-install.ps1'
|
|
48
|
+
Invoke-WebRequest https://riftri.dev/install.ps1 -OutFile $Installer
|
|
49
|
+
& $Installer
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Both installers verify SHA-256 and the binary version, install to a per-user
|
|
53
|
+
directory, and never use sudo, edit shell profiles, or activate Git
|
|
54
|
+
interception. Node.js is not required. Read
|
|
55
|
+
[`install.sh`](package/install.sh) or [`install.ps1`](package/install.ps1)
|
|
56
|
+
first if you prefer, and see the [installation guide](docs/install.md) for all
|
|
57
|
+
eight targets, manual downloads, and checksum verification.
|
|
58
|
+
|
|
59
|
+
Or build from source:
|
|
60
|
+
|
|
61
|
+
```console
|
|
62
|
+
$ cargo build --release -p riftri-cli
|
|
63
|
+
$ ./target/release/riftri doctor
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The interactive `riftri setup` terminal UI is behind an off-by-default `tui`
|
|
67
|
+
feature; add `--features tui` to build it in (the published binaries do). The
|
|
68
|
+
default build keeps `setup` working through plain text prompts.
|
|
69
|
+
|
|
70
|
+
The npm launcher is a separate distribution channel. It is not currently
|
|
71
|
+
available because the initial publication is incomplete; use the verified
|
|
72
|
+
standalone installer or GitHub release assets for now.
|
|
73
|
+
|
|
74
|
+
## Quick start
|
|
75
|
+
|
|
76
|
+
For an interactive walkthrough (v0.3.1 and later), run
|
|
77
|
+
`riftri setup` inside your existing repository. It checks the destination,
|
|
78
|
+
confirms a new worktree at `HEAD`, and then asks whether to open Claude Code,
|
|
79
|
+
Codex, another installed executable, or no agent. Repository enablement and
|
|
80
|
+
agent launch require a separate confirmation. See the
|
|
81
|
+
[setup reference](docs/cli.md#riftri-setup-options).
|
|
82
|
+
|
|
83
|
+
The explicit commands below work in the current release and in automation.
|
|
84
|
+
|
|
85
|
+
Check that the repository and destination are compatible — this creates
|
|
86
|
+
nothing:
|
|
87
|
+
|
|
88
|
+
```console
|
|
89
|
+
$ riftri doctor --destination ../app-auth
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then create a worktree:
|
|
93
|
+
|
|
94
|
+
```console
|
|
95
|
+
$ riftri worktree add ../app-auth -b feature/auth main
|
|
96
|
+
$ cd ../app-auth
|
|
97
|
+
$ git status
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
To attach an existing branch instead, drop the `-b` flag:
|
|
101
|
+
|
|
102
|
+
```console
|
|
103
|
+
$ riftri worktree add ../app-auth feature/auth
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The new directory behaves like any other Git worktree.
|
|
107
|
+
|
|
108
|
+
## Use normal `git worktree` commands
|
|
109
|
+
|
|
110
|
+
Activate the Git shim in your shell, then opt in each repository:
|
|
111
|
+
|
|
112
|
+
```console
|
|
113
|
+
$ eval "$(riftri shell hook zsh)"
|
|
114
|
+
$ cd app
|
|
115
|
+
$ riftri enable
|
|
116
|
+
$ git worktree add -b feature/auth ../app-auth main
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
For a single agent or command tree, skip the shell hook entirely:
|
|
120
|
+
|
|
121
|
+
```console
|
|
122
|
+
$ riftri enable
|
|
123
|
+
$ riftri exec -- claude
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Activating the shell hook does **not** enable Riftri everywhere: `riftri enable`
|
|
127
|
+
is still required per repository, and other repositories go straight to real
|
|
128
|
+
Git. Use `riftri disable` to opt out and `riftri shell status` to inspect
|
|
129
|
+
activation.
|
|
130
|
+
|
|
131
|
+
PowerShell setup, IDE and container edge cases, and agent harness integration
|
|
132
|
+
are covered in [global activation](docs/global-activation.md) and the
|
|
133
|
+
[agent integration guide](docs/agent-integration.md).
|
|
134
|
+
|
|
135
|
+
## What works today
|
|
136
|
+
|
|
137
|
+
| Platform | Backend | Status |
|
|
138
|
+
| --- | --- | --- |
|
|
139
|
+
| macOS | APFS clones | Supported |
|
|
140
|
+
| Linux | Btrfs / reflink-enabled XFS | Supported |
|
|
141
|
+
| Linux | OverlayFS | Experimental |
|
|
142
|
+
| Windows | ReFS block clones | Supported |
|
|
143
|
+
|
|
144
|
+
On those backends Riftri creates real linked worktrees, intercepts Git per
|
|
145
|
+
repository or per process, removes, moves, prunes, and compacts them, reuses
|
|
146
|
+
immutable bases with disk accounting, and recovers from interruption through
|
|
147
|
+
journaled repair and garbage collection.
|
|
148
|
+
|
|
149
|
+
OverlayFS additionally needs either a mount-capable namespace or a one-time
|
|
150
|
+
helper. The helper is installed system-wide but intercepts nothing on its own,
|
|
151
|
+
and `riftri enable` remains a separate per-repository choice:
|
|
152
|
+
|
|
153
|
+
```console
|
|
154
|
+
$ sudo riftri overlayfs install-helper
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Riftri fails closed.** When a checkout cannot be reproduced exactly — custom
|
|
158
|
+
filters, sparse checkout, submodules, external attributes, checkout hooks, or non-canonical
|
|
159
|
+
[Git LFS](docs/git-lfs.md) setups — it stops before changing anything rather
|
|
160
|
+
than silently falling back to a full copy. See
|
|
161
|
+
[how Riftri stays safe](docs/safety.md).
|
|
162
|
+
|
|
163
|
+
## Common commands
|
|
164
|
+
|
|
165
|
+
```console
|
|
166
|
+
$ riftri doctor # check compatibility
|
|
167
|
+
$ riftri worktree list # managed worktrees and their disk use
|
|
168
|
+
$ riftri status # bases, references, and accounting
|
|
169
|
+
$ riftri worktree compact ../app-auth
|
|
170
|
+
$ riftri repair # resume or roll back interrupted work
|
|
171
|
+
$ riftri gc --apply # delete unreferenced bases
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Explicit lifecycle and diagnostic commands accept `--json` and `--json-errors`
|
|
175
|
+
for structured success and failure receipts, so automation does not have to
|
|
176
|
+
scrape human text. Interactive `setup` is for terminal users, not automation.
|
|
177
|
+
The full reference is in [docs/cli.md](docs/cli.md).
|
|
178
|
+
|
|
179
|
+
## Optional agent skill
|
|
180
|
+
|
|
181
|
+
The repository includes a reviewable
|
|
182
|
+
[`riftri-worktrees` skill](skills/riftri-worktrees/SKILL.md) for agents that need
|
|
183
|
+
to create or use Riftri worktrees. It explains explicit creation, process-scoped
|
|
184
|
+
Git interception, and safe cleanup; it is not required for copy-on-write safety
|
|
185
|
+
and does not install Riftri or enable a repository.
|
|
186
|
+
|
|
187
|
+
Install it through the [skills CLI](https://skills.sh/docs/cli):
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
npx skills add assistant-ui/riftri --skill riftri-worktrees
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Review the skill first and choose the agent and installation scope in the CLI.
|
|
194
|
+
The skill files are hosted here on GitHub; [skills.sh](https://skills.sh/docs/faq)
|
|
195
|
+
discovers and ranks skills through CLI installation telemetry. A directory
|
|
196
|
+
listing is not a separate package publication or a guarantee of inclusion.
|
|
197
|
+
|
|
198
|
+
## Documentation
|
|
199
|
+
|
|
200
|
+
[docs/README.md](docs/README.md) indexes everything. Start with:
|
|
201
|
+
|
|
202
|
+
- [Architecture](docs/architecture.md) — immutable bases, transactions, journals
|
|
203
|
+
- [How Riftri stays safe](docs/safety.md) — fail-closed rules and real-filesystem CI
|
|
204
|
+
- [Troubleshooting](docs/troubleshooting.md) — symptom-first fixes
|
|
205
|
+
- [CLI reference](docs/cli.md) · [Installation](docs/install.md) · [Benchmarks](docs/benchmarks.md)
|
|
206
|
+
- [Project definition](PROJECT.md) · [Roadmap](ROADMAP.md) · [Releasing](RELEASING.md)
|
|
207
|
+
|
|
208
|
+
## Contributing
|
|
209
|
+
|
|
210
|
+
Run the complete local quality gate before opening a pull request:
|
|
211
|
+
|
|
212
|
+
```console
|
|
213
|
+
$ cargo fmt --all --check
|
|
214
|
+
$ cargo clippy --workspace --all-targets --all-features -- -D warnings
|
|
215
|
+
$ cargo test --workspace
|
|
216
|
+
$ npm test
|
|
217
|
+
$ npm run smoke:installed
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Riftri is licensed under the [MIT License](LICENSE).
|
package/bin/riftri.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { spawn } = require("node:child_process");
|
|
6
|
+
const { resolveBinary } = require("../lib/platform.js");
|
|
7
|
+
const { signalExitCode } = require("../lib/signals.js");
|
|
8
|
+
|
|
9
|
+
function fail(message) {
|
|
10
|
+
process.stderr.write(`riftri: ${message}\n`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let binary;
|
|
15
|
+
try {
|
|
16
|
+
binary = resolveBinary();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const child = spawn(binary, process.argv.slice(2), {
|
|
22
|
+
stdio: "inherit",
|
|
23
|
+
windowsHide: false,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Mirror the termination contract of native `riftri exec` (docs/cli.md) while
|
|
27
|
+
// the native process runs. The launcher survives SIGINT (Ctrl-C) and SIGQUIT
|
|
28
|
+
// (Ctrl-\): a terminal delivers both to the whole foreground process group,
|
|
29
|
+
// so the native process receives its own copy and — like a shell waiting on a
|
|
30
|
+
// foreground job — the command alone decides whether the interrupt is fatal.
|
|
31
|
+
// Dying here instead would return the shell prompt while riftri and a command
|
|
32
|
+
// that caught the interrupt keep running on the terminal. PID-directed
|
|
33
|
+
// SIGTERM and SIGHUP are forwarded to the native process, which applies the
|
|
34
|
+
// same contract downstream to the scoped command. Windows has no equivalent
|
|
35
|
+
// to preserve: the console already delivers Ctrl-C events to every attached
|
|
36
|
+
// process, and a hard TerminateProcess cannot be intercepted.
|
|
37
|
+
const ignoredSignals = ["SIGINT", "SIGQUIT"];
|
|
38
|
+
const forwardedSignals = ["SIGTERM", "SIGHUP"];
|
|
39
|
+
const ignoreSignal = () => {};
|
|
40
|
+
const forwardSignal = (signal) => {
|
|
41
|
+
child.kill(signal);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (process.platform !== "win32") {
|
|
45
|
+
for (const signal of ignoredSignals) {
|
|
46
|
+
process.on(signal, ignoreSignal);
|
|
47
|
+
}
|
|
48
|
+
for (const signal of forwardedSignals) {
|
|
49
|
+
process.on(signal, forwardSignal);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function releaseSignals() {
|
|
54
|
+
for (const signal of ignoredSignals) {
|
|
55
|
+
process.removeListener(signal, ignoreSignal);
|
|
56
|
+
}
|
|
57
|
+
for (const signal of forwardedSignals) {
|
|
58
|
+
process.removeListener(signal, forwardSignal);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
child.on("error", (error) => {
|
|
63
|
+
releaseSignals();
|
|
64
|
+
fail(`could not start the native executable at ${binary}: ${error.message}`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
child.on("exit", (code, signal) => {
|
|
68
|
+
releaseSignals();
|
|
69
|
+
if (signal !== null) {
|
|
70
|
+
const exitCode = signalExitCode(signal);
|
|
71
|
+
if (exitCode === null) {
|
|
72
|
+
fail(`native executable stopped after unknown signal ${signal}`);
|
|
73
|
+
}
|
|
74
|
+
process.exit(exitCode);
|
|
75
|
+
}
|
|
76
|
+
process.exit(code ?? 1);
|
|
77
|
+
});
|
package/lib/platform.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
const PLATFORM_PACKAGES = Object.freeze({
|
|
7
|
+
"darwin-arm64": "riftri-darwin-arm64",
|
|
8
|
+
"darwin-x64": "riftri-darwin-x64",
|
|
9
|
+
"linux-arm64-gnu": "riftri-linux-arm64-gnu",
|
|
10
|
+
"linux-arm64-musl": "riftri-linux-arm64-musl",
|
|
11
|
+
"linux-x64-gnu": "riftri-linux-x64-gnu",
|
|
12
|
+
"linux-x64-musl": "riftri-linux-x64-musl",
|
|
13
|
+
"win32-arm64": "riftri-win32-arm64",
|
|
14
|
+
"win32-x64": "riftri-win32-x64",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function detectLinuxLibc(report = process.report) {
|
|
18
|
+
if (!report || typeof report.getReport !== "function") {
|
|
19
|
+
return "unknown";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const header = report.getReport()?.header;
|
|
23
|
+
return header?.glibcVersionRuntime ? "gnu" : "musl";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function platformKey(
|
|
27
|
+
platform = process.platform,
|
|
28
|
+
architecture = process.arch,
|
|
29
|
+
report = process.report,
|
|
30
|
+
) {
|
|
31
|
+
if (platform === "linux") {
|
|
32
|
+
return `${platform}-${architecture}-${detectLinuxLibc(report)}`;
|
|
33
|
+
}
|
|
34
|
+
return `${platform}-${architecture}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function packageNameForPlatform(platform, architecture, report) {
|
|
38
|
+
return PLATFORM_PACKAGES[platformKey(platform, architecture, report)] ?? null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function binaryName(platform = process.platform) {
|
|
42
|
+
return platform === "win32" ? "riftri.exe" : "riftri";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function assertUsableBinary(candidate, source) {
|
|
46
|
+
try {
|
|
47
|
+
fs.accessSync(
|
|
48
|
+
candidate,
|
|
49
|
+
process.platform === "win32" ? fs.constants.F_OK : fs.constants.X_OK,
|
|
50
|
+
);
|
|
51
|
+
} catch {
|
|
52
|
+
throw new Error(`${source} does not contain an executable Riftri binary: ${candidate}`);
|
|
53
|
+
}
|
|
54
|
+
return candidate;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function launcherVersion() {
|
|
58
|
+
for (const candidate of [
|
|
59
|
+
path.join(__dirname, "..", "package.json"),
|
|
60
|
+
path.join(__dirname, "..", "..", "package.json"),
|
|
61
|
+
]) {
|
|
62
|
+
try {
|
|
63
|
+
const manifest = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
64
|
+
if (manifest.name === "riftri" && typeof manifest.version === "string") {
|
|
65
|
+
return manifest.version;
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
// The source and staged package layouts put the root manifest at different depths.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
throw new Error("could not read the riftri launcher version");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function assertCompatiblePackageManifest(manifest, packageName, expectedVersion) {
|
|
75
|
+
if (manifest.name !== packageName || manifest.version !== expectedVersion) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`the optional native package ${packageName} does not match riftri ${expectedVersion}; reinstall riftri and its optional dependencies together`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveBinary(options = {}) {
|
|
83
|
+
const environment = options.environment ?? process.env;
|
|
84
|
+
const platform = options.platform ?? process.platform;
|
|
85
|
+
const architecture = options.architecture ?? process.arch;
|
|
86
|
+
const report = options.report ?? process.report;
|
|
87
|
+
|
|
88
|
+
if (environment.RIFTRI_BINARY) {
|
|
89
|
+
const override = path.resolve(environment.RIFTRI_BINARY);
|
|
90
|
+
return assertUsableBinary(override, "RIFTRI_BINARY");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const packageName = packageNameForPlatform(platform, architecture, report);
|
|
94
|
+
if (!packageName) {
|
|
95
|
+
const key = platformKey(platform, architecture, report);
|
|
96
|
+
throw new Error(
|
|
97
|
+
`no prebuilt native package is available for ${key}; supported packages: ${Object.keys(PLATFORM_PACKAGES).join(", ")}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let packageJson;
|
|
102
|
+
try {
|
|
103
|
+
packageJson = require.resolve(`${packageName}/package.json`);
|
|
104
|
+
} catch {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`the optional native package ${packageName} is missing; reinstall without --omit=optional, or build Riftri from source`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let packageManifest;
|
|
111
|
+
try {
|
|
112
|
+
packageManifest = JSON.parse(fs.readFileSync(packageJson, "utf8"));
|
|
113
|
+
} catch {
|
|
114
|
+
throw new Error(`the optional native package ${packageName} has an invalid manifest`);
|
|
115
|
+
}
|
|
116
|
+
assertCompatiblePackageManifest(
|
|
117
|
+
packageManifest,
|
|
118
|
+
packageName,
|
|
119
|
+
options.expectedVersion ?? launcherVersion(),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const candidate = path.join(path.dirname(packageJson), "bin", binaryName(platform));
|
|
123
|
+
return assertUsableBinary(candidate, packageName);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
PLATFORM_PACKAGES,
|
|
128
|
+
assertCompatiblePackageManifest,
|
|
129
|
+
binaryName,
|
|
130
|
+
detectLinuxLibc,
|
|
131
|
+
packageNameForPlatform,
|
|
132
|
+
platformKey,
|
|
133
|
+
resolveBinary,
|
|
134
|
+
};
|
package/lib/signals.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const os = require("node:os");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Exit code the launcher reports for a native process that died from `signal`,
|
|
7
|
+
* following the shell convention native `riftri exec` itself uses: 128 plus
|
|
8
|
+
* the signal's number on this platform. The code is computed numerically —
|
|
9
|
+
* never by re-raising the signal through `process.kill`, which is a silent
|
|
10
|
+
* no-op for signals Node ignores or reserves (SIGPIPE, SIGUSR1, SIGWINCH,
|
|
11
|
+
* SIGCHLD, SIGURG) and would turn a killed run into exit 0.
|
|
12
|
+
*
|
|
13
|
+
* Returns `null` when the name is not a signal this platform knows, so the
|
|
14
|
+
* caller can report the failure instead of guessing a number.
|
|
15
|
+
*/
|
|
16
|
+
function signalExitCode(signal) {
|
|
17
|
+
if (typeof signal !== "string") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const number = os.constants.signals[signal];
|
|
21
|
+
if (!Number.isInteger(number) || number <= 0) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return 128 + number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { signalExitCode };
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "riftri",
|
|
3
|
+
"version": "0.3.5",
|
|
4
|
+
"description": "Experimental lightweight Git workspaces for parallel development",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Assistant UI contributors",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"bin": {
|
|
9
|
+
"riftri": "bin/riftri.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin",
|
|
13
|
+
"lib",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18.18"
|
|
19
|
+
},
|
|
20
|
+
"optionalDependencies": {
|
|
21
|
+
"riftri-darwin-arm64": "0.3.5",
|
|
22
|
+
"riftri-darwin-x64": "0.3.5",
|
|
23
|
+
"riftri-linux-arm64-gnu": "0.3.5",
|
|
24
|
+
"riftri-linux-arm64-musl": "0.3.5",
|
|
25
|
+
"riftri-linux-x64-gnu": "0.3.5",
|
|
26
|
+
"riftri-linux-x64-musl": "0.3.5",
|
|
27
|
+
"riftri-win32-arm64": "0.3.5",
|
|
28
|
+
"riftri-win32-x64": "0.3.5"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/assistant-ui/riftri.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/assistant-ui/riftri/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://riftri.dev",
|
|
38
|
+
"keywords": [
|
|
39
|
+
"git",
|
|
40
|
+
"worktree",
|
|
41
|
+
"copy-on-write",
|
|
42
|
+
"developer-tools",
|
|
43
|
+
"coding-agents"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"provenance": true
|
|
48
|
+
}
|
|
49
|
+
}
|