@unerr-ai/unerr 0.1.7 → 0.1.8
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 +9 -2
- package/dist/cli.js +1353 -279
- package/package.json +9 -2
package/dist/cli.js
CHANGED
|
@@ -245,16 +245,16 @@ async function getParser(language) {
|
|
|
245
245
|
const parser = new TreeSitter();
|
|
246
246
|
const langFile = `tree-sitter-${language}.wasm`;
|
|
247
247
|
try {
|
|
248
|
-
const { join:
|
|
248
|
+
const { join: join85 } = await import("path");
|
|
249
249
|
const { existsSync: existsSync82 } = await import("fs");
|
|
250
250
|
const possiblePaths = [
|
|
251
|
-
|
|
251
|
+
join85(
|
|
252
252
|
process.cwd(),
|
|
253
253
|
"node_modules",
|
|
254
254
|
`tree-sitter-${language}`,
|
|
255
255
|
langFile
|
|
256
256
|
),
|
|
257
|
-
|
|
257
|
+
join85(process.cwd(), "node_modules", "web-tree-sitter", langFile)
|
|
258
258
|
];
|
|
259
259
|
let wasmPath = null;
|
|
260
260
|
for (const p of possiblePaths) {
|
|
@@ -5770,6 +5770,7 @@ var init_startup_log = __esm({
|
|
|
5770
5770
|
// src/config/settings.ts
|
|
5771
5771
|
var settings_exports = {};
|
|
5772
5772
|
__export(settings_exports, {
|
|
5773
|
+
FetchUrlConfigSchema: () => FetchUrlConfigSchema,
|
|
5773
5774
|
LocalLlmConfigSchema: () => LocalLlmConfigSchema,
|
|
5774
5775
|
SettingsSchema: () => SettingsSchema,
|
|
5775
5776
|
loadSettings: () => loadSettings,
|
|
@@ -5852,7 +5853,7 @@ function loadSettings(cwd) {
|
|
|
5852
5853
|
};
|
|
5853
5854
|
return SettingsSchema.parse(merged);
|
|
5854
5855
|
}
|
|
5855
|
-
var LlmProviderEnum, EndpointConfigSchema, LocalLlmConfigSchema, LlmConfigSchema, SettingsSchema, DEFAULTS;
|
|
5856
|
+
var LlmProviderEnum, EndpointConfigSchema, LocalLlmConfigSchema, LlmConfigSchema, FetchUrlConfigSchema, SettingsSchema, DEFAULTS;
|
|
5856
5857
|
var init_settings = __esm({
|
|
5857
5858
|
"src/config/settings.ts"() {
|
|
5858
5859
|
"use strict";
|
|
@@ -5885,6 +5886,17 @@ var init_settings = __esm({
|
|
|
5885
5886
|
apiKey: z.string().optional(),
|
|
5886
5887
|
baseUrl: z.string().optional()
|
|
5887
5888
|
});
|
|
5889
|
+
FetchUrlConfigSchema = z.object({
|
|
5890
|
+
playwright: z.object({
|
|
5891
|
+
enabled: z.boolean().default(false),
|
|
5892
|
+
timeoutMs: z.number().int().min(1e3).max(6e4).default(15e3),
|
|
5893
|
+
waitUntil: z.enum(["load", "domcontentloaded", "networkidle"]).default("networkidle")
|
|
5894
|
+
}).default(() => ({
|
|
5895
|
+
enabled: false,
|
|
5896
|
+
timeoutMs: 15e3,
|
|
5897
|
+
waitUntil: "networkidle"
|
|
5898
|
+
}))
|
|
5899
|
+
});
|
|
5888
5900
|
SettingsSchema = z.object({
|
|
5889
5901
|
/** Default Claude model for interactive sessions */
|
|
5890
5902
|
model: z.string().default("claude-sonnet-4-20250514"),
|
|
@@ -5899,7 +5911,15 @@ var init_settings = __esm({
|
|
|
5899
5911
|
/** AI SDK LLM configuration (Sprint D — unified multi-provider) */
|
|
5900
5912
|
llm: LlmConfigSchema.optional(),
|
|
5901
5913
|
/** BYO-LLM configuration for local LLM providers */
|
|
5902
|
-
localLlm: LocalLlmConfigSchema.optional()
|
|
5914
|
+
localLlm: LocalLlmConfigSchema.optional(),
|
|
5915
|
+
/** fetch_url runtime config (Playwright SPA fallback, etc.) */
|
|
5916
|
+
fetchUrl: FetchUrlConfigSchema.default(() => ({
|
|
5917
|
+
playwright: {
|
|
5918
|
+
enabled: false,
|
|
5919
|
+
timeoutMs: 15e3,
|
|
5920
|
+
waitUntil: "networkidle"
|
|
5921
|
+
}
|
|
5922
|
+
}))
|
|
5903
5923
|
});
|
|
5904
5924
|
DEFAULTS = SettingsSchema.parse({});
|
|
5905
5925
|
}
|
|
@@ -6551,6 +6571,19 @@ CREATE TABLE IF NOT EXISTS meta (
|
|
|
6551
6571
|
key TEXT PRIMARY KEY,
|
|
6552
6572
|
value TEXT NOT NULL
|
|
6553
6573
|
);
|
|
6574
|
+
|
|
6575
|
+
CREATE TABLE IF NOT EXISTS fetch_cache (
|
|
6576
|
+
url TEXT PRIMARY KEY,
|
|
6577
|
+
content_hash TEXT NOT NULL,
|
|
6578
|
+
markdown TEXT NOT NULL,
|
|
6579
|
+
title TEXT NOT NULL,
|
|
6580
|
+
extractor TEXT NOT NULL,
|
|
6581
|
+
raw_bytes INTEGER NOT NULL,
|
|
6582
|
+
compressed_bytes INTEGER NOT NULL,
|
|
6583
|
+
fetched_at INTEGER NOT NULL,
|
|
6584
|
+
hit_count INTEGER NOT NULL DEFAULT 0
|
|
6585
|
+
);
|
|
6586
|
+
CREATE INDEX IF NOT EXISTS idx_fetch_cache_fetched ON fetch_cache(fetched_at);
|
|
6554
6587
|
`;
|
|
6555
6588
|
SCHEMA_VERSION = "1";
|
|
6556
6589
|
MetricsStore = class {
|
|
@@ -6660,9 +6693,39 @@ CREATE TABLE IF NOT EXISTS meta (
|
|
|
6660
6693
|
`),
|
|
6661
6694
|
allSessionSummaries: this.db.prepare(`
|
|
6662
6695
|
SELECT * FROM session_summaries ORDER BY ended_at DESC
|
|
6696
|
+
`),
|
|
6697
|
+
upsertFetchCache: this.db.prepare(`
|
|
6698
|
+
INSERT INTO fetch_cache
|
|
6699
|
+
(url, content_hash, markdown, title, extractor,
|
|
6700
|
+
raw_bytes, compressed_bytes, fetched_at, hit_count)
|
|
6701
|
+
VALUES (@url, @content_hash, @markdown, @title, @extractor,
|
|
6702
|
+
@raw_bytes, @compressed_bytes, @fetched_at, 0)
|
|
6703
|
+
ON CONFLICT(url) DO UPDATE SET
|
|
6704
|
+
content_hash = excluded.content_hash,
|
|
6705
|
+
markdown = excluded.markdown,
|
|
6706
|
+
title = excluded.title,
|
|
6707
|
+
extractor = excluded.extractor,
|
|
6708
|
+
raw_bytes = excluded.raw_bytes,
|
|
6709
|
+
compressed_bytes = excluded.compressed_bytes,
|
|
6710
|
+
fetched_at = excluded.fetched_at
|
|
6711
|
+
`),
|
|
6712
|
+
getFetchCache: this.db.prepare(`
|
|
6713
|
+
SELECT * FROM fetch_cache WHERE url = @url
|
|
6714
|
+
`),
|
|
6715
|
+
bumpFetchCacheHit: this.db.prepare(`
|
|
6716
|
+
UPDATE fetch_cache SET hit_count = hit_count + 1 WHERE url = @url
|
|
6663
6717
|
`)
|
|
6664
6718
|
};
|
|
6665
6719
|
}
|
|
6720
|
+
upsertFetchCacheRow(row) {
|
|
6721
|
+
this.stmt.upsertFetchCache.run(row);
|
|
6722
|
+
}
|
|
6723
|
+
getFetchCacheRow(url) {
|
|
6724
|
+
return this.stmt.getFetchCache.get({ url }) ?? null;
|
|
6725
|
+
}
|
|
6726
|
+
bumpFetchCacheHitFor(url) {
|
|
6727
|
+
this.stmt.bumpFetchCacheHit.run({ url });
|
|
6728
|
+
}
|
|
6666
6729
|
// ── Writes ──────────────────────────────────────────────────────────
|
|
6667
6730
|
insertCompression(row) {
|
|
6668
6731
|
return Number(this.stmt.insertCompression.run(row).lastInsertRowid);
|
|
@@ -11178,7 +11241,7 @@ async function resolveJavaBuildTool(projectRoot, _options) {
|
|
|
11178
11241
|
};
|
|
11179
11242
|
writeNeedsInput(projectRoot, [signal]);
|
|
11180
11243
|
log6.info(
|
|
11181
|
-
`Java build tool: ${choice.tool} (auto: ${choice.reason}). Override: unerr
|
|
11244
|
+
`Java build tool: ${choice.tool} (auto: ${choice.reason}). Override: unerr pm config . --java-build-tool=<tool>`
|
|
11182
11245
|
);
|
|
11183
11246
|
} else {
|
|
11184
11247
|
log6.info(`Java build tool: ${choice.tool} (${choice.reason})`);
|
|
@@ -11813,6 +11876,7 @@ __export(local_indexer_exports, {
|
|
|
11813
11876
|
discoverSourceFiles: () => discoverSourceFiles,
|
|
11814
11877
|
indexLocalProject: () => indexLocalProject,
|
|
11815
11878
|
reindexFile: () => reindexFile,
|
|
11879
|
+
removeOrphanedEntities: () => removeOrphanedEntities,
|
|
11816
11880
|
resolveImportSourceToFile: () => resolveImportSourceToFile,
|
|
11817
11881
|
runCommunityDetection: () => runCommunityDetection,
|
|
11818
11882
|
runConventionDetection: () => runConventionDetection
|
|
@@ -12830,7 +12894,45 @@ async function removeOrphanedEntities(graphStore, liveKeys) {
|
|
|
12830
12894
|
const orphanKeys = existingKeys.filter((k) => !liveKeys.has(k));
|
|
12831
12895
|
if (orphanKeys.length === 0) return;
|
|
12832
12896
|
log7.info(`Removing ${orphanKeys.length} orphaned entities from graph`);
|
|
12833
|
-
for (
|
|
12897
|
+
for (let i = 0; i < orphanKeys.length; i += ORPHAN_BATCH_SIZE) {
|
|
12898
|
+
const chunk = orphanKeys.slice(i, i + ORPHAN_BATCH_SIZE);
|
|
12899
|
+
const keyRows = chunk.map((k) => [k]);
|
|
12900
|
+
try {
|
|
12901
|
+
await db.run(
|
|
12902
|
+
`orphan[k] <- $keys
|
|
12903
|
+
?[from_key, to_key, type] := orphan[from_key], *edges{from_key, to_key, type}
|
|
12904
|
+
:rm edges {from_key, to_key, type}`,
|
|
12905
|
+
{ keys: keyRows }
|
|
12906
|
+
);
|
|
12907
|
+
await db.run(
|
|
12908
|
+
`orphan[k] <- $keys
|
|
12909
|
+
?[from_key, to_key, type] := orphan[to_key], *edges{from_key, to_key, type}
|
|
12910
|
+
:rm edges {from_key, to_key, type}`,
|
|
12911
|
+
{ keys: keyRows }
|
|
12912
|
+
);
|
|
12913
|
+
await db.run(
|
|
12914
|
+
`orphan[k] <- $keys
|
|
12915
|
+
?[token, entity_key] := orphan[entity_key], *search_tokens[token, entity_key]
|
|
12916
|
+
:rm search_tokens {token, entity_key}`,
|
|
12917
|
+
{ keys: keyRows }
|
|
12918
|
+
);
|
|
12919
|
+
await db.run(
|
|
12920
|
+
`orphan[k] <- $keys
|
|
12921
|
+
?[file_path, entity_key] := orphan[entity_key], *file_index[file_path, entity_key]
|
|
12922
|
+
:rm file_index {file_path, entity_key}`,
|
|
12923
|
+
{ keys: keyRows }
|
|
12924
|
+
);
|
|
12925
|
+
await db.run("?[key] <- $keys :rm entities {key}", { keys: keyRows });
|
|
12926
|
+
} catch (err) {
|
|
12927
|
+
log7.info(
|
|
12928
|
+
`Batched orphan removal failed for chunk of ${chunk.length} (${formatUnknownError(err)}); falling back to per-key`
|
|
12929
|
+
);
|
|
12930
|
+
await removeOrphansPerKey(db, chunk);
|
|
12931
|
+
}
|
|
12932
|
+
}
|
|
12933
|
+
}
|
|
12934
|
+
async function removeOrphansPerKey(db, keys) {
|
|
12935
|
+
for (const key of keys) {
|
|
12834
12936
|
try {
|
|
12835
12937
|
await db.run(
|
|
12836
12938
|
"?[from_key, to_key, type] := *edges{from_key, to_key, type}, from_key = $key :rm edges {from_key, to_key, type}",
|
|
@@ -12987,7 +13089,7 @@ async function indexDocumentFiles(projectRoot, graphStore) {
|
|
|
12987
13089
|
}
|
|
12988
13090
|
return docKeys;
|
|
12989
13091
|
}
|
|
12990
|
-
var INDEXABLE_EXTENSIONS, EXCLUDED_DIRS2, EXCLUDED_PATH_PREFIXES, MAX_FILE_SIZE, DOCUMENT_EXTENSIONS, DOCUMENT_NAMES, CI_CD_DIRS, DOC_READ_LIMIT, log7, DOC_TOKEN_PATTERNS;
|
|
13092
|
+
var INDEXABLE_EXTENSIONS, EXCLUDED_DIRS2, EXCLUDED_PATH_PREFIXES, MAX_FILE_SIZE, DOCUMENT_EXTENSIONS, DOCUMENT_NAMES, CI_CD_DIRS, DOC_READ_LIMIT, log7, ORPHAN_BATCH_SIZE, DOC_TOKEN_PATTERNS;
|
|
12991
13093
|
var init_local_indexer = __esm({
|
|
12992
13094
|
"src/intelligence/local-indexer.ts"() {
|
|
12993
13095
|
"use strict";
|
|
@@ -13136,6 +13238,7 @@ var init_local_indexer = __esm({
|
|
|
13136
13238
|
`);
|
|
13137
13239
|
}
|
|
13138
13240
|
};
|
|
13241
|
+
ORPHAN_BATCH_SIZE = 1e3;
|
|
13139
13242
|
DOC_TOKEN_PATTERNS = {
|
|
13140
13243
|
".md": /^#{1,6}\s+(.+)$/gm,
|
|
13141
13244
|
".mdx": /^#{1,6}\s+(.+)$/gm,
|
|
@@ -16881,9 +16984,9 @@ function createEmptyAllTime() {
|
|
|
16881
16984
|
function loadStats() {
|
|
16882
16985
|
const currentWeek = getWeekStart();
|
|
16883
16986
|
try {
|
|
16884
|
-
const { readFileSync:
|
|
16987
|
+
const { readFileSync: readFileSync71 } = __require("fs");
|
|
16885
16988
|
const raw = JSON.parse(
|
|
16886
|
-
|
|
16989
|
+
readFileSync71(getStatsPath(), "utf-8")
|
|
16887
16990
|
);
|
|
16888
16991
|
if (raw.version !== 1) {
|
|
16889
16992
|
return {
|
|
@@ -17601,9 +17704,9 @@ function getCumulativePath() {
|
|
|
17601
17704
|
function loadCumulativeStats() {
|
|
17602
17705
|
const currentWeek = getWeekStart2();
|
|
17603
17706
|
try {
|
|
17604
|
-
const { readFileSync:
|
|
17707
|
+
const { readFileSync: readFileSync71 } = __require("fs");
|
|
17605
17708
|
const raw = JSON.parse(
|
|
17606
|
-
|
|
17709
|
+
readFileSync71(getCumulativePath(), "utf-8")
|
|
17607
17710
|
);
|
|
17608
17711
|
if (raw.weekStart !== currentWeek) {
|
|
17609
17712
|
return {
|
|
@@ -17664,9 +17767,9 @@ function createEmptyCumulativeLocal(weekStart) {
|
|
|
17664
17767
|
function loadCumulativeLocalStats() {
|
|
17665
17768
|
const currentWeek = getWeekStart2();
|
|
17666
17769
|
try {
|
|
17667
|
-
const { readFileSync:
|
|
17770
|
+
const { readFileSync: readFileSync71 } = __require("fs");
|
|
17668
17771
|
const raw = JSON.parse(
|
|
17669
|
-
|
|
17772
|
+
readFileSync71(getCumulativeLocalPath(), "utf-8")
|
|
17670
17773
|
);
|
|
17671
17774
|
if (raw.weekStartDate !== currentWeek) {
|
|
17672
17775
|
return createEmptyCumulativeLocal(currentWeek);
|
|
@@ -19119,6 +19222,24 @@ var init_tool_clusters = __esm({
|
|
|
19119
19222
|
"starting",
|
|
19120
19223
|
"planning"
|
|
19121
19224
|
]
|
|
19225
|
+
},
|
|
19226
|
+
{
|
|
19227
|
+
id: "web",
|
|
19228
|
+
name: "Web Fetch",
|
|
19229
|
+
tools: ["fetch_url"],
|
|
19230
|
+
triggerKeywords: [
|
|
19231
|
+
"fetch",
|
|
19232
|
+
"url",
|
|
19233
|
+
"webpage",
|
|
19234
|
+
"web page",
|
|
19235
|
+
"http",
|
|
19236
|
+
"https",
|
|
19237
|
+
"scrape",
|
|
19238
|
+
"docs at",
|
|
19239
|
+
"documentation at",
|
|
19240
|
+
"blog",
|
|
19241
|
+
"article"
|
|
19242
|
+
]
|
|
19122
19243
|
}
|
|
19123
19244
|
];
|
|
19124
19245
|
TOOL_TO_CLUSTER = /* @__PURE__ */ new Map();
|
|
@@ -19236,6 +19357,11 @@ var init_tool_descriptions = __esm({
|
|
|
19236
19357
|
active: "Find callers or callees of an entity across the codebase. Pass direction:'callers' (default) or 'callees'. Catches indirect refs grep misses.",
|
|
19237
19358
|
locked: "[tier 1 \u2014 always exposed]"
|
|
19238
19359
|
},
|
|
19360
|
+
fetch_url: {
|
|
19361
|
+
tier: 1,
|
|
19362
|
+
active: "Fetch a web page and return DOM-extracted markdown passages. Strips chrome, converts to ATX-markdown, splits by heading, ranks by BM25 when prompt is set, caches by content hash. Use instead of built-in WebFetch \u2014 5\u201310\xD7 fewer tokens.",
|
|
19363
|
+
locked: "[tier 1 \u2014 always exposed]"
|
|
19364
|
+
},
|
|
19239
19365
|
// ── Tier 2 — structural unlock ─────────────────────────────────────────
|
|
19240
19366
|
get_critical_nodes: {
|
|
19241
19367
|
tier: 2,
|
|
@@ -19407,6 +19533,36 @@ var init_tool_definitions = __esm({
|
|
|
19407
19533
|
openWorldHint: false
|
|
19408
19534
|
}
|
|
19409
19535
|
},
|
|
19536
|
+
fetch_url: {
|
|
19537
|
+
inputSchema: {
|
|
19538
|
+
type: "object",
|
|
19539
|
+
properties: {
|
|
19540
|
+
url: {
|
|
19541
|
+
type: "string",
|
|
19542
|
+
description: "Absolute URL to fetch (http or https). Localhost is allowed without TLS upgrade."
|
|
19543
|
+
},
|
|
19544
|
+
prompt: {
|
|
19545
|
+
type: "string",
|
|
19546
|
+
description: "Optional. When set AND extracted markdown > 8 KB, passages are re-ranked by BM25 relevance to this prompt (default top 20)."
|
|
19547
|
+
},
|
|
19548
|
+
offset: {
|
|
19549
|
+
type: "integer",
|
|
19550
|
+
description: "Passage index to start at for pagination (default 0). Pair with limit to walk long pages without re-fetching."
|
|
19551
|
+
},
|
|
19552
|
+
limit: {
|
|
19553
|
+
type: "integer",
|
|
19554
|
+
description: "Maximum passages to return (default 30, max 300). Also acts as BM25 topK when prompt is set."
|
|
19555
|
+
},
|
|
19556
|
+
token_budget: TOKEN_BUDGET_PROP
|
|
19557
|
+
},
|
|
19558
|
+
required: ["url"]
|
|
19559
|
+
},
|
|
19560
|
+
annotations: {
|
|
19561
|
+
title: "Fetch Web Page (Markdown + BM25)",
|
|
19562
|
+
readOnlyHint: true,
|
|
19563
|
+
openWorldHint: true
|
|
19564
|
+
}
|
|
19565
|
+
},
|
|
19410
19566
|
file_outline: {
|
|
19411
19567
|
inputSchema: {
|
|
19412
19568
|
type: "object",
|
|
@@ -23084,6 +23240,12 @@ var init_wire_cap = __esm({
|
|
|
23084
23240
|
// statically. The caller should pick from the returned `entities[].name`
|
|
23085
23241
|
// and re-call file_read with that exact value. A literal `entity:<name>`
|
|
23086
23242
|
// hint trains the agent to paste the placeholder verbatim.
|
|
23243
|
+
},
|
|
23244
|
+
fetch_url: {
|
|
23245
|
+
arrayKey: "passages",
|
|
23246
|
+
defaultLimit: 30,
|
|
23247
|
+
maxLimit: 300,
|
|
23248
|
+
cursorArg: "limit"
|
|
23087
23249
|
}
|
|
23088
23250
|
};
|
|
23089
23251
|
HARD_BYTE_CAP = 8192;
|
|
@@ -24309,6 +24471,785 @@ var init_file_read_protocol = __esm({
|
|
|
24309
24471
|
}
|
|
24310
24472
|
});
|
|
24311
24473
|
|
|
24474
|
+
// src/tools/web/bm25-rank.ts
|
|
24475
|
+
function tokenize2(text2) {
|
|
24476
|
+
return text2.toLowerCase().replace(/[^a-z0-9\s]+/g, " ").split(/\s+/).filter((t) => t.length > 1 && !STOPWORDS.has(t));
|
|
24477
|
+
}
|
|
24478
|
+
async function rankPassagesByPrompt(passages, opts) {
|
|
24479
|
+
const prompt = opts.prompt?.trim();
|
|
24480
|
+
if (!prompt || passages.length === 0) return passages;
|
|
24481
|
+
const promptTokens = tokenize2(prompt);
|
|
24482
|
+
if (promptTokens.length === 0) return passages;
|
|
24483
|
+
const mod = await import("wink-bm25-text-search");
|
|
24484
|
+
const engine = mod.default();
|
|
24485
|
+
engine.defineConfig({ fldWeights: { text: 1, heading: 2 } });
|
|
24486
|
+
engine.definePrepTasks([tokenize2]);
|
|
24487
|
+
for (const p of passages) {
|
|
24488
|
+
engine.addDoc(
|
|
24489
|
+
{ text: p.text, heading: p.heading ?? "" },
|
|
24490
|
+
String(p.index)
|
|
24491
|
+
);
|
|
24492
|
+
}
|
|
24493
|
+
engine.consolidate();
|
|
24494
|
+
const hits = engine.search(prompt);
|
|
24495
|
+
if (hits.length === 0) return passages;
|
|
24496
|
+
const topK = Math.max(1, opts.topK ?? Math.min(20, passages.length));
|
|
24497
|
+
const ranked = [];
|
|
24498
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24499
|
+
for (const [docId] of hits.slice(0, topK)) {
|
|
24500
|
+
const idx = Number(docId);
|
|
24501
|
+
const p = passages.find((x2) => x2.index === idx);
|
|
24502
|
+
if (p && !seen.has(idx)) {
|
|
24503
|
+
ranked.push(p);
|
|
24504
|
+
seen.add(idx);
|
|
24505
|
+
}
|
|
24506
|
+
}
|
|
24507
|
+
if (ranked.length === 0) return passages;
|
|
24508
|
+
return ranked;
|
|
24509
|
+
}
|
|
24510
|
+
var STOPWORDS;
|
|
24511
|
+
var init_bm25_rank = __esm({
|
|
24512
|
+
"src/tools/web/bm25-rank.ts"() {
|
|
24513
|
+
"use strict";
|
|
24514
|
+
STOPWORDS = /* @__PURE__ */ new Set([
|
|
24515
|
+
"a",
|
|
24516
|
+
"an",
|
|
24517
|
+
"the",
|
|
24518
|
+
"is",
|
|
24519
|
+
"are",
|
|
24520
|
+
"was",
|
|
24521
|
+
"were",
|
|
24522
|
+
"be",
|
|
24523
|
+
"been",
|
|
24524
|
+
"being",
|
|
24525
|
+
"of",
|
|
24526
|
+
"and",
|
|
24527
|
+
"or",
|
|
24528
|
+
"but",
|
|
24529
|
+
"in",
|
|
24530
|
+
"on",
|
|
24531
|
+
"at",
|
|
24532
|
+
"to",
|
|
24533
|
+
"for",
|
|
24534
|
+
"with",
|
|
24535
|
+
"by",
|
|
24536
|
+
"from",
|
|
24537
|
+
"up",
|
|
24538
|
+
"down",
|
|
24539
|
+
"out",
|
|
24540
|
+
"off",
|
|
24541
|
+
"over",
|
|
24542
|
+
"under",
|
|
24543
|
+
"as",
|
|
24544
|
+
"this",
|
|
24545
|
+
"that",
|
|
24546
|
+
"these",
|
|
24547
|
+
"those",
|
|
24548
|
+
"it",
|
|
24549
|
+
"its",
|
|
24550
|
+
"into",
|
|
24551
|
+
"if",
|
|
24552
|
+
"then",
|
|
24553
|
+
"do",
|
|
24554
|
+
"does",
|
|
24555
|
+
"did"
|
|
24556
|
+
]);
|
|
24557
|
+
}
|
|
24558
|
+
});
|
|
24559
|
+
|
|
24560
|
+
// src/tools/web/diff-cache.ts
|
|
24561
|
+
import { createHash as createHash3 } from "crypto";
|
|
24562
|
+
import { join as join60 } from "path";
|
|
24563
|
+
function hashHtml(html) {
|
|
24564
|
+
return createHash3("sha256").update(html).digest("hex").slice(0, 32);
|
|
24565
|
+
}
|
|
24566
|
+
function lookupFetchCache(cwd, url) {
|
|
24567
|
+
try {
|
|
24568
|
+
const store = openMetricsStore(join60(cwd, ".unerr"));
|
|
24569
|
+
const row = store.getFetchCacheRow(url);
|
|
24570
|
+
if (!row) return { hit: false, prior: null };
|
|
24571
|
+
return { hit: true, prior: row };
|
|
24572
|
+
} catch {
|
|
24573
|
+
return { hit: false, prior: null };
|
|
24574
|
+
}
|
|
24575
|
+
}
|
|
24576
|
+
function storeFetchCache(cwd, row) {
|
|
24577
|
+
try {
|
|
24578
|
+
const store = openMetricsStore(join60(cwd, ".unerr"));
|
|
24579
|
+
store.upsertFetchCacheRow({ ...row, hit_count: 0 });
|
|
24580
|
+
} catch {
|
|
24581
|
+
}
|
|
24582
|
+
}
|
|
24583
|
+
function bumpCacheHit(cwd, url) {
|
|
24584
|
+
try {
|
|
24585
|
+
const store = openMetricsStore(join60(cwd, ".unerr"));
|
|
24586
|
+
store.bumpFetchCacheHitFor(url);
|
|
24587
|
+
} catch {
|
|
24588
|
+
}
|
|
24589
|
+
}
|
|
24590
|
+
function summarizeMarkdownDiff(oldMd, newMd) {
|
|
24591
|
+
if (oldMd === newMd) {
|
|
24592
|
+
return { unchanged: true, changedRegions: 0, addedLines: 0, removedLines: 0 };
|
|
24593
|
+
}
|
|
24594
|
+
const oldLines = new Set(oldMd.split("\n"));
|
|
24595
|
+
const newLines = newMd.split("\n");
|
|
24596
|
+
let added = 0;
|
|
24597
|
+
let regions = 0;
|
|
24598
|
+
let inRegion = false;
|
|
24599
|
+
for (const line of newLines) {
|
|
24600
|
+
if (!oldLines.has(line)) {
|
|
24601
|
+
added++;
|
|
24602
|
+
if (!inRegion) {
|
|
24603
|
+
regions++;
|
|
24604
|
+
inRegion = true;
|
|
24605
|
+
}
|
|
24606
|
+
} else {
|
|
24607
|
+
inRegion = false;
|
|
24608
|
+
}
|
|
24609
|
+
}
|
|
24610
|
+
const newLineSet = new Set(newLines);
|
|
24611
|
+
let removed = 0;
|
|
24612
|
+
for (const line of oldMd.split("\n")) {
|
|
24613
|
+
if (!newLineSet.has(line)) removed++;
|
|
24614
|
+
}
|
|
24615
|
+
return {
|
|
24616
|
+
unchanged: false,
|
|
24617
|
+
changedRegions: regions,
|
|
24618
|
+
addedLines: added,
|
|
24619
|
+
removedLines: removed
|
|
24620
|
+
};
|
|
24621
|
+
}
|
|
24622
|
+
var init_diff_cache = __esm({
|
|
24623
|
+
"src/tools/web/diff-cache.ts"() {
|
|
24624
|
+
"use strict";
|
|
24625
|
+
init_metrics_store();
|
|
24626
|
+
}
|
|
24627
|
+
});
|
|
24628
|
+
|
|
24629
|
+
// src/tools/web/extract.ts
|
|
24630
|
+
async function extractMainContent(html, baseUrl) {
|
|
24631
|
+
const { JSDOM } = await import("jsdom");
|
|
24632
|
+
const dom = new JSDOM(html, { url: baseUrl });
|
|
24633
|
+
const defuddled = await tryDefuddle(dom);
|
|
24634
|
+
if (defuddled && stripTags(defuddled.contentHtml).length >= MIN_USEFUL_CHARS) {
|
|
24635
|
+
return defuddled;
|
|
24636
|
+
}
|
|
24637
|
+
const readabilityResult = await tryReadability(dom);
|
|
24638
|
+
if (readabilityResult && stripTags(readabilityResult.contentHtml).length >= MIN_USEFUL_CHARS) {
|
|
24639
|
+
return readabilityResult;
|
|
24640
|
+
}
|
|
24641
|
+
return rawBodyFallback(dom);
|
|
24642
|
+
}
|
|
24643
|
+
async function tryDefuddle(dom) {
|
|
24644
|
+
try {
|
|
24645
|
+
const mod = await import("defuddle");
|
|
24646
|
+
const DefuddleCtor = mod.default;
|
|
24647
|
+
const doc = dom.window.document;
|
|
24648
|
+
const result = new DefuddleCtor(doc, { markdown: false }).parse();
|
|
24649
|
+
if (!result?.content) return null;
|
|
24650
|
+
return {
|
|
24651
|
+
title: result.title ?? "",
|
|
24652
|
+
contentHtml: result.content,
|
|
24653
|
+
wordCount: result.wordCount ?? wordCountOf(result.content),
|
|
24654
|
+
byline: result.author ?? void 0,
|
|
24655
|
+
excerpt: result.description ?? void 0,
|
|
24656
|
+
extractor: "defuddle"
|
|
24657
|
+
};
|
|
24658
|
+
} catch {
|
|
24659
|
+
return null;
|
|
24660
|
+
}
|
|
24661
|
+
}
|
|
24662
|
+
async function tryReadability(dom) {
|
|
24663
|
+
try {
|
|
24664
|
+
const mod = await import("@mozilla/readability");
|
|
24665
|
+
const doc = dom.window.document;
|
|
24666
|
+
if (mod.isProbablyReaderable && !mod.isProbablyReaderable(doc)) {
|
|
24667
|
+
return null;
|
|
24668
|
+
}
|
|
24669
|
+
const article = new mod.Readability(
|
|
24670
|
+
doc
|
|
24671
|
+
).parse();
|
|
24672
|
+
if (!article?.content) return null;
|
|
24673
|
+
return {
|
|
24674
|
+
title: article.title ?? "",
|
|
24675
|
+
contentHtml: article.content,
|
|
24676
|
+
wordCount: wordCountOf(article.textContent ?? article.content),
|
|
24677
|
+
byline: article.byline ?? void 0,
|
|
24678
|
+
excerpt: article.excerpt ?? void 0,
|
|
24679
|
+
lang: article.lang ?? void 0,
|
|
24680
|
+
extractor: "readability"
|
|
24681
|
+
};
|
|
24682
|
+
} catch {
|
|
24683
|
+
return null;
|
|
24684
|
+
}
|
|
24685
|
+
}
|
|
24686
|
+
function rawBodyFallback(dom) {
|
|
24687
|
+
const doc = dom.window.document;
|
|
24688
|
+
for (const sel of ["script", "style", "noscript", "iframe", "svg"]) {
|
|
24689
|
+
for (const node of doc.querySelectorAll(sel)) node.remove();
|
|
24690
|
+
}
|
|
24691
|
+
const main = doc.querySelector("main") ?? doc.querySelector("article") ?? doc.body ?? doc.documentElement;
|
|
24692
|
+
const html = main?.innerHTML ?? "";
|
|
24693
|
+
return {
|
|
24694
|
+
title: doc.title ?? "",
|
|
24695
|
+
contentHtml: html,
|
|
24696
|
+
wordCount: wordCountOf(main?.textContent ?? ""),
|
|
24697
|
+
extractor: "raw-body"
|
|
24698
|
+
};
|
|
24699
|
+
}
|
|
24700
|
+
function stripTags(html) {
|
|
24701
|
+
return html.replace(/<[^>]+>/g, "").trim();
|
|
24702
|
+
}
|
|
24703
|
+
function wordCountOf(text2) {
|
|
24704
|
+
return text2.trim().split(/\s+/).filter(Boolean).length;
|
|
24705
|
+
}
|
|
24706
|
+
var MIN_USEFUL_CHARS;
|
|
24707
|
+
var init_extract = __esm({
|
|
24708
|
+
"src/tools/web/extract.ts"() {
|
|
24709
|
+
"use strict";
|
|
24710
|
+
MIN_USEFUL_CHARS = 200;
|
|
24711
|
+
}
|
|
24712
|
+
});
|
|
24713
|
+
|
|
24714
|
+
// src/tools/web/markdown.ts
|
|
24715
|
+
async function htmlToMarkdown(html) {
|
|
24716
|
+
if (!html.trim()) return "";
|
|
24717
|
+
const TurndownModule = await import("turndown");
|
|
24718
|
+
const Turndown = TurndownModule.default;
|
|
24719
|
+
const td = new Turndown({
|
|
24720
|
+
headingStyle: "atx",
|
|
24721
|
+
codeBlockStyle: "fenced",
|
|
24722
|
+
hr: "---",
|
|
24723
|
+
bulletListMarker: "-",
|
|
24724
|
+
emDelimiter: "_"
|
|
24725
|
+
});
|
|
24726
|
+
td.addRule("drop-anchor-only", {
|
|
24727
|
+
filter: (node) => {
|
|
24728
|
+
if (node.nodeName !== "A") return false;
|
|
24729
|
+
const href = node.getAttribute("href") ?? "";
|
|
24730
|
+
const text2 = (node.textContent ?? "").trim();
|
|
24731
|
+
return href.startsWith("#") || href === "" && text2 === "";
|
|
24732
|
+
},
|
|
24733
|
+
replacement: (content) => content
|
|
24734
|
+
});
|
|
24735
|
+
td.addRule("strip-tracking-params", {
|
|
24736
|
+
filter: (node) => node.nodeName === "A",
|
|
24737
|
+
replacement: (content, node) => {
|
|
24738
|
+
const rawHref = node.getAttribute("href") ?? "";
|
|
24739
|
+
const cleanHref = cleanUrl(rawHref);
|
|
24740
|
+
const title = node.getAttribute("title");
|
|
24741
|
+
if (!cleanHref) return content;
|
|
24742
|
+
const titleSuffix = title ? ` "${title}"` : "";
|
|
24743
|
+
return `[${content}](${cleanHref}${titleSuffix})`;
|
|
24744
|
+
}
|
|
24745
|
+
});
|
|
24746
|
+
return td.turndown(html);
|
|
24747
|
+
}
|
|
24748
|
+
function cleanUrl(href) {
|
|
24749
|
+
if (!href || href.startsWith("#") || href.startsWith("javascript:")) {
|
|
24750
|
+
return href;
|
|
24751
|
+
}
|
|
24752
|
+
try {
|
|
24753
|
+
const url = new URL(href, "https://example.invalid");
|
|
24754
|
+
const params = url.searchParams;
|
|
24755
|
+
const drop = [];
|
|
24756
|
+
for (const key of params.keys()) {
|
|
24757
|
+
if (TRACKING_PARAMS.test(key)) drop.push(key);
|
|
24758
|
+
}
|
|
24759
|
+
for (const key of drop) params.delete(key);
|
|
24760
|
+
return url.protocol === "https:" && url.hostname === "example.invalid" ? url.pathname + url.search + url.hash : url.toString();
|
|
24761
|
+
} catch {
|
|
24762
|
+
return href;
|
|
24763
|
+
}
|
|
24764
|
+
}
|
|
24765
|
+
var TRACKING_PARAMS;
|
|
24766
|
+
var init_markdown = __esm({
|
|
24767
|
+
"src/tools/web/markdown.ts"() {
|
|
24768
|
+
"use strict";
|
|
24769
|
+
TRACKING_PARAMS = /^(utm_|mc_|gclid$|fbclid$|yclid$|msclkid$|ref$|ref_)/i;
|
|
24770
|
+
}
|
|
24771
|
+
});
|
|
24772
|
+
|
|
24773
|
+
// src/tools/web/passage-split.ts
|
|
24774
|
+
function splitMarkdownIntoPassages(markdown) {
|
|
24775
|
+
const lines = markdown.split("\n");
|
|
24776
|
+
const passages = [];
|
|
24777
|
+
let currentHeading = null;
|
|
24778
|
+
let buffer = [];
|
|
24779
|
+
let bufferStart = 0;
|
|
24780
|
+
let inFence = false;
|
|
24781
|
+
let fenceMarker = "";
|
|
24782
|
+
const flush = () => {
|
|
24783
|
+
const text2 = buffer.join("\n").trim();
|
|
24784
|
+
if (text2) {
|
|
24785
|
+
passages.push({
|
|
24786
|
+
index: passages.length,
|
|
24787
|
+
heading: currentHeading,
|
|
24788
|
+
text: text2,
|
|
24789
|
+
startLine: bufferStart + 1
|
|
24790
|
+
});
|
|
24791
|
+
}
|
|
24792
|
+
buffer = [];
|
|
24793
|
+
};
|
|
24794
|
+
for (let i = 0; i < lines.length; i++) {
|
|
24795
|
+
const line = lines[i] ?? "";
|
|
24796
|
+
const fenceMatch = line.match(/^(```|~~~)/);
|
|
24797
|
+
if (fenceMatch) {
|
|
24798
|
+
if (!inFence) {
|
|
24799
|
+
inFence = true;
|
|
24800
|
+
fenceMarker = fenceMatch[1] ?? "```";
|
|
24801
|
+
if (buffer.length === 0) bufferStart = i;
|
|
24802
|
+
buffer.push(line);
|
|
24803
|
+
} else if (line.startsWith(fenceMarker)) {
|
|
24804
|
+
buffer.push(line);
|
|
24805
|
+
inFence = false;
|
|
24806
|
+
} else {
|
|
24807
|
+
buffer.push(line);
|
|
24808
|
+
}
|
|
24809
|
+
continue;
|
|
24810
|
+
}
|
|
24811
|
+
if (inFence) {
|
|
24812
|
+
buffer.push(line);
|
|
24813
|
+
continue;
|
|
24814
|
+
}
|
|
24815
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+?)\s*$/);
|
|
24816
|
+
if (headingMatch) {
|
|
24817
|
+
flush();
|
|
24818
|
+
currentHeading = headingMatch[2] ?? null;
|
|
24819
|
+
bufferStart = i;
|
|
24820
|
+
buffer.push(line);
|
|
24821
|
+
continue;
|
|
24822
|
+
}
|
|
24823
|
+
if (line.trim() === "") {
|
|
24824
|
+
if (buffer.length > 0) flush();
|
|
24825
|
+
continue;
|
|
24826
|
+
}
|
|
24827
|
+
if (buffer.length === 0) bufferStart = i;
|
|
24828
|
+
buffer.push(line);
|
|
24829
|
+
}
|
|
24830
|
+
flush();
|
|
24831
|
+
return passages;
|
|
24832
|
+
}
|
|
24833
|
+
var init_passage_split = __esm({
|
|
24834
|
+
"src/tools/web/passage-split.ts"() {
|
|
24835
|
+
"use strict";
|
|
24836
|
+
}
|
|
24837
|
+
});
|
|
24838
|
+
|
|
24839
|
+
// src/tools/web/post-process.ts
|
|
24840
|
+
import { readdirSync as readdirSync14, readFileSync as readFileSync51 } from "fs";
|
|
24841
|
+
import { join as join61 } from "path";
|
|
24842
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24843
|
+
function loadRules() {
|
|
24844
|
+
if (rulesCache) return rulesCache;
|
|
24845
|
+
const map = /* @__PURE__ */ new Map();
|
|
24846
|
+
if (!RULES_DIR) {
|
|
24847
|
+
rulesCache = map;
|
|
24848
|
+
return map;
|
|
24849
|
+
}
|
|
24850
|
+
let entries;
|
|
24851
|
+
try {
|
|
24852
|
+
entries = readdirSync14(RULES_DIR);
|
|
24853
|
+
} catch {
|
|
24854
|
+
rulesCache = map;
|
|
24855
|
+
return map;
|
|
24856
|
+
}
|
|
24857
|
+
for (const name of entries) {
|
|
24858
|
+
if (!name.endsWith(".json")) continue;
|
|
24859
|
+
try {
|
|
24860
|
+
const raw = readFileSync51(join61(RULES_DIR, name), "utf-8");
|
|
24861
|
+
const parsed = JSON.parse(raw);
|
|
24862
|
+
if (parsed.host) map.set(parsed.host.toLowerCase(), parsed);
|
|
24863
|
+
} catch {
|
|
24864
|
+
}
|
|
24865
|
+
}
|
|
24866
|
+
rulesCache = map;
|
|
24867
|
+
return map;
|
|
24868
|
+
}
|
|
24869
|
+
function postProcessMarkdown(markdown, opts = {}) {
|
|
24870
|
+
let out = universalCleanup(markdown);
|
|
24871
|
+
const hostRule = pickHostRule(opts);
|
|
24872
|
+
if (hostRule) out = applyHostRule(out, hostRule);
|
|
24873
|
+
out = universalCleanup(out);
|
|
24874
|
+
return out;
|
|
24875
|
+
}
|
|
24876
|
+
function universalCleanup(md) {
|
|
24877
|
+
return md.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n").replace(/^\[\s*\]\([^)]*\)\s*$/gm, "").trim();
|
|
24878
|
+
}
|
|
24879
|
+
function pickHostRule(opts) {
|
|
24880
|
+
if (opts.rules?.length) {
|
|
24881
|
+
return mergeRules(opts.rules);
|
|
24882
|
+
}
|
|
24883
|
+
if (!opts.url) return null;
|
|
24884
|
+
try {
|
|
24885
|
+
const host = new URL(opts.url).hostname.toLowerCase();
|
|
24886
|
+
const rules = loadRules();
|
|
24887
|
+
const exact = rules.get(host);
|
|
24888
|
+
if (exact) return exact;
|
|
24889
|
+
const parts = host.split(".");
|
|
24890
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
24891
|
+
const suffix = parts.slice(i).join(".");
|
|
24892
|
+
const match = rules.get(suffix);
|
|
24893
|
+
if (match) return match;
|
|
24894
|
+
}
|
|
24895
|
+
return null;
|
|
24896
|
+
} catch {
|
|
24897
|
+
return null;
|
|
24898
|
+
}
|
|
24899
|
+
}
|
|
24900
|
+
function mergeRules(rules) {
|
|
24901
|
+
return {
|
|
24902
|
+
host: "merged",
|
|
24903
|
+
drop: rules.flatMap((r) => r.drop ?? []),
|
|
24904
|
+
replace: rules.flatMap((r) => r.replace ?? [])
|
|
24905
|
+
};
|
|
24906
|
+
}
|
|
24907
|
+
function applyHostRule(md, rule) {
|
|
24908
|
+
let out = md;
|
|
24909
|
+
if (rule.drop?.length) {
|
|
24910
|
+
for (const pat of rule.drop) {
|
|
24911
|
+
try {
|
|
24912
|
+
const re = new RegExp(pat, "gm");
|
|
24913
|
+
out = out.replace(re, "");
|
|
24914
|
+
} catch {
|
|
24915
|
+
}
|
|
24916
|
+
}
|
|
24917
|
+
}
|
|
24918
|
+
if (rule.replace?.length) {
|
|
24919
|
+
for (const { pattern, with: replacement } of rule.replace) {
|
|
24920
|
+
try {
|
|
24921
|
+
const re = new RegExp(pattern, "gm");
|
|
24922
|
+
out = out.replace(re, replacement);
|
|
24923
|
+
} catch {
|
|
24924
|
+
}
|
|
24925
|
+
}
|
|
24926
|
+
}
|
|
24927
|
+
return out;
|
|
24928
|
+
}
|
|
24929
|
+
var RULES_DIR, rulesCache;
|
|
24930
|
+
var init_post_process = __esm({
|
|
24931
|
+
"src/tools/web/post-process.ts"() {
|
|
24932
|
+
"use strict";
|
|
24933
|
+
RULES_DIR = (() => {
|
|
24934
|
+
try {
|
|
24935
|
+
return join61(fileURLToPath3(new URL(".", import.meta.url)), "rules");
|
|
24936
|
+
} catch {
|
|
24937
|
+
return "";
|
|
24938
|
+
}
|
|
24939
|
+
})();
|
|
24940
|
+
rulesCache = null;
|
|
24941
|
+
}
|
|
24942
|
+
});
|
|
24943
|
+
|
|
24944
|
+
// src/tools/web/spa-render.ts
|
|
24945
|
+
function shouldUsePlaywright(args) {
|
|
24946
|
+
if (!args.config?.playwright?.enabled) return false;
|
|
24947
|
+
if (args.extractor !== "raw-body") return false;
|
|
24948
|
+
return args.wordCount < SPA_EMPTY_WC;
|
|
24949
|
+
}
|
|
24950
|
+
async function renderWithPlaywright(url, config) {
|
|
24951
|
+
const pw = await loadPlaywright();
|
|
24952
|
+
if (!pw) return null;
|
|
24953
|
+
const browser = await pw.chromium.launch({ headless: true });
|
|
24954
|
+
try {
|
|
24955
|
+
const ctx = await browser.newContext({
|
|
24956
|
+
userAgent: "unerr-fetch-url/1.0 (+https://unerr.dev) Mozilla/5.0 compatible"
|
|
24957
|
+
});
|
|
24958
|
+
const page = await ctx.newPage();
|
|
24959
|
+
const response = await page.goto(url, {
|
|
24960
|
+
waitUntil: config.playwright.waitUntil,
|
|
24961
|
+
timeout: config.playwright.timeoutMs
|
|
24962
|
+
});
|
|
24963
|
+
const html = await page.content();
|
|
24964
|
+
return {
|
|
24965
|
+
html,
|
|
24966
|
+
finalUrl: page.url(),
|
|
24967
|
+
status: response?.status() ?? 200
|
|
24968
|
+
};
|
|
24969
|
+
} finally {
|
|
24970
|
+
await browser.close();
|
|
24971
|
+
}
|
|
24972
|
+
}
|
|
24973
|
+
async function loadPlaywright() {
|
|
24974
|
+
try {
|
|
24975
|
+
return await import("playwright");
|
|
24976
|
+
} catch {
|
|
24977
|
+
return null;
|
|
24978
|
+
}
|
|
24979
|
+
}
|
|
24980
|
+
var SPA_EMPTY_WC;
|
|
24981
|
+
var init_spa_render = __esm({
|
|
24982
|
+
"src/tools/web/spa-render.ts"() {
|
|
24983
|
+
"use strict";
|
|
24984
|
+
SPA_EMPTY_WC = 40;
|
|
24985
|
+
}
|
|
24986
|
+
});
|
|
24987
|
+
|
|
24988
|
+
// src/tools/web/telemetry.ts
|
|
24989
|
+
function recordFetchUrlTelemetry(cwd, ev) {
|
|
24990
|
+
const savedPct = ev.rawBytes > 0 ? Math.round((ev.rawBytes - ev.compressedBytes) / ev.rawBytes * 100) : 0;
|
|
24991
|
+
appendCompressionLog(cwd, {
|
|
24992
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24993
|
+
command: `fetch_url ${ev.url}`,
|
|
24994
|
+
category: "fetch_url",
|
|
24995
|
+
confidence: 1,
|
|
24996
|
+
rawBytes: ev.rawBytes,
|
|
24997
|
+
compressedBytes: ev.compressedBytes,
|
|
24998
|
+
savedPct,
|
|
24999
|
+
omniFallback: ev.extractor === "raw-body",
|
|
25000
|
+
teeFile: ev.cacheHit ? "cache-hit" : void 0
|
|
25001
|
+
});
|
|
25002
|
+
}
|
|
25003
|
+
var init_telemetry = __esm({
|
|
25004
|
+
"src/tools/web/telemetry.ts"() {
|
|
25005
|
+
"use strict";
|
|
25006
|
+
init_shell_compression_log();
|
|
25007
|
+
}
|
|
25008
|
+
});
|
|
25009
|
+
|
|
25010
|
+
// src/tools/web/fetch-url-protocol.ts
|
|
25011
|
+
var fetch_url_protocol_exports = {};
|
|
25012
|
+
__export(fetch_url_protocol_exports, {
|
|
25013
|
+
runFetchUrl: () => runFetchUrl
|
|
25014
|
+
});
|
|
25015
|
+
function shouldRank(args, markdown) {
|
|
25016
|
+
if (!args.prompt || args.prompt.trim().length === 0) return false;
|
|
25017
|
+
return Buffer.byteLength(markdown, "utf-8") > BM25_GATE_BYTES;
|
|
25018
|
+
}
|
|
25019
|
+
async function runFetchUrl(args, ctx) {
|
|
25020
|
+
if (!args.url || typeof args.url !== "string") {
|
|
25021
|
+
throw new Error("fetch_url requires a string `url` argument");
|
|
25022
|
+
}
|
|
25023
|
+
const url = upgradeToHttps(args.url);
|
|
25024
|
+
const started = Date.now();
|
|
25025
|
+
const fetched = await fetchHtml(url, ctx.abortSignal);
|
|
25026
|
+
const contentHash = hashHtml(fetched.html);
|
|
25027
|
+
const cache = lookupFetchCache(ctx.cwd, fetched.finalUrl);
|
|
25028
|
+
let markdown;
|
|
25029
|
+
let title;
|
|
25030
|
+
let extractor;
|
|
25031
|
+
let wordCount;
|
|
25032
|
+
let cacheHit = false;
|
|
25033
|
+
let diff;
|
|
25034
|
+
if (cache.hit && cache.prior && cache.prior.content_hash === contentHash) {
|
|
25035
|
+
markdown = cache.prior.markdown;
|
|
25036
|
+
title = cache.prior.title;
|
|
25037
|
+
extractor = "cache";
|
|
25038
|
+
wordCount = markdown.trim().split(/\s+/).filter(Boolean).length;
|
|
25039
|
+
cacheHit = true;
|
|
25040
|
+
diff = {
|
|
25041
|
+
unchanged: true,
|
|
25042
|
+
changed_regions: 0,
|
|
25043
|
+
added_lines: 0,
|
|
25044
|
+
removed_lines: 0
|
|
25045
|
+
};
|
|
25046
|
+
bumpCacheHit(ctx.cwd, fetched.finalUrl);
|
|
25047
|
+
} else {
|
|
25048
|
+
let extracted = await extractMainContent(fetched.html, fetched.finalUrl);
|
|
25049
|
+
const settings = safeLoadSettings(ctx.cwd);
|
|
25050
|
+
if (shouldUsePlaywright({
|
|
25051
|
+
config: settings?.fetchUrl,
|
|
25052
|
+
extractor: extracted.extractor,
|
|
25053
|
+
wordCount: extracted.wordCount
|
|
25054
|
+
}) && settings) {
|
|
25055
|
+
const rendered = await renderWithPlaywright(
|
|
25056
|
+
fetched.finalUrl,
|
|
25057
|
+
settings.fetchUrl
|
|
25058
|
+
);
|
|
25059
|
+
if (rendered) {
|
|
25060
|
+
fetched.html = rendered.html;
|
|
25061
|
+
fetched.finalUrl = rendered.finalUrl;
|
|
25062
|
+
fetched.status = rendered.status;
|
|
25063
|
+
extracted = await extractMainContent(rendered.html, rendered.finalUrl);
|
|
25064
|
+
}
|
|
25065
|
+
}
|
|
25066
|
+
const rawMarkdown = await htmlToMarkdown(extracted.contentHtml);
|
|
25067
|
+
markdown = postProcessMarkdown(rawMarkdown, { url: fetched.finalUrl });
|
|
25068
|
+
title = extracted.title;
|
|
25069
|
+
extractor = extracted.extractor;
|
|
25070
|
+
wordCount = extracted.wordCount;
|
|
25071
|
+
if (cache.prior) {
|
|
25072
|
+
const summary = summarizeMarkdownDiff(cache.prior.markdown, markdown);
|
|
25073
|
+
diff = {
|
|
25074
|
+
unchanged: summary.unchanged,
|
|
25075
|
+
changed_regions: summary.changedRegions,
|
|
25076
|
+
added_lines: summary.addedLines,
|
|
25077
|
+
removed_lines: summary.removedLines
|
|
25078
|
+
};
|
|
25079
|
+
}
|
|
25080
|
+
storeFetchCache(ctx.cwd, {
|
|
25081
|
+
url: fetched.finalUrl,
|
|
25082
|
+
content_hash: contentHash,
|
|
25083
|
+
markdown,
|
|
25084
|
+
title,
|
|
25085
|
+
extractor,
|
|
25086
|
+
raw_bytes: Buffer.byteLength(fetched.html, "utf-8"),
|
|
25087
|
+
compressed_bytes: Buffer.byteLength(markdown, "utf-8"),
|
|
25088
|
+
fetched_at: Date.now()
|
|
25089
|
+
});
|
|
25090
|
+
}
|
|
25091
|
+
const allPassages = splitMarkdownIntoPassages(markdown);
|
|
25092
|
+
const ranked = shouldRank(args, markdown) ? await rankPassagesByPrompt(allPassages, {
|
|
25093
|
+
prompt: args.prompt ?? "",
|
|
25094
|
+
topK: args.limit ?? BM25_DEFAULT_TOPK
|
|
25095
|
+
}) : allPassages;
|
|
25096
|
+
const offset = Math.max(0, args.offset ?? 0);
|
|
25097
|
+
const sliced = offset > 0 ? ranked.slice(offset) : ranked;
|
|
25098
|
+
const rawBytes = byteLength(fetched.html);
|
|
25099
|
+
const compressedBytes = byteLength(markdown);
|
|
25100
|
+
recordFetchUrlTelemetry(ctx.cwd, {
|
|
25101
|
+
url: fetched.finalUrl,
|
|
25102
|
+
rawBytes,
|
|
25103
|
+
compressedBytes,
|
|
25104
|
+
durationMs: Date.now() - started,
|
|
25105
|
+
extractor: extractor === "cache" ? "raw-body" : extractor,
|
|
25106
|
+
cacheHit
|
|
25107
|
+
});
|
|
25108
|
+
return {
|
|
25109
|
+
url: args.url,
|
|
25110
|
+
final_url: fetched.finalUrl,
|
|
25111
|
+
status: fetched.status,
|
|
25112
|
+
title,
|
|
25113
|
+
extractor,
|
|
25114
|
+
word_count: wordCount,
|
|
25115
|
+
cache_hit: cacheHit,
|
|
25116
|
+
diff,
|
|
25117
|
+
raw_bytes: rawBytes,
|
|
25118
|
+
extracted_bytes: compressedBytes,
|
|
25119
|
+
compression_ratio: rawBytes > 0 ? Math.round((1 - compressedBytes / rawBytes) * 100) / 100 : 0,
|
|
25120
|
+
passages: sliced.map((p) => ({
|
|
25121
|
+
index: p.index,
|
|
25122
|
+
heading: p.heading,
|
|
25123
|
+
text: p.text,
|
|
25124
|
+
start_line: p.startLine
|
|
25125
|
+
})),
|
|
25126
|
+
total: allPassages.length
|
|
25127
|
+
};
|
|
25128
|
+
}
|
|
25129
|
+
function upgradeToHttps(url) {
|
|
25130
|
+
if (!url.startsWith("http://")) return url;
|
|
25131
|
+
try {
|
|
25132
|
+
const host = new URL(url).hostname;
|
|
25133
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1") {
|
|
25134
|
+
return url;
|
|
25135
|
+
}
|
|
25136
|
+
} catch {
|
|
25137
|
+
return url;
|
|
25138
|
+
}
|
|
25139
|
+
return "https://" + url.slice(7);
|
|
25140
|
+
}
|
|
25141
|
+
async function fetchHtml(url, abortSignal) {
|
|
25142
|
+
const controller = new AbortController();
|
|
25143
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
25144
|
+
const signal = abortSignal ? composeSignals(controller.signal, abortSignal) : controller.signal;
|
|
25145
|
+
try {
|
|
25146
|
+
const res = await fetch(url, {
|
|
25147
|
+
signal,
|
|
25148
|
+
redirect: "follow",
|
|
25149
|
+
headers: {
|
|
25150
|
+
"user-agent": "unerr-fetch-url/1.0 (+https://unerr.dev) Mozilla/5.0 compatible",
|
|
25151
|
+
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
25152
|
+
}
|
|
25153
|
+
});
|
|
25154
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
25155
|
+
if (!contentType.includes("html") && !contentType.includes("xml")) {
|
|
25156
|
+
throw new Error(
|
|
25157
|
+
`fetch_url expected HTML response, got content-type: ${contentType || "unknown"}`
|
|
25158
|
+
);
|
|
25159
|
+
}
|
|
25160
|
+
const reader = res.body?.getReader();
|
|
25161
|
+
if (!reader) {
|
|
25162
|
+
const text2 = await res.text();
|
|
25163
|
+
return capHtml({
|
|
25164
|
+
html: text2,
|
|
25165
|
+
finalUrl: res.url,
|
|
25166
|
+
status: res.status,
|
|
25167
|
+
contentType
|
|
25168
|
+
});
|
|
25169
|
+
}
|
|
25170
|
+
const chunks = [];
|
|
25171
|
+
let total = 0;
|
|
25172
|
+
for (; ; ) {
|
|
25173
|
+
const { done, value } = await reader.read();
|
|
25174
|
+
if (done) break;
|
|
25175
|
+
if (value) {
|
|
25176
|
+
total += value.byteLength;
|
|
25177
|
+
if (total > MAX_HTML_BYTES) {
|
|
25178
|
+
await reader.cancel();
|
|
25179
|
+
break;
|
|
25180
|
+
}
|
|
25181
|
+
chunks.push(value);
|
|
25182
|
+
}
|
|
25183
|
+
}
|
|
25184
|
+
const merged = concatBytes(chunks);
|
|
25185
|
+
const html = new TextDecoder("utf-8", { fatal: false }).decode(merged);
|
|
25186
|
+
return capHtml({
|
|
25187
|
+
html,
|
|
25188
|
+
finalUrl: res.url,
|
|
25189
|
+
status: res.status,
|
|
25190
|
+
contentType
|
|
25191
|
+
});
|
|
25192
|
+
} finally {
|
|
25193
|
+
clearTimeout(timer);
|
|
25194
|
+
}
|
|
25195
|
+
}
|
|
25196
|
+
function capHtml(fetched) {
|
|
25197
|
+
if (fetched.html.length > MAX_HTML_BYTES) {
|
|
25198
|
+
return { ...fetched, html: fetched.html.slice(0, MAX_HTML_BYTES) };
|
|
25199
|
+
}
|
|
25200
|
+
return fetched;
|
|
25201
|
+
}
|
|
25202
|
+
function composeSignals(a, b) {
|
|
25203
|
+
const controller = new AbortController();
|
|
25204
|
+
const onAbort = () => controller.abort();
|
|
25205
|
+
if (a.aborted || b.aborted) controller.abort();
|
|
25206
|
+
else {
|
|
25207
|
+
a.addEventListener("abort", onAbort, { once: true });
|
|
25208
|
+
b.addEventListener("abort", onAbort, { once: true });
|
|
25209
|
+
}
|
|
25210
|
+
return controller.signal;
|
|
25211
|
+
}
|
|
25212
|
+
function byteLength(s) {
|
|
25213
|
+
return Buffer.byteLength(s, "utf-8");
|
|
25214
|
+
}
|
|
25215
|
+
function safeLoadSettings(cwd) {
|
|
25216
|
+
try {
|
|
25217
|
+
return loadSettings(cwd);
|
|
25218
|
+
} catch {
|
|
25219
|
+
return null;
|
|
25220
|
+
}
|
|
25221
|
+
}
|
|
25222
|
+
function concatBytes(chunks) {
|
|
25223
|
+
let total = 0;
|
|
25224
|
+
for (const c of chunks) total += c.byteLength;
|
|
25225
|
+
const out = new Uint8Array(total);
|
|
25226
|
+
let offset = 0;
|
|
25227
|
+
for (const c of chunks) {
|
|
25228
|
+
out.set(c, offset);
|
|
25229
|
+
offset += c.byteLength;
|
|
25230
|
+
}
|
|
25231
|
+
return out;
|
|
25232
|
+
}
|
|
25233
|
+
var FETCH_TIMEOUT_MS, MAX_HTML_BYTES, BM25_GATE_BYTES, BM25_DEFAULT_TOPK;
|
|
25234
|
+
var init_fetch_url_protocol = __esm({
|
|
25235
|
+
"src/tools/web/fetch-url-protocol.ts"() {
|
|
25236
|
+
"use strict";
|
|
25237
|
+
init_settings();
|
|
25238
|
+
init_bm25_rank();
|
|
25239
|
+
init_diff_cache();
|
|
25240
|
+
init_extract();
|
|
25241
|
+
init_markdown();
|
|
25242
|
+
init_passage_split();
|
|
25243
|
+
init_post_process();
|
|
25244
|
+
init_spa_render();
|
|
25245
|
+
init_telemetry();
|
|
25246
|
+
FETCH_TIMEOUT_MS = 15e3;
|
|
25247
|
+
MAX_HTML_BYTES = 5 * 1024 * 1024;
|
|
25248
|
+
BM25_GATE_BYTES = 8 * 1024;
|
|
25249
|
+
BM25_DEFAULT_TOPK = 20;
|
|
25250
|
+
}
|
|
25251
|
+
});
|
|
25252
|
+
|
|
24312
25253
|
// src/intelligence/query-router.ts
|
|
24313
25254
|
var query_router_exports = {};
|
|
24314
25255
|
__export(query_router_exports, {
|
|
@@ -24564,7 +25505,9 @@ var init_query_router = __esm({
|
|
|
24564
25505
|
"get_test_coverage",
|
|
24565
25506
|
// Sprint FE-B: file read protocol
|
|
24566
25507
|
"file_outline",
|
|
24567
|
-
"file_read"
|
|
25508
|
+
"file_read",
|
|
25509
|
+
// Sprint FU-1: web fetch
|
|
25510
|
+
"fetch_url"
|
|
24568
25511
|
]);
|
|
24569
25512
|
ENRICHABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
24570
25513
|
// Agent-facing tools
|
|
@@ -25362,7 +26305,8 @@ var init_query_router = __esm({
|
|
|
25362
26305
|
const saved = estimate.tokensWithout - estimate.tokensUsed;
|
|
25363
26306
|
if (saved > 0) {
|
|
25364
26307
|
const isFileNav = toolName === "file_read" || toolName === "file_outline" || toolName === "get_file";
|
|
25365
|
-
const
|
|
26308
|
+
const isFetchUrl = toolName === "fetch_url";
|
|
26309
|
+
const mechanism = isFetchUrl ? "fetch_url" : isFileNav ? "file_read" : "graph_query";
|
|
25366
26310
|
enrichTokensSaved = saved;
|
|
25367
26311
|
enrichSavingsMechanism = mechanism;
|
|
25368
26312
|
const dollarSavings = calculateDollarSavings(saved);
|
|
@@ -26280,11 +27224,11 @@ var init_query_router = __esm({
|
|
|
26280
27224
|
const entity = await this.resolveEntityWithOverlay(key);
|
|
26281
27225
|
if (entity?.file_path && entity.start_line > 0) {
|
|
26282
27226
|
try {
|
|
26283
|
-
const { readFileSync:
|
|
27227
|
+
const { readFileSync: readFileSync71 } = await import("fs");
|
|
26284
27228
|
const { resolve: resolve6 } = await import("path");
|
|
26285
27229
|
const cwd = this.projectRoot ?? process.cwd();
|
|
26286
27230
|
const abs = resolve6(cwd, entity.file_path);
|
|
26287
|
-
const lines =
|
|
27231
|
+
const lines = readFileSync71(abs, "utf-8").split("\n");
|
|
26288
27232
|
const start = entity.start_line - 1;
|
|
26289
27233
|
const end = entity.end_line ?? lines.length;
|
|
26290
27234
|
const bodyLines = lines.slice(start, end);
|
|
@@ -26522,6 +27466,12 @@ var init_query_router = __esm({
|
|
|
26522
27466
|
graph: this.localGraph
|
|
26523
27467
|
});
|
|
26524
27468
|
}
|
|
27469
|
+
case "fetch_url": {
|
|
27470
|
+
const { runFetchUrl: runFetchUrl2 } = await Promise.resolve().then(() => (init_fetch_url_protocol(), fetch_url_protocol_exports));
|
|
27471
|
+
return runFetchUrl2(args, {
|
|
27472
|
+
cwd: this.projectRoot ?? process.cwd()
|
|
27473
|
+
});
|
|
27474
|
+
}
|
|
26525
27475
|
default:
|
|
26526
27476
|
throw new Error(`Unknown local tool: ${toolName}`);
|
|
26527
27477
|
}
|
|
@@ -27881,8 +28831,8 @@ __export(causal_bridge_exports, {
|
|
|
27881
28831
|
assembleCausalChain: () => assembleCausalChain,
|
|
27882
28832
|
computeDurability: () => computeDurability
|
|
27883
28833
|
});
|
|
27884
|
-
import { existsSync as existsSync59, readFileSync as
|
|
27885
|
-
import { join as
|
|
28834
|
+
import { existsSync as existsSync59, readFileSync as readFileSync52 } from "fs";
|
|
28835
|
+
import { join as join62 } from "path";
|
|
27886
28836
|
function computeAggregateDurability(interactions) {
|
|
27887
28837
|
if (interactions.length === 0) return 1;
|
|
27888
28838
|
const survivedCount = interactions.filter((i) => i.survived).length;
|
|
@@ -28039,9 +28989,9 @@ var init_causal_bridge = __esm({
|
|
|
28039
28989
|
return chains;
|
|
28040
28990
|
}
|
|
28041
28991
|
loadEntityLedgerEntries(entityKey2) {
|
|
28042
|
-
const ledgerPath =
|
|
28992
|
+
const ledgerPath = join62(this.unerrDir, "ledger", "shadow.jsonl");
|
|
28043
28993
|
if (!existsSync59(ledgerPath)) return [];
|
|
28044
|
-
const content =
|
|
28994
|
+
const content = readFileSync52(ledgerPath, "utf-8");
|
|
28045
28995
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
28046
28996
|
const entries = [];
|
|
28047
28997
|
for (const line of lines) {
|
|
@@ -28527,11 +29477,11 @@ var context_ledger_exports = {};
|
|
|
28527
29477
|
__export(context_ledger_exports, {
|
|
28528
29478
|
createContextLedger: () => createContextLedger
|
|
28529
29479
|
});
|
|
28530
|
-
import { existsSync as existsSync60, mkdirSync as mkdirSync32, readFileSync as
|
|
28531
|
-
import { join as
|
|
29480
|
+
import { existsSync as existsSync60, mkdirSync as mkdirSync32, readFileSync as readFileSync53, writeFileSync as writeFileSync27 } from "fs";
|
|
29481
|
+
import { join as join63 } from "path";
|
|
28532
29482
|
function createContextLedger(unerrDir2) {
|
|
28533
|
-
const stateDir =
|
|
28534
|
-
const filePath =
|
|
29483
|
+
const stateDir = join63(unerrDir2, "state");
|
|
29484
|
+
const filePath = join63(stateDir, "context-ledger.json");
|
|
28535
29485
|
let records = [];
|
|
28536
29486
|
let index = /* @__PURE__ */ new Map();
|
|
28537
29487
|
function ensureDir() {
|
|
@@ -28550,7 +29500,7 @@ function createContextLedger(unerrDir2) {
|
|
|
28550
29500
|
return /* @__PURE__ */ new Map();
|
|
28551
29501
|
}
|
|
28552
29502
|
try {
|
|
28553
|
-
const raw =
|
|
29503
|
+
const raw = readFileSync53(filePath, "utf-8");
|
|
28554
29504
|
const parsed = JSON.parse(raw);
|
|
28555
29505
|
records = Array.isArray(parsed) ? parsed : [];
|
|
28556
29506
|
} catch {
|
|
@@ -29477,11 +30427,11 @@ __export(intent_correlator_exports, {
|
|
|
29477
30427
|
import {
|
|
29478
30428
|
existsSync as existsSync61,
|
|
29479
30429
|
mkdirSync as mkdirSync33,
|
|
29480
|
-
readFileSync as
|
|
30430
|
+
readFileSync as readFileSync54,
|
|
29481
30431
|
renameSync as renameSync2,
|
|
29482
30432
|
writeFileSync as writeFileSync28
|
|
29483
30433
|
} from "fs";
|
|
29484
|
-
import { join as
|
|
30434
|
+
import { join as join64 } from "path";
|
|
29485
30435
|
function extractFiles4(args) {
|
|
29486
30436
|
const files = args.files;
|
|
29487
30437
|
if (files && Array.isArray(files)) {
|
|
@@ -29529,8 +30479,8 @@ var init_intent_correlator = __esm({
|
|
|
29529
30479
|
pendingPath;
|
|
29530
30480
|
pending = [];
|
|
29531
30481
|
constructor(unerrDir2) {
|
|
29532
|
-
this.ledgerDir =
|
|
29533
|
-
this.pendingPath =
|
|
30482
|
+
this.ledgerDir = join64(unerrDir2, "ledger");
|
|
30483
|
+
this.pendingPath = join64(this.ledgerDir, "pending_correlations.json");
|
|
29534
30484
|
if (!existsSync61(this.ledgerDir)) {
|
|
29535
30485
|
mkdirSync33(this.ledgerDir, { recursive: true });
|
|
29536
30486
|
}
|
|
@@ -29625,7 +30575,7 @@ var init_intent_correlator = __esm({
|
|
|
29625
30575
|
load() {
|
|
29626
30576
|
if (!existsSync61(this.pendingPath)) return;
|
|
29627
30577
|
try {
|
|
29628
|
-
const raw =
|
|
30578
|
+
const raw = readFileSync54(this.pendingPath, "utf-8");
|
|
29629
30579
|
const parsed = JSON.parse(raw);
|
|
29630
30580
|
if (Array.isArray(parsed)) {
|
|
29631
30581
|
this.pending = parsed;
|
|
@@ -30190,7 +31140,7 @@ var init_session_state = __esm({
|
|
|
30190
31140
|
|
|
30191
31141
|
// src/proxy/tool-exposure-store.ts
|
|
30192
31142
|
import { promises as fs6 } from "fs";
|
|
30193
|
-
import { dirname as dirname16, join as
|
|
31143
|
+
import { dirname as dirname16, join as join65 } from "path";
|
|
30194
31144
|
var ToolExposureStore;
|
|
30195
31145
|
var init_tool_exposure_store = __esm({
|
|
30196
31146
|
"src/proxy/tool-exposure-store.ts"() {
|
|
@@ -30200,7 +31150,7 @@ var init_tool_exposure_store = __esm({
|
|
|
30200
31150
|
sessionId;
|
|
30201
31151
|
ensuredDir = false;
|
|
30202
31152
|
constructor(unerrDir2, sessionId) {
|
|
30203
|
-
this.filePath =
|
|
31153
|
+
this.filePath = join65(unerrDir2, "router", "exposure-events.jsonl");
|
|
30204
31154
|
this.sessionId = sessionId;
|
|
30205
31155
|
}
|
|
30206
31156
|
/**
|
|
@@ -30462,11 +31412,11 @@ var init_router_gateway = __esm({
|
|
|
30462
31412
|
|
|
30463
31413
|
// src/timeline/timeline-store.ts
|
|
30464
31414
|
import { existsSync as existsSync62, mkdirSync as mkdirSync34 } from "fs";
|
|
30465
|
-
import { join as
|
|
31415
|
+
import { join as join66 } from "path";
|
|
30466
31416
|
async function openTimelineDb(projectRoot) {
|
|
30467
|
-
const unerrDir2 =
|
|
31417
|
+
const unerrDir2 = join66(projectRoot, ".unerr");
|
|
30468
31418
|
mkdirSync34(unerrDir2, { recursive: true });
|
|
30469
|
-
const dbPath =
|
|
31419
|
+
const dbPath = join66(unerrDir2, TIMELINE_DB_FILENAME);
|
|
30470
31420
|
const isNew = !existsSync62(dbPath);
|
|
30471
31421
|
const cozoModule = await import("cozo-node");
|
|
30472
31422
|
const CozoDbConstructor = cozoModule.default ? cozoModule.default.CozoDb : cozoModule.CozoDb;
|
|
@@ -31510,23 +32460,23 @@ import {
|
|
|
31510
32460
|
appendFileSync as appendFileSync8,
|
|
31511
32461
|
existsSync as existsSync63,
|
|
31512
32462
|
mkdirSync as mkdirSync35,
|
|
31513
|
-
readFileSync as
|
|
32463
|
+
readFileSync as readFileSync55,
|
|
31514
32464
|
renameSync as renameSync3,
|
|
31515
32465
|
writeFileSync as writeFileSync29
|
|
31516
32466
|
} from "fs";
|
|
31517
|
-
import { join as
|
|
32467
|
+
import { join as join67 } from "path";
|
|
31518
32468
|
import { gzipSync } from "zlib";
|
|
31519
32469
|
function archiveShadowLedger(unerrDir2, opts = {}) {
|
|
31520
|
-
const ledgerDir =
|
|
31521
|
-
const filePath =
|
|
31522
|
-
const archiveDir =
|
|
32470
|
+
const ledgerDir = join67(unerrDir2, "ledger");
|
|
32471
|
+
const filePath = join67(ledgerDir, "shadow.jsonl");
|
|
32472
|
+
const archiveDir = join67(ledgerDir, "archive");
|
|
31523
32473
|
if (!existsSync63(filePath)) {
|
|
31524
32474
|
return { archived: 0, kept: 0, archivePath: null };
|
|
31525
32475
|
}
|
|
31526
32476
|
const retainMs = opts.retainMs ?? DEFAULT_RETAIN_MS;
|
|
31527
32477
|
const now = opts.nowMs ?? Date.now();
|
|
31528
32478
|
const cutoff = now - retainMs;
|
|
31529
|
-
const raw =
|
|
32479
|
+
const raw = readFileSync55(filePath, "utf-8");
|
|
31530
32480
|
const initialBytes = Buffer.byteLength(raw);
|
|
31531
32481
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
31532
32482
|
const keep = [];
|
|
@@ -31551,7 +32501,7 @@ function archiveShadowLedger(unerrDir2, opts = {}) {
|
|
|
31551
32501
|
}
|
|
31552
32502
|
mkdirSync35(archiveDir, { recursive: true });
|
|
31553
32503
|
const dateStamp = new Date(now).toISOString().slice(0, 10);
|
|
31554
|
-
const archivePath =
|
|
32504
|
+
const archivePath = join67(archiveDir, `${dateStamp}.jsonl.gz`);
|
|
31555
32505
|
const payload = `${archive.join("\n")}
|
|
31556
32506
|
`;
|
|
31557
32507
|
const gz = gzipSync(Buffer.from(payload, "utf-8"));
|
|
@@ -31563,7 +32513,7 @@ function archiveShadowLedger(unerrDir2, opts = {}) {
|
|
|
31563
32513
|
const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
31564
32514
|
const kept = `${keep.join("\n")}${keep.length > 0 ? "\n" : ""}`;
|
|
31565
32515
|
writeFileSync29(tmpPath, kept, "utf-8");
|
|
31566
|
-
const afterRaw =
|
|
32516
|
+
const afterRaw = readFileSync55(filePath, "utf-8");
|
|
31567
32517
|
const afterBytes = Buffer.byteLength(afterRaw);
|
|
31568
32518
|
if (afterBytes > initialBytes) {
|
|
31569
32519
|
const tail = afterRaw.slice(raw.length);
|
|
@@ -32072,12 +33022,12 @@ import { randomBytes as randomBytes5 } from "crypto";
|
|
|
32072
33022
|
import {
|
|
32073
33023
|
existsSync as existsSync64,
|
|
32074
33024
|
mkdirSync as mkdirSync36,
|
|
32075
|
-
readFileSync as
|
|
32076
|
-
readdirSync as
|
|
33025
|
+
readFileSync as readFileSync56,
|
|
33026
|
+
readdirSync as readdirSync15,
|
|
32077
33027
|
rmSync as rmSync2,
|
|
32078
33028
|
writeFileSync as writeFileSync30
|
|
32079
33029
|
} from "fs";
|
|
32080
|
-
import { join as
|
|
33030
|
+
import { join as join68 } from "path";
|
|
32081
33031
|
var _log2, MAX_SNAPSHOTS, AUTO_SNAPSHOT_COOLDOWN_MS, WorkingSnapshotStore;
|
|
32082
33032
|
var init_working_snapshots = __esm({
|
|
32083
33033
|
"src/tracking/working-snapshots.ts"() {
|
|
@@ -32095,7 +33045,7 @@ var init_working_snapshots = __esm({
|
|
|
32095
33045
|
unerrDir;
|
|
32096
33046
|
constructor(unerrDir2) {
|
|
32097
33047
|
this.unerrDir = unerrDir2;
|
|
32098
|
-
this.snapshotDir =
|
|
33048
|
+
this.snapshotDir = join68(unerrDir2, "snapshots");
|
|
32099
33049
|
if (!existsSync64(this.snapshotDir)) {
|
|
32100
33050
|
mkdirSync36(this.snapshotDir, { recursive: true });
|
|
32101
33051
|
}
|
|
@@ -32116,7 +33066,7 @@ var init_working_snapshots = __esm({
|
|
|
32116
33066
|
processed: false
|
|
32117
33067
|
};
|
|
32118
33068
|
writeFileSync30(
|
|
32119
|
-
|
|
33069
|
+
join68(this.snapshotDir, `${id}.json`),
|
|
32120
33070
|
JSON.stringify(snapshot, null, 2),
|
|
32121
33071
|
"utf-8"
|
|
32122
33072
|
);
|
|
@@ -32130,10 +33080,10 @@ var init_working_snapshots = __esm({
|
|
|
32130
33080
|
* Get a snapshot by ID.
|
|
32131
33081
|
*/
|
|
32132
33082
|
get(snapshotId) {
|
|
32133
|
-
const filePath =
|
|
33083
|
+
const filePath = join68(this.snapshotDir, `${snapshotId}.json`);
|
|
32134
33084
|
if (!existsSync64(filePath)) return null;
|
|
32135
33085
|
try {
|
|
32136
|
-
return JSON.parse(
|
|
33086
|
+
return JSON.parse(readFileSync56(filePath, "utf-8"));
|
|
32137
33087
|
} catch {
|
|
32138
33088
|
return null;
|
|
32139
33089
|
}
|
|
@@ -32143,13 +33093,13 @@ var init_working_snapshots = __esm({
|
|
|
32143
33093
|
*/
|
|
32144
33094
|
list() {
|
|
32145
33095
|
if (!existsSync64(this.snapshotDir)) return [];
|
|
32146
|
-
const files =
|
|
33096
|
+
const files = readdirSync15(this.snapshotDir).filter(
|
|
32147
33097
|
(f) => f.endsWith(".json")
|
|
32148
33098
|
);
|
|
32149
33099
|
const snapshots = [];
|
|
32150
33100
|
for (const file of files) {
|
|
32151
33101
|
try {
|
|
32152
|
-
const raw =
|
|
33102
|
+
const raw = readFileSync56(join68(this.snapshotDir, file), "utf-8");
|
|
32153
33103
|
snapshots.push(JSON.parse(raw));
|
|
32154
33104
|
} catch {
|
|
32155
33105
|
}
|
|
@@ -32190,7 +33140,7 @@ var init_working_snapshots = __esm({
|
|
|
32190
33140
|
if (!snapshot) return;
|
|
32191
33141
|
snapshot.processed = true;
|
|
32192
33142
|
writeFileSync30(
|
|
32193
|
-
|
|
33143
|
+
join68(this.snapshotDir, `${snapshotId}.json`),
|
|
32194
33144
|
JSON.stringify(snapshot, null, 2),
|
|
32195
33145
|
"utf-8"
|
|
32196
33146
|
);
|
|
@@ -32205,7 +33155,7 @@ var init_working_snapshots = __esm({
|
|
|
32205
33155
|
* Delete a snapshot.
|
|
32206
33156
|
*/
|
|
32207
33157
|
delete(snapshotId) {
|
|
32208
|
-
const filePath =
|
|
33158
|
+
const filePath = join68(this.snapshotDir, `${snapshotId}.json`);
|
|
32209
33159
|
if (!existsSync64(filePath)) return false;
|
|
32210
33160
|
rmSync2(filePath);
|
|
32211
33161
|
return true;
|
|
@@ -32214,10 +33164,10 @@ var init_working_snapshots = __esm({
|
|
|
32214
33164
|
* Get the timeline branch counter from branch_context.json.
|
|
32215
33165
|
*/
|
|
32216
33166
|
getTimelineBranch() {
|
|
32217
|
-
const contextPath =
|
|
33167
|
+
const contextPath = join68(this.unerrDir, "branch_context.json");
|
|
32218
33168
|
if (!existsSync64(contextPath)) return 0;
|
|
32219
33169
|
try {
|
|
32220
|
-
const ctx = JSON.parse(
|
|
33170
|
+
const ctx = JSON.parse(readFileSync56(contextPath, "utf-8"));
|
|
32221
33171
|
return ctx.timelineBranch ?? 0;
|
|
32222
33172
|
} catch {
|
|
32223
33173
|
return 0;
|
|
@@ -32227,11 +33177,11 @@ var init_working_snapshots = __esm({
|
|
|
32227
33177
|
* Increment the timeline branch counter. Returns the new value.
|
|
32228
33178
|
*/
|
|
32229
33179
|
incrementTimelineBranch() {
|
|
32230
|
-
const contextPath =
|
|
33180
|
+
const contextPath = join68(this.unerrDir, "branch_context.json");
|
|
32231
33181
|
let ctx = {};
|
|
32232
33182
|
if (existsSync64(contextPath)) {
|
|
32233
33183
|
try {
|
|
32234
|
-
ctx = JSON.parse(
|
|
33184
|
+
ctx = JSON.parse(readFileSync56(contextPath, "utf-8"));
|
|
32235
33185
|
} catch {
|
|
32236
33186
|
ctx = {};
|
|
32237
33187
|
}
|
|
@@ -32404,8 +33354,8 @@ var quality_signals_exports = {};
|
|
|
32404
33354
|
__export(quality_signals_exports, {
|
|
32405
33355
|
QualitySignalTracker: () => QualitySignalTracker
|
|
32406
33356
|
});
|
|
32407
|
-
import { existsSync as existsSync65, readFileSync as
|
|
32408
|
-
import { join as
|
|
33357
|
+
import { existsSync as existsSync65, readFileSync as readFileSync57, writeFileSync as writeFileSync31 } from "fs";
|
|
33358
|
+
import { join as join69 } from "path";
|
|
32409
33359
|
function computeDurabilityFromAge(survivalMs) {
|
|
32410
33360
|
if (survivalMs < FRAGILE_THRESHOLD_MS) {
|
|
32411
33361
|
return 0.1 + survivalMs / FRAGILE_THRESHOLD_MS * 0.2;
|
|
@@ -32432,7 +33382,7 @@ var init_quality_signals = __esm({
|
|
|
32432
33382
|
/** Maximum corrections to retain in memory/disk. */
|
|
32433
33383
|
static MAX_CORRECTIONS = 200;
|
|
32434
33384
|
constructor(unerrDir2) {
|
|
32435
|
-
this.signalsPath =
|
|
33385
|
+
this.signalsPath = join69(unerrDir2, "state", "quality_signals.json");
|
|
32436
33386
|
this.signals = this.load();
|
|
32437
33387
|
}
|
|
32438
33388
|
/**
|
|
@@ -32524,7 +33474,7 @@ var init_quality_signals = __esm({
|
|
|
32524
33474
|
*/
|
|
32525
33475
|
save() {
|
|
32526
33476
|
try {
|
|
32527
|
-
const dir =
|
|
33477
|
+
const dir = join69(this.signalsPath, "..");
|
|
32528
33478
|
if (!existsSync65(dir)) {
|
|
32529
33479
|
const { mkdirSync: mkdirSync48 } = __require("fs");
|
|
32530
33480
|
mkdirSync48(dir, { recursive: true });
|
|
@@ -32580,7 +33530,7 @@ var init_quality_signals = __esm({
|
|
|
32580
33530
|
}
|
|
32581
33531
|
try {
|
|
32582
33532
|
return JSON.parse(
|
|
32583
|
-
|
|
33533
|
+
readFileSync57(this.signalsPath, "utf-8")
|
|
32584
33534
|
);
|
|
32585
33535
|
} catch {
|
|
32586
33536
|
return {
|
|
@@ -33599,8 +34549,8 @@ var incomplete_work_exports = {};
|
|
|
33599
34549
|
__export(incomplete_work_exports, {
|
|
33600
34550
|
IncompleteWorkDetector: () => IncompleteWorkDetector
|
|
33601
34551
|
});
|
|
33602
|
-
import { existsSync as existsSync66, mkdirSync as mkdirSync37, readFileSync as
|
|
33603
|
-
import { join as
|
|
34552
|
+
import { existsSync as existsSync66, mkdirSync as mkdirSync37, readFileSync as readFileSync58, writeFileSync as writeFileSync32 } from "fs";
|
|
34553
|
+
import { join as join70 } from "path";
|
|
33604
34554
|
function severityRank(severity) {
|
|
33605
34555
|
switch (severity) {
|
|
33606
34556
|
case "high":
|
|
@@ -33793,9 +34743,9 @@ var init_incomplete_work = __esm({
|
|
|
33793
34743
|
persistItems(items) {
|
|
33794
34744
|
if (!this.unerrDir) return false;
|
|
33795
34745
|
try {
|
|
33796
|
-
const stateDir =
|
|
34746
|
+
const stateDir = join70(this.unerrDir, "state");
|
|
33797
34747
|
if (!existsSync66(stateDir)) mkdirSync37(stateDir, { recursive: true });
|
|
33798
|
-
const filePath =
|
|
34748
|
+
const filePath = join70(stateDir, PERSISTENCE_FILE);
|
|
33799
34749
|
writeFileSync32(
|
|
33800
34750
|
filePath,
|
|
33801
34751
|
JSON.stringify({
|
|
@@ -33815,9 +34765,9 @@ var init_incomplete_work = __esm({
|
|
|
33815
34765
|
*/
|
|
33816
34766
|
static readPersistedItems(unerrDir2) {
|
|
33817
34767
|
try {
|
|
33818
|
-
const filePath =
|
|
34768
|
+
const filePath = join70(unerrDir2, "state", PERSISTENCE_FILE);
|
|
33819
34769
|
if (!existsSync66(filePath)) return [];
|
|
33820
|
-
const data = JSON.parse(
|
|
34770
|
+
const data = JSON.parse(readFileSync58(filePath, "utf-8"));
|
|
33821
34771
|
return data.items ?? [];
|
|
33822
34772
|
} catch {
|
|
33823
34773
|
return [];
|
|
@@ -35597,8 +36547,8 @@ __export(git_trailers_exports, {
|
|
|
35597
36547
|
parseTrailersFromMessage: () => parseTrailersFromMessage,
|
|
35598
36548
|
uninstallPrepareCommitMsgHook: () => uninstallPrepareCommitMsgHook
|
|
35599
36549
|
});
|
|
35600
|
-
import { existsSync as existsSync68, readFileSync as
|
|
35601
|
-
import { join as
|
|
36550
|
+
import { existsSync as existsSync68, readFileSync as readFileSync59, writeFileSync as writeFileSync33 } from "fs";
|
|
36551
|
+
import { join as join71 } from "path";
|
|
35602
36552
|
function getCommitTrailers(ledger, timelineBranch, branch) {
|
|
35603
36553
|
const recent = ledger.getRecentEntries(1);
|
|
35604
36554
|
if (recent.length === 0) return null;
|
|
@@ -35618,15 +36568,15 @@ function formatTrailers(trailers) {
|
|
|
35618
36568
|
].join("\n");
|
|
35619
36569
|
}
|
|
35620
36570
|
function installPrepareCommitMsgHook(projectRoot) {
|
|
35621
|
-
const hooksDir =
|
|
36571
|
+
const hooksDir = join71(projectRoot, ".git", "hooks");
|
|
35622
36572
|
if (!existsSync68(hooksDir)) {
|
|
35623
36573
|
return false;
|
|
35624
36574
|
}
|
|
35625
|
-
const hookPath =
|
|
36575
|
+
const hookPath = join71(hooksDir, "prepare-commit-msg");
|
|
35626
36576
|
const marker = "# unerr-trailer-injection";
|
|
35627
36577
|
if (existsSync68(hookPath)) {
|
|
35628
36578
|
try {
|
|
35629
|
-
const existing =
|
|
36579
|
+
const existing = readFileSync59(hookPath, "utf-8");
|
|
35630
36580
|
if (existing.includes(marker)) {
|
|
35631
36581
|
return true;
|
|
35632
36582
|
}
|
|
@@ -35655,10 +36605,10 @@ ${generateHookScript()}`;
|
|
|
35655
36605
|
}
|
|
35656
36606
|
}
|
|
35657
36607
|
function uninstallPrepareCommitMsgHook(projectRoot) {
|
|
35658
|
-
const hookPath =
|
|
36608
|
+
const hookPath = join71(projectRoot, ".git", "hooks", "prepare-commit-msg");
|
|
35659
36609
|
if (!existsSync68(hookPath)) return true;
|
|
35660
36610
|
try {
|
|
35661
|
-
const content =
|
|
36611
|
+
const content = readFileSync59(hookPath, "utf-8");
|
|
35662
36612
|
const marker = "# unerr-trailer-injection";
|
|
35663
36613
|
if (!content.includes(marker)) return true;
|
|
35664
36614
|
const lines = content.split("\n");
|
|
@@ -35825,13 +36775,13 @@ var init_http_transport = __esm({
|
|
|
35825
36775
|
import {
|
|
35826
36776
|
existsSync as existsSync69,
|
|
35827
36777
|
mkdirSync as mkdirSync39,
|
|
35828
|
-
readFileSync as
|
|
35829
|
-
readdirSync as
|
|
36778
|
+
readFileSync as readFileSync60,
|
|
36779
|
+
readdirSync as readdirSync16,
|
|
35830
36780
|
rmSync as rmSync3,
|
|
35831
36781
|
statSync as statSync11,
|
|
35832
36782
|
writeFileSync as writeFileSync34
|
|
35833
36783
|
} from "fs";
|
|
35834
|
-
import { join as
|
|
36784
|
+
import { join as join72 } from "path";
|
|
35835
36785
|
function sanitizeBranchName(branch) {
|
|
35836
36786
|
return branch.replace(/\//g, "__").replace(/[^a-zA-Z0-9_.\-]/g, "_");
|
|
35837
36787
|
}
|
|
@@ -35849,7 +36799,7 @@ var init_branch_snapshot = __esm({
|
|
|
35849
36799
|
branchDir;
|
|
35850
36800
|
projectRoot;
|
|
35851
36801
|
constructor(unerrDir2, projectRoot) {
|
|
35852
|
-
this.branchDir =
|
|
36802
|
+
this.branchDir = join72(unerrDir2, "drift", "branches");
|
|
35853
36803
|
this.projectRoot = projectRoot;
|
|
35854
36804
|
}
|
|
35855
36805
|
/**
|
|
@@ -35863,7 +36813,7 @@ var init_branch_snapshot = __esm({
|
|
|
35863
36813
|
log17.info(`No drift entities/edges to snapshot for branch ${branch}`);
|
|
35864
36814
|
}
|
|
35865
36815
|
const dirName = sanitizeBranchName(branch);
|
|
35866
|
-
const snapshotDir =
|
|
36816
|
+
const snapshotDir = join72(this.branchDir, dirName);
|
|
35867
36817
|
if (!existsSync69(snapshotDir)) {
|
|
35868
36818
|
mkdirSync39(snapshotDir, { recursive: true });
|
|
35869
36819
|
}
|
|
@@ -35875,12 +36825,12 @@ var init_branch_snapshot = __esm({
|
|
|
35875
36825
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35876
36826
|
};
|
|
35877
36827
|
writeFileSync34(
|
|
35878
|
-
|
|
36828
|
+
join72(snapshotDir, OVERLAY_FILE),
|
|
35879
36829
|
JSON.stringify(snapshot, null, 2),
|
|
35880
36830
|
"utf-8"
|
|
35881
36831
|
);
|
|
35882
36832
|
writeFileSync34(
|
|
35883
|
-
|
|
36833
|
+
join72(snapshotDir, HASHES_FILE),
|
|
35884
36834
|
JSON.stringify(fileHashState, null, 2),
|
|
35885
36835
|
"utf-8"
|
|
35886
36836
|
);
|
|
@@ -35896,14 +36846,14 @@ var init_branch_snapshot = __esm({
|
|
|
35896
36846
|
*/
|
|
35897
36847
|
async restoreSnapshot(branch, localGraph) {
|
|
35898
36848
|
const dirName = sanitizeBranchName(branch);
|
|
35899
|
-
const snapshotDir =
|
|
35900
|
-
const overlayPath =
|
|
36849
|
+
const snapshotDir = join72(this.branchDir, dirName);
|
|
36850
|
+
const overlayPath = join72(snapshotDir, OVERLAY_FILE);
|
|
35901
36851
|
if (!existsSync69(overlayPath)) {
|
|
35902
36852
|
log17.info(`No snapshot for branch ${branch} \u2014 first visit`);
|
|
35903
36853
|
return null;
|
|
35904
36854
|
}
|
|
35905
36855
|
try {
|
|
35906
|
-
const raw =
|
|
36856
|
+
const raw = readFileSync60(overlayPath, "utf-8");
|
|
35907
36857
|
const snapshot = JSON.parse(raw);
|
|
35908
36858
|
for (const entity of snapshot.entities) {
|
|
35909
36859
|
await localGraph.upsertDriftEntity(entity);
|
|
@@ -35915,7 +36865,7 @@ var init_branch_snapshot = __esm({
|
|
|
35915
36865
|
}
|
|
35916
36866
|
const now = /* @__PURE__ */ new Date();
|
|
35917
36867
|
writeFileSync34(
|
|
35918
|
-
|
|
36868
|
+
join72(snapshotDir, ".last_access"),
|
|
35919
36869
|
now.toISOString(),
|
|
35920
36870
|
"utf-8"
|
|
35921
36871
|
);
|
|
@@ -35935,17 +36885,17 @@ var init_branch_snapshot = __esm({
|
|
|
35935
36885
|
*/
|
|
35936
36886
|
hasSnapshot(branch) {
|
|
35937
36887
|
const dirName = sanitizeBranchName(branch);
|
|
35938
|
-
return existsSync69(
|
|
36888
|
+
return existsSync69(join72(this.branchDir, dirName, OVERLAY_FILE));
|
|
35939
36889
|
}
|
|
35940
36890
|
/**
|
|
35941
36891
|
* Get the file hash state from a branch snapshot.
|
|
35942
36892
|
*/
|
|
35943
36893
|
getSnapshotFileHashes(branch) {
|
|
35944
36894
|
const dirName = sanitizeBranchName(branch);
|
|
35945
|
-
const hashesPath =
|
|
36895
|
+
const hashesPath = join72(this.branchDir, dirName, HASHES_FILE);
|
|
35946
36896
|
if (!existsSync69(hashesPath)) return null;
|
|
35947
36897
|
try {
|
|
35948
|
-
const raw =
|
|
36898
|
+
const raw = readFileSync60(hashesPath, "utf-8");
|
|
35949
36899
|
return JSON.parse(raw);
|
|
35950
36900
|
} catch {
|
|
35951
36901
|
return null;
|
|
@@ -35956,7 +36906,7 @@ var init_branch_snapshot = __esm({
|
|
|
35956
36906
|
*/
|
|
35957
36907
|
deleteSnapshot(branch) {
|
|
35958
36908
|
const dirName = sanitizeBranchName(branch);
|
|
35959
|
-
const snapshotDir =
|
|
36909
|
+
const snapshotDir = join72(this.branchDir, dirName);
|
|
35960
36910
|
if (!existsSync69(snapshotDir)) return false;
|
|
35961
36911
|
rmSync3(snapshotDir, { recursive: true, force: true });
|
|
35962
36912
|
log17.info(`Deleted branch snapshot: ${branch}`);
|
|
@@ -35973,7 +36923,7 @@ var init_branch_snapshot = __esm({
|
|
|
35973
36923
|
let removed = 0;
|
|
35974
36924
|
for (const snapshot of snapshots) {
|
|
35975
36925
|
if (!gitBranches.has(snapshot.branch)) {
|
|
35976
|
-
const snapshotDir =
|
|
36926
|
+
const snapshotDir = join72(this.branchDir, snapshot.id);
|
|
35977
36927
|
rmSync3(snapshotDir, { recursive: true, force: true });
|
|
35978
36928
|
log17.info(`GC removed snapshot for deleted branch: ${snapshot.branch}`);
|
|
35979
36929
|
removed++;
|
|
@@ -35987,16 +36937,16 @@ var init_branch_snapshot = __esm({
|
|
|
35987
36937
|
listSnapshots() {
|
|
35988
36938
|
if (!existsSync69(this.branchDir)) return [];
|
|
35989
36939
|
try {
|
|
35990
|
-
const entries =
|
|
36940
|
+
const entries = readdirSync16(this.branchDir, { withFileTypes: true });
|
|
35991
36941
|
const snapshots = [];
|
|
35992
36942
|
for (const entry of entries) {
|
|
35993
36943
|
if (!entry.isDirectory()) continue;
|
|
35994
|
-
const overlayPath =
|
|
36944
|
+
const overlayPath = join72(this.branchDir, entry.name, OVERLAY_FILE);
|
|
35995
36945
|
if (!existsSync69(overlayPath)) continue;
|
|
35996
36946
|
try {
|
|
35997
|
-
const raw =
|
|
36947
|
+
const raw = readFileSync60(overlayPath, "utf-8");
|
|
35998
36948
|
const snapshot = JSON.parse(raw);
|
|
35999
|
-
const accessPath =
|
|
36949
|
+
const accessPath = join72(this.branchDir, entry.name, ".last_access");
|
|
36000
36950
|
let accessedAt;
|
|
36001
36951
|
if (existsSync69(accessPath)) {
|
|
36002
36952
|
accessedAt = statSync11(accessPath).mtime;
|
|
@@ -36025,7 +36975,7 @@ var init_branch_snapshot = __esm({
|
|
|
36025
36975
|
if (snapshots.length <= MAX_BRANCH_SNAPSHOTS) return;
|
|
36026
36976
|
const toRemove = snapshots.slice(MAX_BRANCH_SNAPSHOTS);
|
|
36027
36977
|
for (const snapshot of toRemove) {
|
|
36028
|
-
const dir =
|
|
36978
|
+
const dir = join72(this.branchDir, snapshot.id);
|
|
36029
36979
|
rmSync3(dir, { recursive: true, force: true });
|
|
36030
36980
|
log17.info(`LRU evicted branch snapshot: ${snapshot.branch}`);
|
|
36031
36981
|
}
|
|
@@ -36040,17 +36990,17 @@ __export(file_hash_state_exports, {
|
|
|
36040
36990
|
FileHashManager: () => FileHashManager,
|
|
36041
36991
|
contentSha256: () => contentSha256
|
|
36042
36992
|
});
|
|
36043
|
-
import { createHash as
|
|
36993
|
+
import { createHash as createHash4 } from "crypto";
|
|
36044
36994
|
import {
|
|
36045
36995
|
existsSync as existsSync70,
|
|
36046
36996
|
mkdirSync as mkdirSync40,
|
|
36047
|
-
readFileSync as
|
|
36997
|
+
readFileSync as readFileSync61,
|
|
36048
36998
|
renameSync as renameSync4,
|
|
36049
36999
|
writeFileSync as writeFileSync35
|
|
36050
37000
|
} from "fs";
|
|
36051
|
-
import { join as
|
|
37001
|
+
import { join as join73 } from "path";
|
|
36052
37002
|
function contentSha256(content) {
|
|
36053
|
-
return
|
|
37003
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
36054
37004
|
}
|
|
36055
37005
|
var STATE_FILE2, FileHashManager;
|
|
36056
37006
|
var init_file_hash_state = __esm({
|
|
@@ -36062,8 +37012,8 @@ var init_file_hash_state = __esm({
|
|
|
36062
37012
|
statePath;
|
|
36063
37013
|
state;
|
|
36064
37014
|
constructor(unerrDir2) {
|
|
36065
|
-
this.stateDir =
|
|
36066
|
-
this.statePath =
|
|
37015
|
+
this.stateDir = join73(unerrDir2, "state");
|
|
37016
|
+
this.statePath = join73(this.stateDir, STATE_FILE2);
|
|
36067
37017
|
this.state = this.load();
|
|
36068
37018
|
}
|
|
36069
37019
|
/**
|
|
@@ -36139,7 +37089,7 @@ var init_file_hash_state = __esm({
|
|
|
36139
37089
|
return { files: {} };
|
|
36140
37090
|
}
|
|
36141
37091
|
try {
|
|
36142
|
-
const raw =
|
|
37092
|
+
const raw = readFileSync61(this.statePath, "utf-8");
|
|
36143
37093
|
return JSON.parse(raw);
|
|
36144
37094
|
} catch {
|
|
36145
37095
|
return { files: {} };
|
|
@@ -36153,13 +37103,13 @@ var init_file_hash_state = __esm({
|
|
|
36153
37103
|
import {
|
|
36154
37104
|
existsSync as existsSync71,
|
|
36155
37105
|
mkdirSync as mkdirSync41,
|
|
36156
|
-
readFileSync as
|
|
36157
|
-
readdirSync as
|
|
37106
|
+
readFileSync as readFileSync62,
|
|
37107
|
+
readdirSync as readdirSync17,
|
|
36158
37108
|
rmSync as rmSync4,
|
|
36159
37109
|
statSync as statSync12,
|
|
36160
37110
|
writeFileSync as writeFileSync36
|
|
36161
37111
|
} from "fs";
|
|
36162
|
-
import { join as
|
|
37112
|
+
import { join as join74 } from "path";
|
|
36163
37113
|
var MAX_STASH_SNAPSHOTS, OVERLAY_FILE2, HASHES_FILE2, _log6, StashManager;
|
|
36164
37114
|
var init_stash_manager = __esm({
|
|
36165
37115
|
"src/tracking/stash-manager.ts"() {
|
|
@@ -36177,8 +37127,8 @@ var init_stash_manager = __esm({
|
|
|
36177
37127
|
constructor(unerrDir2, projectRoot) {
|
|
36178
37128
|
this.unerrDir = unerrDir2;
|
|
36179
37129
|
this.projectRoot = projectRoot;
|
|
36180
|
-
this.stashDir =
|
|
36181
|
-
this.gitDir =
|
|
37130
|
+
this.stashDir = join74(unerrDir2, "drift", "stash");
|
|
37131
|
+
this.gitDir = join74(projectRoot, ".git");
|
|
36182
37132
|
this.previousStashRef = this.readStashRef();
|
|
36183
37133
|
this.previousStashCount = this.getStashCount();
|
|
36184
37134
|
}
|
|
@@ -36223,7 +37173,7 @@ var init_stash_manager = __esm({
|
|
|
36223
37173
|
return null;
|
|
36224
37174
|
}
|
|
36225
37175
|
const snapshotId = stashRef.slice(0, 12);
|
|
36226
|
-
const snapshotDir =
|
|
37176
|
+
const snapshotDir = join74(this.stashDir, snapshotId);
|
|
36227
37177
|
if (!existsSync71(snapshotDir)) {
|
|
36228
37178
|
mkdirSync41(snapshotDir, { recursive: true });
|
|
36229
37179
|
}
|
|
@@ -36235,12 +37185,12 @@ var init_stash_manager = __esm({
|
|
|
36235
37185
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
36236
37186
|
};
|
|
36237
37187
|
writeFileSync36(
|
|
36238
|
-
|
|
37188
|
+
join74(snapshotDir, OVERLAY_FILE2),
|
|
36239
37189
|
JSON.stringify(snapshot, null, 2),
|
|
36240
37190
|
"utf-8"
|
|
36241
37191
|
);
|
|
36242
37192
|
writeFileSync36(
|
|
36243
|
-
|
|
37193
|
+
join74(snapshotDir, HASHES_FILE2),
|
|
36244
37194
|
JSON.stringify(fileHashState, null, 2),
|
|
36245
37195
|
"utf-8"
|
|
36246
37196
|
);
|
|
@@ -36261,14 +37211,14 @@ var init_stash_manager = __esm({
|
|
|
36261
37211
|
return 0;
|
|
36262
37212
|
}
|
|
36263
37213
|
const latest = snapshots[0];
|
|
36264
|
-
const snapshotDir =
|
|
36265
|
-
const overlayPath =
|
|
37214
|
+
const snapshotDir = join74(this.stashDir, latest.id);
|
|
37215
|
+
const overlayPath = join74(snapshotDir, OVERLAY_FILE2);
|
|
36266
37216
|
if (!existsSync71(overlayPath)) {
|
|
36267
37217
|
_log6.warn(`Snapshot ${latest.id} missing overlay file`);
|
|
36268
37218
|
return 0;
|
|
36269
37219
|
}
|
|
36270
37220
|
try {
|
|
36271
|
-
const raw =
|
|
37221
|
+
const raw = readFileSync62(overlayPath, "utf-8");
|
|
36272
37222
|
const snapshot = JSON.parse(raw);
|
|
36273
37223
|
for (const entity of snapshot.entities) {
|
|
36274
37224
|
await localGraph.upsertDriftEntity(entity);
|
|
@@ -36298,10 +37248,10 @@ var init_stash_manager = __esm({
|
|
|
36298
37248
|
const snapshots = this.listSnapshots();
|
|
36299
37249
|
if (snapshots.length === 0) return null;
|
|
36300
37250
|
const latest = snapshots[0];
|
|
36301
|
-
const hashesPath =
|
|
37251
|
+
const hashesPath = join74(this.stashDir, latest.id, HASHES_FILE2);
|
|
36302
37252
|
if (!existsSync71(hashesPath)) return null;
|
|
36303
37253
|
try {
|
|
36304
|
-
const raw =
|
|
37254
|
+
const raw = readFileSync62(hashesPath, "utf-8");
|
|
36305
37255
|
return JSON.parse(raw);
|
|
36306
37256
|
} catch {
|
|
36307
37257
|
return null;
|
|
@@ -36312,7 +37262,7 @@ var init_stash_manager = __esm({
|
|
|
36312
37262
|
*/
|
|
36313
37263
|
dropSnapshot(stashRef) {
|
|
36314
37264
|
const snapshotId = stashRef.slice(0, 12);
|
|
36315
|
-
const snapshotDir =
|
|
37265
|
+
const snapshotDir = join74(this.stashDir, snapshotId);
|
|
36316
37266
|
if (!existsSync71(snapshotDir)) return false;
|
|
36317
37267
|
rmSync4(snapshotDir, { recursive: true, force: true });
|
|
36318
37268
|
_log6.info(`Dropped stash snapshot: ${snapshotId}`);
|
|
@@ -36324,11 +37274,11 @@ var init_stash_manager = __esm({
|
|
|
36324
37274
|
listSnapshots() {
|
|
36325
37275
|
if (!existsSync71(this.stashDir)) return [];
|
|
36326
37276
|
try {
|
|
36327
|
-
const entries =
|
|
37277
|
+
const entries = readdirSync17(this.stashDir, { withFileTypes: true });
|
|
36328
37278
|
const snapshots = [];
|
|
36329
37279
|
for (const entry of entries) {
|
|
36330
37280
|
if (!entry.isDirectory()) continue;
|
|
36331
|
-
const overlayPath =
|
|
37281
|
+
const overlayPath = join74(this.stashDir, entry.name, OVERLAY_FILE2);
|
|
36332
37282
|
if (!existsSync71(overlayPath)) continue;
|
|
36333
37283
|
try {
|
|
36334
37284
|
const stat2 = statSync12(overlayPath);
|
|
@@ -36346,10 +37296,10 @@ var init_stash_manager = __esm({
|
|
|
36346
37296
|
* Read the current stash ref SHA from `.git/refs/stash`.
|
|
36347
37297
|
*/
|
|
36348
37298
|
readStashRef() {
|
|
36349
|
-
const stashPath =
|
|
37299
|
+
const stashPath = join74(this.gitDir, "refs", "stash");
|
|
36350
37300
|
if (!existsSync71(stashPath)) return null;
|
|
36351
37301
|
try {
|
|
36352
|
-
return
|
|
37302
|
+
return readFileSync62(stashPath, "utf-8").trim() || null;
|
|
36353
37303
|
} catch {
|
|
36354
37304
|
return null;
|
|
36355
37305
|
}
|
|
@@ -36358,10 +37308,10 @@ var init_stash_manager = __esm({
|
|
|
36358
37308
|
* Count current stash entries via `.git/logs/refs/stash`.
|
|
36359
37309
|
*/
|
|
36360
37310
|
getStashCount() {
|
|
36361
|
-
const logPath =
|
|
37311
|
+
const logPath = join74(this.gitDir, "logs", "refs", "stash");
|
|
36362
37312
|
if (!existsSync71(logPath)) return 0;
|
|
36363
37313
|
try {
|
|
36364
|
-
const content =
|
|
37314
|
+
const content = readFileSync62(logPath, "utf-8");
|
|
36365
37315
|
return content.split("\n").filter((line) => line.trim().length > 0).length;
|
|
36366
37316
|
} catch {
|
|
36367
37317
|
return 0;
|
|
@@ -36375,7 +37325,7 @@ var init_stash_manager = __esm({
|
|
|
36375
37325
|
if (snapshots.length <= MAX_STASH_SNAPSHOTS) return;
|
|
36376
37326
|
const toRemove = snapshots.slice(MAX_STASH_SNAPSHOTS);
|
|
36377
37327
|
for (const snapshot of toRemove) {
|
|
36378
|
-
const dir =
|
|
37328
|
+
const dir = join74(this.stashDir, snapshot.id);
|
|
36379
37329
|
rmSync4(dir, { recursive: true, force: true });
|
|
36380
37330
|
_log6.info(`LRU evicted stash snapshot: ${snapshot.id}`);
|
|
36381
37331
|
}
|
|
@@ -36395,11 +37345,11 @@ __export(drift_tracker_exports, {
|
|
|
36395
37345
|
import {
|
|
36396
37346
|
existsSync as existsSync72,
|
|
36397
37347
|
mkdirSync as mkdirSync42,
|
|
36398
|
-
readFileSync as
|
|
37348
|
+
readFileSync as readFileSync63,
|
|
36399
37349
|
statSync as statSync13,
|
|
36400
37350
|
writeFileSync as writeFileSync37
|
|
36401
37351
|
} from "fs";
|
|
36402
|
-
import { join as
|
|
37352
|
+
import { join as join75 } from "path";
|
|
36403
37353
|
function determineOrigin(lastSyncTimestamp) {
|
|
36404
37354
|
if (lastSyncTimestamp === 0) return "human";
|
|
36405
37355
|
const elapsed = Date.now() - lastSyncTimestamp;
|
|
@@ -36610,7 +37560,7 @@ var init_drift_tracker = __esm({
|
|
|
36610
37560
|
crossFileInvalidated: 0,
|
|
36611
37561
|
edgesExtracted: 0
|
|
36612
37562
|
};
|
|
36613
|
-
const absPath = filePath.startsWith("/") ? filePath :
|
|
37563
|
+
const absPath = filePath.startsWith("/") ? filePath : join75(this.config.projectRoot, filePath);
|
|
36614
37564
|
const relPath = filePath.startsWith("/") ? filePath.slice(this.config.projectRoot.length + 1) : filePath;
|
|
36615
37565
|
const language = detectLanguage2(relPath);
|
|
36616
37566
|
if (!language) return result;
|
|
@@ -36656,7 +37606,7 @@ var init_drift_tracker = __esm({
|
|
|
36656
37606
|
this.maybeEmitDrift(relPath, result);
|
|
36657
37607
|
return result;
|
|
36658
37608
|
}
|
|
36659
|
-
const content =
|
|
37609
|
+
const content = readFileSync63(absPath, "utf-8");
|
|
36660
37610
|
const sha = contentSha256(content);
|
|
36661
37611
|
const decision = this.fileHashManager.shouldProcess(relPath, sha, headSha);
|
|
36662
37612
|
if (decision === "skip") {
|
|
@@ -36890,7 +37840,7 @@ var init_drift_tracker = __esm({
|
|
|
36890
37840
|
return await this.stashManager.restoreSnapshot(this.localGraph);
|
|
36891
37841
|
}
|
|
36892
37842
|
async markFileDeleted(filePath, intentId) {
|
|
36893
|
-
const absPath = filePath.startsWith("/") ? filePath :
|
|
37843
|
+
const absPath = filePath.startsWith("/") ? filePath : join75(this.config.projectRoot, filePath);
|
|
36894
37844
|
this.mtimeCache.evict(absPath);
|
|
36895
37845
|
const baseEntities = await this.localGraph.getEntitiesByFile(filePath);
|
|
36896
37846
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -36975,13 +37925,13 @@ var init_drift_tracker = __esm({
|
|
|
36975
37925
|
return invalidated;
|
|
36976
37926
|
}
|
|
36977
37927
|
async saveDriftSummary() {
|
|
36978
|
-
const driftDir =
|
|
37928
|
+
const driftDir = join75(this.config.unerrDir, "drift");
|
|
36979
37929
|
if (!existsSync72(driftDir)) {
|
|
36980
37930
|
mkdirSync42(driftDir, { recursive: true });
|
|
36981
37931
|
}
|
|
36982
37932
|
const summary = await this.getDriftSummary();
|
|
36983
37933
|
writeFileSync37(
|
|
36984
|
-
|
|
37934
|
+
join75(driftDir, "drift_summary.json"),
|
|
36985
37935
|
JSON.stringify(summary, null, 2),
|
|
36986
37936
|
"utf-8"
|
|
36987
37937
|
);
|
|
@@ -37392,8 +38342,8 @@ var incremental_indexer_exports = {};
|
|
|
37392
38342
|
__export(incremental_indexer_exports, {
|
|
37393
38343
|
indexFilesIncremental: () => indexFilesIncremental
|
|
37394
38344
|
});
|
|
37395
|
-
import { existsSync as existsSync73, readFileSync as
|
|
37396
|
-
import { join as
|
|
38345
|
+
import { existsSync as existsSync73, readFileSync as readFileSync64 } from "fs";
|
|
38346
|
+
import { join as join76, relative as relative5 } from "path";
|
|
37397
38347
|
async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repoId) {
|
|
37398
38348
|
const startMs = Date.now();
|
|
37399
38349
|
const db = {
|
|
@@ -37411,7 +38361,7 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
|
|
|
37411
38361
|
const changedEntityKeys = /* @__PURE__ */ new Set();
|
|
37412
38362
|
const deletedEntityKeys = /* @__PURE__ */ new Set();
|
|
37413
38363
|
for (const filePath of changedFiles) {
|
|
37414
|
-
const absPath = filePath.startsWith("/") ? filePath :
|
|
38364
|
+
const absPath = filePath.startsWith("/") ? filePath : join76(projectRoot, filePath);
|
|
37415
38365
|
const relPath = filePath.startsWith("/") ? relative5(projectRoot, filePath) : filePath;
|
|
37416
38366
|
if (!existsSync73(absPath)) {
|
|
37417
38367
|
const deleted2 = await deleteFileFromGraph(db, relPath);
|
|
@@ -37426,7 +38376,7 @@ async function indexFilesIncremental(projectRoot, changedFiles, graphStore, repo
|
|
|
37426
38376
|
}
|
|
37427
38377
|
let content;
|
|
37428
38378
|
try {
|
|
37429
|
-
content =
|
|
38379
|
+
content = readFileSync64(absPath, "utf-8");
|
|
37430
38380
|
} catch {
|
|
37431
38381
|
continue;
|
|
37432
38382
|
}
|
|
@@ -38604,8 +39554,8 @@ var workspace_manifest_exports = {};
|
|
|
38604
39554
|
__export(workspace_manifest_exports, {
|
|
38605
39555
|
WorkspaceManifest: () => WorkspaceManifest
|
|
38606
39556
|
});
|
|
38607
|
-
import { existsSync as existsSync74, mkdirSync as mkdirSync43, readFileSync as
|
|
38608
|
-
import { join as
|
|
39557
|
+
import { existsSync as existsSync74, mkdirSync as mkdirSync43, readFileSync as readFileSync65, writeFileSync as writeFileSync38 } from "fs";
|
|
39558
|
+
import { join as join77 } from "path";
|
|
38609
39559
|
var WorkspaceManifest;
|
|
38610
39560
|
var init_workspace_manifest = __esm({
|
|
38611
39561
|
"src/tracking/workspace-manifest.ts"() {
|
|
@@ -38615,7 +39565,7 @@ var init_workspace_manifest = __esm({
|
|
|
38615
39565
|
this.unerrDir = unerrDir2;
|
|
38616
39566
|
this.repoId = repoId;
|
|
38617
39567
|
this.sessionId = sessionId;
|
|
38618
|
-
this.manifestPath =
|
|
39568
|
+
this.manifestPath = join77(unerrDir2, "manifest.json");
|
|
38619
39569
|
this.data = this.load();
|
|
38620
39570
|
}
|
|
38621
39571
|
unerrDir;
|
|
@@ -38720,7 +39670,7 @@ var init_workspace_manifest = __esm({
|
|
|
38720
39670
|
};
|
|
38721
39671
|
}
|
|
38722
39672
|
try {
|
|
38723
|
-
const raw =
|
|
39673
|
+
const raw = readFileSync65(this.manifestPath, "utf-8");
|
|
38724
39674
|
const parsed = JSON.parse(raw);
|
|
38725
39675
|
if (parsed.repoId !== this.repoId) {
|
|
38726
39676
|
return {
|
|
@@ -38945,7 +39895,7 @@ import {
|
|
|
38945
39895
|
statSync as statSync14,
|
|
38946
39896
|
watch
|
|
38947
39897
|
} from "fs";
|
|
38948
|
-
import { join as
|
|
39898
|
+
import { join as join78 } from "path";
|
|
38949
39899
|
function formatSize(bytes) {
|
|
38950
39900
|
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
38951
39901
|
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
@@ -39092,9 +40042,9 @@ function tokenFlowRowToEntry(r) {
|
|
|
39092
40042
|
};
|
|
39093
40043
|
}
|
|
39094
40044
|
function startLogTailer(cwd, options) {
|
|
39095
|
-
const unerrDir2 =
|
|
39096
|
-
const logsDir =
|
|
39097
|
-
const generalPath =
|
|
40045
|
+
const unerrDir2 = join78(cwd, ".unerr");
|
|
40046
|
+
const logsDir = join78(unerrDir2, "logs");
|
|
40047
|
+
const generalPath = join78(logsDir, "events.jsonl");
|
|
39098
40048
|
const pollIntervalMs = options?.pollIntervalMs ?? 500;
|
|
39099
40049
|
const generalState = {
|
|
39100
40050
|
path: generalPath,
|
|
@@ -39240,13 +40190,13 @@ var init_middleware = __esm({
|
|
|
39240
40190
|
});
|
|
39241
40191
|
|
|
39242
40192
|
// src/server/routes/drift.ts
|
|
39243
|
-
import { existsSync as existsSync76, readFileSync as
|
|
39244
|
-
import { join as
|
|
40193
|
+
import { existsSync as existsSync76, readFileSync as readFileSync66, readdirSync as readdirSync18 } from "fs";
|
|
40194
|
+
import { join as join79 } from "path";
|
|
39245
40195
|
import { Hono as Hono2 } from "hono";
|
|
39246
40196
|
function readFlags(path7) {
|
|
39247
40197
|
try {
|
|
39248
40198
|
if (!existsSync76(path7)) return null;
|
|
39249
|
-
return JSON.parse(
|
|
40199
|
+
return JSON.parse(readFileSync66(path7, "utf8"));
|
|
39250
40200
|
} catch {
|
|
39251
40201
|
return null;
|
|
39252
40202
|
}
|
|
@@ -39254,7 +40204,7 @@ function readFlags(path7) {
|
|
|
39254
40204
|
function listSessionFiles(stateDir) {
|
|
39255
40205
|
try {
|
|
39256
40206
|
if (!existsSync76(stateDir)) return [];
|
|
39257
|
-
return
|
|
40207
|
+
return readdirSync18(stateDir).filter((f) => f.startsWith("nudge-") && f.endsWith(".flags")).map((f) => join79(stateDir, f));
|
|
39258
40208
|
} catch {
|
|
39259
40209
|
return [];
|
|
39260
40210
|
}
|
|
@@ -39274,7 +40224,7 @@ function rowFor(file) {
|
|
|
39274
40224
|
}
|
|
39275
40225
|
function createDriftRoutes(deps) {
|
|
39276
40226
|
const app = new Hono2();
|
|
39277
|
-
const stateDir =
|
|
40227
|
+
const stateDir = join79(deps.cwd, ".unerr", "state");
|
|
39278
40228
|
app.get("/sessions", (c) => {
|
|
39279
40229
|
const rows = [];
|
|
39280
40230
|
for (const file of listSessionFiles(stateDir)) {
|
|
@@ -39295,7 +40245,7 @@ function createDriftRoutes(deps) {
|
|
|
39295
40245
|
});
|
|
39296
40246
|
app.get("/current", (c) => {
|
|
39297
40247
|
const id = process.env.UNERR_SESSION_ID ?? `pid-${process.pid}`;
|
|
39298
|
-
const file =
|
|
40248
|
+
const file = join79(stateDir, `nudge-${id}.flags`);
|
|
39299
40249
|
const row = rowFor(file);
|
|
39300
40250
|
return c.json({
|
|
39301
40251
|
data: row ?? {
|
|
@@ -41458,13 +42408,13 @@ function createSystemRoutes(deps) {
|
|
|
41458
42408
|
});
|
|
41459
42409
|
app.get("/config", async (c) => {
|
|
41460
42410
|
const start = performance.now();
|
|
41461
|
-
const { existsSync: existsSync82, readFileSync:
|
|
41462
|
-
const { join:
|
|
42411
|
+
const { existsSync: existsSync82, readFileSync: readFileSync71 } = await import("fs");
|
|
42412
|
+
const { join: join85 } = await import("path");
|
|
41463
42413
|
let config = {};
|
|
41464
|
-
const configPath2 =
|
|
42414
|
+
const configPath2 = join85(deps.cwd, ".unerr", "config.json");
|
|
41465
42415
|
if (existsSync82(configPath2)) {
|
|
41466
42416
|
try {
|
|
41467
|
-
config = JSON.parse(
|
|
42417
|
+
config = JSON.parse(readFileSync71(configPath2, "utf-8"));
|
|
41468
42418
|
} catch {
|
|
41469
42419
|
config = { error: "unreadable" };
|
|
41470
42420
|
}
|
|
@@ -42757,10 +43707,10 @@ var http_exports = {};
|
|
|
42757
43707
|
__export(http_exports, {
|
|
42758
43708
|
startDashboardServer: () => startDashboardServer
|
|
42759
43709
|
});
|
|
42760
|
-
import { existsSync as existsSync77, readFileSync as
|
|
43710
|
+
import { existsSync as existsSync77, readFileSync as readFileSync67, unlinkSync as unlinkSync14, writeFileSync as writeFileSync39 } from "fs";
|
|
42761
43711
|
import { createServer as createServer5 } from "net";
|
|
42762
|
-
import { dirname as dirname17, join as
|
|
42763
|
-
import { fileURLToPath as
|
|
43712
|
+
import { dirname as dirname17, join as join80 } from "path";
|
|
43713
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
42764
43714
|
import { serve as serve2 } from "@hono/node-server";
|
|
42765
43715
|
import { serveStatic as serveStatic2 } from "@hono/node-server/serve-static";
|
|
42766
43716
|
import { Hono as Hono13 } from "hono";
|
|
@@ -42819,10 +43769,10 @@ async function startDashboardServer(opts) {
|
|
|
42819
43769
|
app.route("/api/router", createRouterApiV2(opts.routerV2));
|
|
42820
43770
|
}
|
|
42821
43771
|
if (!opts.apiOnly) {
|
|
42822
|
-
const distDir =
|
|
42823
|
-
const spaIndex =
|
|
43772
|
+
const distDir = join80(dirname17(fileURLToPath4(import.meta.url)), "ui");
|
|
43773
|
+
const spaIndex = join80(distDir, "index.html");
|
|
42824
43774
|
if (existsSync77(spaIndex)) {
|
|
42825
|
-
const spaHtml =
|
|
43775
|
+
const spaHtml = readFileSync67(spaIndex, "utf-8");
|
|
42826
43776
|
app.use("*", serveStatic2({ root: distDir }));
|
|
42827
43777
|
app.get("*", (c) => {
|
|
42828
43778
|
const path7 = c.req.path;
|
|
@@ -42854,7 +43804,7 @@ async function startDashboardServer(opts) {
|
|
|
42854
43804
|
port,
|
|
42855
43805
|
hostname: "127.0.0.1"
|
|
42856
43806
|
});
|
|
42857
|
-
const serverJsonPath =
|
|
43807
|
+
const serverJsonPath = join80(opts.stateDir, "server.json");
|
|
42858
43808
|
const serverInfo = {
|
|
42859
43809
|
port,
|
|
42860
43810
|
pid: process.pid,
|
|
@@ -43284,15 +44234,15 @@ import {
|
|
|
43284
44234
|
existsSync as existsSync78,
|
|
43285
44235
|
writeFileSync as fsWriteFileSync,
|
|
43286
44236
|
mkdirSync as mkdirSync44,
|
|
43287
|
-
readFileSync as
|
|
43288
|
-
readdirSync as
|
|
44237
|
+
readFileSync as readFileSync68,
|
|
44238
|
+
readdirSync as readdirSync19
|
|
43289
44239
|
} from "fs";
|
|
43290
|
-
import { join as
|
|
44240
|
+
import { join as join81 } from "path";
|
|
43291
44241
|
async function getProxyFactStore(unerrDir2) {
|
|
43292
44242
|
if (proxyFactStore !== void 0) return proxyFactStore;
|
|
43293
44243
|
try {
|
|
43294
44244
|
const { TemporalFactStore: TemporalFactStore2 } = await Promise.resolve().then(() => (init_temporal_facts(), temporal_facts_exports));
|
|
43295
|
-
const cwd =
|
|
44245
|
+
const cwd = join81(unerrDir2, "..");
|
|
43296
44246
|
proxyFactStore = await TemporalFactStore2.create(cwd);
|
|
43297
44247
|
return proxyFactStore;
|
|
43298
44248
|
} catch {
|
|
@@ -43436,9 +44386,9 @@ async function handleRecallFactsProxy(args, unerrDir2, effectiveness) {
|
|
|
43436
44386
|
}
|
|
43437
44387
|
function migrateAgentPermissions(cwd) {
|
|
43438
44388
|
try {
|
|
43439
|
-
const settingsPath =
|
|
44389
|
+
const settingsPath = join81(cwd, ".claude", "settings.json");
|
|
43440
44390
|
if (!existsSync78(settingsPath)) return;
|
|
43441
|
-
const raw =
|
|
44391
|
+
const raw = readFileSync68(settingsPath, "utf-8");
|
|
43442
44392
|
const settings = JSON.parse(raw);
|
|
43443
44393
|
const deny = settings?.permissions?.deny;
|
|
43444
44394
|
if (!Array.isArray(deny)) return;
|
|
@@ -43455,7 +44405,7 @@ function migrateAgentPermissions(cwd) {
|
|
|
43455
44405
|
}
|
|
43456
44406
|
async function startProxy(opts = {}) {
|
|
43457
44407
|
installFileLogger({
|
|
43458
|
-
filePath:
|
|
44408
|
+
filePath: join81(process.cwd(), ".unerr", "logs", "unerr.log"),
|
|
43459
44409
|
maxBytes: 5e6,
|
|
43460
44410
|
keep: 5
|
|
43461
44411
|
});
|
|
@@ -43468,7 +44418,7 @@ async function startProxy(opts = {}) {
|
|
|
43468
44418
|
const lifecycle = createLifecycleActor(process.cwd());
|
|
43469
44419
|
lifecycle.send({ type: "START_DETECT" });
|
|
43470
44420
|
startup.setLocalMode(true);
|
|
43471
|
-
const stateDir =
|
|
44421
|
+
const stateDir = join81(process.cwd(), ".unerr", "state");
|
|
43472
44422
|
if (!existsSync78(stateDir)) {
|
|
43473
44423
|
mkdirSync44(stateDir, { recursive: true });
|
|
43474
44424
|
}
|
|
@@ -43487,7 +44437,7 @@ async function startProxy(opts = {}) {
|
|
|
43487
44437
|
startupLog.step(
|
|
43488
44438
|
`PID ${process.pid} ${startupLog.fmt.muted(`\xB7 health localhost:${lockResult.healthPort}`)}`
|
|
43489
44439
|
);
|
|
43490
|
-
const ledgerDir =
|
|
44440
|
+
const ledgerDir = join81(process.cwd(), ".unerr", "ledger");
|
|
43491
44441
|
const previousSession = detectSessionResume(stateDir, ledgerDir);
|
|
43492
44442
|
if (previousSession) {
|
|
43493
44443
|
stats.isResumedSession = true;
|
|
@@ -43502,21 +44452,21 @@ async function startProxy(opts = {}) {
|
|
|
43502
44452
|
if (opts.repoId) {
|
|
43503
44453
|
repoIds = [opts.repoId];
|
|
43504
44454
|
} else {
|
|
43505
|
-
const configPath2 =
|
|
44455
|
+
const configPath2 = join81(process.cwd(), ".unerr", "config.json");
|
|
43506
44456
|
if (existsSync78(configPath2)) {
|
|
43507
44457
|
try {
|
|
43508
|
-
const config = JSON.parse(
|
|
44458
|
+
const config = JSON.parse(readFileSync68(configPath2, "utf-8"));
|
|
43509
44459
|
if (config.repoId) repoIds = [config.repoId];
|
|
43510
44460
|
} catch {
|
|
43511
44461
|
}
|
|
43512
44462
|
}
|
|
43513
|
-
const manifestsDir =
|
|
44463
|
+
const manifestsDir = join81(process.cwd(), ".unerr", "manifests");
|
|
43514
44464
|
if (repoIds.length === 0 && existsSync78(manifestsDir)) {
|
|
43515
|
-
repoIds =
|
|
44465
|
+
repoIds = readdirSync19(manifestsDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
43516
44466
|
}
|
|
43517
44467
|
}
|
|
43518
44468
|
if (repoIds.length === 0) {
|
|
43519
|
-
const { createHash:
|
|
44469
|
+
const { createHash: createHash6 } = await import("crypto");
|
|
43520
44470
|
let repoIdentifier = process.cwd();
|
|
43521
44471
|
try {
|
|
43522
44472
|
const { getRemoteUrl: getRemoteUrl2 } = await Promise.resolve().then(() => (init_git(), git_exports));
|
|
@@ -43524,7 +44474,7 @@ async function startProxy(opts = {}) {
|
|
|
43524
44474
|
if (remote) repoIdentifier = remote;
|
|
43525
44475
|
} catch {
|
|
43526
44476
|
}
|
|
43527
|
-
const localRepoId =
|
|
44477
|
+
const localRepoId = createHash6("sha256").update(repoIdentifier).digest("hex").slice(0, 12);
|
|
43528
44478
|
repoIds = [localRepoId];
|
|
43529
44479
|
startupLog.done(
|
|
43530
44480
|
`Repository ${startupLog.fmt.cyan(localRepoId)} ${startupLog.fmt.muted(`(from ${repoIdentifier === process.cwd() ? "directory" : "git remote"})`)}`
|
|
@@ -43636,7 +44586,7 @@ async function startProxy(opts = {}) {
|
|
|
43636
44586
|
`Migrated snapshot to persistent graph ${startupLog.fmt.muted(`\u2192 ${dbPath}`)}`
|
|
43637
44587
|
);
|
|
43638
44588
|
try {
|
|
43639
|
-
const migrationUnerrDir =
|
|
44589
|
+
const migrationUnerrDir = join81(process.cwd(), ".unerr");
|
|
43640
44590
|
const factStoreForMigration = await getProxyFactStore(migrationUnerrDir);
|
|
43641
44591
|
if (factStoreForMigration) {
|
|
43642
44592
|
const { detectLocalConventions: detectLocalConventions2 } = await Promise.resolve().then(() => (init_local_convention_detector(), local_convention_detector_exports));
|
|
@@ -43735,7 +44685,7 @@ async function startProxy(opts = {}) {
|
|
|
43735
44685
|
let efficiencyTracker = createEfficiencyTracker2();
|
|
43736
44686
|
router.setTokenCounter(tokenCounter);
|
|
43737
44687
|
router.setEfficiencyTracker(efficiencyTracker);
|
|
43738
|
-
const proxyFactStore2 = await getProxyFactStore(
|
|
44688
|
+
const proxyFactStore2 = await getProxyFactStore(join81(process.cwd(), ".unerr"));
|
|
43739
44689
|
if (proxyFactStore2) {
|
|
43740
44690
|
router.setFactStore(proxyFactStore2);
|
|
43741
44691
|
}
|
|
@@ -43743,7 +44693,7 @@ async function startProxy(opts = {}) {
|
|
|
43743
44693
|
try {
|
|
43744
44694
|
const { generateSessionResume: generateSessionResume2 } = await Promise.resolve().then(() => (init_session_resume(), session_resume_exports));
|
|
43745
44695
|
const { ShadowLedger: ResumeLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
43746
|
-
const resumeLedger = new ResumeLedger(
|
|
44696
|
+
const resumeLedger = new ResumeLedger(join81(process.cwd(), ".unerr"));
|
|
43747
44697
|
const ledgerEntries = resumeLedger.getRecentEntries(50);
|
|
43748
44698
|
const resumeCtx = generateSessionResume2(ledgerEntries);
|
|
43749
44699
|
if (resumeCtx) {
|
|
@@ -43764,7 +44714,7 @@ async function startProxy(opts = {}) {
|
|
|
43764
44714
|
const { createDurabilityScorer: createDurabilityScorer2 } = await Promise.resolve().then(() => (init_durability_scorer(), durability_scorer_exports));
|
|
43765
44715
|
const durabilityScorer = createDurabilityScorer2();
|
|
43766
44716
|
const { ShadowLedger: DurLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
43767
|
-
const durLedger = new DurLedger(
|
|
44717
|
+
const durLedger = new DurLedger(join81(process.cwd(), ".unerr"));
|
|
43768
44718
|
const durEntries = durLedger.getRecentEntries(200);
|
|
43769
44719
|
if (durEntries.length > 0) {
|
|
43770
44720
|
durabilityScorer.computeScores(durEntries);
|
|
@@ -43784,7 +44734,7 @@ async function startProxy(opts = {}) {
|
|
|
43784
44734
|
try {
|
|
43785
44735
|
const { detectInstableEntities: detectInstableEntities2 } = await Promise.resolve().then(() => (init_negative_knowledge(), negative_knowledge_exports));
|
|
43786
44736
|
const { ShadowLedger: NkLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
43787
|
-
const nkLedger = new NkLedger(
|
|
44737
|
+
const nkLedger = new NkLedger(join81(process.cwd(), ".unerr"));
|
|
43788
44738
|
const nkEntries = nkLedger.getRecentEntries(200);
|
|
43789
44739
|
if (nkEntries.length > 0) {
|
|
43790
44740
|
const antiPatterns = detectInstableEntities2(nkEntries);
|
|
@@ -43806,7 +44756,7 @@ async function startProxy(opts = {}) {
|
|
|
43806
44756
|
try {
|
|
43807
44757
|
const { CausalBridge: CausalBridge2 } = await Promise.resolve().then(() => (init_causal_bridge(), causal_bridge_exports));
|
|
43808
44758
|
const causalBridge = new CausalBridge2(
|
|
43809
|
-
|
|
44759
|
+
join81(process.cwd(), ".unerr"),
|
|
43810
44760
|
process.cwd()
|
|
43811
44761
|
);
|
|
43812
44762
|
router.setCausalBridge(causalBridge);
|
|
@@ -43816,7 +44766,7 @@ async function startProxy(opts = {}) {
|
|
|
43816
44766
|
try {
|
|
43817
44767
|
const { learnConventions: learnConventions2 } = await Promise.resolve().then(() => (init_convention_learner(), convention_learner_exports));
|
|
43818
44768
|
const { ShadowLedger: ConvLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
43819
|
-
const convLedger = new ConvLedger(
|
|
44769
|
+
const convLedger = new ConvLedger(join81(process.cwd(), ".unerr"));
|
|
43820
44770
|
const convEntries = convLedger.getRecentEntries(100);
|
|
43821
44771
|
if (convEntries.length > 0) {
|
|
43822
44772
|
const learned = learnConventions2(convEntries);
|
|
@@ -43839,7 +44789,7 @@ async function startProxy(opts = {}) {
|
|
|
43839
44789
|
try {
|
|
43840
44790
|
const { computePromptDurabilityProfiles: computePromptDurabilityProfiles2 } = await Promise.resolve().then(() => (init_prompt_durability(), prompt_durability_exports));
|
|
43841
44791
|
const { ShadowLedger: DurProfLedger } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
43842
|
-
const durProfLedger = new DurProfLedger(
|
|
44792
|
+
const durProfLedger = new DurProfLedger(join81(process.cwd(), ".unerr"));
|
|
43843
44793
|
const durProfEntries = durProfLedger.getRecentEntries(200);
|
|
43844
44794
|
if (durProfEntries.length > 0) {
|
|
43845
44795
|
const profiles = computePromptDurabilityProfiles2(durProfEntries);
|
|
@@ -43859,7 +44809,7 @@ async function startProxy(opts = {}) {
|
|
|
43859
44809
|
}
|
|
43860
44810
|
try {
|
|
43861
44811
|
const { createContextLedger: createContextLedger2 } = await Promise.resolve().then(() => (init_context_ledger(), context_ledger_exports));
|
|
43862
|
-
const contextLedger = createContextLedger2(
|
|
44812
|
+
const contextLedger = createContextLedger2(join81(process.cwd(), ".unerr"));
|
|
43863
44813
|
contextLedger.load();
|
|
43864
44814
|
contextLedger.prune();
|
|
43865
44815
|
router.setContextLedger(contextLedger);
|
|
@@ -43984,7 +44934,7 @@ async function startProxy(opts = {}) {
|
|
|
43984
44934
|
});
|
|
43985
44935
|
const { ShadowLedger: ShadowLedger2 } = await Promise.resolve().then(() => (init_shadow_ledger(), shadow_ledger_exports));
|
|
43986
44936
|
const { IntentCorrelator: IntentCorrelator2 } = await Promise.resolve().then(() => (init_intent_correlator(), intent_correlator_exports));
|
|
43987
|
-
const unerrDirForLedger =
|
|
44937
|
+
const unerrDirForLedger = join81(process.cwd(), ".unerr");
|
|
43988
44938
|
const shadowLedger = new ShadowLedger2(unerrDirForLedger);
|
|
43989
44939
|
const intentCorrelator = new IntentCorrelator2(unerrDirForLedger);
|
|
43990
44940
|
log22.info(
|
|
@@ -44094,7 +45044,7 @@ async function startProxy(opts = {}) {
|
|
|
44094
45044
|
try {
|
|
44095
45045
|
const { writeFileSync: writeFileSync42 } = await import("fs");
|
|
44096
45046
|
writeFileSync42(
|
|
44097
|
-
|
|
45047
|
+
join81(unerrDirForLedger, "state", "session.id"),
|
|
44098
45048
|
shadowLedger.getSessionId(),
|
|
44099
45049
|
"utf-8"
|
|
44100
45050
|
);
|
|
@@ -44522,7 +45472,7 @@ ${signalFooter.trimEnd()}` : "";
|
|
|
44522
45472
|
lifecycle.send({ type: "INDEX_COMPLETE" });
|
|
44523
45473
|
lifecycle.send({ type: "MCP_READY" });
|
|
44524
45474
|
const { TransportMux: TransportMux2 } = await Promise.resolve().then(() => (init_transport_mux(), transport_mux_exports));
|
|
44525
|
-
const sockPath2 =
|
|
45475
|
+
const sockPath2 = join81(stateDir, "proxy.sock");
|
|
44526
45476
|
const transportMux = new TransportMux2(sockPath2);
|
|
44527
45477
|
const agentNameByClient = /* @__PURE__ */ new Map();
|
|
44528
45478
|
transportMux.setCustomHttpHandler("/commit-context", (_url) => {
|
|
@@ -44807,7 +45757,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
44807
45757
|
try {
|
|
44808
45758
|
const { DriftTracker: DriftTracker2 } = await Promise.resolve().then(() => (init_drift_tracker(), drift_tracker_exports));
|
|
44809
45759
|
const { FileHashManager: FileHashManager2 } = await Promise.resolve().then(() => (init_file_hash_state(), file_hash_state_exports));
|
|
44810
|
-
const unerrDir2 =
|
|
45760
|
+
const unerrDir2 = join81(process.cwd(), ".unerr");
|
|
44811
45761
|
const fileHashManager = new FileHashManager2(unerrDir2);
|
|
44812
45762
|
_driftTracker = new DriftTracker2(
|
|
44813
45763
|
{ projectRoot: process.cwd(), repoId: repoIds[0], unerrDir: unerrDir2 },
|
|
@@ -45031,7 +45981,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45031
45981
|
}
|
|
45032
45982
|
const { WorkspaceManifest: WorkspaceManifest2 } = await Promise.resolve().then(() => (init_workspace_manifest(), workspace_manifest_exports));
|
|
45033
45983
|
const workspaceManifest = repoIds[0] ? new WorkspaceManifest2(
|
|
45034
|
-
|
|
45984
|
+
join81(process.cwd(), ".unerr"),
|
|
45035
45985
|
repoIds[0],
|
|
45036
45986
|
shadowLedger.getSessionId()
|
|
45037
45987
|
) : null;
|
|
@@ -45116,7 +46066,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45116
46066
|
if (proxyMode === "parse" && parseIndex) {
|
|
45117
46067
|
try {
|
|
45118
46068
|
const { extractEntitiesFromSource: extractEntitiesFromSource2 } = await Promise.resolve().then(() => (init_auto_bootstrap(), auto_bootstrap_exports));
|
|
45119
|
-
const allFiles =
|
|
46069
|
+
const allFiles = readdirSync19(process.cwd(), {
|
|
45120
46070
|
recursive: true,
|
|
45121
46071
|
encoding: "utf-8"
|
|
45122
46072
|
});
|
|
@@ -45130,7 +46080,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45130
46080
|
});
|
|
45131
46081
|
for (const file of sourceFiles.slice(0, 500)) {
|
|
45132
46082
|
try {
|
|
45133
|
-
const content =
|
|
46083
|
+
const content = readFileSync68(join81(process.cwd(), file), "utf-8");
|
|
45134
46084
|
const entities = extractEntitiesFromSource2(file, content);
|
|
45135
46085
|
parseIndex.addEntities(entities);
|
|
45136
46086
|
} catch {
|
|
@@ -45194,7 +46144,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45194
46144
|
}
|
|
45195
46145
|
const { writeFileSync: writeStatsFile } = await import("fs");
|
|
45196
46146
|
const { computePercentiles: computePercentiles2 } = await Promise.resolve().then(() => (init_session_stats(), session_stats_exports));
|
|
45197
|
-
const statsSnapshotPath =
|
|
46147
|
+
const statsSnapshotPath = join81(stateDir, "session_stats.json");
|
|
45198
46148
|
const statsSnapshotInterval = setInterval(() => {
|
|
45199
46149
|
try {
|
|
45200
46150
|
const total = stats.toolCallsLocal;
|
|
@@ -45232,7 +46182,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45232
46182
|
const { startDashboardServer: startDashboardServer2 } = await Promise.resolve().then(() => (init_http(), http_exports));
|
|
45233
46183
|
const { detectIde: detectIdeDashboard } = await Promise.resolve().then(() => (init_detect(), detect_exports));
|
|
45234
46184
|
const ideType = await detectIdeDashboard(process.cwd());
|
|
45235
|
-
const unerrDirForApi =
|
|
46185
|
+
const unerrDirForApi = join81(process.cwd(), ".unerr");
|
|
45236
46186
|
dashboardHandle = await startDashboardServer2({
|
|
45237
46187
|
system: {
|
|
45238
46188
|
stats,
|
|
@@ -45300,16 +46250,16 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45300
46250
|
temporal: await (async () => {
|
|
45301
46251
|
try {
|
|
45302
46252
|
const { TemporalFactStore: TemporalFactStore2 } = await Promise.resolve().then(() => (init_temporal_facts(), temporal_facts_exports));
|
|
45303
|
-
const { readdirSync:
|
|
46253
|
+
const { readdirSync: readdirSync20, readFileSync: readFileSync71 } = await import("fs");
|
|
45304
46254
|
const factStore = await TemporalFactStore2.create(process.cwd());
|
|
45305
46255
|
return {
|
|
45306
46256
|
factStore,
|
|
45307
46257
|
loadRecentSessions: (limit) => {
|
|
45308
46258
|
try {
|
|
45309
|
-
const sessDir =
|
|
45310
|
-
const files =
|
|
46259
|
+
const sessDir = join81(unerrDirForApi, "sessions");
|
|
46260
|
+
const files = readdirSync20(sessDir).filter((f) => f.endsWith(".jsonl")).sort().slice(-limit);
|
|
45311
46261
|
return files.map((f) => {
|
|
45312
|
-
const content =
|
|
46262
|
+
const content = readFileSync71(join81(sessDir, f), "utf-8").trim().split("\n").pop();
|
|
45313
46263
|
return JSON.parse(content);
|
|
45314
46264
|
});
|
|
45315
46265
|
} catch {
|
|
@@ -45396,7 +46346,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45396
46346
|
const topMech = Object.entries(tokenFlowSummary.by_mechanism).sort(
|
|
45397
46347
|
([, a], [, b]) => b.tokens_saved - a.tokens_saved
|
|
45398
46348
|
)[0];
|
|
45399
|
-
appendSessionHistory2(
|
|
46349
|
+
appendSessionHistory2(join81(process.cwd(), ".unerr"), {
|
|
45400
46350
|
sessionId: shadowLedger.getSessionId(),
|
|
45401
46351
|
startedAt: new Date(stats.sessionStartedAt).toISOString(),
|
|
45402
46352
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -45556,7 +46506,7 @@ ${signalFooter2.trimEnd()}` : "";
|
|
|
45556
46506
|
try {
|
|
45557
46507
|
const correctionModule = (init_correction_detector(), __toCommonJS(correction_detector_exports));
|
|
45558
46508
|
const detectCorrections2 = correctionModule.detectCorrections;
|
|
45559
|
-
const ledgerPath =
|
|
46509
|
+
const ledgerPath = join81(
|
|
45560
46510
|
process.cwd(),
|
|
45561
46511
|
".unerr",
|
|
45562
46512
|
"ledger",
|
|
@@ -45920,19 +46870,19 @@ __export(setup_wizard_exports, {
|
|
|
45920
46870
|
promptLocalOrExit: () => promptLocalOrExit,
|
|
45921
46871
|
runSetup: () => runSetup
|
|
45922
46872
|
});
|
|
45923
|
-
import { createHash as
|
|
46873
|
+
import { createHash as createHash5 } from "crypto";
|
|
45924
46874
|
import { existsSync as existsSync80, mkdirSync as mkdirSync46, writeFileSync as writeFileSync40 } from "fs";
|
|
45925
|
-
import { join as
|
|
46875
|
+
import { join as join82 } from "path";
|
|
45926
46876
|
import * as clack from "@clack/prompts";
|
|
45927
46877
|
async function runSetup(cwd) {
|
|
45928
46878
|
const projectDir = cwd ?? process.cwd();
|
|
45929
46879
|
clack.intro("unerr");
|
|
45930
46880
|
clack.log.step("Project Setup");
|
|
45931
46881
|
const repoId = await generateRepoId(projectDir);
|
|
45932
|
-
const configDir =
|
|
46882
|
+
const configDir = join82(projectDir, ".unerr");
|
|
45933
46883
|
mkdirSync46(configDir, { recursive: true });
|
|
45934
|
-
const configPath2 =
|
|
45935
|
-
const settingsPath =
|
|
46884
|
+
const configPath2 = join82(configDir, "config.json");
|
|
46885
|
+
const settingsPath = join82(configDir, "settings.json");
|
|
45936
46886
|
writeFileSync40(configPath2, `${JSON.stringify({ repoId }, null, 2)}
|
|
45937
46887
|
`);
|
|
45938
46888
|
let existingSettings = {};
|
|
@@ -46003,7 +46953,7 @@ async function generateRepoId(cwd) {
|
|
|
46003
46953
|
let repoIdentifier = cwd;
|
|
46004
46954
|
const remote = await getRemoteUrl(cwd);
|
|
46005
46955
|
if (remote) repoIdentifier = remote;
|
|
46006
|
-
return
|
|
46956
|
+
return createHash5("sha256").update(repoIdentifier).digest("hex").slice(0, 12);
|
|
46007
46957
|
}
|
|
46008
46958
|
function formatSize2(bytes) {
|
|
46009
46959
|
if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(1)}GB`;
|
|
@@ -46316,6 +47266,112 @@ var init_setup_wizard = __esm({
|
|
|
46316
47266
|
}
|
|
46317
47267
|
});
|
|
46318
47268
|
|
|
47269
|
+
// src/proxy/bridge-catalog.ts
|
|
47270
|
+
var bridge_catalog_exports = {};
|
|
47271
|
+
__export(bridge_catalog_exports, {
|
|
47272
|
+
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
47273
|
+
SERVER_INFO: () => SERVER_INFO,
|
|
47274
|
+
StaticCatalogInterceptor: () => StaticCatalogInterceptor,
|
|
47275
|
+
buildInitializeResult: () => buildInitializeResult,
|
|
47276
|
+
buildToolsListResult: () => buildToolsListResult
|
|
47277
|
+
});
|
|
47278
|
+
function tryIntercept(line) {
|
|
47279
|
+
let msg;
|
|
47280
|
+
try {
|
|
47281
|
+
const parsed = JSON.parse(line);
|
|
47282
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
47283
|
+
msg = parsed;
|
|
47284
|
+
} catch {
|
|
47285
|
+
return null;
|
|
47286
|
+
}
|
|
47287
|
+
if (msg.method === "initialize") {
|
|
47288
|
+
return JSON.stringify({
|
|
47289
|
+
jsonrpc: "2.0",
|
|
47290
|
+
id: msg.id ?? null,
|
|
47291
|
+
result: {
|
|
47292
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
47293
|
+
capabilities: { tools: {} },
|
|
47294
|
+
serverInfo: SERVER_INFO
|
|
47295
|
+
}
|
|
47296
|
+
});
|
|
47297
|
+
}
|
|
47298
|
+
if (msg.method === "tools/list") {
|
|
47299
|
+
return JSON.stringify({
|
|
47300
|
+
jsonrpc: "2.0",
|
|
47301
|
+
id: msg.id ?? null,
|
|
47302
|
+
result: { tools: TOOL_DEFINITIONS }
|
|
47303
|
+
});
|
|
47304
|
+
}
|
|
47305
|
+
return null;
|
|
47306
|
+
}
|
|
47307
|
+
function buildInitializeResult(id) {
|
|
47308
|
+
return {
|
|
47309
|
+
jsonrpc: "2.0",
|
|
47310
|
+
id,
|
|
47311
|
+
result: {
|
|
47312
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
47313
|
+
capabilities: { tools: {} },
|
|
47314
|
+
serverInfo: SERVER_INFO
|
|
47315
|
+
}
|
|
47316
|
+
};
|
|
47317
|
+
}
|
|
47318
|
+
function buildToolsListResult(id) {
|
|
47319
|
+
return {
|
|
47320
|
+
jsonrpc: "2.0",
|
|
47321
|
+
id,
|
|
47322
|
+
result: { tools: TOOL_DEFINITIONS }
|
|
47323
|
+
};
|
|
47324
|
+
}
|
|
47325
|
+
var PROTOCOL_VERSION, SERVER_INFO, StaticCatalogInterceptor;
|
|
47326
|
+
var init_bridge_catalog = __esm({
|
|
47327
|
+
"src/proxy/bridge-catalog.ts"() {
|
|
47328
|
+
"use strict";
|
|
47329
|
+
init_tool_definitions();
|
|
47330
|
+
PROTOCOL_VERSION = "2024-11-05";
|
|
47331
|
+
SERVER_INFO = {
|
|
47332
|
+
name: "unerr-local",
|
|
47333
|
+
version: "0.1.7"
|
|
47334
|
+
};
|
|
47335
|
+
StaticCatalogInterceptor = class {
|
|
47336
|
+
partial = "";
|
|
47337
|
+
ingest(chunk) {
|
|
47338
|
+
const replies = [];
|
|
47339
|
+
const forward = [];
|
|
47340
|
+
this.partial += chunk.toString("utf8");
|
|
47341
|
+
const lastNewline = this.partial.lastIndexOf("\n");
|
|
47342
|
+
if (lastNewline < 0) {
|
|
47343
|
+
return { replies, forward };
|
|
47344
|
+
}
|
|
47345
|
+
const completeBlock = this.partial.slice(0, lastNewline + 1);
|
|
47346
|
+
this.partial = this.partial.slice(lastNewline + 1);
|
|
47347
|
+
for (const rawLine of completeBlock.split("\n")) {
|
|
47348
|
+
if (rawLine === "") continue;
|
|
47349
|
+
const reply = tryIntercept(rawLine);
|
|
47350
|
+
if (reply !== null) {
|
|
47351
|
+
replies.push(`${reply}
|
|
47352
|
+
`);
|
|
47353
|
+
} else {
|
|
47354
|
+
forward.push(Buffer.from(`${rawLine}
|
|
47355
|
+
`, "utf8"));
|
|
47356
|
+
}
|
|
47357
|
+
}
|
|
47358
|
+
return { replies, forward };
|
|
47359
|
+
}
|
|
47360
|
+
/**
|
|
47361
|
+
* Returns any unfinished line (no terminating newline yet). Call exactly
|
|
47362
|
+
* once when handing the pre-buffer off to the bridge so partial frames are
|
|
47363
|
+
* forwarded to the proxy without being dropped.
|
|
47364
|
+
*/
|
|
47365
|
+
drainPartial() {
|
|
47366
|
+
if (this.partial.length === 0) return null;
|
|
47367
|
+
const buf = Buffer.from(this.partial, "utf8");
|
|
47368
|
+
this.partial = "";
|
|
47369
|
+
return buf;
|
|
47370
|
+
}
|
|
47371
|
+
};
|
|
47372
|
+
}
|
|
47373
|
+
});
|
|
47374
|
+
|
|
46319
47375
|
// src/daemon/spawn-lock.ts
|
|
46320
47376
|
var spawn_lock_exports = {};
|
|
46321
47377
|
__export(spawn_lock_exports, {
|
|
@@ -46327,16 +47383,16 @@ import {
|
|
|
46327
47383
|
closeSync as closeSync2,
|
|
46328
47384
|
mkdirSync as mkdirSync47,
|
|
46329
47385
|
openSync as openSync2,
|
|
46330
|
-
readFileSync as
|
|
47386
|
+
readFileSync as readFileSync69,
|
|
46331
47387
|
unlinkSync as unlinkSync16,
|
|
46332
47388
|
writeFileSync as writeFileSync41
|
|
46333
47389
|
} from "fs";
|
|
46334
|
-
import { join as
|
|
47390
|
+
import { join as join83 } from "path";
|
|
46335
47391
|
function spawnLockPath() {
|
|
46336
|
-
return
|
|
47392
|
+
return join83(globalDir(), "state", "spawn.lock");
|
|
46337
47393
|
}
|
|
46338
47394
|
function tryAcquireSpawnLock() {
|
|
46339
|
-
mkdirSync47(
|
|
47395
|
+
mkdirSync47(join83(globalDir(), "state"), { recursive: true });
|
|
46340
47396
|
const path7 = spawnLockPath();
|
|
46341
47397
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
46342
47398
|
try {
|
|
@@ -46364,7 +47420,7 @@ function releaseSpawnLock() {
|
|
|
46364
47420
|
function reclaimIfStale(path7) {
|
|
46365
47421
|
let body = null;
|
|
46366
47422
|
try {
|
|
46367
|
-
body = JSON.parse(
|
|
47423
|
+
body = JSON.parse(readFileSync69(path7, "utf8"));
|
|
46368
47424
|
} catch {
|
|
46369
47425
|
try {
|
|
46370
47426
|
unlinkSync16(path7);
|
|
@@ -46536,8 +47592,8 @@ var init_bridge = __esm({
|
|
|
46536
47592
|
});
|
|
46537
47593
|
|
|
46538
47594
|
// src/entrypoints/cli.ts
|
|
46539
|
-
import { existsSync as existsSync81, readFileSync as
|
|
46540
|
-
import { join as
|
|
47595
|
+
import { existsSync as existsSync81, readFileSync as readFileSync70 } from "fs";
|
|
47596
|
+
import { join as join84 } from "path";
|
|
46541
47597
|
import { Command } from "commander";
|
|
46542
47598
|
|
|
46543
47599
|
// src/commands/branches.ts
|
|
@@ -49573,12 +50629,12 @@ async function tryLoadGraphForShellBoost(cwd) {
|
|
|
49573
50629
|
if (cached && Date.now() - cached.loadedAt < BOOST_TTL_MS) {
|
|
49574
50630
|
return cached.kind === "ok" ? cached.graph : null;
|
|
49575
50631
|
}
|
|
49576
|
-
const { existsSync: existsSync82, readFileSync:
|
|
49577
|
-
const { join:
|
|
49578
|
-
const snapshotsDir =
|
|
49579
|
-
let snapshotPath2 =
|
|
50632
|
+
const { existsSync: existsSync82, readFileSync: readFileSync71 } = await import("fs");
|
|
50633
|
+
const { join: join85 } = await import("path");
|
|
50634
|
+
const snapshotsDir = join85(cwd, ".unerr", "snapshots");
|
|
50635
|
+
let snapshotPath2 = join85(snapshotsDir, "graph.msgpack.gz");
|
|
49580
50636
|
if (!existsSync82(snapshotPath2)) {
|
|
49581
|
-
snapshotPath2 =
|
|
50637
|
+
snapshotPath2 = join85(snapshotsDir, "graph.msgpack");
|
|
49582
50638
|
}
|
|
49583
50639
|
if (!existsSync82(snapshotPath2)) {
|
|
49584
50640
|
const reason = `snapshot missing at ${snapshotsDir}`;
|
|
@@ -49605,7 +50661,7 @@ async function tryLoadGraphForShellBoost(cwd) {
|
|
|
49605
50661
|
let buffer;
|
|
49606
50662
|
try {
|
|
49607
50663
|
const { gunzipSync: gunzipSync3 } = await import("zlib");
|
|
49608
|
-
const raw =
|
|
50664
|
+
const raw = readFileSync71(snapshotPath2);
|
|
49609
50665
|
try {
|
|
49610
50666
|
buffer = gunzipSync3(raw);
|
|
49611
50667
|
} catch {
|
|
@@ -54765,9 +55821,9 @@ When your next action is Edit, use built-in Read with offset/limit on the target
|
|
|
54765
55821
|
|
|
54766
55822
|
**Common failure mode:** Using \`file_read\` to understand a file, then attempting Edit without calling built-in Read first. The Edit tool WILL reject with "File has not been read yet". Always call built-in Read (with offset/limit) immediately before Edit.` : "";
|
|
54767
55823
|
const summaryEditNote = isClaudeCode ? "\nNEVER use built-in Read/Grep/Glob for code navigation. EXCEPTION: built-in Read (with offset/limit) is REQUIRED immediately before Edit (file_read cannot substitute \u2014 Edit will fail without it)." : "\nNEVER use built-in Read/Grep/Glob for code navigation \u2014 use unerr MCP tools instead.";
|
|
54768
|
-
return `## REQUIRED: Use unerr Graph Intelligence Tools (
|
|
55824
|
+
return `## REQUIRED: Use unerr Graph Intelligence Tools (20 MCP tools)
|
|
54769
55825
|
|
|
54770
|
-
This project has unerr MCP tools installed. You MUST use these instead of built-in Read/Grep/Glob for code navigation. unerr tools are graph-backed, return results in <5ms, and include project context that built-in tools miss.
|
|
55826
|
+
This project has unerr MCP tools installed. You MUST use these instead of built-in Read/Grep/Glob for code navigation, and \`fetch_url\` instead of built-in WebFetch. unerr tools are graph-backed, return results in <5ms, and include project context that built-in tools miss.
|
|
54771
55827
|
|
|
54772
55828
|
### Tool Routing (MANDATORY \u2014 match your goal before calling any tool)
|
|
54773
55829
|
|
|
@@ -54781,6 +55837,7 @@ ${readForEditRow}
|
|
|
54781
55837
|
| Get a specific function or class | \`get_entity\` or \`file_read\` with \`entity\` param | Reading entire file |
|
|
54782
55838
|
| Trace imports/dependencies | \`get_imports\` or \`get_references\` (direction: callees) | Manual import scanning |
|
|
54783
55839
|
| Find hotspots / high fan-in / blast-radius candidates | \`get_critical_nodes\` | \`get_entity\` (won't show ranked list), guessing |
|
|
55840
|
+
| Fetch a web page by URL | \`fetch_url\` (Defuddle/Readability \u2192 markdown passages \u2192 BM25 ranking when \`prompt\` supplied \u2192 diff-cache) | Built-in WebFetch |
|
|
54784
55841
|
| Run a shell command | Automatic \u2014 routed through shell intelligence | N/A |
|
|
54785
55842
|
|
|
54786
55843
|
### FORBIDDEN Patterns (these waste tokens and miss context)
|
|
@@ -54791,7 +55848,8 @@ ${readForEditRow}
|
|
|
54791
55848
|
- Reading multiple files to understand conventions -> use \`get_conventions\`
|
|
54792
55849
|
- Guessing code style for new code -> use \`get_conventions\`
|
|
54793
55850
|
- Guessing which entity has the highest fan-in / is the biggest hotspot -> use \`get_critical_nodes\`
|
|
54794
|
-
- Reading a full file when you only need a section -> use \`file_read\` with \`entity\` param or offset/limit
|
|
55851
|
+
- Reading a full file when you only need a section -> use \`file_read\` with \`entity\` param or offset/limit
|
|
55852
|
+
- Using built-in WebFetch for a URL -> use \`fetch_url\` (DOM extraction + markdown + BM25 passage selection cuts 5\u201310\xD7 tokens; pass \`prompt\` to rank passages by relevance)${twoStepSection}
|
|
54795
55853
|
|
|
54796
55854
|
### Tool Reference
|
|
54797
55855
|
|
|
@@ -54829,6 +55887,14 @@ ${readForEditRow}
|
|
|
54829
55887
|
- \`purpose:'explore'\` (default) \u2014 budget-capped, returns outline for large files. Use for browsing and pre-edit understanding.
|
|
54830
55888
|
- \`purpose:'reference'\` \u2014 tight budget, entity/offset reads only. Use for quick lookups.
|
|
54831
55889
|
|
|
55890
|
+
#### Web Fetch (1 tool)
|
|
55891
|
+
|
|
55892
|
+
| Task | Tool | Replaces |
|
|
55893
|
+
|------|------|----------|
|
|
55894
|
+
| Fetch a web page by URL | \`fetch_url\` | Built-in WebFetch |
|
|
55895
|
+
|
|
55896
|
+
\`fetch_url\` strips chrome (nav, footer, ads), converts to markdown, splits into heading-bounded passages, optionally re-ranks passages with BM25 when you pass \`prompt\`, and caches by content hash so re-fetching an unchanged page costs near-zero tokens. Pass \`offset\`/\`limit\` to paginate large pages.
|
|
55897
|
+
|
|
54832
55898
|
#### Shell Compression (automatic)
|
|
54833
55899
|
|
|
54834
55900
|
All shell commands automatically route through unerr's compression layer. It strips ANSI codes, classifies output (diffs, test results, logs, errors), and returns compressed summaries \u2014 saving tokens without losing critical information.
|
|
@@ -55187,7 +56253,8 @@ function registerInstallCommand(program2) {
|
|
|
55187
56253
|
}
|
|
55188
56254
|
process.stderr.write("\n");
|
|
55189
56255
|
process.stderr.write(
|
|
55190
|
-
|
|
56256
|
+
` \x1B[38;2;161;161;170mRestart ${agentDef.name} to start using unerr.\x1B[0m
|
|
56257
|
+
`
|
|
55191
56258
|
);
|
|
55192
56259
|
process.stderr.write("\n");
|
|
55193
56260
|
}
|
|
@@ -55243,29 +56310,13 @@ async function runInstall(cwd, ide, opts) {
|
|
|
55243
56310
|
const { daemonSockPath: daemonSockPath2, probeDaemon: probeDaemon2, ensureRepo: ensureRepo2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
55244
56311
|
const { addRepo: addRepo2, findRepo: findRepo2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
55245
56312
|
const sock = daemonSockPath2();
|
|
55246
|
-
|
|
55247
|
-
if (daemonRunning) {
|
|
56313
|
+
if (await probeDaemon2(sock)) {
|
|
55248
56314
|
if (!findRepo2(cwd)) {
|
|
55249
56315
|
addRepo2(cwd, {});
|
|
55250
56316
|
repoRegistered = true;
|
|
55251
|
-
process.stderr.write(
|
|
55252
|
-
"\x1B[38;2;52;211;153m\u2713\x1B[0m Registered repo with unerrd.\n"
|
|
55253
|
-
);
|
|
55254
|
-
}
|
|
55255
|
-
try {
|
|
55256
|
-
await ensureRepo2(sock, cwd);
|
|
55257
|
-
process.stderr.write(
|
|
55258
|
-
"\x1B[38;2;52;211;153m\u2713\x1B[0m unerr process started via daemon.\n"
|
|
55259
|
-
);
|
|
55260
|
-
} catch {
|
|
55261
|
-
process.stderr.write(
|
|
55262
|
-
"\x1B[38;2;251;191;36m\u26A0\x1B[0m Repo registered but process did not start. It will start on next IDE connection.\n"
|
|
55263
|
-
);
|
|
55264
56317
|
}
|
|
55265
|
-
|
|
55266
|
-
|
|
55267
|
-
"\x1B[38;2;34;211;238m\u25B8\x1B[0m Daemon not running. To use daemon mode: \x1B[1munerr daemon initialize\x1B[0m\n For standalone mode: run \x1B[1munerr\x1B[0m in this directory.\n"
|
|
55268
|
-
);
|
|
56318
|
+
await ensureRepo2(sock, cwd).catch(() => {
|
|
56319
|
+
});
|
|
55269
56320
|
}
|
|
55270
56321
|
} catch {
|
|
55271
56322
|
}
|
|
@@ -55417,12 +56468,12 @@ function showSetupInstructions(agentName) {
|
|
|
55417
56468
|
w(" After restarting your agent, verify unerr tools are available.\n");
|
|
55418
56469
|
w(" You should see tools like: get_callers, search_code, file_read,\n");
|
|
55419
56470
|
w(" file_outline, get_imports, get_callees.\n\n");
|
|
55420
|
-
w(" \x1B[1mStep 4:
|
|
55421
|
-
w(" \x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m\n");
|
|
56471
|
+
w(" \x1B[1mStep 4: Restart your agent\x1B[0m\n");
|
|
56472
|
+
w(" \x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m\n");
|
|
55422
56473
|
w(
|
|
55423
|
-
"
|
|
56474
|
+
" unerr starts automatically when your agent first connects to its MCP server.\n"
|
|
55424
56475
|
);
|
|
55425
|
-
w("
|
|
56476
|
+
w(" No background service to install \u2014 no boot-time setup needed.\n");
|
|
55426
56477
|
}
|
|
55427
56478
|
w("\n");
|
|
55428
56479
|
}
|
|
@@ -55757,8 +56808,8 @@ async function persistPatterns(patterns, _unerrDir) {
|
|
|
55757
56808
|
);
|
|
55758
56809
|
return;
|
|
55759
56810
|
}
|
|
55760
|
-
const { readFileSync:
|
|
55761
|
-
const config = JSON.parse(
|
|
56811
|
+
const { readFileSync: readFileSync71 } = await import("fs");
|
|
56812
|
+
const config = JSON.parse(readFileSync71(configPath2, "utf-8"));
|
|
55762
56813
|
if (!config.repoId || !config.orgId) {
|
|
55763
56814
|
warn("Repo not configured \u2014 cannot persist to CozoDB.");
|
|
55764
56815
|
return;
|
|
@@ -55776,7 +56827,7 @@ async function persistPatterns(patterns, _unerrDir) {
|
|
|
55776
56827
|
const { CozoGraphStore: CozoGraphStore2 } = await Promise.resolve().then(() => (init_local_graph(), local_graph_exports));
|
|
55777
56828
|
const store = await CozoGraphStore2.create(db);
|
|
55778
56829
|
const { unpack } = await import("msgpackr");
|
|
55779
|
-
const raw =
|
|
56830
|
+
const raw = readFileSync71(snapshotPath2);
|
|
55780
56831
|
const buffer = gunzipSync2(raw);
|
|
55781
56832
|
const envelope = unpack(buffer);
|
|
55782
56833
|
await store.loadSnapshot(envelope);
|
|
@@ -56037,7 +57088,13 @@ function parseSettingsFlags(raw) {
|
|
|
56037
57088
|
var write3 = (msg) => process.stderr.write(msg);
|
|
56038
57089
|
function registerPmCommand(program2) {
|
|
56039
57090
|
const pm = program2.command("pm").description("Manage the unerr process manager");
|
|
56040
|
-
pm.command("start").description("Start the unerr process manager").option(
|
|
57091
|
+
pm.command("start").description("Start the unerr process manager").option(
|
|
57092
|
+
"--foreground",
|
|
57093
|
+
"Run in foreground (blocks terminal \u2014 useful for debugging)"
|
|
57094
|
+
).option(
|
|
57095
|
+
"--detached",
|
|
57096
|
+
"Run in-process as a detached supervisor (used by auto-spawn)"
|
|
57097
|
+
).action(async (opts) => {
|
|
56041
57098
|
if (opts.foreground || opts.detached) {
|
|
56042
57099
|
process.title = "unerrd";
|
|
56043
57100
|
const { startDaemon: startDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
|
|
@@ -56049,7 +57106,12 @@ function registerPmCommand(program2) {
|
|
|
56049
57106
|
const child = spawn(
|
|
56050
57107
|
process.execPath,
|
|
56051
57108
|
[unerrBin, "pm", "start", "--detached"],
|
|
56052
|
-
{
|
|
57109
|
+
{
|
|
57110
|
+
detached: true,
|
|
57111
|
+
stdio: "ignore",
|
|
57112
|
+
windowsHide: true,
|
|
57113
|
+
env: { ...process.env }
|
|
57114
|
+
}
|
|
56053
57115
|
);
|
|
56054
57116
|
child.unref();
|
|
56055
57117
|
const { daemonSockPath: daemonSockPath2, probeDaemon: probeDaemon2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
@@ -56066,12 +57128,12 @@ function registerPmCommand(program2) {
|
|
|
56066
57128
|
}
|
|
56067
57129
|
if (running) {
|
|
56068
57130
|
const { homedir: homedir12 } = await import("os");
|
|
56069
|
-
const { join:
|
|
56070
|
-
const pidPath2 =
|
|
57131
|
+
const { join: join85 } = await import("path");
|
|
57132
|
+
const pidPath2 = join85(homedir12(), ".unerr", "unerrd.pid");
|
|
56071
57133
|
let daemonPid = String(child.pid);
|
|
56072
57134
|
try {
|
|
56073
|
-
const { readFileSync:
|
|
56074
|
-
daemonPid =
|
|
57135
|
+
const { readFileSync: readFileSync71 } = await import("fs");
|
|
57136
|
+
daemonPid = readFileSync71(pidPath2, "utf-8").trim();
|
|
56075
57137
|
} catch {
|
|
56076
57138
|
}
|
|
56077
57139
|
write3(
|
|
@@ -56088,8 +57150,8 @@ function registerPmCommand(program2) {
|
|
|
56088
57150
|
pm.command("stop").description("Stop the unerrd supervisor").action(async () => {
|
|
56089
57151
|
const { createConnection: createConnection3 } = await import("net");
|
|
56090
57152
|
const { globalDir: globalDir2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
56091
|
-
const { join:
|
|
56092
|
-
const sock =
|
|
57153
|
+
const { join: join85 } = await import("path");
|
|
57154
|
+
const sock = join85(globalDir2(), "unerrd.sock");
|
|
56093
57155
|
const { existsSync: existsSync82 } = await import("fs");
|
|
56094
57156
|
if (!existsSync82(sock)) {
|
|
56095
57157
|
write3("unerrd is not running (no socket found).\n");
|
|
@@ -56214,7 +57276,7 @@ function registerPmCommand(program2) {
|
|
|
56214
57276
|
pm.command("status").description("List all registered repos and their state").action(async () => {
|
|
56215
57277
|
const repos = listRepos();
|
|
56216
57278
|
if (repos.length === 0) {
|
|
56217
|
-
write3("No repos registered. Use `unerr
|
|
57279
|
+
write3("No repos registered. Use `unerr pm add .` to register.\n");
|
|
56218
57280
|
return;
|
|
56219
57281
|
}
|
|
56220
57282
|
let liveStatus = null;
|
|
@@ -56263,7 +57325,7 @@ function registerPmCommand(program2) {
|
|
|
56263
57325
|
}
|
|
56264
57326
|
write3(
|
|
56265
57327
|
`
|
|
56266
|
-
\x1B[1munerr
|
|
57328
|
+
\x1B[1munerr pm\x1B[0m \u2014 ${repos.length} repo${repos.length === 1 ? "" : "s"} registered
|
|
56267
57329
|
|
|
56268
57330
|
`
|
|
56269
57331
|
);
|
|
@@ -56296,7 +57358,7 @@ function registerPmCommand(program2) {
|
|
|
56296
57358
|
write3(
|
|
56297
57359
|
` ${ni.key}: auto-selected \x1B[1m${ni.auto}\x1B[0m (${ni.reason})
|
|
56298
57360
|
Alternatives: ${ni.alternatives.join(", ")}
|
|
56299
|
-
Override: unerr
|
|
57361
|
+
Override: unerr pm config ${repo.path} --${toKebab(ni.key)}=${ni.alternatives[0]}
|
|
56300
57362
|
`
|
|
56301
57363
|
);
|
|
56302
57364
|
}
|
|
@@ -56327,7 +57389,7 @@ function registerPmCommand(program2) {
|
|
|
56327
57389
|
if (!entry) {
|
|
56328
57390
|
write3(
|
|
56329
57391
|
`\x1B[38;2;248;113;113m\u2717\x1B[0m Not registered: ${targetPath}
|
|
56330
|
-
Register first: unerr
|
|
57392
|
+
Register first: unerr pm add ${targetPath}
|
|
56331
57393
|
`
|
|
56332
57394
|
);
|
|
56333
57395
|
process.exitCode = 1;
|
|
@@ -56382,7 +57444,7 @@ function registerPmCommand(program2) {
|
|
|
56382
57444
|
if (!updated) {
|
|
56383
57445
|
write3(
|
|
56384
57446
|
`\x1B[38;2;248;113;113m\u2717\x1B[0m Not registered: ${targetPath}
|
|
56385
|
-
Register first: unerr
|
|
57447
|
+
Register first: unerr pm add ${targetPath}
|
|
56386
57448
|
`
|
|
56387
57449
|
);
|
|
56388
57450
|
process.exitCode = 1;
|
|
@@ -58025,8 +59087,8 @@ function registerStatusCommand(program2) {
|
|
|
58025
59087
|
try {
|
|
58026
59088
|
const logsDir = join53(unerrDir2, "logs");
|
|
58027
59089
|
if (existsSync50(logsDir)) {
|
|
58028
|
-
const { readdirSync:
|
|
58029
|
-
const logs =
|
|
59090
|
+
const { readdirSync: readdirSync20, statSync: statSync16 } = await import("fs");
|
|
59091
|
+
const logs = readdirSync20(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log")).map((f) => ({
|
|
58030
59092
|
name: f,
|
|
58031
59093
|
mtime: statSync16(join53(logsDir, f)).mtimeMs
|
|
58032
59094
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
@@ -59034,7 +60096,7 @@ var SOURCE_DIRS = /* @__PURE__ */ new Set([
|
|
|
59034
60096
|
]);
|
|
59035
60097
|
var DETECTION_THRESHOLD = 5;
|
|
59036
60098
|
async function detectProjectRoot(cwd) {
|
|
59037
|
-
const { readdirSync:
|
|
60099
|
+
const { readdirSync: readdirSync20 } = await import("fs");
|
|
59038
60100
|
let score = 0;
|
|
59039
60101
|
const signals = [];
|
|
59040
60102
|
let hasGit = false;
|
|
@@ -59052,7 +60114,7 @@ async function detectProjectRoot(cwd) {
|
|
|
59052
60114
|
}
|
|
59053
60115
|
let rootEntries = [];
|
|
59054
60116
|
try {
|
|
59055
|
-
rootEntries =
|
|
60117
|
+
rootEntries = readdirSync20(cwd, { withFileTypes: true }).map((e) => ({
|
|
59056
60118
|
name: e.name,
|
|
59057
60119
|
isFile: e.isFile(),
|
|
59058
60120
|
isDir: e.isDirectory()
|
|
@@ -59143,7 +60205,7 @@ async function detectProjectRoot(cwd) {
|
|
|
59143
60205
|
}
|
|
59144
60206
|
function dirHasCodeFile(dir) {
|
|
59145
60207
|
try {
|
|
59146
|
-
const entries =
|
|
60208
|
+
const entries = readdirSync20(dir, { withFileTypes: true });
|
|
59147
60209
|
for (const entry of entries) {
|
|
59148
60210
|
if (!entry.isFile()) continue;
|
|
59149
60211
|
const dot = entry.name.lastIndexOf(".");
|
|
@@ -59167,7 +60229,7 @@ async function detectProjectRoot(cwd) {
|
|
|
59167
60229
|
for (const dirEntry of rootEntries) {
|
|
59168
60230
|
if (!dirEntry.isDir || dirEntry.name.startsWith(".")) continue;
|
|
59169
60231
|
if (!SOURCE_DIRS.has(dirEntry.name.toLowerCase())) continue;
|
|
59170
|
-
const sourceDir =
|
|
60232
|
+
const sourceDir = join84(cwd, dirEntry.name);
|
|
59171
60233
|
const srcCodeFile = dirHasCodeFile(sourceDir);
|
|
59172
60234
|
if (srcCodeFile) {
|
|
59173
60235
|
score += 6;
|
|
@@ -59175,11 +60237,11 @@ async function detectProjectRoot(cwd) {
|
|
|
59175
60237
|
break;
|
|
59176
60238
|
}
|
|
59177
60239
|
try {
|
|
59178
|
-
const subEntries =
|
|
60240
|
+
const subEntries = readdirSync20(sourceDir, { withFileTypes: true });
|
|
59179
60241
|
let found = false;
|
|
59180
60242
|
for (const sub of subEntries) {
|
|
59181
60243
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
59182
|
-
const deepCode = dirHasCodeFile(
|
|
60244
|
+
const deepCode = dirHasCodeFile(join84(sourceDir, sub.name));
|
|
59183
60245
|
if (deepCode) {
|
|
59184
60246
|
score += 6;
|
|
59185
60247
|
signals.push(`code-src: ${dirEntry.name}/${sub.name}/${deepCode}`);
|
|
@@ -59202,10 +60264,10 @@ async function detectProjectRoot(cwd) {
|
|
|
59202
60264
|
};
|
|
59203
60265
|
}
|
|
59204
60266
|
function readLocalConfig(cwd) {
|
|
59205
|
-
const configPath2 =
|
|
60267
|
+
const configPath2 = join84(cwd, ".unerr", "config.json");
|
|
59206
60268
|
if (!existsSync81(configPath2)) return null;
|
|
59207
60269
|
try {
|
|
59208
|
-
return JSON.parse(
|
|
60270
|
+
return JSON.parse(readFileSync70(configPath2, "utf-8"));
|
|
59209
60271
|
} catch {
|
|
59210
60272
|
return null;
|
|
59211
60273
|
}
|
|
@@ -59328,8 +60390,8 @@ async function daemonChildBoot(cwd) {
|
|
|
59328
60390
|
repoId: config.repoId,
|
|
59329
60391
|
daemonChild: true
|
|
59330
60392
|
});
|
|
59331
|
-
const stateDir =
|
|
59332
|
-
const sockPath2 =
|
|
60393
|
+
const stateDir = join84(cwd, ".unerr", "state");
|
|
60394
|
+
const sockPath2 = join84(stateDir, "proxy.sock");
|
|
59333
60395
|
if (process.send) {
|
|
59334
60396
|
process.send({ type: "ready", sock: sockPath2 });
|
|
59335
60397
|
}
|
|
@@ -59383,8 +60445,18 @@ async function mcpBoot(cwd) {
|
|
|
59383
60445
|
keep: 5
|
|
59384
60446
|
});
|
|
59385
60447
|
initFileLog(cwd);
|
|
60448
|
+
const { StaticCatalogInterceptor: StaticCatalogInterceptor2 } = await Promise.resolve().then(() => (init_bridge_catalog(), bridge_catalog_exports));
|
|
60449
|
+
const interceptor = new StaticCatalogInterceptor2();
|
|
59386
60450
|
const stdinPreBuffer = [];
|
|
59387
|
-
const preBufferHandler = (chunk) =>
|
|
60451
|
+
const preBufferHandler = (chunk) => {
|
|
60452
|
+
const out = interceptor.ingest(chunk);
|
|
60453
|
+
for (const reply of out.replies) {
|
|
60454
|
+
process.stdout.write(reply);
|
|
60455
|
+
}
|
|
60456
|
+
for (const buf of out.forward) {
|
|
60457
|
+
stdinPreBuffer.push(buf);
|
|
60458
|
+
}
|
|
60459
|
+
};
|
|
59388
60460
|
process.stdin.on("data", preBufferHandler);
|
|
59389
60461
|
let stdinEndedEarly = false;
|
|
59390
60462
|
const earlyEndHandler = () => {
|
|
@@ -59446,6 +60518,8 @@ ${antiSignals.length > 0 ? ` Negative signals: ${antiSignals.map((s) => s.repla
|
|
|
59446
60518
|
if (process.stdin.listenerCount("data") > 0 && stdinPreBuffer.length >= 0) {
|
|
59447
60519
|
process.stdin.removeListener("data", preBufferHandler);
|
|
59448
60520
|
process.stdin.removeListener("end", earlyEndHandler);
|
|
60521
|
+
const partial = interceptor.drainPartial();
|
|
60522
|
+
if (partial) stdinPreBuffer.push(partial);
|
|
59449
60523
|
bufferForBridge = stdinPreBuffer.slice();
|
|
59450
60524
|
stdinPreBuffer.length = 0;
|
|
59451
60525
|
}
|
|
@@ -59500,10 +60574,10 @@ async function discoverWithRetry(cwd, daemonSockPath2, probeDaemon2, ensureRepo2
|
|
|
59500
60574
|
let attempt = 0;
|
|
59501
60575
|
let spawnAttempted = false;
|
|
59502
60576
|
for (; ; ) {
|
|
59503
|
-
const repoSock =
|
|
60577
|
+
const repoSock = join84(cwd, ".unerr", "state", "proxy.sock");
|
|
59504
60578
|
if (existsSync81(repoSock)) {
|
|
59505
60579
|
const { PidLock: PidLock2 } = await Promise.resolve().then(() => (init_pid_lock(), pid_lock_exports));
|
|
59506
|
-
const pidLock = new PidLock2(
|
|
60580
|
+
const pidLock = new PidLock2(join84(cwd, ".unerr", "state"));
|
|
59507
60581
|
const probeResult = await pidLock.probe();
|
|
59508
60582
|
if (probeResult.alive) {
|
|
59509
60583
|
return {
|