openkrew 0.2.28 → 0.2.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openkrew",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "Distributed multi-machine AI agent team platform",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -16,10 +16,12 @@
16
16
  "openkrew.mjs",
17
17
  "core-bootstrap.mjs",
18
18
  "scripts/check-global.mjs",
19
+ "scripts/postinstall.mjs",
19
20
  "LICENSE"
20
21
  ],
21
22
  "scripts": {
22
23
  "preinstall": "node scripts/check-global.mjs",
24
+ "postinstall": "node scripts/postinstall.mjs",
23
25
  "prebuild": "node scripts/sync-version.mjs",
24
26
  "version:sync": "node scripts/sync-version.mjs"
25
27
  },
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Post-install hook: pre-downloads the platform-specific .NET binary from S3
5
+ * so the first `openkrew` invocation doesn't have a cold-start download delay.
6
+ *
7
+ * Failures are non-fatal — the bootstrapper will retry on first run.
8
+ */
9
+
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, createWriteStream, chmodSync, rmSync } from "node:fs";
11
+ import { homedir, platform, arch } from "node:os";
12
+ import { join } from "node:path";
13
+ import { pipeline } from "node:stream/promises";
14
+ import { spawn } from "node:child_process";
15
+
16
+ const OPENKREW_DIR = join(homedir(), ".openkrew");
17
+ const BIN_DIR = join(OPENKREW_DIR, "bin");
18
+ const VERSION_FILE = join(BIN_DIR, ".version");
19
+ const S3_BASE = "https://openkrew-releases.s3.amazonaws.com";
20
+
21
+ const PACKAGE_VERSION = (() => {
22
+ try {
23
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
24
+ return pkg.version;
25
+ } catch {
26
+ return null;
27
+ }
28
+ })();
29
+
30
+ function getRuntimeId() {
31
+ const p = platform();
32
+ const a = arch();
33
+ const map = {
34
+ "darwin-arm64": "osx-arm64",
35
+ "darwin-x64": "osx-x64",
36
+ "linux-x64": "linux-x64",
37
+ "linux-arm64": "linux-arm64",
38
+ "win32-x64": "win-x64",
39
+ "win32-arm64": "win-arm64",
40
+ };
41
+ return map[`${p}-${a}`];
42
+ }
43
+
44
+ function getBinaryName() {
45
+ return platform() === "win32" ? "openkrew.exe" : "openkrew";
46
+ }
47
+
48
+ function getGatewayName() {
49
+ return platform() === "win32" ? "openkrew-gateway.exe" : "openkrew-gateway";
50
+ }
51
+
52
+ function isInstalled() {
53
+ const binaryPath = join(BIN_DIR, getBinaryName());
54
+ if (!existsSync(binaryPath)) return false;
55
+ try {
56
+ return readFileSync(VERSION_FILE, "utf8").trim() === PACKAGE_VERSION;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ async function downloadBinary() {
63
+ const rid = getRuntimeId();
64
+ if (!rid) return; // Unsupported platform — bootstrapper will handle error on first run
65
+
66
+ const url = `${S3_BASE}/stable/${rid}/openkrew.tar.gz`;
67
+
68
+ process.stderr.write(`\x1b[36mOpenKrew\x1b[0m: downloading ${rid} binary (v${PACKAGE_VERSION})...\n`);
69
+
70
+ mkdirSync(BIN_DIR, { recursive: true });
71
+
72
+ const response = await fetch(url);
73
+ if (!response.ok) {
74
+ process.stderr.write(`\x1b[33mWarning: failed to pre-download binary (HTTP ${response.status}). Will retry on first run.\x1b[0m\n`);
75
+ return;
76
+ }
77
+
78
+ const tmpFile = join(OPENKREW_DIR, `download-${Date.now()}.tar.gz`);
79
+
80
+ try {
81
+ const fileStream = createWriteStream(tmpFile);
82
+ await pipeline(response.body, fileStream);
83
+
84
+ const tarProcess = spawn("tar", ["xzf", tmpFile, "-C", BIN_DIR], { stdio: "inherit" });
85
+ await new Promise((resolve, reject) => {
86
+ tarProcess.on("close", (code) =>
87
+ code === 0 ? resolve() : reject(new Error(`tar exited with code ${code}`)));
88
+ tarProcess.on("error", reject);
89
+ });
90
+
91
+ if (platform() !== "win32") {
92
+ for (const name of [getBinaryName(), getGatewayName()]) {
93
+ const binPath = join(BIN_DIR, name);
94
+ if (existsSync(binPath)) chmodSync(binPath, 0o755);
95
+ }
96
+ }
97
+
98
+ if (platform() === "darwin") {
99
+ for (const name of [getBinaryName(), getGatewayName()]) {
100
+ const binPath = join(BIN_DIR, name);
101
+ if (existsSync(binPath)) {
102
+ try {
103
+ const codesign = spawn("codesign", ["--force", "--sign", "-", binPath], { stdio: "ignore" });
104
+ await new Promise((resolve) => codesign.on("close", resolve));
105
+ } catch {}
106
+ }
107
+ }
108
+ }
109
+
110
+ writeFileSync(VERSION_FILE, PACKAGE_VERSION);
111
+ process.stderr.write(`\x1b[32mOpenKrew v${PACKAGE_VERSION} ready.\x1b[0m\n`);
112
+ } finally {
113
+ try { rmSync(tmpFile); } catch {}
114
+ }
115
+ }
116
+
117
+ // ── Main ──────────────────────────────────────────────────────────────
118
+
119
+ try {
120
+ if (!PACKAGE_VERSION) {
121
+ process.exit(0); // Can't determine version, skip silently
122
+ }
123
+
124
+ if (!isInstalled()) {
125
+ await downloadBinary();
126
+ }
127
+ } catch (err) {
128
+ // Non-fatal — bootstrapper will retry on first run
129
+ process.stderr.write(`\x1b[33mWarning: post-install binary download failed: ${err.message}\x1b[0m\n`);
130
+ }