@vibesdotdev/bin 1.0.2 → 1.0.3-rc.1

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/checkout.mjs ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bun
2
+ import { captureCheckoutCliLaunch } from '@vibesdotdev/host-bun/cli-checkout';
3
+ import { startHost } from '@vibesdotdev/plugins/host-start.mjs';
4
+ // The checkout command is the same public CLI rooted at the declared checkout:
5
+ // it declares the published package's version, `vibes.plugins` selection and
6
+ // `vibes.config` block, resolved in the checkout's own graph.
7
+ import manifest from './package.json' with { type: 'json' };
8
+
9
+ const launch = captureCheckoutCliLaunch(import.meta.url);
10
+ await startHost({
11
+ ...launch,
12
+ host: { ...launch.host, version: manifest.version },
13
+ hostPluginSpecifiers: manifest.vibes.plugins,
14
+ hostConfig: manifest.vibes.config,
15
+ attestHostGraph: true,
16
+ globalPackagePlugins: false
17
+ });
package/package.json CHANGED
@@ -1,42 +1,116 @@
1
1
  {
2
2
  "name": "@vibesdotdev/bin",
3
- "version": "1.0.2",
4
- "description": "Vibes CLI - the command-line interface for the Vibes platform",
5
- "license": "MIT",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/vibesdotdev/monorepo.git",
9
- "directory": "packages/cli-bin"
10
- },
11
- "homepage": "https://vibes.dev",
12
- "bin": {
13
- "vibes": "bin/vibes"
3
+ "version": "1.0.3-rc.1",
4
+ "dependencies": {
5
+ "@vibesdotdev/account": "^1.0.0-rc.2",
6
+ "@vibesdotdev/agent-execution": "^1.0.0-rc.1",
7
+ "@vibesdotdev/ai": "^1.0.0-rc.3",
8
+ "@vibesdotdev/ai-schemas": "^1.0.0-rc.3",
9
+ "@vibesdotdev/applets": "^1.0.0-rc.2",
10
+ "@vibesdotdev/auth": "^1.0.0-rc.3",
11
+ "@vibesdotdev/bridge": "^1.0.0-rc.0",
12
+ "@vibesdotdev/chat": "^1.0.0-rc.0",
13
+ "@vibesdotdev/cli": "^1.0.0-rc.2",
14
+ "@vibesdotdev/code-sessions": "^1.0.0-rc.0",
15
+ "@vibesdotdev/command-palette": "^1.0.0-rc.3",
16
+ "@vibesdotdev/control": "^1.0.0-rc.2",
17
+ "@vibesdotdev/execution-local": "^1.0.0-rc.3",
18
+ "@vibesdotdev/filesystem": "^1.0.0-rc.2",
19
+ "@vibesdotdev/git-operations": "^1.0.0-rc.2",
20
+ "@vibesdotdev/host-bun": "^1.0.0-rc.2",
21
+ "@vibesdotdev/hq": "^1.0.0-rc.1",
22
+ "@vibesdotdev/infra-core": "^1.0.0-rc.4",
23
+ "@vibesdotdev/infra-deploy": "^1.0.0-rc.4",
24
+ "@vibesdotdev/integrations": "^1.0.0-rc.2",
25
+ "@vibesdotdev/integrations-vibes": "^1.0.0-rc.1",
26
+ "@vibesdotdev/kernel": "^1.0.0-rc.6",
27
+ "@vibesdotdev/kernel-rpc": "^1.0.0-rc.2",
28
+ "@vibesdotdev/kit": "^1.0.0-rc.3",
29
+ "@vibesdotdev/knowledge": "^1.0.0-rc.1",
30
+ "@vibesdotdev/mcp": "^1.0.0-rc.0",
31
+ "@vibesdotdev/notifications": "^1.0.0-rc.2",
32
+ "@vibesdotdev/platform": "^1.0.0-rc.1",
33
+ "@vibesdotdev/plugins": "^1.0.0-rc.0",
34
+ "@vibesdotdev/pm": "^1.0.0-rc.2",
35
+ "@vibesdotdev/projects": "^1.0.0-rc.0",
36
+ "@vibesdotdev/prompts": "^1.0.0-rc.0",
37
+ "@vibesdotdev/registry": "^1.0.0-rc.3",
38
+ "@vibesdotdev/scope": "^1.0.0-rc.2",
39
+ "@vibesdotdev/secrets": "^1.0.0-rc.3",
40
+ "@vibesdotdev/session": "^1.0.0-rc.1",
41
+ "@vibesdotdev/storage": "^1.0.0-rc.3",
42
+ "@vibesdotdev/studio": "^1.0.0-rc.0",
43
+ "@vibesdotdev/surface-sveltekit": "^1.0.0-rc.3",
44
+ "@vibesdotdev/sync": "^1.0.0-rc.2",
45
+ "@vibesdotdev/terminal-client": "^1.0.0-rc.2",
46
+ "@vibesdotdev/testing": "^1.0.0-rc.2",
47
+ "@vibesdotdev/tui": "^1.0.0-rc.2",
48
+ "@vibesdotdev/vessel": "^1.0.0-rc.2",
49
+ "@vibesdotdev/workboard": "^1.0.0-rc.0",
50
+ "@vibesdotdev/workspace": "^1.0.0-rc.1"
14
51
  },
15
52
  "files": [
16
- "bin/vibes",
17
- "scripts/postinstall.js",
18
- "README.md"
53
+ "vibes.mjs",
54
+ "checkout.mjs"
19
55
  ],
20
- "scripts": {
21
- "postinstall": "node scripts/postinstall.js",
22
- "pack:dry-run": "npm pack --dry-run --json --ignore-scripts",
23
- "publish:npm": "npm publish --access public --registry=https://registry.npmjs.org",
24
- "check": "bun --bun tsc -p tsconfig.json --noEmit"
25
- },
26
- "os": [
27
- "darwin",
28
- "linux",
29
- "win32"
30
- ],
31
- "cpu": [
32
- "x64",
33
- "arm64"
34
- ],
35
- "engines": {
36
- "node": ">=18"
37
- },
38
56
  "publishConfig": {
39
- "registry": "https://registry.npmjs.org",
57
+ "registry": "https://packages.vibes.dev",
40
58
  "access": "public"
59
+ },
60
+ "vibes": {
61
+ "visibility": "public",
62
+ "tier": "public",
63
+ "compositionRoots": [
64
+ "vibes.mjs",
65
+ "checkout.mjs"
66
+ ],
67
+ "plugins": [
68
+ "@vibesdotdev/plugins/consumer.plugin",
69
+ "@vibesdotdev/infra-core/service",
70
+ "@vibesdotdev/integrations-vibes/services",
71
+ "@vibesdotdev/infra-core/plugin",
72
+ "@vibesdotdev/infra-core/generate/plugin",
73
+ "@vibesdotdev/infra-deploy/plugin",
74
+ "@vibesdotdev/infra-deploy/cli/plugin",
75
+ "@vibesdotdev/pm/plugin",
76
+ "@vibesdotdev/pm/cli/plugin",
77
+ "@vibesdotdev/platform/cli/plugin",
78
+ "@vibesdotdev/platform/fleets/cli/plugin",
79
+ "@vibesdotdev/workboard/marshal/cli/plugin",
80
+ "@vibesdotdev/sync/plugin",
81
+ "@vibesdotdev/sync/cli/plugin",
82
+ "@vibesdotdev/studio/cli/plugin",
83
+ "@vibesdotdev/projects/app/cli/plugin",
84
+ "@vibesdotdev/plugins/tui/plugin"
85
+ ],
86
+ "config": {
87
+ "runtime.host": {
88
+ "environmentTier": "local"
89
+ },
90
+ "infra.services": {
91
+ "environment": "production"
92
+ }
93
+ },
94
+ "registry": {
95
+ "repositoryId": "80ee5ed7-a88d-49f8-86e8-f867006d1b4d",
96
+ "directory": "npm-bin"
97
+ }
98
+ },
99
+ "description": "Vibes CLI. Starts the public plugin host with no setup command.",
100
+ "type": "module",
101
+ "license": "MIT",
102
+ "bin": {
103
+ "vibes": "./vibes.mjs"
104
+ },
105
+ "repository": {
106
+ "type": "git",
107
+ "url": "ssh://git@git.vibes.dev/vibesdotdev/cli-bin.git",
108
+ "directory": "npm-bin"
109
+ },
110
+ "gitHead": "a7f6e9d14b82eb7b4e792d85047b071108d7f190",
111
+ "_vibes_source": {
112
+ "repo": "ssh://git@git.vibes.dev/vibesdotdev/cli-bin.git",
113
+ "sha": "a7f6e9d14b82eb7b4e792d85047b071108d7f190",
114
+ "workspaceDigest": "sha256:5d4d9f9b903fb38485a09b0f2c4cc1c33d839d24c11048f23dff55177ab7a24a"
41
115
  }
42
116
  }
