@rebon/cli-win32-x64 0.1.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/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # rebon
2
+
3
+ An agent CLI for coding and more — a terminal harness that drives an LLM through
4
+ a real agent loop with tools, sessions, permissions, and a full TUI.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ npm install -g @rebon/cli
10
+ ```
11
+
12
+ Upgrade later:
13
+
14
+ ```bash
15
+ npm install -g @rebon/cli@latest
16
+ ```
17
+
18
+ `@rebon/cli` is a thin launcher that pulls the matching native binary as
19
+ an optional dependency. Supported platforms: `win32-x64`, `darwin-x64`,
20
+ `darwin-arm64`, `linux-x64`, `linux-arm64`.
21
+
22
+ > If install fails with "missing optional platform package", you likely passed
23
+ > `--omit=optional` or `--no-optional`. Reinstall without that flag.
24
+
25
+ ## Quick start
26
+
27
+ ```bash
28
+ # Local TUI in the current directory
29
+ rebon
30
+
31
+ # Resume a previous session
32
+ rebon --resume sess-0-1712937600000
33
+
34
+ # Override the active provider / model
35
+ rebon --provider openrouter --model gpt-5.5
36
+
37
+ # Run as an ACP JSON-RPC server on stdio (for editor / IDE integrations)
38
+ rebon --acp
39
+ ```
40
+
41
+ The first run drops you into onboarding: pick a provider, sign in (OAuth +
42
+ PKCE for Claude / OpenAI, or paste an API key), and you're in.
43
+
44
+ ## CLI flags
45
+
46
+ | Flag | Purpose |
47
+ | ------------------- | ------------------------------------------------------------ |
48
+ | `--acp` | Run as an ACP JSON-RPC server on stdio. |
49
+ | `--provider <name>` | Override `activeCustomProvider` from `~/.rebon/config.json`. |
50
+ | `--model <id>` | Override the resolved provider's default model. |
51
+ | `--resume <id>` | Load an on-disk transcript and replay it into the TUI. |
52
+
53
+ ## Config & data
54
+
55
+ - `~/.rebon/config.json` — providers, models, credentials, defaults.
56
+ - `~/.rebon/sessions/` — saved transcripts.
57
+ - `~/.rebon/skills/`, `~/.rebon/agents/`, `~/.rebon/memory/` — user assets.
58
+ - `$REBON_LOG_DIR/rebon.log` — TUI log file (defaults to
59
+ `%TEMP%/rebon/logs/rebon.log` on Windows, `$TMPDIR/rebon/logs/rebon.log`
60
+ elsewhere). `--acp` mode logs to stderr instead.
61
+
62
+ Project-level overrides live under `.rebon/` and `.claude/` in your working
63
+ directory.
64
+
65
+ ## What you get
66
+
67
+ - **Local TUI** — prompt input with history, paste, image paste, `@`-mention
68
+ and slash-command pickers, queued submissions, mode cycling; streaming
69
+ transcript with markdown, tool grouping, thinking blocks, plan approval,
70
+ permission modal; full dialog stack for onboarding, settings, resume,
71
+ rewind, quick-open, history search, global search, tasks, background
72
+ tasks, teams, agents.
73
+ - **Agentic tool loop** — Bash / PowerShell, Read / Write / Edit, Glob /
74
+ Grep, Sleep, TaskCreate / TaskUpdate / TaskList / TaskGet / TaskStop,
75
+ Agent, SkillTool, AskUserQuestion, SendMessage, EnterPlanMode /
76
+ ExitPlanMode, ToolSearch, Team{Create,Delete,Files,Mailbox,Manager},
77
+ Worktree, plus MCP tools (stdio, Streamable HTTP, legacy SSE).
78
+ - **Providers** — Anthropic, OpenAI, and OpenAI-Responses, with streaming,
79
+ compact / context-prune passes, and automatic session titles.
80
+ - **Permissions, hooks, sandbox** — fine-grained allow / deny for shell,
81
+ filesystem, web-fetch, and skill invocations; user-defined hooks; sandbox
82
+ config with violation reporting.
83
+ - **Skills, agents, memory** — bundled and user skills; spawnable
84
+ worker agents and a coordinator for background tasks; a persistent memory
85
+ layer with recall and surfacing.
86
+ - **ACP server** — drive Rebon from an editor over stdio JSON-RPC:
87
+ `initialize`, `session/new`, `session/load`, `session/list`,
88
+ `session/prompt`, `session/cancel`, reverse-RPC permission prompts,
89
+ streamed tool output.
90
+
91
+ ## Common tasks
92
+
93
+ ```bash
94
+ # Start a fresh session in the current repo
95
+ rebon
96
+
97
+ # Resume the last session you worked on (use Tab in the TUI to browse)
98
+ rebon --resume <session-id>
99
+
100
+ # Plug Rebon into an editor over ACP (the editor manages stdio for you)
101
+ rebon --acp
102
+
103
+ # Override provider/model without editing config
104
+ rebon --provider anthropic --model claude-opus-4-7
105
+ ```
106
+
107
+ Inside the TUI:
108
+
109
+ - `?` — keyboard help
110
+ - `/` — slash-command picker
111
+ - `@` — file / symbol mention
112
+ - `Tab` — switch panel / list
113
+ - `Esc` — cancel current step or dismiss a dialog
114
+
115
+ ## Updating
116
+
117
+ `rebon` ships with a built-in updater that checks for new releases on launch.
118
+ You can also just rerun:
119
+
120
+ ```bash
121
+ npm install -g @rebon/cli@latest
122
+ ```
123
+
124
+ ## Troubleshooting
125
+
126
+ - **"missing optional platform package"** — reinstall without
127
+ `--omit=optional` / `--no-optional` / `--ignore-optional`.
128
+ - **"unsupported platform"** — your `process.platform`/`process.arch` is not
129
+ one of the five supported targets.
130
+ - **TUI looks scrambled** — make sure your terminal supports truecolor and
131
+ Unicode width tables (modern Windows Terminal, iTerm2, Alacritty, WezTerm,
132
+ Kitty all work). The legacy `cmd.exe` and `conhost` are not supported.
133
+ - **Logs** — check `$REBON_LOG_DIR/rebon.log` (TUI) or stderr (`--acp`).
package/bin/rebon.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const {
5
+ readPackageInfo,
6
+ resolveExecutable,
7
+ spawnResolvedExecutable,
8
+ } = require("../lib/windows-managed");
9
+
10
+ const resolved = resolveExecutable(readPackageInfo());
11
+ if (!resolved.exe) {
12
+ const detail = resolved.installError ? ` (${resolved.installError.message})` : "";
13
+ console.error(`rebon: could not find installed rebon.exe${detail}. Try reinstalling the npm package without disabling scripts.`);
14
+ process.exit(1);
15
+ }
16
+
17
+ if (resolved.usedFallback) {
18
+ const detail = resolved.installError ? `: ${resolved.installError.message}` : "";
19
+ console.error(`rebon: using package payload fallback${detail}. Future npm upgrades may require closing this rebon process.`);
20
+ }
21
+
22
+ spawnResolvedExecutable(resolved, process.argv.slice(2), require("child_process").spawn);
@@ -0,0 +1,389 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const CURRENT_MANIFEST = "current.json";
7
+ const EXECUTABLE_NAME = "rebon.exe";
8
+ const PAYLOAD_DIR = "payload";
9
+ const VERSIONS_DIR = "versions";
10
+ const STAGING_DIR = "staging";
11
+
12
+ function defaultPackageRoot() {
13
+ return path.resolve(__dirname, "..");
14
+ }
15
+
16
+ function managedBinDir() {
17
+ const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE && path.join(process.env.USERPROFILE, ".rebon"));
18
+ if (!base) {
19
+ return null;
20
+ }
21
+ return path.join(base, "rebon", "bin");
22
+ }
23
+
24
+ function requireManagedBinDir() {
25
+ const binDir = managedBinDir();
26
+ if (!binDir) {
27
+ throw new Error("LOCALAPPDATA and USERPROFILE are both unset; cannot choose a per-user install directory");
28
+ }
29
+ return binDir;
30
+ }
31
+
32
+ function sanitizeVersionPart(value) {
33
+ return String(value || "unknown").replace(/[^0-9A-Za-z._-]/g, "_");
34
+ }
35
+
36
+ function firstString(candidates) {
37
+ for (const candidate of candidates) {
38
+ if (typeof candidate === "string" && candidate.length > 0) {
39
+ return candidate;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+
45
+ function packageBuildId(packageJson) {
46
+ const rebon = packageJson && packageJson.rebon;
47
+ return firstString([
48
+ rebon && rebon.build,
49
+ rebon && rebon.buildId,
50
+ rebon && rebon.commit,
51
+ rebon && rebon.hash,
52
+ packageJson && packageJson.rebonBuild,
53
+ packageJson && packageJson.rebonBuildId,
54
+ ]);
55
+ }
56
+
57
+ function versionKeyFromPackageJson(packageJson) {
58
+ const version = sanitizeVersionPart(packageJson && packageJson.version);
59
+ const buildId = packageBuildId(packageJson);
60
+ return buildId ? `${version}-${sanitizeVersionPart(buildId)}` : version;
61
+ }
62
+
63
+ function payloadSignature(payloadPath) {
64
+ const stat = fs.statSync(payloadPath);
65
+ return { size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) };
66
+ }
67
+
68
+ function readPackageInfo(packageRoot) {
69
+ const root = path.resolve(packageRoot || defaultPackageRoot());
70
+ const packageJson = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
71
+ const payloadDir = path.join(root, PAYLOAD_DIR);
72
+ const payloadPath = path.join(payloadDir, EXECUTABLE_NAME);
73
+ return {
74
+ packageRoot: root,
75
+ name: String(packageJson.name || "rebon"),
76
+ version: String(packageJson.version || "unknown"),
77
+ versionKey: versionKeyFromPackageJson(packageJson),
78
+ payloadDir,
79
+ payloadPath,
80
+ payload: fs.existsSync(payloadPath) ? payloadSignature(payloadPath) : null,
81
+ };
82
+ }
83
+
84
+ function managedRelativeExecutable(versionKey) {
85
+ return `${VERSIONS_DIR}/${versionKey}/${EXECUTABLE_NAME}`;
86
+ }
87
+
88
+ function payloadSpecificVersionKey(packageInfo) {
89
+ if (!packageInfo.payload) {
90
+ return packageInfo.versionKey;
91
+ }
92
+ return `${packageInfo.versionKey}-${packageInfo.payload.size}-${packageInfo.payload.mtimeMs}`;
93
+ }
94
+
95
+ function acceptedVersionKeys(packageInfo) {
96
+ const keys = [packageInfo.versionKey];
97
+ const payloadKey = payloadSpecificVersionKey(packageInfo);
98
+ if (payloadKey !== packageInfo.versionKey) {
99
+ keys.push(payloadKey);
100
+ }
101
+ return keys;
102
+ }
103
+
104
+ function layoutForVersion(binDir, versionKey) {
105
+ const versionDir = path.join(binDir, VERSIONS_DIR, versionKey);
106
+ const stagingDir = path.join(binDir, STAGING_DIR, versionKey);
107
+ return {
108
+ versionDir,
109
+ stagingDir,
110
+ exePath: path.join(versionDir, EXECUTABLE_NAME),
111
+ manifestPath: path.join(binDir, CURRENT_MANIFEST),
112
+ manifestRelativePath: managedRelativeExecutable(versionKey),
113
+ };
114
+ }
115
+
116
+ function normalizeManifestPath(value) {
117
+ return String(value || "").replace(/\\/g, "/").replace(/\/+/g, "/");
118
+ }
119
+
120
+ function resolveManifestPath(binDir, manifestPath) {
121
+ if (typeof manifestPath !== "string" || path.isAbsolute(manifestPath)) {
122
+ return null;
123
+ }
124
+
125
+ const resolved = path.resolve(binDir, manifestPath.replace(/[\\/]+/g, path.sep));
126
+ const relative = path.relative(path.resolve(binDir), resolved);
127
+ if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
128
+ return null;
129
+ }
130
+ return resolved;
131
+ }
132
+
133
+ function readCurrentManifest(binDir) {
134
+ try {
135
+ return JSON.parse(fs.readFileSync(path.join(binDir, CURRENT_MANIFEST), "utf8"));
136
+ } catch (_) {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ function payloadMatchesManifest(manifest, packageInfo) {
142
+ if (!packageInfo.payload) {
143
+ return true;
144
+ }
145
+ if (!manifest.payload) {
146
+ return false;
147
+ }
148
+ return (
149
+ Number(manifest.payload.size) === packageInfo.payload.size &&
150
+ Math.trunc(Number(manifest.payload.mtimeMs)) === packageInfo.payload.mtimeMs
151
+ );
152
+ }
153
+
154
+ function manifestMatchesPackage(manifest, binDir, packageInfo) {
155
+ if (!manifest) {
156
+ return false;
157
+ }
158
+ if (manifest.package !== packageInfo.name || manifest.version !== packageInfo.version) {
159
+ return false;
160
+ }
161
+ if (!acceptedVersionKeys(packageInfo).includes(manifest.versionKey)) {
162
+ return false;
163
+ }
164
+ if (normalizeManifestPath(manifest.path) !== managedRelativeExecutable(manifest.versionKey)) {
165
+ return false;
166
+ }
167
+
168
+ const resolved = resolveManifestPath(binDir, manifest.path);
169
+ return Boolean(resolved && fs.existsSync(resolved) && payloadMatchesManifest(manifest, packageInfo));
170
+ }
171
+
172
+ function readInstalledManifest(packageInfo) {
173
+ const binDir = managedBinDir();
174
+ if (!binDir) {
175
+ return null;
176
+ }
177
+
178
+ const manifest = readCurrentManifest(binDir);
179
+ if (!manifestMatchesPackage(manifest, binDir, packageInfo)) {
180
+ return null;
181
+ }
182
+ return manifest;
183
+ }
184
+
185
+ function readInstalledExecutable(packageInfo) {
186
+ const binDir = managedBinDir();
187
+ const manifest = readInstalledManifest(packageInfo);
188
+ if (!binDir || !manifest) {
189
+ return null;
190
+ }
191
+ return resolveManifestPath(binDir, manifest.path);
192
+ }
193
+
194
+ function writeManifest(manifestPath, manifest) {
195
+ const nonce = Math.random().toString(36).slice(2, 10);
196
+ const tmpPath = `${manifestPath}.${process.pid}.${nonce}.tmp`;
197
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
198
+ fs.writeFileSync(tmpPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
199
+ fs.renameSync(tmpPath, manifestPath);
200
+ }
201
+
202
+ function copyPayloadDirectory(srcDir, destDir) {
203
+ fs.mkdirSync(destDir, { recursive: true });
204
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
205
+ for (const entry of entries) {
206
+ const src = path.join(srcDir, entry.name);
207
+ const dest = path.join(destDir, entry.name);
208
+ if (entry.isDirectory()) {
209
+ copyPayloadDirectory(src, dest);
210
+ } else if (entry.isFile()) {
211
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
212
+ fs.copyFileSync(src, dest);
213
+ }
214
+ }
215
+ }
216
+
217
+ function resetDirectory(dir) {
218
+ try {
219
+ fs.rmSync(dir, { recursive: true, force: true });
220
+ } catch (_) {
221
+ }
222
+ fs.mkdirSync(dir, { recursive: true });
223
+ }
224
+
225
+ function removeDirectoryIfExists(dir) {
226
+ try {
227
+ fs.rmSync(dir, { recursive: true, force: true });
228
+ } catch (_) {
229
+ }
230
+ }
231
+
232
+ function cleanupDirectoryEntries(rootDir, keepName) {
233
+ let entries;
234
+ try {
235
+ entries = fs.readdirSync(rootDir, { withFileTypes: true });
236
+ } catch (_) {
237
+ return;
238
+ }
239
+
240
+ for (const entry of entries) {
241
+ if (!entry.isDirectory() || entry.name === keepName) {
242
+ continue;
243
+ }
244
+ removeDirectoryIfExists(path.join(rootDir, entry.name));
245
+ }
246
+ }
247
+
248
+ function cleanupLegacyRootFiles(binDir) {
249
+ let entries;
250
+ try {
251
+ entries = fs.readdirSync(binDir, { withFileTypes: true });
252
+ } catch (_) {
253
+ return;
254
+ }
255
+
256
+ for (const entry of entries) {
257
+ if (!entry.isFile()) {
258
+ continue;
259
+ }
260
+ if (!/^rebon-.+\.exe$/i.test(entry.name)) {
261
+ continue;
262
+ }
263
+ try {
264
+ fs.unlinkSync(path.join(binDir, entry.name));
265
+ } catch (_) {
266
+ }
267
+ }
268
+ }
269
+
270
+ function cleanupOldVersions(binDir, keepVersionKey) {
271
+ cleanupDirectoryEntries(path.join(binDir, VERSIONS_DIR), keepVersionKey);
272
+ cleanupDirectoryEntries(path.join(binDir, STAGING_DIR), keepVersionKey);
273
+ cleanupLegacyRootFiles(binDir);
274
+ }
275
+
276
+ function promoteStagingDirectory(stagingDir, versionDir) {
277
+ fs.mkdirSync(path.dirname(versionDir), { recursive: true });
278
+ if (fs.existsSync(versionDir)) {
279
+ fs.rmSync(versionDir, { recursive: true, force: true });
280
+ }
281
+ fs.renameSync(stagingDir, versionDir);
282
+ }
283
+
284
+ function installToVersionKey(packageInfo, binDir, versionKey) {
285
+ const layout = layoutForVersion(binDir, versionKey);
286
+ resetDirectory(layout.stagingDir);
287
+ copyPayloadDirectory(packageInfo.payloadDir, layout.stagingDir);
288
+ promoteStagingDirectory(layout.stagingDir, layout.versionDir);
289
+
290
+ writeManifest(layout.manifestPath, {
291
+ version: packageInfo.version,
292
+ versionKey,
293
+ path: layout.manifestRelativePath,
294
+ package: packageInfo.name,
295
+ payload: packageInfo.payload || payloadSignature(packageInfo.payloadPath),
296
+ installedAt: new Date().toISOString(),
297
+ });
298
+ cleanupOldVersions(binDir, versionKey);
299
+ return layout.exePath;
300
+ }
301
+
302
+ function installManagedPackage(packageInfo) {
303
+ const binDir = managedBinDir();
304
+ if (!binDir || !fs.existsSync(packageInfo.payloadPath)) {
305
+ return null;
306
+ }
307
+
308
+ const installedManifest = readInstalledManifest(packageInfo);
309
+ if (installedManifest) {
310
+ cleanupOldVersions(binDir, installedManifest.versionKey);
311
+ return resolveManifestPath(binDir, installedManifest.path);
312
+ }
313
+
314
+ try {
315
+ return installToVersionKey(packageInfo, binDir, packageInfo.versionKey);
316
+ } catch (error) {
317
+ const fallbackVersionKey = payloadSpecificVersionKey(packageInfo);
318
+ if (fallbackVersionKey === packageInfo.versionKey) {
319
+ throw error;
320
+ }
321
+ return installToVersionKey(packageInfo, binDir, fallbackVersionKey);
322
+ }
323
+ }
324
+
325
+ function resolveExecutable(packageInfo) {
326
+ const info = packageInfo || readPackageInfo();
327
+ const installed = readInstalledExecutable(info);
328
+ if (installed) {
329
+ return { exe: installed, usedFallback: false, installError: null };
330
+ }
331
+
332
+ let installError = null;
333
+ try {
334
+ const managed = installManagedPackage(info);
335
+ if (managed) {
336
+ return { exe: managed, usedFallback: false, installError: null };
337
+ }
338
+ } catch (error) {
339
+ installError = error;
340
+ }
341
+
342
+ if (fs.existsSync(info.payloadPath)) {
343
+ return { exe: info.payloadPath, usedFallback: true, installError };
344
+ }
345
+
346
+ return { exe: null, usedFallback: false, installError };
347
+ }
348
+
349
+ function spawnResolvedExecutable(resolved, argv, spawnImpl) {
350
+ const child = spawnImpl(resolved.exe, argv, {
351
+ stdio: "inherit",
352
+ windowsHide: false,
353
+ });
354
+
355
+ child.on("error", (error) => {
356
+ console.error(`rebon: failed to start ${resolved.exe}: ${error.message}`);
357
+ process.exit(1);
358
+ });
359
+
360
+ child.on("exit", (code, signal) => {
361
+ if (signal) {
362
+ try {
363
+ process.kill(process.pid, signal);
364
+ } catch (_) {
365
+ process.exit(1);
366
+ }
367
+ return;
368
+ }
369
+ process.exit(code === null ? 1 : code);
370
+ });
371
+
372
+ return child;
373
+ }
374
+
375
+ module.exports = {
376
+ cleanupOldVersions,
377
+ installManagedPackage,
378
+ layoutForVersion,
379
+ managedBinDir,
380
+ managedRelativeExecutable,
381
+ payloadSignature,
382
+ readCurrentManifest,
383
+ readInstalledExecutable,
384
+ readPackageInfo,
385
+ requireManagedBinDir,
386
+ resolveExecutable,
387
+ spawnResolvedExecutable,
388
+ versionKeyFromPackageJson,
389
+ };
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@rebon/cli-win32-x64",
3
+ "version": "0.1.3",
4
+ "description": "Agent cli for coding and more.",
5
+ "license": "MIT OR Apache-2.0",
6
+ "bin": {
7
+ "rebon": "bin/rebon.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "README.md",
12
+ "scripts",
13
+ "payload",
14
+ "lib"
15
+ ],
16
+ "os": [
17
+ "win32"
18
+ ],
19
+ "cpu": [
20
+ "x64"
21
+ ],
22
+ "scripts": {
23
+ "postinstall": "node scripts/postinstall.js"
24
+ }
25
+ }
Binary file
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+
3
+ const childProcess = require("child_process");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+
7
+ const INSTALL_SUPERVISOR_ENV = "REBON_INSTALL_SUPERVISOR";
8
+
9
+ function log(message) {
10
+ console.log(`[rebon postinstall] ${message}`);
11
+ }
12
+
13
+ function envFlagEnabled(value) {
14
+ return /^(1|true|yes|on)$/i.test(String(value || "").trim());
15
+ }
16
+
17
+ function supervisorHintMessage() {
18
+ return "background supervisor scheduler was not registered; run `rebon agents service install` to keep queued jobs moving after login, or set REBON_INSTALL_SUPERVISOR=1 before install to opt in.";
19
+ }
20
+
21
+ function packageRootFromScript() {
22
+ return path.resolve(__dirname, "..");
23
+ }
24
+
25
+ function installWindowsPayload(packageRoot) {
26
+ const {
27
+ installManagedPackage,
28
+ readPackageInfo,
29
+ requireManagedBinDir,
30
+ } = require("../lib/windows-managed");
31
+
32
+ requireManagedBinDir();
33
+ const packageInfo = readPackageInfo(packageRoot);
34
+ if (!fs.existsSync(packageInfo.payloadPath)) {
35
+ throw new Error(`payload executable is missing: ${packageInfo.payloadPath}`);
36
+ }
37
+
38
+ const managedExe = installManagedPackage(packageInfo);
39
+ if (!managedExe) {
40
+ throw new Error("could not install payload executable into managed bin directory");
41
+ }
42
+ return managedExe;
43
+ }
44
+
45
+ function packageExecutable(packageRoot, platform) {
46
+ if (platform === "win32") {
47
+ return installWindowsPayload(packageRoot);
48
+ }
49
+
50
+ const exePath = path.join(packageRoot, "bin", "rebon");
51
+ if (!fs.existsSync(exePath)) {
52
+ throw new Error(`payload executable is missing: ${exePath}`);
53
+ }
54
+
55
+ try {
56
+ fs.chmodSync(exePath, 0o755);
57
+ } catch (_) {
58
+ }
59
+ return exePath;
60
+ }
61
+
62
+ function installSupervisorScheduler(exePath, spawnSyncImpl) {
63
+ const result = spawnSyncImpl(exePath, ["agents", "service", "install"], {
64
+ stdio: "inherit",
65
+ windowsHide: true,
66
+ });
67
+
68
+ if (result.error) {
69
+ throw result.error;
70
+ }
71
+ if (result.status !== 0) {
72
+ throw new Error(`supervisor scheduler install exited with status ${result.status}`);
73
+ }
74
+ }
75
+
76
+ function runPostinstall(options = {}) {
77
+ const platform = options.platform || process.platform;
78
+ const env = options.env || process.env;
79
+ const logger = options.log || log;
80
+ const spawnSyncImpl = options.spawnSync || childProcess.spawnSync;
81
+ const packageRoot = options.packageRoot || packageRootFromScript();
82
+ const exePath = packageExecutable(packageRoot, platform);
83
+
84
+ logger(`installed ${exePath}`);
85
+
86
+ if (envFlagEnabled(env[INSTALL_SUPERVISOR_ENV])) {
87
+ logger(`registering per-user background supervisor scheduler because ${INSTALL_SUPERVISOR_ENV} is enabled`);
88
+ installSupervisorScheduler(exePath, spawnSyncImpl);
89
+ logger("registered background supervisor scheduler");
90
+ return;
91
+ }
92
+
93
+ logger(supervisorHintMessage());
94
+ }
95
+
96
+ try {
97
+ if (require.main === module) {
98
+ runPostinstall();
99
+ }
100
+ } catch (error) {
101
+ console.error(`[rebon postinstall] ${error.message}`);
102
+ process.exit(1);
103
+ }
104
+
105
+ module.exports = {
106
+ INSTALL_SUPERVISOR_ENV,
107
+ envFlagEnabled,
108
+ installSupervisorScheduler,
109
+ packageExecutable,
110
+ runPostinstall,
111
+ supervisorHintMessage,
112
+ };