@safedep/gryph 0.0.1

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/.npmignore ADDED
@@ -0,0 +1,17 @@
1
+ # Exclude the actual binary (but keep the wrapper script)
2
+ bin/gryph
3
+ bin/gryph.exe
4
+
5
+ # Exclude temp files and directories
6
+ temp/
7
+ .temp/
8
+
9
+ # Exclude development files
10
+ node_modules/
11
+ .git/
12
+ .gitignore
13
+ *.log
14
+ .DS_Store
15
+
16
+ # Keep only the wrapper script in bin/
17
+ !bin/gryph.js
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Gryph
2
+
3
+ AI coding agents read files, write code, and execute commands on your behalf. But what exactly did they do?
4
+
5
+ **Gryph** is a local-first audit trail for AI coding agents. It hooks into your agents, logs every action to a local SQLite database, and gives you powerful querying capabilities to understand, review, and debug agent activity.
6
+
7
+ ## Why Gryph?
8
+
9
+ - **Transparency** - See exactly what files were read, written, and what commands were run
10
+ - **Pre-commit review** - Verify agent changes before committing to git
11
+ - **Debugging** - Replay sessions to understand what went wrong
12
+ - **Privacy** - All data stays local. No cloud, no telemetry
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install -g @safedep/gryph
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```bash
23
+ # Install hooks for all detected agents
24
+ gryph install
25
+
26
+ # Verify installation
27
+ gryph status
28
+
29
+ # Start using your AI coding agent (Claude Code, Cursor, Gemini CLI, etc.)
30
+ # ...
31
+
32
+ # Review what happened
33
+ gryph logs
34
+ ```
35
+
36
+
37
+ See more details at our [GitHub Project](https://github.com/safedep/gryph)
package/bin/gryph.js ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { spawn } = require("child_process");
6
+ const { ORG_NAME, PACKAGE_NAME, BINARY_NAME } = require("../config");
7
+
8
+ const BINARY_NAME_WITH_EXT =
9
+ process.platform === "win32" ? `${BINARY_NAME}.exe` : BINARY_NAME;
10
+ const BINARY_PATH = path.join(__dirname, BINARY_NAME_WITH_EXT);
11
+
12
+ function main() {
13
+ // Check if binary exists
14
+ if (!fs.existsSync(BINARY_PATH)) {
15
+ console.error(`❌ ${BINARY_NAME_WITH_EXT} binary not found`);
16
+ console.error(
17
+ `Try reinstalling: npm install -g ${ORG_NAME}/${PACKAGE_NAME}`,
18
+ );
19
+ process.exit(1);
20
+ }
21
+
22
+ // Verify binary is executable
23
+ try {
24
+ fs.accessSync(BINARY_PATH, fs.constants.F_OK | fs.constants.X_OK);
25
+ } catch (error) {
26
+ console.error(`❌ ${BINARY_NAME_WITH_EXT} is not executable`);
27
+ console.error(
28
+ `Try reinstalling: npm install -g ${ORG_NAME}/${PACKAGE_NAME}`,
29
+ );
30
+ process.exit(1);
31
+ }
32
+
33
+ // Pass all arguments to the binary
34
+ const args = process.argv.slice(2);
35
+
36
+ // Spawn the binary with inherited stdio for proper terminal interaction
37
+ const child = spawn(BINARY_PATH, args, {
38
+ stdio: "inherit",
39
+ windowsHide: false,
40
+ });
41
+
42
+ // Handle process termination
43
+ child.on("error", (error) => {
44
+ console.error(
45
+ `❌ Failed to execute ${BINARY_NAME_WITH_EXT}: ${error.message}`,
46
+ );
47
+ console.error(
48
+ `Try reinstalling: npm install -g ${ORG_NAME}/${PACKAGE_NAME}`,
49
+ );
50
+ process.exit(1);
51
+ });
52
+
53
+ // Exit with the same code as the child process
54
+ child.on("exit", (code, signal) => {
55
+ if (signal) {
56
+ process.kill(process.pid, signal);
57
+ } else {
58
+ process.exit(code || 0);
59
+ }
60
+ });
61
+
62
+ // Handle termination signals
63
+ process.on("SIGTERM", () => {
64
+ child.kill("SIGTERM");
65
+ });
66
+
67
+ process.on("SIGINT", () => {
68
+ child.kill("SIGINT");
69
+ });
70
+ }
71
+
72
+ if (require.main === module) {
73
+ main();
74
+ }
75
+
76
+ module.exports = { main };
package/config.js ADDED
@@ -0,0 +1,34 @@
1
+ // Configuration for npm binary wrapper
2
+
3
+ const ORG_NAME = "@safedep";
4
+ const PACKAGE_NAME = "gryph";
5
+ const BINARY_NAME = "gryph";
6
+
7
+ // GitHub repository information for releases
8
+ const REPO_OWNER = "safedep";
9
+ const REPO_NAME = "gryph";
10
+
11
+ // GitHub releases base URL (constructed from repo info)
12
+ const GITHUB_RELEASES_BASE = `https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download`;
13
+
14
+ // Platform-specific binary filename patterns (GoReleaser format)
15
+ const BINARY_PATTERNS = {
16
+ "darwin-x64": `${BINARY_NAME}_Darwin_all.tar.gz`,
17
+ "darwin-arm64": `${BINARY_NAME}_Darwin_all.tar.gz`,
18
+ "linux-x64": `${BINARY_NAME}_Linux_x86_64.tar.gz`,
19
+ "linux-arm64": `${BINARY_NAME}_Linux_arm64.tar.gz`,
20
+ "linux-ia32": `${BINARY_NAME}_Linux_i386.tar.gz`,
21
+ "win32-x64": `${BINARY_NAME}_Windows_x86_64.zip`,
22
+ "win32-arm64": `${BINARY_NAME}_Windows_arm64.zip`,
23
+ "win32-ia32": `${BINARY_NAME}_Windows_i386.zip`,
24
+ };
25
+
26
+ module.exports = {
27
+ ORG_NAME,
28
+ PACKAGE_NAME,
29
+ BINARY_NAME,
30
+ REPO_OWNER,
31
+ REPO_NAME,
32
+ GITHUB_RELEASES_BASE,
33
+ BINARY_PATTERNS,
34
+ };
package/install.js ADDED
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+ const https = require("https");
7
+ const crypto = require("crypto");
8
+ const { execSync } = require("child_process");
9
+ const {
10
+ BINARY_NAME,
11
+ REPO_OWNER,
12
+ REPO_NAME,
13
+ GITHUB_RELEASES_BASE,
14
+ BINARY_PATTERNS,
15
+ } = require("./config");
16
+
17
+ // Read version from package.json with strict validation
18
+ function getValidatedVersion() {
19
+ try {
20
+ const packageJson = JSON.parse(
21
+ fs.readFileSync(path.join(__dirname, "package.json"), "utf8"),
22
+ );
23
+
24
+ const version = packageJson.version;
25
+
26
+ // Strict validation: must be valid semver (x.y.z)
27
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
28
+ throw new Error(`Invalid version format: ${version}`);
29
+ }
30
+
31
+ return `v${version}`;
32
+ } catch (error) {
33
+ throw new Error(`Failed to read valid version: ${error.message}`);
34
+ }
35
+ }
36
+
37
+ const RELEASE_VERSION = getValidatedVersion();
38
+ const BASE_URL = `${GITHUB_RELEASES_BASE}/${RELEASE_VERSION}`;
39
+
40
+ // Platform-specific binary URLs (constructed from config)
41
+ const BINARY_URLS = {};
42
+ Object.keys(BINARY_PATTERNS).forEach((platform) => {
43
+ BINARY_URLS[platform] = `${BASE_URL}/${BINARY_PATTERNS[platform]}`;
44
+ });
45
+
46
+ const CHECKSUMS_URL = `${BASE_URL}/checksums.txt`;
47
+
48
+ function getPlatformKey() {
49
+ const platform = process.platform;
50
+ const arch = process.arch;
51
+ return `${platform}-${arch}`;
52
+ }
53
+
54
+ function downloadFile(url, dest, maxRedirects = 5) {
55
+ return new Promise((resolve, reject) => {
56
+ if (maxRedirects < 0) {
57
+ reject(new Error("Too many redirects"));
58
+ return;
59
+ }
60
+
61
+ const file = fs.createWriteStream(dest);
62
+
63
+ https
64
+ .get(url, (response) => {
65
+ if (response.statusCode === 302 || response.statusCode === 301) {
66
+ file.close();
67
+ fs.unlink(dest, () => { });
68
+ return downloadFile(response.headers.location, dest, maxRedirects - 1)
69
+ .then(resolve)
70
+ .catch(reject);
71
+ }
72
+
73
+ if (response.statusCode !== 200) {
74
+ file.close();
75
+ fs.unlink(dest, () => { });
76
+ reject(new Error(`Download failed: ${response.statusCode}`));
77
+ return;
78
+ }
79
+
80
+ response.pipe(file);
81
+
82
+ file.on("finish", () => {
83
+ file.close();
84
+ resolve();
85
+ });
86
+
87
+ file.on("error", (err) => {
88
+ fs.unlink(dest, () => { });
89
+ reject(err);
90
+ });
91
+ })
92
+ .on("error", reject);
93
+ });
94
+ }
95
+
96
+ function calculateChecksum(filePath) {
97
+ const fileBuffer = fs.readFileSync(filePath);
98
+ const hashSum = crypto.createHash("sha256");
99
+ hashSum.update(fileBuffer);
100
+ return hashSum.digest("hex");
101
+ }
102
+
103
+ function validateChecksum(filePath, expectedChecksum) {
104
+ const actualChecksum = calculateChecksum(filePath);
105
+ return actualChecksum === expectedChecksum;
106
+ }
107
+
108
+ function extractArchive(archivePath, extractDir) {
109
+ const isZip = archivePath.endsWith(".zip");
110
+
111
+ if (isZip) {
112
+ execSync(`unzip -o "${archivePath}" -d "${extractDir}"`, { stdio: "pipe" });
113
+ } else {
114
+ execSync(`tar -xzf "${archivePath}" -C "${extractDir}"`, { stdio: "pipe" });
115
+ }
116
+ }
117
+
118
+ async function install() {
119
+ let tempWorkspace;
120
+
121
+ try {
122
+ console.log("📦 Installing Gryph binary...");
123
+
124
+ // Get platform-specific URL
125
+ const platformKey = getPlatformKey();
126
+ const binaryUrl = BINARY_URLS[platformKey];
127
+
128
+ if (!binaryUrl) {
129
+ throw new Error(`Unsupported platform: ${platformKey}`);
130
+ }
131
+
132
+ console.log(`🔍 Platform: ${platformKey}`);
133
+ console.log(`📡 Version: ${RELEASE_VERSION}`);
134
+
135
+ // Create directories
136
+ const binDir = path.join(__dirname, "bin");
137
+ tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), "gryph-install-"));
138
+
139
+ fs.mkdirSync(binDir, { recursive: true });
140
+
141
+ // Download binary archive
142
+ const archiveFilename = path.basename(binaryUrl);
143
+ const archivePath = path.join(tempWorkspace, archiveFilename);
144
+
145
+ console.log(`⬇️ Downloading binary...`);
146
+ await downloadFile(binaryUrl, archivePath);
147
+
148
+ // Download checksums
149
+ const checksumsPath = path.join(tempWorkspace, "checksums.txt");
150
+ console.log(`⬇️ Downloading checksums...`);
151
+ await downloadFile(CHECKSUMS_URL, checksumsPath);
152
+
153
+ // Parse checksums file
154
+ const checksumsContent = fs.readFileSync(checksumsPath, "utf8");
155
+ const checksumLines = checksumsContent.split("\n");
156
+
157
+ let expectedChecksum = null;
158
+ for (const line of checksumLines) {
159
+ if (line.includes(archiveFilename)) {
160
+ expectedChecksum = line.split(/\s+/)[0];
161
+ break;
162
+ }
163
+ }
164
+
165
+ if (!expectedChecksum) {
166
+ throw new Error(`Checksum not found for ${archiveFilename}`);
167
+ }
168
+
169
+ // Validate checksum
170
+ console.log(`🔐 Validating checksum...`);
171
+ if (!validateChecksum(archivePath, expectedChecksum)) {
172
+ throw new Error(
173
+ "Checksum validation failed - binary may be corrupted or tampered",
174
+ );
175
+ }
176
+
177
+ console.log(`✅ Checksum validated`);
178
+
179
+ // Extract archive
180
+ console.log(`📂 Extracting binary...`);
181
+ extractArchive(archivePath, tempWorkspace);
182
+
183
+ // Find and move binary
184
+ const binaryName =
185
+ process.platform === "win32" ? `${BINARY_NAME}.exe` : BINARY_NAME;
186
+ const extractedBinaryPath = path.join(tempWorkspace, binaryName);
187
+ const finalBinaryPath = path.join(binDir, binaryName);
188
+
189
+ if (!fs.existsSync(extractedBinaryPath)) {
190
+ throw new Error(
191
+ `Binary not found at expected location: ${extractedBinaryPath}`,
192
+ );
193
+ }
194
+
195
+ // Move binary to final location (handle cross-device links)
196
+ try {
197
+ fs.renameSync(extractedBinaryPath, finalBinaryPath);
198
+ } catch (error) {
199
+ if (error.code === "EXDEV") {
200
+ // Cross-device link not permitted, copy and delete instead
201
+ fs.copyFileSync(extractedBinaryPath, finalBinaryPath);
202
+ fs.unlinkSync(extractedBinaryPath);
203
+ } else {
204
+ throw error;
205
+ }
206
+ }
207
+
208
+ // Make executable on Unix systems
209
+ if (process.platform !== "win32") {
210
+ fs.chmodSync(finalBinaryPath, "755");
211
+ }
212
+
213
+ // Clean up
214
+ fs.rmSync(tempWorkspace, { recursive: true, force: true });
215
+
216
+ console.log("✅ Gryph binary installed successfully!");
217
+ } catch (error) {
218
+ console.error("❌ Installation failed:", error.message);
219
+
220
+ // Clean up on failure
221
+ try {
222
+ if (tempWorkspace && fs.existsSync(tempWorkspace)) {
223
+ fs.rmSync(tempWorkspace, { recursive: true, force: true });
224
+ }
225
+ } catch (cleanupError) {
226
+ console.warn("⚠️ Failed to clean up:", cleanupError.message);
227
+ }
228
+
229
+ process.exit(1);
230
+ }
231
+ }
232
+
233
+ // Run installation
234
+ install();
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@safedep/gryph",
3
+ "description": "AI coding agent audit trail tool",
4
+ "main": "bin/gryph.js",
5
+ "bin": {
6
+ "gryph": "bin/gryph.js"
7
+ },
8
+ "scripts": {
9
+ "preinstall": "echo \"Installing Gryph binary for your platform...\"",
10
+ "postinstall": "node install.js"
11
+ },
12
+ "keywords": [
13
+ "security",
14
+ "package-manager",
15
+ "malicious-packages",
16
+ "npm",
17
+ "cli",
18
+ "vulnerability",
19
+ "dependency-security",
20
+ "safedep"
21
+ ],
22
+ "author": "SafeDep <devops@safedep.io>",
23
+ "license": "Apache-2.0",
24
+ "homepage": "https://github.com/safedep/gryph#readme",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/safedep/gryph.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/safedep/gryph/issues"
31
+ },
32
+ "engines": {
33
+ "node": ">=14"
34
+ },
35
+ "os": [
36
+ "darwin",
37
+ "linux",
38
+ "win32"
39
+ ],
40
+ "cpu": [
41
+ "x64",
42
+ "arm64",
43
+ "ia32"
44
+ ],
45
+ "files": [
46
+ "bin/gryph.js",
47
+ "install.js",
48
+ "config.js",
49
+ "test.js",
50
+ "README.md",
51
+ ".npmignore"
52
+ ],
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "dependencies": {},
57
+ "version": "0.0.1"
58
+ }