@solongate/proxy 0.83.55 → 0.83.57
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/dist/api-client/audit.d.ts +0 -13
- package/dist/commands/index.js +154 -79
- package/dist/global-install.d.ts +28 -0
- package/dist/global-install.js +344 -129
- package/dist/hook-health.d.ts +26 -0
- package/dist/hook-launcher.d.ts +57 -0
- package/dist/index.js +507 -329
- package/dist/tui/index.js +418 -244
- package/dist/tui/local-log.d.ts +0 -2
- package/hooks/opencode-plugin.mjs +21 -1
- package/package.json +7 -7
package/dist/tui/index.js
CHANGED
|
@@ -8,38 +8,230 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
// src/hook-launcher.ts
|
|
12
|
+
function launcherScript(pinnedNode) {
|
|
13
|
+
const pinned = pinnedNode.replace(/'/g, `'\\''`);
|
|
14
|
+
return `#!/bin/sh
|
|
15
|
+
# SolonGate hook launcher \u2014 generated by \`solongate init --global\` / \`repair\`.
|
|
16
|
+
#
|
|
17
|
+
# Finds a working node and execs the hook with it. Do not edit: a reinstall
|
|
18
|
+
# overwrites this file, and on macOS it is chflags-locked besides.
|
|
19
|
+
#
|
|
20
|
+
# The reason it exists rather than the hook naming a node directly: an absolute
|
|
21
|
+
# node path recorded at install time is a Homebrew Cellar path or an nvm version
|
|
22
|
+
# directory, and both are deleted by a routine upgrade. The hook then could not
|
|
23
|
+
# start, nothing was enforced, nothing was logged, and every check still said
|
|
24
|
+
# the guard was registered \u2014 because it was.
|
|
25
|
+
|
|
26
|
+
set -u
|
|
27
|
+
|
|
28
|
+
script="\${1:-}"
|
|
29
|
+
[ -n "$script" ] || { echo "solongate: launcher called with no hook script" >&2; exit 1; }
|
|
30
|
+
shift
|
|
31
|
+
|
|
32
|
+
hook=\${script##*/}
|
|
33
|
+
home=\${HOME:-~}
|
|
34
|
+
beatdir="$home/.solongate/${BEAT_DIR}"
|
|
35
|
+
|
|
36
|
+
# The beat. Written BEFORE node is resolved, because its whole job is to record
|
|
37
|
+
# that the client invoked us \u2014 which is true even when everything after this
|
|
38
|
+
# line fails. The file's modification time is the timestamp; nothing is forked
|
|
39
|
+
# to produce one, and the directory test is a builtin.
|
|
40
|
+
#
|
|
41
|
+
# The braces matter. A redirect into a missing directory is reported by the
|
|
42
|
+
# SHELL, before the command runs, so a \`2>/dev/null\` on the printf alone does
|
|
43
|
+
# not suppress it \u2014 it lands on the hook's stderr, and Claude Code shows a
|
|
44
|
+
# hook's stderr to the person using it. Redirecting the group catches both.
|
|
45
|
+
beat() {
|
|
46
|
+
[ -d "$beatdir" ] || mkdir -p "$beatdir" 2>/dev/null || return 0
|
|
47
|
+
{ printf '%s\\n' "$1" > "$beatdir/$hook"; } 2>/dev/null || true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try() {
|
|
51
|
+
[ -n "\${1:-}" ] && [ -x "$1" ]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
resolve_node() {
|
|
55
|
+
# Told explicitly.
|
|
56
|
+
if try "\${SOLONGATE_NODE:-}"; then echo "$SOLONGATE_NODE"; return 0; fi
|
|
57
|
+
# The node this was installed with.
|
|
58
|
+
if try '${pinned}'; then echo '${pinned}'; return 0; fi
|
|
59
|
+
# PATH, when the client gave us one worth having.
|
|
60
|
+
p=$(command -v node 2>/dev/null) || p=
|
|
61
|
+
if try "$p"; then echo "$p"; return 0; fi
|
|
62
|
+
# The fixed locations.
|
|
63
|
+
for c in ${NODE_CANDIDATES.map((c2) => `"${c2}"`).join(" ")}; do
|
|
64
|
+
for g in $c; do
|
|
65
|
+
if try "$g"; then echo "$g"; return 0; fi
|
|
66
|
+
done
|
|
67
|
+
done
|
|
68
|
+
# The version managers.
|
|
69
|
+
for c in ${NODE_GLOBS.join(" ")}; do
|
|
70
|
+
for g in $c; do
|
|
71
|
+
if try "$g"; then echo "$g"; return 0; fi
|
|
72
|
+
done
|
|
73
|
+
done
|
|
74
|
+
return 1
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
node_bin=$(resolve_node) || node_bin=
|
|
78
|
+
|
|
79
|
+
# --sg-doctor: report what would be used and leave. This is what \`solongate
|
|
80
|
+
# doctor\` runs, so the health check exercises the REAL resolution rather than a
|
|
81
|
+
# copy of it that can drift.
|
|
82
|
+
if [ "$script" = "--sg-doctor" ] || [ "\${1:-}" = "--sg-doctor" ]; then
|
|
83
|
+
[ -n "$node_bin" ] && { echo "$node_bin"; exit 0; }
|
|
84
|
+
echo "no node found" >&2
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [ -z "$node_bin" ]; then
|
|
89
|
+
beat "no-node"
|
|
90
|
+
# Nothing can be enforced without node, and which way to fail is not a
|
|
91
|
+
# judgement call: the guard is fail-closed, so it refuses the call and says
|
|
92
|
+
# why. Everything else here only records what already happened, and refusing
|
|
93
|
+
# a tool call because the log could not be written would be a worse product
|
|
94
|
+
# than a gap in the log.
|
|
95
|
+
echo "solongate: no node runtime found, so $hook did not run." >&2
|
|
96
|
+
echo "solongate: run \\\`solongate repair\\\` in a terminal, or set SOLONGATE_NODE to your node binary." >&2
|
|
97
|
+
case "$hook" in
|
|
98
|
+
guard.mjs) echo "solongate: this tool call is REFUSED \u2014 the guard is fail-closed." >&2; exit 2 ;;
|
|
99
|
+
*) exit 0 ;;
|
|
100
|
+
esac
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
beat "$node_bin"
|
|
104
|
+
exec "$node_bin" "$script" "$@"
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
var NODE_CANDIDATES, NODE_GLOBS, LAUNCHER_NAME, BEAT_DIR;
|
|
108
|
+
var init_hook_launcher = __esm({
|
|
109
|
+
"src/hook-launcher.ts"() {
|
|
110
|
+
"use strict";
|
|
111
|
+
NODE_CANDIDATES = [
|
|
112
|
+
// Homebrew, by the STABLE symlink rather than the Cellar path behind it —
|
|
113
|
+
// the exact distinction this file exists for. Apple Silicon then Intel.
|
|
114
|
+
"/opt/homebrew/bin/node",
|
|
115
|
+
"/usr/local/bin/node",
|
|
116
|
+
// Homebrew's keg-only layout, both prefixes.
|
|
117
|
+
"/opt/homebrew/opt/node/bin/node",
|
|
118
|
+
"/usr/local/opt/node/bin/node",
|
|
119
|
+
// Distro and hand-built.
|
|
120
|
+
"/usr/bin/node",
|
|
121
|
+
"/usr/local/n/versions/node/*/bin/node",
|
|
122
|
+
"/snap/bin/node"
|
|
123
|
+
];
|
|
124
|
+
NODE_GLOBS = [
|
|
125
|
+
// nvm. NVM_DIR is exported by its own shell hook, which a non-interactive
|
|
126
|
+
// hook environment does not run, so the default location is tried too.
|
|
127
|
+
'"${NVM_DIR:-}"/versions/node/*/bin/node',
|
|
128
|
+
'"$home"/.nvm/versions/node/*/bin/node',
|
|
129
|
+
// fnm, both the XDG location and the macOS Application Support one.
|
|
130
|
+
'"$home"/.local/share/fnm/node-versions/*/installation/bin/node',
|
|
131
|
+
'"$home"/Library/Application Support/fnm/node-versions/*/installation/bin/node',
|
|
132
|
+
// Volta.
|
|
133
|
+
'"$home"/.volta/tools/image/node/*/bin/node',
|
|
134
|
+
// asdf, old layout and the current plugin one.
|
|
135
|
+
'"$home"/.asdf/installs/nodejs/*/bin/node',
|
|
136
|
+
'"$home"/.asdf/installs/node/*/bin/node'
|
|
137
|
+
];
|
|
138
|
+
LAUNCHER_NAME = "sg-run.sh";
|
|
139
|
+
BEAT_DIR = ".beat";
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// src/hook-health.ts
|
|
144
|
+
import { execFileSync } from "child_process";
|
|
145
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
146
|
+
import { join as join3 } from "path";
|
|
14
147
|
import { homedir as homedir3 } from "os";
|
|
148
|
+
function hookCanStart() {
|
|
149
|
+
const launcher = join3(hooksDir(), LAUNCHER_NAME);
|
|
150
|
+
if (process.platform === "win32") {
|
|
151
|
+
const ok = existsSync2(process.execPath);
|
|
152
|
+
return { ok, node: process.execPath, detail: ok ? process.execPath : `${process.execPath} is gone` };
|
|
153
|
+
}
|
|
154
|
+
if (!existsSync2(launcher)) {
|
|
155
|
+
return { ok: false, detail: "hook launcher missing - run `solongate repair`" };
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const out = execFileSync("/bin/sh", [launcher, "--sg-doctor"], {
|
|
159
|
+
encoding: "utf-8",
|
|
160
|
+
timeout: 5e3,
|
|
161
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
162
|
+
}).trim();
|
|
163
|
+
if (!out) return { ok: false, detail: "launcher found no node runtime - set SOLONGATE_NODE or install node" };
|
|
164
|
+
return { ok: true, node: out, detail: out };
|
|
165
|
+
} catch (e) {
|
|
166
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
167
|
+
return { ok: false, detail: `launcher will not run: ${msg.split("\n")[0]}` };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function hookBeats() {
|
|
171
|
+
const dir = join3(sgDir(), BEAT_DIR);
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const hook of ["guard.mjs", "audit.mjs", "conversation.mjs", "stop.mjs"]) {
|
|
174
|
+
const f = join3(dir, hook);
|
|
175
|
+
try {
|
|
176
|
+
const st = statSync2(f);
|
|
177
|
+
out.push({ hook, at: st.mtime, node: readFileSync3(f, "utf-8").trim() });
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return out.sort((a, b) => b.at.getTime() - a.at.getTime());
|
|
182
|
+
}
|
|
183
|
+
function guardBeat() {
|
|
184
|
+
return hookBeats().find((b) => b.hook === "guard.mjs") ?? null;
|
|
185
|
+
}
|
|
186
|
+
function agoLabel(d) {
|
|
187
|
+
const s = Math.max(0, Math.round((Date.now() - d.getTime()) / 1e3));
|
|
188
|
+
if (s < 60) return s <= 3 ? "just now" : `${s}s ago`;
|
|
189
|
+
if (s < 3600) return `${Math.round(s / 60)}m ago`;
|
|
190
|
+
if (s < 86400) return `${Math.round(s / 3600)}h ago`;
|
|
191
|
+
return `${Math.round(s / 86400)}d ago`;
|
|
192
|
+
}
|
|
193
|
+
var sgDir, hooksDir;
|
|
194
|
+
var init_hook_health = __esm({
|
|
195
|
+
"src/hook-health.ts"() {
|
|
196
|
+
"use strict";
|
|
197
|
+
init_hook_launcher();
|
|
198
|
+
sgDir = () => join3(homedir3(), ".solongate");
|
|
199
|
+
hooksDir = () => join3(sgDir(), "hooks");
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// src/global-install.ts
|
|
204
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync, rmSync, rmdirSync, readdirSync as readdirSync2, statSync as statSync3, chmodSync, copyFileSync, renameSync } from "fs";
|
|
205
|
+
import { resolve, join as join4, dirname, basename } from "path";
|
|
206
|
+
import { homedir as homedir4 } from "os";
|
|
15
207
|
import { createRequire } from "module";
|
|
16
208
|
import { fileURLToPath } from "url";
|
|
17
209
|
import { createInterface } from "readline";
|
|
18
|
-
import { execFileSync, spawn } from "child_process";
|
|
210
|
+
import { execFileSync as execFileSync2, spawn } from "child_process";
|
|
19
211
|
function lockFile(file) {
|
|
20
|
-
if (!
|
|
212
|
+
if (!existsSync3(file)) return;
|
|
21
213
|
try {
|
|
22
214
|
if (process.platform === "win32") {
|
|
23
215
|
try {
|
|
24
|
-
|
|
216
|
+
execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE,WDAC,WO)"], { stdio: "ignore" });
|
|
25
217
|
} catch {
|
|
26
218
|
}
|
|
27
219
|
try {
|
|
28
|
-
|
|
220
|
+
execFileSync2("icacls", [file, "/grant", "*S-1-3-4:(RX)"], { stdio: "ignore" });
|
|
29
221
|
} catch {
|
|
30
222
|
}
|
|
31
223
|
try {
|
|
32
|
-
|
|
224
|
+
execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
|
|
33
225
|
} catch {
|
|
34
226
|
}
|
|
35
227
|
} else if (process.platform === "darwin") {
|
|
36
228
|
try {
|
|
37
|
-
|
|
229
|
+
execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
|
|
38
230
|
} catch {
|
|
39
231
|
}
|
|
40
232
|
} else {
|
|
41
233
|
try {
|
|
42
|
-
|
|
234
|
+
execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
|
|
43
235
|
} catch {
|
|
44
236
|
}
|
|
45
237
|
try {
|
|
@@ -51,33 +243,33 @@ function lockFile(file) {
|
|
|
51
243
|
}
|
|
52
244
|
}
|
|
53
245
|
function unlockFile(file) {
|
|
54
|
-
if (!
|
|
246
|
+
if (!existsSync3(file)) return;
|
|
55
247
|
try {
|
|
56
248
|
if (process.platform === "win32") {
|
|
57
249
|
try {
|
|
58
|
-
|
|
250
|
+
execFileSync2("icacls", [file, "/remove:g", "*S-1-3-4"], { stdio: "ignore" });
|
|
59
251
|
} catch {
|
|
60
252
|
}
|
|
61
253
|
try {
|
|
62
|
-
|
|
254
|
+
execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
|
|
63
255
|
} catch {
|
|
64
256
|
}
|
|
65
257
|
try {
|
|
66
|
-
|
|
258
|
+
execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
|
|
67
259
|
} catch {
|
|
68
260
|
}
|
|
69
261
|
try {
|
|
70
|
-
|
|
262
|
+
execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
|
|
71
263
|
} catch {
|
|
72
264
|
}
|
|
73
265
|
} else if (process.platform === "darwin") {
|
|
74
266
|
try {
|
|
75
|
-
|
|
267
|
+
execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
|
|
76
268
|
} catch {
|
|
77
269
|
}
|
|
78
270
|
} else {
|
|
79
271
|
try {
|
|
80
|
-
|
|
272
|
+
execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
|
|
81
273
|
} catch {
|
|
82
274
|
}
|
|
83
275
|
try {
|
|
@@ -91,14 +283,19 @@ function unlockFile(file) {
|
|
|
91
283
|
function protectedTargets() {
|
|
92
284
|
const p = globalPaths();
|
|
93
285
|
return [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
286
|
+
join4(p.hooksDir, "guard.mjs"),
|
|
287
|
+
join4(p.hooksDir, "audit.mjs"),
|
|
288
|
+
join4(p.hooksDir, "stop.mjs"),
|
|
289
|
+
join4(p.hooksDir, "shield.mjs"),
|
|
98
290
|
// The conversation record is locked with the rest. A guest who could edit
|
|
99
291
|
// it could decide what their host sees them say, which is the same class of
|
|
100
292
|
// problem as editing the guard.
|
|
101
|
-
|
|
293
|
+
join4(p.hooksDir, "conversation.mjs"),
|
|
294
|
+
// The launcher is the enforcement path now: every hook command in every
|
|
295
|
+
// client config runs THROUGH it. A program that could rewrite it could
|
|
296
|
+
// point every hook at /bin/true and disarm the guard without touching a
|
|
297
|
+
// single file that used to be locked.
|
|
298
|
+
join4(p.hooksDir, LAUNCHER_NAME),
|
|
102
299
|
p.configPath,
|
|
103
300
|
p.settingsPath,
|
|
104
301
|
p.antigravityHooksPath,
|
|
@@ -132,52 +329,52 @@ function writeProtectedFile(file, contents) {
|
|
|
132
329
|
}
|
|
133
330
|
}
|
|
134
331
|
function globalPaths() {
|
|
135
|
-
const home =
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
const claudeDir =
|
|
139
|
-
const antigravityDir =
|
|
140
|
-
const codexDir = process.env["CODEX_HOME"] ? resolve(process.env["CODEX_HOME"]) :
|
|
141
|
-
const opencodeDir =
|
|
142
|
-
const binDir =
|
|
332
|
+
const home = homedir4();
|
|
333
|
+
const sgDir2 = join4(home, ".solongate");
|
|
334
|
+
const hooksDir2 = join4(sgDir2, "hooks");
|
|
335
|
+
const claudeDir = join4(home, ".claude");
|
|
336
|
+
const antigravityDir = join4(home, ".gemini", "config");
|
|
337
|
+
const codexDir = process.env["CODEX_HOME"] ? resolve(process.env["CODEX_HOME"]) : join4(home, ".codex");
|
|
338
|
+
const opencodeDir = join4(process.env["XDG_CONFIG_HOME"] ? resolve(process.env["XDG_CONFIG_HOME"]) : join4(home, ".config"), "opencode");
|
|
339
|
+
const binDir = join4(sgDir2, "bin");
|
|
143
340
|
return {
|
|
144
341
|
home,
|
|
145
|
-
sgDir,
|
|
146
|
-
hooksDir,
|
|
342
|
+
sgDir: sgDir2,
|
|
343
|
+
hooksDir: hooksDir2,
|
|
147
344
|
binDir,
|
|
148
345
|
claudeDir,
|
|
149
346
|
antigravityDir,
|
|
150
347
|
codexDir,
|
|
151
348
|
opencodeDir,
|
|
152
|
-
settingsPath:
|
|
153
|
-
backupPath:
|
|
154
|
-
configPath:
|
|
349
|
+
settingsPath: join4(claudeDir, "settings.json"),
|
|
350
|
+
backupPath: join4(claudeDir, "settings.solongate.bak"),
|
|
351
|
+
configPath: join4(sgDir2, "cloud-guard.json"),
|
|
155
352
|
// Antigravity reads global hooks from ~/.gemini/config/hooks.json. Only the
|
|
156
353
|
// guard is registered there (PreToolUse); Antigravity's ALLOW-path audit is
|
|
157
354
|
// covered by the passive session-log collector, not a hook.
|
|
158
|
-
antigravityHooksPath:
|
|
159
|
-
antigravityBackupPath:
|
|
355
|
+
antigravityHooksPath: join4(antigravityDir, "hooks.json"),
|
|
356
|
+
antigravityBackupPath: join4(antigravityDir, "hooks.solongate.bak"),
|
|
160
357
|
// Codex reads user-level hooks from ~/.codex/hooks.json (or a [hooks] table
|
|
161
358
|
// in ~/.codex/config.toml — we use the JSON file so we never have to rewrite
|
|
162
359
|
// the user's TOML, which also holds the hook TRUST state Codex manages).
|
|
163
|
-
codexHooksPath:
|
|
164
|
-
codexBackupPath:
|
|
165
|
-
codexConfigPath:
|
|
360
|
+
codexHooksPath: join4(codexDir, "hooks.json"),
|
|
361
|
+
codexBackupPath: join4(codexDir, "hooks.solongate.bak"),
|
|
362
|
+
codexConfigPath: join4(codexDir, "config.toml"),
|
|
166
363
|
// OpenCode scans its plugin folder at startup and loads every module in it.
|
|
167
364
|
// Measured on 1.18.10: BOTH `plugin/` and `plugins/` are scanned, so the
|
|
168
365
|
// docs and the field reports are each half right. We write the documented
|
|
169
366
|
// one. There is nothing to register anywhere — dropping the file IS the
|
|
170
367
|
// installation, which also means deleting the file IS the uninstall.
|
|
171
|
-
opencodePluginDir:
|
|
172
|
-
opencodePluginPath:
|
|
368
|
+
opencodePluginDir: join4(opencodeDir, "plugins"),
|
|
369
|
+
opencodePluginPath: join4(opencodeDir, "plugins", "solongate.js")
|
|
173
370
|
};
|
|
174
371
|
}
|
|
175
372
|
function readHook(filename) {
|
|
176
|
-
return
|
|
373
|
+
return readFileSync4(join4(HOOKS_DIR, filename), "utf-8");
|
|
177
374
|
}
|
|
178
375
|
function firstAccountCredential() {
|
|
179
376
|
try {
|
|
180
|
-
const raw = JSON.parse(
|
|
377
|
+
const raw = JSON.parse(readFileSync4(join4(homedir4(), ".solongate", "accounts.json"), "utf-8"));
|
|
181
378
|
if (Array.isArray(raw)) {
|
|
182
379
|
const acc = raw.find((a) => a && typeof a.apiKey === "string" && a.apiKey);
|
|
183
380
|
if (acc) return { apiKey: acc.apiKey, apiUrl: typeof acc.apiUrl === "string" ? acc.apiUrl : void 0 };
|
|
@@ -187,8 +384,8 @@ function firstAccountCredential() {
|
|
|
187
384
|
return {};
|
|
188
385
|
}
|
|
189
386
|
function readGuard() {
|
|
190
|
-
const bundled =
|
|
191
|
-
return
|
|
387
|
+
const bundled = join4(HOOKS_DIR, "guard.bundled.mjs");
|
|
388
|
+
return existsSync3(bundled) ? readFileSync4(bundled, "utf-8") : readHook("guard.mjs");
|
|
192
389
|
}
|
|
193
390
|
function installGoBinaries(binDir) {
|
|
194
391
|
const os_ = process.platform === "win32" ? "win32" : process.platform;
|
|
@@ -208,10 +405,10 @@ function installGoBinaries(binDir) {
|
|
|
208
405
|
return placed;
|
|
209
406
|
}
|
|
210
407
|
for (const name of ["solongate-guard", "solongate"]) {
|
|
211
|
-
const from =
|
|
212
|
-
const to =
|
|
408
|
+
const from = join4(pkgDir, name + suffix);
|
|
409
|
+
const to = join4(binDir, name + suffix);
|
|
213
410
|
try {
|
|
214
|
-
if (!
|
|
411
|
+
if (!existsSync3(from)) continue;
|
|
215
412
|
const tmp = to + ".new";
|
|
216
413
|
copyFileSync(from, tmp);
|
|
217
414
|
chmodSync(tmp, 493);
|
|
@@ -223,16 +420,14 @@ function installGoBinaries(binDir) {
|
|
|
223
420
|
return placed;
|
|
224
421
|
}
|
|
225
422
|
function antigravityHookCommand(guardAbs) {
|
|
226
|
-
|
|
227
|
-
const call = process.platform === "win32" ? "& " : "";
|
|
228
|
-
return `${call}"${nodeBin}" "${guardAbs.replace(/\\/g, "/")}" antigravity "Antigravity"`;
|
|
423
|
+
return hookCommandFor(dirname(guardAbs), basename(guardAbs), "antigravity", "Antigravity");
|
|
229
424
|
}
|
|
230
425
|
function installAntigravityGuard(p, guardAbs) {
|
|
231
426
|
mkdirSync(p.antigravityDir, { recursive: true });
|
|
232
427
|
let existing = {};
|
|
233
|
-
if (
|
|
234
|
-
const raw =
|
|
235
|
-
if (!
|
|
428
|
+
if (existsSync3(p.antigravityHooksPath)) {
|
|
429
|
+
const raw = readFileSync4(p.antigravityHooksPath, "utf-8");
|
|
430
|
+
if (!existsSync3(p.antigravityBackupPath)) writeFileSync2(p.antigravityBackupPath, raw);
|
|
236
431
|
try {
|
|
237
432
|
existing = JSON.parse(raw);
|
|
238
433
|
} catch {
|
|
@@ -248,9 +443,9 @@ function installAntigravityGuard(p, guardAbs) {
|
|
|
248
443
|
writeFileSync2(p.antigravityHooksPath, JSON.stringify(merged, null, 2) + "\n");
|
|
249
444
|
}
|
|
250
445
|
function removeAntigravityGuard(p) {
|
|
251
|
-
if (!
|
|
446
|
+
if (!existsSync3(p.antigravityHooksPath)) return;
|
|
252
447
|
try {
|
|
253
|
-
const s = JSON.parse(
|
|
448
|
+
const s = JSON.parse(readFileSync4(p.antigravityHooksPath, "utf-8"));
|
|
254
449
|
if (!(ANTIGRAVITY_GROUP in s)) return;
|
|
255
450
|
delete s[ANTIGRAVITY_GROUP];
|
|
256
451
|
writeFileSync2(p.antigravityHooksPath, JSON.stringify(s, null, 2) + "\n");
|
|
@@ -258,8 +453,10 @@ function removeAntigravityGuard(p) {
|
|
|
258
453
|
}
|
|
259
454
|
}
|
|
260
455
|
function codexHookCommand(scriptAbs) {
|
|
261
|
-
|
|
262
|
-
|
|
456
|
+
if (process.platform === "win32") {
|
|
457
|
+
return `"${process.execPath.replace(/\\/g, "/")}" "${scriptAbs.replace(/\\/g, "/")}" codex "Codex"`;
|
|
458
|
+
}
|
|
459
|
+
return hookCommandFor(dirname(scriptAbs), basename(scriptAbs), "codex", "Codex");
|
|
263
460
|
}
|
|
264
461
|
function isOurCodexGroup(group) {
|
|
265
462
|
const hooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
@@ -267,10 +464,10 @@ function isOurCodexGroup(group) {
|
|
|
267
464
|
}
|
|
268
465
|
function readCodexHooksFile(path) {
|
|
269
466
|
const out = { hooks: {}, rest: {} };
|
|
270
|
-
if (!
|
|
467
|
+
if (!existsSync3(path)) return out;
|
|
271
468
|
let raw;
|
|
272
469
|
try {
|
|
273
|
-
raw = JSON.parse(
|
|
470
|
+
raw = JSON.parse(readFileSync4(path, "utf-8"));
|
|
274
471
|
} catch {
|
|
275
472
|
return out;
|
|
276
473
|
}
|
|
@@ -315,10 +512,10 @@ function writeCodexHooksFile(path, file) {
|
|
|
315
512
|
body["hooks"] = hooks;
|
|
316
513
|
writeFileSync2(path, JSON.stringify(body, null, 2) + "\n");
|
|
317
514
|
}
|
|
318
|
-
function installCodexGuard(p,
|
|
515
|
+
function installCodexGuard(p, hooksDir2) {
|
|
319
516
|
mkdirSync(p.codexDir, { recursive: true });
|
|
320
|
-
if (
|
|
321
|
-
writeFileSync2(p.codexBackupPath,
|
|
517
|
+
if (existsSync3(p.codexHooksPath) && !existsSync3(p.codexBackupPath)) {
|
|
518
|
+
writeFileSync2(p.codexBackupPath, readFileSync4(p.codexHooksPath, "utf-8"));
|
|
322
519
|
}
|
|
323
520
|
const file = readCodexHooksFile(p.codexHooksPath);
|
|
324
521
|
const script = {
|
|
@@ -342,7 +539,7 @@ function installCodexGuard(p, hooksDir) {
|
|
|
342
539
|
...ev === "Stop" ? {} : { matcher: "*" },
|
|
343
540
|
hooks: [{
|
|
344
541
|
type: "command",
|
|
345
|
-
command: codexHookCommand(
|
|
542
|
+
command: codexHookCommand(join4(hooksDir2, script[ev]).replace(/\\/g, "/")),
|
|
346
543
|
timeout: CODEX_TIMEOUT_SEC,
|
|
347
544
|
statusMessage: status[ev]
|
|
348
545
|
}]
|
|
@@ -352,7 +549,7 @@ function installCodexGuard(p, hooksDir) {
|
|
|
352
549
|
writeCodexHooksFile(p.codexHooksPath, file);
|
|
353
550
|
}
|
|
354
551
|
function removeCodexGuard(p) {
|
|
355
|
-
if (!
|
|
552
|
+
if (!existsSync3(p.codexHooksPath)) return;
|
|
356
553
|
try {
|
|
357
554
|
const file = readCodexHooksFile(p.codexHooksPath);
|
|
358
555
|
let changed = false;
|
|
@@ -378,7 +575,7 @@ function isCodexGuardInstalled() {
|
|
|
378
575
|
}
|
|
379
576
|
function codexDetected() {
|
|
380
577
|
try {
|
|
381
|
-
return
|
|
578
|
+
return existsSync3(globalPaths().codexDir);
|
|
382
579
|
} catch {
|
|
383
580
|
return false;
|
|
384
581
|
}
|
|
@@ -388,7 +585,7 @@ function codexHooksStatus() {
|
|
|
388
585
|
const registered = isCodexGuardInstalled();
|
|
389
586
|
let trusted = false, disabled = false;
|
|
390
587
|
try {
|
|
391
|
-
const toml =
|
|
588
|
+
const toml = readFileSync4(p.codexConfigPath, "utf-8");
|
|
392
589
|
trusted = /trusted_hash\s*=/.test(toml);
|
|
393
590
|
disabled = /^\s*hooks\s*=\s*false\s*$/m.test(toml);
|
|
394
591
|
} catch {
|
|
@@ -407,10 +604,10 @@ function removeOpencodeGuard(p) {
|
|
|
407
604
|
} catch {
|
|
408
605
|
}
|
|
409
606
|
}
|
|
410
|
-
function warmPolicyCache(
|
|
607
|
+
function warmPolicyCache(hooksDir2, agents) {
|
|
411
608
|
for (const agent of agents) {
|
|
412
609
|
try {
|
|
413
|
-
const child = spawn(process.execPath, [
|
|
610
|
+
const child = spawn(process.execPath, [join4(hooksDir2, "guard.mjs"), agent, "--sg-refresh-policy"], {
|
|
414
611
|
detached: true,
|
|
415
612
|
stdio: "ignore",
|
|
416
613
|
windowsHide: true
|
|
@@ -424,23 +621,23 @@ function warmPolicyCache(hooksDir, agents) {
|
|
|
424
621
|
}
|
|
425
622
|
function isOpencodeGuardInstalled() {
|
|
426
623
|
try {
|
|
427
|
-
return
|
|
624
|
+
return readFileSync4(globalPaths().opencodePluginPath, "utf-8").includes("tool.execute.before");
|
|
428
625
|
} catch {
|
|
429
626
|
return false;
|
|
430
627
|
}
|
|
431
628
|
}
|
|
432
629
|
function opencodeDetected() {
|
|
433
630
|
try {
|
|
434
|
-
return
|
|
631
|
+
return existsSync3(globalPaths().opencodeDir);
|
|
435
632
|
} catch {
|
|
436
633
|
return false;
|
|
437
634
|
}
|
|
438
635
|
}
|
|
439
|
-
function sweepStrayScratchDirs(root =
|
|
636
|
+
function sweepStrayScratchDirs(root = homedir4(), maxDepth = 6, budget = 4e4) {
|
|
440
637
|
let removed = 0;
|
|
441
638
|
let visited = 0;
|
|
442
639
|
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".next", "build"]);
|
|
443
|
-
const store =
|
|
640
|
+
const store = join4(homedir4(), ".solongate");
|
|
444
641
|
const walk = (dir, depth) => {
|
|
445
642
|
if (depth > maxDepth || visited++ > budget) return;
|
|
446
643
|
let entries;
|
|
@@ -451,11 +648,11 @@ function sweepStrayScratchDirs(root = homedir3(), maxDepth = 6, budget = 4e4) {
|
|
|
451
648
|
}
|
|
452
649
|
for (const name of entries) {
|
|
453
650
|
if (skip.has(name)) continue;
|
|
454
|
-
const full =
|
|
651
|
+
const full = join4(dir, name);
|
|
455
652
|
if (full === store) continue;
|
|
456
653
|
let isDir = false;
|
|
457
654
|
try {
|
|
458
|
-
isDir =
|
|
655
|
+
isDir = statSync3(full).isDirectory();
|
|
459
656
|
} catch {
|
|
460
657
|
continue;
|
|
461
658
|
}
|
|
@@ -466,7 +663,7 @@ function sweepStrayScratchDirs(root = homedir3(), maxDepth = 6, budget = 4e4) {
|
|
|
466
663
|
for (const f of readdirSync2(full)) {
|
|
467
664
|
if (SCRATCH_FILES.has(f)) {
|
|
468
665
|
try {
|
|
469
|
-
rmSync(
|
|
666
|
+
rmSync(join4(full, f), { force: true });
|
|
470
667
|
} catch {
|
|
471
668
|
left.push(f);
|
|
472
669
|
}
|
|
@@ -488,12 +685,17 @@ function sweepStrayScratchDirs(root = homedir3(), maxDepth = 6, budget = 4e4) {
|
|
|
488
685
|
}
|
|
489
686
|
function repairQuiet() {
|
|
490
687
|
const p = globalPaths();
|
|
491
|
-
const has = (f) =>
|
|
492
|
-
const guardFile =
|
|
688
|
+
const has = (f) => existsSync3(f);
|
|
689
|
+
const guardFile = join4(p.hooksDir, "guard.mjs");
|
|
493
690
|
const line = (label, ok, yes, no) => ({ label, ok, detail: ok ? yes : no });
|
|
691
|
+
const runtime = () => {
|
|
692
|
+
const r2 = hookCanStart();
|
|
693
|
+
return { label: "hook runtime", ok: r2.ok, detail: r2.ok ? `node ${r2.detail}` : r2.detail };
|
|
694
|
+
};
|
|
494
695
|
const before = [
|
|
495
696
|
line("guard hook file", has(guardFile), "present", "MISSING"),
|
|
496
697
|
line("cloud credential", has(p.configPath), "present", "MISSING"),
|
|
698
|
+
runtime(),
|
|
497
699
|
line("Claude hooks", isGuardInstalled(), "guard registered", "guard NOT registered"),
|
|
498
700
|
line("Antigravity hooks", has(p.antigravityHooksPath), "guard registered", "guard NOT registered"),
|
|
499
701
|
line("Codex hooks", isCodexGuardInstalled(), "guard registered", "guard NOT registered"),
|
|
@@ -503,6 +705,7 @@ function repairQuiet() {
|
|
|
503
705
|
if (!r.ok) return { ok: false, message: r.message, before, after: [], notes: [] };
|
|
504
706
|
const after = [
|
|
505
707
|
{ label: "guard hook file", ok: true, detail: `present (v${installedGuardVersion() ?? "?"})` },
|
|
708
|
+
runtime(),
|
|
506
709
|
line("Claude hooks", isGuardInstalled(), "guard registered", "NOT registered"),
|
|
507
710
|
line("Antigravity hooks", has(p.antigravityHooksPath), "guard registered", "NOT registered"),
|
|
508
711
|
line("Codex hooks", isCodexGuardInstalled(), "guard registered", "NOT registered"),
|
|
@@ -529,7 +732,7 @@ function installGlobalQuiet() {
|
|
|
529
732
|
let apiKey = process.env["SOLONGATE_API_KEY"] || "";
|
|
530
733
|
let apiUrl = process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
|
|
531
734
|
try {
|
|
532
|
-
const cfg = JSON.parse(
|
|
735
|
+
const cfg = JSON.parse(readFileSync4(p.configPath, "utf-8"));
|
|
533
736
|
if (cfg && typeof cfg.apiKey === "string") apiKey = apiKey || cfg.apiKey;
|
|
534
737
|
if (cfg && typeof cfg.apiUrl === "string") apiUrl = cfg.apiUrl;
|
|
535
738
|
} catch {
|
|
@@ -545,26 +748,26 @@ function installGlobalQuiet() {
|
|
|
545
748
|
mkdirSync(p.hooksDir, { recursive: true });
|
|
546
749
|
mkdirSync(p.claudeDir, { recursive: true });
|
|
547
750
|
unlockProtected();
|
|
548
|
-
writeFileSync2(
|
|
751
|
+
writeFileSync2(join4(p.hooksDir, "guard.mjs"), readGuard());
|
|
549
752
|
installGoBinaries(p.binDir);
|
|
550
|
-
writeFileSync2(
|
|
551
|
-
writeFileSync2(
|
|
552
|
-
writeFileSync2(
|
|
553
|
-
writeFileSync2(
|
|
753
|
+
writeFileSync2(join4(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
754
|
+
writeFileSync2(join4(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
755
|
+
writeFileSync2(join4(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
756
|
+
writeFileSync2(join4(p.hooksDir, "conversation.mjs"), readHook("conversation.mjs"));
|
|
757
|
+
writeLauncher(p.hooksDir);
|
|
758
|
+
writeLauncher(p.hooksDir);
|
|
554
759
|
writeFileSync2(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
555
760
|
let existing = {};
|
|
556
|
-
if (
|
|
557
|
-
const raw =
|
|
558
|
-
if (!
|
|
761
|
+
if (existsSync3(p.settingsPath)) {
|
|
762
|
+
const raw = readFileSync4(p.settingsPath, "utf-8");
|
|
763
|
+
if (!existsSync3(p.backupPath)) writeFileSync2(p.backupPath, raw);
|
|
559
764
|
try {
|
|
560
765
|
existing = JSON.parse(raw);
|
|
561
766
|
} catch {
|
|
562
767
|
existing = {};
|
|
563
768
|
}
|
|
564
769
|
}
|
|
565
|
-
const
|
|
566
|
-
const call = process.platform === "win32" ? "& " : "";
|
|
567
|
-
const hookCmd = (script) => `${call}"${nodeBin}" "${join3(p.hooksDir, script).replace(/\\/g, "/")}" claude-code "Claude Code"`;
|
|
770
|
+
const hookCmd = (script) => hookCommandFor(p.hooksDir, script);
|
|
568
771
|
const merged = {
|
|
569
772
|
...existing,
|
|
570
773
|
hooks: {
|
|
@@ -591,7 +794,7 @@ function installGlobalQuiet() {
|
|
|
591
794
|
};
|
|
592
795
|
writeFileSync2(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
593
796
|
try {
|
|
594
|
-
installAntigravityGuard(p,
|
|
797
|
+
installAntigravityGuard(p, join4(p.hooksDir, "guard.mjs").replace(/\\/g, "/"));
|
|
595
798
|
} catch {
|
|
596
799
|
}
|
|
597
800
|
try {
|
|
@@ -612,7 +815,7 @@ function installGlobalQuiet() {
|
|
|
612
815
|
function installedGuardVersion() {
|
|
613
816
|
try {
|
|
614
817
|
const p = globalPaths();
|
|
615
|
-
const s =
|
|
818
|
+
const s = readFileSync4(join4(p.hooksDir, "guard.mjs"), "utf-8");
|
|
616
819
|
const m = s.match(/HOOK_VERSION\s*=\s*(\d+)/);
|
|
617
820
|
return m ? parseInt(m[1], 10) : null;
|
|
618
821
|
} catch {
|
|
@@ -622,16 +825,35 @@ function installedGuardVersion() {
|
|
|
622
825
|
function guardHookOutdated() {
|
|
623
826
|
try {
|
|
624
827
|
const p = globalPaths();
|
|
625
|
-
return
|
|
828
|
+
return readFileSync4(join4(p.hooksDir, "guard.mjs"), "utf-8") !== readGuard();
|
|
626
829
|
} catch {
|
|
627
830
|
return false;
|
|
628
831
|
}
|
|
629
832
|
}
|
|
833
|
+
function hookCommandFor(hooksDir2, script, client = "claude-code", label = "Claude Code") {
|
|
834
|
+
const target = join4(hooksDir2, script).replace(/\\/g, "/");
|
|
835
|
+
if (process.platform === "win32") {
|
|
836
|
+
return `& "${process.execPath.replace(/\\/g, "/")}" "${target}" ${client} "${label}"`;
|
|
837
|
+
}
|
|
838
|
+
const launcher = join4(hooksDir2, LAUNCHER_NAME).replace(/\\/g, "/");
|
|
839
|
+
return `/bin/sh "${launcher}" "${target}" ${client} "${label}"`;
|
|
840
|
+
}
|
|
841
|
+
function writeLauncher(hooksDir2) {
|
|
842
|
+
writeFileSync2(join4(hooksDir2, LAUNCHER_NAME), launcherScript(process.execPath));
|
|
843
|
+
try {
|
|
844
|
+
chmodSync(join4(hooksDir2, LAUNCHER_NAME), 493);
|
|
845
|
+
} catch {
|
|
846
|
+
}
|
|
847
|
+
try {
|
|
848
|
+
mkdirSync(join4(hooksDir2, "..", BEAT_DIR), { recursive: true });
|
|
849
|
+
} catch {
|
|
850
|
+
}
|
|
851
|
+
}
|
|
630
852
|
function isGuardInstalled() {
|
|
631
853
|
try {
|
|
632
854
|
const p = globalPaths();
|
|
633
|
-
if (!
|
|
634
|
-
const s = JSON.parse(
|
|
855
|
+
if (!existsSync3(p.settingsPath)) return false;
|
|
856
|
+
const s = JSON.parse(readFileSync4(p.settingsPath, "utf-8"));
|
|
635
857
|
return !!s.hooks && JSON.stringify(s.hooks).includes(".solongate");
|
|
636
858
|
} catch {
|
|
637
859
|
return false;
|
|
@@ -654,8 +876,8 @@ function uninstallGlobalQuiet() {
|
|
|
654
876
|
removeOpencodeGuard(p);
|
|
655
877
|
} catch {
|
|
656
878
|
}
|
|
657
|
-
if (!
|
|
658
|
-
const s = JSON.parse(
|
|
879
|
+
if (!existsSync3(p.settingsPath)) return { ok: true, message: "guard removed (open a new session)" };
|
|
880
|
+
const s = JSON.parse(readFileSync4(p.settingsPath, "utf-8"));
|
|
659
881
|
delete s.hooks;
|
|
660
882
|
writeFileSync2(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
661
883
|
return { ok: true, message: "guard removed (open a new session)" };
|
|
@@ -669,17 +891,17 @@ function escapeRe(s) {
|
|
|
669
891
|
function shimTargets() {
|
|
670
892
|
if (process.platform === "win32") {
|
|
671
893
|
try {
|
|
672
|
-
const prof =
|
|
894
|
+
const prof = execFileSync2("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
|
|
673
895
|
return prof ? [prof] : [];
|
|
674
896
|
} catch {
|
|
675
897
|
return [];
|
|
676
898
|
}
|
|
677
899
|
}
|
|
678
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
900
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join4(homedir4(), f)).filter((f) => existsSync3(f));
|
|
679
901
|
}
|
|
680
902
|
function writeShimBlock(file, block2) {
|
|
681
903
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
682
|
-
let content =
|
|
904
|
+
let content = existsSync3(file) ? readFileSync4(file, "utf-8") : "";
|
|
683
905
|
content = content.replace(re, "");
|
|
684
906
|
if (block2) {
|
|
685
907
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
@@ -700,6 +922,10 @@ var __dirname, HOOKS_DIR, ANTIGRAVITY_GROUP, CODEX_EVENTS, CODEX_TIMEOUT_SEC, SC
|
|
|
700
922
|
var init_global_install = __esm({
|
|
701
923
|
"src/global-install.ts"() {
|
|
702
924
|
"use strict";
|
|
925
|
+
init_hook_launcher();
|
|
926
|
+
init_hook_launcher();
|
|
927
|
+
init_hook_health();
|
|
928
|
+
init_hook_health();
|
|
703
929
|
__dirname = dirname(fileURLToPath(import.meta.url));
|
|
704
930
|
HOOKS_DIR = resolve(__dirname, "..", "hooks");
|
|
705
931
|
ANTIGRAVITY_GROUP = "solongate-guard";
|
|
@@ -713,8 +939,8 @@ var init_global_install = __esm({
|
|
|
713
939
|
|
|
714
940
|
// src/tui/index.tsx
|
|
715
941
|
import { appendFileSync, mkdirSync as mkdirSync7 } from "fs";
|
|
716
|
-
import { homedir as
|
|
717
|
-
import { join as
|
|
942
|
+
import { homedir as homedir11 } from "os";
|
|
943
|
+
import { join as join11 } from "path";
|
|
718
944
|
import { render } from "ink";
|
|
719
945
|
|
|
720
946
|
// src/tui/App.tsx
|
|
@@ -915,8 +1141,8 @@ function KeyHints({ hints }) {
|
|
|
915
1141
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
916
1142
|
import TextInput from "ink-text-input";
|
|
917
1143
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
918
|
-
import { homedir as
|
|
919
|
-
import { join as
|
|
1144
|
+
import { homedir as homedir6 } from "os";
|
|
1145
|
+
import { join as join6, resolve as resolve3 } from "path";
|
|
920
1146
|
|
|
921
1147
|
// src/tui/local-log.ts
|
|
922
1148
|
import { closeSync, existsSync, openSync, readdirSync, readFileSync as readFileSync2, readSync, statSync, writeFileSync } from "fs";
|
|
@@ -948,9 +1174,9 @@ function localLogsSetting() {
|
|
|
948
1174
|
return off;
|
|
949
1175
|
}
|
|
950
1176
|
function policyCachesNewestFirst() {
|
|
951
|
-
const
|
|
952
|
-
return readdirSync(
|
|
953
|
-
const p = join2(
|
|
1177
|
+
const sgDir2 = join2(homedir2(), ".solongate");
|
|
1178
|
+
return readdirSync(sgDir2).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json")).map((f) => {
|
|
1179
|
+
const p = join2(sgDir2, f);
|
|
954
1180
|
let mtime = 0;
|
|
955
1181
|
try {
|
|
956
1182
|
mtime = statSync(p).mtimeMs;
|
|
@@ -961,9 +1187,9 @@ function policyCachesNewestFirst() {
|
|
|
961
1187
|
}
|
|
962
1188
|
function localLogFile() {
|
|
963
1189
|
try {
|
|
964
|
-
const
|
|
965
|
-
const caches = readdirSync(
|
|
966
|
-
const p = join2(
|
|
1190
|
+
const sgDir2 = join2(homedir2(), ".solongate");
|
|
1191
|
+
const caches = readdirSync(sgDir2).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json")).map((f) => {
|
|
1192
|
+
const p = join2(sgDir2, f);
|
|
967
1193
|
let mtime = 0;
|
|
968
1194
|
try {
|
|
969
1195
|
mtime = statSync(p).mtimeMs;
|
|
@@ -1024,31 +1250,6 @@ function tailLines(file, maxBytes = 131072) {
|
|
|
1024
1250
|
return [];
|
|
1025
1251
|
}
|
|
1026
1252
|
}
|
|
1027
|
-
function deleteLocalEntry(at, tool, session) {
|
|
1028
|
-
try {
|
|
1029
|
-
const file = localLogFile();
|
|
1030
|
-
const lines = readFileSync2(file, "utf-8").split("\n");
|
|
1031
|
-
let removed = 0;
|
|
1032
|
-
const kept = lines.filter((line) => {
|
|
1033
|
-
if (!line.trim()) return false;
|
|
1034
|
-
if (removed) return true;
|
|
1035
|
-
try {
|
|
1036
|
-
const j = JSON.parse(line);
|
|
1037
|
-
const hit = Date.parse(j.ts ?? "") === at && (j.tool ?? "?") === tool && (!session || j.session_id === session);
|
|
1038
|
-
if (hit) {
|
|
1039
|
-
removed++;
|
|
1040
|
-
return false;
|
|
1041
|
-
}
|
|
1042
|
-
} catch {
|
|
1043
|
-
}
|
|
1044
|
-
return true;
|
|
1045
|
-
});
|
|
1046
|
-
if (removed) writeFileSync(file, kept.length ? kept.join("\n") + "\n" : "");
|
|
1047
|
-
return removed;
|
|
1048
|
-
} catch {
|
|
1049
|
-
return 0;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
1253
|
function clearLocalLog() {
|
|
1053
1254
|
try {
|
|
1054
1255
|
const file = localLogFile();
|
|
@@ -1099,15 +1300,15 @@ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2
|
|
|
1099
1300
|
|
|
1100
1301
|
// src/api-client/client.ts
|
|
1101
1302
|
init_global_install();
|
|
1102
|
-
import { readFileSync as
|
|
1103
|
-
import { resolve as resolve2, join as
|
|
1104
|
-
import { homedir as
|
|
1303
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
1304
|
+
import { resolve as resolve2, join as join5 } from "path";
|
|
1305
|
+
import { homedir as homedir5 } from "os";
|
|
1105
1306
|
var DEFAULT_API_URL = "https://api.solongate.com";
|
|
1106
|
-
var accountsFile = () =>
|
|
1307
|
+
var accountsFile = () => join5(homedir5(), ".solongate", "accounts.json");
|
|
1107
1308
|
function listAccounts() {
|
|
1108
1309
|
let list5 = [];
|
|
1109
1310
|
try {
|
|
1110
|
-
const raw = JSON.parse(
|
|
1311
|
+
const raw = JSON.parse(readFileSync5(accountsFile(), "utf-8"));
|
|
1111
1312
|
if (Array.isArray(raw)) list5 = raw.filter((a) => a && typeof a.apiKey === "string");
|
|
1112
1313
|
} catch {
|
|
1113
1314
|
}
|
|
@@ -1121,7 +1322,7 @@ function saveAccount(acc) {
|
|
|
1121
1322
|
try {
|
|
1122
1323
|
const list5 = (() => {
|
|
1123
1324
|
try {
|
|
1124
|
-
const raw = JSON.parse(
|
|
1325
|
+
const raw = JSON.parse(readFileSync5(accountsFile(), "utf-8"));
|
|
1125
1326
|
return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
|
|
1126
1327
|
} catch {
|
|
1127
1328
|
return [];
|
|
@@ -1129,7 +1330,7 @@ function saveAccount(acc) {
|
|
|
1129
1330
|
})();
|
|
1130
1331
|
const next = list5.filter((a) => a.apiKey !== acc.apiKey);
|
|
1131
1332
|
next.unshift({ ...acc, addedAt: acc.addedAt ?? Date.now() });
|
|
1132
|
-
mkdirSync2(
|
|
1333
|
+
mkdirSync2(join5(homedir5(), ".solongate"), { recursive: true });
|
|
1133
1334
|
writeFileSync3(accountsFile(), JSON.stringify(next, null, 2));
|
|
1134
1335
|
} catch {
|
|
1135
1336
|
}
|
|
@@ -1138,7 +1339,7 @@ function removeAccount(apiKey) {
|
|
|
1138
1339
|
try {
|
|
1139
1340
|
const list5 = (() => {
|
|
1140
1341
|
try {
|
|
1141
|
-
const raw = JSON.parse(
|
|
1342
|
+
const raw = JSON.parse(readFileSync5(accountsFile(), "utf-8"));
|
|
1142
1343
|
return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
|
|
1143
1344
|
} catch {
|
|
1144
1345
|
return [];
|
|
@@ -1161,12 +1362,12 @@ function isActiveAccount(apiKey) {
|
|
|
1161
1362
|
}
|
|
1162
1363
|
function setActiveAccount(creds) {
|
|
1163
1364
|
try {
|
|
1164
|
-
const dir =
|
|
1365
|
+
const dir = join5(homedir5(), ".solongate");
|
|
1165
1366
|
mkdirSync2(dir, { recursive: true });
|
|
1166
|
-
const p =
|
|
1367
|
+
const p = join5(dir, ["cloud", "guard.json"].join("-"));
|
|
1167
1368
|
let existing = {};
|
|
1168
1369
|
try {
|
|
1169
|
-
existing = JSON.parse(
|
|
1370
|
+
existing = JSON.parse(readFileSync5(p, "utf-8"));
|
|
1170
1371
|
} catch {
|
|
1171
1372
|
}
|
|
1172
1373
|
if (!writeProtectedFile(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2))) return false;
|
|
@@ -1178,14 +1379,14 @@ function setActiveAccount(creds) {
|
|
|
1178
1379
|
}
|
|
1179
1380
|
function clearActiveCredential() {
|
|
1180
1381
|
try {
|
|
1181
|
-
const p =
|
|
1182
|
-
if (!
|
|
1382
|
+
const p = join5(homedir5(), ".solongate", ["cloud", "guard.json"].join("-"));
|
|
1383
|
+
if (!existsSync4(p)) {
|
|
1183
1384
|
cached2 = null;
|
|
1184
1385
|
return true;
|
|
1185
1386
|
}
|
|
1186
1387
|
let existing = {};
|
|
1187
1388
|
try {
|
|
1188
|
-
existing = JSON.parse(
|
|
1389
|
+
existing = JSON.parse(readFileSync5(p, "utf-8"));
|
|
1189
1390
|
} catch {
|
|
1190
1391
|
}
|
|
1191
1392
|
delete existing.apiKey;
|
|
@@ -1216,9 +1417,9 @@ var NotAuthenticatedError = class extends Error {
|
|
|
1216
1417
|
};
|
|
1217
1418
|
function loginCredentialFile() {
|
|
1218
1419
|
try {
|
|
1219
|
-
const p =
|
|
1220
|
-
if (!
|
|
1221
|
-
const c2 = JSON.parse(
|
|
1420
|
+
const p = join5(homedir5(), ".solongate", "cloud-guard.json");
|
|
1421
|
+
if (!existsSync4(p)) return {};
|
|
1422
|
+
const c2 = JSON.parse(readFileSync5(p, "utf-8"));
|
|
1222
1423
|
return c2 && typeof c2 === "object" ? c2 : {};
|
|
1223
1424
|
} catch {
|
|
1224
1425
|
return {};
|
|
@@ -1227,8 +1428,8 @@ function loginCredentialFile() {
|
|
|
1227
1428
|
function dotenvApiKey() {
|
|
1228
1429
|
try {
|
|
1229
1430
|
const envPath = resolve2(".env");
|
|
1230
|
-
if (!
|
|
1231
|
-
for (const line of
|
|
1431
|
+
if (!existsSync4(envPath)) return void 0;
|
|
1432
|
+
for (const line of readFileSync5(envPath, "utf-8").split("\n")) {
|
|
1232
1433
|
const trimmed = line.trim();
|
|
1233
1434
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1234
1435
|
const eq = trimmed.indexOf("=");
|
|
@@ -1545,19 +1746,11 @@ var audit_exports = {};
|
|
|
1545
1746
|
__export(audit_exports, {
|
|
1546
1747
|
block: () => block,
|
|
1547
1748
|
list: () => list2,
|
|
1548
|
-
remove: () => remove2,
|
|
1549
|
-
removeAll: () => removeAll,
|
|
1550
1749
|
whitelist: () => whitelist
|
|
1551
1750
|
});
|
|
1552
1751
|
function list2(query = {}) {
|
|
1553
1752
|
return request("GET", "/audit-logs", { query });
|
|
1554
1753
|
}
|
|
1555
|
-
function remove2(ids) {
|
|
1556
|
-
return request("DELETE", "/audit-logs", { body: { ids } });
|
|
1557
|
-
}
|
|
1558
|
-
function removeAll() {
|
|
1559
|
-
return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
|
|
1560
|
-
}
|
|
1561
1754
|
function whitelist(id, scope = "exact") {
|
|
1562
1755
|
return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
|
|
1563
1756
|
}
|
|
@@ -1720,7 +1913,7 @@ var projectKey = (dir) => {
|
|
|
1720
1913
|
}
|
|
1721
1914
|
return h.toString(16);
|
|
1722
1915
|
};
|
|
1723
|
-
var RING =
|
|
1916
|
+
var RING = join6(homedir6(), ".solongate", "projects", projectKey(resolve3(process.cwd())), ".eval-ring.jsonl");
|
|
1724
1917
|
var fmtUp = (ms) => {
|
|
1725
1918
|
const s = Math.floor(ms / 1e3);
|
|
1726
1919
|
const p = (n) => String(n).padStart(2, "0");
|
|
@@ -2327,9 +2520,9 @@ function LivePanel({ active: active2 }) {
|
|
|
2327
2520
|
else if (input === "x") toggleSignal("dlp");
|
|
2328
2521
|
else if (input === "r") toggleSignal("ratelimit");
|
|
2329
2522
|
else if (input === "e") {
|
|
2330
|
-
const file =
|
|
2523
|
+
const file = join6(homedir6(), ".solongate", "live-export.jsonl");
|
|
2331
2524
|
try {
|
|
2332
|
-
mkdirSync3(
|
|
2525
|
+
mkdirSync3(join6(homedir6(), ".solongate"), { recursive: true });
|
|
2333
2526
|
writeFileSync4(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
2334
2527
|
setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
|
|
2335
2528
|
} catch (err2) {
|
|
@@ -3929,8 +4122,8 @@ function DlpPanel({ focused }) {
|
|
|
3929
4122
|
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
3930
4123
|
import TextInput5 from "ink-text-input";
|
|
3931
4124
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
3932
|
-
import { homedir as
|
|
3933
|
-
import { join as
|
|
4125
|
+
import { homedir as homedir7 } from "os";
|
|
4126
|
+
import { join as join7 } from "path";
|
|
3934
4127
|
import { useState as useState7 } from "react";
|
|
3935
4128
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3936
4129
|
var DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
@@ -4003,8 +4196,6 @@ var AUDIT_HELP = [
|
|
|
4003
4196
|
["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
|
|
4004
4197
|
["t / n", "tool / agent filter (type, enter done)"],
|
|
4005
4198
|
["/", "free-text search"],
|
|
4006
|
-
["x", "delete ONLY the selected entry (press x twice)"],
|
|
4007
|
-
["X", "delete ALL matched logs of the source (press X twice)"],
|
|
4008
4199
|
["e", "export this page \u2192 ~/.solongate/audit-export-<src>.jsonl"],
|
|
4009
4200
|
["E", "export ALL matched rows (cloud: up to 10k)"],
|
|
4010
4201
|
["c", "clear every filter (incl. session)"]
|
|
@@ -4055,7 +4246,6 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4055
4246
|
const [si, setSi] = useState7(0);
|
|
4056
4247
|
const [sessSearch, setSessSearch] = useState7("");
|
|
4057
4248
|
const [sessSel, setSessSel] = useState7(0);
|
|
4058
|
-
const [confirm, setConfirm] = useState7(null);
|
|
4059
4249
|
const [msg, setMsg] = useState7(null);
|
|
4060
4250
|
const [showHelp, setShowHelp] = useState7(false);
|
|
4061
4251
|
const [frozen, setFrozen] = useState7(false);
|
|
@@ -4137,34 +4327,11 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4137
4327
|
const currentSess = sessionsFiltered[Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1))];
|
|
4138
4328
|
const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
|
|
4139
4329
|
const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
|
|
4140
|
-
const doDelete = (kind) => {
|
|
4141
|
-
setMsg({ text: "deleting\u2026", level: "ok" });
|
|
4142
|
-
const run = async () => {
|
|
4143
|
-
if (source === "cloud") {
|
|
4144
|
-
if (kind === "one") {
|
|
4145
|
-
if (!current) throw new Error("nothing selected");
|
|
4146
|
-
await api.audit.remove([current.id]);
|
|
4147
|
-
} else {
|
|
4148
|
-
await api.audit.removeAll();
|
|
4149
|
-
}
|
|
4150
|
-
cloudQ.reload();
|
|
4151
|
-
statsQ.reloadQuiet();
|
|
4152
|
-
} else {
|
|
4153
|
-
const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
|
|
4154
|
-
if (!n) throw new Error("entry not found in the local file");
|
|
4155
|
-
localQ.reload();
|
|
4156
|
-
}
|
|
4157
|
-
};
|
|
4158
|
-
run().then(() => {
|
|
4159
|
-
setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
|
|
4160
|
-
toTop();
|
|
4161
|
-
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
4162
|
-
};
|
|
4163
4330
|
const doExport = (kind) => {
|
|
4164
4331
|
setMsg({ text: "exporting\u2026", level: "ok" });
|
|
4165
4332
|
const run = async () => {
|
|
4166
|
-
const dir =
|
|
4167
|
-
const file =
|
|
4333
|
+
const dir = join7(homedir7(), ".solongate");
|
|
4334
|
+
const file = join7(dir, `audit-export-${source}.jsonl`);
|
|
4168
4335
|
let rows2;
|
|
4169
4336
|
if (kind === "page") rows2 = pageRows;
|
|
4170
4337
|
else if (source === "cloud") {
|
|
@@ -4210,8 +4377,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4210
4377
|
return;
|
|
4211
4378
|
}
|
|
4212
4379
|
if (view === "logs" ? logsLoading : sessLoading) return;
|
|
4213
|
-
if (
|
|
4214
|
-
setConfirm(null);
|
|
4380
|
+
if (msg) {
|
|
4215
4381
|
setMsg(null);
|
|
4216
4382
|
}
|
|
4217
4383
|
if (input === "s") {
|
|
@@ -4275,23 +4441,6 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4275
4441
|
setGi((n) => (n + 1) % SIGNALS.length);
|
|
4276
4442
|
setPage(0);
|
|
4277
4443
|
toTop();
|
|
4278
|
-
} else if (input === "x") {
|
|
4279
|
-
if (!current) return;
|
|
4280
|
-
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
4281
|
-
setConfirm({ kind: "one", key: current.id });
|
|
4282
|
-
setMsg({ text: `x = delete ONLY the selected entry: ${current.decision} ${current.tool} (${ago(current.at)} ago) \u2014 press x again`, level: "bad" });
|
|
4283
|
-
return;
|
|
4284
|
-
}
|
|
4285
|
-
setConfirm(null);
|
|
4286
|
-
doDelete("one");
|
|
4287
|
-
} else if (input === "X") {
|
|
4288
|
-
if (confirm?.kind !== "all") {
|
|
4289
|
-
setConfirm({ kind: "all", key: "all" });
|
|
4290
|
-
setMsg({ text: `\u26A0 X = delete ALL ${source} logs \u2014 every one of the ${total} matched entries! press X again`, level: "bad" });
|
|
4291
|
-
return;
|
|
4292
|
-
}
|
|
4293
|
-
setConfirm(null);
|
|
4294
|
-
doDelete("all");
|
|
4295
4444
|
} else if (input === "e") doExport("page");
|
|
4296
4445
|
else if (input === "E") doExport("all");
|
|
4297
4446
|
else if (input === "t") setEditing("tool");
|
|
@@ -4556,10 +4705,11 @@ import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } fro
|
|
|
4556
4705
|
init_global_install();
|
|
4557
4706
|
|
|
4558
4707
|
// src/commands/doctor.ts
|
|
4559
|
-
import { existsSync as
|
|
4560
|
-
import { homedir as
|
|
4561
|
-
import { join as
|
|
4708
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
4709
|
+
import { homedir as homedir8 } from "os";
|
|
4710
|
+
import { join as join8 } from "path";
|
|
4562
4711
|
init_global_install();
|
|
4712
|
+
init_hook_health();
|
|
4563
4713
|
async function collectChecks() {
|
|
4564
4714
|
const checks = [];
|
|
4565
4715
|
if (!isAuthenticated()) {
|
|
@@ -4607,8 +4757,32 @@ async function collectChecks() {
|
|
|
4607
4757
|
ok: claudeReg,
|
|
4608
4758
|
detail: claudeReg ? "guard registered" : "guard NOT registered - run `solongate repair`"
|
|
4609
4759
|
});
|
|
4610
|
-
if (
|
|
4611
|
-
const
|
|
4760
|
+
if (claudeReg) {
|
|
4761
|
+
const start = hookCanStart();
|
|
4762
|
+
checks.push({
|
|
4763
|
+
name: "hook runtime",
|
|
4764
|
+
ok: start.ok,
|
|
4765
|
+
detail: start.ok ? `node ${start.detail}` : `${start.detail} - nothing is being enforced or logged`
|
|
4766
|
+
});
|
|
4767
|
+
const beat = guardBeat();
|
|
4768
|
+
if (!beat) {
|
|
4769
|
+
checks.push({
|
|
4770
|
+
name: "guard fired",
|
|
4771
|
+
ok: "warn",
|
|
4772
|
+
detail: "never - open your agent and run one tool call, then check again"
|
|
4773
|
+
});
|
|
4774
|
+
} else if (beat.node === "no-node") {
|
|
4775
|
+
checks.push({
|
|
4776
|
+
name: "guard fired",
|
|
4777
|
+
ok: false,
|
|
4778
|
+
detail: `${agoLabel(beat.at)}, but found no node to run with - run \`solongate repair\``
|
|
4779
|
+
});
|
|
4780
|
+
} else {
|
|
4781
|
+
checks.push({ name: "guard fired", ok: true, detail: `${agoLabel(beat.at)} \xB7 ${beat.node}` });
|
|
4782
|
+
}
|
|
4783
|
+
}
|
|
4784
|
+
if (existsSync5(globalPaths().antigravityDir)) {
|
|
4785
|
+
const reg = existsSync5(globalPaths().antigravityHooksPath);
|
|
4612
4786
|
checks.push({
|
|
4613
4787
|
name: "Antigravity hooks",
|
|
4614
4788
|
ok: reg,
|
|
@@ -4636,7 +4810,7 @@ async function collectChecks() {
|
|
|
4636
4810
|
});
|
|
4637
4811
|
}
|
|
4638
4812
|
try {
|
|
4639
|
-
const raw =
|
|
4813
|
+
const raw = readFileSync6(join8(homedir8(), ".solongate", ".key-rejected.json"), "utf-8");
|
|
4640
4814
|
const m = JSON.parse(raw);
|
|
4641
4815
|
const ageMin = m.ts ? Math.round((Date.now() - m.ts) / 6e4) : null;
|
|
4642
4816
|
checks.push({
|
|
@@ -4647,8 +4821,8 @@ async function collectChecks() {
|
|
|
4647
4821
|
} catch {
|
|
4648
4822
|
}
|
|
4649
4823
|
const LOCAL_LOG2 = localLogFile();
|
|
4650
|
-
if (
|
|
4651
|
-
const st =
|
|
4824
|
+
if (existsSync5(LOCAL_LOG2)) {
|
|
4825
|
+
const st = statSync4(LOCAL_LOG2);
|
|
4652
4826
|
const ageMin = (Date.now() - st.mtimeMs) / 6e4;
|
|
4653
4827
|
checks.push({ name: "local logs", ok: true, detail: `on \xB7 ${(st.size / 1024).toFixed(0)}KB \xB7 last write ${ageMin < 1 ? "just now" : Math.round(ageMin) + "m ago"}` });
|
|
4654
4828
|
} else {
|
|
@@ -4659,17 +4833,17 @@ async function collectChecks() {
|
|
|
4659
4833
|
|
|
4660
4834
|
// src/logs-server-daemon.ts
|
|
4661
4835
|
import { spawn as spawn4 } from "child_process";
|
|
4662
|
-
import { mkdirSync as mkdirSync5, openSync as openSync2, readFileSync as
|
|
4663
|
-
import { homedir as
|
|
4664
|
-
import { dirname as dirname2, join as
|
|
4836
|
+
import { mkdirSync as mkdirSync5, openSync as openSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
4837
|
+
import { homedir as homedir9 } from "os";
|
|
4838
|
+
import { dirname as dirname2, join as join9 } from "path";
|
|
4665
4839
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4666
|
-
var DIR =
|
|
4667
|
-
var STATE_FILE =
|
|
4668
|
-
var LOG_FILE =
|
|
4840
|
+
var DIR = join9(homedir9(), ".solongate");
|
|
4841
|
+
var STATE_FILE = join9(DIR, ".logs-server.json");
|
|
4842
|
+
var LOG_FILE = join9(DIR, "logs-server.log");
|
|
4669
4843
|
var LOGS_SERVER_PORT = 8788;
|
|
4670
4844
|
function readState() {
|
|
4671
4845
|
try {
|
|
4672
|
-
const s = JSON.parse(
|
|
4846
|
+
const s = JSON.parse(readFileSync7(STATE_FILE, "utf-8"));
|
|
4673
4847
|
return s && typeof s === "object" ? s : {};
|
|
4674
4848
|
} catch {
|
|
4675
4849
|
return {};
|
|
@@ -4705,7 +4879,7 @@ function startLogsServerDaemon() {
|
|
|
4705
4879
|
try {
|
|
4706
4880
|
mkdirSync5(DIR, { recursive: true });
|
|
4707
4881
|
const log = openSync2(LOG_FILE, "a");
|
|
4708
|
-
const cli =
|
|
4882
|
+
const cli = join9(dirname2(fileURLToPath2(import.meta.url)), "index.js");
|
|
4709
4883
|
const p = spawn4(process.execPath, [cli, "logs-server"], {
|
|
4710
4884
|
detached: true,
|
|
4711
4885
|
stdio: ["ignore", log, log],
|
|
@@ -4736,22 +4910,22 @@ function stopLogsServerDaemon() {
|
|
|
4736
4910
|
}
|
|
4737
4911
|
|
|
4738
4912
|
// src/self-update.ts
|
|
4739
|
-
import { execFile, execFileSync as
|
|
4740
|
-
import { access, mkdirSync as mkdirSync6, openSync as openSync3, readFileSync as
|
|
4741
|
-
import { homedir as
|
|
4742
|
-
import { dirname as dirname3, join as
|
|
4913
|
+
import { execFile, execFileSync as execFileSync3, spawn as spawn5 } from "child_process";
|
|
4914
|
+
import { access, mkdirSync as mkdirSync6, openSync as openSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync7, constants as FS } from "fs";
|
|
4915
|
+
import { homedir as homedir10 } from "os";
|
|
4916
|
+
import { dirname as dirname3, join as join10, sep } from "path";
|
|
4743
4917
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4744
4918
|
var PKG = "@solongate/proxy";
|
|
4745
4919
|
var CHECK_EVERY_MS = 30 * 60 * 1e3;
|
|
4746
4920
|
var ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
|
|
4747
|
-
var STATE_FILE2 =
|
|
4748
|
-
var LOG_FILE2 =
|
|
4749
|
-
var LOCK_FILE =
|
|
4921
|
+
var STATE_FILE2 = join10(homedir10(), ".solongate", ".self-update.json");
|
|
4922
|
+
var LOG_FILE2 = join10(homedir10(), ".solongate", "self-update.log");
|
|
4923
|
+
var LOCK_FILE = join10(homedir10(), ".solongate", ".update-install.lock");
|
|
4750
4924
|
var LOCK_STALE_MS = 3 * 6e4;
|
|
4751
4925
|
var NEEDS_ADMIN_RE = /\bEACCES\b|\bEPERM\b|permission denied|operation not permitted/i;
|
|
4752
4926
|
function readState2() {
|
|
4753
4927
|
try {
|
|
4754
|
-
const s = JSON.parse(
|
|
4928
|
+
const s = JSON.parse(readFileSync8(STATE_FILE2, "utf-8"));
|
|
4755
4929
|
return s && typeof s === "object" ? s : {};
|
|
4756
4930
|
} catch {
|
|
4757
4931
|
return {};
|
|
@@ -4759,7 +4933,7 @@ function readState2() {
|
|
|
4759
4933
|
}
|
|
4760
4934
|
function writeState2(s) {
|
|
4761
4935
|
try {
|
|
4762
|
-
mkdirSync6(
|
|
4936
|
+
mkdirSync6(join10(homedir10(), ".solongate"), { recursive: true });
|
|
4763
4937
|
writeFileSync7(STATE_FILE2, JSON.stringify(s));
|
|
4764
4938
|
} catch {
|
|
4765
4939
|
}
|
|
@@ -4780,7 +4954,7 @@ function setAutoUpdate(on) {
|
|
|
4780
4954
|
}
|
|
4781
4955
|
function currentVersion() {
|
|
4782
4956
|
try {
|
|
4783
|
-
const pkg = JSON.parse(
|
|
4957
|
+
const pkg = JSON.parse(readFileSync8(join10(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf-8"));
|
|
4784
4958
|
return pkg.version ?? "0.0.0";
|
|
4785
4959
|
} catch {
|
|
4786
4960
|
return "0.0.0";
|
|
@@ -4817,7 +4991,7 @@ async function fetchLatest() {
|
|
|
4817
4991
|
function runGlobalInstall(version) {
|
|
4818
4992
|
return new Promise((resolve4) => {
|
|
4819
4993
|
try {
|
|
4820
|
-
mkdirSync6(
|
|
4994
|
+
mkdirSync6(join10(homedir10(), ".solongate"), { recursive: true });
|
|
4821
4995
|
execFile(
|
|
4822
4996
|
"npm",
|
|
4823
4997
|
["install", "-g", `${PKG}@${version}`],
|
|
@@ -5981,11 +6155,11 @@ async function launchTui() {
|
|
|
5981
6155
|
return;
|
|
5982
6156
|
}
|
|
5983
6157
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
5984
|
-
const debugLog =
|
|
6158
|
+
const debugLog = join11(homedir11(), ".solongate", "dataroom-debug.log");
|
|
5985
6159
|
const saved = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
|
|
5986
6160
|
const toFile = (level) => (...args) => {
|
|
5987
6161
|
try {
|
|
5988
|
-
mkdirSync7(
|
|
6162
|
+
mkdirSync7(join11(homedir11(), ".solongate"), { recursive: true });
|
|
5989
6163
|
appendFileSync(debugLog, `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
5990
6164
|
`);
|
|
5991
6165
|
} catch {
|