@sigmashake/ssg 0.29.86 → 0.29.89

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.
@@ -3,53 +3,55 @@
3
3
  // Also registers a per-install canary for distribution tracking.
4
4
  // This runs on `postinstall` to ensure npm takes precedence
5
5
 
6
- const fs = require('fs');
7
- const path = require('path');
8
- const os = require('os');
9
- const https = require('https');
10
- const crypto = require('crypto');
6
+ const fs = require("node:fs");
7
+ const path = require("node:path");
8
+ const os = require("node:os");
9
+ const https = require("node:https");
10
+ const crypto = require("node:crypto");
11
11
 
12
12
  // Only run cleanup if installed globally via npm (or we assume global if running in a global node_modules directory)
13
- const isGlobal =
14
- process.env.npm_config_global === 'true' ||
15
- __dirname.includes('/lib/node_modules/') ||
16
- __dirname.includes('\\npm\\node_modules\\');
13
+ const isGlobal =
14
+ process.env.npm_config_global === "true" ||
15
+ __dirname.includes("/lib/node_modules/") ||
16
+ __dirname.includes("\\npm\\node_modules\\");
17
17
 
18
18
  if (!isGlobal) {
19
- process.exit(0);
19
+ process.exit(0);
20
20
  }
21
21
 
22
22
  const homedir = os.homedir();
23
23
  const targets = [
24
- path.join(homedir, '.local', 'bin', 'ssg'),
25
- path.join(homedir, '.local', 'bin', 'libssg_eval.so'),
26
- path.join(homedir, '.bun', 'bin', 'ssg'),
24
+ path.join(homedir, ".local", "bin", "ssg"),
25
+ path.join(homedir, ".local", "bin", "libssg_eval.so"),
26
+ path.join(homedir, ".bun", "bin", "ssg"),
27
27
  ];
28
28
 
29
29
  let cleaned = false;
30
30
 
31
31
  for (const target of targets) {
32
- try {
33
- if (!fs.existsSync(target)) continue;
34
-
35
- // Do not delete if it actually belongs to this exact npm installation (symlink loopback check)
36
- // Sometimes package managers like bun map symlinks directly to the node_modules bin.
37
- const realPath = fs.realpathSync(target);
38
- if (realPath.includes(__dirname)) {
39
- continue;
40
- }
41
-
42
- // Attempt to remove the conflicting binary
43
- fs.rmSync(target, {force: true});
44
- console.log(`[ssg-cleanup] Removed conflicting global binary: ${target}`);
45
- cleaned = true;
46
- } catch (e) {
47
- console.warn(`[ssg-cleanup] Could not remove ${target}: ${e.message}`);
48
- }
32
+ try {
33
+ if (!fs.existsSync(target)) continue;
34
+
35
+ // Do not delete if it actually belongs to this exact npm installation (symlink loopback check)
36
+ // Sometimes package managers like bun map symlinks directly to the node_modules bin.
37
+ const realPath = fs.realpathSync(target);
38
+ if (realPath.includes(__dirname)) {
39
+ continue;
40
+ }
41
+
42
+ // Attempt to remove the conflicting binary
43
+ fs.rmSync(target, { force: true });
44
+ console.log(`[ssg-cleanup] Removed conflicting global binary: ${target}`);
45
+ cleaned = true;
46
+ } catch (e) {
47
+ console.warn(`[ssg-cleanup] Could not remove ${target}: ${e.message}`);
48
+ }
49
49
  }
50
50
 
51
51
  if (cleaned) {
52
- console.log('[ssg-cleanup] Successfully cleaned up old binary conflicts designed for local development.');
52
+ console.log(
53
+ "[ssg-cleanup] Successfully cleaned up old binary conflicts designed for local development.",
54
+ );
53
55
  }
54
56
 
55
57
  // ── Per-install canary registration ─────────────────────────────────────────
