@xai-official/grok 1.0.13 → 1.0.14

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/bin/grok CHANGED
@@ -1,149 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // Thin trampoline: resolves the grok binary and execs it.
3
- //
4
- // Resolution order:
5
- // 1. $GROK_HOME/bin/grok — canonical versioned symlink (installed by postinstall.js)
6
- // 2. bootstrap it from the per-platform @xai-official/grok-<platform> package,
7
- // decompressing the brotli payload straight into $GROK_HOME/bin
8
- // 3. last resort (no resolvable version or an unwritable home): decompress the
9
- // payload in place under node_modules and exec that
10
- //
11
- // Per-platform binaries ship brotli-compressed to stay under npm's ~200 MB
12
- // tarball ceiling. See sibling packages @xai-official/grok-*.
13
- const { spawn } = require('child_process');
14
- const path = require('path');
15
- const fs = require('fs');
16
- const os = require('os');
17
- const zlib = require('zlib');
18
-
19
- const pkgName = '@xai-official/grok';
20
- const IS_WINDOWS = process.platform === 'win32';
21
- const EXE = IS_WINDOWS ? '.exe' : '';
22
- const BIN_NAME = `grok${EXE}`;
23
- // $GROK_HOME/bin (else ~/.grok/bin), matching the Rust grok_home(), including
24
- // its canonicalized-home default (so a symlinked $HOME resolves the same way).
25
- function defaultGrokHome() {
26
- const home = os.homedir();
27
- try { return path.join(fs.realpathSync(home), '.grok'); } catch { return path.join(home, '.grok'); }
28
- }
29
- const GROK_HOME = process.env.GROK_HOME ?? defaultGrokHome();
30
- const CANONICAL_DIR = path.join(GROK_HOME, 'bin');
31
- const CANONICAL_PATH = path.join(CANONICAL_DIR, BIN_NAME);
32
-
33
- function readLocalVersion() {
34
- try { return require('../package.json').version; } catch { return undefined; }
35
- }
36
-
37
- // Resolve the per-platform sibling package's directory. Returns null if the
38
- // matching optional dependency wasn't installed (unsupported platform, or
39
- // npm refused to install optionalDependencies).
40
- function resolvePlatformPackageDir() {
41
- const platformPkg = `@xai-official/grok-${process.platform}-${process.arch}`;
42
- try {
43
- return path.dirname(require.resolve(`${platformPkg}/package.json`));
44
- } catch {
45
- return null;
46
- }
47
- }
48
-
49
- function writeVendorBinary(brPath, rawPath, destPath) {
50
- const tmp = destPath + `.tmp.${process.pid}`;
51
- try {
52
- if (fs.existsSync(brPath)) {
53
- fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brPath)));
54
- } else if (fs.existsSync(rawPath)) {
55
- fs.copyFileSync(rawPath, tmp);
56
- } else {
57
- return false;
58
- }
59
- if (!IS_WINDOWS) fs.chmodSync(tmp, 0o755);
60
- fs.renameSync(tmp, destPath);
61
- return true;
62
- } catch {
63
- return false;
64
- } finally {
65
- try { fs.unlinkSync(tmp); } catch {}
66
- }
67
- }
68
-
69
- function swapCanonical(versionedName, versionedPath) {
70
- if (!IS_WINDOWS) {
71
- const tmpLink = CANONICAL_PATH + `.link.${process.pid}`;
72
- try { fs.unlinkSync(tmpLink); } catch {}
73
- fs.symlinkSync(versionedName, tmpLink);
74
- fs.renameSync(tmpLink, CANONICAL_PATH);
75
- return;
76
- }
77
- const oldPath = CANONICAL_PATH + '.old';
78
- try { fs.unlinkSync(oldPath); } catch {}
79
- try {
80
- try { fs.unlinkSync(CANONICAL_PATH); } catch {}
81
- fs.copyFileSync(versionedPath, CANONICAL_PATH);
82
- } catch {
83
- fs.renameSync(CANONICAL_PATH, oldPath);
84
- try {
85
- fs.copyFileSync(versionedPath, CANONICAL_PATH);
86
- } catch {
87
- try { fs.renameSync(oldPath, CANONICAL_PATH); } catch {}
88
- throw new Error('locked');
89
- }
90
- }
91
- }
92
-
93
- function bootstrapCanonical(brPath, rawPath, version) {
94
- try {
95
- fs.mkdirSync(CANONICAL_DIR, { recursive: true });
96
- const versionedName = `grok-${version}${EXE}`;
97
- const versionedPath = path.join(CANONICAL_DIR, versionedName);
98
- if (!fs.existsSync(versionedPath) && !writeVendorBinary(brPath, rawPath, versionedPath)) {
99
- return null;
100
- }
101
- swapCanonical(versionedName, versionedPath);
102
- // null on a broken wire-up so the caller falls back to in-place launch.
103
- return fs.existsSync(CANONICAL_PATH) ? CANONICAL_PATH : null;
104
- } catch {
105
- return null;
106
- }
107
- }
108
-
109
- function resolveBinary() {
110
- if (fs.existsSync(CANONICAL_PATH)) return CANONICAL_PATH;
111
-
112
- const platformDir = resolvePlatformPackageDir();
113
- if (!platformDir) {
114
- console.error(`${pkgName}: no platform binary installed for ${process.platform}-${process.arch}.`);
115
- console.error(` Expected sibling package @xai-official/grok-${process.platform}-${process.arch}.`);
116
- console.error(` This usually means npm skipped optionalDependencies (e.g. --no-optional)`);
117
- console.error(` or the platform is not supported.`);
118
- process.exit(1);
119
- }
120
-
121
- const rawPath = path.join(platformDir, 'bin', BIN_NAME);
122
- const brPath = rawPath + '.br';
123
- const version = readLocalVersion();
124
-
125
- // Prefer the canonical layout, decompressing straight into CANONICAL_DIR so
126
- // no second uncompressed copy lands under node_modules.
127
- if (version) {
128
- const bootstrapped = bootstrapCanonical(brPath, rawPath, version);
129
- if (bootstrapped) return bootstrapped;
130
- }
131
-
132
- // Fallback (unresolved version or unwritable home): materialize in place.
133
- if (!fs.existsSync(rawPath) && !writeVendorBinary(brPath, rawPath, rawPath)) {
134
- console.error(`${pkgName}: missing binary at ${rawPath}`);
135
- process.exit(1);
136
- }
137
- return rawPath;
138
- }
139
-
140
- const execPath = resolveBinary();
141
- const childEnv = { ...process.env, GROK_MANAGED_BY_NPM: '1' };
142
- const child = spawn(execPath, process.argv.slice(2), { stdio: 'inherit', env: childEnv });
143
- child.on('exit', (code, signal) => {
144
- if (signal) {
145
- process.kill(process.pid, signal);
146
- } else {
147
- process.exit(code ?? 0);
148
- }
149
- });
2
+ // On Unix npm installs, postinstall.js replaces this entry with a symlink to
3
+ // the sibling grok-native binary; every other install launches through node.
4
+ require('./grok-bootstrap.js');
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+ // Resolves the grok binary and runs it, in order of preference:
3
+ // 1. $GROK_HOME/bin/grok, the versioned symlink postinstall.js installs
4
+ // 2. bootstrap it from the per-platform @xai-official/grok-<platform>
5
+ // package, decompressing the compressed binary into $GROK_HOME/bin
6
+ // 3. decompress in place under node_modules (no resolvable version, or
7
+ // an unwritable home)
8
+ //
9
+ // Binaries ship brotli-compressed to stay under npm's tarball size limit.
10
+ const { spawn } = require('child_process');
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+ const os = require('os');
14
+ const zlib = require('zlib');
15
+
16
+ const pkgName = '@xai-official/grok';
17
+ const IS_WINDOWS = process.platform === 'win32';
18
+ const EXE = IS_WINDOWS ? '.exe' : '';
19
+ const BIN_NAME = `grok${EXE}`;
20
+ // $GROK_HOME/bin (else ~/.grok/bin), matching the Rust grok_home():
21
+ // a symlinked $HOME resolves the same way.
22
+ function defaultGrokHome() {
23
+ const home = os.homedir();
24
+ try { return path.join(fs.realpathSync(home), '.grok'); } catch { return path.join(home, '.grok'); }
25
+ }
26
+ const GROK_HOME = process.env.GROK_HOME ?? defaultGrokHome();
27
+ const CANONICAL_DIR = path.join(GROK_HOME, 'bin');
28
+ const CANONICAL_PATH = path.join(CANONICAL_DIR, BIN_NAME);
29
+
30
+ function readLocalVersion() {
31
+ try { return require('../package.json').version; } catch { return undefined; }
32
+ }
33
+
34
+ // Returns null when npm skipped the matching optional dependency
35
+ // (unsupported platform, or --no-optional).
36
+ function resolvePlatformPackageDir() {
37
+ const platformPkg = `@xai-official/grok-${process.platform}-${process.arch}`;
38
+ try {
39
+ return path.dirname(require.resolve(`${platformPkg}/package.json`));
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ function writeVendorBinary(brotliPath, binaryPath, destPath) {
46
+ const tmp = destPath + `.tmp.${process.pid}`;
47
+ try {
48
+ if (fs.existsSync(brotliPath)) {
49
+ fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brotliPath)));
50
+ } else if (fs.existsSync(binaryPath)) {
51
+ fs.copyFileSync(binaryPath, tmp);
52
+ } else {
53
+ return false;
54
+ }
55
+ if (!IS_WINDOWS) fs.chmodSync(tmp, 0o755);
56
+ fs.renameSync(tmp, destPath);
57
+ return true;
58
+ } catch {
59
+ return false;
60
+ } finally {
61
+ try { fs.unlinkSync(tmp); } catch {}
62
+ }
63
+ }
64
+
65
+ function swapCanonical(versionedName, versionedPath) {
66
+ if (!IS_WINDOWS) {
67
+ const tmpLink = CANONICAL_PATH + `.link.${process.pid}`;
68
+ try { fs.unlinkSync(tmpLink); } catch {}
69
+ fs.symlinkSync(versionedName, tmpLink);
70
+ fs.renameSync(tmpLink, CANONICAL_PATH);
71
+ return;
72
+ }
73
+ const oldPath = CANONICAL_PATH + '.old';
74
+ try { fs.unlinkSync(oldPath); } catch {}
75
+ try {
76
+ try { fs.unlinkSync(CANONICAL_PATH); } catch {}
77
+ fs.copyFileSync(versionedPath, CANONICAL_PATH);
78
+ } catch {
79
+ fs.renameSync(CANONICAL_PATH, oldPath);
80
+ try {
81
+ fs.copyFileSync(versionedPath, CANONICAL_PATH);
82
+ } catch {
83
+ try { fs.renameSync(oldPath, CANONICAL_PATH); } catch {}
84
+ throw new Error('locked');
85
+ }
86
+ }
87
+ }
88
+
89
+ function bootstrapCanonical(brotliPath, binaryPath, version) {
90
+ try {
91
+ fs.mkdirSync(CANONICAL_DIR, { recursive: true });
92
+ const versionedName = `grok-${version}${EXE}`;
93
+ const versionedPath = path.join(CANONICAL_DIR, versionedName);
94
+ if (!fs.existsSync(versionedPath) && !writeVendorBinary(brotliPath, binaryPath, versionedPath)) {
95
+ return null;
96
+ }
97
+ swapCanonical(versionedName, versionedPath);
98
+ // null on a broken wire-up so the caller falls back to in-place launch.
99
+ return fs.existsSync(CANONICAL_PATH) ? CANONICAL_PATH : null;
100
+ } catch {
101
+ return null;
102
+ }
103
+ }
104
+
105
+ function resolveBinary() {
106
+ if (fs.existsSync(CANONICAL_PATH)) return CANONICAL_PATH;
107
+
108
+ const platformDir = resolvePlatformPackageDir();
109
+ if (!platformDir) {
110
+ console.error(`${pkgName}: no platform binary installed for ${process.platform}-${process.arch}.`);
111
+ console.error(` Expected sibling package @xai-official/grok-${process.platform}-${process.arch}.`);
112
+ console.error(` This usually means npm skipped optionalDependencies (e.g. --no-optional)`);
113
+ console.error(` or the platform is not supported.`);
114
+ process.exit(1);
115
+ }
116
+
117
+ const binaryPath = path.join(platformDir, 'bin', BIN_NAME);
118
+ const brotliPath = binaryPath + '.br';
119
+ const version = readLocalVersion();
120
+
121
+ if (version) {
122
+ const bootstrapped = bootstrapCanonical(brotliPath, binaryPath, version);
123
+ if (bootstrapped) return bootstrapped;
124
+ }
125
+
126
+ if (!fs.existsSync(binaryPath) && !writeVendorBinary(brotliPath, binaryPath, binaryPath)) {
127
+ console.error(`${pkgName}: missing binary at ${binaryPath}`);
128
+ process.exit(1);
129
+ }
130
+ return binaryPath;
131
+ }
132
+
133
+ const execPath = resolveBinary();
134
+ const childEnv = { ...process.env, GROK_MANAGED_BY_NPM: '1' };
135
+ const child = spawn(execPath, process.argv.slice(2), { stdio: 'inherit', env: childEnv });
136
+ child.on('exit', (code, signal) => {
137
+ if (signal) {
138
+ process.kill(process.pid, signal);
139
+ } else {
140
+ process.exit(code ?? 0);
141
+ }
142
+ });
@@ -16,8 +16,8 @@ const zlib = require('zlib');
16
16
  const { execSync } = require('child_process');
