glimmervoid 0.0.0-stage → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +48 -0
- package/README.md +195 -2
- package/dist/bin/glimmervoid.js +275 -0
- package/dist/chunks/adapters-Dho1VSY5.js +2 -0
- package/dist/chunks/adapters-zPDx1Xox.js +575 -0
- package/dist/chunks/agent-api-cli-D0MmtEdz.js +71 -0
- package/dist/chunks/agent-api-core-Dz0r21y9.js +103 -0
- package/dist/chunks/agent-setup-cli-B5Atxr3F.js +139 -0
- package/dist/chunks/ascii-figure-core-pDAwtlKO.js +45 -0
- package/dist/chunks/child-process-safe-DRGsp0TE.js +69 -0
- package/dist/chunks/child-process-safe-DvQjXsgz.js +2 -0
- package/dist/chunks/config-path-core-DFt-TemS.js +38 -0
- package/dist/chunks/config-store-CC8T3okC.js +2 -0
- package/dist/chunks/config-store-mm7MgzoL.js +1412 -0
- package/dist/chunks/contracts-DGkKNh9W.js +3020 -0
- package/dist/chunks/grok-DuU8xVLZ.js +436 -0
- package/dist/chunks/hook-command-core-DclEoE9e.js +14 -0
- package/dist/chunks/hook-relay-core-BHfYFnYV.js +88 -0
- package/dist/chunks/hook-tools-pSJia7ks.js +74 -0
- package/dist/chunks/ingest-core-DWI1Mbu4.js +342 -0
- package/dist/chunks/ingest-editor-core-BuI1gMF_.js +445 -0
- package/dist/chunks/loopback-post-C7C22eYi.js +79 -0
- package/dist/chunks/main-CuIZQPhZ.js +43559 -0
- package/dist/chunks/node-pty-preflight-CK1knGIR.js +162 -0
- package/dist/chunks/pair-cli-w-gCAF-1.js +110 -0
- package/dist/chunks/path-doctor-C23VAqeu.js +63 -0
- package/dist/chunks/refocus-core-BKQKllq7.js +38 -0
- package/dist/chunks/relay-stdin-CpHvLu6M.js +20 -0
- package/dist/chunks/remote-config-C2ytQwrs.js +525 -0
- package/dist/chunks/rtk-hook-core-Bl6gYHPN.js +133 -0
- package/dist/chunks/rtk-resolver-CLo-gZzw.js +21 -0
- package/dist/chunks/rtk-resolver-CMQAidlI.js +2 -0
- package/dist/chunks/runtime-paths-Ci_ksSkj.js +75 -0
- package/dist/chunks/runtime-paths-JLrZ3wyi.js +2 -0
- package/dist/chunks/sandbox-deps-Dd5-kZnf.js +67 -0
- package/dist/chunks/session-Cez8aIWg.js +164 -0
- package/dist/chunks/spawn-command-Cc40FiZI.js +134 -0
- package/dist/chunks/visions-cli-BXQFIBCX.js +98 -0
- package/dist/chunks/visions-setup-CvVnOe1r.js +1753 -0
- package/dist/client/assets/favicon-CKygeLwW.svg +7 -0
- package/dist/client/assets/index-B1MFijjb.css +1 -0
- package/dist/client/assets/index-CU1iPPdn.js +249 -0
- package/dist/client/index.html +108 -0
- package/dist/package.json +3 -0
- package/dist/scripts/postinstall-path-check.js +36 -0
- package/dist/server/index.js +92 -0
- package/dist/session/command-hook-relay.js +16 -0
- package/dist/session/hook-relay.js +24 -0
- package/dist/session/hook-tool-relay.js +107 -0
- package/dist/session/statusline-relay.js +82 -0
- package/dist/session/visions-relay.js +627 -0
- package/dist/tools/vscode-visions/extension.js +289 -0
- package/dist/tools/vscode-visions/lsp-convert.js +80 -0
- package/dist/tools/vscode-visions/package.json +32 -0
- package/dist/tools/vscode-visions/visions-lsp-core.js +112 -0
- package/package.json +96 -4
- package/scripts/postinstall.mjs +4 -0
- package/scripts/prepare-build.js +38 -0
- package/scripts/recover-handoff.mjs +82 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { X as equalsIgnoringCaseOnWindows, g as writeJsonAtomicSync, h as writeJsonAtomic, q as canonicalizePath, s as glimmervoidHomeDir } from "./config-store-mm7MgzoL.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
//#region server/core/pairing-token.ts
|
|
6
|
+
var DEFAULT_TOKEN_TTL_MS = 6e5;
|
|
7
|
+
var DEFAULT_DEVICE_MAX_AGE_MS = 15552e6;
|
|
8
|
+
function hashSecret(value) {
|
|
9
|
+
return crypto.createHash("sha256").update(String(value), "utf8").digest("hex");
|
|
10
|
+
}
|
|
11
|
+
function mintPairingToken({ now = Date.now(), ttlMs = DEFAULT_TOKEN_TTL_MS, randomBytes = crypto.randomBytes } = {}) {
|
|
12
|
+
const token = Buffer.from(randomBytes(32)).toString("base64url");
|
|
13
|
+
return {
|
|
14
|
+
token,
|
|
15
|
+
tokenHash: hashSecret(token),
|
|
16
|
+
createdAt: now,
|
|
17
|
+
expiresAt: now + ttlMs
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function decideRedemption({ record, now = Date.now() }) {
|
|
21
|
+
if (!record) return {
|
|
22
|
+
ok: false,
|
|
23
|
+
reason: "unknown"
|
|
24
|
+
};
|
|
25
|
+
if (record.usedAt) return {
|
|
26
|
+
ok: false,
|
|
27
|
+
reason: "used"
|
|
28
|
+
};
|
|
29
|
+
if (typeof record.expiresAt === "number" && now > record.expiresAt) return {
|
|
30
|
+
ok: false,
|
|
31
|
+
reason: "expired"
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
reason: null
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function mintDeviceCredential({ randomBytes = crypto.randomBytes } = {}) {
|
|
39
|
+
const id = Buffer.from(randomBytes(8)).toString("base64url");
|
|
40
|
+
const secret = Buffer.from(randomBytes(32)).toString("base64url");
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
secret,
|
|
44
|
+
secretHash: hashSecret(secret),
|
|
45
|
+
cookieValue: `${id}.${secret}`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function decideDeviceAuth({ record, now = Date.now(), maxAgeMs = DEFAULT_DEVICE_MAX_AGE_MS, secretMatches } = {}) {
|
|
49
|
+
if (!record) return {
|
|
50
|
+
ok: false,
|
|
51
|
+
reason: "unknown"
|
|
52
|
+
};
|
|
53
|
+
if (record.revokedAt) return {
|
|
54
|
+
ok: false,
|
|
55
|
+
reason: "revoked"
|
|
56
|
+
};
|
|
57
|
+
if (typeof record.createdAt === "number" && maxAgeMs > 0 && now - record.createdAt > maxAgeMs) return {
|
|
58
|
+
ok: false,
|
|
59
|
+
reason: "expired"
|
|
60
|
+
};
|
|
61
|
+
if (secretMatches !== true) return {
|
|
62
|
+
ok: false,
|
|
63
|
+
reason: "bad-secret"
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
reason: null
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var UA_PATTERNS = [
|
|
71
|
+
[/iphone/i, "iPhone"],
|
|
72
|
+
[/ipad/i, "iPad"],
|
|
73
|
+
[/android/i, "Android"],
|
|
74
|
+
[/mac os x|macintosh/i, "Mac"],
|
|
75
|
+
[/windows/i, "Windows"],
|
|
76
|
+
[/cros/i, "ChromeOS"],
|
|
77
|
+
[/linux/i, "Linux"]
|
|
78
|
+
];
|
|
79
|
+
function deviceNameFromUserAgent(ua) {
|
|
80
|
+
if (typeof ua !== "string" || ua.trim() === "") return "unnamed device";
|
|
81
|
+
for (const [pattern, label] of UA_PATTERNS) if (pattern.test(ua)) return label;
|
|
82
|
+
return "unnamed device";
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region server/pairings-store.ts
|
|
86
|
+
var SNAPSHOT_RELOAD_MS = 3e4;
|
|
87
|
+
var REVOCATION_PROPAGATION_SECONDS = Math.ceil(SNAPSHOT_RELOAD_MS / 1e3);
|
|
88
|
+
var LOCK_SUFFIX = ".lock";
|
|
89
|
+
var LOCK_RETRY_MS = 50;
|
|
90
|
+
var LOCK_MAX_ATTEMPTS = 10;
|
|
91
|
+
var LOCK_STALE_MS = 5e3;
|
|
92
|
+
function errorLabel(err) {
|
|
93
|
+
const failure = err ?? {};
|
|
94
|
+
return String(failure.code || failure.message || err);
|
|
95
|
+
}
|
|
96
|
+
function errorMessage(err) {
|
|
97
|
+
return err instanceof Error ? err.message : String(err);
|
|
98
|
+
}
|
|
99
|
+
function sleepSync(ms) {
|
|
100
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
101
|
+
}
|
|
102
|
+
function emptyDoc() {
|
|
103
|
+
return {
|
|
104
|
+
version: 1,
|
|
105
|
+
pending: [],
|
|
106
|
+
devices: []
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function configSiblingPath(configPath, name) {
|
|
110
|
+
if (configPath) return path.join(path.dirname(configPath), name);
|
|
111
|
+
return path.join(glimmervoidHomeDir(), name);
|
|
112
|
+
}
|
|
113
|
+
function defaultPairingsPath(configPath) {
|
|
114
|
+
return configSiblingPath(configPath, "pairings.json");
|
|
115
|
+
}
|
|
116
|
+
function defaultSeenPath(configPath) {
|
|
117
|
+
return configSiblingPath(configPath, "pairings-seen.json");
|
|
118
|
+
}
|
|
119
|
+
function coerceDoc(parsed) {
|
|
120
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return emptyDoc();
|
|
121
|
+
const source = parsed;
|
|
122
|
+
return {
|
|
123
|
+
version: 1,
|
|
124
|
+
pending: Array.isArray(source.pending) ? source.pending.filter((r) => r && typeof r.tokenHash === "string") : [],
|
|
125
|
+
devices: Array.isArray(source.devices) ? source.devices.filter((r) => r && typeof r.id === "string") : []
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function pairingsSignature(doc) {
|
|
129
|
+
const safe = coerceDoc(doc);
|
|
130
|
+
return JSON.stringify(safe);
|
|
131
|
+
}
|
|
132
|
+
function createPairingsStore({ filePath = defaultPairingsPath(), now = Date.now, randomBytes, warn = console.warn, setIntervalFn = (fn, ms) => setInterval(fn, ms), clearIntervalFn = (handle) => clearInterval(handle) } = {}) {
|
|
133
|
+
const pairingsPath = filePath;
|
|
134
|
+
let snapshot = emptyDoc();
|
|
135
|
+
function readDocSync() {
|
|
136
|
+
let raw;
|
|
137
|
+
try {
|
|
138
|
+
raw = fs.readFileSync(pairingsPath, "utf8");
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (err?.code === "ENOENT") return {
|
|
141
|
+
doc: emptyDoc(),
|
|
142
|
+
missing: true,
|
|
143
|
+
corrupt: false
|
|
144
|
+
};
|
|
145
|
+
warn(`[pairings] Failed to read ${pairingsPath}: ${errorLabel(err)}`);
|
|
146
|
+
return {
|
|
147
|
+
doc: emptyDoc(),
|
|
148
|
+
missing: false,
|
|
149
|
+
corrupt: true
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
return {
|
|
154
|
+
doc: coerceDoc(JSON.parse(raw)),
|
|
155
|
+
missing: false,
|
|
156
|
+
corrupt: false
|
|
157
|
+
};
|
|
158
|
+
} catch (err) {
|
|
159
|
+
warn(`[pairings] Invalid JSON in ${pairingsPath}: ${errorMessage(err)} - treating as no paired devices`);
|
|
160
|
+
return {
|
|
161
|
+
doc: emptyDoc(),
|
|
162
|
+
missing: false,
|
|
163
|
+
corrupt: true
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function load() {
|
|
168
|
+
snapshot = readDocSync().doc;
|
|
169
|
+
return snapshot;
|
|
170
|
+
}
|
|
171
|
+
const lockPath = `${pairingsPath}${LOCK_SUFFIX}`;
|
|
172
|
+
function ensureDir() {
|
|
173
|
+
fs.mkdirSync(path.dirname(pairingsPath), {
|
|
174
|
+
recursive: true,
|
|
175
|
+
mode: 448
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function removeIfStale() {
|
|
179
|
+
try {
|
|
180
|
+
const stat = fs.statSync(lockPath);
|
|
181
|
+
if (Date.now() - stat.mtimeMs <= LOCK_STALE_MS) return false;
|
|
182
|
+
fs.unlinkSync(lockPath);
|
|
183
|
+
warn("[pairings] Removed a stale write lock left by an earlier process");
|
|
184
|
+
return true;
|
|
185
|
+
} catch {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function acquireLock() {
|
|
190
|
+
for (let attempt = 0; attempt < LOCK_MAX_ATTEMPTS; attempt += 1) {
|
|
191
|
+
try {
|
|
192
|
+
fs.closeSync(fs.openSync(lockPath, "wx", 384));
|
|
193
|
+
return true;
|
|
194
|
+
} catch (err) {
|
|
195
|
+
if (err?.code !== "EEXIST") {
|
|
196
|
+
warn(`[pairings] Could not take the write lock: ${errorLabel(err)}`);
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (removeIfStale()) continue;
|
|
201
|
+
sleepSync(LOCK_RETRY_MS);
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
function releaseLock() {
|
|
206
|
+
try {
|
|
207
|
+
fs.unlinkSync(lockPath);
|
|
208
|
+
} catch {}
|
|
209
|
+
}
|
|
210
|
+
function save(mutator) {
|
|
211
|
+
try {
|
|
212
|
+
ensureDir();
|
|
213
|
+
} catch (err) {
|
|
214
|
+
warn(`[pairings] Failed to create ${path.dirname(pairingsPath)}: ${errorLabel(err)}`);
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
if (!acquireLock()) {
|
|
218
|
+
warn("[pairings] Another process holds the pairings write lock - nothing was written");
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const { doc, corrupt } = readDocSync();
|
|
223
|
+
if (corrupt) {
|
|
224
|
+
warn("[pairings] Refusing to write over an unreadable pairings file");
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
if (!mutator(doc)) {
|
|
228
|
+
snapshot = doc;
|
|
229
|
+
return doc;
|
|
230
|
+
}
|
|
231
|
+
writeJsonAtomicSync(pairingsPath, doc, { mode: 384 });
|
|
232
|
+
snapshot = doc;
|
|
233
|
+
return doc;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
warn(`[pairings] Failed to write ${pairingsPath}: ${errorLabel(err)}`);
|
|
236
|
+
return null;
|
|
237
|
+
} finally {
|
|
238
|
+
releaseLock();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function isDeadPending(record, at) {
|
|
242
|
+
if (!record) return true;
|
|
243
|
+
if (record.usedAt) return true;
|
|
244
|
+
return typeof record.expiresAt === "number" && at > record.expiresAt;
|
|
245
|
+
}
|
|
246
|
+
function mintPending({ name = "", ttlMs } = {}) {
|
|
247
|
+
const minted = mintPairingToken({
|
|
248
|
+
now: now(),
|
|
249
|
+
ttlMs,
|
|
250
|
+
randomBytes
|
|
251
|
+
});
|
|
252
|
+
if (!save((doc) => {
|
|
253
|
+
doc.pending = doc.pending.filter((p) => !isDeadPending(p, now()));
|
|
254
|
+
doc.pending.push({
|
|
255
|
+
tokenHash: minted.tokenHash,
|
|
256
|
+
name: String(name || ""),
|
|
257
|
+
createdAt: minted.createdAt,
|
|
258
|
+
expiresAt: minted.expiresAt,
|
|
259
|
+
usedAt: null
|
|
260
|
+
});
|
|
261
|
+
return true;
|
|
262
|
+
})) return null;
|
|
263
|
+
return {
|
|
264
|
+
token: minted.token,
|
|
265
|
+
expiresAt: minted.expiresAt,
|
|
266
|
+
name: String(name || "")
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function redeem(token, { fallbackName = "" } = {}) {
|
|
270
|
+
const tokenHash = hashSecret(token);
|
|
271
|
+
let outcome = {
|
|
272
|
+
ok: false,
|
|
273
|
+
reason: "unknown",
|
|
274
|
+
device: null
|
|
275
|
+
};
|
|
276
|
+
if (!save((doc) => {
|
|
277
|
+
const record = doc.pending.find((p) => p.tokenHash === tokenHash) || null;
|
|
278
|
+
const verdict = decideRedemption({
|
|
279
|
+
record,
|
|
280
|
+
now: now()
|
|
281
|
+
});
|
|
282
|
+
if (!verdict.ok) {
|
|
283
|
+
outcome = {
|
|
284
|
+
ok: false,
|
|
285
|
+
reason: verdict.reason,
|
|
286
|
+
device: null
|
|
287
|
+
};
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
if (!record) return false;
|
|
291
|
+
const credential = mintDeviceCredential({ randomBytes });
|
|
292
|
+
record.usedAt = now();
|
|
293
|
+
const device = {
|
|
294
|
+
id: credential.id,
|
|
295
|
+
secretHash: credential.secretHash,
|
|
296
|
+
name: record.name || String(fallbackName || "") || "unnamed device",
|
|
297
|
+
createdAt: now(),
|
|
298
|
+
revokedAt: null
|
|
299
|
+
};
|
|
300
|
+
doc.devices.push(device);
|
|
301
|
+
outcome = {
|
|
302
|
+
ok: true,
|
|
303
|
+
reason: null,
|
|
304
|
+
device,
|
|
305
|
+
cookieValue: credential.cookieValue
|
|
306
|
+
};
|
|
307
|
+
return true;
|
|
308
|
+
})) return {
|
|
309
|
+
ok: false,
|
|
310
|
+
reason: "write-failed",
|
|
311
|
+
device: null
|
|
312
|
+
};
|
|
313
|
+
return outcome;
|
|
314
|
+
}
|
|
315
|
+
function findDevice(id) {
|
|
316
|
+
if (typeof id !== "string" || id === "") return null;
|
|
317
|
+
return snapshot.devices.find((d) => d.id === id) || null;
|
|
318
|
+
}
|
|
319
|
+
function listDevices() {
|
|
320
|
+
return snapshot.devices.map((d) => ({ ...d }));
|
|
321
|
+
}
|
|
322
|
+
function revokeDevice(id) {
|
|
323
|
+
let found = false;
|
|
324
|
+
if (!save((doc) => {
|
|
325
|
+
const device = doc.devices.find((d) => d.id === id);
|
|
326
|
+
if (!device) return false;
|
|
327
|
+
found = true;
|
|
328
|
+
device.revokedAt = now();
|
|
329
|
+
return true;
|
|
330
|
+
})) return {
|
|
331
|
+
ok: false,
|
|
332
|
+
reason: "write-failed"
|
|
333
|
+
};
|
|
334
|
+
if (!found) return {
|
|
335
|
+
ok: false,
|
|
336
|
+
reason: "unknown"
|
|
337
|
+
};
|
|
338
|
+
return {
|
|
339
|
+
ok: true,
|
|
340
|
+
reason: null
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function prunePending() {
|
|
344
|
+
const at = now();
|
|
345
|
+
let removed = 0;
|
|
346
|
+
save((doc) => {
|
|
347
|
+
const kept = doc.pending.filter((p) => !isDeadPending(p, at));
|
|
348
|
+
removed = doc.pending.length - kept.length;
|
|
349
|
+
if (removed === 0) return false;
|
|
350
|
+
doc.pending = kept;
|
|
351
|
+
return true;
|
|
352
|
+
});
|
|
353
|
+
return removed;
|
|
354
|
+
}
|
|
355
|
+
function watch(onChange) {
|
|
356
|
+
let timer = null;
|
|
357
|
+
let watcher = null;
|
|
358
|
+
const dir = path.dirname(pairingsPath);
|
|
359
|
+
let lastSignature = pairingsSignature(snapshot);
|
|
360
|
+
function refresh() {
|
|
361
|
+
load();
|
|
362
|
+
const signature = pairingsSignature(snapshot);
|
|
363
|
+
if (signature === lastSignature) return;
|
|
364
|
+
lastSignature = signature;
|
|
365
|
+
if (onChange) onChange(snapshot);
|
|
366
|
+
}
|
|
367
|
+
const reloadInterval = setIntervalFn(refresh, SNAPSHOT_RELOAD_MS);
|
|
368
|
+
if (reloadInterval.unref) reloadInterval.unref();
|
|
369
|
+
try {
|
|
370
|
+
ensureDir();
|
|
371
|
+
watcher = fs.watch(canonicalizePath(dir), (_event, filename) => {
|
|
372
|
+
if (filename && !equalsIgnoringCaseOnWindows(path.basename(String(filename)), path.basename(pairingsPath))) return;
|
|
373
|
+
if (timer) clearTimeout(timer);
|
|
374
|
+
timer = setTimeout(refresh, 200);
|
|
375
|
+
if (timer.unref) timer.unref();
|
|
376
|
+
});
|
|
377
|
+
} catch (err) {
|
|
378
|
+
warn(`[pairings] Failed to watch ${dir}: ${errorMessage(err)} - falling back to the ${REVOCATION_PROPAGATION_SECONDS}s reload interval`);
|
|
379
|
+
}
|
|
380
|
+
return function stop() {
|
|
381
|
+
if (timer) clearTimeout(timer);
|
|
382
|
+
clearIntervalFn(reloadInterval);
|
|
383
|
+
if (watcher) try {
|
|
384
|
+
watcher.close();
|
|
385
|
+
} catch {}
|
|
386
|
+
watcher = null;
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
load();
|
|
390
|
+
return {
|
|
391
|
+
path: pairingsPath,
|
|
392
|
+
load,
|
|
393
|
+
save,
|
|
394
|
+
mintPending,
|
|
395
|
+
redeem,
|
|
396
|
+
findDevice,
|
|
397
|
+
listDevices,
|
|
398
|
+
revokeDevice,
|
|
399
|
+
prunePending,
|
|
400
|
+
watch,
|
|
401
|
+
get snapshot() {
|
|
402
|
+
return snapshot;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function createSeenStore({ filePath = defaultSeenPath(), throttleMs = 6e4, now = Date.now } = {}) {
|
|
407
|
+
const lastWriteByDevice = /* @__PURE__ */ new Map();
|
|
408
|
+
let pending = null;
|
|
409
|
+
function readAll() {
|
|
410
|
+
try {
|
|
411
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
412
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
413
|
+
return {};
|
|
414
|
+
} catch {
|
|
415
|
+
return {};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const doc = readAll();
|
|
419
|
+
function touch(deviceId) {
|
|
420
|
+
if (!deviceId) return;
|
|
421
|
+
const at = now();
|
|
422
|
+
const last = lastWriteByDevice.get(deviceId);
|
|
423
|
+
if (last != null && at - last < throttleMs) return;
|
|
424
|
+
lastWriteByDevice.set(deviceId, at);
|
|
425
|
+
doc[deviceId] = at;
|
|
426
|
+
pending = writeJsonAtomic(filePath, doc, { mode: 384 }).catch(() => {});
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
touch,
|
|
430
|
+
readAll,
|
|
431
|
+
get pending() {
|
|
432
|
+
return pending;
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region server/core/remote-config.ts
|
|
438
|
+
function toPort(value) {
|
|
439
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
440
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) return Number.parseInt(value.trim(), 10);
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
function normalizeRemoteConfig(raw) {
|
|
444
|
+
const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
445
|
+
const enabled = src.enabled === true;
|
|
446
|
+
const port = toPort(src.port);
|
|
447
|
+
const publicHost = typeof src.publicHost === "string" ? src.publicHost.trim() : "";
|
|
448
|
+
const ownerLogin = typeof src.ownerLogin === "string" ? src.ownerLogin.trim().toLowerCase() : "";
|
|
449
|
+
if (!enabled) return {
|
|
450
|
+
enabled,
|
|
451
|
+
port,
|
|
452
|
+
publicHost,
|
|
453
|
+
allowedOrigins: [],
|
|
454
|
+
ownerLogin
|
|
455
|
+
};
|
|
456
|
+
const listed = Array.isArray(src.allowedOrigins) ? src.allowedOrigins.filter((o) => typeof o === "string" && o.trim() !== "").map((o) => o.trim()) : [];
|
|
457
|
+
if (listed.length === 0 && publicHost !== "") return {
|
|
458
|
+
enabled,
|
|
459
|
+
port,
|
|
460
|
+
publicHost,
|
|
461
|
+
allowedOrigins: [`https://${publicHost}`],
|
|
462
|
+
ownerLogin
|
|
463
|
+
};
|
|
464
|
+
return {
|
|
465
|
+
enabled,
|
|
466
|
+
port,
|
|
467
|
+
publicHost,
|
|
468
|
+
allowedOrigins: listed,
|
|
469
|
+
ownerLogin
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function validateRemoteConfig(remote, localPort) {
|
|
473
|
+
if (!remote || remote.enabled !== true) return {
|
|
474
|
+
ok: true,
|
|
475
|
+
error: null
|
|
476
|
+
};
|
|
477
|
+
if (remote.port == null) return {
|
|
478
|
+
ok: false,
|
|
479
|
+
error: "remote.enabled is true but remote.port is not set"
|
|
480
|
+
};
|
|
481
|
+
if (!Number.isInteger(remote.port) || remote.port < 1 || remote.port > 65535) return {
|
|
482
|
+
ok: false,
|
|
483
|
+
error: `remote.port must be an integer 1-65535 (got ${JSON.stringify(remote.port)})`
|
|
484
|
+
};
|
|
485
|
+
if (remote.port === localPort) return {
|
|
486
|
+
ok: false,
|
|
487
|
+
error: `remote.port (${remote.port}) must differ from the local port (${localPort})`
|
|
488
|
+
};
|
|
489
|
+
return {
|
|
490
|
+
ok: true,
|
|
491
|
+
error: null
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function isLoopbackHost(host) {
|
|
495
|
+
if (typeof host !== "string") return false;
|
|
496
|
+
const bare = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
497
|
+
if (bare === "" || bare === "localhost") return bare === "localhost";
|
|
498
|
+
if (bare === "::1" || bare === "0:0:0:0:0:0:0:1") return true;
|
|
499
|
+
return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(bare);
|
|
500
|
+
}
|
|
501
|
+
function decideBindHost({ envHost, insecureBind } = {}) {
|
|
502
|
+
const host = typeof envHost === "string" ? envHost.trim() : "";
|
|
503
|
+
if (host === "") return {
|
|
504
|
+
host: "127.0.0.1",
|
|
505
|
+
allowed: true,
|
|
506
|
+
reason: null
|
|
507
|
+
};
|
|
508
|
+
if (isLoopbackHost(host)) return {
|
|
509
|
+
host,
|
|
510
|
+
allowed: true,
|
|
511
|
+
reason: null
|
|
512
|
+
};
|
|
513
|
+
if (insecureBind === true) return {
|
|
514
|
+
host,
|
|
515
|
+
allowed: true,
|
|
516
|
+
reason: "insecure-bind"
|
|
517
|
+
};
|
|
518
|
+
return {
|
|
519
|
+
host,
|
|
520
|
+
allowed: false,
|
|
521
|
+
reason: "non-loopback"
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
//#endregion
|
|
525
|
+
export { configSiblingPath as a, defaultPairingsPath as c, decideDeviceAuth as d, deviceNameFromUserAgent as f, REVOCATION_PROPAGATION_SECONDS as i, defaultSeenPath as l, normalizeRemoteConfig as n, createPairingsStore as o, hashSecret as p, validateRemoteConfig as r, createSeenStore as s, decideBindHost as t, DEFAULT_DEVICE_MAX_AGE_MS as u };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
//#region session/core/sane-yolo.ts
|
|
2
|
+
var SANE_YOLO_PATH_ENV = "GLIMMERVOID_SANE_YOLO_PATH";
|
|
3
|
+
var SANE_YOLO_POLICY = {
|
|
4
|
+
version: 1,
|
|
5
|
+
destructive_command_protection: {
|
|
6
|
+
enabled: true,
|
|
7
|
+
overrides: {
|
|
8
|
+
"git.branch-force-delete": "off",
|
|
9
|
+
"git.merge-abort": "off",
|
|
10
|
+
"git.rebase-abort": "off",
|
|
11
|
+
"rm.recursive-force-dynamic-target": "on"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
secret_protection: { enabled: false }
|
|
15
|
+
};
|
|
16
|
+
var SANE_YOLO_RULES = {
|
|
17
|
+
version: 1,
|
|
18
|
+
rules: ["infra"],
|
|
19
|
+
overrides: {},
|
|
20
|
+
transparent_wrappers: ["rtk", "npx"]
|
|
21
|
+
};
|
|
22
|
+
var INFRA_REASON = "Sane YOLO blocks infrastructure teardown; run this command by hand.";
|
|
23
|
+
var TERRAFORM_DESTROY_FLAGS = [
|
|
24
|
+
"-destroy",
|
|
25
|
+
"--destroy",
|
|
26
|
+
"-destroy=true",
|
|
27
|
+
"--destroy=true"
|
|
28
|
+
];
|
|
29
|
+
var SANE_YOLO_FILES = {
|
|
30
|
+
"policy.json": SANE_YOLO_POLICY,
|
|
31
|
+
"rules/rule.json": SANE_YOLO_RULES,
|
|
32
|
+
"rules/infra/rulebook.json": {
|
|
33
|
+
rulebook_version: 1,
|
|
34
|
+
name: "infra",
|
|
35
|
+
version: "1.1.0",
|
|
36
|
+
allowed_commands: [
|
|
37
|
+
"terraform",
|
|
38
|
+
"tofu",
|
|
39
|
+
"pulumi",
|
|
40
|
+
"kubectl"
|
|
41
|
+
],
|
|
42
|
+
rules: [
|
|
43
|
+
{
|
|
44
|
+
name: "terraform-destroy",
|
|
45
|
+
command: "terraform",
|
|
46
|
+
subcommand: "destroy",
|
|
47
|
+
block_args: ["destroy"],
|
|
48
|
+
reason: INFRA_REASON
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "terraform-apply-destroy",
|
|
52
|
+
command: "terraform",
|
|
53
|
+
subcommand: "apply",
|
|
54
|
+
block_args: TERRAFORM_DESTROY_FLAGS,
|
|
55
|
+
reason: INFRA_REASON
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "tofu-destroy",
|
|
59
|
+
command: "tofu",
|
|
60
|
+
subcommand: "destroy",
|
|
61
|
+
block_args: ["destroy"],
|
|
62
|
+
reason: INFRA_REASON
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "tofu-apply-destroy",
|
|
66
|
+
command: "tofu",
|
|
67
|
+
subcommand: "apply",
|
|
68
|
+
block_args: TERRAFORM_DESTROY_FLAGS,
|
|
69
|
+
reason: INFRA_REASON
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "pulumi-destroy",
|
|
73
|
+
command: "pulumi",
|
|
74
|
+
subcommand: "destroy",
|
|
75
|
+
block_args: ["destroy"],
|
|
76
|
+
reason: INFRA_REASON
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "pulumi-down",
|
|
80
|
+
command: "pulumi",
|
|
81
|
+
subcommand: "down",
|
|
82
|
+
block_args: ["down"],
|
|
83
|
+
reason: INFRA_REASON
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "kubectl-delete-namespace",
|
|
87
|
+
command: "kubectl",
|
|
88
|
+
subcommand: "delete",
|
|
89
|
+
block_args: [
|
|
90
|
+
"ns",
|
|
91
|
+
"namespace",
|
|
92
|
+
"namespaces"
|
|
93
|
+
],
|
|
94
|
+
reason: INFRA_REASON
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
function saneYoloEnv(homeDir) {
|
|
100
|
+
return {
|
|
101
|
+
CC_SAFETY_NET_HOME: homeDir,
|
|
102
|
+
CC_SAFETY_NET_PROJECT_TIGHTEN_ONLY: "1",
|
|
103
|
+
CC_SAFETY_NET_AUDIT_HOME: homeDir,
|
|
104
|
+
CC_SAFETY_NET_AUDIT_SCOPE: "blocked"
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region session/core/rtk-hook-core.ts
|
|
109
|
+
var RTK_PATH_ENV = "GLIMMERVOID_RTK_PATH";
|
|
110
|
+
var MAX_RTK_STDOUT_BYTES = 65536;
|
|
111
|
+
function isPlainObject(value) {
|
|
112
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
113
|
+
}
|
|
114
|
+
function normalizeRtkHookResponse(stdoutText) {
|
|
115
|
+
if (typeof stdoutText !== "string") return "";
|
|
116
|
+
const trimmed = stdoutText.trim();
|
|
117
|
+
if (!trimmed) return "";
|
|
118
|
+
if (Buffer.byteLength(trimmed) > 65536) return "";
|
|
119
|
+
let parsed = null;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(trimmed);
|
|
122
|
+
} catch {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
if (!isPlainObject(parsed)) return "";
|
|
126
|
+
const hookSpecificOutput = parsed.hookSpecificOutput;
|
|
127
|
+
if (isPlainObject(hookSpecificOutput) && isPlainObject(hookSpecificOutput.updatedInput)) {
|
|
128
|
+
if (typeof hookSpecificOutput.permissionDecision !== "string") hookSpecificOutput.permissionDecision = "allow";
|
|
129
|
+
}
|
|
130
|
+
return JSON.stringify(parsed);
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { SANE_YOLO_PATH_ENV as a, SANE_YOLO_FILES as i, RTK_PATH_ENV as n, saneYoloEnv as o, normalizeRtkHookResponse as r, MAX_RTK_STDOUT_BYTES as t };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { i as execSync } from "./child-process-safe-DRGsp0TE.js";
|
|
2
|
+
import { n as glimmervoidHomeDir } from "./config-path-core-DFt-TemS.js";
|
|
3
|
+
import { r as resolveRtkPath } from "./hook-tools-pSJia7ks.js";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
//#region server/rtk-resolver.ts
|
|
7
|
+
function resolveRtkPathFromSystem() {
|
|
8
|
+
return resolveRtkPath({
|
|
9
|
+
glimmervoidHome: glimmervoidHomeDir(os.homedir(), process.env),
|
|
10
|
+
platform: process.platform,
|
|
11
|
+
exec: execSync,
|
|
12
|
+
fsApi: fs
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
var cachedRtkPath = null;
|
|
16
|
+
function getRtkPath(resolve = resolveRtkPathFromSystem) {
|
|
17
|
+
if (!cachedRtkPath) cachedRtkPath = resolve();
|
|
18
|
+
return cachedRtkPath;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { getRtkPath as t };
|