@@ -57,63 +59,73 @@ if (cleaned) {
57
59
  // the source installation if a cracked binary appears in the wild.
58
60
  // Fire-and-forget: never blocks or fails the install.
59
61
  (function registerCanary() {
60
- try {
61
- const homedir = os.homedir();
62
- const ssgDir = path.join(homedir, '.sigmashake');
63
- const canaryPath = path.join(ssgDir, '.canary');
64
-
65
- // Skip if canary already registered for this install
66
- if (fs.existsSync(canaryPath)) return;
67
-
68
- const pkg = require('../package.json');
69
- const version = pkg.version;
70
- const platform = process.platform;
71
- const arch = process.arch;
72
-
73
- // Generate a stable machine fingerprint (same signals as fingerprint.ts)
74
- let machineId = '';
75
- try {
76
- if (process.platform === 'linux') {
77
- const p = '/etc/machine-id';
78
- if (fs.existsSync(p)) machineId = fs.readFileSync(p, 'utf8').trim();
79
- }
80
- } catch { /* ignore */ }
81
-
82
- const fingerprint = crypto
83
- .createHash('sha256')
84
- .update([machineId, platform, arch, os.hostname(), homedir].join('|'))
85
- .digest('hex');
86
-
87
- const payload = JSON.stringify({fingerprint, version, platform, arch});
88
-
89
- const req = https.request(
90
- {
91
- hostname: 'api.sigmashake.com',
92
- path: '/v1/install/register',
93
- method: 'POST',
94
- headers: {
95
- 'Content-Type': 'application/json',
96
- 'Content-Length': Buffer.byteLength(payload),
97
- },
98
- timeout: 5000,
99
- },
100
- res => {
101
- const chunks = [];
102
- res.on('data', c => chunks.push(c));
103
- res.on('end', () => {
104
- try {
105
- const body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
106
- if (body && body.canary_id) {
107
- fs.mkdirSync(ssgDir, {recursive: true});
108
- fs.writeFileSync(canaryPath, body.canary_id, {mode: 0o600});
109
- }
110
- } catch { /* ignore parse errors */ }
111
- });
112
- },
113
- );
114
- req.on('error', () => { /* non-blocking, ignore network errors */ });
115
- req.on('timeout', () => { req.destroy(); });
116
- req.write(payload);
117
- req.end();
118
- } catch { /* never crash the install */ }
62
+ try {
63
+ const homedir = os.homedir();
64
+ const ssgDir = path.join(homedir, ".sigmashake");
65
+ const canaryPath = path.join(ssgDir, ".canary");
66
+
67
+ // Skip if canary already registered for this install
68
+ if (fs.existsSync(canaryPath)) return;
69
+
70
+ const pkg = require("../package.json");
71
+ const version = pkg.version;
72
+ const platform = process.platform;
73
+ const arch = process.arch;
74
+
75
+ // Generate a stable machine fingerprint (same signals as fingerprint.ts)
76
+ let machineId = "";
77
+ try {
78
+ if (process.platform === "linux") {
79
+ const p = "/etc/machine-id";
80
+ if (fs.existsSync(p)) machineId = fs.readFileSync(p, "utf8").trim();
81
+ }
82
+ } catch {
83
+ /* ignore */
84
+ }
85
+
86
+ const fingerprint = crypto
87
+ .createHash("sha256")
88
+ .update([machineId, platform, arch, os.hostname(), homedir].join("|"))
89
+ .digest("hex");
90
+
91
+ const payload = JSON.stringify({ fingerprint, version, platform, arch });
92
+
93
+ const req = https.request(
94
+ {
95
+ hostname: "api.sigmashake.com",
96
+ path: "/v1/install/register",
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/json",
100
+ "Content-Length": Buffer.byteLength(payload),
101
+ },
102
+ timeout: 5000,
103
+ },
104
+ (res) => {
105
+ const chunks = [];
106
+ res.on("data", (c) => chunks.push(c));
107
+ res.on("end", () => {
108
+ try {
109
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
110
+ if (body?.canary_id) {
111
+ fs.mkdirSync(ssgDir, { recursive: true });
112
+ fs.writeFileSync(canaryPath, body.canary_id, { mode: 0o600 });
113
+ }
114
+ } catch {
115
+ /* ignore parse errors */
116
+ }
117
+ });
118
+ },
119
+ );
120
+ req.on("error", () => {
121
+ /* non-blocking, ignore network errors */
122
+ });
123
+ req.on("timeout", () => {
124
+ req.destroy();
125
+ });
126
+ req.write(payload);
127
+ req.end();
128
+ } catch {
129
+ /* never crash the install */
130
+ }
119
131
  })();
package/bin/ssg.cjs CHANGED
@@ -2,13 +2,13 @@
2
2
  // bin/ssg.cjs — launcher for @sigmashake/ssg
