@aptove/aptove 0.1.9 → 0.1.11

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.
Files changed (3) hide show
  1. package/bin/aptove +35 -48
  2. package/package.json +6 -6
  3. package/postinstall.js +122 -67
package/bin/aptove CHANGED
@@ -1,12 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * aptove - ACP AI coding agent
5
- *
6
- * This script finds and executes the platform-specific binary.
7
- */
8
-
9
- const { execFileSync } = require('child_process');
3
+ const { spawnSync } = require('child_process');
10
4
  const path = require('path');
11
5
  const fs = require('fs');
12
6
 
@@ -18,60 +12,53 @@ const PLATFORM_PACKAGES = {
18
12
  'win32-x64': '@aptove/aptove-win32-x64',
19
13
  };
20
14
 
21
- function getBinaryPath() {
22
- const platformKey = `${process.platform}-${process.arch}`;
23
- const packageName = PLATFORM_PACKAGES[platformKey];
15
+ const platformKey = `${process.platform}-${process.arch}`;
16
+ const packageName = PLATFORM_PACKAGES[platformKey];
24
17
 
25
- if (!packageName) {
26
- console.error(`Unsupported platform: ${platformKey}`);
27
- console.error('Supported platforms: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64');
28
- process.exit(1);
29
- }
18
+ if (!packageName) {
19
+ console.error(`aptove: unsupported platform ${platformKey}`);
20
+ console.error('Supported: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64');
21
+ process.exit(1);
22
+ }
30
23
 
31
- const binaryName = process.platform === 'win32' ? 'aptove.exe' : 'aptove';
24
+ const binaryName = process.platform === 'win32' ? 'aptove.exe' : 'aptove';
32
25
 
33
- const possiblePaths = [
34
- // npm/pnpm hoisted
35
- path.join(__dirname, '..', packageName, 'bin', binaryName),
36
- // npm nested node_modules
37
- path.join(__dirname, '..', '..', packageName, 'bin', binaryName),
38
- // pnpm virtual store
39
- path.join(__dirname, '..', 'node_modules', packageName, 'bin', binaryName),
40
- ];
26
+ // Both @aptove/aptove and @aptove/aptove-<platform> live under the same
27
+ // @aptove scope directory, so the platform package is always a sibling:
28
+ // __dirname = .../node_modules/@aptove/aptove/bin
29
+ // platform = .../node_modules/@aptove/aptove-darwin-arm64/bin/aptove
30
+ const shortName = packageName.split('/')[1]; // e.g. "aptove-darwin-arm64"
31
+ const siblingPath = path.join(__dirname, '..', '..', shortName, 'bin', binaryName);
41
32
 
42
- for (const binaryPath of possiblePaths) {
43
- if (fs.existsSync(binaryPath)) {
44
- return binaryPath;
45
- }
46
- }
33
+ let binaryPath = fs.existsSync(siblingPath) ? siblingPath : null;
47
34
 
48
- // Try require.resolve as fallback
35
+ // Fallback: require.resolve handles pnpm virtual stores and other layouts
36
+ if (!binaryPath) {
49
37
  try {
50
- const packagePath = require.resolve(`${packageName}/package.json`);
51
- const binaryPath = path.join(path.dirname(packagePath), 'bin', binaryName);
52
- if (fs.existsSync(binaryPath)) {
53
- return binaryPath;
38
+ const pkgJson = require.resolve(`${packageName}/package.json`);
39
+ const candidate = path.join(path.dirname(pkgJson), 'bin', binaryName);
40
+ if (fs.existsSync(candidate)) {
41
+ binaryPath = candidate;
54
42
  }
55
43
  } catch (e) {
56
- // Package not found
44
+ // not found
57
45
  }
46
+ }
58
47
 
59
- console.error(`Could not find aptove binary for ${platformKey}`);
60
- console.error(`Please ensure ${packageName} is installed.`);
48
+ if (!binaryPath) {
49
+ console.error(`aptove: platform package not installed for ${platformKey}`);
61
50
  console.error('');
62
- console.error('Try reinstalling with:');
63
- console.error(' npm install -g @aptove/aptove');
51
+ console.error('Run:');
52
+ console.error(` npm install -g ${packageName}`);
64
53
  process.exit(1);
65
54
  }
66
55
 
67
- const binaryPath = getBinaryPath();
68
- const args = process.argv.slice(2);
56
+ const result = spawnSync(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
69
57
 
70
- try {
71
- execFileSync(binaryPath, args, { stdio: 'inherit' });
72
- } catch (error) {
73
- if (error.status !== undefined) {
74
- process.exit(error.status);
75
- }
76
- throw error;
58
+ if (result.error) {
59
+ console.error(`aptove: failed to execute binary: ${result.error.message}`);
60
+ console.error(` Binary: ${binaryPath}`);
61
+ process.exit(1);
77
62
  }
63
+
64
+ process.exit(result.status ?? 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aptove/aptove",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "ACP AI coding agent — connects to Claude, Gemini, and OpenAI",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -35,10 +35,10 @@
35
35
  "node": ">=16"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@aptove/aptove-darwin-arm64": "0.1.9",
39
- "@aptove/aptove-darwin-x64": "0.1.9",
40
- "@aptove/aptove-linux-arm64": "0.1.9",
41
- "@aptove/aptove-linux-x64": "0.1.9",
42
- "@aptove/aptove-win32-x64": "0.1.9"
38
+ "@aptove/aptove-darwin-arm64": "0.1.11",
39
+ "@aptove/aptove-darwin-x64": "0.1.11",
40
+ "@aptove/aptove-linux-arm64": "0.1.11",
41
+ "@aptove/aptove-linux-x64": "0.1.11",
42
+ "@aptove/aptove-win32-x64": "0.1.11"
43
43
  }
44
44
  }
package/postinstall.js CHANGED
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * aptove postinstall script
3
3
  *
4
- * Ensures the correct platform-specific binary package is installed.
5
- * npm sometimes skips optional dependencies during global installs, so
6
- * we detect this and install the platform package explicitly if needed.
4
+ * Ensures the platform-specific binary package is installed at the correct version.
5
+ * npm sometimes skips optional dependencies during global installs, so we detect
6
+ * this and install the platform package explicitly if needed.
7
+ * Validates the final binary by running it with --version.
7
8
  */
8
9
 
9
- const { execSync } = require('child_process');
10
+ const { execSync, spawnSync } = require('child_process');
10
11
  const path = require('path');
11
12
  const fs = require('fs');
12
13
 
@@ -18,96 +19,150 @@ const PLATFORM_PACKAGES = {
18
19
  'win32-x64': '@aptove/aptove-win32-x64',
19
20
  };
20
21
 
21
- function getPlatformPackage() {
22
- const platformKey = `${process.platform}-${process.arch}`;
23
- return { platformKey, packageName: PLATFORM_PACKAGES[platformKey] };
22
+ function isBinaryPresent(binaryPath) {
23
+ return fs.existsSync(binaryPath);
24
24
  }
25
25
 
26
- const BINARY_NAME = process.platform === 'win32' ? 'aptove.exe' : 'aptove';
27
-
28
- // Check via require.resolve (works before install, may use module cache after).
29
- function isBinaryInstalled(packageName) {
26
+ // Ensures the binary has executable permissions (no-op on Windows).
27
+ function ensureExecutable(binaryPath) {
28
+ if (process.platform === 'win32') return;
30
29
  try {
31
- const packagePath = require.resolve(`${packageName}/package.json`);
32
- return fs.existsSync(path.join(path.dirname(packagePath), 'bin', BINARY_NAME));
30
+ fs.chmodSync(binaryPath, 0o755);
33
31
  } catch (e) {
34
- return false;
32
+ // best-effort
35
33
  }
36
34
  }
37
35
 
38
- // Check directly using the prefix path, bypassing Node.js module resolution cache.
39
- // Used for post-install verification after an explicit `npm install --prefix`.
40
- function isBinaryInstalledAtPrefix(packageName) {
41
- const prefix = process.env.npm_config_prefix;
42
- if (!prefix) return false;
43
- // Scoped packages: @aptove/aptove-darwin-arm64 → lib/node_modules/@aptove/aptove-darwin-arm64
44
- const packageDir = path.join(prefix, 'lib', 'node_modules', ...packageName.split('/'));
45
- return fs.existsSync(path.join(packageDir, 'bin', BINARY_NAME));
36
+ // Returns the version string from `aptove --version` (e.g. "0.1.9"), or null on failure.
37
+ function getBinaryVersion(binaryPath) {
38
+ const result = spawnSync(binaryPath, ['--version'], { stdio: 'pipe' });
39
+ if (result.error || result.status !== 0) return null;
40
+ const output = (result.stdout || '').toString().trim(); // e.g. "aptove 0.1.9"
41
+ const parts = output.split(' ');
42
+ return parts.length >= 2 ? parts[1] : output;
46
43
  }
47
44
 
48
- function getPackageVersion() {
45
+ // Reads the expected platform package version from the main package's optionalDependencies.
46
+ function getExpectedVersion(packageJsonPath) {
49
47
  try {
50
- const pkg = require('./package.json');
48
+ delete require.cache[require.resolve(packageJsonPath)];
49
+ const pkg = require(packageJsonPath);
51
50
  // optionalDependencies values are updated by the release workflow to match the published version
52
51
  const deps = pkg.optionalDependencies || {};
53
52
  const versions = Object.values(deps).filter(v => v !== '*');
54
53
  return versions[0] || pkg.version;
55
54
  } catch (e) {
56
- return 'latest';
55
+ return null;
57
56
  }
58
57
  }
59
58
 
60
- function installPlatformPackage(packageName, version) {
61
- // During `npm install -g`, npm_config_prefix points to the global prefix (e.g. /usr/local or ~/.nvm/...).
62
- // Installing with --prefix ensures the package lands in the same global node_modules tree.
63
- const prefix = process.env.npm_config_prefix;
64
- const prefixFlag = prefix ? `--prefix "${prefix}"` : '';
65
-
66
- console.log(` Installing ${packageName}@${version}...`);
67
- execSync(
68
- `npm install ${prefixFlag} --no-save --no-audit --no-fund "${packageName}@${version}"`,
69
- { stdio: 'inherit' }
70
- );
71
- }
72
-
73
- function main() {
74
- const { platformKey, packageName } = getPlatformPackage();
59
+ /**
60
+ * Main postinstall logic.
61
+ *
62
+ * All I/O is injectable for testing:
63
+ * baseDir — replaces __dirname for computing the sibling binary path
64
+ * packageJsonPath— path to the main package's package.json
65
+ * platformKey — override platform detection (default: process.platform-process.arch)
66
+ * npmPrefix — override npm_config_prefix
67
+ * installFn — (packageName, version) => void; throws on failure
68
+ * defaults to running `npm install` via execSync
69
+ * log / warn — console.log / console.warn replacements
70
+ * exitFn — process.exit replacement
71
+ */
72
+ function main({
73
+ baseDir = __dirname,
74
+ packageJsonPath = path.join(__dirname, 'package.json'),
75
+ platformKey = `${process.platform}-${process.arch}`,
76
+ npmPrefix = process.env.npm_config_prefix,
77
+ installFn = null,
78
+ log = (...a) => console.log(...a),
79
+ warn = (...a) => console.warn(...a),
80
+ exitFn = process.exit,
81
+ } = {}) {
82
+ const packageName = PLATFORM_PACKAGES[platformKey];
75
83
 
76
84
  if (!packageName) {
77
- console.warn(`⚠️ aptove: Unsupported platform ${platformKey}`);
78
- console.warn(' Supported: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64');
79
- return;
85
+ warn(`⚠️ aptove: unsupported platform ${platformKey}`);
86
+ exitFn(0); return;
80
87
  }
81
88
 
82
- if (isBinaryInstalled(packageName)) {
83
- console.log(`✓ aptove installed successfully for ${platformKey}`);
84
- return;
89
+ const binaryName = process.platform === 'win32' ? 'aptove.exe' : 'aptove';
90
+ const shortName = packageName.split('/')[1]; // e.g. "aptove-darwin-arm64"
91
+
92
+ // postinstall.js lives at @aptove/aptove/postinstall.js
93
+ // platform binary lives at @aptove/aptove-darwin-arm64/bin/aptove (sibling package)
94
+ const siblingBinaryPath = path.join(baseDir, '..', shortName, 'bin', binaryName);
95
+
96
+ function tryInstall(version) {
97
+ const fn = installFn || ((pkg, ver) => {
98
+ const prefixFlag = npmPrefix ? `--prefix "${npmPrefix}"` : '';
99
+ execSync(
100
+ `npm install ${prefixFlag} --no-save --no-audit --no-fund "${pkg}@${ver}"`,
101
+ { stdio: 'inherit' }
102
+ );
103
+ });
104
+ try {
105
+ log(` Installing ${packageName}@${version}...`);
106
+ fn(packageName, version);
107
+ // GitHub artifact uploads strip execute permissions; restore them.
108
+ if (isBinaryPresent(siblingBinaryPath)) ensureExecutable(siblingBinaryPath);
109
+ return true;
110
+ } catch (e) {
111
+ return false;
112
+ }
85
113
  }
86
114
 
87
- // Optional dependency was not installed (common with `npm install -g`).
88
- // Install it explicitly.
89
- console.log(`⬇ aptove: platform package not found, installing ${packageName}...`);
90
- const version = getPackageVersion();
91
-
92
- try {
93
- installPlatformPackage(packageName, version);
94
- } catch (e) {
95
- console.error(`✗ aptove: failed to install ${packageName}@${version}`);
96
- console.error(` You can install it manually with:`);
97
- console.error(` npm install -g ${packageName}@${version}`);
98
- process.exit(1);
115
+ const expectedVersion = getExpectedVersion(packageJsonPath);
116
+
117
+ if (isBinaryPresent(siblingBinaryPath)) {
118
+ // Fix permissions in case the binary was published without the execute bit
119
+ // (GitHub Actions artifacts do not preserve file permissions).
120
+ ensureExecutable(siblingBinaryPath);
121
+ const installedVersion = getBinaryVersion(siblingBinaryPath);
122
+ if (installedVersion && expectedVersion && installedVersion !== expectedVersion) {
123
+ log(`⬆ aptove: updating platform binary ${installedVersion} → ${expectedVersion}...`);
124
+ if (!tryInstall(expectedVersion)) {
125
+ warn(`⚠️ aptove: update failed — run: npm install -g ${packageName}@${expectedVersion}`);
126
+ exitFn(0); return;
127
+ }
128
+ } else {
129
+ log(`✓ aptove ${installedVersion || '(unknown)'} installed successfully for ${platformKey}`);
130
+ exitFn(0); return;
131
+ }
132
+ } else {
133
+ log(`\n⬇ aptove: platform binary not found, installing ${packageName}...`);
134
+ if (!tryInstall(expectedVersion || 'latest')) {
135
+ warn(`\n⚠️ aptove: failed to install ${packageName}`);
136
+ warn(` Run manually: npm install -g ${packageName}${expectedVersion ? '@' + expectedVersion : ''}`);
137
+ exitFn(0); return;
138
+ }
99
139
  }
100
140
 
101
- // After explicit install, verify using the prefix path directly.
102
- // require.resolve cannot be used here because Node.js caches negative
103
- // module resolution results within the same process.
104
- if (isBinaryInstalledAtPrefix(packageName)) {
105
- console.log(`✓ aptove installed successfully for ${platformKey}`);
141
+ // Validate after install using sibling path directly.
142
+ // (require.resolve cannot be used here Node.js caches negative module
143
+ // resolution results within the same process.)
144
+ if (isBinaryPresent(siblingBinaryPath)) {
145
+ ensureExecutable(siblingBinaryPath);
146
+ const installedVersion = getBinaryVersion(siblingBinaryPath);
147
+ if (installedVersion) {
148
+ if (expectedVersion && installedVersion !== expectedVersion) {
149
+ warn(`⚠️ aptove: installed ${installedVersion} but expected ${expectedVersion} (may not be published yet)`);
150
+ } else {
151
+ log(`✓ aptove ${installedVersion} installed successfully for ${platformKey}`);
152
+ }
153
+ } else {
154
+ warn(`⚠️ aptove: binary present but failed to run — try: ${siblingBinaryPath} --version`);
155
+ }
106
156
  } else {
107
- console.error(`✗ aptove: binary not found after installing ${packageName}`);
108
- console.error(` Try: npm install -g ${packageName}@${version}`);
109
- process.exit(1);
157
+ warn(`\n⚠️ aptove: binary not found after installation`);
158
+ warn(` Run manually: npm install -g ${packageName}${expectedVersion ? '@' + expectedVersion : ''}`);
110
159
  }
160
+
161
+ exitFn(0);
111
162
  }
112
163
 
113
- main();
164
+ module.exports = { main, PLATFORM_PACKAGES, isBinaryPresent, ensureExecutable, getBinaryVersion, getExpectedVersion };
165
+
166
+ if (require.main === module) {
167
+ main();
168
+ }