@bolt-foundry/gambit 0.5.5 → 0.6.0

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/CHANGELOG.md CHANGED
@@ -5,10 +5,22 @@ since = "b0ddcc2ba4e861e0879dc92cc713e8cde30d230f"
5
5
 
6
6
  # Changelog
7
7
 
8
- ## Unreleased (v0.5.6)
8
+ ## Unreleased (v0.6.1)
9
9
 
10
10
  - TBD
11
11
 
12
+ ## v0.6.0
13
+
14
+ ### Release and packaging
15
+
16
+ - Add an npm CLI wrapper that downloads prebuilt Gambit binaries with checksum
17
+ verification.
18
+ - Normalize release binary asset names and checksum generation for predictable
19
+ npm downloads.
20
+ - Fix the npm build to resolve gambit-core in the mirror layout and derive
21
+ import-map targets from the detected path.
22
+ - Add `@deno/dnt` to the Gambit package and refresh release/npm docs.
23
+
12
24
  ## v0.5.5
13
25
 
14
26
  ### Release and packaging
package/bin/gambit.cjs ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+ const https = require("https");
8
+ const http = require("http");
9
+ const crypto = require("crypto");
10
+ const { spawnSync } = require("child_process");
11
+
12
+ const MAX_REDIRECTS = 5;
13
+
14
+ const readJson = (filePath) => {
15
+ const raw = fs.readFileSync(filePath, "utf8");
16
+ return JSON.parse(raw);
17
+ };
18
+
19
+ const getPackageVersion = () => {
20
+ const pkgPath = path.join(__dirname, "..", "package.json");
21
+ const pkg = readJson(pkgPath);
22
+ if (!pkg.version) {
23
+ throw new Error(`Missing version in ${pkgPath}`);
24
+ }
25
+ return pkg.version;
26
+ };
27
+
28
+ const getPlatformAsset = () => {
29
+ const { platform, arch } = process;
30
+ if (platform === "darwin" && arch === "arm64") {
31
+ return "gambit-darwin-arm64";
32
+ }
33
+ if (platform === "darwin" && arch === "x64") {
34
+ return "gambit-darwin-x64";
35
+ }
36
+ if (platform === "linux" && arch === "x64") {
37
+ return "gambit-linux-x64";
38
+ }
39
+ if (platform === "linux" && arch === "arm64") {
40
+ return "gambit-linux-arm64";
41
+ }
42
+ return "";
43
+ };
44
+
45
+ const getCacheDir = (version) => {
46
+ const cacheRoot = process.env.XDG_CACHE_HOME ||
47
+ (process.env.HOME ? path.join(process.env.HOME, ".cache") : os.tmpdir());
48
+ return path.join(cacheRoot, "bolt-foundry", "gambit", version);
49
+ };
50
+
51
+ const request = (url, redirectCount = 0) =>
52
+ new Promise((resolve, reject) => {
53
+ const client = url.startsWith("https:") ? https : http;
54
+ const req = client.get(url, (res) => {
55
+ const status = res.statusCode || 0;
56
+ if (
57
+ status >= 300 && status < 400 && res.headers.location &&
58
+ redirectCount < MAX_REDIRECTS
59
+ ) {
60
+ const nextUrl = new URL(res.headers.location, url).toString();
61
+ res.resume();
62
+ resolve(request(nextUrl, redirectCount + 1));
63
+ return;
64
+ }
65
+ if (status !== 200) {
66
+ res.resume();
67
+ reject(new Error(`HTTP ${status} for ${url}`));
68
+ return;
69
+ }
70
+ resolve(res);
71
+ });
72
+ req.on("error", reject);
73
+ });
74
+
75
+ const downloadFile = async (url, dest) => {
76
+ const tmpDest = `${dest}.tmp-${process.pid}`;
77
+ await fs.promises.mkdir(path.dirname(dest), { recursive: true });
78
+ const res = await request(url);
79
+ await new Promise((resolve, reject) => {
80
+ const file = fs.createWriteStream(tmpDest, { mode: 0o755 });
81
+ res.pipe(file);
82
+ res.on("error", (err) => {
83
+ file.close(() => reject(err));
84
+ });
85
+ file.on("finish", () => file.close(resolve));
86
+ file.on("error", reject);
87
+ });
88
+ await fs.promises.rename(tmpDest, dest);
89
+ };
90
+
91
+ const fetchText = async (url) => {
92
+ const res = await request(url);
93
+ const chunks = [];
94
+ for await (const chunk of res) {
95
+ chunks.push(chunk);
96
+ }
97
+ return Buffer.concat(chunks).toString("utf8");
98
+ };
99
+
100
+ const sha256File = (filePath) =>
101
+ new Promise((resolve, reject) => {
102
+ const hash = crypto.createHash("sha256");
103
+ const stream = fs.createReadStream(filePath);
104
+ stream.on("data", (chunk) => hash.update(chunk));
105
+ stream.on("error", reject);
106
+ stream.on("end", () => resolve(hash.digest("hex")));
107
+ });
108
+
109
+ const allowUnverified = process.env.GAMBIT_ALLOW_UNVERIFIED === "1";
110
+
111
+ const verifyChecksum = async (checksumUrl, assetName, filePath) => {
112
+ let text;
113
+ try {
114
+ text = await fetchText(checksumUrl);
115
+ } catch (err) {
116
+ if (allowUnverified) {
117
+ console.warn(`Warning: unable to fetch checksums: ${err.message}`);
118
+ return;
119
+ }
120
+ throw new Error(`Unable to fetch checksums: ${err.message}`);
121
+ }
122
+ const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
123
+ const match = lines
124
+ .map((line) => line.match(/^([a-f0-9]{64})\s+(.+)$/i))
125
+ .find((result) => result && path.posix.basename(result[2]) === assetName);
126
+ if (!match) {
127
+ if (allowUnverified) {
128
+ console.warn(`Warning: checksum for ${assetName} not found.`);
129
+ return;
130
+ }
131
+ throw new Error(`Checksum for ${assetName} not found.`);
132
+ }
133
+ const expected = match[1].toLowerCase();
134
+ const actual = (await sha256File(filePath)).toLowerCase();
135
+ if (expected !== actual) {
136
+ throw new Error(`Checksum mismatch for ${assetName}`);
137
+ }
138
+ };
139
+
140
+ const main = async () => {
141
+ const version = getPackageVersion();
142
+ const assetName = process.env.GAMBIT_BINARY_NAME || getPlatformAsset();
143
+ if (!assetName) {
144
+ console.error(
145
+ `Unsupported platform ${process.platform}/${process.arch}.`,
146
+ );
147
+ process.exit(1);
148
+ }
149
+
150
+ const baseUrl = process.env.GAMBIT_BINARY_BASE_URL ||
151
+ `https://github.com/bolt-foundry/gambit/releases/download/v${version}`;
152
+ const binaryUrl = process.env.GAMBIT_BINARY_URL || `${baseUrl}/${assetName}`;
153
+ const checksumUrl = process.env.GAMBIT_BINARY_CHECKSUM_URL ||
154
+ `${baseUrl}/SHA256SUMS`;
155
+
156
+ const cacheDir = getCacheDir(version);
157
+ const binPath = path.join(cacheDir, assetName);
158
+ const ensureBinary = async () => {
159
+ if (fs.existsSync(binPath)) {
160
+ try {
161
+ await verifyChecksum(checksumUrl, assetName, binPath);
162
+ return;
163
+ } catch (err) {
164
+ console.warn(
165
+ `Cached binary failed checksum; deleting and re-downloading.`,
166
+ );
167
+ try {
168
+ await fs.promises.unlink(binPath);
169
+ } catch {
170
+ // ignore
171
+ }
172
+ }
173
+ }
174
+ console.log(`Downloading ${binaryUrl}...`);
175
+ await downloadFile(binaryUrl, binPath);
176
+ try {
177
+ await verifyChecksum(checksumUrl, assetName, binPath);
178
+ } catch (err) {
179
+ try {
180
+ await fs.promises.unlink(binPath);
181
+ } catch {
182
+ // ignore
183
+ }
184
+ throw err;
185
+ }
186
+ };
187
+
188
+ try {
189
+ await ensureBinary();
190
+ } catch (err) {
191
+ console.error(`Checksum verification failed: ${err.message}`);
192
+ process.exit(1);
193
+ }
194
+
195
+ try {
196
+ await fs.promises.chmod(binPath, 0o755);
197
+ } catch {
198
+ // ignore
199
+ }
200
+
201
+ const result = spawnSync(binPath, process.argv.slice(2), {
202
+ stdio: "inherit",
203
+ });
204
+ if (result.error) {
205
+ console.error(`Failed to launch gambit: ${result.error.message}`);
206
+ process.exit(1);
207
+ }
208
+ process.exit(result.status ?? 1);
209
+ };
210
+
211
+ main().catch((err) => {
212
+ console.error(err.stack || err.message || String(err));
213
+ process.exit(1);
214
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolt-foundry/gambit",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "Agent harness framework for building, running, and verifying LLM workflows in Markdown and code.",
5
5
  "homepage": "https://github.com/bolt-foundry/gambit",
6
6
  "repository": {
@@ -20,8 +20,11 @@
20
20
  }
21
21
  },
22
22
  "scripts": {},
23
+ "bin": {
24
+ "gambit": "bin/gambit.cjs"
25
+ },
23
26
  "dependencies": {
24
- "@bolt-foundry/gambit-core": "^0.5.5",
27
+ "@bolt-foundry/gambit-core": "^0.6.0",
25
28
  "openai": "^4.78.1",
26
29
  "zod": "^3.23.8",
27
30
  "zod-to-json-schema": "^3.23.0",