3
3
  // Resolves the native binary from the platform-specific optional dependency,
4
4
  // falls back to a local dist/ build (dev environment), then to Bun + source.
5
- 'use strict';
5
+ "use strict";
6
6
 
7
- const {execFileSync, spawnSync} = require('child_process');
8
- const path = require('path');
9
- const fs = require('fs');
7
+ const { execFileSync, spawnSync } = require("node:child_process");
8
+ const path = require("node:path");
9
+ const fs = require("node:fs");
10
10
 
11
- const ext = process.platform === 'win32' ? '.exe' : '';
11
+ const ext = process.platform === "win32" ? ".exe" : "";
12
12
  const platformPkg = `@sigmashake/ssg-${process.platform}-${process.arch}`;
13
13
  let binaryPath;
14
14
 
@@ -18,30 +18,32 @@ let binaryPath;
18
18
  let hookBinPath;
19
19
  let evalServerBinPath;
20
20
  try {
21
- const pkgRoot = path.dirname(require.resolve(`${platformPkg}/package.json`));
22
- const candidate = path.join(pkgRoot, 'bin', `ssg${ext}`);
23
- if (fs.existsSync(candidate)) binaryPath = candidate;
24
- const hookCandidate = path.join(pkgRoot, 'bin', `ssg-hook-fast${ext}`);
25
- if (fs.existsSync(hookCandidate)) hookBinPath = hookCandidate;
26
- const sidecarCandidate = path.join(pkgRoot, 'bin', `ssg-eval-server${ext}`);
27
- if (fs.existsSync(sidecarCandidate)) evalServerBinPath = sidecarCandidate;
21
+ const pkgRoot = path.dirname(require.resolve(`${platformPkg}/package.json`));
22
+ const candidate = path.join(pkgRoot, "bin", `ssg${ext}`);
23
+ if (fs.existsSync(candidate)) binaryPath = candidate;
24
+ const hookCandidate = path.join(pkgRoot, "bin", `ssg-hook-fast${ext}`);
25
+ if (fs.existsSync(hookCandidate)) hookBinPath = hookCandidate;
26
+ const sidecarCandidate = path.join(pkgRoot, "bin", `ssg-eval-server${ext}`);
27
+ if (fs.existsSync(sidecarCandidate)) evalServerBinPath = sidecarCandidate;
28
28
  } catch {}
29
29
 
30
30
  // 2. Check if binary was bundled in the root (dev: local npm pack or manual build)
31
31
  if (!binaryPath) {
32
- const rootBin = path.resolve(__dirname, '..', `ssg${ext}`);
33
- if (fs.existsSync(rootBin)) {
34
- binaryPath = rootBin;
35
- }
32
+ const rootBin = path.resolve(__dirname, "..", `ssg${ext}`);
33
+ if (fs.existsSync(rootBin)) {
34
+ binaryPath = rootBin;
35
+ }
36
36
  }
37
37
 
38
38
  // 3. Fall back to local dist/ (dev build / bun build --compile)
39
39
  if (!binaryPath) {
40
- const devBin = path.resolve(
41
- __dirname, '..', 'dist',
42
- `ssg-${process.platform}-${process.arch}${ext}`,
43
- );
44
- if (fs.existsSync(devBin)) binaryPath = devBin;
40
+ const devBin = path.resolve(
41
+ __dirname,
42
+ "..",
43
+ "dist",
44
+ `ssg-${process.platform}-${process.arch}${ext}`,
45
+ );
46
+ if (fs.existsSync(devBin)) binaryPath = devBin;
45
47
  }
46
48
 
47
49
  // 2.5 Hook-eval fast-path short-circuit (perf):