17
17
  const TOML = require('@iarna/toml');
18
18
 
19
- // $GROK_HOME (else ~/.grok), matching the Rust grok_home() including its
20
- // canonicalized-home default. Lets fleets relocate the binary off a slow $HOME
19
+ // $GROK_HOME (else ~/.grok), matching the Rust grok_home(): a symlinked
20
+ // $HOME resolves the same way. Lets fleets relocate the binary off a slow $HOME
21
21
  // (NFS); old code hardcoded os.homedir().
22
22
  function defaultGrokHome() {
23
23
  const home = os.homedir();
@@ -65,13 +65,13 @@ const EXE = IS_WINDOWS ? '.exe' : '';
65
65
 
66
66
  fs.mkdirSync(CANONICAL_DIR, { recursive: true });
67
67
 
68
- function writeVendorBinary(brPath, rawPath, destPath) {
68
+ function writeVendorBinary(brotliPath, binaryPath, destPath) {
69
69
  const tmp = destPath + `.tmp.${process.pid}`;
70
70
  try {
71
- if (fs.existsSync(brPath)) {
72
- fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brPath)));
73
- } else if (fs.existsSync(rawPath)) {
74
- fs.copyFileSync(rawPath, tmp);
71
+ if (fs.existsSync(brotliPath)) {
72
+ fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brotliPath)));
73
+ } else if (fs.existsSync(binaryPath)) {
74
+ fs.copyFileSync(binaryPath, tmp);
75
75
  } else {
76
76
  return false;
77
77
  }
