@ruelya/grok-build 1.0.1 → 1.0.2
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 +12 -14
- package/artifacts/BUILD_INFO.txt +2 -2
- package/bin/cli.mjs +45 -45
- package/bin/install.mjs +63 -28
- package/bin/platform.mjs +7 -1
- package/config_example.toml +3 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -24,13 +24,12 @@ grok --version # expect …-rev (…) [stable]
|
|
|
24
24
|
npx grok-build restore # restore previous binary from backup
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
| Platform key |
|
|
28
|
-
|
|
29
|
-
| `win32-x64` | `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `linux-arm64` | `artifacts/bin/linux-arm64/grok` |
|
|
27
|
+
| Platform key | Release asset |
|
|
28
|
+
|--------------|---------------|
|
|
29
|
+
| `win32-x64` | `grok-win32-x64.exe` |
|
|
30
|
+
| `linux-x64` | `grok-linux-x64` |
|
|
31
|
+
| `linux-arm64` | `grok-linux-arm64` |
|
|
32
|
+
| `darwin-arm64` | `grok-darwin-arm64` |
|
|
34
33
|
|
|
35
34
|
```bash
|
|
36
35
|
bash scripts/stage-binaries.sh win32-x64 ./target/release/xai-grok-pager.exe
|
|
@@ -277,13 +276,12 @@ grok --version # 期望 …-rev (…) [stable]
|
|
|
277
276
|
npx grok-build restore # 从备份恢复上一份二进制
|
|
278
277
|
```
|
|
279
278
|
|
|
280
|
-
| 平台键 |
|
|
281
|
-
|
|
282
|
-
| `win32-x64` | `
|
|
283
|
-
| `
|
|
284
|
-
| `
|
|
285
|
-
| `
|
|
286
|
-
| `linux-arm64` | `artifacts/bin/linux-arm64/grok` |
|
|
279
|
+
| 平台键 | Release 资源 |
|
|
280
|
+
|--------|--------------|
|
|
281
|
+
| `win32-x64` | `grok-win32-x64.exe` |
|
|
282
|
+
| `linux-x64` | `grok-linux-x64` |
|
|
283
|
+
| `linux-arm64` | `grok-linux-arm64` |
|
|
284
|
+
| `darwin-arm64` | `grok-darwin-arm64` |
|
|
287
285
|
|
|
288
286
|
```bash
|
|
289
287
|
bash scripts/stage-binaries.sh win32-x64 ./target/release/xai-grok-pager.exe
|
package/artifacts/BUILD_INFO.txt
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
version=1.0.
|
|
2
|
-
release=https://github.com/Ruelya/grok-build-rev/releases/download/v1.0.
|
|
1
|
+
version=1.0.2
|
|
2
|
+
release=https://github.com/Ruelya/grok-build-rev/releases/download/v1.0.2
|
package/bin/cli.mjs
CHANGED
|
@@ -197,65 +197,65 @@ function installThemes(home, { dryRun = false } = {}) {
|
|
|
197
197
|
* Replaces a previous notes block if present; never changes non-comment settings.
|
|
198
198
|
*/
|
|
199
199
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
200
|
+
* Point [cli] auto_update at our npm package (not official x.ai CDN).
|
|
201
|
+
* Sets auto_update = true and installer = "npm".
|
|
202
202
|
*/
|
|
203
|
-
function
|
|
203
|
+
function configureForkUpdater(home, { dryRun = false } = {}) {
|
|
204
204
|
const cfgPath = join(home, "config.toml");
|
|
205
|
+
const block =
|
|
206
|
+
`# Written by Grok Build fork install — updates via npm (@ruelya/grok-build).\n` +
|
|
207
|
+
`[cli]\nauto_update = true\ninstaller = "npm"\n`;
|
|
205
208
|
if (!existsSync(cfgPath)) {
|
|
206
209
|
if (dryRun) return { ok: true, action: "create-cli", path: cfgPath };
|
|
207
210
|
mkdirSync(home, { recursive: true });
|
|
208
|
-
writeFileSync(
|
|
209
|
-
cfgPath,
|
|
210
|
-
`# Written by Grok Build fork install — keep auto_update off so upstream cannot clobber the fork.\n[cli]\nauto_update = false\n`,
|
|
211
|
-
"utf8"
|
|
212
|
-
);
|
|
211
|
+
writeFileSync(cfgPath, block, "utf8");
|
|
213
212
|
return { ok: true, action: "create-cli", path: cfgPath };
|
|
214
213
|
}
|
|
215
214
|
let raw = readFileSync(cfgPath, "utf8").replace(/\r\n/g, "\n");
|
|
215
|
+
let changed = false;
|
|
216
216
|
|
|
217
|
-
// Already disabled somewhere (user may have multiple [cli] tables).
|
|
218
217
|
if (/^\s*auto_update\s*=\s*false\s*$/m.test(raw)) {
|
|
219
|
-
|
|
218
|
+
raw = raw.replace(/^\s*auto_update\s*=\s*false\s*$/gm, "auto_update = true");
|
|
219
|
+
changed = true;
|
|
220
|
+
} else if (!/^\s*auto_update\s*=/m.test(raw)) {
|
|
221
|
+
const cliIdx = raw.search(/^\[cli\]\s*$/m);
|
|
222
|
+
if (cliIdx >= 0) {
|
|
223
|
+
const lineEnd = raw.indexOf("\n", cliIdx);
|
|
224
|
+
const insertAt = lineEnd === -1 ? raw.length : lineEnd + 1;
|
|
225
|
+
raw = raw.slice(0, insertAt) + "auto_update = true\n" + raw.slice(insertAt);
|
|
226
|
+
} else {
|
|
227
|
+
raw = raw.replace(/\s*$/, "\n\n") + "[cli]\nauto_update = true\n";
|
|
228
|
+
}
|
|
229
|
+
changed = true;
|
|
220
230
|
}
|
|
221
231
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
if (/^\s*installer\s*=\s*".*"\s*$/m.test(raw)) {
|
|
233
|
+
const next = raw.replace(/^\s*installer\s*=\s*".*"\s*$/gm, 'installer = "npm"');
|
|
234
|
+
if (next !== raw) {
|
|
235
|
+
raw = next;
|
|
236
|
+
changed = true;
|
|
237
|
+
}
|
|
238
|
+
} else if (!/^\s*installer\s*=/m.test(raw)) {
|
|
239
|
+
const cliIdx = raw.search(/^\[cli\]\s*$/m);
|
|
240
|
+
if (cliIdx >= 0) {
|
|
241
|
+
const lineEnd = raw.indexOf("\n", cliIdx);
|
|
242
|
+
const insertAt = lineEnd === -1 ? raw.length : lineEnd + 1;
|
|
243
|
+
raw = raw.slice(0, insertAt) + 'installer = "npm"\n' + raw.slice(insertAt);
|
|
244
|
+
} else {
|
|
245
|
+
raw = raw.replace(/\s*$/, "\n\n") + '[cli]\ninstaller = "npm"\n';
|
|
230
246
|
}
|
|
231
|
-
|
|
232
|
-
return { ok: true, action: "flip-true", path: cfgPath };
|
|
247
|
+
changed = true;
|
|
233
248
|
}
|
|
234
249
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
let next;
|
|
238
|
-
let action;
|
|
239
|
-
if (cliIdx >= 0) {
|
|
240
|
-
const lineEnd = raw.indexOf("\n", cliIdx);
|
|
241
|
-
const insertAt = lineEnd === -1 ? raw.length : lineEnd + 1;
|
|
242
|
-
next =
|
|
243
|
-
raw.slice(0, insertAt) +
|
|
244
|
-
"auto_update = false\n" +
|
|
245
|
-
raw.slice(insertAt);
|
|
246
|
-
action = "insert-cli";
|
|
247
|
-
} else {
|
|
248
|
-
next = raw.replace(/\s*$/, "\n\n") + "[cli]\nauto_update = false\n";
|
|
249
|
-
action = "append-cli";
|
|
250
|
-
}
|
|
251
|
-
if (dryRun) return { ok: true, action, path: cfgPath };
|
|
250
|
+
if (!changed) return { ok: true, action: "already-fork-npm", path: cfgPath };
|
|
251
|
+
if (dryRun) return { ok: true, action: "update-cli", path: cfgPath };
|
|
252
252
|
try {
|
|
253
253
|
copyFileSync(cfgPath, cfgPath + `.bak-auto-update-${stamp()}`);
|
|
254
254
|
} catch {
|
|
255
255
|
/* ignore */
|
|
256
256
|
}
|
|
257
|
-
writeFileSync(cfgPath,
|
|
258
|
-
return { ok: true, action, path: cfgPath };
|
|
257
|
+
writeFileSync(cfgPath, raw, "utf8");
|
|
258
|
+
return { ok: true, action: "update-cli", path: cfgPath };
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
/**
|
|
@@ -435,7 +435,7 @@ function cmdInstallSideBySide({ dryRun = false, bootstrap = false } = {}) {
|
|
|
435
435
|
}
|
|
436
436
|
replaceBinary(fork, dest);
|
|
437
437
|
const installedThemes = installThemes(home, { dryRun: false });
|
|
438
|
-
const autoResult =
|
|
438
|
+
const autoResult = configureForkUpdater(home, { dryRun: false });
|
|
439
439
|
const syncResult = installUsageSyncExample(home, { dryRun: false });
|
|
440
440
|
console.log("\nInstalled fork side-by-side.");
|
|
441
441
|
console.log(` run : ${dest}`);
|
|
@@ -513,7 +513,7 @@ function cmdApply({ dryRun = false } = {}) {
|
|
|
513
513
|
|
|
514
514
|
const themeNames = installThemes(home, { dryRun: true });
|
|
515
515
|
const cfgPlan = installConfigNotes(home, { dryRun: true });
|
|
516
|
-
const autoPlan =
|
|
516
|
+
const autoPlan = configureForkUpdater(home, { dryRun: true });
|
|
517
517
|
const syncPlan = installUsageSyncExample(home, { dryRun: true });
|
|
518
518
|
|
|
519
519
|
console.log(`Installed : ${exe}`);
|
|
@@ -530,7 +530,7 @@ function cmdApply({ dryRun = false } = {}) {
|
|
|
530
530
|
console.log(`Config : ${cfgPlan.path} (${cfgPlan.action} comment block)`);
|
|
531
531
|
}
|
|
532
532
|
if (autoPlan.ok) {
|
|
533
|
-
console.log(`Auto-update: ${autoPlan.action} → [cli].auto_update =
|
|
533
|
+
console.log(`Auto-update: ${autoPlan.action} → [cli].auto_update=true installer=npm (@ruelya/grok-build)`);
|
|
534
534
|
}
|
|
535
535
|
if (syncPlan.ok) {
|
|
536
536
|
console.log(`Usage sync : ${syncPlan.action} → ${syncPlan.path}`);
|
|
@@ -538,7 +538,7 @@ function cmdApply({ dryRun = false } = {}) {
|
|
|
538
538
|
|
|
539
539
|
if (dryRun) {
|
|
540
540
|
console.log(
|
|
541
|
-
"\n[dry-run] would backup stock, install fork as primary grok + themes +
|
|
541
|
+
"\n[dry-run] would backup stock, install fork as primary grok + themes + enable npm auto_update + seed usage sync.toml. No changes made."
|
|
542
542
|
);
|
|
543
543
|
return;
|
|
544
544
|
}
|
|
@@ -570,7 +570,7 @@ function cmdApply({ dryRun = false } = {}) {
|
|
|
570
570
|
replaceBinary(fork, exe);
|
|
571
571
|
const installedThemes = installThemes(home, { dryRun: false });
|
|
572
572
|
const cfgResult = installConfigNotes(home, { dryRun: false });
|
|
573
|
-
const autoResult =
|
|
573
|
+
const autoResult = configureForkUpdater(home, { dryRun: false });
|
|
574
574
|
const syncResult = installUsageSyncExample(home, { dryRun: false });
|
|
575
575
|
|
|
576
576
|
const after = runExeVersion(exe);
|
|
@@ -602,7 +602,7 @@ function cmdApply({ dryRun = false } = {}) {
|
|
|
602
602
|
console.log(` config : ${cfgResult.action} → ${cfgResult.path}`);
|
|
603
603
|
}
|
|
604
604
|
if (autoResult.ok) {
|
|
605
|
-
console.log(` auto_update : ${autoResult.action} (
|
|
605
|
+
console.log(` auto_update : ${autoResult.action} (npm @ruelya/grok-build)`);
|
|
606
606
|
}
|
|
607
607
|
if (syncResult.ok) {
|
|
608
608
|
console.log(` usage sync : ${syncResult.action} → ${syncResult.path}`);
|
package/bin/install.mjs
CHANGED
|
@@ -178,40 +178,70 @@ function installThemes(home, { dryRun = false } = {}) {
|
|
|
178
178
|
return files.map((f) => f.replace(/\.toml$/, ""));
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Point in-app auto-update at our npm package (not official x.ai CDN).
|
|
183
|
+
*
|
|
184
|
+
* Writes:
|
|
185
|
+
* [cli]
|
|
186
|
+
* auto_update = true
|
|
187
|
+
* installer = "npm"
|
|
188
|
+
*
|
|
189
|
+
* The fork binary hard-blocks the official internal/CDN installer; this only
|
|
190
|
+
* ensures config matches so `grok update` uses `npm i -g @ruelya/grok-build`.
|
|
191
|
+
*/
|
|
192
|
+
function configureForkUpdater(home, { dryRun = false } = {}) {
|
|
182
193
|
const cfgPath = join(home, "config.toml");
|
|
194
|
+
const header =
|
|
195
|
+
"# Grok Build fork — auto_update via npm (@ruelya/grok-build), never official CDN.\n";
|
|
196
|
+
const block = `${header}[cli]\nauto_update = true\ninstaller = "npm"\n`;
|
|
197
|
+
|
|
183
198
|
if (!existsSync(cfgPath)) {
|
|
184
199
|
if (dryRun) return { ok: true, action: "create-cli" };
|
|
185
200
|
mkdirSync(home, { recursive: true });
|
|
186
|
-
writeFileSync(
|
|
187
|
-
cfgPath,
|
|
188
|
-
`# Grok Build fork — disable stock auto_update so it cannot overwrite the fork.\n[cli]\nauto_update = false\n`,
|
|
189
|
-
"utf8"
|
|
190
|
-
);
|
|
201
|
+
writeFileSync(cfgPath, block, "utf8");
|
|
191
202
|
return { ok: true, action: "create-cli" };
|
|
192
203
|
}
|
|
204
|
+
|
|
193
205
|
let raw = readFileSync(cfgPath, "utf8").replace(/\r\n/g, "\n");
|
|
206
|
+
let changed = false;
|
|
207
|
+
|
|
194
208
|
if (/^\s*auto_update\s*=\s*false\s*$/m.test(raw)) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
const
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
209
|
+
raw = raw.replace(/^\s*auto_update\s*=\s*false\s*$/gm, "auto_update = true");
|
|
210
|
+
changed = true;
|
|
211
|
+
} else if (!/^\s*auto_update\s*=/m.test(raw)) {
|
|
212
|
+
const cliIdx = raw.search(/^\[cli\]\s*$/m);
|
|
213
|
+
if (cliIdx >= 0) {
|
|
214
|
+
const lineEnd = raw.indexOf("\n", cliIdx);
|
|
215
|
+
const insertAt = lineEnd === -1 ? raw.length : lineEnd + 1;
|
|
216
|
+
raw = raw.slice(0, insertAt) + "auto_update = true\n" + raw.slice(insertAt);
|
|
217
|
+
} else {
|
|
218
|
+
raw = raw.replace(/\s*$/, "\n\n") + "[cli]\nauto_update = true\n";
|
|
219
|
+
}
|
|
220
|
+
changed = true;
|
|
202
221
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
222
|
+
|
|
223
|
+
if (/^\s*installer\s*=\s*".*"\s*$/m.test(raw)) {
|
|
224
|
+
const next = raw.replace(/^\s*installer\s*=\s*".*"\s*$/gm, 'installer = "npm"');
|
|
225
|
+
if (next !== raw) {
|
|
226
|
+
raw = next;
|
|
227
|
+
changed = true;
|
|
228
|
+
}
|
|
229
|
+
} else if (!/^\s*installer\s*=/m.test(raw)) {
|
|
230
|
+
const cliIdx = raw.search(/^\[cli\]\s*$/m);
|
|
231
|
+
if (cliIdx >= 0) {
|
|
232
|
+
const lineEnd = raw.indexOf("\n", cliIdx);
|
|
233
|
+
const insertAt = lineEnd === -1 ? raw.length : lineEnd + 1;
|
|
234
|
+
raw = raw.slice(0, insertAt) + 'installer = "npm"\n' + raw.slice(insertAt);
|
|
235
|
+
} else {
|
|
236
|
+
raw = raw.replace(/\s*$/, "\n\n") + '[cli]\ninstaller = "npm"\n';
|
|
237
|
+
}
|
|
238
|
+
changed = true;
|
|
211
239
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return { ok: true, action: "
|
|
240
|
+
|
|
241
|
+
if (!changed) return { ok: true, action: "already-fork-npm" };
|
|
242
|
+
if (dryRun) return { ok: true, action: "update-cli" };
|
|
243
|
+
writeFileSync(cfgPath, raw, "utf8");
|
|
244
|
+
return { ok: true, action: "update-cli" };
|
|
215
245
|
}
|
|
216
246
|
|
|
217
247
|
async function resolveSourceBinary() {
|
|
@@ -221,7 +251,7 @@ async function resolveSourceBinary() {
|
|
|
221
251
|
[
|
|
222
252
|
`Unsupported platform: ${t.key}`,
|
|
223
253
|
` shipped builds: ${supportedTargets().join(", ")}`,
|
|
224
|
-
` (Intel macOS
|
|
254
|
+
` (Intel macOS is not published — use win32-x64, linux-x64, linux-arm64, or darwin-arm64)`,
|
|
225
255
|
``,
|
|
226
256
|
`Override: GROK_FORK_BIN=/path/to/grok or GROK_FORK_RELEASE_BASE=https://…/releases/download/vX.Y.Z`,
|
|
227
257
|
].join("\n")
|
|
@@ -310,8 +340,9 @@ async function cmdInstall({ dryRun = false } = {}) {
|
|
|
310
340
|
}
|
|
311
341
|
|
|
312
342
|
const installedThemes = installThemes(home, { dryRun: false });
|
|
313
|
-
const auto =
|
|
343
|
+
const auto = configureForkUpdater(home, { dryRun: false });
|
|
314
344
|
const after = runVersion(exe);
|
|
345
|
+
const packageVersion = loadPkg().version || null;
|
|
315
346
|
|
|
316
347
|
const marker = {
|
|
317
348
|
appliedAt: new Date().toISOString(),
|
|
@@ -328,12 +359,14 @@ async function cmdInstall({ dryRun = false } = {}) {
|
|
|
328
359
|
themes: installedThemes,
|
|
329
360
|
autoUpdate: auto,
|
|
330
361
|
package: PKG_ROOT,
|
|
331
|
-
packageVersion
|
|
362
|
+
packageVersion,
|
|
363
|
+
npmPackage: "@ruelya/grok-build",
|
|
332
364
|
};
|
|
333
365
|
writeFileSync(join(home, MARKER), JSON.stringify(marker, null, 2) + "\n");
|
|
334
366
|
|
|
335
367
|
console.log("\nInstalled Grok Build fork as primary client.");
|
|
336
368
|
console.log(` after : ${after || "?"}`);
|
|
369
|
+
console.log(` pkg : @ruelya/grok-build@${packageVersion || "?"}`);
|
|
337
370
|
console.log(` run : ${exe}`);
|
|
338
371
|
console.log(` or : grok --version (if ~/.grok/bin is on PATH)`);
|
|
339
372
|
if (installedThemes.length) {
|
|
@@ -343,7 +376,9 @@ async function cmdInstall({ dryRun = false } = {}) {
|
|
|
343
376
|
} else {
|
|
344
377
|
console.log(" themes : (none bundled — package missing artifacts/themes/*.toml)");
|
|
345
378
|
}
|
|
346
|
-
if (auto.ok)
|
|
379
|
+
if (auto.ok) {
|
|
380
|
+
console.log(` update : npm installer (${auto.action}) — grok update → @ruelya/grok-build`);
|
|
381
|
+
}
|
|
347
382
|
}
|
|
348
383
|
|
|
349
384
|
function cmdRestore({ dryRun = false } = {}) {
|
package/bin/platform.mjs
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Release assets (v${version}):
|
|
8
8
|
* grok-win32-x64.exe
|
|
9
9
|
* grok-linux-x64
|
|
10
|
+
* grok-linux-arm64
|
|
10
11
|
* grok-darwin-arm64
|
|
11
12
|
*
|
|
12
13
|
* Override: GROK_FORK_RELEASE_BASE, package.json forkReleaseBase, GROK_FORK_BIN
|
|
@@ -23,7 +24,12 @@ export const PKG_ROOT = resolve(__dirname, "..");
|
|
|
23
24
|
const DEFAULT_REPO = "Ruelya/grok-build-rev";
|
|
24
25
|
|
|
25
26
|
/** Platforms we ship (no Intel macOS). */
|
|
26
|
-
export const SUPPORTED_TARGETS = [
|
|
27
|
+
export const SUPPORTED_TARGETS = [
|
|
28
|
+
"win32-x64",
|
|
29
|
+
"linux-x64",
|
|
30
|
+
"linux-arm64",
|
|
31
|
+
"darwin-arm64",
|
|
32
|
+
];
|
|
27
33
|
|
|
28
34
|
/** @returns {{ platform: string, arch: string, key: string, exeName: string }} */
|
|
29
35
|
export function detectTarget() {
|
package/config_example.toml
CHANGED
|
@@ -13,8 +13,9 @@ disabled_mcp_servers = []
|
|
|
13
13
|
# [cli]
|
|
14
14
|
# -----------------------------------------------------------------------------
|
|
15
15
|
[cli]
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
# Fork: keep true + installer=npm so updates use @ruelya/grok-build (never x.ai CDN)
|
|
17
|
+
auto_update = true
|
|
18
|
+
# installer = "npm" | "gh-release" | "internal"(blocked on fork)
|
|
18
19
|
installer = "npm"
|
|
19
20
|
# npm_registry = "https://registry.npmjs.org"
|
|
20
21
|
# channel = "alpha" | "stable"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruelya/grok-build",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Grok Build fork (rev). Thin npm installer — downloads native binary from GitHub Releases.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"access": "public",
|
|
49
49
|
"registry": "https://registry.npmjs.org/"
|
|
50
50
|
},
|
|
51
|
-
"forkReleaseBase": "https://github.com/Ruelya/grok-build-rev/releases/download/v1.0.
|
|
51
|
+
"forkReleaseBase": "https://github.com/Ruelya/grok-build-rev/releases/download/v1.0.2"
|
|
52
52
|
}
|