package/vibes.mjs ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bun
2
+ import { loadedCliHostDeclaration } from '@vibesdotdev/host-bun/resolution-root';
3
+ import { startHost } from '@vibesdotdev/plugins/host-start.mjs';
4
+ // The published package identity is the executable's declaration: its version
5
+ // is what `vibes --version` reports, and `vibes.plugins` is the public plugin
6
+ // selection this package installs as `dependencies` and composes at the graph
7
+ // it was installed into. `vibes.config` is the executable's own config block
8
+ // (the environment tier credentials resolve under, the services environment).
9
+ // Nothing here lists a plugin or a setting.
10
+ import manifest from './package.json' with { type: 'json' };
11
+
12
+ await startHost({
13
+ host: {
14
+ ...loadedCliHostDeclaration({
15
+ scriptUrl: import.meta.url,
16
+ hostEntryUrl: import.meta.resolve('@vibesdotdev/plugins/host-start.mjs')
17
+ }),
18
+ version: manifest.version
19
+ },
20
+ hostPluginSpecifiers: manifest.vibes.plugins,
21
+ hostConfig: manifest.vibes.config
22
+ });
package/README.md DELETED
@@ -1,37 +0,0 @@
1
- # Vibes CLI
2
-
3
- Published npm wrapper for the public `vibes` binary.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install -g @vibesdotdev/bin
9
- ```
10
-
11
- Or with bun:
12
-
13
- ```bash
14
- bun add -g @vibesdotdev/bin
15
- ```
16
-
17
- ## Usage
18
-
19
- The package downloads a platform-specific `vibes` binary during `postinstall` and exposes it through the package `bin` field.
20
-
21
- Set `VIBES_BINARY_MIRROR` to override the release download base URL. The default
22
- mirror is `https://assets.vibes.dev/downloads/cli`. The installer verifies the
23
- download against `SHA256SUMS.txt` from the same mirror before completing.
24
-
25
- To inspect the exact npm tarball before release:
26
-
27
- ```bash
28
- npm run pack:dry-run
29
- ```
30
-
31
- To publish from this package after npm auth is configured:
32
-
33
- ```bash
34
- npm run publish:npm
35
- ```
36
-
37
- See [https://vibes.dev](https://vibes.dev) for documentation.
package/bin/vibes DELETED
@@ -1,149 +0,0 @@
1
- #!/usr/bin/env node
2
- // Cross-platform launcher for the vibes CLI binary.
3
- //
4
- // npm's bin field is static, but the published package downloads a
5
- // platform-specific binary at install time (scripts/postinstall.js):
6
- // - Unix: overwrites THIS file (bin/vibes) with the binary itself,
7
- // so subsequent invocations skip Node entirely.
8
- // - Windows: writes bin/vibes.exe alongside this file. npm-on-Windows
9
- // generates a vibes.cmd that does `node bin/vibes`, so this
10
- // shim has to be a JS file that spawns the .exe. Without it,
11
- // Windows would try to execute a shell script and fail.
12
- //
13
- // If neither artifact is present, postinstall didn't complete — surface a
14
- // clear error rather than silently doing nothing.
15
-
16
- 'use strict';
17
-
18
- const { spawnSync } = require('child_process');
19
- const fs = require('fs');
20
- const os = require('os');
21
- const path = require('path');
22
-
23
- const binDir = __dirname;
24
- const installerPath = path.join(binDir, '..', 'scripts', 'postinstall.js');
25
- const exePath = path.join(binDir, 'vibes.exe');
26
-
27
- function getPersistentBinDir() {
28
- if (process.env.VIBES_INSTALL_DIR) return process.env.VIBES_INSTALL_DIR;
29
-
30
- const bunGlobalBin = readCommandOutput('bun', ['pm', 'bin', '-g']);
31
- if (bunGlobalBin) return bunGlobalBin;
32
-
33
- if (process.env.BUN_INSTALL) return path.join(process.env.BUN_INSTALL, 'bin');
34
-
35
- if (process.platform === 'win32') {
36
- const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
37
- return path.join(base, 'Programs', 'vibes', 'bin');
38
- }
39
-
40
- if (process.env.XDG_BIN_HOME) return process.env.XDG_BIN_HOME;
41
- return path.join(os.homedir(), '.local', 'bin');
42
- }
43
-
44
- function readCommandOutput(command, args) {
45
- const result = spawnSync(command, args, {
46
- encoding: 'utf8',
47
- stdio: ['ignore', 'pipe', 'ignore'],
48
- windowsHide: true
49
- });
50
- if (result.error || result.status !== 0) return undefined;
51
- const value = result.stdout.trim();
52
- return value.length > 0 ? value : undefined;
53
- }
54
-
55
- function getPersistentBinaryPath() {
56
- return path.join(getPersistentBinDir(), process.platform === 'win32' ? 'vibes.exe' : 'vibes');
57
- }
58
-
59
- function runInstaller() {
60
- const result = spawnSync(process.execPath, [installerPath], {
61
- stdio: 'inherit',
62
- windowsHide: false,
63
- env: { ...process.env, VIBES_INSTALL_PERSISTENT: '1' }
64
- });
65
- if (result.error) {
66
- process.stderr.write(`Failed to run installer: ${result.error.message}\n`);
67
- process.exit(1);
68
- }
69
- if (result.status !== 0) {
70
- process.exit(result.status === null ? 1 : result.status);
71
- }
72
- }
73
-
74
- function reportMissingBinary() {
75
- process.stderr.write('Error: vibes binary not installed.\n');
76
- process.stderr.write('\n');
77
- process.stderr.write('The installer could not place the platform binary.\n');
78
- process.stderr.write('\n');
79
- process.stderr.write('If the problem persists, see https://github.com/vibesdotdev/monorepo/issues\n');
80
- process.exit(1);
81
- }
82
-
83
- function isStillNodeShim(filePath) {
84
- try {
85
- const content = fs.readFileSync(filePath, 'utf8');
86
- return content.includes('Cross-platform launcher for the vibes CLI binary');
87
- } catch {
88
- return false;
89
- }
90
- }
91
-
92
- if (process.platform === 'win32') {
93
- const persistentPath = getPersistentBinaryPath();
94
- if (fs.existsSync(persistentPath)) {
95
- const result = spawnSync(persistentPath, process.argv.slice(2), {
96
- stdio: 'inherit',
97
- windowsHide: false
98
- });
99
- if (result.error) {
100
- process.stderr.write(`Failed to spawn ${persistentPath}: ${result.error.message}\n`);
101
- process.exit(1);
102
- }
103
- process.exit(result.status === null ? 1 : result.status);
104
- }
105
- if (!fs.existsSync(exePath)) {
106
- runInstaller();
107
- }
108
- if (!fs.existsSync(exePath)) {
109
- reportMissingBinary();
110
- }
111
- const result = spawnSync(exePath, process.argv.slice(2), {
112
- stdio: 'inherit',
113
- windowsHide: false
114
- });
115
- if (result.error) {
116
- process.stderr.write(`Failed to spawn vibes.exe: ${result.error.message}\n`);
117
- process.exit(1);
118
- }
119
- process.exit(result.status === null ? 1 : result.status);
120
- }
121
-
122
- const persistentPath = getPersistentBinaryPath();
123
- if (fs.existsSync(persistentPath) && !isStillNodeShim(persistentPath)) {
124
- const result = spawnSync(persistentPath, process.argv.slice(2), {
125
- stdio: 'inherit',
126
- windowsHide: false
127
- });
128
- if (result.error) {
129
- process.stderr.write(`Failed to spawn ${persistentPath}: ${result.error.message}\n`);
130
- process.exit(1);
131
- }
132
- process.exit(result.status === null ? 1 : result.status);
133
- }
134
-
135
- runInstaller();
136
-
137
- if (!fs.existsSync(persistentPath) || isStillNodeShim(persistentPath)) {
138
- reportMissingBinary();
139
- }
140
-
141
- const result = spawnSync(persistentPath, process.argv.slice(2), {
142
- stdio: 'inherit',
143
- windowsHide: false
144
- });
145
- if (result.error) {
146
- process.stderr.write(`Failed to spawn vibes: ${result.error.message}\n`);
147
- process.exit(1);
148
- }
149
- process.exit(result.status === null ? 1 : result.status);
@@ -1,310 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const https = require('https');
4
- const http = require('http');
5
- const crypto = require('crypto');
6
- const { spawnSync } = require('child_process');
7
- const fs = require('fs');
8
- const path = require('path');
9
- const os = require('os');
10
- const BASE_URL = process.env.VIBES_BINARY_MIRROR || 'https://assets.vibes.dev/downloads/cli';
11
-
12
- function getPlatform() {
13
- const platform = os.platform();
14
- if (platform === 'win32') return 'win32';
15
- if (platform === 'darwin') return 'darwin';
16
- return 'linux';
17
- }
18
-
19
- function getArch() {
20
- const arch = os.arch();
21
- if (arch === 'x64') return 'x64';
22
- if (arch === 'arm64') return 'arm64';
23
- // Node on macOS can report 'arm' for Apple Silicon
24
- if (arch === 'arm' && getPlatform() === 'darwin') return 'arm64';
25
- return 'x64';
26
- }
27
-
28
- function getBinaryName() {
29
- const platform = getPlatform();
30
- const arch = getArch();
31
- const baseName = `vibes-${platform}-${arch}`;
32
- return platform === 'win32' ? `${baseName}.exe` : baseName;
33
- }
34
-
35
- function getOutputPath() {
36
- const platform = getPlatform();
37
- if (process.env.VIBES_INSTALL_PERSISTENT === '1') {
38
- const dir = getPersistentBinDir();
39
- const outputName = platform === 'win32' ? 'vibes.exe' : 'vibes';
40
- return path.join(dir, outputName);
41
- }
42
- const dir = path.join(__dirname, '..', 'bin');
43
- const outputName = platform === 'win32' ? 'vibes.exe' : 'vibes';
44
- return path.join(dir, outputName);
45
- }
46
-
47
- function getPersistentBinDir() {
48
- if (process.env.VIBES_INSTALL_DIR) return process.env.VIBES_INSTALL_DIR;
49
-
50
- const bunGlobalBin = readCommandOutput('bun', ['pm', 'bin', '-g']);
51
- if (bunGlobalBin) return bunGlobalBin;
52
-
53
- if (process.env.BUN_INSTALL) return path.join(process.env.BUN_INSTALL, 'bin');
54
-
55
- if (process.platform === 'win32') {
56
- const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
57
- return path.join(base, 'Programs', 'vibes', 'bin');
58
- }
59
-
60
- if (process.env.XDG_BIN_HOME) return process.env.XDG_BIN_HOME;
61
-
62
- // POSIX fallback used by Linux distributions and many shell setups. If it is
63
- // not on PATH, the installer prints a warning with the exact directory.
64
- return path.join(os.homedir(), '.local', 'bin');
65
- }
66
-
67
- function readCommandOutput(command, args) {
68
- const result = spawnSync(command, args, {
69
- encoding: 'utf8',
70
- stdio: ['ignore', 'pipe', 'ignore'],
71
- windowsHide: true
72
- });
73
- if (result.error || result.status !== 0) return undefined;
74
- const value = result.stdout.trim();
75
- return value.length > 0 ? value : undefined;
76
- }
77
-
78
- function pathHasDir(dir) {
79
- const pathValue = process.env.PATH || '';
80
- const resolvedDir = path.resolve(dir);
81
- return pathValue
82
- .split(path.delimiter)
83
- .filter(Boolean)
84
- .some((entry) => path.resolve(entry) === resolvedDir);
85
- }
86
-
87
- function showProgress(total, current) {
88
- if (total > 0) {
89
- const percent = Math.round((current / total) * 100);
90
- const bar = '[' + '='.repeat(Math.floor(percent / 5)) + ' '.repeat(20 - Math.floor(percent / 5)) + ']';
91
- process.stdout.write(`\r${bar} ${percent}%`);
92
- } else {
93
- process.stdout.write('.');
94
- }
95
- }
96
-
97
- function download(url, dest, callback) {
98
- const protocol = url.startsWith('https') ? https : http;
99
-
100
- const request = protocol.get(url, { timeout: 30000 }, (response) => {
101
- // Handle redirects
102
- if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
103
- download(response.headers.location, dest, callback);
104
- return;
105
- }
106
-
107
- if (response.statusCode !== 200) {
108
- callback(new Error(`Failed to download: HTTP ${response.statusCode}`));
109
- return;
110
- }
111
-
112
- const total = parseInt(response.headers['content-length'], 10);
113
- let downloaded = 0;
114
-
115
- const fp = fs.openSync(dest, 'w');
116
- response.on('data', (chunk) => {
117
- downloaded += chunk.length;
118
- showProgress(total, downloaded);
119
- fs.writeSync(fp, chunk);
120
- });
121
-
122
- response.on('end', () => {
123
- fs.closeSync(fp);
124
- console.log();
125
- callback(null);
126
- });
127
-
128
- response.on('error', (err) => {
129
- fs.closeSync(fp);
130
- callback(err);
131
- });
132
- });
133
-
134
- request.on('error', (err) => {
135
- callback(err);
136
- });
137
-
138
- request.on('timeout', () => {
139
- request.destroy();
140
- callback(new Error('Download timed out'));
141
- });
142
- }
143
-
144
- function fetchText(url, callback) {
145
- const protocol = url.startsWith('https') ? https : http;
146
-
147
- const request = protocol.get(url, { timeout: 30000 }, (response) => {
148
- if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
149
- fetchText(response.headers.location, callback);
150
- return;
151
- }
152
-
153
- if (response.statusCode !== 200) {
154
- callback(new Error(`HTTP ${response.statusCode}`));
155
- return;
156
- }
157
-
158
- let body = '';
159
- response.setEncoding('utf8');
160
- response.on('data', (chunk) => {
161
- body += chunk;
162
- });
163
- response.on('end', () => callback(null, body));
164
- response.on('error', callback);
165
- });
166
-
167
- request.on('error', callback);
168
- request.on('timeout', () => {
169
- request.destroy();
170
- callback(new Error('Download timed out'));
171
- });
172
- }
173
-
174
- function verifyChecksum(binaryName, filePath) {
175
- return new Promise((resolve, reject) => {
176
- if (process.env.VIBES_SKIP_CHECKSUM === '1') {
177
- console.warn('Warning: VIBES_SKIP_CHECKSUM=1 set; skipping checksum verification.');
178
- resolve();
179
- return;
180
- }
181
-
182
- const checksumUrl = `${BASE_URL}/SHA256SUMS.txt`;
183
- fetchText(checksumUrl, (err, text) => {
184
- if (err) {
185
- reject(new Error(`Could not fetch checksum file from ${checksumUrl}: ${err.message}`));
186
- return;
187
- }
188
-
189
- const line = text
190
- .split(/\r?\n/)
191
- .find((entry) => entry.trim().endsWith(` ${binaryName}`));
192
- if (!line) {
193
- reject(new Error(`No checksum entry for ${binaryName} in ${checksumUrl}`));
194
- return;
195
- }
196
-
197
- const expected = line.trim().split(/\s+/)[0];
198
- const actual = crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
199
- if (actual !== expected) {
200
- reject(new Error(`Checksum mismatch for ${binaryName}`));
201
- return;
202
- }
203
-
204
- console.log('Verified release checksum');
205
- resolve();
206
- });
207
- });
208
- }
209
-
210
- function makeExecutable(filePath) {
211
- const platform = getPlatform();
212
- if (platform === 'win32') return;
213
-
214
- try {
215
- fs.chmodSync(filePath, 0o755);
216
- } catch (err) {
217
- console.error('Warning: Could not chmod +x:', err.message);
218
- }
219
- }
220
-
221
- function isWorkspaceInstall() {
222
- // Explicit opt-out (set in CI, the monorepo's bun install, or any env that
223
- // already has the binary built locally and doesn't want network on install).
224
- if (process.env.VIBES_SKIP_BINARY_DOWNLOAD === '1') return true;
225
-
226
- // When this package is installed from the npm registry, the realpath of the
227
- // script location includes `/node_modules/` (the unpacked tarball path). When
228
- // bun installs us via a workspace link, realpath dereferences the symlink to
229
- // the monorepo source — which lives outside any `node_modules` directory. We
230
- // use this asymmetry to skip downloads cleanly inside the workspace while
231
- // failing loudly for real end-user installs.
232
- try {
233
- const real = fs.realpathSync(__dirname);
234
- const sep = path.sep;
235
- return !real.includes(`${sep}node_modules${sep}`);
236
- } catch {
237
- return false;
238
- }
239
- }
240
-
241
- async function main() {
242
- if (isWorkspaceInstall()) {
243
- console.log('Vibes CLI installer: workspace install detected, skipping binary download.');
244
- return;
245
- }
246
-
247
- const binaryName = getBinaryName();
248
- const outputPath = getOutputPath();
249
- const url = `${BASE_URL}/${binaryName}`;
250
-
251
- console.log(`Vibes CLI installer`);
252
- console.log(`Platform: ${getPlatform()}, Arch: ${getArch()}`);
253
- console.log(`Downloading ${binaryName}...`);
254
-
255
- // Ensure bin directory exists
256
- const binDir = path.dirname(outputPath);
257
- if (!fs.existsSync(binDir)) {
258
- fs.mkdirSync(binDir, { recursive: true });
259
- }
260
-
261
- // Check if already exists and up-to-date
262
- if (fs.existsSync(outputPath)) {
263
- try {
264
- fs.unlinkSync(outputPath);
265
- } catch (err) {
266
- console.error('Could not remove existing binary:', err.message);
267
- }
268
- }
269
-
270
- return new Promise((resolve, reject) => {
271
- download(url, outputPath, (err) => {
272
- if (err) {
273
- console.error();
274
- console.error('Error:', err.message);
275
- console.error(`Could not download binary from ${url}`);
276
- console.error();
277
- console.error('To resolve:');
278
- console.error(` - Confirm a release exists at ${BASE_URL}`);
279
- console.error(` - Confirm ${BASE_URL}/SHA256SUMS.txt is reachable and contains ${binaryName}`);
280
- console.error(' - Or point VIBES_BINARY_MIRROR at an alternate download base URL');
281
- console.error(' - Or download the binary manually and place it at:');
282
- console.error(` ${outputPath}`);
283
- console.error(' - Or set VIBES_SKIP_BINARY_DOWNLOAD=1 to skip this step');
284
- console.error(' - Or set VIBES_SKIP_CHECKSUM=1 only if you trust the mirror and need to bypass checksum verification');
285
- reject(err);
286
- return;
287
- }
288
-
289
- verifyChecksum(binaryName, outputPath)
290
- .then(() => {
291
- makeExecutable(outputPath);
292
- console.log(`Installed to ${outputPath}`);
293
- const outputDir = path.dirname(outputPath);
294
- if (process.env.VIBES_INSTALL_PERSISTENT === '1' && !pathHasDir(outputDir)) {
295
- console.warn(`Warning: ${outputDir} is not on PATH. Add it to run \`vibes\` directly.`);
296
- }
297
- resolve();
298
- })
299
- .catch(reject);
300
- });
301
- });
302
- }
303
-
304
- main().catch((err) => {
305
- console.error('Installation failed:', err.message);
306
- // End-user installs reach here when isWorkspaceInstall() was false above
307
- // (i.e., the script lives under node_modules) and the download failed. Fail
308
- // loud so the user sees a broken install instead of a silently empty bin/.
309
- process.exit(1);
310
- });