@@ -60,79 +62,107 @@ if (!binaryPath) {
60
62
  // full bun-startup cost on macOS (~1.3 s), even though the actual eval
61
63
  // only takes ~40 ms.
62
64
  if (
63
- hookBinPath &&
64
- process.env['SSG_HOOK_NO_FAST'] !== '1' &&
65
- process.argv.length === 4 &&
66
- process.argv[2] === 'hook' &&
67
- process.argv[3] === 'eval'
65
+ hookBinPath &&
66
+ process.env.SSG_HOOK_NO_FAST !== "1" &&
67
+ process.argv.length === 4 &&
68
+ process.argv[2] === "hook" &&
69
+ process.argv[3] === "eval"
68
70
  ) {
69
- const env = { ...process.env, SSG_PUBLIC_DIR: path.resolve(__dirname, '..', 'public') };
70
- if (evalServerBinPath) env['SSG_EVAL_SERVER_BIN'] = evalServerBinPath;
71
- const r = spawnSync(hookBinPath, [], { stdio: 'inherit', env, windowsHide: true });
72
- // status 99 = sentinel for "needs JS path" (ask mode, TTY) — fall through.
73
- // null/undefined status with error → fall through and let bun ssg handle.
74
- if (r.status !== 99 && r.status !== null && r.status !== undefined && !r.error) {
75
- process.exit(r.status);
76
- }
77
- // Otherwise fall through to the bun ssg below (handles ask mode, TTY,
78
- // and surface any error diagnostics consistently with the rest of the CLI).
71
+ const env = {
72
+ ...process.env,
73
+ SSG_PUBLIC_DIR: path.resolve(__dirname, "..", "public"),
74
+ };
75
+ if (evalServerBinPath) env.SSG_EVAL_SERVER_BIN = evalServerBinPath;
76
+ const r = spawnSync(hookBinPath, [], {
77
+ stdio: "inherit",
78
+ env,
79
+ windowsHide: true,
80
+ });
81
+ // status 99 = sentinel for "needs JS path" (ask mode, TTY) — fall through.
82
+ // null/undefined status with error → fall through and let bun ssg handle.
83
+ if (
84
+ r.status !== 99 &&
85
+ r.status !== null &&
86
+ r.status !== undefined &&
87
+ !r.error
88
+ ) {
89
+ process.exit(r.status);
90
+ }
91
+ // Otherwise fall through to the bun ssg below (handles ask mode, TTY,
92
+ // and surface any error diagnostics consistently with the rest of the CLI).
79
93
  }
80
94
 
81
95
  // 3. Run binary if found
82
96
  if (binaryPath) {
83
- const env = { ...process.env, SSG_PUBLIC_DIR: path.resolve(__dirname, '..', 'public') };
84
- if (hookBinPath) env['SSG_HOOK_FAST_BIN'] = hookBinPath;
85
- if (evalServerBinPath) env['SSG_EVAL_SERVER_BIN'] = evalServerBinPath;
86
- const result = spawnSync(binaryPath, process.argv.slice(2), {
87
- stdio: 'inherit',
88
- env,
89
- windowsHide: true,
90
- });
91
- process.exit(result.status ?? 1);
97
+ const env = {
98
+ ...process.env,
99
+ SSG_PUBLIC_DIR: path.resolve(__dirname, "..", "public"),
100
+ };
101
+ if (hookBinPath) env.SSG_HOOK_FAST_BIN = hookBinPath;
102
+ if (evalServerBinPath) env.SSG_EVAL_SERVER_BIN = evalServerBinPath;
103
+ const result = spawnSync(binaryPath, process.argv.slice(2), {
104
+ stdio: "inherit",
105
+ env,
106
+ windowsHide: true,
107
+ });
108
+ process.exit(result.status ?? 1);
92
109
  }
93
110
 
94
111
  // 4. Last resort: run TypeScript source via Bun (unsupported platform / dev)
95
- const src = path.resolve(__dirname, '..', 'src', 'cli.ts');
96
- const isWin = process.platform === 'win32';
97
- const bunExe = isWin ? 'bun.exe' : 'bun';
112
+ const src = path.resolve(__dirname, "..", "src", "cli.ts");
113
+ const isWin = process.platform === "win32";
114
+ const bunExe = isWin ? "bun.exe" : "bun";
98
115
 
99
116
  let bunPath;
100
117
  try {
101
- // `where` on Windows, `which` everywhere else.
102
- bunPath = execFileSync(isWin ? 'where' : 'which', [bunExe], {encoding: 'utf8'})
103
- .trim()
104
- .split(/\r?\n/)[0];
118
+ // `where` on Windows, `which` everywhere else.
119
+ bunPath = execFileSync(isWin ? "where" : "which", [bunExe], {
120
+ encoding: "utf8",
121
+ })
122
+ .trim()
123
+ .split(/\r?\n/)[0];
105
124
  } catch {
106
- const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
107
- const candidates = isWin
108
- ? [
109
- path.join(home, '.bun', 'bin', 'bun.exe'),
110
- path.join(process.env.LOCALAPPDATA ?? '', 'Programs', 'bun', 'bin', 'bun.exe'),
111
- 'C:\\Program Files\\bun\\bin\\bun.exe',
112
- ]
113
- : [
114
- path.join(home, '.bun', 'bin', 'bun'),
115
- '/usr/local/bin/bun',
116
- '/opt/homebrew/bin/bun',
117
- ];
118
- bunPath = candidates.find(p => {
119
- try { fs.accessSync(p, fs.constants.X_OK); return true; } catch { return false; }
120
- });
125
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
126
+ const candidates = isWin
127
+ ? [
128
+ path.join(home, ".bun", "bin", "bun.exe"),
129
+ path.join(
130
+ process.env.LOCALAPPDATA ?? "",
131
+ "Programs",
132
+ "bun",
133
+ "bin",
134
+ "bun.exe",
135
+ ),
136
+ "C:\\Program Files\\bun\\bin\\bun.exe",
137
+ ]
138
+ : [
139
+ path.join(home, ".bun", "bin", "bun"),
140
+ "/usr/local/bin/bun",
141
+ "/opt/homebrew/bin/bun",
142
+ ];
143
+ bunPath = candidates.find((p) => {
144
+ try {
145
+ fs.accessSync(p, fs.constants.X_OK);
146
+ return true;
147
+ } catch {
148
+ return false;
149
+ }
150
+ });
121
151
  }
122
152
 
123
153
  if (!bunPath) {
124
- process.stderr.write(
125
- `ssg: no pre-compiled binary for ${process.platform}-${process.arch}.\n` +
126
- `Expected platform package: ${platformPkg}\n` +
127
- 'Install Bun to run from source (https://bun.sh):\n\n' +
128
- ' curl -fsSL https://bun.sh/install | bash\n\n',
129
- );
130
- process.exit(1);
154
+ process.stderr.write(
155
+ `ssg: no pre-compiled binary for ${process.platform}-${process.arch}.\n` +
156
+ `Expected platform package: ${platformPkg}\n` +
157
+ "Install Bun to run from source (https://bun.sh):\n\n" +
158
+ " curl -fsSL https://bun.sh/install | bash\n\n",
159
+ );
160
+ process.exit(1);
131
161
  }
132
162
 
133
163
  const result = spawnSync(bunPath, [src, ...process.argv.slice(2)], {
134
- stdio: 'inherit',
135
- env: process.env,
136
- windowsHide: true,
164
+ stdio: "inherit",
165
+ env: process.env,
166
+ windowsHide: true,
137
167
  });
138
168
  process.exit(result.status ?? 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigmashake/ssg",
3
- "version": "0.29.86",
3
+ "version": "0.29.89",
4
4
  "description": "AI Agent Governance CLI — evaluate tool calls against rules, block dangerous operations, and surface blocked commands",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -14,8 +14,8 @@
14
14
  "README.md"
15
15
  ],
16
16
  "optionalDependencies": {
17
- "@sigmashake/ssg-linux-x64": "0.29.86",
18
- "@sigmashake/ssg-linux-arm64": "0.29.86",
17
+ "@sigmashake/ssg-linux-x64": "0.29.89",
18
+ "@sigmashake/ssg-linux-arm64": "0.29.89",
19
19
  "@sigmashake/ssg-darwin-arm64": "0.29.82",
20
20
  "@sigmashake/ssg-darwin-x64": "0.29.82",
21
21
  "@sigmashake/ssg-win32-x64": "0.29.85"
@@ -107,7 +107,8 @@
107
107
  "check:rust-audit:windows": "cd native/rust-audit && cargo zigbuild --target x86_64-pc-windows-gnu",
108
108
  "check:rust-audit:all": "bun run check:rust-audit:linux-x64 && bun run check:rust-audit:linux-arm64 && bun run check:rust-audit:darwin-arm64 && bun run check:rust-audit:darwin-x64 && bun run check:rust-audit:windows",
109
109
  "test:regression": "bun test test/regression",
110
- "test:configuration": "bun test test/configuration"
110
+ "test:configuration": "bun test test/configuration",
111
+ "typecheck": "bun run typecheck:client && bun run compile"
111
112
  },
112
113
  "keywords": [
113
114
  "ai",