@bnomei/frigg 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.
Files changed (2) hide show
  1. package/bin/frigg.js +209 -0
  2. package/package.json +20 -0
package/bin/frigg.js ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ // npm distribution shim: downloads the platform release binary into a local cache and forwards argv.
3
+ "use strict";
4
+
5
+ const childProcess = require("child_process");
6
+ const crypto = require("crypto");
7
+ const fs = require("fs");
8
+ const https = require("https");
9
+ const os = require("os");
10
+ const path = require("path");
11
+
12
+ const packageJson = require("../package.json");
13
+
14
+ const BIN = "frigg";
15
+ const REPOSITORY = process.env.FRIGG_REPOSITORY || "bnomei/frigg";
16
+ const VERSION = normalizeVersion(process.env.FRIGG_VERSION || packageJson.version);
17
+
18
+ main().catch((error) => {
19
+ console.error(`frigg npm wrapper: ${error.message}`);
20
+ process.exit(1);
21
+ });
22
+
23
+ async function main() {
24
+ const release = releaseTarget();
25
+ const binaryPath = path.join(cacheDir(), VERSION, release.target, release.binary);
26
+
27
+ if (!fs.existsSync(binaryPath)) {
28
+ await installRelease(binaryPath, release);
29
+ }
30
+
31
+ const result = childProcess.spawnSync(binaryPath, process.argv.slice(2), {
32
+ stdio: "inherit",
33
+ });
34
+
35
+ if (result.error) {
36
+ throw result.error;
37
+ }
38
+
39
+ if (result.signal) {
40
+ process.kill(process.pid, result.signal);
41
+ return;
42
+ }
43
+
44
+ process.exit(result.status || 0);
45
+ }
46
+
47
+ function normalizeVersion(version) {
48
+ return version.startsWith("v") ? version : `v${version}`;
49
+ }
50
+
51
+ function releaseTarget() {
52
+ const platform = process.platform;
53
+ const arch = process.arch;
54
+
55
+ if (platform === "linux" && arch === "x64") {
56
+ return unixRelease("x86_64-unknown-linux-gnu");
57
+ }
58
+ if (platform === "linux" && arch === "arm64") {
59
+ return unixRelease("aarch64-unknown-linux-gnu");
60
+ }
61
+ if (platform === "darwin" && arch === "x64") {
62
+ return unixRelease("x86_64-apple-darwin");
63
+ }
64
+ if (platform === "darwin" && arch === "arm64") {
65
+ return unixRelease("aarch64-apple-darwin");
66
+ }
67
+ if (platform === "win32" && arch === "x64") {
68
+ return {
69
+ target: "x86_64-pc-windows-msvc",
70
+ archiveExt: ".zip",
71
+ binary: `${BIN}.exe`,
72
+ };
73
+ }
74
+
75
+ throw new Error(`unsupported platform ${platform}/${arch}`);
76
+ }
77
+
78
+ function unixRelease(target) {
79
+ return {
80
+ target,
81
+ archiveExt: ".tar.gz",
82
+ binary: BIN,
83
+ };
84
+ }
85
+
86
+ function cacheDir() {
87
+ if (process.env.FRIGG_NPM_CACHE) {
88
+ return process.env.FRIGG_NPM_CACHE;
89
+ }
90
+
91
+ if (process.platform === "win32") {
92
+ return path.join(process.env.LOCALAPPDATA || os.tmpdir(), "frigg", "npm");
93
+ }
94
+
95
+ return path.join(process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache"), "frigg", "npm");
96
+ }
97
+
98
+ async function installRelease(binaryPath, release) {
99
+ const archive = `${BIN}-${VERSION}-${release.target}${release.archiveExt}`;
100
+ const baseUrl = `https://github.com/${REPOSITORY}/releases/download/${VERSION}/${archive}`;
101
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "frigg-npm-"));
102
+
103
+ try {
104
+ const archivePath = path.join(tmp, archive);
105
+ const checksumPath = `${archivePath}.sha256`;
106
+
107
+ await download(`${baseUrl}.sha256`, checksumPath);
108
+ await download(baseUrl, archivePath);
109
+ verifyChecksum(archivePath, checksumPath);
110
+ extractArchive(archivePath, tmp, release.archiveExt);
111
+
112
+ const extracted = path.join(tmp, release.binary);
113
+ if (!fs.existsSync(extracted)) {
114
+ throw new Error(`release archive did not contain ${release.binary}`);
115
+ }
116
+
117
+ fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
118
+ fs.copyFileSync(extracted, binaryPath);
119
+ if (process.platform !== "win32") {
120
+ fs.chmodSync(binaryPath, 0o755);
121
+ }
122
+ } finally {
123
+ fs.rmSync(tmp, { force: true, recursive: true });
124
+ }
125
+ }
126
+
127
+ function download(url, destination, redirects = 0) {
128
+ return new Promise((resolve, reject) => {
129
+ const request = https.get(
130
+ url,
131
+ {
132
+ headers: {
133
+ "user-agent": "frigg-npm-wrapper",
134
+ },
135
+ },
136
+ (response) => {
137
+ if (
138
+ response.statusCode >= 300 &&
139
+ response.statusCode < 400 &&
140
+ response.headers.location
141
+ ) {
142
+ response.resume();
143
+ if (redirects > 5) {
144
+ reject(new Error(`too many redirects downloading ${url}`));
145
+ return;
146
+ }
147
+ download(response.headers.location, destination, redirects + 1).then(resolve, reject);
148
+ return;
149
+ }
150
+
151
+ if (response.statusCode !== 200) {
152
+ response.resume();
153
+ reject(new Error(`download failed with HTTP ${response.statusCode}: ${url}`));
154
+ return;
155
+ }
156
+
157
+ const file = fs.createWriteStream(destination);
158
+ response.pipe(file);
159
+ file.on("finish", () => file.close(resolve));
160
+ file.on("error", reject);
161
+ },
162
+ );
163
+
164
+ request.on("error", reject);
165
+ });
166
+ }
167
+
168
+ function verifyChecksum(archivePath, checksumPath) {
169
+ const expected = fs.readFileSync(checksumPath, "utf8").trim().split(/\s+/)[0].toLowerCase();
170
+ if (!/^[a-f0-9]{64}$/.test(expected)) {
171
+ throw new Error("checksum file did not contain a SHA-256 digest");
172
+ }
173
+
174
+ const actual = crypto.createHash("sha256").update(fs.readFileSync(archivePath)).digest("hex");
175
+ if (actual !== expected) {
176
+ throw new Error("checksum mismatch");
177
+ }
178
+ }
179
+
180
+ function extractArchive(archivePath, destination, archiveExt) {
181
+ if (archiveExt === ".tar.gz") {
182
+ childProcess.execFileSync("tar", ["-xzf", archivePath, "-C", destination], {
183
+ stdio: "ignore",
184
+ });
185
+ return;
186
+ }
187
+
188
+ const powershell = path.join(
189
+ process.env.SystemRoot || "C:\\Windows",
190
+ "System32",
191
+ "WindowsPowerShell",
192
+ "v1.0",
193
+ "powershell.exe",
194
+ );
195
+ childProcess.execFileSync(
196
+ powershell,
197
+ [
198
+ "-NoProfile",
199
+ "-NonInteractive",
200
+ "-ExecutionPolicy",
201
+ "Bypass",
202
+ "-Command",
203
+ "Expand-Archive -LiteralPath $args[0] -DestinationPath $args[1] -Force",
204
+ archivePath,
205
+ destination,
206
+ ],
207
+ { stdio: "ignore" },
208
+ );
209
+ }
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@bnomei/frigg",
3
+ "version": "0.6.0",
4
+ "description": "Frigg gives AI agents local, source-backed code search and navigation without sending whole repositories through every prompt.",
5
+ "homepage": "https://github.com/bnomei/frigg",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/bnomei/frigg.git"
9
+ },
10
+ "license": "MIT AND MPL-2.0",
11
+ "bin": {
12
+ "frigg": "bin/frigg.js"
13
+ },
14
+ "files": [
15
+ "bin/frigg.js"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18"
19
+ }
20
+ }