glimmervoid 0.30.0 → 0.32.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/README.md +1 -1
- package/dist/bin/glimmervoid.js +144 -57
- package/dist/chunks/{adapters-Dho1VSY5.js → adapters-BU74M4QZ.js} +1 -1
- package/dist/chunks/{adapters-zPDx1Xox.js → adapters-CoC18rdt.js} +56 -31
- package/dist/chunks/agent-api-cli-V_BkYhBI.js +142 -0
- package/dist/chunks/agent-api-core-B3YI8SYs.js +192 -0
- package/dist/chunks/{agent-setup-cli-B5Atxr3F.js → agent-setup-cli-D4956BfN.js} +11 -28
- package/dist/chunks/backoff-DjVpfHk7.js +14 -0
- package/dist/chunks/child-process-safe-B3zcPdFM.js +2 -0
- package/dist/chunks/{child-process-safe-DRGsp0TE.js → child-process-safe-DDELk9XC.js} +3 -2
- package/dist/chunks/coerce-NSst8oRP.js +32 -0
- package/dist/chunks/{contracts-DGkKNh9W.js → config-CZ4H51D0.js} +374 -1684
- package/dist/chunks/{config-store-mm7MgzoL.js → config-store-BVtdHkDK.js} +594 -329
- package/dist/chunks/config-store-Cp62jN1O.js +2 -0
- package/dist/chunks/contracts-DXujp-Zn.js +2131 -0
- package/dist/chunks/git-version-CrLW4a6r.js +58 -0
- package/dist/chunks/{grok-DuU8xVLZ.js → grok-C4lzPFEg.js} +24 -21
- package/dist/chunks/{hook-relay-core-BHfYFnYV.js → hook-relay-core-DA_bz2KA.js} +10 -5
- package/dist/chunks/{hook-tools-pSJia7ks.js → hook-tools-UX2fmt81.js} +3 -5
- package/dist/chunks/{ingest-editor-core-BuI1gMF_.js → ingest-editor-core-vR6Wv6bP.js} +24 -13
- package/dist/chunks/json-core-dfa_7Fak.js +23 -0
- package/dist/chunks/json-file-oEH9vt-0.js +352 -0
- package/dist/chunks/knowledge-graph-cli-Bjy9_kv5.js +1354 -0
- package/dist/chunks/{loopback-post-C7C22eYi.js → loopback-post-DepQfdcM.js} +40 -26
- package/dist/chunks/{main-CuIZQPhZ.js → main-DSbpYn2Y.js} +27880 -19139
- package/dist/chunks/{node-pty-preflight-CK1knGIR.js → node-pty-preflight-C8QIlrtU.js} +4 -6
- package/dist/chunks/{pair-cli-w-gCAF-1.js → pair-cli-CKn-wUw9.js} +4 -3
- package/dist/chunks/paths-CeJl2Vcg.js +55 -0
- package/dist/chunks/{remote-config-C2ytQwrs.js → remote-config-DP44Ybt9.js} +37 -41
- package/dist/chunks/{rtk-hook-core-Bl6gYHPN.js → rtk-hook-core-BmEz2aq9.js} +3 -5
- package/dist/chunks/rtk-resolver-CIVLE_xk.js +2 -0
- package/dist/chunks/{rtk-resolver-CLo-gZzw.js → rtk-resolver-jJxy_Mrt.js} +2 -2
- package/dist/chunks/{runtime-paths-Ci_ksSkj.js → runtime-paths-BPoxENmK.js} +2 -6
- package/dist/chunks/runtime-paths-DU5jBEEO.js +2 -0
- package/dist/chunks/{sandbox-deps-Dd5-kZnf.js → sandbox-deps-DuaNFq7n.js} +16 -13
- package/dist/chunks/sandbox-proxy-core-hnQgKR0T.js +27 -0
- package/dist/chunks/{session-Cez8aIWg.js → session-B8fJb_Or.js} +25 -2
- package/dist/chunks/text-DoirglWV.js +21 -0
- package/dist/chunks/timer-deps-u2DAQLok.js +15 -0
- package/dist/chunks/{visions-cli-BXQFIBCX.js → visions-cli-BhFdym5n.js} +1 -1
- package/dist/chunks/{visions-setup-CvVnOe1r.js → visions-setup-CfgNHiG3.js} +20 -357
- package/dist/client/assets/index-C_XKsDFx.css +1 -0
- package/dist/client/assets/index-CoqWjYxP.js +254 -0
- package/dist/client/fonts/CommitMono-OFL.txt +92 -0
- package/dist/client/fonts/commit-mono-400-italic.woff2 +0 -0
- package/dist/client/fonts/commit-mono-400-regular.woff2 +0 -0
- package/dist/client/fonts/commit-mono-700-italic.woff2 +0 -0
- package/dist/client/fonts/commit-mono-700-regular.woff2 +0 -0
- package/dist/client/index.html +7 -24
- package/dist/client/octicons-LICENSE.txt +21 -0
- package/dist/scripts/postinstall-path-check.js +1 -1
- package/dist/server/index.js +3 -3
- package/dist/session/command-hook-relay.js +1 -1
- package/dist/session/hook-relay.js +3 -3
- package/dist/session/hook-tool-relay.js +2 -2
- package/dist/session/sandbox-open-launcher.js +79 -0
- package/dist/session/statusline-relay.js +8 -18
- package/dist/session/visions-relay.js +20 -11
- package/package.json +4 -1
- package/dist/chunks/agent-api-cli-D0MmtEdz.js +0 -71
- package/dist/chunks/agent-api-core-Dz0r21y9.js +0 -103
- package/dist/chunks/child-process-safe-DvQjXsgz.js +0 -2
- package/dist/chunks/config-store-CC8T3okC.js +0 -2
- package/dist/chunks/ingest-core-DWI1Mbu4.js +0 -342
- package/dist/chunks/rtk-resolver-CMQAidlI.js +0 -2
- package/dist/chunks/runtime-paths-JLrZ3wyi.js +0 -2
- package/dist/client/assets/index-B1MFijjb.css +0 -1
- package/dist/client/assets/index-CU1iPPdn.js +0 -249
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { a as AgentSpawnRequest, i as AgentAttentionRequest, t as AGENT_API_VERBS } from "./session-Cez8aIWg.js";
|
|
2
|
-
import crypto from "node:crypto";
|
|
3
|
-
var MAX_SPAWNS_IN_FLIGHT = 1;
|
|
4
|
-
var SPAWNABLE_DEPTH = 0;
|
|
5
|
-
var REFUSAL_REASON = "unknown session";
|
|
6
|
-
var INVALID_BODY_STATUS = 400;
|
|
7
|
-
function tokensMatch(presented, expected) {
|
|
8
|
-
if (presented.length !== expected.length) return false;
|
|
9
|
-
try {
|
|
10
|
-
return crypto.timingSafeEqual(Buffer.from(presented, "utf8"), Buffer.from(expected, "utf8"));
|
|
11
|
-
} catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
function refused() {
|
|
16
|
-
return {
|
|
17
|
-
ok: false,
|
|
18
|
-
status: 404,
|
|
19
|
-
reason: REFUSAL_REASON
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function decideAgentRequest({ sessionId, presentedToken, expectedToken, isLoopback, enabled }) {
|
|
23
|
-
if (!enabled) return refused();
|
|
24
|
-
if (!isLoopback) return refused();
|
|
25
|
-
if (!sessionId) return refused();
|
|
26
|
-
if (!expectedToken) return refused();
|
|
27
|
-
if (!presentedToken) return refused();
|
|
28
|
-
if (!tokensMatch(presentedToken, expectedToken)) return refused();
|
|
29
|
-
return { ok: true };
|
|
30
|
-
}
|
|
31
|
-
function decideSpawnAllowance({ liveChildren, lifetimeSpawns, inFlight, depth }) {
|
|
32
|
-
if (depth !== SPAWNABLE_DEPTH) return {
|
|
33
|
-
ok: false,
|
|
34
|
-
status: 403,
|
|
35
|
-
reason: "a spawned session may not spawn another"
|
|
36
|
-
};
|
|
37
|
-
if (inFlight >= MAX_SPAWNS_IN_FLIGHT) return {
|
|
38
|
-
ok: false,
|
|
39
|
-
status: 429,
|
|
40
|
-
reason: "a spawn is already in flight"
|
|
41
|
-
};
|
|
42
|
-
if (liveChildren >= 3) return {
|
|
43
|
-
ok: false,
|
|
44
|
-
status: 429,
|
|
45
|
-
reason: `at most 3 spawned sessions may be live at once`
|
|
46
|
-
};
|
|
47
|
-
if (lifetimeSpawns >= 10) return {
|
|
48
|
-
ok: false,
|
|
49
|
-
status: 429,
|
|
50
|
-
reason: `this session has used its 10 spawns`
|
|
51
|
-
};
|
|
52
|
-
return { ok: true };
|
|
53
|
-
}
|
|
54
|
-
function isAgentApiVerb(verb) {
|
|
55
|
-
return AGENT_API_VERBS.includes(verb);
|
|
56
|
-
}
|
|
57
|
-
function refuseInvalidBody(issues) {
|
|
58
|
-
return {
|
|
59
|
-
ok: false,
|
|
60
|
-
status: INVALID_BODY_STATUS,
|
|
61
|
-
error: issues[0]?.message || "invalid request"
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
function parseAgentVerb(verb, payload) {
|
|
65
|
-
if (!isAgentApiVerb(verb)) return {
|
|
66
|
-
ok: false,
|
|
67
|
-
status: 404,
|
|
68
|
-
error: REFUSAL_REASON
|
|
69
|
-
};
|
|
70
|
-
if (verb === "board") return {
|
|
71
|
-
ok: true,
|
|
72
|
-
verb
|
|
73
|
-
};
|
|
74
|
-
if (verb === "attention") {
|
|
75
|
-
const parsed = AgentAttentionRequest.safeParse(payload);
|
|
76
|
-
if (!parsed.success) return refuseInvalidBody(parsed.error.issues);
|
|
77
|
-
return {
|
|
78
|
-
ok: true,
|
|
79
|
-
verb,
|
|
80
|
-
request: parsed.data
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
const parsed = AgentSpawnRequest.safeParse(payload);
|
|
84
|
-
if (!parsed.success) return refuseInvalidBody(parsed.error.issues);
|
|
85
|
-
return {
|
|
86
|
-
ok: true,
|
|
87
|
-
verb,
|
|
88
|
-
request: parsed.data
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
function deriveChildSessionName(parentName, existingNames) {
|
|
92
|
-
const base = `${String(parentName || "").trim() || "session"} agent`;
|
|
93
|
-
const taken = new Set(existingNames);
|
|
94
|
-
let candidate = base;
|
|
95
|
-
let suffix = 1;
|
|
96
|
-
while (taken.has(candidate)) {
|
|
97
|
-
suffix += 1;
|
|
98
|
-
candidate = `${base} ${suffix}`;
|
|
99
|
-
}
|
|
100
|
-
return candidate;
|
|
101
|
-
}
|
|
102
|
-
//#endregion
|
|
103
|
-
export { isAgentApiVerb as a, deriveChildSessionName as i, decideAgentRequest as n, parseAgentVerb as o, decideSpawnAllowance as r, REFUSAL_REASON as t };
|
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
//#region server/core/ingest-number-core.ts
|
|
2
|
-
function positiveInt(value, fallback) {
|
|
3
|
-
const number = Number(value);
|
|
4
|
-
if (!Number.isFinite(number) || number <= 0) return fallback;
|
|
5
|
-
return Math.floor(number);
|
|
6
|
-
}
|
|
7
|
-
//#endregion
|
|
8
|
-
//#region server/core/ingest-core.ts
|
|
9
|
-
var SOURCE_NAMES = [
|
|
10
|
-
"terminal",
|
|
11
|
-
"agentLogs",
|
|
12
|
-
"git",
|
|
13
|
-
"fs",
|
|
14
|
-
"shellHistory",
|
|
15
|
-
"editor"
|
|
16
|
-
];
|
|
17
|
-
var SOURCE_DEFAULTS = Object.freeze({
|
|
18
|
-
terminal: Object.freeze({
|
|
19
|
-
maxEntries: 200,
|
|
20
|
-
maxBytes: 262144,
|
|
21
|
-
digestQuota: 8,
|
|
22
|
-
flushMs: 500,
|
|
23
|
-
accumulatorBytes: 8192,
|
|
24
|
-
windowBytes: 65536
|
|
25
|
-
}),
|
|
26
|
-
agentLogs: Object.freeze({
|
|
27
|
-
maxEntries: 200,
|
|
28
|
-
maxBytes: 131072,
|
|
29
|
-
digestQuota: 8,
|
|
30
|
-
pollMs: 2e3
|
|
31
|
-
}),
|
|
32
|
-
git: Object.freeze({
|
|
33
|
-
maxEntries: 100,
|
|
34
|
-
maxBytes: 65536,
|
|
35
|
-
digestQuota: 6,
|
|
36
|
-
debounceMs: 1e3,
|
|
37
|
-
pollMs: 6e4
|
|
38
|
-
}),
|
|
39
|
-
fs: Object.freeze({
|
|
40
|
-
maxEntries: 500,
|
|
41
|
-
maxBytes: 131072,
|
|
42
|
-
digestQuota: 8,
|
|
43
|
-
batchMs: 500
|
|
44
|
-
}),
|
|
45
|
-
shellHistory: Object.freeze({
|
|
46
|
-
maxEntries: 100,
|
|
47
|
-
maxBytes: 32768,
|
|
48
|
-
digestQuota: 6,
|
|
49
|
-
pollMs: 2e3
|
|
50
|
-
}),
|
|
51
|
-
editor: Object.freeze({
|
|
52
|
-
maxEntries: 100,
|
|
53
|
-
maxBytes: 32768,
|
|
54
|
-
digestQuota: 6
|
|
55
|
-
})
|
|
56
|
-
});
|
|
57
|
-
var KINDS_BY_SOURCE = Object.freeze({
|
|
58
|
-
terminal: Object.freeze(["output"]),
|
|
59
|
-
agentLogs: Object.freeze(["agent-turn", "agent-tool"]),
|
|
60
|
-
git: Object.freeze([
|
|
61
|
-
"commit",
|
|
62
|
-
"status-change",
|
|
63
|
-
"branch-change"
|
|
64
|
-
]),
|
|
65
|
-
fs: Object.freeze(["file-change"]),
|
|
66
|
-
shellHistory: Object.freeze(["command"]),
|
|
67
|
-
editor: Object.freeze([
|
|
68
|
-
"doc-save",
|
|
69
|
-
"doc-open",
|
|
70
|
-
"doc-close"
|
|
71
|
-
])
|
|
72
|
-
});
|
|
73
|
-
var MAX_DETAIL_CHARS = 1e3;
|
|
74
|
-
var MAX_DETAIL_KEYS = 12;
|
|
75
|
-
var DEFAULT_DIGEST_BUDGET_CHARS = 2e3;
|
|
76
|
-
var SCRUB_PLACEHOLDER = "[scrubbed]";
|
|
77
|
-
var DIGEST_HEADER = "Recent activity on this machine, newest first:";
|
|
78
|
-
var LIST_KEYS = Object.freeze({
|
|
79
|
-
fs: "roots",
|
|
80
|
-
shellHistory: "shells"
|
|
81
|
-
});
|
|
82
|
-
function stringList(raw) {
|
|
83
|
-
if (!Array.isArray(raw)) return [];
|
|
84
|
-
const values = [];
|
|
85
|
-
for (const entry of raw) {
|
|
86
|
-
if (typeof entry !== "string" || !entry.trim() || values.includes(entry.trim())) continue;
|
|
87
|
-
values.push(entry.trim());
|
|
88
|
-
}
|
|
89
|
-
return values;
|
|
90
|
-
}
|
|
91
|
-
function disabledSource(name) {
|
|
92
|
-
const source = {
|
|
93
|
-
enabled: false,
|
|
94
|
-
...SOURCE_DEFAULTS[name]
|
|
95
|
-
};
|
|
96
|
-
const listKey = LIST_KEYS[name];
|
|
97
|
-
if (listKey) source[listKey] = [];
|
|
98
|
-
return source;
|
|
99
|
-
}
|
|
100
|
-
function disabledConfig() {
|
|
101
|
-
const sources = {};
|
|
102
|
-
for (const name of SOURCE_NAMES) sources[name] = disabledSource(name);
|
|
103
|
-
return {
|
|
104
|
-
enabled: false,
|
|
105
|
-
sources
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
function resolveSource(name, raw) {
|
|
109
|
-
const defaults = SOURCE_DEFAULTS[name];
|
|
110
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return disabledSource(name);
|
|
111
|
-
const rawSource = raw;
|
|
112
|
-
const resolved = { enabled: rawSource.enabled === true };
|
|
113
|
-
for (const [key, value] of Object.entries(defaults)) resolved[key] = positiveInt(rawSource[key], value);
|
|
114
|
-
const listKey = LIST_KEYS[name];
|
|
115
|
-
if (listKey) resolved[listKey] = stringList(rawSource[listKey]);
|
|
116
|
-
return resolved;
|
|
117
|
-
}
|
|
118
|
-
function resolveIngestConfig(raw) {
|
|
119
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return disabledConfig();
|
|
120
|
-
const rawConfig = raw;
|
|
121
|
-
if (rawConfig.enabled !== true) return disabledConfig();
|
|
122
|
-
const rawSourcesValue = rawConfig.sources;
|
|
123
|
-
const rawSources = rawSourcesValue && typeof rawSourcesValue === "object" && !Array.isArray(rawSourcesValue) ? rawSourcesValue : {};
|
|
124
|
-
const sources = {};
|
|
125
|
-
for (const name of SOURCE_NAMES) sources[name] = resolveSource(name, rawSources[name]);
|
|
126
|
-
return {
|
|
127
|
-
enabled: true,
|
|
128
|
-
sources
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function enabledSourceNames(config) {
|
|
132
|
-
if (!config || config.enabled !== true) return [];
|
|
133
|
-
return SOURCE_NAMES.filter((name) => config.sources[name] && config.sources[name].enabled === true);
|
|
134
|
-
}
|
|
135
|
-
var SECRET_WORD = "(?:password|passwd|pwd|token|api[_-]?key|secret|credential|auth[_-]?token|access[_-]?key|secret[_-]?key|private[_-]?key|client[_-]?secret|connection[_-]?string)";
|
|
136
|
-
var QUOTED_OR_BARE = "(?:\"[^\"\\r\\n]*\"|'[^'\\r\\n]*'|[^\\s\\r\\n]+)";
|
|
137
|
-
var GAP = "[^\\S\\r\\n]";
|
|
138
|
-
var FLAG_REACH = "[^\\r\\n;&|]{0,200}?";
|
|
139
|
-
var SCRUB_PATTERNS = [
|
|
140
|
-
new RegExp(`(${SECRET_WORD}${GAP}*(?:=>|[:=])${GAP}*)${QUOTED_OR_BARE}`, "gi"),
|
|
141
|
-
new RegExp(`((?:^|\\s)--?${SECRET_WORD}${GAP}+)${QUOTED_OR_BARE}`, "gi"),
|
|
142
|
-
new RegExp(`((?:^|\\s)-(?:pwd|pw)(?![a-z])${GAP}*=?${GAP}*)${QUOTED_OR_BARE}`, "g"),
|
|
143
|
-
new RegExp(`(-AsPlainText${GAP}+)${QUOTED_OR_BARE}`, "gi"),
|
|
144
|
-
new RegExp(`((?:^|\\s)setx${GAP}+[^\\s\\r\\n]*${SECRET_WORD}[^\\s\\r\\n]*${GAP}+)${QUOTED_OR_BARE}`, "gi"),
|
|
145
|
-
new RegExp(`(\\bBearer${GAP}+)[A-Za-z0-9._~+/=-]{8,}`, "gi"),
|
|
146
|
-
new RegExp(`(\\bauthorization${GAP}*:${GAP}*Basic${GAP}+)[A-Za-z0-9+/=_-]{8,}`, "gi"),
|
|
147
|
-
new RegExp(`(\\bdocker${GAP}+login\\b${FLAG_REACH}${GAP}-p(?:${GAP}+|=))${QUOTED_OR_BARE}`, "gi"),
|
|
148
|
-
new RegExp(`(\\bcurl\\b${FLAG_REACH}${GAP}(?:--user|-u)(?:${GAP}+|=)[^\\s:\\r\\n]+:)${QUOTED_OR_BARE}`, "gi"),
|
|
149
|
-
new RegExp(`(\\b(?:mysql|mysqldump|mysqladmin|mysqlshow|mariadb|mariadb-dump)\\b${FLAG_REACH}${GAP}-p)${QUOTED_OR_BARE}`, "g"),
|
|
150
|
-
/\b([a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:)[^\s@/]+(?=@)/gi
|
|
151
|
-
];
|
|
152
|
-
var SECRET_SHAPE_PATTERNS = [
|
|
153
|
-
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
154
|
-
/\bgh[pousr]_[A-Za-z0-9]{16,}\b/g,
|
|
155
|
-
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
156
|
-
/\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g,
|
|
157
|
-
/\beyJ[A-Za-z0-9_=-]{6,}\.[A-Za-z0-9_=-]{6,}\.[A-Za-z0-9_=-]{4,}/g,
|
|
158
|
-
/-----BEGIN(?:[A-Z0-9 ]+)? PRIVATE KEY-----/g
|
|
159
|
-
];
|
|
160
|
-
function scrubText(text) {
|
|
161
|
-
if (typeof text !== "string" || !text) return typeof text === "string" ? text : "";
|
|
162
|
-
let scrubbed = text;
|
|
163
|
-
for (const pattern of SCRUB_PATTERNS) {
|
|
164
|
-
pattern.lastIndex = 0;
|
|
165
|
-
scrubbed = scrubbed.replace(pattern, (_match, prefix) => `${prefix}${SCRUB_PLACEHOLDER}`);
|
|
166
|
-
}
|
|
167
|
-
for (const pattern of SECRET_SHAPE_PATTERNS) {
|
|
168
|
-
pattern.lastIndex = 0;
|
|
169
|
-
scrubbed = scrubbed.replace(pattern, SCRUB_PLACEHOLDER);
|
|
170
|
-
}
|
|
171
|
-
return scrubbed;
|
|
172
|
-
}
|
|
173
|
-
function scrubDetail(detail) {
|
|
174
|
-
if (!detail || typeof detail !== "object" || Array.isArray(detail)) return null;
|
|
175
|
-
const scrubbed = {};
|
|
176
|
-
let keys = 0;
|
|
177
|
-
for (const [key, value] of Object.entries(detail)) {
|
|
178
|
-
if (keys >= MAX_DETAIL_KEYS) break;
|
|
179
|
-
keys += 1;
|
|
180
|
-
if (typeof value === "string") {
|
|
181
|
-
scrubbed[key] = scrubText(value).slice(0, MAX_DETAIL_CHARS);
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (typeof value === "number" && Number.isFinite(value)) scrubbed[key] = value;
|
|
185
|
-
if (typeof value === "boolean") scrubbed[key] = value;
|
|
186
|
-
}
|
|
187
|
-
return scrubbed;
|
|
188
|
-
}
|
|
189
|
-
function nonEmptyString(value) {
|
|
190
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
191
|
-
}
|
|
192
|
-
function normalizeScope(raw) {
|
|
193
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {
|
|
194
|
-
root: null,
|
|
195
|
-
sessionId: null
|
|
196
|
-
};
|
|
197
|
-
const rawScope = raw;
|
|
198
|
-
return {
|
|
199
|
-
root: nonEmptyString(rawScope.root),
|
|
200
|
-
sessionId: nonEmptyString(rawScope.sessionId)
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
function normalizeEvent(raw, { seq, now }) {
|
|
204
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
205
|
-
const rawEvent = raw;
|
|
206
|
-
const source = nonEmptyString(rawEvent.source);
|
|
207
|
-
if (!source || !KINDS_BY_SOURCE[source]) return null;
|
|
208
|
-
const kind = nonEmptyString(rawEvent.kind);
|
|
209
|
-
if (!kind || !KINDS_BY_SOURCE[source].includes(kind)) return null;
|
|
210
|
-
const summary = scrubText(typeof rawEvent.summary === "string" ? rawEvent.summary : "").replace(/\s+/g, " ").trim().slice(0, 400);
|
|
211
|
-
if (!summary) return null;
|
|
212
|
-
const ts = Number(rawEvent.ts);
|
|
213
|
-
return {
|
|
214
|
-
source,
|
|
215
|
-
kind,
|
|
216
|
-
ts: Number.isFinite(ts) && ts > 0 ? Math.floor(ts) : now,
|
|
217
|
-
seq,
|
|
218
|
-
scope: normalizeScope(rawEvent.scope),
|
|
219
|
-
summary,
|
|
220
|
-
detail: scrubDetail(rawEvent.detail)
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
function eventBytes(event) {
|
|
224
|
-
return Buffer.byteLength(JSON.stringify(event), "utf8");
|
|
225
|
-
}
|
|
226
|
-
function createRing({ maxEntries, maxBytes }) {
|
|
227
|
-
return {
|
|
228
|
-
entries: [],
|
|
229
|
-
totalBytes: 0,
|
|
230
|
-
maxEntries,
|
|
231
|
-
maxBytes
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
function pushToRing(ring, event) {
|
|
235
|
-
const bytes = eventBytes(event);
|
|
236
|
-
ring.entries.push({
|
|
237
|
-
event,
|
|
238
|
-
bytes
|
|
239
|
-
});
|
|
240
|
-
ring.totalBytes += bytes;
|
|
241
|
-
while (ring.entries.length > 1 && (ring.entries.length > ring.maxEntries || ring.totalBytes > ring.maxBytes)) {
|
|
242
|
-
const evicted = ring.entries.shift();
|
|
243
|
-
ring.totalBytes -= evicted ? evicted.bytes : 0;
|
|
244
|
-
}
|
|
245
|
-
return event;
|
|
246
|
-
}
|
|
247
|
-
function createIngestStore(config) {
|
|
248
|
-
const resolved = config?.sources ? config : disabledConfig();
|
|
249
|
-
const rings = /* @__PURE__ */ new Map();
|
|
250
|
-
for (const name of enabledSourceNames(resolved)) rings.set(name, createRing(resolved.sources[name]));
|
|
251
|
-
return {
|
|
252
|
-
config: resolved,
|
|
253
|
-
rings,
|
|
254
|
-
seq: 0
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
function publishEvent(store, raw, now) {
|
|
258
|
-
const source = nonEmptyString((raw ?? null)?.source);
|
|
259
|
-
if (!source) return null;
|
|
260
|
-
const ring = store.rings.get(source);
|
|
261
|
-
if (!ring) return null;
|
|
262
|
-
const event = normalizeEvent(raw, {
|
|
263
|
-
seq: store.seq + 1,
|
|
264
|
-
now
|
|
265
|
-
});
|
|
266
|
-
if (!event) return null;
|
|
267
|
-
store.seq += 1;
|
|
268
|
-
return pushToRing(ring, event);
|
|
269
|
-
}
|
|
270
|
-
function latestSeq(store) {
|
|
271
|
-
const seq = Number(store?.seq);
|
|
272
|
-
if (!Number.isFinite(seq)) return 0;
|
|
273
|
-
return seq;
|
|
274
|
-
}
|
|
275
|
-
function snapshotEvents(store, { limit = 200 } = {}) {
|
|
276
|
-
const all = [];
|
|
277
|
-
for (const ring of store.rings.values()) for (const entry of ring.entries) all.push(entry.event);
|
|
278
|
-
all.sort((left, right) => right.seq - left.seq);
|
|
279
|
-
return all.slice(0, Math.max(0, Math.floor(limit)));
|
|
280
|
-
}
|
|
281
|
-
function ringStats(store) {
|
|
282
|
-
const stats = [];
|
|
283
|
-
for (const [source, ring] of store.rings) stats.push({
|
|
284
|
-
source,
|
|
285
|
-
events: ring.entries.length,
|
|
286
|
-
bytes: ring.totalBytes
|
|
287
|
-
});
|
|
288
|
-
return stats;
|
|
289
|
-
}
|
|
290
|
-
var SOURCE_LABELS = Object.freeze({
|
|
291
|
-
terminal: "terminal",
|
|
292
|
-
agentLogs: "agent",
|
|
293
|
-
git: "git",
|
|
294
|
-
fs: "files",
|
|
295
|
-
shellHistory: "shell",
|
|
296
|
-
editor: "editor"
|
|
297
|
-
});
|
|
298
|
-
function ageText(ts, now) {
|
|
299
|
-
const seconds = Math.max(0, Math.floor((now - ts) / 1e3));
|
|
300
|
-
if (seconds < 60) return `${seconds}s ago`;
|
|
301
|
-
const minutes = Math.floor(seconds / 60);
|
|
302
|
-
if (minutes < 60) return `${minutes}m ago`;
|
|
303
|
-
const hours = Math.floor(minutes / 60);
|
|
304
|
-
if (hours < 24) return `${hours}h ago`;
|
|
305
|
-
return `${Math.floor(hours / 24)}d ago`;
|
|
306
|
-
}
|
|
307
|
-
function digestLine(event, now) {
|
|
308
|
-
const label = SOURCE_LABELS[event.source] || event.source;
|
|
309
|
-
const scope = event.scope.root ? "" : " (machine scope)";
|
|
310
|
-
return `- ${label} ${ageText(event.ts, now)}${scope}: ${event.summary}`;
|
|
311
|
-
}
|
|
312
|
-
function matchesScopes(event, scopes) {
|
|
313
|
-
if (!Array.isArray(scopes) || scopes.length === 0) return true;
|
|
314
|
-
if (!event.scope.root) return true;
|
|
315
|
-
return scopes.includes(event.scope.root);
|
|
316
|
-
}
|
|
317
|
-
function buildContextDigest(store, { scopes = null, budgetChars = DEFAULT_DIGEST_BUDGET_CHARS, now = NaN } = {}) {
|
|
318
|
-
const budget = positiveInt(budgetChars, DEFAULT_DIGEST_BUDGET_CHARS);
|
|
319
|
-
const candidates = [];
|
|
320
|
-
const takenBySource = /* @__PURE__ */ new Map();
|
|
321
|
-
for (const event of snapshotEvents(store, { limit: Number.MAX_SAFE_INTEGER })) {
|
|
322
|
-
if (!matchesScopes(event, scopes)) continue;
|
|
323
|
-
const quota = store.config.sources[event.source].digestQuota;
|
|
324
|
-
const taken = takenBySource.get(event.source) || 0;
|
|
325
|
-
if (taken >= quota) continue;
|
|
326
|
-
takenBySource.set(event.source, taken + 1);
|
|
327
|
-
candidates.push(event);
|
|
328
|
-
}
|
|
329
|
-
if (candidates.length === 0) return "";
|
|
330
|
-
const lines = [DIGEST_HEADER];
|
|
331
|
-
let used = 46;
|
|
332
|
-
for (const event of candidates) {
|
|
333
|
-
const line = scrubText(digestLine(event, now));
|
|
334
|
-
if (used + line.length + 1 > budget) break;
|
|
335
|
-
lines.push(line);
|
|
336
|
-
used += line.length + 1;
|
|
337
|
-
}
|
|
338
|
-
if (lines.length === 1) return "";
|
|
339
|
-
return lines.join("\n");
|
|
340
|
-
}
|
|
341
|
-
//#endregion
|
|
342
|
-
export { createIngestStore as a, publishEvent as c, scrubText as d, snapshotEvents as f, buildContextDigest as i, resolveIngestConfig as l, SOURCE_DEFAULTS as n, enabledSourceNames as o, positiveInt as p, SOURCE_NAMES as r, latestSeq as s, DEFAULT_DIGEST_BUDGET_CHARS as t, ringStats as u };
|