@merittdev/horus 0.8.11 → 0.8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +196 -52
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -43846,7 +43846,7 @@ var require_pg_pool = __commonJS({
|
|
|
43846
43846
|
function throwOnDoubleRelease() {
|
|
43847
43847
|
throw new Error("Release called on client which has already been released to the pool.");
|
|
43848
43848
|
}
|
|
43849
|
-
function
|
|
43849
|
+
function promisify6(Promise2, callback) {
|
|
43850
43850
|
if (callback) {
|
|
43851
43851
|
return { callback, result: void 0 };
|
|
43852
43852
|
}
|
|
@@ -43984,7 +43984,7 @@ var require_pg_pool = __commonJS({
|
|
|
43984
43984
|
const err2 = new Error("Cannot use a pool after calling end on the pool");
|
|
43985
43985
|
return cb ? cb(err2) : this.Promise.reject(err2);
|
|
43986
43986
|
}
|
|
43987
|
-
const response =
|
|
43987
|
+
const response = promisify6(this.Promise, cb);
|
|
43988
43988
|
const result = response.result;
|
|
43989
43989
|
if (this._isFull() || this._idle.length) {
|
|
43990
43990
|
if (this._idle.length) {
|
|
@@ -44169,7 +44169,7 @@ var require_pg_pool = __commonJS({
|
|
|
44169
44169
|
}
|
|
44170
44170
|
query(text2, values2, cb) {
|
|
44171
44171
|
if (typeof text2 === "function") {
|
|
44172
|
-
const response2 =
|
|
44172
|
+
const response2 = promisify6(this.Promise, text2);
|
|
44173
44173
|
setImmediate(function() {
|
|
44174
44174
|
return response2.callback(new Error("Passing a function as the first parameter to pool.query is not supported"));
|
|
44175
44175
|
});
|
|
@@ -44179,7 +44179,7 @@ var require_pg_pool = __commonJS({
|
|
|
44179
44179
|
cb = values2;
|
|
44180
44180
|
values2 = void 0;
|
|
44181
44181
|
}
|
|
44182
|
-
const response =
|
|
44182
|
+
const response = promisify6(this.Promise, cb);
|
|
44183
44183
|
cb = response.callback;
|
|
44184
44184
|
this.connect((err2, client) => {
|
|
44185
44185
|
if (err2) {
|
|
@@ -44224,7 +44224,7 @@ var require_pg_pool = __commonJS({
|
|
|
44224
44224
|
return cb ? cb(err2) : this.Promise.reject(err2);
|
|
44225
44225
|
}
|
|
44226
44226
|
this.ending = true;
|
|
44227
|
-
const promised =
|
|
44227
|
+
const promised = promisify6(this.Promise, cb);
|
|
44228
44228
|
this._endCallback = promised.callback;
|
|
44229
44229
|
this._pulseQueue();
|
|
44230
44230
|
return promised.result;
|
|
@@ -65880,8 +65880,8 @@ init_cjs_shims();
|
|
|
65880
65880
|
|
|
65881
65881
|
// ../../packages/core/src/version.ts
|
|
65882
65882
|
init_cjs_shims();
|
|
65883
|
-
var HORUS_VERSION = true ? "0.8.
|
|
65884
|
-
var PINNED_SOURCE_VERSION = "1.5.
|
|
65883
|
+
var HORUS_VERSION = true ? "0.8.13" : "dev";
|
|
65884
|
+
var PINNED_SOURCE_VERSION = "1.5.2";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
65887
65887
|
init_cjs_shims();
|
|
@@ -70987,7 +70987,7 @@ async function getSourceVersion() {
|
|
|
70987
70987
|
var SourceVersionMismatchError = class extends Error {
|
|
70988
70988
|
constructor(installed, pinned) {
|
|
70989
70989
|
super(
|
|
70990
|
-
`horus-source ${installed} is installed but Horus is pinned to ${pinned}. A drifted backend builds a graph this CLI cannot map and can corrupt the index (e.g. duplicate-primary-key failures during "Running initial index").
|
|
70990
|
+
`horus-source ${installed} is installed but Horus is pinned to ${pinned}. A drifted backend builds a graph this CLI cannot map and can corrupt the index (e.g. duplicate-primary-key failures during "Running initial index"). Run \`horus update\` to sync the backend, or re-run the installer: curl -fsSL https://horus.sh/install.sh | bash`
|
|
70991
70991
|
);
|
|
70992
70992
|
this.installed = installed;
|
|
70993
70993
|
this.pinned = pinned;
|
|
@@ -71007,7 +71007,7 @@ function isAnalyzed(root) {
|
|
|
71007
71007
|
}
|
|
71008
71008
|
async function analyzeRepo(root) {
|
|
71009
71009
|
const bin = await resolveSourceBin();
|
|
71010
|
-
if (!bin) throw new Error("horus-source not found on PATH. Install it:
|
|
71010
|
+
if (!bin) throw new Error("horus-source not found on PATH. Install it: curl -fsSL https://horus.sh/install.sh | bash");
|
|
71011
71011
|
await exec(bin, ["analyze", "."], {
|
|
71012
71012
|
cwd: root,
|
|
71013
71013
|
timeout: 9e5,
|
|
@@ -71083,7 +71083,7 @@ function startHost(root, port) {
|
|
|
71083
71083
|
child.on("error", (err2) => {
|
|
71084
71084
|
if (err2.code === "ENOENT") {
|
|
71085
71085
|
process.stderr.write(
|
|
71086
|
-
"\nhorus-source not found on PATH. Install it:
|
|
71086
|
+
"\nhorus-source not found on PATH. Install it: curl -fsSL https://horus.sh/install.sh | bash\n"
|
|
71087
71087
|
);
|
|
71088
71088
|
}
|
|
71089
71089
|
});
|
|
@@ -71159,7 +71159,14 @@ var SourceCodeProvider = class {
|
|
|
71159
71159
|
};
|
|
71160
71160
|
}
|
|
71161
71161
|
async rows(query) {
|
|
71162
|
-
|
|
71162
|
+
try {
|
|
71163
|
+
return (await this.client.cypher(query)).rows;
|
|
71164
|
+
} catch (err2) {
|
|
71165
|
+
if (err2 instanceof SourceHttpError && err2.status >= 400 && err2.status < 500) {
|
|
71166
|
+
return [];
|
|
71167
|
+
}
|
|
71168
|
+
throw err2;
|
|
71169
|
+
}
|
|
71163
71170
|
}
|
|
71164
71171
|
cypherRowToSymbol(row, idIdx, nameIdx, fileIdx, lineIdx, classNameIdx) {
|
|
71165
71172
|
const startLine = lineIdx != null && row[lineIdx] != null ? Number(row[lineIdx]) : void 0;
|
|
@@ -71571,6 +71578,18 @@ function buildLevelFilter(m5, minLevel) {
|
|
|
71571
71578
|
);
|
|
71572
71579
|
return { terms: { [m5.levelField]: stringLevels } };
|
|
71573
71580
|
}
|
|
71581
|
+
function buildWhereFilters(where) {
|
|
71582
|
+
if (where === void 0 || where.length === 0) return [];
|
|
71583
|
+
return where.map((w4) => ({
|
|
71584
|
+
bool: {
|
|
71585
|
+
should: [
|
|
71586
|
+
{ term: { [w4.field]: w4.value } },
|
|
71587
|
+
{ term: { [`${w4.field}.keyword`]: w4.value } }
|
|
71588
|
+
],
|
|
71589
|
+
minimum_should_match: 1
|
|
71590
|
+
}
|
|
71591
|
+
}));
|
|
71592
|
+
}
|
|
71574
71593
|
function buildTextMust(q2, mapping = MERITT_FIELD_MAPPING) {
|
|
71575
71594
|
if (q2.text === void 0) return [{ match_all: {} }];
|
|
71576
71595
|
if (q2.broadText === true) {
|
|
@@ -71595,6 +71614,7 @@ function buildSearchBody(q2, mapping = MERITT_FIELD_MAPPING) {
|
|
|
71595
71614
|
if (q2.service !== void 0) {
|
|
71596
71615
|
filters.push({ term: { [serviceTermField(mapping)]: q2.service } });
|
|
71597
71616
|
}
|
|
71617
|
+
filters.push(...buildWhereFilters(q2.where));
|
|
71598
71618
|
const mustClause = buildTextMust(q2, mapping);
|
|
71599
71619
|
return {
|
|
71600
71620
|
query: {
|
|
@@ -71623,6 +71643,7 @@ function buildErrorAggBody(q2, field, mapping = MERITT_FIELD_MAPPING) {
|
|
|
71623
71643
|
if (q2.eventCode !== void 0) {
|
|
71624
71644
|
filters.push({ term: { [signatureTermField(mapping)]: q2.eventCode } });
|
|
71625
71645
|
}
|
|
71646
|
+
filters.push(...buildWhereFilters(q2.where));
|
|
71626
71647
|
const aggField = field === mapping.eventCodeField ? signatureTermField(mapping) : `${field}.keyword`;
|
|
71627
71648
|
return {
|
|
71628
71649
|
size: 0,
|
|
@@ -71857,6 +71878,7 @@ function buildErrorAnalysisBody(q2, field = "event_code", mapping = MERITT_FIELD
|
|
|
71857
71878
|
if (q2.eventCode !== void 0) {
|
|
71858
71879
|
filters.push({ term: { [sigTerm]: q2.eventCode } });
|
|
71859
71880
|
}
|
|
71881
|
+
filters.push(...buildWhereFilters(q2.where));
|
|
71860
71882
|
const mustClause = buildTextMust(q2, mapping);
|
|
71861
71883
|
return {
|
|
71862
71884
|
size: 0,
|
|
@@ -74269,6 +74291,14 @@ function parseGitLog(stdout) {
|
|
|
74269
74291
|
}
|
|
74270
74292
|
return commits;
|
|
74271
74293
|
}
|
|
74294
|
+
async function resolvesToCommit(repoPath, rev) {
|
|
74295
|
+
try {
|
|
74296
|
+
await exec2("git", ["-C", repoPath, "rev-parse", "--verify", "--quiet", `${rev}^{commit}`]);
|
|
74297
|
+
return true;
|
|
74298
|
+
} catch {
|
|
74299
|
+
return false;
|
|
74300
|
+
}
|
|
74301
|
+
}
|
|
74272
74302
|
async function gitLog(repoPath, opts = {}) {
|
|
74273
74303
|
const args2 = [
|
|
74274
74304
|
"-C",
|
|
@@ -74278,11 +74308,16 @@ async function gitLog(repoPath, opts = {}) {
|
|
|
74278
74308
|
"--pretty=format:%H%h%an%aI%s",
|
|
74279
74309
|
"--name-only"
|
|
74280
74310
|
];
|
|
74281
|
-
if (opts.since !== void 0) {
|
|
74282
|
-
|
|
74283
|
-
|
|
74284
|
-
|
|
74285
|
-
|
|
74311
|
+
if (opts.since !== void 0 && await resolvesToCommit(repoPath, opts.since)) {
|
|
74312
|
+
const end = opts.until !== void 0 && await resolvesToCommit(repoPath, opts.until) ? opts.until : "HEAD";
|
|
74313
|
+
args2.push(`${opts.since}..${end}`);
|
|
74314
|
+
} else {
|
|
74315
|
+
if (opts.since !== void 0) {
|
|
74316
|
+
args2.push("--since=" + opts.since);
|
|
74317
|
+
}
|
|
74318
|
+
if (opts.until !== void 0) {
|
|
74319
|
+
args2.push("--until=" + opts.until);
|
|
74320
|
+
}
|
|
74286
74321
|
}
|
|
74287
74322
|
args2.push("--max-count=" + (opts.maxCount ?? 200));
|
|
74288
74323
|
const { stdout } = await exec2("git", args2, { maxBuffer: 10 * 1024 * 1024 });
|
|
@@ -93854,7 +93889,7 @@ async function investigate(input, deps) {
|
|
|
93854
93889
|
const headlineLinked = headlineCause !== void 0 && isLinkedToSeed(headlineCause.sourceEvidenceIds);
|
|
93855
93890
|
if (headlineCause && !headlineLinked) confidence = Math.min(confidence, 0.6);
|
|
93856
93891
|
const banner2 = degradedNoSource ? "Runtime-only (source intelligence unavailable). " : "";
|
|
93857
|
-
const scope = top ? `resolved to ${label} (${area})` : `over runtime evidence (${area})`;
|
|
93892
|
+
const scope = top ? seedIsLowConfidence ? "could not be confidently localized from source alone" : `resolved to ${label} (${area})` : `over runtime evidence (${area})`;
|
|
93858
93893
|
const seedDisclaimer = seedIsLowConfidence && top ? `\u26A0 No symbol closely matched "${hint}" \u2014 "${top.name}" is a low-confidence closest match (semantic). Refine with an exact symbol or error code to target precisely. ` : "";
|
|
93859
93894
|
let summary = headlineCause ? headlineLinked ? `${seedDisclaimer}${banner2}Investigation of "${hint}" ${scope}. Top suspected cause: ${headlineCause.title}.` : `${seedDisclaimer}${banner2}Investigation of "${hint}" ${scope}. No cause is structurally linked to the seed; the strongest co-occurring signal (a lead to verify, not a diagnosis) is: ${headlineCause.title}.` : `${seedDisclaimer}${banner2}Investigation of "${hint}" ${scope}. No dominant suspected cause emerged from the available ${degradedNoSource ? "runtime" : "structural"} evidence.`;
|
|
93860
93895
|
const hasIncidentSignal = directLogEvIds.length > 0 || latencyMetricEvIds.length > 0 || queueMetricEvIds.length > 0 || dataAnomalyEvIds.length > 0 || queueFailureSignals.length > 0 || findings2.some((f5) => f5.kind === "anomaly") || headlineCause !== void 0;
|
|
@@ -94374,7 +94409,19 @@ var EXTERNAL_MARKERS = [
|
|
|
94374
94409
|
"elasticsearch",
|
|
94375
94410
|
"prometheus",
|
|
94376
94411
|
"mongo",
|
|
94377
|
-
"graphql"
|
|
94412
|
+
"graphql",
|
|
94413
|
+
// Python ecosystem (HOR-356): the markers above are Node-centric, so Python backends'
|
|
94414
|
+
// DB/queue/web stacks went undetected (e.g. redash's Postgres + Celery). 'mongo'/'redis'
|
|
94415
|
+
// already cover pymongo/redis-py by substring.
|
|
94416
|
+
"sqlalchemy",
|
|
94417
|
+
"sqlmodel",
|
|
94418
|
+
"psycopg",
|
|
94419
|
+
"celery",
|
|
94420
|
+
"fastapi",
|
|
94421
|
+
"flask",
|
|
94422
|
+
"django",
|
|
94423
|
+
"kafka",
|
|
94424
|
+
"boto3"
|
|
94378
94425
|
];
|
|
94379
94426
|
async function discoverArchitecture(deps) {
|
|
94380
94427
|
const nodeStats = await (async () => {
|
|
@@ -97295,7 +97342,7 @@ function parseHostPort(hostUrl) {
|
|
|
97295
97342
|
function ensureHostReasonHint(reason) {
|
|
97296
97343
|
switch (reason) {
|
|
97297
97344
|
case "source-unavailable":
|
|
97298
|
-
return "horus-source is not installed \u2014 install it:
|
|
97345
|
+
return "horus-source is not installed \u2014 install it: curl -fsSL https://horus.sh/install.sh | bash";
|
|
97299
97346
|
case "version-mismatch":
|
|
97300
97347
|
return "the installed horus-source backend does not match the version Horus is pinned to \u2014 reinstall the pinned version (run `horus status` to see it)";
|
|
97301
97348
|
case "not-analyzed":
|
|
@@ -97444,7 +97491,7 @@ async function runIndex(opts) {
|
|
|
97444
97491
|
spawned = true;
|
|
97445
97492
|
console.log(import_picocolors3.default.bold(`Indexing ${label}`) + import_picocolors3.default.dim(` (${root})`));
|
|
97446
97493
|
if (!await sourceAvailable()) {
|
|
97447
|
-
console.error(import_picocolors3.default.red("horus-source not found on PATH. Install it:
|
|
97494
|
+
console.error(import_picocolors3.default.red("horus-source not found on PATH. Install it: curl -fsSL https://horus.sh/install.sh | bash"));
|
|
97448
97495
|
return 1;
|
|
97449
97496
|
}
|
|
97450
97497
|
try {
|
|
@@ -97465,28 +97512,41 @@ async function runIndex(opts) {
|
|
|
97465
97512
|
console.log(import_picocolors3.default.dim(" already analyzed"));
|
|
97466
97513
|
}
|
|
97467
97514
|
const priorPort = parseHostPort(configuredHost ?? "") ?? parseHostPort(readSourceHostUrl(root) ?? "");
|
|
97468
|
-
|
|
97469
|
-
|
|
97470
|
-
|
|
97471
|
-
|
|
97472
|
-
|
|
97515
|
+
const MAX_PORT_ATTEMPTS = 4;
|
|
97516
|
+
let started = false;
|
|
97517
|
+
for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS && !started; attempt += 1) {
|
|
97518
|
+
let port;
|
|
97519
|
+
if (attempt === 0 && priorPort !== null) {
|
|
97520
|
+
try {
|
|
97521
|
+
port = await findFreePort(priorPort, priorPort);
|
|
97522
|
+
} catch {
|
|
97523
|
+
port = await findFreePort();
|
|
97524
|
+
}
|
|
97525
|
+
} else {
|
|
97473
97526
|
port = await findFreePort();
|
|
97474
97527
|
}
|
|
97475
|
-
|
|
97476
|
-
|
|
97528
|
+
hostUrl = `http://127.0.0.1:${port}`;
|
|
97529
|
+
console.log(
|
|
97530
|
+
import_picocolors3.default.dim(
|
|
97531
|
+
attempt === 0 ? ` starting source-intelligence host on port ${port}\u2026` : ` port contended \u2014 retrying on ${port}\u2026`
|
|
97532
|
+
)
|
|
97533
|
+
);
|
|
97534
|
+
startHost(root, port);
|
|
97535
|
+
if (await waitForHost(hostUrl)) {
|
|
97536
|
+
reconcileSpawnedHostPid(root, port);
|
|
97537
|
+
started = true;
|
|
97538
|
+
} else {
|
|
97539
|
+
removeSpawnedHostRecord(root);
|
|
97540
|
+
}
|
|
97477
97541
|
}
|
|
97478
|
-
|
|
97479
|
-
console.log(import_picocolors3.default.dim(` starting source-intelligence host on port ${port}\u2026`));
|
|
97480
|
-
startHost(root, port);
|
|
97481
|
-
if (!await waitForHost(hostUrl)) {
|
|
97482
|
-
removeSpawnedHostRecord(root);
|
|
97542
|
+
if (!started) {
|
|
97483
97543
|
console.error(
|
|
97484
97544
|
import_picocolors3.default.red(` Source-intelligence host did not become healthy \u2014 see ${root}/.horus/source-host.log`)
|
|
97485
97545
|
);
|
|
97486
97546
|
return 1;
|
|
97487
97547
|
}
|
|
97488
|
-
reconcileSpawnedHostPid(root, port);
|
|
97489
97548
|
}
|
|
97549
|
+
if (!hostUrl) return 1;
|
|
97490
97550
|
await stitchQueueMap(hostUrl, dbUrl, label);
|
|
97491
97551
|
buildKnowledgeIndex(root, config2, label, configuredProject, opts);
|
|
97492
97552
|
const hasLocalConfig = discoverLocalConfig(root) !== null;
|
|
@@ -112108,6 +112168,19 @@ function resolveRawLevel(opts) {
|
|
|
112108
112168
|
if (opts.allLevels) return void 0;
|
|
112109
112169
|
return opts.level ?? "error";
|
|
112110
112170
|
}
|
|
112171
|
+
function parseWhere(raw) {
|
|
112172
|
+
if (raw === void 0) return [];
|
|
112173
|
+
return raw.map((entry2) => {
|
|
112174
|
+
const eq2 = entry2.indexOf("=");
|
|
112175
|
+
if (eq2 <= 0) {
|
|
112176
|
+
throw new Error(
|
|
112177
|
+
`Invalid --where "${entry2}" \u2014 expected field=value, e.g. --where context.brand_id=42`
|
|
112178
|
+
);
|
|
112179
|
+
}
|
|
112180
|
+
return { field: entry2.slice(0, eq2).trim(), value: entry2.slice(eq2 + 1).trim() };
|
|
112181
|
+
});
|
|
112182
|
+
}
|
|
112183
|
+
var whereLabel = (where) => where.map((w4) => `${w4.field}=${w4.value}`).join(", ");
|
|
112111
112184
|
function levelColor(level, text2) {
|
|
112112
112185
|
if (level === "error" || level === "fatal") return import_picocolors27.default.red(text2);
|
|
112113
112186
|
if (level === "warn") return import_picocolors27.default.yellow(text2);
|
|
@@ -112123,6 +112196,15 @@ function matchServiceLabel(requested, candidates) {
|
|
|
112123
112196
|
}
|
|
112124
112197
|
async function runLogs(service, opts) {
|
|
112125
112198
|
try {
|
|
112199
|
+
let where;
|
|
112200
|
+
try {
|
|
112201
|
+
where = parseWhere(opts.where);
|
|
112202
|
+
} catch (err2) {
|
|
112203
|
+
console.error(import_picocolors27.default.red(err2.message));
|
|
112204
|
+
return 1;
|
|
112205
|
+
}
|
|
112206
|
+
const hasWhere = where.length > 0;
|
|
112207
|
+
const whereArg = hasWhere ? where : void 0;
|
|
112126
112208
|
const config2 = await loadConfig(opts.config, { name: opts.name });
|
|
112127
112209
|
let renv;
|
|
112128
112210
|
try {
|
|
@@ -112174,8 +112256,9 @@ async function runLogs(service, opts) {
|
|
|
112174
112256
|
const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
|
|
112175
112257
|
const fromDisplay = from.slice(0, 16).replace("T", " ");
|
|
112176
112258
|
if (opts.raw === true) {
|
|
112177
|
-
const rawLevel = resolveRawLevel(opts);
|
|
112178
|
-
const
|
|
112259
|
+
const rawLevel = hasWhere && opts.level === void 0 ? void 0 : resolveRawLevel(opts);
|
|
112260
|
+
const defaultLimit = hasWhere ? 200 : 20;
|
|
112261
|
+
const limit = opts.limit !== void 0 ? Math.min(Number(opts.limit), 1e3) : defaultLimit;
|
|
112179
112262
|
const records = await logs.searchLogs({
|
|
112180
112263
|
service: resolvedService,
|
|
112181
112264
|
from,
|
|
@@ -112184,8 +112267,10 @@ async function runLogs(service, opts) {
|
|
|
112184
112267
|
// grep searches message + detail + context.* so error strings buried
|
|
112185
112268
|
// outside the message field are still found (HOR-216).
|
|
112186
112269
|
broadText: opts.grep !== void 0,
|
|
112270
|
+
where: whereArg,
|
|
112187
112271
|
limit
|
|
112188
112272
|
});
|
|
112273
|
+
const truncated = records.length === limit;
|
|
112189
112274
|
if (opts.json) {
|
|
112190
112275
|
console.log(
|
|
112191
112276
|
JSON.stringify(
|
|
@@ -112197,7 +112282,9 @@ async function runLogs(service, opts) {
|
|
|
112197
112282
|
index: indexPattern,
|
|
112198
112283
|
from,
|
|
112199
112284
|
level: rawLevel ?? "all",
|
|
112200
|
-
grep: opts.grep ?? null
|
|
112285
|
+
grep: opts.grep ?? null,
|
|
112286
|
+
where: whereArg ?? null,
|
|
112287
|
+
truncated
|
|
112201
112288
|
},
|
|
112202
112289
|
records: records.map((r) => ({
|
|
112203
112290
|
timestamp: r.timestamp,
|
|
@@ -112219,6 +112306,11 @@ async function runLogs(service, opts) {
|
|
|
112219
112306
|
);
|
|
112220
112307
|
return 0;
|
|
112221
112308
|
}
|
|
112309
|
+
console.error(
|
|
112310
|
+
import_picocolors27.default.dim(
|
|
112311
|
+
`logs: ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + ` \xB7 from ${fromDisplay} UTC \xB7 level ${rawLevel ?? "all"}` + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (rawLevel === "error" && !hasWhere ? " (error+ only \u2014 use --all-levels for info/debug)" : "")
|
|
112312
|
+
)
|
|
112313
|
+
);
|
|
112222
112314
|
if (records.length === 0) {
|
|
112223
112315
|
console.log(import_picocolors27.default.dim("No logs matched."));
|
|
112224
112316
|
return 0;
|
|
@@ -112233,6 +112325,13 @@ async function runLogs(service, opts) {
|
|
|
112233
112325
|
console.log(import_picocolors27.default.dim(` ${key}: ${value.slice(0, 200)}`));
|
|
112234
112326
|
}
|
|
112235
112327
|
}
|
|
112328
|
+
if (truncated) {
|
|
112329
|
+
console.error(
|
|
112330
|
+
import_picocolors27.default.yellow(
|
|
112331
|
+
`[warn] showing the first ${limit} record(s) \u2014 results may be truncated. Narrow with --where/--since/--level or raise --limit (max 1000).`
|
|
112332
|
+
)
|
|
112333
|
+
);
|
|
112334
|
+
}
|
|
112236
112335
|
return 0;
|
|
112237
112336
|
}
|
|
112238
112337
|
if (opts.groupBy !== void 0) {
|
|
@@ -112242,7 +112341,8 @@ async function runLogs(service, opts) {
|
|
|
112242
112341
|
service: resolvedService,
|
|
112243
112342
|
from,
|
|
112244
112343
|
text: opts.grep,
|
|
112245
|
-
broadText: opts.grep !== void 0
|
|
112344
|
+
broadText: opts.grep !== void 0,
|
|
112345
|
+
where: whereArg
|
|
112246
112346
|
},
|
|
112247
112347
|
field
|
|
112248
112348
|
);
|
|
@@ -112258,6 +112358,7 @@ async function runLogs(service, opts) {
|
|
|
112258
112358
|
from,
|
|
112259
112359
|
severity: "error+",
|
|
112260
112360
|
grep: opts.grep ?? null,
|
|
112361
|
+
where: whereArg ?? null,
|
|
112261
112362
|
groupBy: field
|
|
112262
112363
|
},
|
|
112263
112364
|
buckets
|
|
@@ -112270,7 +112371,7 @@ async function runLogs(service, opts) {
|
|
|
112270
112371
|
}
|
|
112271
112372
|
console.log(
|
|
112272
112373
|
import_picocolors27.default.bold(`Top ${field}`) + import_picocolors27.default.dim(
|
|
112273
|
-
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
112374
|
+
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112274
112375
|
)
|
|
112275
112376
|
);
|
|
112276
112377
|
console.log(import_picocolors27.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
@@ -112291,12 +112392,13 @@ async function runLogs(service, opts) {
|
|
|
112291
112392
|
service: resolvedService,
|
|
112292
112393
|
from,
|
|
112293
112394
|
text: opts.grep,
|
|
112294
|
-
broadText: opts.grep !== void 0
|
|
112395
|
+
broadText: opts.grep !== void 0,
|
|
112396
|
+
where: whereArg
|
|
112295
112397
|
});
|
|
112296
112398
|
let scopeService = resolvedService;
|
|
112297
112399
|
let broadeningNote;
|
|
112298
112400
|
if (analysis.signatures.length === 0 && resolvedService !== void 0) {
|
|
112299
|
-
const broader = await logs.analyzeErrors({ from, text: opts.grep, broadText: opts.grep !== void 0 });
|
|
112401
|
+
const broader = await logs.analyzeErrors({ from, text: opts.grep, broadText: opts.grep !== void 0, where: whereArg });
|
|
112300
112402
|
if (broader.signatures.length > 0) {
|
|
112301
112403
|
const match = matchServiceLabel(resolvedService, broader.affectedServices);
|
|
112302
112404
|
if (match.kind === "unique" && match.matched !== void 0 && match.matched !== resolvedService) {
|
|
@@ -112304,7 +112406,8 @@ async function runLogs(service, opts) {
|
|
|
112304
112406
|
service: match.matched,
|
|
112305
112407
|
from,
|
|
112306
112408
|
text: opts.grep,
|
|
112307
|
-
broadText: opts.grep !== void 0
|
|
112409
|
+
broadText: opts.grep !== void 0,
|
|
112410
|
+
where: whereArg
|
|
112308
112411
|
});
|
|
112309
112412
|
if (scoped.signatures.length > 0) {
|
|
112310
112413
|
analysis = scoped;
|
|
@@ -112337,7 +112440,8 @@ async function runLogs(service, opts) {
|
|
|
112337
112440
|
index: indexPattern,
|
|
112338
112441
|
from,
|
|
112339
112442
|
severity: "error+",
|
|
112340
|
-
grep: opts.grep ?? null
|
|
112443
|
+
grep: opts.grep ?? null,
|
|
112444
|
+
where: whereArg ?? null
|
|
112341
112445
|
},
|
|
112342
112446
|
totalErrors: analysis.totalErrors,
|
|
112343
112447
|
signatures: analysis.signatures,
|
|
@@ -112352,7 +112456,7 @@ async function runLogs(service, opts) {
|
|
|
112352
112456
|
}
|
|
112353
112457
|
console.log(
|
|
112354
112458
|
import_picocolors27.default.bold(`Error analysis`) + import_picocolors27.default.dim(
|
|
112355
|
-
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
112459
|
+
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112356
112460
|
)
|
|
112357
112461
|
);
|
|
112358
112462
|
console.log(import_picocolors27.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
@@ -112838,7 +112942,7 @@ async function runSetup(opts) {
|
|
|
112838
112942
|
write(
|
|
112839
112943
|
import_picocolors32.default.dim(
|
|
112840
112944
|
` install it (Python 3.11+ required):
|
|
112841
|
-
|
|
112945
|
+
curl -fsSL https://horus.sh/install.sh | bash
|
|
112842
112946
|
ensure ~/.local/bin is on your PATH`
|
|
112843
112947
|
)
|
|
112844
112948
|
);
|
|
@@ -112850,7 +112954,7 @@ async function runSetup(opts) {
|
|
|
112850
112954
|
write(
|
|
112851
112955
|
import_picocolors32.default.dim(
|
|
112852
112956
|
` update it:
|
|
112853
|
-
|
|
112957
|
+
horus update`
|
|
112854
112958
|
)
|
|
112855
112959
|
);
|
|
112856
112960
|
} else {
|
|
@@ -114792,7 +114896,7 @@ async function runReadiness(opts) {
|
|
|
114792
114896
|
status: "warn",
|
|
114793
114897
|
blocking: false,
|
|
114794
114898
|
detail: "not installed \u2014 source intelligence unavailable",
|
|
114795
|
-
next: `
|
|
114899
|
+
next: `curl -fsSL https://horus.sh/install.sh | bash`
|
|
114796
114900
|
});
|
|
114797
114901
|
} else if (sourceVersion !== PINNED_SOURCE_VERSION) {
|
|
114798
114902
|
checks.push({
|
|
@@ -114800,7 +114904,7 @@ async function runReadiness(opts) {
|
|
|
114800
114904
|
status: "warn",
|
|
114801
114905
|
blocking: false,
|
|
114802
114906
|
detail: `version mismatch (installed: ${sourceVersion}, required: ${PINNED_SOURCE_VERSION})`,
|
|
114803
|
-
next: `
|
|
114907
|
+
next: `horus update`
|
|
114804
114908
|
});
|
|
114805
114909
|
} else {
|
|
114806
114910
|
checks.push({
|
|
@@ -115390,7 +115494,40 @@ var import_node_fs19 = require("fs");
|
|
|
115390
115494
|
var import_node_path21 = require("path");
|
|
115391
115495
|
var import_node_os6 = require("os");
|
|
115392
115496
|
var import_node_crypto7 = require("crypto");
|
|
115497
|
+
var import_node_child_process9 = require("child_process");
|
|
115498
|
+
var import_node_util5 = require("util");
|
|
115393
115499
|
var import_picocolors43 = __toESM(require_picocolors(), 1);
|
|
115500
|
+
var execFileAsync2 = (0, import_node_util5.promisify)(import_node_child_process9.execFile);
|
|
115501
|
+
async function ensureBackendPinned(write) {
|
|
115502
|
+
let installed = null;
|
|
115503
|
+
try {
|
|
115504
|
+
installed = await getSourceVersion();
|
|
115505
|
+
} catch {
|
|
115506
|
+
installed = null;
|
|
115507
|
+
}
|
|
115508
|
+
if (installed === PINNED_SOURCE_VERSION) {
|
|
115509
|
+
write(` ${import_picocolors43.default.green("\u2713")} Source backend already on pinned ${PINNED_SOURCE_VERSION}.`);
|
|
115510
|
+
return;
|
|
115511
|
+
}
|
|
115512
|
+
if (installed === null) {
|
|
115513
|
+
write(
|
|
115514
|
+
` ${import_picocolors43.default.dim("Source backend not installed \u2014 install it: curl -fsSL https://horus.sh/install.sh | bash")}`
|
|
115515
|
+
);
|
|
115516
|
+
return;
|
|
115517
|
+
}
|
|
115518
|
+
write(` Upgrading source backend ${installed} \u2192 ${PINNED_SOURCE_VERSION}\u2026`);
|
|
115519
|
+
try {
|
|
115520
|
+
await execFileAsync2(
|
|
115521
|
+
"uv",
|
|
115522
|
+
["tool", "install", "--force", `horus-source==${PINNED_SOURCE_VERSION}`],
|
|
115523
|
+
{ timeout: 3e5 }
|
|
115524
|
+
);
|
|
115525
|
+
write(` ${import_picocolors43.default.green("\u2713")} Source backend upgraded to ${PINNED_SOURCE_VERSION}.`);
|
|
115526
|
+
} catch {
|
|
115527
|
+
write(` ${import_picocolors43.default.yellow("!")} Couldn't auto-upgrade the source backend. Re-run the installer:`);
|
|
115528
|
+
write(` ${import_picocolors43.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
|
|
115529
|
+
}
|
|
115530
|
+
}
|
|
115394
115531
|
var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
|
|
115395
115532
|
function parseVersion(v4) {
|
|
115396
115533
|
const m5 = v4.replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
@@ -115458,6 +115595,7 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115458
115595
|
if (!newer && !opts.force) {
|
|
115459
115596
|
write(`
|
|
115460
115597
|
${import_picocolors43.default.green("\u2713")} Already on the latest version.`);
|
|
115598
|
+
await ensureBackendPinned(write);
|
|
115461
115599
|
return 0;
|
|
115462
115600
|
}
|
|
115463
115601
|
const tag = release.tag_name;
|
|
@@ -115553,19 +115691,20 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115553
115691
|
}
|
|
115554
115692
|
write(` ${import_picocolors43.default.green("\u2713")} Updated: ${import_picocolors43.default.bold(currentVersion)} \u2192 ${import_picocolors43.default.bold(latest)}`);
|
|
115555
115693
|
write(` ${import_picocolors43.default.dim(binaryPath)}`);
|
|
115694
|
+
await ensureBackendPinned(write);
|
|
115556
115695
|
return 0;
|
|
115557
115696
|
}
|
|
115558
115697
|
|
|
115559
115698
|
// ../../packages/cli/src/commands/login.ts
|
|
115560
115699
|
init_cjs_shims();
|
|
115561
|
-
var
|
|
115700
|
+
var import_node_child_process10 = require("child_process");
|
|
115562
115701
|
var import_picocolors44 = __toESM(require_picocolors(), 1);
|
|
115563
115702
|
var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
115564
115703
|
function openBrowser(url) {
|
|
115565
115704
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
115566
115705
|
const args2 = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
115567
115706
|
try {
|
|
115568
|
-
(0,
|
|
115707
|
+
(0, import_node_child_process10.execFile)(cmd, args2, () => {
|
|
115569
115708
|
});
|
|
115570
115709
|
} catch {
|
|
115571
115710
|
}
|
|
@@ -115704,7 +115843,7 @@ var import_promises3 = require("readline/promises");
|
|
|
115704
115843
|
|
|
115705
115844
|
// ../../packages/cli/src/lib/cloud/git.ts
|
|
115706
115845
|
init_cjs_shims();
|
|
115707
|
-
var
|
|
115846
|
+
var import_node_child_process11 = require("child_process");
|
|
115708
115847
|
function parseRepoName(remoteUrl) {
|
|
115709
115848
|
const cleaned = remoteUrl.trim().replace(/\.git$/, "");
|
|
115710
115849
|
const lastSegment = cleaned.split(/[/:]/).filter(Boolean).pop() ?? "";
|
|
@@ -115728,7 +115867,7 @@ function inferProvider(remoteUrl) {
|
|
|
115728
115867
|
}
|
|
115729
115868
|
function detectGitRemote(root) {
|
|
115730
115869
|
try {
|
|
115731
|
-
const remoteUrl = (0,
|
|
115870
|
+
const remoteUrl = (0, import_node_child_process11.execFileSync)("git", ["-C", root, "remote", "get-url", "origin"], {
|
|
115732
115871
|
encoding: "utf8",
|
|
115733
115872
|
stdio: ["ignore", "pipe", "ignore"]
|
|
115734
115873
|
}).trim();
|
|
@@ -116652,7 +116791,12 @@ Examples:
|
|
|
116652
116791
|
});
|
|
116653
116792
|
program2.command("logs [service]").description(
|
|
116654
116793
|
"Synthesize error evidence from logs (signatures, first/last, affected services); --raw for lines"
|
|
116655
|
-
).option("-c, --config <path>", "path to horus.config.ts").option("--name <name>", "registered project name (resolves via the registry)").option("--project <name>", "project name").option("--env <name>", "environment name (e.g. production)").option("--since <when>", "time window, e.g. 24h, 7d, or an ISO date").option("--level <level>", "minimum level (with --raw): trace|debug|info|warn|error|fatal").option("--grep <text>", "match text in the message").option("--raw", "dump individual log lines instead of synthesized evidence (error+ by default)").option("--all-levels", "with --raw: show all severity levels, not just error+").option("--group-by <field>", "aggregate error counts by a context field, e.g. context.brand_id").option(
|
|
116794
|
+
).option("-c, --config <path>", "path to horus.config.ts").option("--name <name>", "registered project name (resolves via the registry)").option("--project <name>", "project name").option("--env <name>", "environment name (e.g. production)").option("--since <when>", "time window, e.g. 24h, 7d, or an ISO date").option("--level <level>", "minimum level (with --raw): trace|debug|info|warn|error|fatal").option("--grep <text>", "match text in the message").option("--raw", "dump individual log lines instead of synthesized evidence (error+ by default)").option("--all-levels", "with --raw: show all severity levels, not just error+").option("--group-by <field>", "aggregate error counts by a context field, e.g. context.brand_id").option(
|
|
116795
|
+
"--where <field=value>",
|
|
116796
|
+
"filter by a structured context field (repeatable, AND-combined), e.g. --where context.brand_id=42",
|
|
116797
|
+
(val, acc) => [...acc, val],
|
|
116798
|
+
[]
|
|
116799
|
+
).option("--limit <n>", "max records (with --raw)").option("--json", "output JSON").option("--ai", "append AI narration of error signatures (default mode only, not --raw or --json)").option("--ai-model <model>", "override the AI model (default: claude-opus-4-8)").action(
|
|
116656
116800
|
async (service, opts) => {
|
|
116657
116801
|
process.exitCode = await runLogs(service, opts);
|
|
116658
116802
|
}
|