@@ -86,8 +86,8 @@ function writeVendorBinary(brPath, rawPath, destPath) {
86
86
  }
87
87
 
88
88
  function installBinary(binName, sourceDir, vendorSubpath) {
89
- const brPath = path.join(sourceDir, 'bin', vendorSubpath + '.br');
90
- const rawPath = path.join(sourceDir, 'bin', vendorSubpath);
89
+ const brotliPath = path.join(sourceDir, 'bin', vendorSubpath + '.br');
90
+ const binaryPath = path.join(sourceDir, 'bin', vendorSubpath);
91
91
 
92
92
  const versionedName = `${binName}-${version}${EXE}`;
93
93
  const versionedPath = path.join(CANONICAL_DIR, versionedName);
@@ -95,8 +95,8 @@ function installBinary(binName, sourceDir, vendorSubpath) {
95
95
  const canonicalPath = path.join(CANONICAL_DIR, canonicalName);
96
96
 
97
97
  // Skip if this exact version is already installed.
98
- if (!fs.existsSync(versionedPath) && !writeVendorBinary(brPath, rawPath, versionedPath)) {
99
- console.error(`@xai-official/grok: missing binary at ${brPath}`);
98
+ if (!fs.existsSync(versionedPath) && !writeVendorBinary(brotliPath, binaryPath, versionedPath)) {
99
+ console.error(`@xai-official/grok: missing binary at ${brotliPath}`);
100
100
  return false;
101
101
  }
102
102
 
@@ -186,7 +186,35 @@ if (!platformDir) {
186
186
  process.exit(0);
187
187
  }
188
188
 
189
- installBinary('grok', platformDir, `grok${EXE}`);
189
+ // Point the bin entry at a binary extracted beside it: launches become one
190
+ // process, and the link can only dangle if the package itself is broken.
191
+ // Windows keeps the node launcher; npm generates its command shims from it.
192
+ function installBinLink(platformDir) {
193
+ if (IS_WINDOWS) return;
194
+ // Other package managers wrap the entry's `#!` line in their own launchers.
195
+ if (!(process.env.npm_config_user_agent ?? '').startsWith('npm/')) return;
196
+ const brotliPath = path.join(platformDir, 'bin', `grok${EXE}.br`);
197
+ const binaryPath = path.join(platformDir, 'bin', `grok${EXE}`);
198
+ const nativePath = path.join(__dirname, 'grok-native');
199
+ const entryPath = path.join(__dirname, 'grok');
200
+ const tmp = entryPath + `.link.${process.pid}`;
201
+ try {
202
+ if (!writeVendorBinary(brotliPath, binaryPath, nativePath)) {
203
+ return;
204
+ }
205
+ try { fs.unlinkSync(tmp); } catch {}
206
+ fs.symlinkSync('./grok-native', tmp);
207
+ fs.renameSync(tmp, entryPath);
208
+ } catch (e) {
209
+ // Losing the link only costs latency; the node launcher still works.
210
+ console.error(`@xai-official/grok: bin link not installed: ${e.message}`);
211
+ try { fs.unlinkSync(tmp); } catch {}
212
+ }
213
+ }
214
+
215
+ if (installBinary('grok', platformDir, `grok${EXE}`)) {
216
+ installBinLink(platformDir);
217
+ }
190
218
  cleanupOldVersions('grok');
191
219
  cleanupOldVersions('grok-pager');
192
220
 
@@ -198,7 +226,7 @@ try { obj = TOML.parse(fs.readFileSync(configPath, 'utf8')); } catch { }
198
226
  obj.cli ??= {};
199
227
  obj.cli.installer = 'npm';
200
228
 
201
- // Persist the npm registry so `grok update` and the trampoline use the same one.
229
+ // Persist the npm registry so `grok update` and the launcher use the same one.
202
230
  const npmRegistry = process.env.GROK_NPM_REGISTRY
203
231
  || (() => {
204
232
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xai-official/grok",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Bring Grok into your terminal",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {
@@ -31,11 +31,11 @@
31
31
  "@iarna/toml": "^3.0.0"
32
32
  },
33
33
  "optionalDependencies": {
34
- "@xai-official/grok-darwin-arm64": "1.0.13",
35
- "@xai-official/grok-darwin-x64": "1.0.13",
36
- "@xai-official/grok-linux-arm64": "1.0.13",
37
- "@xai-official/grok-linux-x64": "1.0.13",
38
- "@xai-official/grok-win32-arm64": "1.0.13",
39
- "@xai-official/grok-win32-x64": "1.0.13"
34
+ "@xai-official/grok-darwin-arm64": "1.0.14",
35
+ "@xai-official/grok-darwin-x64": "1.0.14",
36
+ "@xai-official/grok-linux-arm64": "1.0.14",
37
+ "@xai-official/grok-linux-x64": "1.0.14",
38
+ "@xai-official/grok-win32-arm64": "1.0.14",
39
+ "@xai-official/grok-win32-x64": "1.0.14"
40
40
  }
41
41
  }