@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 +13 -1
- package/bin/gambit.cjs +214 -0
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,10 +5,22 @@ since = "b0ddcc2ba4e861e0879dc92cc713e8cde30d230f"
|
|
|
5
5
|
|
|
6
6
|
# Changelog
|
|
7
7
|
|
|
8
|
-
## Unreleased (v0.
|
|
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.
|
|
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.
|
|
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",
|