@merittdev/horus 0.8.16 → 0.8.18
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 +11 -9
- package/dist/index.cjs +1100 -688
- 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 promisify8(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 = promisify8(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 = promisify8(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 = promisify8(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 = promisify8(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.18" : "dev";
|
|
65884
|
+
var PINNED_SOURCE_VERSION = "1.5.4";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
65887
65887
|
init_cjs_shims();
|
|
@@ -71105,6 +71105,34 @@ function removeSpawnedHostRecord(root) {
|
|
|
71105
71105
|
} catch {
|
|
71106
71106
|
}
|
|
71107
71107
|
}
|
|
71108
|
+
async function killSpawnedHost(root) {
|
|
71109
|
+
const pids = /* @__PURE__ */ new Set();
|
|
71110
|
+
const spawned = readSpawnedHost(root);
|
|
71111
|
+
if (spawned?.pid) pids.add(spawned.pid);
|
|
71112
|
+
const backend = readSourceHostPid(root);
|
|
71113
|
+
if (backend?.pid) pids.add(backend.pid);
|
|
71114
|
+
for (const pid of pids) {
|
|
71115
|
+
for (const signal of ["SIGTERM", "SIGKILL"]) {
|
|
71116
|
+
try {
|
|
71117
|
+
process.kill(pid, signal);
|
|
71118
|
+
} catch {
|
|
71119
|
+
break;
|
|
71120
|
+
}
|
|
71121
|
+
let dead = false;
|
|
71122
|
+
for (let i3 = 0; i3 < 15; i3 += 1) {
|
|
71123
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
71124
|
+
try {
|
|
71125
|
+
process.kill(pid, 0);
|
|
71126
|
+
} catch {
|
|
71127
|
+
dead = true;
|
|
71128
|
+
break;
|
|
71129
|
+
}
|
|
71130
|
+
}
|
|
71131
|
+
if (dead) break;
|
|
71132
|
+
}
|
|
71133
|
+
}
|
|
71134
|
+
removeSpawnedHostRecord(root);
|
|
71135
|
+
}
|
|
71108
71136
|
function reconcileSpawnedHostPid(root, port) {
|
|
71109
71137
|
try {
|
|
71110
71138
|
const backend = readSourceHostPid(root);
|
|
@@ -75560,7 +75588,7 @@ function sql(strings, ...params) {
|
|
|
75560
75588
|
return new SQL([new StringChunk(str3)]);
|
|
75561
75589
|
}
|
|
75562
75590
|
sql2.raw = raw;
|
|
75563
|
-
function
|
|
75591
|
+
function join15(chunks, separator) {
|
|
75564
75592
|
const result = [];
|
|
75565
75593
|
for (const [i3, chunk] of chunks.entries()) {
|
|
75566
75594
|
if (i3 > 0 && separator !== void 0) {
|
|
@@ -75570,7 +75598,7 @@ function sql(strings, ...params) {
|
|
|
75570
75598
|
}
|
|
75571
75599
|
return new SQL(result);
|
|
75572
75600
|
}
|
|
75573
|
-
sql2.join =
|
|
75601
|
+
sql2.join = join15;
|
|
75574
75602
|
function identifier(value) {
|
|
75575
75603
|
return new Name(value);
|
|
75576
75604
|
}
|
|
@@ -79161,7 +79189,7 @@ var PgSelectQueryBuilderBase = class extends TypedQueryBuilder {
|
|
|
79161
79189
|
return (table, on2) => {
|
|
79162
79190
|
const baseTableName = this.tableName;
|
|
79163
79191
|
const tableName = getTableLikeName(table);
|
|
79164
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
79192
|
+
if (typeof tableName === "string" && this.config.joins?.some((join15) => join15.alias === tableName)) {
|
|
79165
79193
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
79166
79194
|
}
|
|
79167
79195
|
if (!this.isPartialSelect) {
|
|
@@ -80173,7 +80201,7 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
80173
80201
|
createJoin(joinType) {
|
|
80174
80202
|
return (table, on2) => {
|
|
80175
80203
|
const tableName = getTableLikeName(table);
|
|
80176
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
80204
|
+
if (typeof tableName === "string" && this.config.joins.some((join15) => join15.alias === tableName)) {
|
|
80177
80205
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
80178
80206
|
}
|
|
80179
80207
|
if (typeof on2 === "function") {
|
|
@@ -80269,10 +80297,10 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
80269
80297
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
80270
80298
|
fields[tableName] = fromFields;
|
|
80271
80299
|
}
|
|
80272
|
-
for (const
|
|
80273
|
-
const tableName2 = getTableLikeName(
|
|
80274
|
-
if (typeof tableName2 === "string" && !is(
|
|
80275
|
-
const fromFields = this.getTableLikeFields(
|
|
80300
|
+
for (const join15 of this.config.joins) {
|
|
80301
|
+
const tableName2 = getTableLikeName(join15.table);
|
|
80302
|
+
if (typeof tableName2 === "string" && !is(join15.table, SQL)) {
|
|
80303
|
+
const fromFields = this.getTableLikeFields(join15.table);
|
|
80276
80304
|
fields[tableName2] = fromFields;
|
|
80277
80305
|
}
|
|
80278
80306
|
}
|
|
@@ -90401,6 +90429,10 @@ function reportToMarkdown(r) {
|
|
|
90401
90429
|
|
|
90402
90430
|
// ../../packages/engine/src/ownership.ts
|
|
90403
90431
|
init_cjs_shims();
|
|
90432
|
+
var BOT_AUTHOR_RE = /\[bot\]|^(?:github-actions|dependabot|renovate(?:-bot)?|greenkeeper|snyk-bot|mergify|codecov|semantic-release-bot|allcontributors)\b/i;
|
|
90433
|
+
function isBotAuthor(author) {
|
|
90434
|
+
return BOT_AUTHOR_RE.test((author ?? "").trim());
|
|
90435
|
+
}
|
|
90404
90436
|
var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
90405
90437
|
"ts",
|
|
90406
90438
|
"tsx",
|
|
@@ -90507,10 +90539,12 @@ async function estimateOwnership(query, deps) {
|
|
|
90507
90539
|
};
|
|
90508
90540
|
}
|
|
90509
90541
|
const contributors = await gitFileContributors(deps.repoPath, file);
|
|
90510
|
-
const
|
|
90511
|
-
const
|
|
90542
|
+
const humans = contributors.filter((c4) => !isBotAuthor(c4.author));
|
|
90543
|
+
const ranked = humans.length > 0 ? humans : contributors;
|
|
90544
|
+
const total = ranked.reduce((n4, c4) => n4 + c4.commits, 0);
|
|
90545
|
+
const lead = ranked[0] ?? null;
|
|
90512
90546
|
const maintainerShare = total > 0 && lead !== null ? lead.commits / total : 0;
|
|
90513
|
-
const byRecent = [...
|
|
90547
|
+
const byRecent = [...ranked].sort(
|
|
90514
90548
|
(a2, b5) => b5.lastDate < a2.lastDate ? -1 : b5.lastDate > a2.lastDate ? 1 : 0
|
|
90515
90549
|
);
|
|
90516
90550
|
const mostActiveRecent = byRecent[0]?.author ?? null;
|
|
@@ -91250,6 +91284,30 @@ function factorRequestContext(sourceEvidenceIds, graph, request) {
|
|
|
91250
91284
|
reason: `Cause directly implicates the investigated service (${request.service})`
|
|
91251
91285
|
};
|
|
91252
91286
|
}
|
|
91287
|
+
function factorIncidentHistory(sourceEvidenceIds, graph, priorIncidents) {
|
|
91288
|
+
if (priorIncidents.size === 0) return null;
|
|
91289
|
+
const idSet = new Set(sourceEvidenceIds);
|
|
91290
|
+
const codeNodes = graph.nodes.filter(
|
|
91291
|
+
(n4) => (n4.type === "symbol" || n4.type === "file") && n4.evidenceIds.some((eid) => idSet.has(eid))
|
|
91292
|
+
);
|
|
91293
|
+
const matched = [];
|
|
91294
|
+
let totalPrior = 0;
|
|
91295
|
+
for (const n4 of codeNodes) {
|
|
91296
|
+
const count = priorIncidents.get(n4.id);
|
|
91297
|
+
if (count && count > 0) {
|
|
91298
|
+
matched.push(n4);
|
|
91299
|
+
totalPrior += count;
|
|
91300
|
+
}
|
|
91301
|
+
}
|
|
91302
|
+
if (matched.length === 0) return null;
|
|
91303
|
+
const delta = +Math.min(matched.length * 0.04, 0.1).toFixed(3);
|
|
91304
|
+
const names = matched.slice(0, 3).map((n4) => n4.label);
|
|
91305
|
+
return {
|
|
91306
|
+
factor: "incident-history",
|
|
91307
|
+
delta,
|
|
91308
|
+
reason: `Implicated code has a prior-incident history (${totalPrior} prior investigation(s) on ${names.join(", ")}${matched.length > 3 ? ", \u2026" : ""})`
|
|
91309
|
+
};
|
|
91310
|
+
}
|
|
91253
91311
|
function scoreCause(input, ctx) {
|
|
91254
91312
|
const now = ctx.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
91255
91313
|
const idSet = new Set(input.sourceEvidenceIds);
|
|
@@ -91263,7 +91321,8 @@ function scoreCause(input, ctx) {
|
|
|
91263
91321
|
factorSignalStrength(attached),
|
|
91264
91322
|
factorFindingUncertainty(input.sourceEvidenceIds, ctx.findings ?? []),
|
|
91265
91323
|
ctx.providerReliability ? factorProviderReliability(attached, ctx.providerReliability) : null,
|
|
91266
|
-
ctx.request ? factorRequestContext(input.sourceEvidenceIds, ctx.graph, ctx.request) : null
|
|
91324
|
+
ctx.request ? factorRequestContext(input.sourceEvidenceIds, ctx.graph, ctx.request) : null,
|
|
91325
|
+
ctx.priorIncidents ? factorIncidentHistory(input.sourceEvidenceIds, ctx.graph, ctx.priorIncidents) : null
|
|
91267
91326
|
];
|
|
91268
91327
|
const explanations = rawFactors.filter((f5) => f5 !== null);
|
|
91269
91328
|
const totalDelta = explanations.reduce((sum, e) => sum + e.delta, 0);
|
|
@@ -92002,6 +92061,14 @@ function scoreSeed(s5, index2, hintTokens, changedFiles, hintHasCode) {
|
|
|
92002
92061
|
if (changedFiles !== void 0 && changedFiles.has(s5.filePath)) score += 5;
|
|
92003
92062
|
if (/\.(resolver|controller|service)\.[jt]sx?$/i.test(s5.filePath)) score += hintHasCode ? 0 : 2;
|
|
92004
92063
|
if (/(^|\/)scripts?\//i.test(s5.filePath)) score -= 2;
|
|
92064
|
+
if (/(^|\/)(tests?|__tests__|spec)\//i.test(s5.filePath) || /(\.|_)(test|spec)\.[jt]sx?$/i.test(s5.filePath) || /(^|\/)test_[^/]*\.py$/i.test(s5.filePath) || /_test\.py$/i.test(s5.filePath)) {
|
|
92065
|
+
score -= 4;
|
|
92066
|
+
}
|
|
92067
|
+
if (/(^|\/)(examples?|samples?|demos?|fixtures?|sandbox|playground|docs|docs_src|tutorials?)(\/|$)/i.test(
|
|
92068
|
+
s5.filePath
|
|
92069
|
+
)) {
|
|
92070
|
+
score -= 4;
|
|
92071
|
+
}
|
|
92005
92072
|
score += Math.max(0, 5 - index2) * (hintHasCode ? 0.8 : 0.1);
|
|
92006
92073
|
if (s5.score !== void 0) score += s5.score * (hintHasCode ? 8 : 1.5);
|
|
92007
92074
|
if (/\.[jt]sx?$/i.test(s5.name)) score -= 5;
|
|
@@ -92678,12 +92745,12 @@ async function investigate(input, deps) {
|
|
|
92678
92745
|
const fp = (s5.filePath ?? "").replace(/^\.?\/+/, "");
|
|
92679
92746
|
return fp === scopePrefix || fp.startsWith(scopePrefix + "/");
|
|
92680
92747
|
};
|
|
92681
|
-
const seedSearchLimit = scopePrefix ?
|
|
92682
|
-
const rawSeeds = code ? (await code.searchSymbols(hint, seedSearchLimit)).filter(inScope)
|
|
92748
|
+
const seedSearchLimit = scopePrefix ? 30 : 20;
|
|
92749
|
+
const rawSeeds = code ? (await code.searchSymbols(hint, seedSearchLimit)).filter(inScope) : [];
|
|
92683
92750
|
const hintTokens = [...new Set(tokenize(hint))];
|
|
92684
92751
|
const hintHasCode = /\b(?=[A-Z0-9_]*[0-9_])[A-Z][A-Z0-9_]{3,}\b/.test(hint);
|
|
92685
92752
|
const ranked = rankSeeds(rawSeeds, hintTokens, changedFilePaths, hintHasCode);
|
|
92686
|
-
let seeds = ranked.map((r) => r.symbol);
|
|
92753
|
+
let seeds = ranked.map((r) => r.symbol).slice(0, 5);
|
|
92687
92754
|
let top = seeds[0];
|
|
92688
92755
|
if (top && code && isTypeLikeName(top.name)) {
|
|
92689
92756
|
const base2 = executableBaseName(top.name);
|
|
@@ -93478,11 +93545,12 @@ async function investigate(input, deps) {
|
|
|
93478
93545
|
const causeChains = buildCauseChains(validated, evidence2, graph, label);
|
|
93479
93546
|
const findings2 = [];
|
|
93480
93547
|
if (top && seedEv) {
|
|
93548
|
+
const seedConfidence = seedIsLowConfidence ? Math.min(0.4, top.score ?? 0.3) : Math.min(0.95, Math.max(0.5, top.score ?? 0.75));
|
|
93481
93549
|
findings2.push({
|
|
93482
93550
|
kind: "observation",
|
|
93483
93551
|
title: `Seed resolves to ${label} at ${seedLoc}`,
|
|
93484
93552
|
detail: top.signature ?? void 0,
|
|
93485
|
-
confidence:
|
|
93553
|
+
confidence: +seedConfidence.toFixed(2),
|
|
93486
93554
|
evidenceIds: [seedEv.id]
|
|
93487
93555
|
});
|
|
93488
93556
|
}
|
|
@@ -93864,12 +93932,31 @@ async function investigate(input, deps) {
|
|
|
93864
93932
|
...deps.queue != null ? { queue: 0.9 } : {},
|
|
93865
93933
|
...deps.metrics != null ? { metrics: 0.75 } : {}
|
|
93866
93934
|
};
|
|
93935
|
+
const priorIncidents = /* @__PURE__ */ new Map();
|
|
93936
|
+
try {
|
|
93937
|
+
const priors = await listInvestigationsWithReports(deps.db, 50);
|
|
93938
|
+
for (const row of priors) {
|
|
93939
|
+
const rep = row.report;
|
|
93940
|
+
if (!rep || !Array.isArray(rep.evidence) || rep.evidence.length === 0) continue;
|
|
93941
|
+
if (typeof rep.confidence === "number" && rep.confidence < 0.5) continue;
|
|
93942
|
+
const priorGraph = buildGraph(rep.evidence);
|
|
93943
|
+
const seen = /* @__PURE__ */ new Set();
|
|
93944
|
+
for (const n4 of priorGraph.nodes) {
|
|
93945
|
+
if ((n4.type === "symbol" || n4.type === "file") && !seen.has(n4.id)) {
|
|
93946
|
+
seen.add(n4.id);
|
|
93947
|
+
priorIncidents.set(n4.id, (priorIncidents.get(n4.id) ?? 0) + 1);
|
|
93948
|
+
}
|
|
93949
|
+
}
|
|
93950
|
+
}
|
|
93951
|
+
} catch {
|
|
93952
|
+
}
|
|
93867
93953
|
const rankedCauses = rankCauses(causeInputs, {
|
|
93868
93954
|
evidence: evidence2,
|
|
93869
93955
|
graph,
|
|
93870
93956
|
findings: findings2,
|
|
93871
93957
|
providerReliability,
|
|
93872
|
-
request: { hint: input.hint, service: input.service }
|
|
93958
|
+
request: { hint: input.hint, service: input.service },
|
|
93959
|
+
...priorIncidents.size > 0 ? { priorIncidents } : {}
|
|
93873
93960
|
});
|
|
93874
93961
|
const evidenceConfidence = computeWeightedEvidenceConfidence(
|
|
93875
93962
|
evidence2,
|
|
@@ -94441,6 +94528,32 @@ var EXTERNAL_MARKERS = [
|
|
|
94441
94528
|
"mysql2",
|
|
94442
94529
|
"mssql"
|
|
94443
94530
|
];
|
|
94531
|
+
var TESTY_TOKENS = /* @__PURE__ */ new Set([
|
|
94532
|
+
"test",
|
|
94533
|
+
"tests",
|
|
94534
|
+
"spec",
|
|
94535
|
+
"specs",
|
|
94536
|
+
"example",
|
|
94537
|
+
"examples",
|
|
94538
|
+
"sample",
|
|
94539
|
+
"samples",
|
|
94540
|
+
"demo",
|
|
94541
|
+
"demos",
|
|
94542
|
+
"docs",
|
|
94543
|
+
"doc",
|
|
94544
|
+
"tutorial",
|
|
94545
|
+
"tutorials",
|
|
94546
|
+
"fixture",
|
|
94547
|
+
"fixtures",
|
|
94548
|
+
"e2e"
|
|
94549
|
+
]);
|
|
94550
|
+
function isTestyCommunity(name2) {
|
|
94551
|
+
return name2.toLowerCase().split(/[+\s/_.-]+/).some((t2) => TESTY_TOKENS.has(t2));
|
|
94552
|
+
}
|
|
94553
|
+
var TEST_EXAMPLE_PATH_RE = /(^|\/)(tests?|__tests__|examples?|samples?|demos?|docs|docs_src|tutorials?|fixtures?|spec|specs)(\/|$)/i;
|
|
94554
|
+
function isTestOrExamplePath(p5) {
|
|
94555
|
+
return TEST_EXAMPLE_PATH_RE.test(p5);
|
|
94556
|
+
}
|
|
94444
94557
|
async function discoverArchitecture(deps) {
|
|
94445
94558
|
const nodeStats = await (async () => {
|
|
94446
94559
|
try {
|
|
@@ -94460,10 +94573,17 @@ async function discoverArchitecture(deps) {
|
|
|
94460
94573
|
const result = await deps.code.cypher(
|
|
94461
94574
|
'MATCH (m)-[r:CodeRelation]->(c:Community) WHERE r.rel_type = "member_of" RETURN c.name, count(m) ORDER BY count(m) DESC'
|
|
94462
94575
|
);
|
|
94463
|
-
|
|
94576
|
+
const rows = result.rows.map((row) => ({
|
|
94464
94577
|
name: String(row[0] ?? ""),
|
|
94465
94578
|
members: Number(row[1] ?? 0)
|
|
94466
94579
|
}));
|
|
94580
|
+
rows.sort((a2, b5) => {
|
|
94581
|
+
const at4 = isTestyCommunity(a2.name) ? 1 : 0;
|
|
94582
|
+
const bt2 = isTestyCommunity(b5.name) ? 1 : 0;
|
|
94583
|
+
if (at4 !== bt2) return at4 - bt2;
|
|
94584
|
+
return b5.members - a2.members;
|
|
94585
|
+
});
|
|
94586
|
+
return rows.slice(0, 12);
|
|
94467
94587
|
} catch {
|
|
94468
94588
|
return [];
|
|
94469
94589
|
}
|
|
@@ -94475,20 +94595,22 @@ async function discoverArchitecture(deps) {
|
|
|
94475
94595
|
for (const edge of edges) {
|
|
94476
94596
|
const key = edge.queueName;
|
|
94477
94597
|
if (!byQueue.has(key)) {
|
|
94478
|
-
byQueue.set(key, { producers: /* @__PURE__ */ new
|
|
94598
|
+
byQueue.set(key, { producers: /* @__PURE__ */ new Map(), workers: /* @__PURE__ */ new Map() });
|
|
94479
94599
|
}
|
|
94480
94600
|
const entry2 = byQueue.get(key);
|
|
94481
94601
|
if (edge.producerSymbol != null && edge.producerSymbol !== "") {
|
|
94482
|
-
|
|
94602
|
+
const file = edge.producerFile ?? null;
|
|
94603
|
+
entry2.producers.set(`${edge.producerSymbol}\0${file ?? ""}`, { symbol: edge.producerSymbol, file });
|
|
94483
94604
|
}
|
|
94484
94605
|
if (edge.workerSymbol != null && edge.workerSymbol !== "") {
|
|
94485
|
-
|
|
94606
|
+
const file = edge.workerFile ?? null;
|
|
94607
|
+
entry2.workers.set(`${edge.workerSymbol}\0${file ?? ""}`, { symbol: edge.workerSymbol, file });
|
|
94486
94608
|
}
|
|
94487
94609
|
}
|
|
94488
94610
|
return Array.from(byQueue.entries()).map(([queueName, { producers, workers }]) => ({
|
|
94489
94611
|
queueName,
|
|
94490
|
-
producers: Array.from(producers),
|
|
94491
|
-
workers: Array.from(workers)
|
|
94612
|
+
producers: Array.from(producers.values()),
|
|
94613
|
+
workers: Array.from(workers.values())
|
|
94492
94614
|
}));
|
|
94493
94615
|
} catch {
|
|
94494
94616
|
return [];
|
|
@@ -94524,16 +94646,20 @@ async function discoverArchitecture(deps) {
|
|
|
94524
94646
|
return 0;
|
|
94525
94647
|
}
|
|
94526
94648
|
})();
|
|
94649
|
+
const ownPackage = deps.project?.toLowerCase();
|
|
94527
94650
|
const externalSystems = await (async () => {
|
|
94528
94651
|
try {
|
|
94529
94652
|
const results = await Promise.all(
|
|
94530
94653
|
EXTERNAL_MARKERS.map(async (marker) => {
|
|
94654
|
+
if (ownPackage && marker.toLowerCase() === ownPackage) {
|
|
94655
|
+
return { name: marker, files: 0 };
|
|
94656
|
+
}
|
|
94531
94657
|
try {
|
|
94532
94658
|
const result = await deps.code.cypher(
|
|
94533
|
-
`MATCH (n:File) WHERE n.content CONTAINS "${marker}" RETURN
|
|
94659
|
+
`MATCH (n:File) WHERE n.content CONTAINS "${marker}" RETURN n.file_path LIMIT 500`
|
|
94534
94660
|
);
|
|
94535
|
-
const
|
|
94536
|
-
return { name: marker, files
|
|
94661
|
+
const files = result.rows.map((r) => String(r[0] ?? "")).filter((p5) => p5 !== "" && !isTestOrExamplePath(p5)).length;
|
|
94662
|
+
return { name: marker, files };
|
|
94537
94663
|
} catch {
|
|
94538
94664
|
return { name: marker, files: 0 };
|
|
94539
94665
|
}
|
|
@@ -94560,6 +94686,10 @@ async function discoverArchitecture(deps) {
|
|
|
94560
94686
|
|
|
94561
94687
|
// ../../packages/engine/src/render-architecture.ts
|
|
94562
94688
|
init_cjs_shims();
|
|
94689
|
+
function fmtEndpoints(endpoints2) {
|
|
94690
|
+
if (endpoints2.length === 0) return "(unknown)";
|
|
94691
|
+
return endpoints2.map((e) => e.file ? `${e.symbol} (${e.file.split("/").pop()})` : e.symbol).join(", ");
|
|
94692
|
+
}
|
|
94563
94693
|
function renderArchitecture(m5) {
|
|
94564
94694
|
const lines = [];
|
|
94565
94695
|
lines.push("# Architecture (discovered)");
|
|
@@ -94580,9 +94710,7 @@ function renderArchitecture(m5) {
|
|
|
94580
94710
|
lines.push("(none)");
|
|
94581
94711
|
} else {
|
|
94582
94712
|
for (const b5 of m5.asyncBoundaries) {
|
|
94583
|
-
|
|
94584
|
-
const workers = b5.workers.length > 0 ? b5.workers.join(", ") : "(unknown)";
|
|
94585
|
-
lines.push(`- ${b5.queueName}: ${producers} -> ${workers}`);
|
|
94713
|
+
lines.push(`- ${b5.queueName}: ${fmtEndpoints(b5.producers)} -> ${fmtEndpoints(b5.workers)}`);
|
|
94586
94714
|
}
|
|
94587
94715
|
}
|
|
94588
94716
|
lines.push("");
|
|
@@ -95747,7 +95875,7 @@ function filterArchitecture(architecture, tokens) {
|
|
|
95747
95875
|
...architecture,
|
|
95748
95876
|
subsystems: architecture.subsystems.filter((s5) => matchesArea(s5.name, tokens)),
|
|
95749
95877
|
asyncBoundaries: architecture.asyncBoundaries.filter(
|
|
95750
|
-
(b5) => matchesArea(b5.queueName, tokens) || b5.producers.some((p5) => matchesArea(p5, tokens)) || b5.workers.some((w4) => matchesArea(w4, tokens))
|
|
95878
|
+
(b5) => matchesArea(b5.queueName, tokens) || b5.producers.some((p5) => matchesArea(p5.symbol, tokens)) || b5.workers.some((w4) => matchesArea(w4.symbol, tokens))
|
|
95751
95879
|
),
|
|
95752
95880
|
keyFlows: architecture.keyFlows.filter((f5) => matchesArea(f5, tokens)),
|
|
95753
95881
|
externalSystems: architecture.externalSystems.filter((e) => matchesArea(e.name, tokens))
|
|
@@ -95861,9 +95989,8 @@ function renderOnboarding(g4) {
|
|
|
95861
95989
|
if (g4.architecture.asyncBoundaries.length > 0) {
|
|
95862
95990
|
lines.push("Async boundaries:");
|
|
95863
95991
|
for (const b5 of g4.architecture.asyncBoundaries) {
|
|
95864
|
-
const
|
|
95865
|
-
|
|
95866
|
-
lines.push(`- ${b5.queueName}: ${producers} -> ${workers}`);
|
|
95992
|
+
const fmt = (xs) => xs.length > 0 ? xs.map((x4) => x4.file ? `${x4.symbol} (${x4.file.split("/").pop()})` : x4.symbol).join(", ") : "(unknown)";
|
|
95993
|
+
lines.push(`- ${b5.queueName}: ${fmt(b5.producers)} -> ${fmt(b5.workers)}`);
|
|
95867
95994
|
}
|
|
95868
95995
|
} else {
|
|
95869
95996
|
lines.push("No async boundaries detected.");
|
|
@@ -97209,27 +97336,27 @@ function extractQueueGraph(input) {
|
|
|
97209
97336
|
}
|
|
97210
97337
|
}
|
|
97211
97338
|
const producers = [];
|
|
97212
|
-
for (const
|
|
97213
|
-
const loopVarEnums = loopVarEnumsFor(
|
|
97214
|
-
for (const m5 of
|
|
97339
|
+
for (const pc50 of input.producerClasses) {
|
|
97340
|
+
const loopVarEnums = loopVarEnumsFor(pc50.content);
|
|
97341
|
+
for (const m5 of pc50.content.matchAll(INJECT_QUEUE_RE)) {
|
|
97215
97342
|
const queue = m5[1] ?? "";
|
|
97216
|
-
if (queue) producers.push({ queue, symbol:
|
|
97343
|
+
if (queue) producers.push({ queue, symbol: pc50.name, file: pc50.filePath });
|
|
97217
97344
|
}
|
|
97218
|
-
for (const m5 of
|
|
97345
|
+
for (const m5 of pc50.content.matchAll(NEW_BULL_RE)) {
|
|
97219
97346
|
if (m5[2] !== "Queue") continue;
|
|
97220
97347
|
const queues2 = resolveArg(m5[3] ?? "", constMap, enums, loopVarEnums);
|
|
97221
97348
|
for (const queue of queues2) {
|
|
97222
97349
|
producers.push({
|
|
97223
97350
|
queue,
|
|
97224
|
-
symbol: (m5[1] ??
|
|
97225
|
-
file:
|
|
97351
|
+
symbol: (m5[1] ?? pc50.name) || baseName(pc50.filePath),
|
|
97352
|
+
file: pc50.filePath
|
|
97226
97353
|
});
|
|
97227
97354
|
}
|
|
97228
97355
|
}
|
|
97229
|
-
for (const m5 of
|
|
97356
|
+
for (const m5 of pc50.content.matchAll(QUEUE_NAME_CONST_RE)) {
|
|
97230
97357
|
const ident = m5[1] ?? "";
|
|
97231
97358
|
const queue = m5[2] ?? "";
|
|
97232
|
-
if (queue) producers.push({ queue, symbol: ident || baseName(
|
|
97359
|
+
if (queue) producers.push({ queue, symbol: ident || baseName(pc50.filePath), file: pc50.filePath });
|
|
97233
97360
|
}
|
|
97234
97361
|
}
|
|
97235
97362
|
const workers = [];
|
|
@@ -97296,6 +97423,60 @@ function extractQueueGraph(input) {
|
|
|
97296
97423
|
}
|
|
97297
97424
|
return { queues, producers, workers, edges };
|
|
97298
97425
|
}
|
|
97426
|
+
var CELERY_TASK_DEF_RE = /@(?:[\w]+\.)*(?:shared_task|periodic_task|task)\b[^\n]*(?:\n[ \t]*@[^\n]*)*\n[ \t]*(?:async[ \t]+)?def[ \t]+(\w+)/g;
|
|
97427
|
+
var CELERY_ENQUEUE_RE = /([A-Za-z_]\w*)\s*\.\s*(?:delay|apply_async)\s*\(/g;
|
|
97428
|
+
function extractCeleryQueueGraph(nodes) {
|
|
97429
|
+
const producers = [];
|
|
97430
|
+
const workers = [];
|
|
97431
|
+
const FILE_NODE_RE = /\.(py|pyi|js|jsx|ts|tsx|mjs|cjs)$/i;
|
|
97432
|
+
for (const n4 of nodes) {
|
|
97433
|
+
for (const m5 of n4.content.matchAll(CELERY_TASK_DEF_RE)) {
|
|
97434
|
+
const task = m5[1];
|
|
97435
|
+
if (task) workers.push({ queue: task, symbol: task, file: n4.filePath });
|
|
97436
|
+
}
|
|
97437
|
+
if (FILE_NODE_RE.test(n4.name)) continue;
|
|
97438
|
+
for (const m5 of n4.content.matchAll(CELERY_ENQUEUE_RE)) {
|
|
97439
|
+
const task = m5[1];
|
|
97440
|
+
if (task) producers.push({ queue: task, symbol: n4.name || baseName(n4.filePath), file: n4.filePath });
|
|
97441
|
+
}
|
|
97442
|
+
}
|
|
97443
|
+
const taskQueues = new Set(workers.map((w4) => w4.queue));
|
|
97444
|
+
const queues = [...taskQueues].sort();
|
|
97445
|
+
const edges = [];
|
|
97446
|
+
for (const q2 of queues) {
|
|
97447
|
+
const P5 = dedupeBySymbolFile(producers.filter((p5) => p5.queue === q2));
|
|
97448
|
+
const W5 = dedupeBySymbolFile(workers.filter((w4) => w4.queue === q2));
|
|
97449
|
+
if (P5.length) {
|
|
97450
|
+
for (const p5 of P5) {
|
|
97451
|
+
for (const w4 of W5) {
|
|
97452
|
+
edges.push({
|
|
97453
|
+
queueName: q2,
|
|
97454
|
+
producerSymbol: p5.symbol,
|
|
97455
|
+
producerFile: p5.file,
|
|
97456
|
+
workerSymbol: w4.symbol,
|
|
97457
|
+
workerFile: w4.file
|
|
97458
|
+
});
|
|
97459
|
+
}
|
|
97460
|
+
}
|
|
97461
|
+
} else {
|
|
97462
|
+
for (const w4 of W5) {
|
|
97463
|
+
edges.push({
|
|
97464
|
+
queueName: q2,
|
|
97465
|
+
producerSymbol: null,
|
|
97466
|
+
producerFile: null,
|
|
97467
|
+
workerSymbol: w4.symbol,
|
|
97468
|
+
workerFile: w4.file
|
|
97469
|
+
});
|
|
97470
|
+
}
|
|
97471
|
+
}
|
|
97472
|
+
}
|
|
97473
|
+
return {
|
|
97474
|
+
queues,
|
|
97475
|
+
producers: producers.filter((p5) => taskQueues.has(p5.queue)),
|
|
97476
|
+
workers,
|
|
97477
|
+
edges
|
|
97478
|
+
};
|
|
97479
|
+
}
|
|
97299
97480
|
function dedupeBySymbolFile(rows) {
|
|
97300
97481
|
const seen = /* @__PURE__ */ new Set();
|
|
97301
97482
|
const out2 = [];
|
|
@@ -97329,7 +97510,16 @@ async function stitch(client, db, opts = {}) {
|
|
|
97329
97510
|
content: String(r[2] ?? "")
|
|
97330
97511
|
}));
|
|
97331
97512
|
const graph = extractQueueGraph({ producerClasses, workerFiles });
|
|
97332
|
-
const
|
|
97513
|
+
const celeryRows = (await client.cypher(
|
|
97514
|
+
'MATCH (n) WHERE n.content CONTAINS ".delay(" OR n.content CONTAINS ".apply_async(" OR n.content CONTAINS "@shared_task" OR n.content CONTAINS "@task" OR n.content CONTAINS ".task" RETURN n.name, n.file_path, n.content'
|
|
97515
|
+
)).rows;
|
|
97516
|
+
const celeryNodes = celeryRows.map((r) => ({
|
|
97517
|
+
name: String(r[0] ?? ""),
|
|
97518
|
+
filePath: String(r[1] ?? ""),
|
|
97519
|
+
content: String(r[2] ?? "")
|
|
97520
|
+
}));
|
|
97521
|
+
const celery = extractCeleryQueueGraph(celeryNodes);
|
|
97522
|
+
const edges = [...graph.edges, ...celery.edges].map((e) => ({
|
|
97333
97523
|
queueName: e.queueName,
|
|
97334
97524
|
producerSymbol: e.producerSymbol,
|
|
97335
97525
|
producerFile: e.producerFile,
|
|
@@ -97340,9 +97530,9 @@ async function stitch(client, db, opts = {}) {
|
|
|
97340
97530
|
}));
|
|
97341
97531
|
await replaceQueueEdges(db, edges, { project: opts.project });
|
|
97342
97532
|
return {
|
|
97343
|
-
queues: graph.queues.length,
|
|
97344
|
-
producers: graph.producers.length,
|
|
97345
|
-
workers: graph.workers.length,
|
|
97533
|
+
queues: graph.queues.length + celery.queues.length,
|
|
97534
|
+
producers: graph.producers.length + celery.producers.length,
|
|
97535
|
+
workers: graph.workers.length + celery.workers.length,
|
|
97346
97536
|
edges: edges.length
|
|
97347
97537
|
};
|
|
97348
97538
|
}
|
|
@@ -97530,6 +97720,7 @@ async function runIndex(opts) {
|
|
|
97530
97720
|
console.log(import_picocolors3.default.dim(" already analyzed"));
|
|
97531
97721
|
}
|
|
97532
97722
|
const priorPort = parseHostPort(configuredHost ?? "") ?? parseHostPort(readSourceHostUrl(root) ?? "");
|
|
97723
|
+
await killSpawnedHost(root);
|
|
97533
97724
|
const MAX_PORT_ATTEMPTS = 4;
|
|
97534
97725
|
let started = false;
|
|
97535
97726
|
for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS && !started; attempt += 1) {
|
|
@@ -97554,7 +97745,7 @@ async function runIndex(opts) {
|
|
|
97554
97745
|
reconcileSpawnedHostPid(root, port);
|
|
97555
97746
|
started = true;
|
|
97556
97747
|
} else {
|
|
97557
|
-
|
|
97748
|
+
await killSpawnedHost(root);
|
|
97558
97749
|
}
|
|
97559
97750
|
}
|
|
97560
97751
|
if (!started) {
|
|
@@ -110077,7 +110268,7 @@ function formatAge(ms) {
|
|
|
110077
110268
|
|
|
110078
110269
|
// ../../packages/cli/src/commands/investigate.ts
|
|
110079
110270
|
init_cjs_shims();
|
|
110080
|
-
var
|
|
110271
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
110081
110272
|
|
|
110082
110273
|
// ../../packages/cli/src/lib/cloud/investigation-sync.ts
|
|
110083
110274
|
init_cjs_shims();
|
|
@@ -110495,9 +110686,110 @@ async function maybePromptFeedback(opts) {
|
|
|
110495
110686
|
}
|
|
110496
110687
|
}
|
|
110497
110688
|
|
|
110498
|
-
// ../../packages/cli/src/lib/
|
|
110689
|
+
// ../../packages/cli/src/lib/freshness.ts
|
|
110499
110690
|
init_cjs_shims();
|
|
110691
|
+
var import_node_child_process5 = require("child_process");
|
|
110692
|
+
var import_node_fs15 = require("fs");
|
|
110693
|
+
var import_node_path16 = require("path");
|
|
110694
|
+
var import_node_util4 = require("util");
|
|
110500
110695
|
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
110696
|
+
var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process5.execFile);
|
|
110697
|
+
async function commitsSince(repoRoot, sinceIso) {
|
|
110698
|
+
try {
|
|
110699
|
+
const { stdout } = await execFileAsync(
|
|
110700
|
+
"git",
|
|
110701
|
+
["-C", repoRoot, "rev-list", "--count", `--since=${sinceIso}`, "HEAD"],
|
|
110702
|
+
{ timeout: 5e3 }
|
|
110703
|
+
);
|
|
110704
|
+
const n4 = parseInt(stdout.trim(), 10);
|
|
110705
|
+
return Number.isFinite(n4) ? n4 : null;
|
|
110706
|
+
} catch {
|
|
110707
|
+
return null;
|
|
110708
|
+
}
|
|
110709
|
+
}
|
|
110710
|
+
var RUNTIME_SOURCES = /* @__PURE__ */ new Set(["logs", "metrics", "state", "queue"]);
|
|
110711
|
+
var STALE_INDEX_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
110712
|
+
function readIndexMeta(repoRoot) {
|
|
110713
|
+
try {
|
|
110714
|
+
const raw = (0, import_node_fs15.readFileSync)((0, import_node_path16.join)(repoRoot, ".horus", "source", "meta.json"), "utf8");
|
|
110715
|
+
const j3 = JSON.parse(raw);
|
|
110716
|
+
return {
|
|
110717
|
+
lastIndexedAt: typeof j3.last_indexed_at === "string" ? j3.last_indexed_at : void 0,
|
|
110718
|
+
version: typeof j3.version === "string" ? j3.version : void 0,
|
|
110719
|
+
stats: j3.stats ?? void 0
|
|
110720
|
+
};
|
|
110721
|
+
} catch {
|
|
110722
|
+
return null;
|
|
110723
|
+
}
|
|
110724
|
+
}
|
|
110725
|
+
function humanAge(ms) {
|
|
110726
|
+
const s5 = Math.max(0, Math.floor(ms / 1e3));
|
|
110727
|
+
if (s5 < 60) return `${s5}s`;
|
|
110728
|
+
const m5 = Math.floor(s5 / 60);
|
|
110729
|
+
if (m5 < 60) return `${m5}m`;
|
|
110730
|
+
const h3 = Math.floor(m5 / 60);
|
|
110731
|
+
if (h3 < 24) return `${h3}h`;
|
|
110732
|
+
return `${Math.floor(h3 / 24)}d`;
|
|
110733
|
+
}
|
|
110734
|
+
function computeFreshness(opts) {
|
|
110735
|
+
const now = Date.parse(opts.nowIso);
|
|
110736
|
+
const meta = opts.meta !== void 0 ? opts.meta : readIndexMeta(opts.repoRoot);
|
|
110737
|
+
const commitsSinceIndex = opts.commitsSinceIndex ?? null;
|
|
110738
|
+
const caveats = [];
|
|
110739
|
+
let indexAgeMs = null;
|
|
110740
|
+
let indexAgeLabel = "index age unknown";
|
|
110741
|
+
let indexStale = false;
|
|
110742
|
+
const indexedAt = meta?.lastIndexedAt ? Date.parse(meta.lastIndexedAt) : NaN;
|
|
110743
|
+
if (!Number.isNaN(indexedAt)) {
|
|
110744
|
+
indexAgeMs = Math.max(0, now - indexedAt);
|
|
110745
|
+
indexAgeLabel = `indexed ${humanAge(indexAgeMs)} ago`;
|
|
110746
|
+
indexStale = indexAgeMs > STALE_INDEX_MS;
|
|
110747
|
+
if (indexStale) {
|
|
110748
|
+
caveats.push(
|
|
110749
|
+
`code index is ${humanAge(indexAgeMs)} old \u2014 re-run \`horus index\` so analysis reflects current code`
|
|
110750
|
+
);
|
|
110751
|
+
}
|
|
110752
|
+
} else {
|
|
110753
|
+
caveats.push("code index age unknown (no .horus/source/meta.json) \u2014 freshness unverified");
|
|
110754
|
+
}
|
|
110755
|
+
if (typeof commitsSinceIndex === "number" && commitsSinceIndex > 0) {
|
|
110756
|
+
caveats.push(
|
|
110757
|
+
`${commitsSinceIndex} commit(s) since the last index \u2014 re-run \`horus index\` so analysis reflects current code`
|
|
110758
|
+
);
|
|
110759
|
+
}
|
|
110760
|
+
const runtime = opts.evidence.filter((e) => e.source && RUNTIME_SOURCES.has(e.source));
|
|
110761
|
+
const runtimeSources = [...new Set(runtime.map((e) => e.source))].sort();
|
|
110762
|
+
const stamps = runtime.map((e) => e.timestamp ? Date.parse(e.timestamp) : NaN).filter((n4) => !Number.isNaN(n4)).sort((a2, b5) => a2 - b5);
|
|
110763
|
+
const runtimeWindow = stamps.length > 0 ? { fromIso: new Date(stamps[0]).toISOString(), toIso: new Date(stamps[stamps.length - 1]).toISOString() } : null;
|
|
110764
|
+
if (runtime.length === 0) {
|
|
110765
|
+
caveats.push(
|
|
110766
|
+
"no runtime evidence \u2014 source-only investigation; connect logs/metrics/queues for deeper grounding"
|
|
110767
|
+
);
|
|
110768
|
+
}
|
|
110769
|
+
return {
|
|
110770
|
+
indexAgeMs,
|
|
110771
|
+
indexAgeLabel,
|
|
110772
|
+
indexStale,
|
|
110773
|
+
commitsSinceIndex,
|
|
110774
|
+
runtimeWindow,
|
|
110775
|
+
runtimeSources,
|
|
110776
|
+
caveats
|
|
110777
|
+
};
|
|
110778
|
+
}
|
|
110779
|
+
function renderFreshness(f5) {
|
|
110780
|
+
const lines = [];
|
|
110781
|
+
const behind = f5.commitsSinceIndex && f5.commitsSinceIndex > 0 ? ` (${f5.commitsSinceIndex} behind)` : "";
|
|
110782
|
+
const label = f5.indexAgeLabel + behind;
|
|
110783
|
+
const idx = f5.indexStale || behind ? import_picocolors9.default.yellow(label) : label;
|
|
110784
|
+
const runtime = f5.runtimeWindow !== null ? `runtime ${f5.runtimeWindow.fromIso.slice(0, 10)}\u2192${f5.runtimeWindow.toIso.slice(0, 10)}` + (f5.runtimeSources.length ? ` (${f5.runtimeSources.join(", ")})` : "") : "no runtime window";
|
|
110785
|
+
lines.push(import_picocolors9.default.dim(`Freshness: code ${idx} \xB7 ${runtime}`));
|
|
110786
|
+
for (const c4 of f5.caveats) lines.push(import_picocolors9.default.yellow(` \u26A0 ${c4}`));
|
|
110787
|
+
return lines.join("\n");
|
|
110788
|
+
}
|
|
110789
|
+
|
|
110790
|
+
// ../../packages/cli/src/lib/investigation-runner.ts
|
|
110791
|
+
init_cjs_shims();
|
|
110792
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
110501
110793
|
async function buildInvestigationContext(renv, opts) {
|
|
110502
110794
|
const log = opts.log ?? ((l2) => console.error(l2));
|
|
110503
110795
|
const code = codeForEnv(renv);
|
|
@@ -110509,20 +110801,20 @@ async function buildInvestigationContext(renv, opts) {
|
|
|
110509
110801
|
const sourceUrl = renv.repositories[0]?.sourceHostUrl;
|
|
110510
110802
|
let health = await code.health();
|
|
110511
110803
|
if (!health.ok && sourceUrl) {
|
|
110512
|
-
log(
|
|
110804
|
+
log(import_picocolors10.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`));
|
|
110513
110805
|
const healed = await ensureSourceHost(renv.path, sourceUrl);
|
|
110514
110806
|
if (healed.ok) {
|
|
110515
|
-
log(
|
|
110807
|
+
log(import_picocolors10.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
|
|
110516
110808
|
health = await code.health();
|
|
110517
110809
|
} else {
|
|
110518
|
-
log(
|
|
110810
|
+
log(import_picocolors10.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
|
|
110519
110811
|
}
|
|
110520
110812
|
}
|
|
110521
110813
|
const runtimeOnly = !health.ok;
|
|
110522
110814
|
if (runtimeOnly) {
|
|
110523
110815
|
log(
|
|
110524
|
-
|
|
110525
|
-
`Proceeding in runtime-only mode \u2014 no source intelligence. Run ${
|
|
110816
|
+
import_picocolors10.default.yellow(
|
|
110817
|
+
`Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors10.default.bold("horus index")} for a full (code-aware) investigation.`
|
|
110526
110818
|
)
|
|
110527
110819
|
);
|
|
110528
110820
|
}
|
|
@@ -110677,37 +110969,37 @@ function renderStoredAIJudgment(judgment, write = (l2) => console.log(l2)) {
|
|
|
110677
110969
|
const sep = "\u2500".repeat(60);
|
|
110678
110970
|
write(`
|
|
110679
110971
|
${sep}`);
|
|
110680
|
-
write(
|
|
110972
|
+
write(import_picocolors11.default.bold("AI Judgment") + import_picocolors11.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
|
|
110681
110973
|
write(sep);
|
|
110682
110974
|
if (judgment.hypothesisJudgments && judgment.hypothesisJudgments.length > 0) {
|
|
110683
|
-
write(
|
|
110975
|
+
write(import_picocolors11.default.bold("Hypothesis verdicts:"));
|
|
110684
110976
|
for (const j3 of judgment.hypothesisJudgments) {
|
|
110685
|
-
const verdictColor = j3.verdict === "supported" ?
|
|
110686
|
-
write(` ${verdictColor(`[${j3.verdict}]`)} ${j3.category}` +
|
|
110687
|
-
write(
|
|
110977
|
+
const verdictColor = j3.verdict === "supported" ? import_picocolors11.default.green : j3.verdict === "weakened" ? import_picocolors11.default.yellow : j3.verdict === "eliminated" ? import_picocolors11.default.red : import_picocolors11.default.dim;
|
|
110978
|
+
write(` ${verdictColor(`[${j3.verdict}]`)} ${j3.category}` + import_picocolors11.default.dim(` (${(j3.confidence * 100).toFixed(0)}%)`));
|
|
110979
|
+
write(import_picocolors11.default.dim(` ${j3.rationale}`));
|
|
110688
110980
|
}
|
|
110689
110981
|
write("");
|
|
110690
110982
|
}
|
|
110691
110983
|
if (judgment.rootCauseAssessment) {
|
|
110692
110984
|
const rca = judgment.rootCauseAssessment;
|
|
110693
|
-
const uncertaintyColor = rca.uncertainty === "low" ?
|
|
110694
|
-
write(
|
|
110985
|
+
const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors11.default.green : rca.uncertainty === "medium" ? import_picocolors11.default.yellow : import_picocolors11.default.red;
|
|
110986
|
+
write(import_picocolors11.default.bold("Root cause assessment:") + import_picocolors11.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
|
|
110695
110987
|
write(rca.summary);
|
|
110696
110988
|
if (rca.citedEvidenceIds.length > 0) {
|
|
110697
|
-
write(
|
|
110989
|
+
write(import_picocolors11.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
|
|
110698
110990
|
}
|
|
110699
110991
|
write("");
|
|
110700
110992
|
}
|
|
110701
|
-
write(
|
|
110702
|
-
write(
|
|
110993
|
+
write(import_picocolors11.default.bold("What:") + " " + judgment.what);
|
|
110994
|
+
write(import_picocolors11.default.bold("Why:") + " " + judgment.why);
|
|
110703
110995
|
if (judgment.whereNext.length > 0) {
|
|
110704
|
-
write(
|
|
110996
|
+
write(import_picocolors11.default.bold("Next steps:"));
|
|
110705
110997
|
for (const step of judgment.whereNext) {
|
|
110706
110998
|
write(` \u2022 ${step}`);
|
|
110707
110999
|
}
|
|
110708
111000
|
}
|
|
110709
111001
|
if (judgment.citations.length > 0) {
|
|
110710
|
-
write(
|
|
111002
|
+
write(import_picocolors11.default.dim(`
|
|
110711
111003
|
Cited evidence: ${judgment.citations.map((c4) => c4.evidenceId).join(", ")}`));
|
|
110712
111004
|
}
|
|
110713
111005
|
}
|
|
@@ -110733,7 +111025,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110733
111025
|
try {
|
|
110734
111026
|
renv = resolveEnvironment(config2, { project: projectName, env: opts.env });
|
|
110735
111027
|
} catch (err2) {
|
|
110736
|
-
console.error(
|
|
111028
|
+
console.error(import_picocolors11.default.red(err2.message));
|
|
110737
111029
|
return 1;
|
|
110738
111030
|
}
|
|
110739
111031
|
const repoRoot = repoRootOrCwd(renv.path);
|
|
@@ -110742,8 +111034,8 @@ async function runInvestigate(hint, opts) {
|
|
|
110742
111034
|
const cloudSession = cloudActive ? authedClient() : null;
|
|
110743
111035
|
if (cloudActive && !cloudSession) {
|
|
110744
111036
|
console.error(
|
|
110745
|
-
|
|
110746
|
-
`This repo is linked to Horus Cloud but you are not logged in \u2014 running locally only. Run ${
|
|
111037
|
+
import_picocolors11.default.yellow(
|
|
111038
|
+
`This repo is linked to Horus Cloud but you are not logged in \u2014 running locally only. Run ${import_picocolors11.default.bold("horus login")} to also sync results to the cloud.`
|
|
110747
111039
|
)
|
|
110748
111040
|
);
|
|
110749
111041
|
}
|
|
@@ -110754,7 +111046,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110754
111046
|
...opts.service !== void 0 ? { service: opts.service } : {}
|
|
110755
111047
|
});
|
|
110756
111048
|
} catch (err2) {
|
|
110757
|
-
console.error(
|
|
111049
|
+
console.error(import_picocolors11.default.red(err2.message));
|
|
110758
111050
|
return 1;
|
|
110759
111051
|
}
|
|
110760
111052
|
try {
|
|
@@ -110771,8 +111063,24 @@ async function runInvestigate(hint, opts) {
|
|
|
110771
111063
|
);
|
|
110772
111064
|
const db = ctx.dbHandle.db;
|
|
110773
111065
|
const format = opts.json ? "json" : opts.format ?? "text";
|
|
110774
|
-
const
|
|
110775
|
-
|
|
111066
|
+
const fmeta = readIndexMeta(repoRoot);
|
|
111067
|
+
const commitsSinceIndex = fmeta?.lastIndexedAt ? await commitsSince(repoRoot, fmeta.lastIndexedAt) : null;
|
|
111068
|
+
const freshness = computeFreshness({
|
|
111069
|
+
repoRoot,
|
|
111070
|
+
evidence: report.evidence,
|
|
111071
|
+
nowIso: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111072
|
+
meta: fmeta,
|
|
111073
|
+
commitsSinceIndex
|
|
111074
|
+
});
|
|
111075
|
+
if (format === "json") {
|
|
111076
|
+
const obj = JSON.parse(reportToJSON(report));
|
|
111077
|
+
obj.freshness = freshness;
|
|
111078
|
+
console.log(JSON.stringify(obj, null, 2));
|
|
111079
|
+
} else {
|
|
111080
|
+
console.log(format === "markdown" || format === "md" ? reportToMarkdown(report) : renderReport(report));
|
|
111081
|
+
console.log("");
|
|
111082
|
+
console.log(renderFreshness(freshness));
|
|
111083
|
+
}
|
|
110776
111084
|
if (opts.ai && format !== "json") {
|
|
110777
111085
|
const ai = resolveAiSettings(config2);
|
|
110778
111086
|
const model = opts.aiModel ?? ai.model ?? "claude-opus-4-8";
|
|
@@ -110780,7 +111088,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110780
111088
|
model,
|
|
110781
111089
|
...ai.anthropicApiKey !== void 0 ? { apiKey: ai.anthropicApiKey } : {}
|
|
110782
111090
|
});
|
|
110783
|
-
console.log(
|
|
111091
|
+
console.log(import_picocolors11.default.dim(`[ai] model: ${model}`));
|
|
110784
111092
|
const narrativeInput = buildNarrativeInput(report);
|
|
110785
111093
|
const { output, fromProvider, degraded, validationErrors } = await renderNarrative(
|
|
110786
111094
|
narrativeInput,
|
|
@@ -110796,24 +111104,24 @@ async function runInvestigate(hint, opts) {
|
|
|
110796
111104
|
renderStoredAIJudgment(stored);
|
|
110797
111105
|
} else if (degraded) {
|
|
110798
111106
|
if (validationErrors && validationErrors.length > 0) {
|
|
110799
|
-
console.error(
|
|
111107
|
+
console.error(import_picocolors11.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
|
|
110800
111108
|
}
|
|
110801
111109
|
console.log("");
|
|
110802
|
-
console.log(
|
|
110803
|
-
console.log(
|
|
111110
|
+
console.log(import_picocolors11.default.dim("\u2500".repeat(60)));
|
|
111111
|
+
console.log(import_picocolors11.default.bold("AI narrative ") + import_picocolors11.default.yellow("(unstructured \u2014 not validated)"));
|
|
110804
111112
|
if (output.what?.trim()) {
|
|
110805
|
-
console.log(
|
|
111113
|
+
console.log(import_picocolors11.default.bold("\nWhat:"), output.what.trim());
|
|
110806
111114
|
}
|
|
110807
111115
|
if (output.why?.trim()) {
|
|
110808
|
-
console.log(
|
|
111116
|
+
console.log(import_picocolors11.default.bold("\nWhy:"), output.why.trim());
|
|
110809
111117
|
}
|
|
110810
111118
|
if (output.whereNext.length > 0) {
|
|
110811
|
-
console.log(
|
|
111119
|
+
console.log(import_picocolors11.default.bold("\nNext:"));
|
|
110812
111120
|
for (const a2 of output.whereNext) console.log(` - ${a2}`);
|
|
110813
111121
|
}
|
|
110814
111122
|
} else {
|
|
110815
111123
|
const reason = classifyAIFailure(validationErrors?.[0]);
|
|
110816
|
-
console.error(
|
|
111124
|
+
console.error(import_picocolors11.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
|
|
110817
111125
|
}
|
|
110818
111126
|
}
|
|
110819
111127
|
track({
|
|
@@ -110844,7 +111152,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110844
111152
|
if (cloudActive && cloudSession && cloudCfg) {
|
|
110845
111153
|
try {
|
|
110846
111154
|
const refs = await uploadInvestigationToCloud(cloudSession.client, cloudCfg, report);
|
|
110847
|
-
const cloudNote =
|
|
111155
|
+
const cloudNote = import_picocolors11.default.dim(`[cloud] investigation saved: ${refs.investigationId}`);
|
|
110848
111156
|
if (format === "json") console.error(cloudNote);
|
|
110849
111157
|
else console.log(cloudNote);
|
|
110850
111158
|
} catch (err2) {
|
|
@@ -110854,16 +111162,16 @@ async function runInvestigate(hint, opts) {
|
|
|
110854
111162
|
if (format !== "json") {
|
|
110855
111163
|
if (report.persisted === false) {
|
|
110856
111164
|
console.error(
|
|
110857
|
-
|
|
111165
|
+
import_picocolors11.default.yellow(`
|
|
110858
111166
|
\u26A0 Results were not saved \u2014 the investigation database was unreachable.`)
|
|
110859
111167
|
);
|
|
110860
111168
|
console.error(
|
|
110861
|
-
|
|
111169
|
+
import_picocolors11.default.dim(
|
|
110862
111170
|
` This report is display-only; \`horus ask\` won't find it. Start the database and re-run to persist.`
|
|
110863
111171
|
)
|
|
110864
111172
|
);
|
|
110865
111173
|
} else {
|
|
110866
|
-
console.log(
|
|
111174
|
+
console.log(import_picocolors11.default.dim(`
|
|
110867
111175
|
Ask a follow-up: horus ask ${report.id} "<question>"`));
|
|
110868
111176
|
}
|
|
110869
111177
|
await maybePromptFeedback({
|
|
@@ -110877,15 +111185,15 @@ Ask a follow-up: horus ask ${report.id} "<question>"`));
|
|
|
110877
111185
|
return 0;
|
|
110878
111186
|
} catch (err2) {
|
|
110879
111187
|
const msg = err2?.message || String(err2);
|
|
110880
|
-
console.error(
|
|
110881
|
-
if (process.env.HORUS_DEBUG) console.error(
|
|
111188
|
+
console.error(import_picocolors11.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
|
|
111189
|
+
if (process.env.HORUS_DEBUG) console.error(import_picocolors11.default.dim(err2?.stack ?? String(err2)));
|
|
110882
111190
|
return 1;
|
|
110883
111191
|
}
|
|
110884
111192
|
}
|
|
110885
111193
|
|
|
110886
111194
|
// ../../packages/cli/src/commands/watch.ts
|
|
110887
111195
|
init_cjs_shims();
|
|
110888
|
-
var
|
|
111196
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
110889
111197
|
function detectNewSentryIncidents(issues, seen) {
|
|
110890
111198
|
const fresh = [];
|
|
110891
111199
|
for (const issue2 of issues) {
|
|
@@ -110967,21 +111275,21 @@ async function runWatch(opts) {
|
|
|
110967
111275
|
try {
|
|
110968
111276
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
110969
111277
|
} catch (err2) {
|
|
110970
|
-
console.error(
|
|
111278
|
+
console.error(import_picocolors12.default.red(err2.message));
|
|
110971
111279
|
return 1;
|
|
110972
111280
|
}
|
|
110973
111281
|
let ctx;
|
|
110974
111282
|
try {
|
|
110975
111283
|
ctx = await buildInvestigationContext(renv, { databaseUrl: config2.database.url });
|
|
110976
111284
|
} catch (err2) {
|
|
110977
|
-
console.error(
|
|
111285
|
+
console.error(import_picocolors12.default.red(err2.message));
|
|
110978
111286
|
return 1;
|
|
110979
111287
|
}
|
|
110980
111288
|
const requested = opts.source ?? "auto";
|
|
110981
111289
|
const source = resolveSource(requested, ctx);
|
|
110982
111290
|
if (source === null) {
|
|
110983
111291
|
console.error(
|
|
110984
|
-
|
|
111292
|
+
import_picocolors12.default.red(
|
|
110985
111293
|
requested === "auto" ? `No Sentry or Elasticsearch connector configured for "${renv.project}" / "${renv.env}".` : `No ${requested} connector configured for "${renv.project}" / "${renv.env}".`
|
|
110986
111294
|
)
|
|
110987
111295
|
);
|
|
@@ -110991,7 +111299,7 @@ async function runWatch(opts) {
|
|
|
110991
111299
|
const intervalSec = Math.max(1, Number(opts.interval) || 60);
|
|
110992
111300
|
const seen = /* @__PURE__ */ new Set();
|
|
110993
111301
|
console.error(
|
|
110994
|
-
|
|
111302
|
+
import_picocolors12.default.dim(
|
|
110995
111303
|
`[watch] source: ${source} \xB7 project: ${renv.project} \xB7 env: ${renv.env}` + (opts.once ? " \xB7 mode: once" : ` \xB7 interval: ${intervalSec}s`)
|
|
110996
111304
|
)
|
|
110997
111305
|
);
|
|
@@ -111000,24 +111308,24 @@ async function runWatch(opts) {
|
|
|
111000
111308
|
try {
|
|
111001
111309
|
incidents = source === "sentry" ? await pollSentry(ctx.sentry, seen) : await pollElasticsearch(ctx.logs, seen, ctx.service);
|
|
111002
111310
|
} catch (err2) {
|
|
111003
|
-
console.error(
|
|
111311
|
+
console.error(import_picocolors12.default.yellow(`[watch] poll failed \u2014 ${err2.message}`));
|
|
111004
111312
|
return;
|
|
111005
111313
|
}
|
|
111006
111314
|
if (incidents.length === 0) {
|
|
111007
|
-
console.error(
|
|
111315
|
+
console.error(import_picocolors12.default.dim(`[watch] no new incidents`));
|
|
111008
111316
|
return;
|
|
111009
111317
|
}
|
|
111010
111318
|
for (const incident of incidents) {
|
|
111011
111319
|
try {
|
|
111012
111320
|
const report = await runOneInvestigation({ hint: incident.hint }, ctx);
|
|
111013
111321
|
const { cause, confidence } = headlineFor(report);
|
|
111014
|
-
const persisted = report.persisted === false ?
|
|
111322
|
+
const persisted = report.persisted === false ? import_picocolors12.default.yellow(" (not persisted)") : "";
|
|
111015
111323
|
console.log(
|
|
111016
|
-
`${
|
|
111324
|
+
`${import_picocolors12.default.bold("\u25CF")} ${incident.hint} ${import_picocolors12.default.dim("\u2192")} ${cause} ${import_picocolors12.default.dim(`(${(confidence * 100).toFixed(0)}%)`)} ${import_picocolors12.default.dim(`id: ${report.id}`)}${persisted}`
|
|
111017
111325
|
);
|
|
111018
111326
|
} catch (err2) {
|
|
111019
111327
|
console.error(
|
|
111020
|
-
|
|
111328
|
+
import_picocolors12.default.yellow(`[watch] investigation failed for "${incident.ref}" \u2014 ${err2.message}`)
|
|
111021
111329
|
);
|
|
111022
111330
|
}
|
|
111023
111331
|
}
|
|
@@ -111032,7 +111340,7 @@ async function runWatch(opts) {
|
|
|
111032
111340
|
const onSignal = () => {
|
|
111033
111341
|
if (stopping) return;
|
|
111034
111342
|
stopping = true;
|
|
111035
|
-
console.error(
|
|
111343
|
+
console.error(import_picocolors12.default.dim(`
|
|
111036
111344
|
[watch] shutting down\u2026`));
|
|
111037
111345
|
if (wake) wake();
|
|
111038
111346
|
};
|
|
@@ -111059,7 +111367,7 @@ async function runWatch(opts) {
|
|
|
111059
111367
|
|
|
111060
111368
|
// ../../packages/cli/src/commands/changes.ts
|
|
111061
111369
|
init_cjs_shims();
|
|
111062
|
-
var
|
|
111370
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
111063
111371
|
var CHANGES_AI_CONTRACT = `Provide a clearly separated AI change-impact review with:
|
|
111064
111372
|
|
|
111065
111373
|
Highest-risk changes
|
|
@@ -111084,7 +111392,7 @@ async function runChanges(base2, compare, opts) {
|
|
|
111084
111392
|
const health = await code.health();
|
|
111085
111393
|
if (!health.ok) {
|
|
111086
111394
|
console.error(
|
|
111087
|
-
|
|
111395
|
+
import_picocolors13.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
111088
111396
|
);
|
|
111089
111397
|
return 1;
|
|
111090
111398
|
}
|
|
@@ -111106,20 +111414,20 @@ async function runChanges(base2, compare, opts) {
|
|
|
111106
111414
|
});
|
|
111107
111415
|
console.log("\n" + renderInterpretation(result));
|
|
111108
111416
|
if (!result.ok) {
|
|
111109
|
-
console.error(
|
|
111417
|
+
console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
|
|
111110
111418
|
}
|
|
111111
111419
|
}
|
|
111112
111420
|
}
|
|
111113
111421
|
return 0;
|
|
111114
111422
|
} catch (err2) {
|
|
111115
|
-
console.error(
|
|
111423
|
+
console.error(import_picocolors13.default.red(err2.message));
|
|
111116
111424
|
return 1;
|
|
111117
111425
|
}
|
|
111118
111426
|
}
|
|
111119
111427
|
|
|
111120
111428
|
// ../../packages/cli/src/commands/timeline.ts
|
|
111121
111429
|
init_cjs_shims();
|
|
111122
|
-
var
|
|
111430
|
+
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
111123
111431
|
var DEFAULT_SINCE = "7 days ago";
|
|
111124
111432
|
function resolveTimelineWindow(opts) {
|
|
111125
111433
|
const usingDefault = !opts.all && opts.since === void 0;
|
|
@@ -111149,7 +111457,7 @@ async function runTimeline(service, opts) {
|
|
|
111149
111457
|
try {
|
|
111150
111458
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
111151
111459
|
} catch (err2) {
|
|
111152
|
-
console.error(
|
|
111460
|
+
console.error(import_picocolors14.default.red(err2.message));
|
|
111153
111461
|
return 1;
|
|
111154
111462
|
}
|
|
111155
111463
|
const { code } = createConnectors(config2);
|
|
@@ -111164,9 +111472,9 @@ async function runTimeline(service, opts) {
|
|
|
111164
111472
|
console.log(renderChangeTimeline(t2));
|
|
111165
111473
|
if (usingDefaultWindow) {
|
|
111166
111474
|
console.log(
|
|
111167
|
-
|
|
111475
|
+
import_picocolors14.default.dim(
|
|
111168
111476
|
`
|
|
111169
|
-
Showing the last 7 days (default). Widen with ${
|
|
111477
|
+
Showing the last 7 days (default). Widen with ${import_picocolors14.default.bold('--since "30 days ago"')}, pin a range with ${import_picocolors14.default.bold("--since <when> --until <when>")}, or see everything with ${import_picocolors14.default.bold("--all")}.`
|
|
111170
111478
|
)
|
|
111171
111479
|
);
|
|
111172
111480
|
}
|
|
@@ -111183,21 +111491,26 @@ async function runTimeline(service, opts) {
|
|
|
111183
111491
|
});
|
|
111184
111492
|
console.log("\n" + renderInterpretation(result));
|
|
111185
111493
|
if (!result.ok) {
|
|
111186
|
-
console.error(
|
|
111494
|
+
console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
|
|
111187
111495
|
}
|
|
111188
111496
|
}
|
|
111189
111497
|
}
|
|
111190
111498
|
return 0;
|
|
111191
111499
|
} catch (err2) {
|
|
111192
|
-
console.error(
|
|
111500
|
+
console.error(import_picocolors14.default.red(err2.message));
|
|
111193
111501
|
return 1;
|
|
111194
111502
|
}
|
|
111195
111503
|
}
|
|
111196
111504
|
|
|
111197
111505
|
// ../../packages/cli/src/commands/what-changed.ts
|
|
111198
111506
|
init_cjs_shims();
|
|
111199
|
-
var
|
|
111507
|
+
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
111200
111508
|
var DEFAULT_SINCE2 = "7 days ago";
|
|
111509
|
+
function looksLikeGitRef(s5) {
|
|
111510
|
+
const t2 = s5.trim();
|
|
111511
|
+
if (/\s/.test(t2) || /^\d{4}-\d{2}-\d{2}/.test(t2)) return false;
|
|
111512
|
+
return /[~^]/.test(t2) || /^HEAD\b/.test(t2) || /^[0-9a-f]{7,40}$/i.test(t2) || /^v?\d+\.\d+/.test(t2);
|
|
111513
|
+
}
|
|
111201
111514
|
var WHAT_CHANGED_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
111202
111515
|
|
|
111203
111516
|
Evidence used
|
|
@@ -111220,11 +111533,18 @@ async function runWhatChanged(service, opts) {
|
|
|
111220
111533
|
try {
|
|
111221
111534
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
111222
111535
|
} catch (err2) {
|
|
111223
|
-
console.error(
|
|
111536
|
+
console.error(import_picocolors15.default.red(err2.message));
|
|
111224
111537
|
return 1;
|
|
111225
111538
|
}
|
|
111226
111539
|
const { code } = createConnectors(config2);
|
|
111227
111540
|
const since = opts.since ?? DEFAULT_SINCE2;
|
|
111541
|
+
if (opts.since !== void 0 && looksLikeGitRef(opts.since) && !await resolvesToCommit(renv.path, opts.since)) {
|
|
111542
|
+
console.error(
|
|
111543
|
+
import_picocolors15.default.yellow(
|
|
111544
|
+
`Warning: --since "${opts.since}" does not resolve to a commit (shallow clone or unknown ref?); it will be treated as a date filter and likely return no commits.`
|
|
111545
|
+
)
|
|
111546
|
+
);
|
|
111547
|
+
}
|
|
111228
111548
|
const r = await whatChanged(
|
|
111229
111549
|
{ repoPath: renv.path, since, until: opts.until, service },
|
|
111230
111550
|
{ code }
|
|
@@ -111243,7 +111563,7 @@ async function runWhatChanged(service, opts) {
|
|
|
111243
111563
|
});
|
|
111244
111564
|
console.log("\n" + renderInterpretation(result));
|
|
111245
111565
|
if (!result.ok) {
|
|
111246
|
-
console.error(
|
|
111566
|
+
console.error(import_picocolors15.default.yellow(`[ai] ${result.warning}`));
|
|
111247
111567
|
}
|
|
111248
111568
|
}
|
|
111249
111569
|
if (opts.push) {
|
|
@@ -111251,19 +111571,19 @@ async function runWhatChanged(service, opts) {
|
|
|
111251
111571
|
}
|
|
111252
111572
|
return 0;
|
|
111253
111573
|
} catch (err2) {
|
|
111254
|
-
console.error(
|
|
111574
|
+
console.error(import_picocolors15.default.red(err2.message));
|
|
111255
111575
|
return 1;
|
|
111256
111576
|
}
|
|
111257
111577
|
}
|
|
111258
111578
|
async function pushChangeReport(r, repoCwd) {
|
|
111259
111579
|
const session = authedClient();
|
|
111260
111580
|
if (!session) {
|
|
111261
|
-
console.error(
|
|
111581
|
+
console.error(import_picocolors15.default.yellow("[cloud] not logged in \u2014 run `horus login` to push reports."));
|
|
111262
111582
|
return;
|
|
111263
111583
|
}
|
|
111264
111584
|
const cfg = readCloudConfig(repoRootOrCwd(repoCwd));
|
|
111265
111585
|
if (!isCloudActive(cfg)) {
|
|
111266
|
-
console.error(
|
|
111586
|
+
console.error(import_picocolors15.default.yellow("[cloud] repo not linked \u2014 run `horus cloud link` to push reports."));
|
|
111267
111587
|
return;
|
|
111268
111588
|
}
|
|
111269
111589
|
const projectId = cfg.project?.id;
|
|
@@ -111309,15 +111629,15 @@ async function pushChangeReport(r, repoCwd) {
|
|
|
111309
111629
|
queueTopologyTouched: r.queueTopology.touched,
|
|
111310
111630
|
payload
|
|
111311
111631
|
});
|
|
111312
|
-
console.error(
|
|
111632
|
+
console.error(import_picocolors15.default.dim("[cloud] pushed change report."));
|
|
111313
111633
|
} catch (err2) {
|
|
111314
|
-
console.error(
|
|
111634
|
+
console.error(import_picocolors15.default.yellow(`[cloud] push failed: ${err2.message}`));
|
|
111315
111635
|
}
|
|
111316
111636
|
}
|
|
111317
111637
|
|
|
111318
111638
|
// ../../packages/cli/src/commands/architecture.ts
|
|
111319
111639
|
init_cjs_shims();
|
|
111320
|
-
var
|
|
111640
|
+
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
111321
111641
|
var ARCHITECTURE_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
111322
111642
|
|
|
111323
111643
|
System summary
|
|
@@ -111346,7 +111666,7 @@ async function runArchitecture(opts) {
|
|
|
111346
111666
|
const health = await code.health();
|
|
111347
111667
|
if (!health.ok) {
|
|
111348
111668
|
console.error(
|
|
111349
|
-
|
|
111669
|
+
import_picocolors16.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
111350
111670
|
);
|
|
111351
111671
|
return 1;
|
|
111352
111672
|
}
|
|
@@ -111374,7 +111694,7 @@ async function runArchitecture(opts) {
|
|
|
111374
111694
|
});
|
|
111375
111695
|
console.log("\n" + renderInterpretation(result));
|
|
111376
111696
|
if (!result.ok) {
|
|
111377
|
-
console.error(
|
|
111697
|
+
console.error(import_picocolors16.default.yellow(`[ai] ${result.warning}`));
|
|
111378
111698
|
}
|
|
111379
111699
|
}
|
|
111380
111700
|
}
|
|
@@ -111383,14 +111703,14 @@ async function runArchitecture(opts) {
|
|
|
111383
111703
|
}
|
|
111384
111704
|
return 0;
|
|
111385
111705
|
} catch (err2) {
|
|
111386
|
-
console.error(
|
|
111706
|
+
console.error(import_picocolors16.default.red(err2.message));
|
|
111387
111707
|
return 1;
|
|
111388
111708
|
}
|
|
111389
111709
|
}
|
|
111390
111710
|
|
|
111391
111711
|
// ../../packages/cli/src/commands/blast-radius.ts
|
|
111392
111712
|
init_cjs_shims();
|
|
111393
|
-
var
|
|
111713
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
111394
111714
|
var BLAST_RADIUS_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
111395
111715
|
|
|
111396
111716
|
Evidence used
|
|
@@ -111417,7 +111737,7 @@ async function runBlastRadius(query, opts) {
|
|
|
111417
111737
|
const { code } = createConnectors(config2);
|
|
111418
111738
|
const health = await code.health();
|
|
111419
111739
|
if (!health.ok) {
|
|
111420
|
-
console.error(
|
|
111740
|
+
console.error(import_picocolors17.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
|
|
111421
111741
|
return 1;
|
|
111422
111742
|
}
|
|
111423
111743
|
let project;
|
|
@@ -111430,12 +111750,12 @@ async function runBlastRadius(query, opts) {
|
|
|
111430
111750
|
const r = await analyzeBlastRadius(query, { code, db, project }, opts.depth ?? 3);
|
|
111431
111751
|
if (!r) {
|
|
111432
111752
|
console.log(`No symbol found for: ${query}`);
|
|
111433
|
-
console.log(
|
|
111753
|
+
console.log(import_picocolors17.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
|
|
111434
111754
|
return 1;
|
|
111435
111755
|
}
|
|
111436
111756
|
if (r.seed.name.toLowerCase() !== query.toLowerCase()) {
|
|
111437
111757
|
console.log(
|
|
111438
|
-
|
|
111758
|
+
import_picocolors17.default.yellow(` No exact match for "${query}"`) + import_picocolors17.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
|
|
111439
111759
|
);
|
|
111440
111760
|
}
|
|
111441
111761
|
if (opts.json) {
|
|
@@ -111455,7 +111775,7 @@ async function runBlastRadius(query, opts) {
|
|
|
111455
111775
|
});
|
|
111456
111776
|
console.log("\n" + renderInterpretation(result));
|
|
111457
111777
|
if (!result.ok) {
|
|
111458
|
-
console.error(
|
|
111778
|
+
console.error(import_picocolors17.default.yellow(`[ai] ${result.warning}`));
|
|
111459
111779
|
}
|
|
111460
111780
|
}
|
|
111461
111781
|
}
|
|
@@ -111464,28 +111784,28 @@ async function runBlastRadius(query, opts) {
|
|
|
111464
111784
|
}
|
|
111465
111785
|
return 0;
|
|
111466
111786
|
} catch (err2) {
|
|
111467
|
-
console.error(
|
|
111787
|
+
console.error(import_picocolors17.default.red(err2.message));
|
|
111468
111788
|
return 1;
|
|
111469
111789
|
}
|
|
111470
111790
|
}
|
|
111471
111791
|
|
|
111472
111792
|
// ../../packages/cli/src/commands/repos.ts
|
|
111473
111793
|
init_cjs_shims();
|
|
111474
|
-
var
|
|
111794
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
111475
111795
|
async function runRepos(opts) {
|
|
111476
111796
|
const config2 = await loadConfig(opts.config);
|
|
111477
111797
|
const providers = repoProviders(config2);
|
|
111478
111798
|
if (providers.length === 0) {
|
|
111479
|
-
console.log(
|
|
111799
|
+
console.log(import_picocolors18.default.dim("No repositories configured."));
|
|
111480
111800
|
return 0;
|
|
111481
111801
|
}
|
|
111482
111802
|
const health = await reposHealth(providers);
|
|
111483
111803
|
for (const h3 of health) {
|
|
111484
|
-
const dot = h3.reachable ?
|
|
111485
|
-
const name2 =
|
|
111486
|
-
const hostUrl =
|
|
111487
|
-
const path =
|
|
111488
|
-
const detail = h3.detail ?
|
|
111804
|
+
const dot = h3.reachable ? import_picocolors18.default.green("\u25CF") : import_picocolors18.default.red("\u25CF");
|
|
111805
|
+
const name2 = import_picocolors18.default.bold(h3.repo.padEnd(20));
|
|
111806
|
+
const hostUrl = import_picocolors18.default.cyan(h3.hostUrl.padEnd(30));
|
|
111807
|
+
const path = import_picocolors18.default.dim(h3.path);
|
|
111808
|
+
const detail = h3.detail ? import_picocolors18.default.dim(`(${h3.detail})`) : "";
|
|
111489
111809
|
console.log(` ${dot} ${name2} ${hostUrl} ${path} ${detail}`);
|
|
111490
111810
|
}
|
|
111491
111811
|
return 0;
|
|
@@ -111493,7 +111813,7 @@ async function runRepos(opts) {
|
|
|
111493
111813
|
|
|
111494
111814
|
// ../../packages/cli/src/commands/search.ts
|
|
111495
111815
|
init_cjs_shims();
|
|
111496
|
-
var
|
|
111816
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
111497
111817
|
async function runSearch(query, opts) {
|
|
111498
111818
|
const config2 = await loadConfig(opts.config);
|
|
111499
111819
|
const providers = repoProviders(config2);
|
|
@@ -111504,16 +111824,16 @@ async function runSearch(query, opts) {
|
|
|
111504
111824
|
}
|
|
111505
111825
|
let totalMatches = 0;
|
|
111506
111826
|
for (const result of results) {
|
|
111507
|
-
const statusDot = result.reachable ?
|
|
111827
|
+
const statusDot = result.reachable ? import_picocolors19.default.green("\u25CF") : import_picocolors19.default.red("\u25CF");
|
|
111508
111828
|
console.log("");
|
|
111509
|
-
console.log(`${statusDot} ${
|
|
111829
|
+
console.log(`${statusDot} ${import_picocolors19.default.bold("## " + result.repo)} ${import_picocolors19.default.dim(result.hostUrl)}`);
|
|
111510
111830
|
if (!result.reachable) {
|
|
111511
|
-
console.log(
|
|
111831
|
+
console.log(import_picocolors19.default.dim(" (unreachable)"));
|
|
111512
111832
|
} else if (result.symbols.length === 0) {
|
|
111513
|
-
console.log(
|
|
111833
|
+
console.log(import_picocolors19.default.dim(" (no matches)"));
|
|
111514
111834
|
} else {
|
|
111515
111835
|
for (const sym of result.symbols) {
|
|
111516
|
-
console.log(` - ${
|
|
111836
|
+
console.log(` - ${import_picocolors19.default.bold(sym.name)} ${import_picocolors19.default.dim(sym.filePath)}`);
|
|
111517
111837
|
}
|
|
111518
111838
|
totalMatches += result.symbols.length;
|
|
111519
111839
|
}
|
|
@@ -111521,7 +111841,7 @@ async function runSearch(query, opts) {
|
|
|
111521
111841
|
console.log("");
|
|
111522
111842
|
const reachableCount = results.filter((r) => r.reachable).length;
|
|
111523
111843
|
console.log(
|
|
111524
|
-
|
|
111844
|
+
import_picocolors19.default.dim(
|
|
111525
111845
|
`${totalMatches} match(es) across ${reachableCount}/${results.length} reachable repo(s) for query: "${query}"`
|
|
111526
111846
|
)
|
|
111527
111847
|
);
|
|
@@ -111530,7 +111850,7 @@ async function runSearch(query, opts) {
|
|
|
111530
111850
|
|
|
111531
111851
|
// ../../packages/cli/src/commands/investigations.ts
|
|
111532
111852
|
init_cjs_shims();
|
|
111533
|
-
var
|
|
111853
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
111534
111854
|
|
|
111535
111855
|
// ../../packages/cli/src/lib/format.ts
|
|
111536
111856
|
init_cjs_shims();
|
|
@@ -111573,8 +111893,8 @@ async function runInvestigations(opts) {
|
|
|
111573
111893
|
const session = authedClient();
|
|
111574
111894
|
if (!session) {
|
|
111575
111895
|
console.error(
|
|
111576
|
-
|
|
111577
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
111896
|
+
import_picocolors20.default.red(
|
|
111897
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors20.default.bold("horus login")} first.`
|
|
111578
111898
|
)
|
|
111579
111899
|
);
|
|
111580
111900
|
return 1;
|
|
@@ -111615,18 +111935,18 @@ async function runInvestigations(opts) {
|
|
|
111615
111935
|
|
|
111616
111936
|
// ../../packages/cli/src/commands/replay.ts
|
|
111617
111937
|
init_cjs_shims();
|
|
111618
|
-
var
|
|
111938
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
111619
111939
|
async function runReplay(id, opts) {
|
|
111620
111940
|
const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
|
|
111621
111941
|
try {
|
|
111622
111942
|
const row = await getInvestigation(db, id);
|
|
111623
111943
|
if (!row) {
|
|
111624
|
-
console.error(
|
|
111944
|
+
console.error(import_picocolors21.default.red("No investigation found: " + id));
|
|
111625
111945
|
return 1;
|
|
111626
111946
|
}
|
|
111627
111947
|
if (!row.report) {
|
|
111628
111948
|
console.error(
|
|
111629
|
-
|
|
111949
|
+
import_picocolors21.default.red(
|
|
111630
111950
|
"Investigation " + id + " has no stored report (run a newer investigation)."
|
|
111631
111951
|
)
|
|
111632
111952
|
);
|
|
@@ -111639,7 +111959,7 @@ async function runReplay(id, opts) {
|
|
|
111639
111959
|
if (opts.ai && fmt !== "json") {
|
|
111640
111960
|
if (report.aiJudgment && !opts.refreshAi) {
|
|
111641
111961
|
renderStoredAIJudgment(report.aiJudgment);
|
|
111642
|
-
console.log(
|
|
111962
|
+
console.log(import_picocolors21.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
|
|
111643
111963
|
} else {
|
|
111644
111964
|
const narrativeInput = buildNarrativeInput(report);
|
|
111645
111965
|
const { provider } = await buildNarrativeProvider({
|
|
@@ -111652,17 +111972,17 @@ async function runReplay(id, opts) {
|
|
|
111652
111972
|
);
|
|
111653
111973
|
if (!fromProvider && degraded) {
|
|
111654
111974
|
if (validationErrors?.length) {
|
|
111655
|
-
console.error(
|
|
111975
|
+
console.error(import_picocolors21.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
|
|
111656
111976
|
}
|
|
111657
111977
|
console.log("");
|
|
111658
|
-
console.log(
|
|
111659
|
-
console.log(
|
|
111660
|
-
if (output.what?.trim()) console.log(
|
|
111661
|
-
if (output.why?.trim()) console.log(
|
|
111978
|
+
console.log(import_picocolors21.default.dim("\u2500".repeat(60)));
|
|
111979
|
+
console.log(import_picocolors21.default.bold("AI narrative ") + import_picocolors21.default.yellow("(unstructured \u2014 not validated)"));
|
|
111980
|
+
if (output.what?.trim()) console.log(import_picocolors21.default.bold("\nWhat:"), output.what.trim());
|
|
111981
|
+
if (output.why?.trim()) console.log(import_picocolors21.default.bold("\nWhy:"), output.why.trim());
|
|
111662
111982
|
} else if (!fromProvider) {
|
|
111663
|
-
console.error(
|
|
111983
|
+
console.error(import_picocolors21.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
|
|
111664
111984
|
if (validationErrors?.length) {
|
|
111665
|
-
console.error(
|
|
111985
|
+
console.error(import_picocolors21.default.dim(` ${validationErrors[0]}`));
|
|
111666
111986
|
}
|
|
111667
111987
|
} else {
|
|
111668
111988
|
const stored = narrativeOutputToStoredJudgment(output, "anthropic");
|
|
@@ -111683,7 +112003,7 @@ async function runReplay(id, opts) {
|
|
|
111683
112003
|
|
|
111684
112004
|
// ../../packages/cli/src/commands/owner.ts
|
|
111685
112005
|
init_cjs_shims();
|
|
111686
|
-
var
|
|
112006
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
111687
112007
|
async function runOwner(query, opts) {
|
|
111688
112008
|
try {
|
|
111689
112009
|
const config2 = await loadConfig(opts.config);
|
|
@@ -111691,7 +112011,7 @@ async function runOwner(query, opts) {
|
|
|
111691
112011
|
try {
|
|
111692
112012
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
111693
112013
|
} catch (err2) {
|
|
111694
|
-
console.error(
|
|
112014
|
+
console.error(import_picocolors22.default.red(err2.message));
|
|
111695
112015
|
return 1;
|
|
111696
112016
|
}
|
|
111697
112017
|
const code = codeForRepo(config2, renv.project);
|
|
@@ -111699,16 +112019,16 @@ async function runOwner(query, opts) {
|
|
|
111699
112019
|
console.log(opts.json ? ownershipToJSON(o5) : renderOwnership(o5));
|
|
111700
112020
|
return 0;
|
|
111701
112021
|
} catch (err2) {
|
|
111702
|
-
console.error(
|
|
112022
|
+
console.error(import_picocolors22.default.red(err2.message));
|
|
111703
112023
|
return 1;
|
|
111704
112024
|
}
|
|
111705
112025
|
}
|
|
111706
112026
|
|
|
111707
112027
|
// ../../packages/cli/src/commands/postmortem.ts
|
|
111708
112028
|
init_cjs_shims();
|
|
111709
|
-
var
|
|
111710
|
-
var
|
|
111711
|
-
var
|
|
112029
|
+
var import_node_fs16 = require("fs");
|
|
112030
|
+
var import_node_path17 = require("path");
|
|
112031
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
111712
112032
|
async function runPostmortem(id, opts) {
|
|
111713
112033
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
111714
112034
|
let sql2;
|
|
@@ -111722,11 +112042,11 @@ async function runPostmortem(id, opts) {
|
|
|
111722
112042
|
const { db } = conn;
|
|
111723
112043
|
const row = await getInvestigation(db, id);
|
|
111724
112044
|
if (!row) {
|
|
111725
|
-
log(
|
|
112045
|
+
log(import_picocolors23.default.red("No investigation found: " + id));
|
|
111726
112046
|
return 1;
|
|
111727
112047
|
}
|
|
111728
112048
|
if (!row.report) {
|
|
111729
|
-
log(
|
|
112049
|
+
log(import_picocolors23.default.red("Investigation " + id + " has no stored report."));
|
|
111730
112050
|
return 1;
|
|
111731
112051
|
}
|
|
111732
112052
|
report = migrateReport(row.report);
|
|
@@ -111818,25 +112138,25 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
111818
112138
|
}
|
|
111819
112139
|
}
|
|
111820
112140
|
if (opts.output) {
|
|
111821
|
-
const outputPath = (0,
|
|
111822
|
-
if ((0,
|
|
111823
|
-
log(
|
|
111824
|
-
log(
|
|
112141
|
+
const outputPath = (0, import_node_path17.resolve)(opts.output);
|
|
112142
|
+
if ((0, import_node_fs16.existsSync)(outputPath) && !opts.force) {
|
|
112143
|
+
log(import_picocolors23.default.red("\u2717") + " " + outputPath + " already exists");
|
|
112144
|
+
log(import_picocolors23.default.dim(" pass --force to overwrite"));
|
|
111825
112145
|
return 1;
|
|
111826
112146
|
}
|
|
111827
112147
|
try {
|
|
111828
|
-
(0,
|
|
112148
|
+
(0, import_node_fs16.writeFileSync)(outputPath, content, "utf8");
|
|
111829
112149
|
} catch (err2) {
|
|
111830
|
-
log(
|
|
112150
|
+
log(import_picocolors23.default.red("\u2717 Could not write " + outputPath + ": ") + err2.message);
|
|
111831
112151
|
return 1;
|
|
111832
112152
|
}
|
|
111833
|
-
log(
|
|
112153
|
+
log(import_picocolors23.default.green("\u2713") + " Saved postmortem to " + outputPath);
|
|
111834
112154
|
} else {
|
|
111835
112155
|
log(content);
|
|
111836
112156
|
}
|
|
111837
112157
|
} catch (err2) {
|
|
111838
112158
|
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
111839
|
-
log(
|
|
112159
|
+
log(import_picocolors23.default.red("postmortem failed: " + message));
|
|
111840
112160
|
return 1;
|
|
111841
112161
|
} finally {
|
|
111842
112162
|
if (sql2) {
|
|
@@ -111848,7 +112168,7 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
111848
112168
|
|
|
111849
112169
|
// ../../packages/cli/src/commands/score.ts
|
|
111850
112170
|
init_cjs_shims();
|
|
111851
|
-
var
|
|
112171
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
111852
112172
|
var SCORE_AI_CONTRACT = `Provide a clearly separated AI score explanation with:
|
|
111853
112173
|
|
|
111854
112174
|
Why this scored this way
|
|
@@ -111867,11 +112187,11 @@ async function runScore(id, opts) {
|
|
|
111867
112187
|
try {
|
|
111868
112188
|
const row = await getInvestigation(db, id);
|
|
111869
112189
|
if (!row) {
|
|
111870
|
-
console.error(
|
|
112190
|
+
console.error(import_picocolors24.default.red("No investigation found: " + id));
|
|
111871
112191
|
return 1;
|
|
111872
112192
|
}
|
|
111873
112193
|
if (!row.report) {
|
|
111874
|
-
console.error(
|
|
112194
|
+
console.error(import_picocolors24.default.red("Investigation " + id + " has no stored report."));
|
|
111875
112195
|
return 1;
|
|
111876
112196
|
}
|
|
111877
112197
|
const s5 = scoreInvestigation(migrateReport(row.report));
|
|
@@ -111892,7 +112212,7 @@ async function runScore(id, opts) {
|
|
|
111892
112212
|
});
|
|
111893
112213
|
console.log("\n" + renderInterpretation(result));
|
|
111894
112214
|
if (!result.ok) {
|
|
111895
|
-
console.error(
|
|
112215
|
+
console.error(import_picocolors24.default.yellow(`[ai] ${result.warning}`));
|
|
111896
112216
|
}
|
|
111897
112217
|
}
|
|
111898
112218
|
}
|
|
@@ -111932,7 +112252,7 @@ async function runScores(opts) {
|
|
|
111932
112252
|
|
|
111933
112253
|
// ../../packages/cli/src/commands/ask.ts
|
|
111934
112254
|
init_cjs_shims();
|
|
111935
|
-
var
|
|
112255
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
111936
112256
|
function renderAnswer(report, directive, opts) {
|
|
111937
112257
|
const answer = answerQuestion(report, directive);
|
|
111938
112258
|
if (answer) {
|
|
@@ -111944,13 +112264,13 @@ function renderAnswer(report, directive, opts) {
|
|
|
111944
112264
|
if (!opts.json && report.aiJudgment) {
|
|
111945
112265
|
const j3 = report.aiJudgment;
|
|
111946
112266
|
console.log("");
|
|
111947
|
-
console.log(
|
|
111948
|
-
console.log(
|
|
112267
|
+
console.log(import_picocolors25.default.dim("\u2500".repeat(60)));
|
|
112268
|
+
console.log(import_picocolors25.default.dim(`Stored AI judgment (${j3.provider}, ${j3.generatedAt}):`));
|
|
111949
112269
|
if (j3.rootCauseAssessment) {
|
|
111950
|
-
console.log(
|
|
111951
|
-
console.log(
|
|
112270
|
+
console.log(import_picocolors25.default.bold("Root cause (AI):"), j3.rootCauseAssessment.summary);
|
|
112271
|
+
console.log(import_picocolors25.default.dim(`Uncertainty: ${j3.rootCauseAssessment.uncertainty}`));
|
|
111952
112272
|
} else {
|
|
111953
|
-
console.log(
|
|
112273
|
+
console.log(import_picocolors25.default.bold("AI Why:"), j3.why);
|
|
111954
112274
|
}
|
|
111955
112275
|
}
|
|
111956
112276
|
return 0;
|
|
@@ -111979,8 +112299,8 @@ async function runAsk(id, directive, opts) {
|
|
|
111979
112299
|
const session = authedClient();
|
|
111980
112300
|
if (!session) {
|
|
111981
112301
|
console.error(
|
|
111982
|
-
|
|
111983
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
112302
|
+
import_picocolors25.default.red(
|
|
112303
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors25.default.bold("horus login")} first.`
|
|
111984
112304
|
)
|
|
111985
112305
|
);
|
|
111986
112306
|
return 1;
|
|
@@ -111989,15 +112309,15 @@ async function runAsk(id, directive, opts) {
|
|
|
111989
112309
|
const report = await fetchInvestigationReportFromCloud(session.client, cloudCfg, id);
|
|
111990
112310
|
if (report) return renderAnswer(report, directive, opts);
|
|
111991
112311
|
console.error(
|
|
111992
|
-
|
|
111993
|
-
`Investigation ${id} has no saved report locally or in Horus Cloud. Run ${
|
|
112312
|
+
import_picocolors25.default.red(
|
|
112313
|
+
`Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors25.default.bold("horus investigate")} first.`
|
|
111994
112314
|
)
|
|
111995
112315
|
);
|
|
111996
112316
|
return 1;
|
|
111997
112317
|
} catch (err2) {
|
|
111998
112318
|
if (err2 instanceof CloudError && err2.status === 404) {
|
|
111999
112319
|
console.error(
|
|
112000
|
-
|
|
112320
|
+
import_picocolors25.default.red(
|
|
112001
112321
|
`No investigation found for "${id}" locally or in Horus Cloud. Use the id printed at the top of \`horus investigate\` output.`
|
|
112002
112322
|
)
|
|
112003
112323
|
);
|
|
@@ -112011,21 +112331,21 @@ async function runAsk(id, directive, opts) {
|
|
|
112011
112331
|
case "found":
|
|
112012
112332
|
return renderAnswer(local.report, directive, opts);
|
|
112013
112333
|
case "no-report":
|
|
112014
|
-
console.error(
|
|
112334
|
+
console.error(import_picocolors25.default.red("Investigation " + id + " has no stored report."));
|
|
112015
112335
|
return 1;
|
|
112016
112336
|
case "error":
|
|
112017
|
-
console.error(
|
|
112337
|
+
console.error(import_picocolors25.default.red(local.message));
|
|
112018
112338
|
return 1;
|
|
112019
112339
|
case "not-found":
|
|
112020
112340
|
default:
|
|
112021
|
-
console.error(
|
|
112341
|
+
console.error(import_picocolors25.default.red("No investigation found: " + id));
|
|
112022
112342
|
return 1;
|
|
112023
112343
|
}
|
|
112024
112344
|
}
|
|
112025
112345
|
|
|
112026
112346
|
// ../../packages/cli/src/commands/onboard.ts
|
|
112027
112347
|
init_cjs_shims();
|
|
112028
|
-
var
|
|
112348
|
+
var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
112029
112349
|
var ONBOARD_AI_CONTRACT = `Provide a clearly separated AI onboarding guide with:
|
|
112030
112350
|
|
|
112031
112351
|
Start here
|
|
@@ -112053,7 +112373,7 @@ async function runOnboard(area, opts) {
|
|
|
112053
112373
|
try {
|
|
112054
112374
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
112055
112375
|
} catch (err2) {
|
|
112056
|
-
console.error(
|
|
112376
|
+
console.error(import_picocolors26.default.red(err2.message));
|
|
112057
112377
|
return 1;
|
|
112058
112378
|
}
|
|
112059
112379
|
const repo = { name: renv.project, path: renv.path };
|
|
@@ -112061,7 +112381,7 @@ async function runOnboard(area, opts) {
|
|
|
112061
112381
|
const health = await code.health();
|
|
112062
112382
|
if (!health.ok) {
|
|
112063
112383
|
console.error(
|
|
112064
|
-
|
|
112384
|
+
import_picocolors26.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
112065
112385
|
);
|
|
112066
112386
|
return 1;
|
|
112067
112387
|
}
|
|
@@ -112088,7 +112408,7 @@ async function runOnboard(area, opts) {
|
|
|
112088
112408
|
});
|
|
112089
112409
|
console.log("\n" + renderInterpretation(result));
|
|
112090
112410
|
if (!result.ok) {
|
|
112091
|
-
console.error(
|
|
112411
|
+
console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
|
|
112092
112412
|
}
|
|
112093
112413
|
}
|
|
112094
112414
|
}
|
|
@@ -112097,14 +112417,14 @@ async function runOnboard(area, opts) {
|
|
|
112097
112417
|
}
|
|
112098
112418
|
return 0;
|
|
112099
112419
|
} catch (err2) {
|
|
112100
|
-
console.error(
|
|
112420
|
+
console.error(import_picocolors26.default.red(err2.message));
|
|
112101
112421
|
return 1;
|
|
112102
112422
|
}
|
|
112103
112423
|
}
|
|
112104
112424
|
|
|
112105
112425
|
// ../../packages/cli/src/commands/simulate.ts
|
|
112106
112426
|
init_cjs_shims();
|
|
112107
|
-
var
|
|
112427
|
+
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
112108
112428
|
async function runSimulate(scenarioId, opts) {
|
|
112109
112429
|
try {
|
|
112110
112430
|
if (scenarioId === void 0) {
|
|
@@ -112114,7 +112434,7 @@ async function runSimulate(scenarioId, opts) {
|
|
|
112114
112434
|
const scenario = getScenario(scenarioId);
|
|
112115
112435
|
if (scenario === null) {
|
|
112116
112436
|
console.error(
|
|
112117
|
-
|
|
112437
|
+
import_picocolors27.default.red(
|
|
112118
112438
|
`Unknown scenario: "${scenarioId}". Run horus simulate to list available scenarios.`
|
|
112119
112439
|
)
|
|
112120
112440
|
);
|
|
@@ -112125,14 +112445,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
112125
112445
|
try {
|
|
112126
112446
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
112127
112447
|
} catch (err2) {
|
|
112128
|
-
console.error(
|
|
112448
|
+
console.error(import_picocolors27.default.red(err2.message));
|
|
112129
112449
|
return 1;
|
|
112130
112450
|
}
|
|
112131
112451
|
const code = codeForRepo(config2, renv.project);
|
|
112132
112452
|
const health = await code.health();
|
|
112133
112453
|
if (!health.ok) {
|
|
112134
112454
|
console.error(
|
|
112135
|
-
|
|
112455
|
+
import_picocolors27.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
112136
112456
|
);
|
|
112137
112457
|
return 1;
|
|
112138
112458
|
}
|
|
@@ -112149,14 +112469,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
112149
112469
|
}
|
|
112150
112470
|
return 0;
|
|
112151
112471
|
} catch (err2) {
|
|
112152
|
-
console.error(
|
|
112472
|
+
console.error(import_picocolors27.default.red(err2.message));
|
|
112153
112473
|
return 1;
|
|
112154
112474
|
}
|
|
112155
112475
|
}
|
|
112156
112476
|
|
|
112157
112477
|
// ../../packages/cli/src/commands/logs.ts
|
|
112158
112478
|
init_cjs_shims();
|
|
112159
|
-
var
|
|
112479
|
+
var import_picocolors28 = __toESM(require_picocolors(), 1);
|
|
112160
112480
|
var LOGS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
112161
112481
|
|
|
112162
112482
|
Evidence used
|
|
@@ -112202,9 +112522,9 @@ function parseWhere(raw) {
|
|
|
112202
112522
|
}
|
|
112203
112523
|
var whereLabel = (where) => where.map((w4) => `${w4.field}=${w4.value}`).join(", ");
|
|
112204
112524
|
function levelColor(level, text2) {
|
|
112205
|
-
if (level === "error" || level === "fatal") return
|
|
112206
|
-
if (level === "warn") return
|
|
112207
|
-
return
|
|
112525
|
+
if (level === "error" || level === "fatal") return import_picocolors28.default.red(text2);
|
|
112526
|
+
if (level === "warn") return import_picocolors28.default.yellow(text2);
|
|
112527
|
+
return import_picocolors28.default.dim(text2);
|
|
112208
112528
|
}
|
|
112209
112529
|
function matchServiceLabel(requested, candidates) {
|
|
112210
112530
|
if (candidates.includes(requested)) return { kind: "unique", matched: requested };
|
|
@@ -112220,7 +112540,7 @@ async function runLogs(service, opts) {
|
|
|
112220
112540
|
try {
|
|
112221
112541
|
where = parseWhere(opts.where);
|
|
112222
112542
|
} catch (err2) {
|
|
112223
|
-
console.error(
|
|
112543
|
+
console.error(import_picocolors28.default.red(err2.message));
|
|
112224
112544
|
return 1;
|
|
112225
112545
|
}
|
|
112226
112546
|
const hasWhere = where.length > 0;
|
|
@@ -112230,13 +112550,13 @@ async function runLogs(service, opts) {
|
|
|
112230
112550
|
try {
|
|
112231
112551
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
112232
112552
|
} catch (err2) {
|
|
112233
|
-
console.error(
|
|
112553
|
+
console.error(import_picocolors28.default.red(err2.message));
|
|
112234
112554
|
return 1;
|
|
112235
112555
|
}
|
|
112236
112556
|
const logs = logsForEnv(renv);
|
|
112237
112557
|
if (logs === null) {
|
|
112238
112558
|
console.error(
|
|
112239
|
-
|
|
112559
|
+
import_picocolors28.default.red(
|
|
112240
112560
|
`No Elasticsearch connector configured for ${renv.project}/${renv.env} (set ES_URL / ES_USERNAME / ES_PASSWORD)`
|
|
112241
112561
|
)
|
|
112242
112562
|
);
|
|
@@ -112244,7 +112564,7 @@ async function runLogs(service, opts) {
|
|
|
112244
112564
|
}
|
|
112245
112565
|
const health = await logs.health();
|
|
112246
112566
|
if (!health.ok) {
|
|
112247
|
-
console.error(
|
|
112567
|
+
console.error(import_picocolors28.default.red(`Elasticsearch unreachable: ${health.detail}`));
|
|
112248
112568
|
return 1;
|
|
112249
112569
|
}
|
|
112250
112570
|
const resolvedService = service ?? renv.connectors.elasticsearch?.serviceName;
|
|
@@ -112257,21 +112577,21 @@ async function runLogs(service, opts) {
|
|
|
112257
112577
|
requiresEventCode: aggregatesEventCode
|
|
112258
112578
|
});
|
|
112259
112579
|
for (const w4 of compat.issues.filter((i3) => i3.severity === "warning")) {
|
|
112260
|
-
console.warn(
|
|
112580
|
+
console.warn(import_picocolors28.default.yellow(`[warn] ${w4.field}: ${w4.message}`));
|
|
112261
112581
|
}
|
|
112262
112582
|
const errors2 = compat.issues.filter((i3) => i3.severity === "error");
|
|
112263
112583
|
if (errors2.length > 0) {
|
|
112264
|
-
console.error(
|
|
112584
|
+
console.error(import_picocolors28.default.red("Elasticsearch field mapping is incompatible with the index:"));
|
|
112265
112585
|
for (const e of errors2) {
|
|
112266
|
-
console.error(
|
|
112586
|
+
console.error(import_picocolors28.default.red(` ${e.field}: ${e.message}`));
|
|
112267
112587
|
}
|
|
112268
112588
|
console.error(
|
|
112269
|
-
|
|
112589
|
+
import_picocolors28.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
|
|
112270
112590
|
);
|
|
112271
112591
|
return 1;
|
|
112272
112592
|
}
|
|
112273
112593
|
} catch {
|
|
112274
|
-
console.warn(
|
|
112594
|
+
console.warn(import_picocolors28.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
|
|
112275
112595
|
}
|
|
112276
112596
|
const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
|
|
112277
112597
|
const fromDisplay = from.slice(0, 16).replace("T", " ");
|
|
@@ -112327,12 +112647,12 @@ async function runLogs(service, opts) {
|
|
|
112327
112647
|
return 0;
|
|
112328
112648
|
}
|
|
112329
112649
|
console.error(
|
|
112330
|
-
|
|
112650
|
+
import_picocolors28.default.dim(
|
|
112331
112651
|
`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)" : "")
|
|
112332
112652
|
)
|
|
112333
112653
|
);
|
|
112334
112654
|
if (records.length === 0) {
|
|
112335
|
-
console.log(
|
|
112655
|
+
console.log(import_picocolors28.default.dim("No logs matched."));
|
|
112336
112656
|
return 0;
|
|
112337
112657
|
}
|
|
112338
112658
|
for (const r of records) {
|
|
@@ -112342,12 +112662,12 @@ async function runLogs(service, opts) {
|
|
|
112342
112662
|
const line2 = `${ts} ${lvl} ${comp} ${r.message.slice(0, 120)}`;
|
|
112343
112663
|
console.log(levelColor(r.level, line2));
|
|
112344
112664
|
for (const { key, value } of extractContextFields(r)) {
|
|
112345
|
-
console.log(
|
|
112665
|
+
console.log(import_picocolors28.default.dim(` ${key}: ${value.slice(0, 200)}`));
|
|
112346
112666
|
}
|
|
112347
112667
|
}
|
|
112348
112668
|
if (truncated) {
|
|
112349
112669
|
console.error(
|
|
112350
|
-
|
|
112670
|
+
import_picocolors28.default.yellow(
|
|
112351
112671
|
`[warn] showing the first ${limit} record(s) \u2014 results may be truncated. Narrow with --where/--since/--level or raise --limit (max 1000).`
|
|
112352
112672
|
)
|
|
112353
112673
|
);
|
|
@@ -112390,22 +112710,22 @@ async function runLogs(service, opts) {
|
|
|
112390
112710
|
return 0;
|
|
112391
112711
|
}
|
|
112392
112712
|
console.log(
|
|
112393
|
-
|
|
112713
|
+
import_picocolors28.default.bold(`Top ${field}`) + import_picocolors28.default.dim(
|
|
112394
112714
|
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112395
112715
|
)
|
|
112396
112716
|
);
|
|
112397
|
-
console.log(
|
|
112717
|
+
console.log(import_picocolors28.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
112398
112718
|
console.log("");
|
|
112399
112719
|
if (buckets.length === 0) {
|
|
112400
|
-
console.log(
|
|
112401
|
-
console.log(
|
|
112720
|
+
console.log(import_picocolors28.default.dim(` No error logs with field "${field}" in the window.`));
|
|
112721
|
+
console.log(import_picocolors28.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
|
|
112402
112722
|
return 0;
|
|
112403
112723
|
}
|
|
112404
112724
|
for (const b5 of buckets) {
|
|
112405
|
-
console.log(` ${
|
|
112725
|
+
console.log(` ${import_picocolors28.default.red(String(b5.count).padStart(6))} ${b5.key}`);
|
|
112406
112726
|
}
|
|
112407
112727
|
console.log("");
|
|
112408
|
-
console.log(
|
|
112728
|
+
console.log(import_picocolors28.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
|
|
112409
112729
|
return 0;
|
|
112410
112730
|
}
|
|
112411
112731
|
let analysis = await logs.analyzeErrors({
|
|
@@ -112475,46 +112795,46 @@ async function runLogs(service, opts) {
|
|
|
112475
112795
|
return 0;
|
|
112476
112796
|
}
|
|
112477
112797
|
console.log(
|
|
112478
|
-
|
|
112798
|
+
import_picocolors28.default.bold(`Error analysis`) + import_picocolors28.default.dim(
|
|
112479
112799
|
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112480
112800
|
)
|
|
112481
112801
|
);
|
|
112482
|
-
console.log(
|
|
112802
|
+
console.log(import_picocolors28.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
112483
112803
|
if (broadeningNote) {
|
|
112484
|
-
console.log(
|
|
112804
|
+
console.log(import_picocolors28.default.yellow(` ${broadeningNote}`));
|
|
112485
112805
|
}
|
|
112486
112806
|
console.log(
|
|
112487
|
-
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${
|
|
112807
|
+
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors28.default.yellow(String(analysis.newSignatures.length))} new`
|
|
112488
112808
|
);
|
|
112489
112809
|
console.log("");
|
|
112490
112810
|
if (analysis.signatures.length === 0) {
|
|
112491
|
-
console.log(
|
|
112811
|
+
console.log(import_picocolors28.default.dim(" No error-level logs in the window."));
|
|
112492
112812
|
console.log(
|
|
112493
|
-
|
|
112813
|
+
import_picocolors28.default.dim(
|
|
112494
112814
|
` Searched: ${scopeService ? `service "${scopeService}"` : "all services"} \xB7 index: ${indexPattern} \xB7 from: ${fromDisplay} UTC`
|
|
112495
112815
|
)
|
|
112496
112816
|
);
|
|
112497
|
-
console.log(
|
|
112817
|
+
console.log(import_picocolors28.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
|
|
112498
112818
|
return 0;
|
|
112499
112819
|
}
|
|
112500
112820
|
for (const s5 of analysis.signatures) {
|
|
112501
112821
|
const tags = [];
|
|
112502
|
-
if (s5.isNew) tags.push(
|
|
112822
|
+
if (s5.isNew) tags.push(import_picocolors28.default.red("NEW"));
|
|
112503
112823
|
else if (s5.ratio !== void 0 && Number.isFinite(s5.ratio) && s5.ratio >= 1.5) {
|
|
112504
|
-
tags.push(
|
|
112824
|
+
tags.push(import_picocolors28.default.yellow(`spike x${s5.ratio.toFixed(1)}`));
|
|
112505
112825
|
}
|
|
112506
|
-
const svc = s5.services.length > 0 ?
|
|
112826
|
+
const svc = s5.services.length > 0 ? import_picocolors28.default.dim(` \xB7 ${s5.services.slice(0, 3).join(", ")}`) : "";
|
|
112507
112827
|
const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
|
|
112508
112828
|
console.log(
|
|
112509
|
-
` ${
|
|
112829
|
+
` ${import_picocolors28.default.red(String(s5.count).padStart(6))} ${import_picocolors28.default.bold(s5.key.padEnd(14).slice(0, 14))} ` + import_picocolors28.default.dim(`first ${shortTs(s5.firstSeen)} \xB7 last ${shortTs(s5.lastSeen)}`) + svc + tagStr
|
|
112510
112830
|
);
|
|
112511
112831
|
}
|
|
112512
112832
|
if (analysis.affectedServices.length > 0) {
|
|
112513
112833
|
console.log("");
|
|
112514
|
-
console.log(` ${
|
|
112834
|
+
console.log(` ${import_picocolors28.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
|
|
112515
112835
|
}
|
|
112516
112836
|
console.log("");
|
|
112517
|
-
console.log(
|
|
112837
|
+
console.log(import_picocolors28.default.dim(" (use --raw to see individual log lines)"));
|
|
112518
112838
|
if (opts.ai) {
|
|
112519
112839
|
const result = await renderAiInterpretation({
|
|
112520
112840
|
command: "logs",
|
|
@@ -112533,19 +112853,19 @@ async function runLogs(service, opts) {
|
|
|
112533
112853
|
});
|
|
112534
112854
|
console.log("\n" + renderInterpretation(result));
|
|
112535
112855
|
if (!result.ok) {
|
|
112536
|
-
console.error(
|
|
112856
|
+
console.error(import_picocolors28.default.yellow(`[ai] ${result.warning}`));
|
|
112537
112857
|
}
|
|
112538
112858
|
}
|
|
112539
112859
|
return 0;
|
|
112540
112860
|
} catch (err2) {
|
|
112541
|
-
console.error(
|
|
112861
|
+
console.error(import_picocolors28.default.red(err2.message));
|
|
112542
112862
|
return 1;
|
|
112543
112863
|
}
|
|
112544
112864
|
}
|
|
112545
112865
|
|
|
112546
112866
|
// ../../packages/cli/src/commands/metrics.ts
|
|
112547
112867
|
init_cjs_shims();
|
|
112548
|
-
var
|
|
112868
|
+
var import_picocolors29 = __toESM(require_picocolors(), 1);
|
|
112549
112869
|
var METRICS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
112550
112870
|
|
|
112551
112871
|
Evidence used
|
|
@@ -112590,10 +112910,10 @@ function anomalyColor(anomaly) {
|
|
|
112590
112910
|
case "latency-spike":
|
|
112591
112911
|
case "error-rate-change":
|
|
112592
112912
|
case "throughput-drop":
|
|
112593
|
-
return
|
|
112913
|
+
return import_picocolors29.default.red;
|
|
112594
112914
|
case "queue-growth":
|
|
112595
112915
|
case "change":
|
|
112596
|
-
return
|
|
112916
|
+
return import_picocolors29.default.yellow;
|
|
112597
112917
|
default:
|
|
112598
112918
|
return (s5) => s5;
|
|
112599
112919
|
}
|
|
@@ -112620,7 +112940,7 @@ async function runMetrics(hint, opts) {
|
|
|
112620
112940
|
const metrics = metricsProviderFromConfig(config2);
|
|
112621
112941
|
if (metrics === null) {
|
|
112622
112942
|
console.error(
|
|
112623
|
-
|
|
112943
|
+
import_picocolors29.default.red(
|
|
112624
112944
|
"Grafana not configured \u2014 set GRAFANA_URL + GRAFANA_USER + GRAFANA_PASSWORD"
|
|
112625
112945
|
)
|
|
112626
112946
|
);
|
|
@@ -112628,7 +112948,7 @@ async function runMetrics(hint, opts) {
|
|
|
112628
112948
|
}
|
|
112629
112949
|
const health = await metrics.health();
|
|
112630
112950
|
if (!health.ok) {
|
|
112631
|
-
console.error(
|
|
112951
|
+
console.error(import_picocolors29.default.red(`Grafana unreachable: ${health.detail}`));
|
|
112632
112952
|
return 1;
|
|
112633
112953
|
}
|
|
112634
112954
|
const stepNum = opts.step !== void 0 ? Number(opts.step) : void 0;
|
|
@@ -112657,14 +112977,14 @@ async function runMetrics(hint, opts) {
|
|
|
112657
112977
|
return 0;
|
|
112658
112978
|
}
|
|
112659
112979
|
if (series.length === 0) {
|
|
112660
|
-
console.log(
|
|
112980
|
+
console.log(import_picocolors29.default.dim("No series returned."));
|
|
112661
112981
|
return 0;
|
|
112662
112982
|
}
|
|
112663
112983
|
for (const s5 of series) {
|
|
112664
112984
|
const summary = summarize(s5);
|
|
112665
112985
|
const label = labelStr(s5.labels);
|
|
112666
112986
|
console.log(
|
|
112667
|
-
` ${
|
|
112987
|
+
` ${import_picocolors29.default.cyan(label || "(no labels)")} last=${import_picocolors29.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
|
|
112668
112988
|
);
|
|
112669
112989
|
}
|
|
112670
112990
|
return 0;
|
|
@@ -112679,36 +112999,36 @@ async function runMetrics(hint, opts) {
|
|
|
112679
112999
|
if (findings2.length === 0) {
|
|
112680
113000
|
if (hint !== void 0) {
|
|
112681
113001
|
const tokens = extractHintTokens(hint);
|
|
112682
|
-
console.log(
|
|
113002
|
+
console.log(import_picocolors29.default.dim(`No panels matched hint "${hint}".`));
|
|
112683
113003
|
if (tokens.length > 0) {
|
|
112684
|
-
console.log(
|
|
113004
|
+
console.log(import_picocolors29.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
|
|
112685
113005
|
}
|
|
112686
113006
|
console.log(
|
|
112687
|
-
|
|
112688
|
-
` Tip: run ${
|
|
113007
|
+
import_picocolors29.default.dim(
|
|
113008
|
+
` Tip: run ${import_picocolors29.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
|
|
112689
113009
|
)
|
|
112690
113010
|
);
|
|
112691
113011
|
} else {
|
|
112692
|
-
console.log(
|
|
113012
|
+
console.log(import_picocolors29.default.dim("No panels found in configured Grafana dashboards."));
|
|
112693
113013
|
}
|
|
112694
113014
|
return 0;
|
|
112695
113015
|
}
|
|
112696
113016
|
const hintSuffix = hint !== void 0 ? ` (hint: "${hint}")` : "";
|
|
112697
113017
|
const matchSources = hint !== void 0 ? [...new Set(findings2.map((f5) => f5.matchSource).filter(Boolean))] : [];
|
|
112698
|
-
const matchSuffix = matchSources.length > 0 ?
|
|
113018
|
+
const matchSuffix = matchSources.length > 0 ? import_picocolors29.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
|
|
112699
113019
|
console.log(
|
|
112700
|
-
|
|
113020
|
+
import_picocolors29.default.bold(
|
|
112701
113021
|
`Grafana metrics${hintSuffix} \u2014 ${findings2.length} series across panels`
|
|
112702
113022
|
) + matchSuffix
|
|
112703
113023
|
);
|
|
112704
113024
|
console.log(
|
|
112705
|
-
|
|
113025
|
+
import_picocolors29.default.dim(
|
|
112706
113026
|
` window: ${new Date(from * 1e3).toISOString()} \u2013 ${new Date(to * 1e3).toISOString()}`
|
|
112707
113027
|
)
|
|
112708
113028
|
);
|
|
112709
113029
|
console.log("");
|
|
112710
113030
|
if (flagged.length === 0) {
|
|
112711
|
-
console.log(
|
|
113031
|
+
console.log(import_picocolors29.default.green(" No anomalies detected."));
|
|
112712
113032
|
} else {
|
|
112713
113033
|
for (const f5 of flagged) {
|
|
112714
113034
|
const color = anomalyColor(f5.anomaly);
|
|
@@ -112717,14 +113037,14 @@ async function runMetrics(hint, opts) {
|
|
|
112717
113037
|
const lblStr = labelStr(f5.labels);
|
|
112718
113038
|
console.log(
|
|
112719
113039
|
color(
|
|
112720
|
-
` ${label.padEnd(22)} ${f5.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f5.baselineAvg)} -> ${fmtNum(f5.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${
|
|
113040
|
+
` ${label.padEnd(22)} ${f5.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f5.baselineAvg)} -> ${fmtNum(f5.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors29.default.dim(lblStr)}` : "")
|
|
112721
113041
|
)
|
|
112722
113042
|
);
|
|
112723
113043
|
}
|
|
112724
113044
|
}
|
|
112725
113045
|
if (ok.length > 0) {
|
|
112726
113046
|
console.log("");
|
|
112727
|
-
console.log(
|
|
113047
|
+
console.log(import_picocolors29.default.dim(` ${ok.length} panel series with no anomaly.`));
|
|
112728
113048
|
}
|
|
112729
113049
|
if (opts.ai) {
|
|
112730
113050
|
const result = await renderAiInterpretation({
|
|
@@ -112739,19 +113059,19 @@ async function runMetrics(hint, opts) {
|
|
|
112739
113059
|
});
|
|
112740
113060
|
console.log("\n" + renderInterpretation(result));
|
|
112741
113061
|
if (!result.ok) {
|
|
112742
|
-
console.error(
|
|
113062
|
+
console.error(import_picocolors29.default.yellow(`[ai] ${result.warning}`));
|
|
112743
113063
|
}
|
|
112744
113064
|
}
|
|
112745
113065
|
return 0;
|
|
112746
113066
|
} catch (err2) {
|
|
112747
|
-
console.error(
|
|
113067
|
+
console.error(import_picocolors29.default.red(err2.message));
|
|
112748
113068
|
return 1;
|
|
112749
113069
|
}
|
|
112750
113070
|
}
|
|
112751
113071
|
|
|
112752
113072
|
// ../../packages/cli/src/commands/state.ts
|
|
112753
113073
|
init_cjs_shims();
|
|
112754
|
-
var
|
|
113074
|
+
var import_picocolors30 = __toESM(require_picocolors(), 1);
|
|
112755
113075
|
var STATE_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
112756
113076
|
|
|
112757
113077
|
Evidence used
|
|
@@ -112777,13 +113097,13 @@ async function runState(opts) {
|
|
|
112777
113097
|
try {
|
|
112778
113098
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
112779
113099
|
} catch (err2) {
|
|
112780
|
-
console.error(
|
|
113100
|
+
console.error(import_picocolors30.default.red(err2.message));
|
|
112781
113101
|
return 1;
|
|
112782
113102
|
}
|
|
112783
113103
|
const mongo = mongoForEnv(renv);
|
|
112784
113104
|
if (mongo === null) {
|
|
112785
113105
|
console.error(
|
|
112786
|
-
|
|
113106
|
+
import_picocolors30.default.red(
|
|
112787
113107
|
`No MongoDB connector configured for ${renv.project}/${renv.env} (set the project's Mongo URL env var).`
|
|
112788
113108
|
)
|
|
112789
113109
|
);
|
|
@@ -112791,7 +113111,7 @@ async function runState(opts) {
|
|
|
112791
113111
|
}
|
|
112792
113112
|
const health = await mongo.health();
|
|
112793
113113
|
if (!health.ok) {
|
|
112794
|
-
console.error(
|
|
113114
|
+
console.error(import_picocolors30.default.red(`MongoDB unreachable: ${health.detail}`));
|
|
112795
113115
|
await mongo.close();
|
|
112796
113116
|
return 1;
|
|
112797
113117
|
}
|
|
@@ -112817,38 +113137,38 @@ async function runState(opts) {
|
|
|
112817
113137
|
);
|
|
112818
113138
|
return 0;
|
|
112819
113139
|
}
|
|
112820
|
-
const discoveryNote = analysis.autoDiscovered ?
|
|
113140
|
+
const discoveryNote = analysis.autoDiscovered ? import_picocolors30.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
|
|
112821
113141
|
console.log(
|
|
112822
|
-
|
|
113142
|
+
import_picocolors30.default.bold("State analysis") + import_picocolors30.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
|
|
112823
113143
|
);
|
|
112824
113144
|
console.log("");
|
|
112825
113145
|
let signals = 0;
|
|
112826
113146
|
for (const c4 of analysis.collections) {
|
|
112827
113147
|
const flags2 = [];
|
|
112828
113148
|
if (c4.isStale === true) {
|
|
112829
|
-
flags2.push(
|
|
113149
|
+
flags2.push(import_picocolors30.default.yellow(`stale (last ${c4.dateField}, ~${Math.round(c4.ageHours ?? 0)}h)`));
|
|
112830
113150
|
}
|
|
112831
113151
|
for (const a2 of c4.anomalies) {
|
|
112832
|
-
flags2.push(
|
|
113152
|
+
flags2.push(import_picocolors30.default.red(`${a2.count}\xD7 "${a2.value}"`));
|
|
112833
113153
|
signals += 1;
|
|
112834
113154
|
}
|
|
112835
113155
|
if (c4.isStale === true) signals += 1;
|
|
112836
|
-
const head = ` ${
|
|
112837
|
-
console.log(flags2.length > 0 ? `${head} ${flags2.join(" ")}` :
|
|
113156
|
+
const head = ` ${import_picocolors30.default.bold(c4.collection.padEnd(18).slice(0, 18))} ${import_picocolors30.default.dim(String(c4.count).padStart(8) + " docs")}`;
|
|
113157
|
+
console.log(flags2.length > 0 ? `${head} ${flags2.join(" ")}` : import_picocolors30.default.dim(head));
|
|
112838
113158
|
}
|
|
112839
113159
|
console.log("");
|
|
112840
113160
|
if (analysis.collections.length === 0) {
|
|
112841
113161
|
console.log(
|
|
112842
|
-
|
|
113162
|
+
import_picocolors30.default.yellow(" No collections discovered in this database.") + import_picocolors30.default.dim(" Verify the database name in your MongoDB connector config.")
|
|
112843
113163
|
);
|
|
112844
113164
|
} else {
|
|
112845
113165
|
const scope = analysis.autoDiscovered ? "collection(s)" : "allowlisted collection(s)";
|
|
112846
113166
|
console.log(
|
|
112847
|
-
signals > 0 ? ` ${
|
|
113167
|
+
signals > 0 ? ` ${import_picocolors30.default.bold(String(signals))} state signal(s) across ${analysis.collections.length} ${scope}` : import_picocolors30.default.dim(` No state anomalies across ${analysis.collections.length} ${scope}.`)
|
|
112848
113168
|
);
|
|
112849
113169
|
if (analysis.autoDiscovered) {
|
|
112850
113170
|
console.log(
|
|
112851
|
-
|
|
113171
|
+
import_picocolors30.default.dim(
|
|
112852
113172
|
` Tip: add a "collections" list to your MongoDB connector config to restrict analysis.`
|
|
112853
113173
|
)
|
|
112854
113174
|
);
|
|
@@ -112866,7 +113186,7 @@ async function runState(opts) {
|
|
|
112866
113186
|
});
|
|
112867
113187
|
console.log("\n" + renderInterpretation(result));
|
|
112868
113188
|
if (!result.ok) {
|
|
112869
|
-
console.error(
|
|
113189
|
+
console.error(import_picocolors30.default.yellow(`[ai] ${result.warning}`));
|
|
112870
113190
|
}
|
|
112871
113191
|
}
|
|
112872
113192
|
return 0;
|
|
@@ -112874,20 +113194,20 @@ async function runState(opts) {
|
|
|
112874
113194
|
await mongo.close();
|
|
112875
113195
|
}
|
|
112876
113196
|
} catch (err2) {
|
|
112877
|
-
console.error(
|
|
113197
|
+
console.error(import_picocolors30.default.red(err2.message));
|
|
112878
113198
|
return 1;
|
|
112879
113199
|
}
|
|
112880
113200
|
}
|
|
112881
113201
|
|
|
112882
113202
|
// ../../packages/cli/src/commands/init.ts
|
|
112883
113203
|
init_cjs_shims();
|
|
112884
|
-
var
|
|
112885
|
-
var
|
|
113204
|
+
var import_picocolors31 = __toESM(require_picocolors(), 1);
|
|
113205
|
+
var import_node_path18 = require("path");
|
|
112886
113206
|
async function runInit(opts) {
|
|
112887
113207
|
try {
|
|
112888
113208
|
const cwd = process.cwd();
|
|
112889
|
-
const root = opts.path ? (0,
|
|
112890
|
-
const name2 = opts.name ?? (0,
|
|
113209
|
+
const root = opts.path ? (0, import_node_path18.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
|
|
113210
|
+
const name2 = opts.name ?? (0, import_node_path18.basename)(root);
|
|
112891
113211
|
const envName = opts.env ?? "production";
|
|
112892
113212
|
const repo = { name: name2, path: root };
|
|
112893
113213
|
if (opts.source) repo["source"] = { hostUrl: opts.source };
|
|
@@ -112902,65 +113222,65 @@ async function runInit(opts) {
|
|
|
112902
113222
|
const configPath = writeLocalConfig(root, file);
|
|
112903
113223
|
registerProject(name2, root, configPath);
|
|
112904
113224
|
ensureProjectGitignore(root);
|
|
112905
|
-
console.log(`${
|
|
112906
|
-
console.log(
|
|
112907
|
-
console.log(
|
|
113225
|
+
console.log(`${import_picocolors31.default.green("\u2713")} Initialized Horus project ${import_picocolors31.default.bold(name2)}`);
|
|
113226
|
+
console.log(import_picocolors31.default.dim(` config: ${configPath}`));
|
|
113227
|
+
console.log(import_picocolors31.default.dim(` registered: horus investigate --name ${name2} "<hint>"`));
|
|
112908
113228
|
if (!opts.source) {
|
|
112909
113229
|
console.log(
|
|
112910
|
-
|
|
113230
|
+
import_picocolors31.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
|
|
112911
113231
|
);
|
|
112912
113232
|
}
|
|
112913
113233
|
console.log(
|
|
112914
|
-
|
|
113234
|
+
import_picocolors31.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
|
|
112915
113235
|
);
|
|
112916
113236
|
console.log(
|
|
112917
|
-
|
|
113237
|
+
import_picocolors31.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
|
|
112918
113238
|
);
|
|
112919
113239
|
return 0;
|
|
112920
113240
|
} catch (err2) {
|
|
112921
|
-
console.error(
|
|
113241
|
+
console.error(import_picocolors31.default.red(err2.message));
|
|
112922
113242
|
return 1;
|
|
112923
113243
|
}
|
|
112924
113244
|
}
|
|
112925
113245
|
|
|
112926
113246
|
// ../../packages/cli/src/commands/projects.ts
|
|
112927
113247
|
init_cjs_shims();
|
|
112928
|
-
var
|
|
113248
|
+
var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
112929
113249
|
async function runProjects() {
|
|
112930
113250
|
const reg = readRegistry();
|
|
112931
113251
|
const names = Object.keys(reg.projects).sort();
|
|
112932
113252
|
if (names.length === 0) {
|
|
112933
113253
|
console.log(
|
|
112934
|
-
|
|
113254
|
+
import_picocolors32.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
|
|
112935
113255
|
);
|
|
112936
113256
|
return 0;
|
|
112937
113257
|
}
|
|
112938
|
-
console.log(
|
|
113258
|
+
console.log(import_picocolors32.default.bold("Registered Horus projects"));
|
|
112939
113259
|
console.log("");
|
|
112940
113260
|
for (const name2 of names) {
|
|
112941
113261
|
const entry2 = reg.projects[name2];
|
|
112942
113262
|
if (entry2 === void 0) continue;
|
|
112943
|
-
console.log(` ${
|
|
113263
|
+
console.log(` ${import_picocolors32.default.bold(name2.padEnd(22))} ${import_picocolors32.default.dim(entry2.root)}`);
|
|
112944
113264
|
}
|
|
112945
113265
|
console.log("");
|
|
112946
|
-
console.log(
|
|
113266
|
+
console.log(import_picocolors32.default.dim(' horus investigate --name <name> "<hint>"'));
|
|
112947
113267
|
return 0;
|
|
112948
113268
|
}
|
|
112949
113269
|
|
|
112950
113270
|
// ../../packages/cli/src/commands/setup.ts
|
|
112951
113271
|
init_cjs_shims();
|
|
112952
|
-
var
|
|
113272
|
+
var import_picocolors33 = __toESM(require_picocolors(), 1);
|
|
112953
113273
|
var DEFAULT_DB_URL3 = "postgresql://horus:horus@localhost:5433/horus";
|
|
112954
113274
|
async function runSetup(opts) {
|
|
112955
113275
|
const write = opts.write ?? ((line2) => console.log(line2));
|
|
112956
113276
|
let ok = true;
|
|
112957
|
-
write(
|
|
113277
|
+
write(import_picocolors33.default.bold("\nHorus setup\n"));
|
|
112958
113278
|
const backendVersion = await getSourceVersion();
|
|
112959
113279
|
if (backendVersion === null) {
|
|
112960
113280
|
ok = false;
|
|
112961
|
-
write(` ${
|
|
113281
|
+
write(` ${import_picocolors33.default.red("\u25CF")} Horus source-intelligence backend not found`);
|
|
112962
113282
|
write(
|
|
112963
|
-
|
|
113283
|
+
import_picocolors33.default.dim(
|
|
112964
113284
|
` install it (Python 3.11+ required):
|
|
112965
113285
|
curl -fsSL https://horus.sh/install.sh | bash
|
|
112966
113286
|
ensure ~/.local/bin is on your PATH`
|
|
@@ -112969,17 +113289,17 @@ async function runSetup(opts) {
|
|
|
112969
113289
|
} else if (backendVersion !== PINNED_SOURCE_VERSION) {
|
|
112970
113290
|
ok = false;
|
|
112971
113291
|
write(
|
|
112972
|
-
` ${
|
|
113292
|
+
` ${import_picocolors33.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors33.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
|
|
112973
113293
|
);
|
|
112974
113294
|
write(
|
|
112975
|
-
|
|
113295
|
+
import_picocolors33.default.dim(
|
|
112976
113296
|
` update it:
|
|
112977
113297
|
horus update`
|
|
112978
113298
|
)
|
|
112979
113299
|
);
|
|
112980
113300
|
} else {
|
|
112981
113301
|
write(
|
|
112982
|
-
` ${
|
|
113302
|
+
` ${import_picocolors33.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors33.default.dim(`(${backendVersion})`)
|
|
112983
113303
|
);
|
|
112984
113304
|
}
|
|
112985
113305
|
let dbUrl = process.env["DATABASE_URL"] ?? DEFAULT_DB_URL3;
|
|
@@ -112991,11 +113311,11 @@ async function runSetup(opts) {
|
|
|
112991
113311
|
}
|
|
112992
113312
|
const db = await checkDatabase(dbUrl);
|
|
112993
113313
|
if (db.reachable) {
|
|
112994
|
-
write(` ${
|
|
113314
|
+
write(` ${import_picocolors33.default.green("\u25CF")} Postgres reachable ${import_picocolors33.default.dim(`(${db.schemaDetail})`)}`);
|
|
112995
113315
|
if (!db.schemaReady) {
|
|
112996
113316
|
ok = false;
|
|
112997
113317
|
write(
|
|
112998
|
-
|
|
113318
|
+
import_picocolors33.default.dim(
|
|
112999
113319
|
` schema not applied \u2014 run migrations:
|
|
113000
113320
|
from the Horus repo: pnpm db migrate
|
|
113001
113321
|
or apply the schema directly: https://github.com/meritt-dev/horus/tree/master/packages/db/drizzle`
|
|
@@ -113004,9 +113324,9 @@ async function runSetup(opts) {
|
|
|
113004
113324
|
}
|
|
113005
113325
|
} else {
|
|
113006
113326
|
ok = false;
|
|
113007
|
-
write(` ${
|
|
113327
|
+
write(` ${import_picocolors33.default.red("\u25CF")} Postgres unreachable`);
|
|
113008
113328
|
write(
|
|
113009
|
-
|
|
113329
|
+
import_picocolors33.default.dim(
|
|
113010
113330
|
` start a local instance:
|
|
113011
113331
|
docker run -d --name horus-db \\
|
|
113012
113332
|
-e POSTGRES_USER=horus -e POSTGRES_PASSWORD=horus -e POSTGRES_DB=horus \\
|
|
@@ -113031,10 +113351,10 @@ async function runSetup(opts) {
|
|
|
113031
113351
|
if (!health.ok) {
|
|
113032
113352
|
ok = false;
|
|
113033
113353
|
write(
|
|
113034
|
-
` ${
|
|
113354
|
+
` ${import_picocolors33.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors33.default.bold(repo.name)} ` + import_picocolors33.default.dim(`(${repoHostUrl})`)
|
|
113035
113355
|
);
|
|
113036
113356
|
write(
|
|
113037
|
-
|
|
113357
|
+
import_picocolors33.default.dim(
|
|
113038
113358
|
` start the source intelligence host:
|
|
113039
113359
|
cd ${repo.path}
|
|
113040
113360
|
horus index`
|
|
@@ -113045,10 +113365,10 @@ async function runSetup(opts) {
|
|
|
113045
113365
|
if (count === 0) {
|
|
113046
113366
|
ok = false;
|
|
113047
113367
|
write(
|
|
113048
|
-
` ${
|
|
113368
|
+
` ${import_picocolors33.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors33.default.bold(repo.name)} is not indexed`
|
|
113049
113369
|
);
|
|
113050
113370
|
write(
|
|
113051
|
-
|
|
113371
|
+
import_picocolors33.default.dim(
|
|
113052
113372
|
` index the repo:
|
|
113053
113373
|
cd ${repo.path}
|
|
113054
113374
|
horus index`
|
|
@@ -113056,7 +113376,7 @@ async function runSetup(opts) {
|
|
|
113056
113376
|
);
|
|
113057
113377
|
} else {
|
|
113058
113378
|
write(
|
|
113059
|
-
` ${
|
|
113379
|
+
` ${import_picocolors33.default.green("\u25CF")} ${import_picocolors33.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors33.default.dim(`(${repoHostUrl})`)
|
|
113060
113380
|
);
|
|
113061
113381
|
}
|
|
113062
113382
|
}
|
|
@@ -113066,10 +113386,10 @@ async function runSetup(opts) {
|
|
|
113066
113386
|
write("");
|
|
113067
113387
|
if (ok) {
|
|
113068
113388
|
write(
|
|
113069
|
-
|
|
113389
|
+
import_picocolors33.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
|
|
113070
113390
|
);
|
|
113071
113391
|
} else {
|
|
113072
|
-
write(
|
|
113392
|
+
write(import_picocolors33.default.yellow("Resolve the items above, then re-run `horus setup`."));
|
|
113073
113393
|
}
|
|
113074
113394
|
return ok ? 0 : 1;
|
|
113075
113395
|
}
|
|
@@ -113077,13 +113397,13 @@ async function runSetup(opts) {
|
|
|
113077
113397
|
// ../../packages/cli/src/commands/connect.ts
|
|
113078
113398
|
init_cjs_shims();
|
|
113079
113399
|
var import_node_readline = require("readline");
|
|
113080
|
-
var
|
|
113081
|
-
var
|
|
113400
|
+
var import_node_child_process7 = require("child_process");
|
|
113401
|
+
var import_picocolors36 = __toESM(require_picocolors(), 1);
|
|
113082
113402
|
|
|
113083
113403
|
// ../../packages/cli/src/lib/tty-selector.ts
|
|
113084
113404
|
init_cjs_shims();
|
|
113085
113405
|
var import_node_tty = require("tty");
|
|
113086
|
-
var
|
|
113406
|
+
var import_picocolors34 = __toESM(require_picocolors(), 1);
|
|
113087
113407
|
var ExitPromptError = class extends Error {
|
|
113088
113408
|
constructor(message = "Prompt was cancelled") {
|
|
113089
113409
|
super(message);
|
|
@@ -113139,7 +113459,7 @@ async function checkboxSearch(opts) {
|
|
|
113139
113459
|
clear();
|
|
113140
113460
|
const lines = [];
|
|
113141
113461
|
lines.push(
|
|
113142
|
-
`? ${
|
|
113462
|
+
`? ${import_picocolors34.default.bold(message)} ${import_picocolors34.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
|
|
113143
113463
|
);
|
|
113144
113464
|
const visible = filtered.slice(visibleOffset, visibleOffset + pageSize);
|
|
113145
113465
|
for (let i3 = 0; i3 < visible.length; i3++) {
|
|
@@ -113147,29 +113467,29 @@ async function checkboxSearch(opts) {
|
|
|
113147
113467
|
const choice = visible[i3];
|
|
113148
113468
|
const isCursor = idx === cursor;
|
|
113149
113469
|
const isSelected = selected.has(choice);
|
|
113150
|
-
const marker = isSelected ?
|
|
113470
|
+
const marker = isSelected ? import_picocolors34.default.green("\u25CF") : " ";
|
|
113151
113471
|
const prefix = `[${marker}]`;
|
|
113152
|
-
const label = isCursor ?
|
|
113153
|
-
const pointer = isCursor ?
|
|
113472
|
+
const label = isCursor ? import_picocolors34.default.cyan(choice) : choice;
|
|
113473
|
+
const pointer = isCursor ? import_picocolors34.default.cyan("\u276F") : " ";
|
|
113154
113474
|
lines.push(` ${pointer} ${prefix} ${label}`);
|
|
113155
113475
|
}
|
|
113156
113476
|
if (filtered.length === 0) {
|
|
113157
|
-
lines.push(
|
|
113477
|
+
lines.push(import_picocolors34.default.dim(" No matches"));
|
|
113158
113478
|
}
|
|
113159
113479
|
const statusParts = [
|
|
113160
113480
|
`${selected.size} selected`,
|
|
113161
113481
|
`${filtered.length}/${choices.length} matches`
|
|
113162
113482
|
];
|
|
113163
113483
|
if (filter) statusParts.push(`filter: ${filter}`);
|
|
113164
|
-
lines.push(
|
|
113484
|
+
lines.push(import_picocolors34.default.dim(` ${statusParts.join(" \xB7 ")}`));
|
|
113165
113485
|
output.write(lines.join("\n"));
|
|
113166
113486
|
lastRenderLines = lines.length;
|
|
113167
113487
|
}
|
|
113168
113488
|
function finish(values2) {
|
|
113169
113489
|
cleanup();
|
|
113170
|
-
const summary = values2.length > 0 ? values2.join(", ") :
|
|
113490
|
+
const summary = values2.length > 0 ? values2.join(", ") : import_picocolors34.default.dim("none");
|
|
113171
113491
|
output.write(`
|
|
113172
|
-
? ${
|
|
113492
|
+
? ${import_picocolors34.default.bold(message)} ${summary}
|
|
113173
113493
|
`);
|
|
113174
113494
|
resolve9(values2);
|
|
113175
113495
|
}
|
|
@@ -113260,10 +113580,10 @@ async function checkboxSearch(opts) {
|
|
|
113260
113580
|
|
|
113261
113581
|
// ../../packages/cli/src/commands/connect-ai.ts
|
|
113262
113582
|
init_cjs_shims();
|
|
113263
|
-
var
|
|
113264
|
-
var
|
|
113583
|
+
var import_node_child_process6 = require("child_process");
|
|
113584
|
+
var import_picocolors35 = __toESM(require_picocolors(), 1);
|
|
113265
113585
|
function cliInstalled(id) {
|
|
113266
|
-
const r = (0,
|
|
113586
|
+
const r = (0, import_node_child_process6.spawnSync)(id, ["--version"], { stdio: "ignore", timeout: 2e3 });
|
|
113267
113587
|
return !r.error;
|
|
113268
113588
|
}
|
|
113269
113589
|
async function probeAnthropic(apiKey) {
|
|
@@ -113289,13 +113609,13 @@ async function runConnectAi(opts) {
|
|
|
113289
113609
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
113290
113610
|
const configPath = discoverLocalConfig(cwd) ?? localConfigPath(root);
|
|
113291
113611
|
const installed = LOCAL_PROVIDER_IDS.filter((id) => cliInstalled(id));
|
|
113292
|
-
console.log(
|
|
113612
|
+
console.log(import_picocolors35.default.bold("\nAI providers"));
|
|
113293
113613
|
console.log(
|
|
113294
|
-
|
|
113614
|
+
import_picocolors35.default.dim(
|
|
113295
113615
|
` Local CLIs on PATH: ${installed.length > 0 ? installed.join(", ") : "none detected"}`
|
|
113296
113616
|
)
|
|
113297
113617
|
);
|
|
113298
|
-
console.log(
|
|
113618
|
+
console.log(import_picocolors35.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
|
|
113299
113619
|
let provider = opts.provider;
|
|
113300
113620
|
if (provider === void 0) {
|
|
113301
113621
|
if (isInteractive()) {
|
|
@@ -113306,7 +113626,7 @@ async function runConnectAi(opts) {
|
|
|
113306
113626
|
}
|
|
113307
113627
|
}
|
|
113308
113628
|
if (!AI_PROVIDERS.includes(provider)) {
|
|
113309
|
-
console.error(
|
|
113629
|
+
console.error(import_picocolors35.default.red(`Unknown AI provider: ${provider}`) + import_picocolors35.default.dim(`
|
|
113310
113630
|
supported: ${AI_PROVIDERS.join(", ")}`));
|
|
113311
113631
|
return 1;
|
|
113312
113632
|
}
|
|
@@ -113319,7 +113639,7 @@ async function runConnectAi(opts) {
|
|
|
113319
113639
|
}
|
|
113320
113640
|
if (!apiKey) {
|
|
113321
113641
|
console.error(
|
|
113322
|
-
|
|
113642
|
+
import_picocolors35.default.red("No API key provided.") + import_picocolors35.default.dim("\n Pass --api-key sk-ant-\u2026 or set ANTHROPIC_API_KEY and re-run.")
|
|
113323
113643
|
);
|
|
113324
113644
|
return 1;
|
|
113325
113645
|
}
|
|
@@ -113327,28 +113647,28 @@ async function runConnectAi(opts) {
|
|
|
113327
113647
|
const probe2 = await probeAnthropic(apiKey);
|
|
113328
113648
|
if (!probe2.ok) {
|
|
113329
113649
|
console.error(`
|
|
113330
|
-
${
|
|
113331
|
-
console.error(
|
|
113650
|
+
${import_picocolors35.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
|
|
113651
|
+
console.error(import_picocolors35.default.dim(" Fix the key and retry, or pass --no-test to skip."));
|
|
113332
113652
|
return 1;
|
|
113333
113653
|
}
|
|
113334
113654
|
console.log(`
|
|
113335
|
-
${
|
|
113655
|
+
${import_picocolors35.default.green("\u2713")} anthropic ${import_picocolors35.default.dim(`(${probe2.detail})`)}`);
|
|
113336
113656
|
}
|
|
113337
113657
|
if (opts.model) aiBlock["anthropic"] = { model: opts.model };
|
|
113338
113658
|
storedKey = apiKey;
|
|
113339
113659
|
} else {
|
|
113340
113660
|
if (!installed.includes(provider)) {
|
|
113341
113661
|
console.error(
|
|
113342
|
-
|
|
113662
|
+
import_picocolors35.default.red(`${provider} CLI not found on PATH.`) + import_picocolors35.default.dim(`
|
|
113343
113663
|
Install it first, or choose anthropic.`)
|
|
113344
113664
|
);
|
|
113345
113665
|
return 1;
|
|
113346
113666
|
}
|
|
113347
113667
|
const desc2 = DEFAULT_LOCAL_PROVIDER_REGISTRY.get(provider);
|
|
113348
113668
|
console.log(`
|
|
113349
|
-
${
|
|
113669
|
+
${import_picocolors35.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors35.default.dim("detected on PATH")}`);
|
|
113350
113670
|
console.log(
|
|
113351
|
-
|
|
113671
|
+
import_picocolors35.default.dim(
|
|
113352
113672
|
" Note: `investigate --ai` currently uses the Anthropic API for narrative.\n Local-CLI narrative generation is recorded as your preference for future use."
|
|
113353
113673
|
)
|
|
113354
113674
|
);
|
|
@@ -113366,15 +113686,15 @@ ${import_picocolors34.default.green("\u2713")} ${desc2?.displayName ?? provider}
|
|
|
113366
113686
|
});
|
|
113367
113687
|
}
|
|
113368
113688
|
console.log(`
|
|
113369
|
-
${
|
|
113370
|
-
console.log(
|
|
113689
|
+
${import_picocolors35.default.green("\u2713")} ${import_picocolors35.default.bold("ai")} provider saved \u2192 ${import_picocolors35.default.dim(configPath)}`);
|
|
113690
|
+
console.log(import_picocolors35.default.dim(` provider: ${provider}`));
|
|
113371
113691
|
if (storedKey && secretsPathStr) {
|
|
113372
|
-
console.log(
|
|
113692
|
+
console.log(import_picocolors35.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
|
|
113373
113693
|
}
|
|
113374
|
-
console.log(
|
|
113694
|
+
console.log(import_picocolors35.default.dim(' run: horus investigate "<hint>" --ai'));
|
|
113375
113695
|
return 0;
|
|
113376
113696
|
} catch (err2) {
|
|
113377
|
-
console.error(
|
|
113697
|
+
console.error(import_picocolors35.default.red(err2 instanceof Error ? err2.message : String(err2)));
|
|
113378
113698
|
return 1;
|
|
113379
113699
|
}
|
|
113380
113700
|
}
|
|
@@ -113406,7 +113726,7 @@ async function runConnect(type, opts) {
|
|
|
113406
113726
|
}
|
|
113407
113727
|
if (!SUPPORTED.includes(type)) {
|
|
113408
113728
|
console.error(
|
|
113409
|
-
|
|
113729
|
+
import_picocolors36.default.red(`Unknown connector type: ${type}`) + import_picocolors36.default.dim(`
|
|
113410
113730
|
supported: ${SUPPORTED.join(", ")}, ai`)
|
|
113411
113731
|
);
|
|
113412
113732
|
return 1;
|
|
@@ -113443,20 +113763,20 @@ async function runConnect(type, opts) {
|
|
|
113443
113763
|
if (!probeResult.ok) {
|
|
113444
113764
|
console.error(
|
|
113445
113765
|
`
|
|
113446
|
-
${
|
|
113766
|
+
${import_picocolors36.default.red(`\u2717 Could not reach ${connectorType}:`)} ${probeResult.detail}` + import_picocolors36.default.dim("\n Fix the connection and retry, or pass --no-test to skip.")
|
|
113447
113767
|
);
|
|
113448
113768
|
return 1;
|
|
113449
113769
|
}
|
|
113450
113770
|
console.log(
|
|
113451
113771
|
`
|
|
113452
|
-
${
|
|
113772
|
+
${import_picocolors36.default.green("\u2713")} ${connectorType} reachable ${import_picocolors36.default.dim(`(${probeResult.detail})`)}`
|
|
113453
113773
|
);
|
|
113454
113774
|
}
|
|
113455
113775
|
const hasLiteralCredentials = filled.url !== void 0 || filled.password !== void 0 || filled.username !== void 0 || filled.authToken !== void 0;
|
|
113456
113776
|
if (hasLiteralCredentials) {
|
|
113457
113777
|
if (isGitTracked(configPath, root)) {
|
|
113458
113778
|
console.error(
|
|
113459
|
-
|
|
113779
|
+
import_picocolors36.default.red(".horus/config.json is already tracked by Git.") + "\nStoring credentials here would expose them in the repository.\n" + import_picocolors36.default.dim(
|
|
113460
113780
|
" Option A \u2014 remove from Git index then re-run:\n git rm --cached .horus/config.json\n horus connect " + type + " ...\n\n Option B \u2014 keep credentials in the environment and reference them:\n export ES_URL=https://...\n export ES_USERNAME=...\n export ES_PASSWORD=...\n Then edit .horus/config.json manually to use urlEnv/usernameEnv/passwordEnv."
|
|
113461
113781
|
)
|
|
113462
113782
|
);
|
|
@@ -113466,18 +113786,18 @@ ${import_picocolors35.default.green("\u2713")} ${connectorType} reachable ${impo
|
|
|
113466
113786
|
}
|
|
113467
113787
|
patchLocalConnector(configPath, connectorType, patch, filled.env);
|
|
113468
113788
|
console.log(
|
|
113469
|
-
`${
|
|
113789
|
+
`${import_picocolors36.default.green("\u2713")} ${import_picocolors36.default.bold(connectorType)} connector saved \u2192 ${import_picocolors36.default.dim(configPath)}`
|
|
113470
113790
|
);
|
|
113471
113791
|
printSummary(connectorType, filled);
|
|
113472
|
-
console.log(
|
|
113792
|
+
console.log(import_picocolors36.default.dim(`
|
|
113473
113793
|
run: horus investigate "<hint>"`));
|
|
113474
113794
|
return 0;
|
|
113475
113795
|
} catch (err2) {
|
|
113476
113796
|
if (err2 instanceof ExitPromptError) {
|
|
113477
|
-
console.error(
|
|
113797
|
+
console.error(import_picocolors36.default.red("Cancelled."));
|
|
113478
113798
|
return 1;
|
|
113479
113799
|
}
|
|
113480
|
-
console.error(
|
|
113800
|
+
console.error(import_picocolors36.default.red(err2.message));
|
|
113481
113801
|
return 1;
|
|
113482
113802
|
}
|
|
113483
113803
|
}
|
|
@@ -113486,7 +113806,7 @@ async function fillInteractive(type, opts) {
|
|
|
113486
113806
|
if (!needsInteraction) return opts;
|
|
113487
113807
|
console.log(
|
|
113488
113808
|
`
|
|
113489
|
-
${
|
|
113809
|
+
${import_picocolors36.default.bold(`Connect ${type}`)} ${import_picocolors36.default.dim("(press Enter to skip optional fields)")}
|
|
113490
113810
|
`
|
|
113491
113811
|
);
|
|
113492
113812
|
const filled = { ...opts };
|
|
@@ -113552,7 +113872,7 @@ ${import_picocolors35.default.bold(`Connect ${type}`)} ${import_picocolors35.def
|
|
|
113552
113872
|
break;
|
|
113553
113873
|
case "redis": {
|
|
113554
113874
|
console.log(
|
|
113555
|
-
|
|
113875
|
+
import_picocolors36.default.dim(
|
|
113556
113876
|
" Tip: embed credentials directly in the URL \u2014 redis://:password@host:6379\n or enter the URL and password separately below."
|
|
113557
113877
|
)
|
|
113558
113878
|
);
|
|
@@ -113594,8 +113914,8 @@ function missingRequired(type, opts) {
|
|
|
113594
113914
|
}
|
|
113595
113915
|
function ask(label, placeholder = "", required2 = true) {
|
|
113596
113916
|
return new Promise((resolve9) => {
|
|
113597
|
-
const hint = placeholder ?
|
|
113598
|
-
const suffix = required2 ? "" :
|
|
113917
|
+
const hint = placeholder ? import_picocolors36.default.dim(` (${placeholder})`) : "";
|
|
113918
|
+
const suffix = required2 ? "" : import_picocolors36.default.dim(" [optional]");
|
|
113599
113919
|
process.stdout.write(` ${label}${suffix}${hint}: `);
|
|
113600
113920
|
const rl = (0, import_node_readline.createInterface)({
|
|
113601
113921
|
input: process.stdin,
|
|
@@ -113612,7 +113932,7 @@ function askPassword(label) {
|
|
|
113612
113932
|
return new Promise((resolve9) => {
|
|
113613
113933
|
const stdin = process.stdin;
|
|
113614
113934
|
if (typeof stdin.setRawMode === "function") {
|
|
113615
|
-
process.stdout.write(` ${label}${
|
|
113935
|
+
process.stdout.write(` ${label}${import_picocolors36.default.dim(" [optional]")}: `);
|
|
113616
113936
|
stdin.setRawMode(true);
|
|
113617
113937
|
stdin.resume();
|
|
113618
113938
|
let value = "";
|
|
@@ -113729,15 +114049,15 @@ function describeProbe(p5) {
|
|
|
113729
114049
|
return `${p5.keyCount} keys \xB7 ${p5.suggestedRoles.join("/")}${examples ? ` \xB7 ${examples}` : ""}`;
|
|
113730
114050
|
}
|
|
113731
114051
|
async function discoverAndSelectDbs(url, bullmqPrefix) {
|
|
113732
|
-
console.log(
|
|
114052
|
+
console.log(import_picocolors36.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
|
|
113733
114053
|
const probes = await probeRedisDatabases(url, { bullmqPrefix });
|
|
113734
114054
|
const nonEmpty = probes.filter((p5) => p5.reachable && p5.keyCount > 0);
|
|
113735
114055
|
if (nonEmpty.length === 0) {
|
|
113736
|
-
console.log(
|
|
114056
|
+
console.log(import_picocolors36.default.dim(" No populated DBs found."));
|
|
113737
114057
|
return [];
|
|
113738
114058
|
}
|
|
113739
114059
|
for (const p5 of nonEmpty) {
|
|
113740
|
-
console.log(` ${
|
|
114060
|
+
console.log(` ${import_picocolors36.default.bold(`DB ${p5.db}`)} ${import_picocolors36.default.dim("\xB7 " + describeProbe(p5))}`);
|
|
113741
114061
|
}
|
|
113742
114062
|
const byLabel = /* @__PURE__ */ new Map();
|
|
113743
114063
|
const choices = nonEmpty.map((p5) => {
|
|
@@ -113877,11 +114197,11 @@ function printSummary(type, opts) {
|
|
|
113877
114197
|
} else if (opts.dashboard) {
|
|
113878
114198
|
lines.push(` dashboard: ${opts.dashboard}`);
|
|
113879
114199
|
}
|
|
113880
|
-
if (lines.length > 0) console.log(
|
|
114200
|
+
if (lines.length > 0) console.log(import_picocolors36.default.dim(lines.join("\n")));
|
|
113881
114201
|
}
|
|
113882
114202
|
function isGitTracked(filePath, cwd) {
|
|
113883
114203
|
try {
|
|
113884
|
-
(0,
|
|
114204
|
+
(0, import_node_child_process7.execFileSync)("git", ["ls-files", "--error-unmatch", filePath], {
|
|
113885
114205
|
cwd,
|
|
113886
114206
|
stdio: "pipe"
|
|
113887
114207
|
});
|
|
@@ -113916,11 +114236,11 @@ async function askIndexSelection(indices) {
|
|
|
113916
114236
|
const shown = indices.slice(0, MAX_DISPLAY);
|
|
113917
114237
|
console.log("\n Available Elasticsearch indexes/data streams:");
|
|
113918
114238
|
shown.forEach((name2, i3) => {
|
|
113919
|
-
console.log(` ${
|
|
114239
|
+
console.log(` ${import_picocolors36.default.dim(`[${i3 + 1}]`)} ${name2}`);
|
|
113920
114240
|
});
|
|
113921
114241
|
if (indices.length > MAX_DISPLAY) {
|
|
113922
114242
|
console.log(
|
|
113923
|
-
|
|
114243
|
+
import_picocolors36.default.dim(
|
|
113924
114244
|
` \u2026 and ${indices.length - MAX_DISPLAY} more (type a pattern manually to match all)`
|
|
113925
114245
|
)
|
|
113926
114246
|
);
|
|
@@ -113964,11 +114284,11 @@ async function askDashboardSelection(dashboards) {
|
|
|
113964
114284
|
const shown = dashboards.slice(0, MAX_DISPLAY);
|
|
113965
114285
|
console.log("\n Available Grafana dashboards:");
|
|
113966
114286
|
shown.forEach((d3, i3) => {
|
|
113967
|
-
const folder = d3.folderTitle ?
|
|
113968
|
-
console.log(` ${
|
|
114287
|
+
const folder = d3.folderTitle ? import_picocolors36.default.dim(` (${d3.folderTitle})`) : "";
|
|
114288
|
+
console.log(` ${import_picocolors36.default.dim(`[${i3 + 1}]`)} ${d3.title}${folder}`);
|
|
113969
114289
|
});
|
|
113970
114290
|
if (dashboards.length > MAX_DISPLAY) {
|
|
113971
|
-
console.log(
|
|
114291
|
+
console.log(import_picocolors36.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
|
|
113972
114292
|
}
|
|
113973
114293
|
const input = (await ask(
|
|
113974
114294
|
` Select dashboards to use (e.g. 1,2 or Enter to type uid manually)`,
|
|
@@ -114018,16 +114338,17 @@ function redactUrl(raw) {
|
|
|
114018
114338
|
|
|
114019
114339
|
// ../../packages/cli/src/commands/stop.ts
|
|
114020
114340
|
init_cjs_shims();
|
|
114021
|
-
var
|
|
114022
|
-
var
|
|
114023
|
-
var
|
|
114024
|
-
var
|
|
114025
|
-
var
|
|
114026
|
-
var
|
|
114027
|
-
var unlinkAsync = (0,
|
|
114341
|
+
var import_node_child_process8 = require("child_process");
|
|
114342
|
+
var import_node_fs17 = require("fs");
|
|
114343
|
+
var import_node_util5 = require("util");
|
|
114344
|
+
var import_node_path19 = require("path");
|
|
114345
|
+
var import_picocolors37 = __toESM(require_picocolors(), 1);
|
|
114346
|
+
var execFileAsync2 = (0, import_node_util5.promisify)(import_node_child_process8.execFile);
|
|
114347
|
+
var unlinkAsync = (0, import_node_util5.promisify)(import_node_fs17.unlink);
|
|
114028
114348
|
var SPAWNED_HOST_FILE2 = "spawned-host.json";
|
|
114029
114349
|
var START_TIME_TOLERANCE_S = 60;
|
|
114030
114350
|
var STOP_WAIT_MS = 5e3;
|
|
114351
|
+
var STOP_KILL_WAIT_MS = 3e3;
|
|
114031
114352
|
var STOP_POLL_MS = 200;
|
|
114032
114353
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
114033
114354
|
async function runStop(opts) {
|
|
@@ -114039,19 +114360,19 @@ async function runStop(opts) {
|
|
|
114039
114360
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
114040
114361
|
const hostUrl = readSourceHostUrl(root);
|
|
114041
114362
|
if (!hostUrl) {
|
|
114042
|
-
console.log(
|
|
114363
|
+
console.log(import_picocolors37.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
|
|
114043
114364
|
return 0;
|
|
114044
114365
|
}
|
|
114045
114366
|
return await stopHost(root, hostUrl);
|
|
114046
114367
|
} catch (err2) {
|
|
114047
|
-
console.error(
|
|
114368
|
+
console.error(import_picocolors37.default.red(err2.message));
|
|
114048
114369
|
return 1;
|
|
114049
114370
|
}
|
|
114050
114371
|
}
|
|
114051
114372
|
async function stopHost(root, hostUrl) {
|
|
114052
114373
|
const port = extractPort(hostUrl);
|
|
114053
114374
|
if (port === null) {
|
|
114054
|
-
console.error(
|
|
114375
|
+
console.error(import_picocolors37.default.red(`Cannot determine port from host URL: ${hostUrl}`));
|
|
114055
114376
|
return 1;
|
|
114056
114377
|
}
|
|
114057
114378
|
const healthy = await isHostHealthy(hostUrl);
|
|
@@ -114061,12 +114382,12 @@ async function stopHost(root, hostUrl) {
|
|
|
114061
114382
|
if (!wrapperAlive) {
|
|
114062
114383
|
const res = await stopBackendServerPid(root, port);
|
|
114063
114384
|
if (res === "failed") return 1;
|
|
114064
|
-
if (res !== "stopped") console.log(
|
|
114385
|
+
if (res !== "stopped") console.log(import_picocolors37.default.dim(`Host ${hostUrl} is already stopped.`));
|
|
114065
114386
|
await cleanupSpawnedRecord(root);
|
|
114066
114387
|
return 0;
|
|
114067
114388
|
}
|
|
114068
114389
|
console.log(
|
|
114069
|
-
|
|
114390
|
+
import_picocolors37.default.dim(
|
|
114070
114391
|
`Host ${hostUrl} is unreachable but pid ${owned.pid} is still running \u2014 terminating a stuck host.`
|
|
114071
114392
|
)
|
|
114072
114393
|
);
|
|
@@ -114074,7 +114395,7 @@ async function stopHost(root, hostUrl) {
|
|
|
114074
114395
|
const spawned = readSpawnedHost(root);
|
|
114075
114396
|
if (spawned === null) {
|
|
114076
114397
|
console.error(
|
|
114077
|
-
|
|
114398
|
+
import_picocolors37.default.red(
|
|
114078
114399
|
`No ownership record found (.horus/${SPAWNED_HOST_FILE2} absent). Horus will not stop a host it did not spawn.`
|
|
114079
114400
|
)
|
|
114080
114401
|
);
|
|
@@ -114082,12 +114403,12 @@ async function stopHost(root, hostUrl) {
|
|
|
114082
114403
|
}
|
|
114083
114404
|
const recordError = validateSpawnedRecord(spawned);
|
|
114084
114405
|
if (recordError !== null) {
|
|
114085
|
-
console.error(
|
|
114406
|
+
console.error(import_picocolors37.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
|
|
114086
114407
|
return 1;
|
|
114087
114408
|
}
|
|
114088
114409
|
if (spawned.port !== port) {
|
|
114089
114410
|
console.error(
|
|
114090
|
-
|
|
114411
|
+
import_picocolors37.default.red(
|
|
114091
114412
|
`Ownership record port (${spawned.port}) does not match host URL port (${port}). Record may be stale.`
|
|
114092
114413
|
)
|
|
114093
114414
|
);
|
|
@@ -114095,7 +114416,7 @@ async function stopHost(root, hostUrl) {
|
|
|
114095
114416
|
}
|
|
114096
114417
|
if (spawned.root !== root) {
|
|
114097
114418
|
console.error(
|
|
114098
|
-
|
|
114419
|
+
import_picocolors37.default.red(
|
|
114099
114420
|
`Ownership record root (${spawned.root}) does not match resolved root (${root}). Record may be stale.`
|
|
114100
114421
|
)
|
|
114101
114422
|
);
|
|
@@ -114106,14 +114427,14 @@ async function stopHost(root, hostUrl) {
|
|
|
114106
114427
|
const res = await stopBackendServerPid(root, port);
|
|
114107
114428
|
if (res === "failed") return 1;
|
|
114108
114429
|
if (res !== "stopped") {
|
|
114109
|
-
console.log(
|
|
114430
|
+
console.log(import_picocolors37.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
|
|
114110
114431
|
}
|
|
114111
114432
|
await cleanupSpawnedRecord(root);
|
|
114112
114433
|
return 0;
|
|
114113
114434
|
}
|
|
114114
114435
|
if (!hostArgvRegex(port).test(info2.args)) {
|
|
114115
114436
|
console.error(
|
|
114116
|
-
|
|
114437
|
+
import_picocolors37.default.red(
|
|
114117
114438
|
`Pid ${spawned.pid} args do not match "horus-source host --port ${port}". Got: "${info2.args.slice(0, 120)}". Aborting for safety.`
|
|
114118
114439
|
)
|
|
114119
114440
|
);
|
|
@@ -114122,51 +114443,33 @@ async function stopHost(root, hostUrl) {
|
|
|
114122
114443
|
const startTs = new Date(spawned.startedAt).getTime();
|
|
114123
114444
|
const recordedAgeS = Math.round((Date.now() - startTs) / 1e3);
|
|
114124
114445
|
if (!Number.isFinite(info2.etimeSeconds)) {
|
|
114125
|
-
console.error(
|
|
114446
|
+
console.error(import_picocolors37.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
|
|
114126
114447
|
return 1;
|
|
114127
114448
|
}
|
|
114128
114449
|
if (Math.abs(info2.etimeSeconds - recordedAgeS) > START_TIME_TOLERANCE_S) {
|
|
114129
114450
|
console.error(
|
|
114130
|
-
|
|
114451
|
+
import_picocolors37.default.red(
|
|
114131
114452
|
`Pid ${spawned.pid} age mismatch: record says ~${recordedAgeS}s, process reports ${info2.etimeSeconds}s elapsed. Possible PID reuse \u2014 aborting for safety.`
|
|
114132
114453
|
)
|
|
114133
114454
|
);
|
|
114134
114455
|
return 1;
|
|
114135
114456
|
}
|
|
114136
|
-
let
|
|
114457
|
+
let terminated;
|
|
114137
114458
|
try {
|
|
114138
|
-
|
|
114139
|
-
signaled = true;
|
|
114459
|
+
terminated = await terminatePid(spawned.pid);
|
|
114140
114460
|
} catch (err2) {
|
|
114141
|
-
|
|
114142
|
-
|
|
114143
|
-
} else {
|
|
114144
|
-
console.error(import_picocolors36.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
|
|
114145
|
-
return 1;
|
|
114146
|
-
}
|
|
114461
|
+
console.error(import_picocolors37.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
|
|
114462
|
+
return 1;
|
|
114147
114463
|
}
|
|
114148
|
-
if (
|
|
114149
|
-
|
|
114150
|
-
|
|
114151
|
-
while (Date.now() < deadline) {
|
|
114152
|
-
await sleep(STOP_POLL_MS);
|
|
114153
|
-
if (await getProcessInfo(spawned.pid) === null) {
|
|
114154
|
-
exited = true;
|
|
114155
|
-
break;
|
|
114156
|
-
}
|
|
114157
|
-
}
|
|
114158
|
-
if (!exited) {
|
|
114159
|
-
console.error(
|
|
114160
|
-
import_picocolors36.default.red(
|
|
114161
|
-
`Host pid ${spawned.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`
|
|
114162
|
-
)
|
|
114163
|
-
);
|
|
114164
|
-
return 1;
|
|
114165
|
-
}
|
|
114166
|
-
console.log(
|
|
114167
|
-
`${import_picocolors36.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors36.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
|
|
114464
|
+
if (!terminated) {
|
|
114465
|
+
console.error(
|
|
114466
|
+
import_picocolors37.default.red(`Host pid ${spawned.pid} did not exit even after SIGKILL \u2014 could not reclaim the port.`)
|
|
114168
114467
|
);
|
|
114468
|
+
return 1;
|
|
114169
114469
|
}
|
|
114470
|
+
console.log(
|
|
114471
|
+
`${import_picocolors37.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors37.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
|
|
114472
|
+
);
|
|
114170
114473
|
if (await stopBackendServerPid(root, port) === "failed") return 1;
|
|
114171
114474
|
await cleanupSpawnedRecord(root);
|
|
114172
114475
|
return 0;
|
|
@@ -114178,7 +114481,7 @@ function hostArgvRegex(port) {
|
|
|
114178
114481
|
}
|
|
114179
114482
|
async function cleanupSpawnedRecord(root) {
|
|
114180
114483
|
try {
|
|
114181
|
-
await unlinkAsync((0,
|
|
114484
|
+
await unlinkAsync((0, import_node_path19.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
|
|
114182
114485
|
} catch {
|
|
114183
114486
|
}
|
|
114184
114487
|
}
|
|
@@ -114191,39 +114494,35 @@ async function stopBackendServerPid(root, port) {
|
|
|
114191
114494
|
if (info2 === null) return "none";
|
|
114192
114495
|
if (!hostArgvRegex(port).test(info2.args)) {
|
|
114193
114496
|
console.error(
|
|
114194
|
-
|
|
114497
|
+
import_picocolors37.default.red(
|
|
114195
114498
|
`Backend host pid ${rec.pid} args do not match "horus-source host --port ${port}". Not signalling \u2014 possible stale source/host.json or PID reuse.`
|
|
114196
114499
|
)
|
|
114197
114500
|
);
|
|
114198
114501
|
return "failed";
|
|
114199
114502
|
}
|
|
114503
|
+
let terminated;
|
|
114200
114504
|
try {
|
|
114201
|
-
|
|
114505
|
+
terminated = await terminatePid(rec.pid);
|
|
114202
114506
|
} catch (err2) {
|
|
114203
|
-
|
|
114204
|
-
console.error(import_picocolors36.default.red(`Failed to signal pid ${rec.pid}: ${err2.message}`));
|
|
114507
|
+
console.error(import_picocolors37.default.red(`Failed to signal pid ${rec.pid}: ${err2.message}`));
|
|
114205
114508
|
return "failed";
|
|
114206
114509
|
}
|
|
114207
|
-
|
|
114208
|
-
|
|
114209
|
-
|
|
114210
|
-
|
|
114211
|
-
|
|
114212
|
-
`${import_picocolors36.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors36.default.dim(`(pid ${rec.pid}, port ${port})`) + ` for ${root}`
|
|
114213
|
-
);
|
|
114214
|
-
return "stopped";
|
|
114215
|
-
}
|
|
114510
|
+
if (!terminated) {
|
|
114511
|
+
console.error(
|
|
114512
|
+
import_picocolors37.default.red(`Host pid ${rec.pid} did not exit even after SIGKILL \u2014 could not reclaim the port.`)
|
|
114513
|
+
);
|
|
114514
|
+
return "failed";
|
|
114216
114515
|
}
|
|
114217
|
-
console.
|
|
114218
|
-
|
|
114516
|
+
console.log(
|
|
114517
|
+
`${import_picocolors37.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors37.default.dim(`(pid ${rec.pid}, port ${port})`) + ` for ${root}`
|
|
114219
114518
|
);
|
|
114220
|
-
return "
|
|
114519
|
+
return "stopped";
|
|
114221
114520
|
}
|
|
114222
114521
|
async function stopAll() {
|
|
114223
114522
|
const registry2 = readRegistry();
|
|
114224
114523
|
const projects2 = Object.entries(registry2.projects);
|
|
114225
114524
|
if (projects2.length === 0) {
|
|
114226
|
-
console.log(
|
|
114525
|
+
console.log(import_picocolors37.default.dim("No registered projects."));
|
|
114227
114526
|
return 0;
|
|
114228
114527
|
}
|
|
114229
114528
|
let stopped = 0;
|
|
@@ -114235,17 +114534,17 @@ async function stopAll() {
|
|
|
114235
114534
|
if (!healthy && readSpawnedHost(entry2.root) === null && readSourceHostPid(entry2.root) === null) {
|
|
114236
114535
|
continue;
|
|
114237
114536
|
}
|
|
114238
|
-
console.log(` Stopping ${
|
|
114537
|
+
console.log(` Stopping ${import_picocolors37.default.bold(name2)} ${import_picocolors37.default.dim(`(${hostUrl})`)}`);
|
|
114239
114538
|
const code = await stopHost(entry2.root, hostUrl);
|
|
114240
114539
|
if (code === 0) stopped++;
|
|
114241
114540
|
else failed++;
|
|
114242
114541
|
}
|
|
114243
114542
|
if (stopped === 0 && failed === 0) {
|
|
114244
|
-
console.log(
|
|
114543
|
+
console.log(import_picocolors37.default.dim("No running source-intelligence hosts found."));
|
|
114245
114544
|
} else {
|
|
114246
114545
|
console.log(
|
|
114247
114546
|
`
|
|
114248
|
-
Stopped ${stopped} host(s)${failed > 0 ?
|
|
114547
|
+
Stopped ${stopped} host(s)${failed > 0 ? import_picocolors37.default.red(`, ${failed} failed`) : ""}.`
|
|
114249
114548
|
);
|
|
114250
114549
|
}
|
|
114251
114550
|
return failed > 0 ? 1 : 0;
|
|
@@ -114272,10 +114571,30 @@ function validateSpawnedRecord(r) {
|
|
|
114272
114571
|
}
|
|
114273
114572
|
return null;
|
|
114274
114573
|
}
|
|
114574
|
+
async function terminatePid(pid) {
|
|
114575
|
+
const escalation = [
|
|
114576
|
+
{ signal: "SIGTERM", waitMs: STOP_WAIT_MS },
|
|
114577
|
+
{ signal: "SIGKILL", waitMs: STOP_KILL_WAIT_MS }
|
|
114578
|
+
];
|
|
114579
|
+
for (const { signal, waitMs } of escalation) {
|
|
114580
|
+
try {
|
|
114581
|
+
process.kill(pid, signal);
|
|
114582
|
+
} catch (err2) {
|
|
114583
|
+
if (err2.code === "ESRCH") return true;
|
|
114584
|
+
throw err2;
|
|
114585
|
+
}
|
|
114586
|
+
const deadline = Date.now() + waitMs;
|
|
114587
|
+
while (Date.now() < deadline) {
|
|
114588
|
+
await sleep(STOP_POLL_MS);
|
|
114589
|
+
if (await getProcessInfo(pid) === null) return true;
|
|
114590
|
+
}
|
|
114591
|
+
}
|
|
114592
|
+
return await getProcessInfo(pid) === null;
|
|
114593
|
+
}
|
|
114275
114594
|
async function getProcessInfo(pid) {
|
|
114276
114595
|
let args2;
|
|
114277
114596
|
try {
|
|
114278
|
-
const argsResult = await
|
|
114597
|
+
const argsResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "args="], {
|
|
114279
114598
|
timeout: 3e3
|
|
114280
114599
|
});
|
|
114281
114600
|
args2 = argsResult.stdout.trim();
|
|
@@ -114285,7 +114604,7 @@ async function getProcessInfo(pid) {
|
|
|
114285
114604
|
if (!args2) return null;
|
|
114286
114605
|
let etimeSeconds = Number.NaN;
|
|
114287
114606
|
try {
|
|
114288
|
-
const etimeResult = await
|
|
114607
|
+
const etimeResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "etime="], {
|
|
114289
114608
|
timeout: 3e3
|
|
114290
114609
|
});
|
|
114291
114610
|
etimeSeconds = parseEtimeSeconds(etimeResult.stdout.trim());
|
|
@@ -114313,14 +114632,77 @@ function extractPort(hostUrl) {
|
|
|
114313
114632
|
|
|
114314
114633
|
// ../../packages/cli/src/commands/hosts.ts
|
|
114315
114634
|
init_cjs_shims();
|
|
114316
|
-
var
|
|
114317
|
-
|
|
114635
|
+
var import_picocolors38 = __toESM(require_picocolors(), 1);
|
|
114636
|
+
|
|
114637
|
+
// ../../packages/cli/src/lib/host-reaper.ts
|
|
114638
|
+
init_cjs_shims();
|
|
114639
|
+
var import_node_child_process9 = require("child_process");
|
|
114640
|
+
var import_node_util6 = require("util");
|
|
114641
|
+
var execFileAsync3 = (0, import_node_util6.promisify)(import_node_child_process9.execFile);
|
|
114642
|
+
var HOST_ARGV = /(?:^|\s)(?:\S*\/)?horus-source\s+host\s+--port(?:=|\s+)(\d+)(?=\s|$)/;
|
|
114643
|
+
function parseSourceHosts(psStdout) {
|
|
114644
|
+
const hosts = [];
|
|
114645
|
+
for (const line2 of psStdout.split("\n")) {
|
|
114646
|
+
const trimmed = line2.trim();
|
|
114647
|
+
if (!trimmed) continue;
|
|
114648
|
+
const sp = trimmed.indexOf(" ");
|
|
114649
|
+
if (sp < 0) continue;
|
|
114650
|
+
const pid = Number(trimmed.slice(0, sp));
|
|
114651
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
114652
|
+
const m5 = HOST_ARGV.exec(trimmed.slice(sp + 1));
|
|
114653
|
+
if (!m5) continue;
|
|
114654
|
+
hosts.push({ pid, port: Number(m5[1]) });
|
|
114655
|
+
}
|
|
114656
|
+
return hosts;
|
|
114657
|
+
}
|
|
114658
|
+
function selectOrphans(running, claimedPids) {
|
|
114659
|
+
return running.filter((h3) => !claimedPids.has(h3.pid));
|
|
114660
|
+
}
|
|
114661
|
+
async function listRunningSourceHosts() {
|
|
114662
|
+
try {
|
|
114663
|
+
const { stdout } = await execFileAsync3("ps", ["-axww", "-o", "pid=,args="], {
|
|
114664
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
114665
|
+
timeout: 5e3
|
|
114666
|
+
});
|
|
114667
|
+
return parseSourceHosts(stdout);
|
|
114668
|
+
} catch {
|
|
114669
|
+
return [];
|
|
114670
|
+
}
|
|
114671
|
+
}
|
|
114672
|
+
function isAlive(pid) {
|
|
114673
|
+
try {
|
|
114674
|
+
process.kill(pid, 0);
|
|
114675
|
+
return true;
|
|
114676
|
+
} catch (err2) {
|
|
114677
|
+
return err2.code === "EPERM";
|
|
114678
|
+
}
|
|
114679
|
+
}
|
|
114680
|
+
async function reapPid(pid, opts = {}) {
|
|
114681
|
+
const termMs = opts.termMs ?? 4e3;
|
|
114682
|
+
const killMs = opts.killMs ?? 3e3;
|
|
114683
|
+
const pollMs = opts.pollMs ?? 200;
|
|
114684
|
+
for (const [signal, wait] of [
|
|
114685
|
+
["SIGTERM", termMs],
|
|
114686
|
+
["SIGKILL", killMs]
|
|
114687
|
+
]) {
|
|
114688
|
+
try {
|
|
114689
|
+
process.kill(pid, signal);
|
|
114690
|
+
} catch (err2) {
|
|
114691
|
+
if (err2.code === "ESRCH") return true;
|
|
114692
|
+
}
|
|
114693
|
+
const deadline = Date.now() + wait;
|
|
114694
|
+
while (Date.now() < deadline) {
|
|
114695
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
114696
|
+
if (!isAlive(pid)) return true;
|
|
114697
|
+
}
|
|
114698
|
+
}
|
|
114699
|
+
return !isAlive(pid);
|
|
114700
|
+
}
|
|
114701
|
+
|
|
114702
|
+
// ../../packages/cli/src/commands/hosts.ts
|
|
114703
|
+
async function runHosts(opts = {}) {
|
|
114318
114704
|
const registry2 = readRegistry();
|
|
114319
114705
|
const projects2 = Object.entries(registry2.projects);
|
|
114320
|
-
if (projects2.length === 0) {
|
|
114321
|
-
console.log(import_picocolors37.default.dim("No registered projects. Run `horus index` in a repo first."));
|
|
114322
|
-
return 0;
|
|
114323
|
-
}
|
|
114324
114706
|
const rows = [];
|
|
114325
114707
|
await Promise.all(
|
|
114326
114708
|
projects2.map(async ([name2, entry2]) => {
|
|
@@ -114329,39 +114711,69 @@ async function runHosts() {
|
|
|
114329
114711
|
rows.push({ name: name2, hostUrl: hostUrl ?? null, healthy, root: entry2.root });
|
|
114330
114712
|
})
|
|
114331
114713
|
);
|
|
114332
|
-
rows.sort(
|
|
114333
|
-
|
|
114334
|
-
|
|
114335
|
-
});
|
|
114336
|
-
const anyHost = rows.some((r) => r.hostUrl !== null);
|
|
114337
|
-
if (!anyHost) {
|
|
114338
|
-
console.log(import_picocolors37.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
|
|
114339
|
-
return 0;
|
|
114340
|
-
}
|
|
114714
|
+
rows.sort(
|
|
114715
|
+
(a2, b5) => a2.healthy !== b5.healthy ? a2.healthy ? -1 : 1 : a2.name.localeCompare(b5.name)
|
|
114716
|
+
);
|
|
114341
114717
|
console.log("");
|
|
114718
|
+
let printedAny = false;
|
|
114342
114719
|
for (const row of rows) {
|
|
114343
114720
|
if (row.hostUrl === null) continue;
|
|
114344
|
-
|
|
114721
|
+
printedAny = true;
|
|
114722
|
+
const status = row.healthy ? import_picocolors38.default.green("\u25CF running") : import_picocolors38.default.red("\u25CF stopped");
|
|
114345
114723
|
const port = extractPort2(row.hostUrl) ?? "?";
|
|
114346
114724
|
console.log(
|
|
114347
|
-
` ${status} ${
|
|
114725
|
+
` ${status} ${import_picocolors38.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors38.default.dim(row.root)}`
|
|
114348
114726
|
);
|
|
114349
114727
|
}
|
|
114350
|
-
console.log("");
|
|
114351
114728
|
const noHost = rows.filter((r) => r.hostUrl === null);
|
|
114352
|
-
|
|
114353
|
-
|
|
114354
|
-
|
|
114729
|
+
for (const row of noHost) console.log(` ${import_picocolors38.default.dim("\u25CB no host")} ${import_picocolors38.default.dim(row.name)}`);
|
|
114730
|
+
if (printedAny || noHost.length > 0) console.log("");
|
|
114731
|
+
const claimedPids = /* @__PURE__ */ new Set();
|
|
114732
|
+
for (const [, entry2] of projects2) {
|
|
114733
|
+
const hp = readSourceHostPid(entry2.root);
|
|
114734
|
+
if (hp?.pid) claimedPids.add(hp.pid);
|
|
114735
|
+
const sp = readSpawnedHost(entry2.root);
|
|
114736
|
+
if (sp?.pid) claimedPids.add(sp.pid);
|
|
114737
|
+
}
|
|
114738
|
+
const orphans = selectOrphans(await listRunningSourceHosts(), claimedPids);
|
|
114739
|
+
let reapFailed = 0;
|
|
114740
|
+
if (orphans.length > 0) {
|
|
114741
|
+
console.log(
|
|
114742
|
+
import_picocolors38.default.yellow(` \u26A0 ${orphans.length} orphaned source host(s) \u2014 no registered repo owns them:`)
|
|
114743
|
+
);
|
|
114744
|
+
for (const o5 of orphans) console.log(import_picocolors38.default.dim(` pid ${o5.pid} port ${o5.port}`));
|
|
114745
|
+
if (opts.reap) {
|
|
114746
|
+
let reaped = 0;
|
|
114747
|
+
for (const o5 of orphans) {
|
|
114748
|
+
if (await reapPid(o5.pid)) {
|
|
114749
|
+
reaped++;
|
|
114750
|
+
console.log(` ${import_picocolors38.default.green("\u2713")} reaped pid ${o5.pid} ${import_picocolors38.default.dim(`(port ${o5.port})`)}`);
|
|
114751
|
+
} else {
|
|
114752
|
+
reapFailed++;
|
|
114753
|
+
console.log(import_picocolors38.default.red(` \u2717 could not stop pid ${o5.pid} (port ${o5.port})`));
|
|
114754
|
+
}
|
|
114755
|
+
}
|
|
114756
|
+
console.log(import_picocolors38.default.dim(` Reaped ${reaped}/${orphans.length} orphan(s).`));
|
|
114757
|
+
} else {
|
|
114758
|
+
console.log(import_picocolors38.default.dim(" Run `horus hosts --reap` to stop them."));
|
|
114355
114759
|
}
|
|
114356
114760
|
console.log("");
|
|
114357
114761
|
}
|
|
114762
|
+
if (projects2.length === 0 && orphans.length === 0) {
|
|
114763
|
+
console.log(import_picocolors38.default.dim("No registered projects. Run `horus index` in a repo first."));
|
|
114764
|
+
return 0;
|
|
114765
|
+
}
|
|
114766
|
+
if (!printedAny && orphans.length === 0) {
|
|
114767
|
+
console.log(import_picocolors38.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
|
|
114768
|
+
return 0;
|
|
114769
|
+
}
|
|
114358
114770
|
const running = rows.filter((r) => r.healthy).length;
|
|
114359
114771
|
console.log(
|
|
114360
|
-
|
|
114361
|
-
`${running} running \xB7 horus
|
|
114772
|
+
import_picocolors38.default.dim(
|
|
114773
|
+
`${running} running${orphans.length ? ` \xB7 ${orphans.length} orphan(s)` : ""} \xB7 horus hosts --reap to clear orphans \xB7 horus stop --all to stop everything`
|
|
114362
114774
|
)
|
|
114363
114775
|
);
|
|
114364
|
-
return 0;
|
|
114776
|
+
return reapFailed > 0 ? 1 : 0;
|
|
114365
114777
|
}
|
|
114366
114778
|
function extractPort2(hostUrl) {
|
|
114367
114779
|
try {
|
|
@@ -114374,12 +114786,12 @@ function extractPort2(hostUrl) {
|
|
|
114374
114786
|
|
|
114375
114787
|
// ../../packages/cli/src/commands/doctor.ts
|
|
114376
114788
|
init_cjs_shims();
|
|
114377
|
-
var
|
|
114789
|
+
var import_picocolors39 = __toESM(require_picocolors(), 1);
|
|
114378
114790
|
var DEFAULT_DB_URL4 = "postgresql://horus:horus@localhost:5433/horus";
|
|
114379
114791
|
function mark2(status) {
|
|
114380
|
-
if (status === "pass") return
|
|
114381
|
-
if (status === "warn") return
|
|
114382
|
-
return
|
|
114792
|
+
if (status === "pass") return import_picocolors39.default.green("\u2713");
|
|
114793
|
+
if (status === "warn") return import_picocolors39.default.yellow("~");
|
|
114794
|
+
return import_picocolors39.default.red("\u2717");
|
|
114383
114795
|
}
|
|
114384
114796
|
async function runDoctor(opts) {
|
|
114385
114797
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -114659,11 +115071,11 @@ async function runDoctor(opts) {
|
|
|
114659
115071
|
write(JSON.stringify(output, null, 2));
|
|
114660
115072
|
return hasFailure ? 1 : 0;
|
|
114661
115073
|
}
|
|
114662
|
-
write(
|
|
115074
|
+
write(import_picocolors39.default.bold("\nHorus readiness check\n"));
|
|
114663
115075
|
for (const check2 of checks) {
|
|
114664
|
-
write(` ${mark2(check2.status)} ${
|
|
115076
|
+
write(` ${mark2(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(26))} ${import_picocolors39.default.dim(check2.detail)}`);
|
|
114665
115077
|
if (check2.next) {
|
|
114666
|
-
write(` ${
|
|
115078
|
+
write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
|
|
114667
115079
|
}
|
|
114668
115080
|
}
|
|
114669
115081
|
write("");
|
|
@@ -114672,20 +115084,20 @@ async function runDoctor(opts) {
|
|
|
114672
115084
|
|
|
114673
115085
|
// ../../packages/cli/src/commands/providers-doctor.ts
|
|
114674
115086
|
init_cjs_shims();
|
|
114675
|
-
var
|
|
114676
|
-
var
|
|
115087
|
+
var import_node_child_process10 = require("child_process");
|
|
115088
|
+
var import_picocolors40 = __toESM(require_picocolors(), 1);
|
|
114677
115089
|
function statusMark(status) {
|
|
114678
|
-
if (status === "ready") return
|
|
114679
|
-
if (status === "installed") return
|
|
114680
|
-
return
|
|
115090
|
+
if (status === "ready") return import_picocolors40.default.green("\u2713");
|
|
115091
|
+
if (status === "installed") return import_picocolors40.default.yellow("~");
|
|
115092
|
+
return import_picocolors40.default.red("\u2717");
|
|
114681
115093
|
}
|
|
114682
115094
|
function statusLabel(status) {
|
|
114683
|
-
if (status === "ready") return
|
|
114684
|
-
if (status === "installed") return
|
|
114685
|
-
return
|
|
115095
|
+
if (status === "ready") return import_picocolors40.default.green("ready");
|
|
115096
|
+
if (status === "installed") return import_picocolors40.default.yellow("installed (not configured)");
|
|
115097
|
+
return import_picocolors40.default.dim("not found on PATH");
|
|
114686
115098
|
}
|
|
114687
115099
|
function detectBinary(id) {
|
|
114688
|
-
const result = (0,
|
|
115100
|
+
const result = (0, import_node_child_process10.spawnSync)(id, ["--version"], { stdio: "pipe", timeout: 2e3 });
|
|
114689
115101
|
if (result.error) {
|
|
114690
115102
|
return { id, status: "unavailable", detail: `${id}: command not found` };
|
|
114691
115103
|
}
|
|
@@ -114699,7 +115111,7 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114699
115111
|
const registry2 = opts?.registry ?? DEFAULT_LOCAL_PROVIDER_REGISTRY;
|
|
114700
115112
|
const write = opts?.write ?? ((line2) => console.log(line2));
|
|
114701
115113
|
const results = buildProviderResults(registry2, opts?._detect);
|
|
114702
|
-
write(
|
|
115114
|
+
write(import_picocolors40.default.bold("\nLocal AI providers\n"));
|
|
114703
115115
|
for (const result of results) {
|
|
114704
115116
|
const descriptor = registry2.get(result.id);
|
|
114705
115117
|
const name2 = descriptor?.displayName ?? result.id;
|
|
@@ -114707,11 +115119,11 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114707
115119
|
` ${statusMark(result.status)} ${result.id.padEnd(8)} ${name2.padEnd(22)} ${statusLabel(result.status)}`
|
|
114708
115120
|
);
|
|
114709
115121
|
if (result.status !== "ready" && result.detail) {
|
|
114710
|
-
write(` ${
|
|
115122
|
+
write(` ${import_picocolors40.default.dim("\u2192 " + result.detail)}`);
|
|
114711
115123
|
}
|
|
114712
115124
|
}
|
|
114713
115125
|
write("");
|
|
114714
|
-
write(
|
|
115126
|
+
write(import_picocolors40.default.bold("Cloud AI providers\n"));
|
|
114715
115127
|
let source = null;
|
|
114716
115128
|
if (opts?._anthropicKey !== void 0) {
|
|
114717
115129
|
source = opts._anthropicKey ? "env" : null;
|
|
@@ -114726,18 +115138,18 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114726
115138
|
}
|
|
114727
115139
|
if (source === "config") {
|
|
114728
115140
|
write(
|
|
114729
|
-
` ${
|
|
115141
|
+
` ${import_picocolors40.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.green("configured (local .horus config)")}`
|
|
114730
115142
|
);
|
|
114731
115143
|
} else if (source === "env") {
|
|
114732
115144
|
write(
|
|
114733
|
-
` ${
|
|
115145
|
+
` ${import_picocolors40.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.green("configured (ANTHROPIC_API_KEY env)")}`
|
|
114734
115146
|
);
|
|
114735
115147
|
} else {
|
|
114736
115148
|
write(
|
|
114737
|
-
` ${
|
|
115149
|
+
` ${import_picocolors40.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.dim("not configured")}`
|
|
114738
115150
|
);
|
|
114739
115151
|
write(
|
|
114740
|
-
` ${
|
|
115152
|
+
` ${import_picocolors40.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
|
|
114741
115153
|
);
|
|
114742
115154
|
}
|
|
114743
115155
|
write("");
|
|
@@ -114746,9 +115158,9 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114746
115158
|
|
|
114747
115159
|
// ../../packages/cli/src/commands/generate-config.ts
|
|
114748
115160
|
init_cjs_shims();
|
|
114749
|
-
var
|
|
114750
|
-
var
|
|
114751
|
-
var
|
|
115161
|
+
var import_node_fs18 = require("fs");
|
|
115162
|
+
var import_node_path20 = require("path");
|
|
115163
|
+
var import_picocolors41 = __toESM(require_picocolors(), 1);
|
|
114752
115164
|
function configTemplate(name2, repoPath) {
|
|
114753
115165
|
return `export default {
|
|
114754
115166
|
database: {
|
|
@@ -114796,39 +115208,39 @@ async function runGenerateConfig(opts) {
|
|
|
114796
115208
|
const defaults2 = localConfigPath2 != null ? projectDefaults(localConfigPath2) : null;
|
|
114797
115209
|
const hasLocalConfig = defaults2 != null;
|
|
114798
115210
|
const defaultOut = hasLocalConfig ? "horus.config.example.js" : "horus.config.js";
|
|
114799
|
-
const outPath = (0,
|
|
115211
|
+
const outPath = (0, import_node_path20.resolve)(cwd, opts.out ?? defaultOut);
|
|
114800
115212
|
const name2 = opts.name ?? defaults2?.name ?? "my-project";
|
|
114801
115213
|
const repoPath = opts.repo ?? defaults2?.repoPath ?? `/path/to/${name2}`;
|
|
114802
|
-
if ((0,
|
|
114803
|
-
log(`${
|
|
114804
|
-
log(
|
|
115214
|
+
if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
|
|
115215
|
+
log(`${import_picocolors41.default.red("\u2717")} ${outPath} already exists`);
|
|
115216
|
+
log(import_picocolors41.default.dim(" pass --force to overwrite"));
|
|
114805
115217
|
return 1;
|
|
114806
115218
|
}
|
|
114807
115219
|
try {
|
|
114808
|
-
(0,
|
|
114809
|
-
(0,
|
|
115220
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
|
|
115221
|
+
(0, import_node_fs18.writeFileSync)(outPath, configTemplate(name2, repoPath), "utf8");
|
|
114810
115222
|
} catch (err2) {
|
|
114811
|
-
log(`${
|
|
115223
|
+
log(`${import_picocolors41.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
|
|
114812
115224
|
return 1;
|
|
114813
115225
|
}
|
|
114814
|
-
log(`${
|
|
114815
|
-
log(
|
|
114816
|
-
log(
|
|
115226
|
+
log(`${import_picocolors41.default.green("\u2713")} Created ${outPath}`);
|
|
115227
|
+
log(import_picocolors41.default.dim(` project: ${name2}`));
|
|
115228
|
+
log(import_picocolors41.default.dim(` repo: ${repoPath}`));
|
|
114817
115229
|
if (hasLocalConfig && localConfigPath2 != null) {
|
|
114818
115230
|
log("");
|
|
114819
|
-
log(
|
|
115231
|
+
log(import_picocolors41.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
|
|
114820
115232
|
log(" \u2022 .horus/config.json \u2014 project config used by `horus investigate` from this repo");
|
|
114821
115233
|
log(" \u2022 horus.config.js \u2014 standalone/global config used with `horus doctor --config <path>`");
|
|
114822
|
-
log(
|
|
115234
|
+
log(import_picocolors41.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
|
|
114823
115235
|
} else {
|
|
114824
|
-
log(
|
|
115236
|
+
log(import_picocolors41.default.dim(` next: horus doctor --config ${outPath}`));
|
|
114825
115237
|
}
|
|
114826
115238
|
return 0;
|
|
114827
115239
|
}
|
|
114828
115240
|
|
|
114829
115241
|
// ../../packages/cli/src/commands/readiness.ts
|
|
114830
115242
|
init_cjs_shims();
|
|
114831
|
-
var
|
|
115243
|
+
var import_picocolors42 = __toESM(require_picocolors(), 1);
|
|
114832
115244
|
var READINESS_AI_CONTRACT = `Provide a clearly separated AI readiness summary with:
|
|
114833
115245
|
|
|
114834
115246
|
Overall assessment
|
|
@@ -114851,9 +115263,9 @@ Confidence / gaps
|
|
|
114851
115263
|
- Which checks Horus could verify vs. which require manual confirmation`;
|
|
114852
115264
|
var DEFAULT_DB_URL5 = "postgresql://horus:horus@localhost:5433/horus";
|
|
114853
115265
|
function mark3(status) {
|
|
114854
|
-
if (status === "pass") return
|
|
114855
|
-
if (status === "warn") return
|
|
114856
|
-
return
|
|
115266
|
+
if (status === "pass") return import_picocolors42.default.green("\u2713");
|
|
115267
|
+
if (status === "warn") return import_picocolors42.default.yellow("~");
|
|
115268
|
+
return import_picocolors42.default.red("\u2717");
|
|
114857
115269
|
}
|
|
114858
115270
|
async function runReadiness(opts) {
|
|
114859
115271
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -115030,20 +115442,20 @@ async function runReadiness(opts) {
|
|
|
115030
115442
|
}
|
|
115031
115443
|
const blockingChecks = checks.filter((c4) => c4.blocking);
|
|
115032
115444
|
const optionalChecks = checks.filter((c4) => !c4.blocking);
|
|
115033
|
-
write(
|
|
115034
|
-
write(
|
|
115445
|
+
write(import_picocolors42.default.bold("\nHorus release readiness\n"));
|
|
115446
|
+
write(import_picocolors42.default.bold(" Blocking"));
|
|
115035
115447
|
for (const check2 of blockingChecks) {
|
|
115036
|
-
write(` ${mark3(check2.status)} ${
|
|
115448
|
+
write(` ${mark3(check2.status)} ${import_picocolors42.default.bold(check2.label.padEnd(22))} ${import_picocolors42.default.dim(check2.detail)}`);
|
|
115037
115449
|
if (check2.next) {
|
|
115038
|
-
write(` ${
|
|
115450
|
+
write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
|
|
115039
115451
|
}
|
|
115040
115452
|
}
|
|
115041
115453
|
write("");
|
|
115042
|
-
write(
|
|
115454
|
+
write(import_picocolors42.default.bold(" Optional"));
|
|
115043
115455
|
for (const check2 of optionalChecks) {
|
|
115044
|
-
write(` ${mark3(check2.status)} ${
|
|
115456
|
+
write(` ${mark3(check2.status)} ${import_picocolors42.default.bold(check2.label.padEnd(22))} ${import_picocolors42.default.dim(check2.detail)}`);
|
|
115045
115457
|
if (check2.next) {
|
|
115046
|
-
write(` ${
|
|
115458
|
+
write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
|
|
115047
115459
|
}
|
|
115048
115460
|
}
|
|
115049
115461
|
write("");
|
|
@@ -115051,21 +115463,21 @@ async function runReadiness(opts) {
|
|
|
115051
115463
|
const optionalWarns = optionalChecks.filter((c4) => c4.status === "warn").length;
|
|
115052
115464
|
if (blockingFails.length === 0) {
|
|
115053
115465
|
if (optionalWarns === 0) {
|
|
115054
|
-
write(
|
|
115466
|
+
write(import_picocolors42.default.green(" Ready for demo/release."));
|
|
115055
115467
|
} else {
|
|
115056
115468
|
write(
|
|
115057
|
-
|
|
115469
|
+
import_picocolors42.default.yellow(
|
|
115058
115470
|
` Ready for a basic demo. ${optionalWarns} optional item(s) not configured \u2014 investigation evidence will be limited.`
|
|
115059
115471
|
)
|
|
115060
115472
|
);
|
|
115061
115473
|
}
|
|
115062
115474
|
} else {
|
|
115063
115475
|
write(
|
|
115064
|
-
|
|
115476
|
+
import_picocolors42.default.red(
|
|
115065
115477
|
` Not ready. ${blockingFails.length} blocking item(s) must be resolved before demo/release.`
|
|
115066
115478
|
)
|
|
115067
115479
|
);
|
|
115068
|
-
write(
|
|
115480
|
+
write(import_picocolors42.default.dim(" Re-run `horus readiness` after resolving the items above."));
|
|
115069
115481
|
}
|
|
115070
115482
|
write("");
|
|
115071
115483
|
if (opts?.ai) {
|
|
@@ -115080,7 +115492,7 @@ async function runReadiness(opts) {
|
|
|
115080
115492
|
});
|
|
115081
115493
|
console.log("\n" + renderInterpretation(result));
|
|
115082
115494
|
if (!result.ok) {
|
|
115083
|
-
console.error(
|
|
115495
|
+
console.error(import_picocolors42.default.yellow(`[ai] ${result.warning}`));
|
|
115084
115496
|
}
|
|
115085
115497
|
}
|
|
115086
115498
|
return blockingFails.length > 0 ? 1 : 0;
|
|
@@ -115088,33 +115500,33 @@ async function runReadiness(opts) {
|
|
|
115088
115500
|
|
|
115089
115501
|
// ../../packages/cli/src/commands/skill.ts
|
|
115090
115502
|
init_cjs_shims();
|
|
115091
|
-
var
|
|
115092
|
-
var
|
|
115503
|
+
var import_node_fs19 = require("fs");
|
|
115504
|
+
var import_node_path21 = require("path");
|
|
115093
115505
|
var import_node_os5 = require("os");
|
|
115094
115506
|
var import_promises2 = require("readline/promises");
|
|
115095
|
-
var
|
|
115507
|
+
var import_picocolors43 = __toESM(require_picocolors(), 1);
|
|
115096
115508
|
var SUPPORTED_TARGETS = ["claude", "codex", "gemini", "cursor", "generic"];
|
|
115097
115509
|
function isValidTarget(target) {
|
|
115098
115510
|
return SUPPORTED_TARGETS.includes(target);
|
|
115099
115511
|
}
|
|
115100
115512
|
function getLegacyClaudeSkillPath(opts) {
|
|
115101
115513
|
const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
|
|
115102
|
-
return (0,
|
|
115514
|
+
return (0, import_node_path21.join)(base2, ".claude", "skills", "horus.md");
|
|
115103
115515
|
}
|
|
115104
115516
|
function getSkillInstallPath(target, opts) {
|
|
115105
115517
|
const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
|
|
115106
115518
|
switch (target) {
|
|
115107
115519
|
case "claude":
|
|
115108
|
-
return (0,
|
|
115520
|
+
return (0, import_node_path21.join)(base2, ".claude", "skills", "horus", "SKILL.md");
|
|
115109
115521
|
case "gemini":
|
|
115110
|
-
return opts.global ? (0,
|
|
115522
|
+
return opts.global ? (0, import_node_path21.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path21.join)(base2, "GEMINI.md");
|
|
115111
115523
|
case "cursor":
|
|
115112
|
-
return opts.global ? (0,
|
|
115524
|
+
return opts.global ? (0, import_node_path21.join)(base2, ".cursorrules") : (0, import_node_path21.join)(base2, ".cursor", "rules", "horus.mdc");
|
|
115113
115525
|
case "codex":
|
|
115114
|
-
return opts.global ? (0,
|
|
115526
|
+
return opts.global ? (0, import_node_path21.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path21.join)(base2, "AGENTS.md");
|
|
115115
115527
|
case "generic":
|
|
115116
115528
|
default:
|
|
115117
|
-
return (0,
|
|
115529
|
+
return (0, import_node_path21.join)(base2, ".horus", "skills", `horus-${target}.md`);
|
|
115118
115530
|
}
|
|
115119
115531
|
}
|
|
115120
115532
|
var BASE_SKILL = `# Horus Grounded Investigation Skill
|
|
@@ -115431,66 +115843,66 @@ async function confirm(message) {
|
|
|
115431
115843
|
async function runSkillInstall(target, opts) {
|
|
115432
115844
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
115433
115845
|
if (!isValidTarget(target)) {
|
|
115434
|
-
log(`${
|
|
115435
|
-
log(
|
|
115846
|
+
log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
|
|
115847
|
+
log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
115436
115848
|
return 1;
|
|
115437
115849
|
}
|
|
115438
115850
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
115439
115851
|
const content = generateSkillContent(target);
|
|
115440
115852
|
if (target === "claude") {
|
|
115441
115853
|
const legacyPath = getLegacyClaudeSkillPath({ global: opts.global, cwd: opts.cwd });
|
|
115442
|
-
if ((0,
|
|
115854
|
+
if ((0, import_node_fs19.existsSync)(legacyPath)) {
|
|
115443
115855
|
if (!opts.force) {
|
|
115444
115856
|
const confirmed = await (opts.confirm ?? confirm)(
|
|
115445
115857
|
`Found old flat skill file at ${legacyPath}. Replace it with the new directory layout?`
|
|
115446
115858
|
);
|
|
115447
115859
|
if (!confirmed) {
|
|
115448
|
-
log(`${
|
|
115449
|
-
log(
|
|
115860
|
+
log(`${import_picocolors43.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
|
|
115861
|
+
log(import_picocolors43.default.dim(" Pass --force to replace without prompting."));
|
|
115450
115862
|
return 1;
|
|
115451
115863
|
}
|
|
115452
115864
|
}
|
|
115453
115865
|
try {
|
|
115454
|
-
(0,
|
|
115455
|
-
log(`${
|
|
115866
|
+
(0, import_node_fs19.rmSync)(legacyPath);
|
|
115867
|
+
log(`${import_picocolors43.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
|
|
115456
115868
|
} catch (err2) {
|
|
115457
|
-
log(`${
|
|
115869
|
+
log(`${import_picocolors43.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err2.message}`);
|
|
115458
115870
|
return 1;
|
|
115459
115871
|
}
|
|
115460
115872
|
}
|
|
115461
115873
|
}
|
|
115462
|
-
if ((0,
|
|
115463
|
-
log(`${
|
|
115464
|
-
log(
|
|
115874
|
+
if ((0, import_node_fs19.existsSync)(outPath) && !opts.force) {
|
|
115875
|
+
log(`${import_picocolors43.default.red("\u2717")} ${outPath} already exists`);
|
|
115876
|
+
log(import_picocolors43.default.dim(" pass --force to overwrite"));
|
|
115465
115877
|
return 1;
|
|
115466
115878
|
}
|
|
115467
115879
|
try {
|
|
115468
|
-
(0,
|
|
115469
|
-
(0,
|
|
115880
|
+
(0, import_node_fs19.mkdirSync)((0, import_node_path21.dirname)(outPath), { recursive: true });
|
|
115881
|
+
(0, import_node_fs19.writeFileSync)(outPath, content, "utf8");
|
|
115470
115882
|
} catch (err2) {
|
|
115471
|
-
log(`${
|
|
115883
|
+
log(`${import_picocolors43.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
|
|
115472
115884
|
return 1;
|
|
115473
115885
|
}
|
|
115474
|
-
log(`${
|
|
115886
|
+
log(`${import_picocolors43.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
|
|
115475
115887
|
if (target === "claude") {
|
|
115476
|
-
log(
|
|
115477
|
-
log(
|
|
115888
|
+
log(import_picocolors43.default.dim(" Claude Code will load this skill automatically."));
|
|
115889
|
+
log(import_picocolors43.default.dim(" Invoke it with: /horus"));
|
|
115478
115890
|
} else if (target === "gemini") {
|
|
115479
|
-
log(
|
|
115891
|
+
log(import_picocolors43.default.dim(" Gemini CLI will load this context file automatically."));
|
|
115480
115892
|
} else if (target === "cursor") {
|
|
115481
|
-
log(
|
|
115893
|
+
log(import_picocolors43.default.dim(" Cursor will load this rule automatically."));
|
|
115482
115894
|
} else if (target === "codex") {
|
|
115483
|
-
log(
|
|
115895
|
+
log(import_picocolors43.default.dim(" Codex CLI will load this file automatically."));
|
|
115484
115896
|
} else {
|
|
115485
|
-
log(
|
|
115897
|
+
log(import_picocolors43.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
|
|
115486
115898
|
}
|
|
115487
115899
|
return 0;
|
|
115488
115900
|
}
|
|
115489
115901
|
async function runSkillPrint(target, opts) {
|
|
115490
115902
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
115491
115903
|
if (!isValidTarget(target)) {
|
|
115492
|
-
log(`${
|
|
115493
|
-
log(
|
|
115904
|
+
log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
|
|
115905
|
+
log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
115494
115906
|
return 1;
|
|
115495
115907
|
}
|
|
115496
115908
|
process.stdout.write(generateSkillContent(target));
|
|
@@ -115499,8 +115911,8 @@ async function runSkillPrint(target, opts) {
|
|
|
115499
115911
|
async function runSkillPath(target, opts) {
|
|
115500
115912
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
115501
115913
|
if (!isValidTarget(target)) {
|
|
115502
|
-
log(`${
|
|
115503
|
-
log(
|
|
115914
|
+
log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
|
|
115915
|
+
log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
115504
115916
|
return 1;
|
|
115505
115917
|
}
|
|
115506
115918
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
@@ -115510,14 +115922,14 @@ async function runSkillPath(target, opts) {
|
|
|
115510
115922
|
|
|
115511
115923
|
// ../../packages/cli/src/commands/update.ts
|
|
115512
115924
|
init_cjs_shims();
|
|
115513
|
-
var
|
|
115514
|
-
var
|
|
115925
|
+
var import_node_fs20 = require("fs");
|
|
115926
|
+
var import_node_path22 = require("path");
|
|
115515
115927
|
var import_node_os6 = require("os");
|
|
115516
115928
|
var import_node_crypto7 = require("crypto");
|
|
115517
|
-
var
|
|
115518
|
-
var
|
|
115519
|
-
var
|
|
115520
|
-
var
|
|
115929
|
+
var import_node_child_process11 = require("child_process");
|
|
115930
|
+
var import_node_util7 = require("util");
|
|
115931
|
+
var import_picocolors44 = __toESM(require_picocolors(), 1);
|
|
115932
|
+
var execFileAsync4 = (0, import_node_util7.promisify)(import_node_child_process11.execFile);
|
|
115521
115933
|
async function ensureBackendPinned(write) {
|
|
115522
115934
|
let installed = null;
|
|
115523
115935
|
try {
|
|
@@ -115526,18 +115938,18 @@ async function ensureBackendPinned(write) {
|
|
|
115526
115938
|
installed = null;
|
|
115527
115939
|
}
|
|
115528
115940
|
if (installed === PINNED_SOURCE_VERSION) {
|
|
115529
|
-
write(` ${
|
|
115941
|
+
write(` ${import_picocolors44.default.green("\u2713")} Source backend already on pinned ${PINNED_SOURCE_VERSION}.`);
|
|
115530
115942
|
return;
|
|
115531
115943
|
}
|
|
115532
115944
|
if (installed === null) {
|
|
115533
115945
|
write(
|
|
115534
|
-
` ${
|
|
115946
|
+
` ${import_picocolors44.default.dim("Source backend not installed \u2014 install it: curl -fsSL https://horus.sh/install.sh | bash")}`
|
|
115535
115947
|
);
|
|
115536
115948
|
return;
|
|
115537
115949
|
}
|
|
115538
115950
|
write(` Upgrading source backend ${installed} \u2192 ${PINNED_SOURCE_VERSION}\u2026`);
|
|
115539
115951
|
try {
|
|
115540
|
-
await
|
|
115952
|
+
await execFileAsync4(
|
|
115541
115953
|
"uv",
|
|
115542
115954
|
// --refresh: a CLI update commonly lands seconds after a backend release, so uv's
|
|
115543
115955
|
// cached PyPI index may not list the just-published version yet and the install
|
|
@@ -115546,10 +115958,10 @@ async function ensureBackendPinned(write) {
|
|
|
115546
115958
|
["tool", "install", "--force", "--refresh", `horus-source==${PINNED_SOURCE_VERSION}`],
|
|
115547
115959
|
{ timeout: 3e5 }
|
|
115548
115960
|
);
|
|
115549
|
-
write(` ${
|
|
115961
|
+
write(` ${import_picocolors44.default.green("\u2713")} Source backend upgraded to ${PINNED_SOURCE_VERSION}.`);
|
|
115550
115962
|
} catch {
|
|
115551
|
-
write(` ${
|
|
115552
|
-
write(` ${
|
|
115963
|
+
write(` ${import_picocolors44.default.yellow("!")} Couldn't auto-upgrade the source backend. Re-run the installer:`);
|
|
115964
|
+
write(` ${import_picocolors44.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
|
|
115553
115965
|
}
|
|
115554
115966
|
}
|
|
115555
115967
|
var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
|
|
@@ -115582,7 +115994,7 @@ function resolveBinaryPath() {
|
|
|
115582
115994
|
const raw = process.argv[1];
|
|
115583
115995
|
if (!raw) throw new Error("Cannot determine binary path from process.argv[1]");
|
|
115584
115996
|
try {
|
|
115585
|
-
return (0,
|
|
115997
|
+
return (0, import_node_fs20.realpathSync)(raw);
|
|
115586
115998
|
} catch {
|
|
115587
115999
|
return raw;
|
|
115588
116000
|
}
|
|
@@ -115592,33 +116004,33 @@ async function runUpdate(opts) {
|
|
|
115592
116004
|
const _fetch = opts._fetch ?? fetch;
|
|
115593
116005
|
const currentVersion = opts._currentVersion ?? HORUS_VERSION;
|
|
115594
116006
|
write(`
|
|
115595
|
-
${
|
|
116007
|
+
${import_picocolors44.default.bold("Horus update")}
|
|
115596
116008
|
`);
|
|
115597
|
-
write(` Current version: ${
|
|
116009
|
+
write(` Current version: ${import_picocolors44.default.cyan(currentVersion)}`);
|
|
115598
116010
|
let release;
|
|
115599
116011
|
try {
|
|
115600
116012
|
release = await fetchLatestRelease(_fetch);
|
|
115601
116013
|
} catch (e) {
|
|
115602
|
-
write(` ${
|
|
116014
|
+
write(` ${import_picocolors44.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
|
|
115603
116015
|
return 1;
|
|
115604
116016
|
}
|
|
115605
116017
|
const latest = release.tag_name.replace(/^v/, "");
|
|
115606
|
-
write(` Latest version: ${
|
|
116018
|
+
write(` Latest version: ${import_picocolors44.default.cyan(latest)}`);
|
|
115607
116019
|
const newer = isNewer(latest, currentVersion);
|
|
115608
116020
|
if (opts.check) {
|
|
115609
116021
|
if (newer) {
|
|
115610
116022
|
write(`
|
|
115611
|
-
${
|
|
115612
|
-
write(` Run ${
|
|
116023
|
+
${import_picocolors44.default.yellow("\u2192")} ${import_picocolors44.default.bold(latest)} is available.`);
|
|
116024
|
+
write(` Run ${import_picocolors44.default.bold("horus update")} to upgrade.`);
|
|
115613
116025
|
} else {
|
|
115614
116026
|
write(`
|
|
115615
|
-
${
|
|
116027
|
+
${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
|
|
115616
116028
|
}
|
|
115617
116029
|
return 0;
|
|
115618
116030
|
}
|
|
115619
116031
|
if (!newer && !opts.force) {
|
|
115620
116032
|
write(`
|
|
115621
|
-
${
|
|
116033
|
+
${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
|
|
115622
116034
|
await ensureBackendPinned(write);
|
|
115623
116035
|
return 0;
|
|
115624
116036
|
}
|
|
@@ -115628,8 +116040,8 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115628
116040
|
const binaryAsset = release.assets.find((a2) => a2.name === assetName);
|
|
115629
116041
|
const checksumAsset = release.assets.find((a2) => a2.name === checksumName);
|
|
115630
116042
|
if (!binaryAsset) {
|
|
115631
|
-
write(` ${
|
|
115632
|
-
write(` Fallback: ${
|
|
116043
|
+
write(` ${import_picocolors44.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
|
|
116044
|
+
write(` Fallback: ${import_picocolors44.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
|
|
115633
116045
|
return 1;
|
|
115634
116046
|
}
|
|
115635
116047
|
write(`
|
|
@@ -115639,7 +116051,7 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115639
116051
|
redirect: "follow"
|
|
115640
116052
|
});
|
|
115641
116053
|
if (!binRes.ok || !binRes.body) {
|
|
115642
|
-
write(` ${
|
|
116054
|
+
write(` ${import_picocolors44.default.red("\u2717")} Download failed: ${binRes.status}`);
|
|
115643
116055
|
return 1;
|
|
115644
116056
|
}
|
|
115645
116057
|
const binBuf = Buffer.from(await binRes.arrayBuffer());
|
|
@@ -115653,82 +116065,82 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115653
116065
|
const expectedHash = csText.trim().split(/\s+/)[0];
|
|
115654
116066
|
const actualHash = sha256hex(binBuf);
|
|
115655
116067
|
if (expectedHash !== actualHash) {
|
|
115656
|
-
write(` ${
|
|
116068
|
+
write(` ${import_picocolors44.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
|
|
115657
116069
|
write(` Expected: ${expectedHash}`);
|
|
115658
116070
|
write(` Got: ${actualHash}`);
|
|
115659
116071
|
return 1;
|
|
115660
116072
|
}
|
|
115661
|
-
write(` ${
|
|
116073
|
+
write(` ${import_picocolors44.default.green("\u2713")} Checksum verified.`);
|
|
115662
116074
|
}
|
|
115663
116075
|
}
|
|
115664
116076
|
let binaryPath;
|
|
115665
116077
|
try {
|
|
115666
116078
|
binaryPath = resolveBinaryPath();
|
|
115667
116079
|
} catch (e) {
|
|
115668
|
-
write(` ${
|
|
116080
|
+
write(` ${import_picocolors44.default.red("\u2717")} ${e.message}`);
|
|
115669
116081
|
return 1;
|
|
115670
116082
|
}
|
|
115671
|
-
if (!(0,
|
|
115672
|
-
write(` ${
|
|
116083
|
+
if (!(0, import_node_fs20.existsSync)(binaryPath)) {
|
|
116084
|
+
write(` ${import_picocolors44.default.red("\u2717")} Binary not found at ${binaryPath}`);
|
|
115673
116085
|
return 1;
|
|
115674
116086
|
}
|
|
115675
|
-
const tmpPath = (0,
|
|
116087
|
+
const tmpPath = (0, import_node_path22.join)((0, import_node_os6.tmpdir)(), `horus-update-${tag}-${process.pid}`);
|
|
115676
116088
|
const backupPath = `${binaryPath}.bak`;
|
|
115677
116089
|
try {
|
|
115678
|
-
(0,
|
|
116090
|
+
(0, import_node_fs20.writeFileSync)(tmpPath, binBuf, { mode: 493 });
|
|
115679
116091
|
} catch (e) {
|
|
115680
|
-
write(` ${
|
|
116092
|
+
write(` ${import_picocolors44.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
|
|
115681
116093
|
return 1;
|
|
115682
116094
|
}
|
|
115683
116095
|
try {
|
|
115684
|
-
(0,
|
|
116096
|
+
(0, import_node_fs20.renameSync)(binaryPath, backupPath);
|
|
115685
116097
|
try {
|
|
115686
|
-
(0,
|
|
115687
|
-
(0,
|
|
116098
|
+
(0, import_node_fs20.renameSync)(tmpPath, binaryPath);
|
|
116099
|
+
(0, import_node_fs20.chmodSync)(binaryPath, 493);
|
|
115688
116100
|
} catch (e) {
|
|
115689
116101
|
try {
|
|
115690
|
-
(0,
|
|
116102
|
+
(0, import_node_fs20.renameSync)(backupPath, binaryPath);
|
|
115691
116103
|
} catch {
|
|
115692
116104
|
}
|
|
115693
116105
|
throw e;
|
|
115694
116106
|
}
|
|
115695
116107
|
try {
|
|
115696
|
-
(0,
|
|
116108
|
+
(0, import_node_fs20.unlinkSync)(backupPath);
|
|
115697
116109
|
} catch {
|
|
115698
116110
|
}
|
|
115699
116111
|
} catch (e) {
|
|
115700
116112
|
try {
|
|
115701
|
-
(0,
|
|
116113
|
+
(0, import_node_fs20.unlinkSync)(tmpPath);
|
|
115702
116114
|
} catch {
|
|
115703
116115
|
}
|
|
115704
116116
|
const msg = e.message ?? String(e);
|
|
115705
116117
|
const needsSudo = e.code === "EACCES" || e.code === "EPERM";
|
|
115706
|
-
write(` ${
|
|
116118
|
+
write(` ${import_picocolors44.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
|
|
115707
116119
|
if (needsSudo) {
|
|
115708
116120
|
write(`
|
|
115709
116121
|
Try with sudo:`);
|
|
115710
116122
|
write(
|
|
115711
|
-
` ${
|
|
116123
|
+
` ${import_picocolors44.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
|
|
115712
116124
|
);
|
|
115713
116125
|
}
|
|
115714
116126
|
return 1;
|
|
115715
116127
|
}
|
|
115716
|
-
write(` ${
|
|
115717
|
-
write(` ${
|
|
116128
|
+
write(` ${import_picocolors44.default.green("\u2713")} Updated: ${import_picocolors44.default.bold(currentVersion)} \u2192 ${import_picocolors44.default.bold(latest)}`);
|
|
116129
|
+
write(` ${import_picocolors44.default.dim(binaryPath)}`);
|
|
115718
116130
|
await ensureBackendPinned(write);
|
|
115719
116131
|
return 0;
|
|
115720
116132
|
}
|
|
115721
116133
|
|
|
115722
116134
|
// ../../packages/cli/src/commands/login.ts
|
|
115723
116135
|
init_cjs_shims();
|
|
115724
|
-
var
|
|
115725
|
-
var
|
|
116136
|
+
var import_node_child_process12 = require("child_process");
|
|
116137
|
+
var import_picocolors45 = __toESM(require_picocolors(), 1);
|
|
115726
116138
|
var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
115727
116139
|
function openBrowser(url) {
|
|
115728
116140
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
115729
116141
|
const args2 = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
115730
116142
|
try {
|
|
115731
|
-
(0,
|
|
116143
|
+
(0, import_node_child_process12.execFile)(cmd, args2, () => {
|
|
115732
116144
|
});
|
|
115733
116145
|
} catch {
|
|
115734
116146
|
}
|
|
@@ -115747,19 +116159,19 @@ async function loginWithToken(apiBaseUrl, token) {
|
|
|
115747
116159
|
}
|
|
115748
116160
|
});
|
|
115749
116161
|
console.log(
|
|
115750
|
-
`${
|
|
116162
|
+
`${import_picocolors45.default.green("\u2713")} Logged in as ${import_picocolors45.default.bold(me3.user.primaryEmail)}. Run ${import_picocolors45.default.bold("horus cloud link")} to connect this repo.`
|
|
115751
116163
|
);
|
|
115752
116164
|
return 0;
|
|
115753
116165
|
} catch (err2) {
|
|
115754
116166
|
if (err2 instanceof CloudOfflineError) {
|
|
115755
|
-
console.error(
|
|
116167
|
+
console.error(import_picocolors45.default.red(err2.message));
|
|
115756
116168
|
return 1;
|
|
115757
116169
|
}
|
|
115758
116170
|
if (err2 instanceof CloudError && err2.status === 401) {
|
|
115759
|
-
console.error(
|
|
116171
|
+
console.error(import_picocolors45.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
|
|
115760
116172
|
return 1;
|
|
115761
116173
|
}
|
|
115762
|
-
console.error(
|
|
116174
|
+
console.error(import_picocolors45.default.red(`Login failed: ${err2.message}`));
|
|
115763
116175
|
return 1;
|
|
115764
116176
|
}
|
|
115765
116177
|
}
|
|
@@ -115770,19 +116182,19 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
115770
116182
|
session = await client.startCliSession();
|
|
115771
116183
|
} catch (err2) {
|
|
115772
116184
|
if (err2 instanceof CloudOfflineError) {
|
|
115773
|
-
console.error(
|
|
116185
|
+
console.error(import_picocolors45.default.red(err2.message));
|
|
115774
116186
|
return 1;
|
|
115775
116187
|
}
|
|
115776
|
-
console.error(
|
|
116188
|
+
console.error(import_picocolors45.default.red(`Could not start login: ${err2.message}`));
|
|
115777
116189
|
return 1;
|
|
115778
116190
|
}
|
|
115779
116191
|
console.log("");
|
|
115780
116192
|
console.log(` To sign in, open this URL in your browser:`);
|
|
115781
|
-
console.log(` ${
|
|
116193
|
+
console.log(` ${import_picocolors45.default.cyan(import_picocolors45.default.bold(session.verificationUri))}`);
|
|
115782
116194
|
console.log("");
|
|
115783
|
-
console.log(` and enter the code: ${
|
|
116195
|
+
console.log(` and enter the code: ${import_picocolors45.default.bold(session.userCode)}`);
|
|
115784
116196
|
console.log("");
|
|
115785
|
-
console.log(
|
|
116197
|
+
console.log(import_picocolors45.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
|
|
115786
116198
|
openBrowser(session.verificationUriComplete);
|
|
115787
116199
|
const deadline = Date.now() + session.expiresIn * 1e3;
|
|
115788
116200
|
let intervalMs = Math.max(session.interval, 1) * 1e3;
|
|
@@ -115793,7 +116205,7 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
115793
116205
|
poll = await client.pollCliSession(session.deviceCode);
|
|
115794
116206
|
} catch (err2) {
|
|
115795
116207
|
if (err2 instanceof CloudOfflineError) {
|
|
115796
|
-
console.error(
|
|
116208
|
+
console.error(import_picocolors45.default.red(`
|
|
115797
116209
|
${err2.message}`));
|
|
115798
116210
|
return 1;
|
|
115799
116211
|
}
|
|
@@ -115806,14 +116218,14 @@ ${err2.message}`));
|
|
|
115806
116218
|
intervalMs += 2e3;
|
|
115807
116219
|
break;
|
|
115808
116220
|
case "denied":
|
|
115809
|
-
console.error(
|
|
116221
|
+
console.error(import_picocolors45.default.red("\nLogin was denied in the browser."));
|
|
115810
116222
|
return 1;
|
|
115811
116223
|
case "expired":
|
|
115812
|
-
console.error(
|
|
116224
|
+
console.error(import_picocolors45.default.red("\nThis login request expired. Run `horus login` again."));
|
|
115813
116225
|
return 1;
|
|
115814
116226
|
case "approved":
|
|
115815
116227
|
if (!poll.token || !poll.account) {
|
|
115816
|
-
console.error(
|
|
116228
|
+
console.error(import_picocolors45.default.red("\nLogin approved but no token was returned. Try again."));
|
|
115817
116229
|
return 1;
|
|
115818
116230
|
}
|
|
115819
116231
|
writeAuth({
|
|
@@ -115823,12 +116235,12 @@ ${err2.message}`));
|
|
|
115823
116235
|
});
|
|
115824
116236
|
console.log(
|
|
115825
116237
|
`
|
|
115826
|
-
${
|
|
116238
|
+
${import_picocolors45.default.green("\u2713")} Logged in as ${import_picocolors45.default.bold(poll.account.email)}. Run ${import_picocolors45.default.bold("horus cloud link")} to connect this repo.`
|
|
115827
116239
|
);
|
|
115828
116240
|
return 0;
|
|
115829
116241
|
}
|
|
115830
116242
|
}
|
|
115831
|
-
console.error(
|
|
116243
|
+
console.error(import_picocolors45.default.red("\nTimed out waiting for approval. Run `horus login` again."));
|
|
115832
116244
|
return 1;
|
|
115833
116245
|
}
|
|
115834
116246
|
async function runLogin(opts = {}) {
|
|
@@ -115842,7 +116254,7 @@ async function runLogin(opts = {}) {
|
|
|
115842
116254
|
async function runLogout() {
|
|
115843
116255
|
const auth = readAuth();
|
|
115844
116256
|
if (!auth) {
|
|
115845
|
-
console.log(
|
|
116257
|
+
console.log(import_picocolors45.default.dim("Not logged in."));
|
|
115846
116258
|
return 0;
|
|
115847
116259
|
}
|
|
115848
116260
|
const session = authedClient();
|
|
@@ -115856,18 +116268,18 @@ async function runLogout() {
|
|
|
115856
116268
|
}
|
|
115857
116269
|
}
|
|
115858
116270
|
clearAuth();
|
|
115859
|
-
console.log(`${
|
|
116271
|
+
console.log(`${import_picocolors45.default.green("\u2713")} Logged out. Local mode still works.`);
|
|
115860
116272
|
return 0;
|
|
115861
116273
|
}
|
|
115862
116274
|
|
|
115863
116275
|
// ../../packages/cli/src/commands/cloud.ts
|
|
115864
116276
|
init_cjs_shims();
|
|
115865
|
-
var
|
|
116277
|
+
var import_picocolors46 = __toESM(require_picocolors(), 1);
|
|
115866
116278
|
var import_promises3 = require("readline/promises");
|
|
115867
116279
|
|
|
115868
116280
|
// ../../packages/cli/src/lib/cloud/git.ts
|
|
115869
116281
|
init_cjs_shims();
|
|
115870
|
-
var
|
|
116282
|
+
var import_node_child_process13 = require("child_process");
|
|
115871
116283
|
function parseRepoName(remoteUrl) {
|
|
115872
116284
|
const cleaned = remoteUrl.trim().replace(/\.git$/, "");
|
|
115873
116285
|
const lastSegment = cleaned.split(/[/:]/).filter(Boolean).pop() ?? "";
|
|
@@ -115891,7 +116303,7 @@ function inferProvider(remoteUrl) {
|
|
|
115891
116303
|
}
|
|
115892
116304
|
function detectGitRemote(root) {
|
|
115893
116305
|
try {
|
|
115894
|
-
const remoteUrl = (0,
|
|
116306
|
+
const remoteUrl = (0, import_node_child_process13.execFileSync)("git", ["-C", root, "remote", "get-url", "origin"], {
|
|
115895
116307
|
encoding: "utf8",
|
|
115896
116308
|
stdio: ["ignore", "pipe", "ignore"]
|
|
115897
116309
|
}).trim();
|
|
@@ -115907,7 +116319,7 @@ async function runCloudLink(opts = {}) {
|
|
|
115907
116319
|
const root = repoRootOrCwd(opts.cwd);
|
|
115908
116320
|
const session = authedClient();
|
|
115909
116321
|
if (!session) {
|
|
115910
|
-
console.error(
|
|
116322
|
+
console.error(import_picocolors46.default.red(`Not logged in. Run ${import_picocolors46.default.bold("horus login")} first.`));
|
|
115911
116323
|
return 1;
|
|
115912
116324
|
}
|
|
115913
116325
|
let ctx;
|
|
@@ -115917,7 +116329,7 @@ async function runCloudLink(opts = {}) {
|
|
|
115917
116329
|
return reportCloudError(err2);
|
|
115918
116330
|
}
|
|
115919
116331
|
const remote = detectGitRemote(root);
|
|
115920
|
-
if (remote) console.log(
|
|
116332
|
+
if (remote) console.log(import_picocolors46.default.dim(`Detected git remote: ${remote.remoteUrl}`));
|
|
115921
116333
|
let resolved = null;
|
|
115922
116334
|
let target = opts.project;
|
|
115923
116335
|
if (!target) {
|
|
@@ -115933,7 +116345,7 @@ async function runCloudLink(opts = {}) {
|
|
|
115933
116345
|
const org = ctx.organizations.find((o5) => o5.id === match.organizationId);
|
|
115934
116346
|
if (ws && org) {
|
|
115935
116347
|
target = `${org.slug}/${ws.slug}/${match.slug}`;
|
|
115936
|
-
console.log(
|
|
116348
|
+
console.log(import_picocolors46.default.dim(`Matched existing project by remote: ${import_picocolors46.default.bold(target)}`));
|
|
115937
116349
|
}
|
|
115938
116350
|
}
|
|
115939
116351
|
}
|
|
@@ -115942,8 +116354,8 @@ async function runCloudLink(opts = {}) {
|
|
|
115942
116354
|
if (created === "aborted") return 1;
|
|
115943
116355
|
if (!created) {
|
|
115944
116356
|
console.error(
|
|
115945
|
-
|
|
115946
|
-
Run ${
|
|
116357
|
+
import_picocolors46.default.red("No project to link.") + `
|
|
116358
|
+
Run ${import_picocolors46.default.bold("horus cloud link")} in a TTY to create one, pass ${import_picocolors46.default.bold("--project <org/workspace/project>")}, or create a project at ${import_picocolors46.default.bold("https://cloud.horus.sh")}.`
|
|
115947
116359
|
);
|
|
115948
116360
|
return 1;
|
|
115949
116361
|
}
|
|
@@ -115953,8 +116365,8 @@ async function runCloudLink(opts = {}) {
|
|
|
115953
116365
|
resolved = resolveTriple(ctx, target);
|
|
115954
116366
|
if (!resolved) {
|
|
115955
116367
|
console.error(
|
|
115956
|
-
|
|
115957
|
-
Run ${
|
|
116368
|
+
import_picocolors46.default.red(`You don't have access to ${import_picocolors46.default.bold(target)}.`) + `
|
|
116369
|
+
Run ${import_picocolors46.default.bold("horus context list")} to see available projects.`
|
|
115958
116370
|
);
|
|
115959
116371
|
return 1;
|
|
115960
116372
|
}
|
|
@@ -115967,10 +116379,10 @@ async function runCloudLink(opts = {}) {
|
|
|
115967
116379
|
project: resolved.project
|
|
115968
116380
|
});
|
|
115969
116381
|
console.log(
|
|
115970
|
-
`${
|
|
116382
|
+
`${import_picocolors46.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors46.default.bold(label)} ${import_picocolors46.default.dim("(cloud)")}.`
|
|
115971
116383
|
);
|
|
115972
116384
|
console.log(
|
|
115973
|
-
|
|
116385
|
+
import_picocolors46.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors46.default.bold("horus context use local")} to switch back.`)
|
|
115974
116386
|
);
|
|
115975
116387
|
return 0;
|
|
115976
116388
|
}
|
|
@@ -115978,11 +116390,11 @@ async function runCloudUnlink(opts = {}) {
|
|
|
115978
116390
|
const root = repoRootOrCwd(opts.cwd);
|
|
115979
116391
|
const cfg = readCloudConfig(root);
|
|
115980
116392
|
if (!cfg || cfg.context !== "cloud") {
|
|
115981
|
-
console.log(
|
|
116393
|
+
console.log(import_picocolors46.default.dim("This repo isn't linked to a cloud project."));
|
|
115982
116394
|
return 0;
|
|
115983
116395
|
}
|
|
115984
116396
|
clearCloudConfig(root);
|
|
115985
|
-
console.log(`${
|
|
116397
|
+
console.log(`${import_picocolors46.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors46.default.bold("local")} mode.`);
|
|
115986
116398
|
return 0;
|
|
115987
116399
|
}
|
|
115988
116400
|
async function runCloudStatus(opts = {}) {
|
|
@@ -115990,18 +116402,18 @@ async function runCloudStatus(opts = {}) {
|
|
|
115990
116402
|
const cfg = readCloudConfig(root);
|
|
115991
116403
|
const session = authedClient();
|
|
115992
116404
|
if (!isCloudActive(cfg) || !cfg?.project) {
|
|
115993
|
-
console.log(`${
|
|
115994
|
-
console.log(`${
|
|
116405
|
+
console.log(`${import_picocolors46.default.bold("Context:")} local`);
|
|
116406
|
+
console.log(`${import_picocolors46.default.bold("Storage:")} local Postgres (~/.horus)`);
|
|
115995
116407
|
console.log(
|
|
115996
|
-
|
|
116408
|
+
import_picocolors46.default.dim(`Tip: ${import_picocolors46.default.bold("horus login")} then ${import_picocolors46.default.bold("horus cloud link")} to share investigations with your team.`)
|
|
115997
116409
|
);
|
|
115998
116410
|
return 0;
|
|
115999
116411
|
}
|
|
116000
116412
|
const triple = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
116001
|
-
console.log(`${
|
|
116413
|
+
console.log(`${import_picocolors46.default.bold("Context:")} ${triple} ${import_picocolors46.default.dim("(cloud)")}`);
|
|
116002
116414
|
let projectLine = `${cfg.project.slug}`;
|
|
116003
116415
|
let metaLines = [];
|
|
116004
|
-
let account =
|
|
116416
|
+
let account = import_picocolors46.default.dim("not logged in");
|
|
116005
116417
|
if (session) {
|
|
116006
116418
|
account = session.auth.account.email;
|
|
116007
116419
|
try {
|
|
@@ -116016,9 +116428,9 @@ async function runCloudStatus(opts = {}) {
|
|
|
116016
116428
|
} catch {
|
|
116017
116429
|
}
|
|
116018
116430
|
}
|
|
116019
|
-
console.log(`${
|
|
116431
|
+
console.log(`${import_picocolors46.default.bold("Project:")} ${projectLine}`);
|
|
116020
116432
|
for (const line2 of metaLines) console.log(line2);
|
|
116021
|
-
console.log(`${
|
|
116433
|
+
console.log(`${import_picocolors46.default.bold("Account:")} ${account}`);
|
|
116022
116434
|
return 0;
|
|
116023
116435
|
}
|
|
116024
116436
|
async function runCloudSync(opts = {}) {
|
|
@@ -116026,13 +116438,13 @@ async function runCloudSync(opts = {}) {
|
|
|
116026
116438
|
const cfg = readCloudConfig(root);
|
|
116027
116439
|
if (!isCloudActive(cfg) || !cfg.project) {
|
|
116028
116440
|
console.error(
|
|
116029
|
-
|
|
116441
|
+
import_picocolors46.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors46.default.bold("horus cloud link")} first.`
|
|
116030
116442
|
);
|
|
116031
116443
|
return 1;
|
|
116032
116444
|
}
|
|
116033
116445
|
const session = authedClient();
|
|
116034
116446
|
if (!session) {
|
|
116035
|
-
console.error(
|
|
116447
|
+
console.error(import_picocolors46.default.red(`Not logged in. Run ${import_picocolors46.default.bold("horus login")} first.`));
|
|
116036
116448
|
return 1;
|
|
116037
116449
|
}
|
|
116038
116450
|
const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
|
|
@@ -116053,26 +116465,26 @@ async function runCloudSync(opts = {}) {
|
|
|
116053
116465
|
const target = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
116054
116466
|
if (uploadable.length === 0) {
|
|
116055
116467
|
console.log(
|
|
116056
|
-
|
|
116468
|
+
import_picocolors46.default.dim(
|
|
116057
116469
|
`No local investigations with reports to upload.` + (skippedNoReport ? ` (${skippedNoReport} without a report skipped)` : "")
|
|
116058
116470
|
)
|
|
116059
116471
|
);
|
|
116060
116472
|
return 0;
|
|
116061
116473
|
}
|
|
116062
|
-
console.log(
|
|
116474
|
+
console.log(import_picocolors46.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors46.default.dim("(cloud)")}:`));
|
|
116063
116475
|
for (const c4 of uploadable.slice(0, 20)) {
|
|
116064
|
-
console.log(` ${
|
|
116476
|
+
console.log(` ${import_picocolors46.default.dim(c4.id.slice(0, 8))} ${(c4.title ?? "untitled").slice(0, 70)}`);
|
|
116065
116477
|
}
|
|
116066
|
-
if (uploadable.length > 20) console.log(
|
|
116067
|
-
if (skippedNoReport) console.log(
|
|
116478
|
+
if (uploadable.length > 20) console.log(import_picocolors46.default.dim(` \u2026and ${uploadable.length - 20} more`));
|
|
116479
|
+
if (skippedNoReport) console.log(import_picocolors46.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
|
|
116068
116480
|
if (opts.dryRun) {
|
|
116069
|
-
console.log(
|
|
116481
|
+
console.log(import_picocolors46.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
|
|
116070
116482
|
return 0;
|
|
116071
116483
|
}
|
|
116072
116484
|
if (!opts.yes) {
|
|
116073
116485
|
if (!process.stdin.isTTY) {
|
|
116074
116486
|
console.error(
|
|
116075
|
-
|
|
116487
|
+
import_picocolors46.default.yellow(`Re-run with ${import_picocolors46.default.bold("--yes")} to upload, or ${import_picocolors46.default.bold("--dry-run")} to preview only.`)
|
|
116076
116488
|
);
|
|
116077
116489
|
return 1;
|
|
116078
116490
|
}
|
|
@@ -116080,7 +116492,7 @@ async function runCloudSync(opts = {}) {
|
|
|
116080
116492
|
try {
|
|
116081
116493
|
const answer = (await rl.question(`Upload ${uploadable.length} investigation(s) to ${target}? (y/N) `)).trim().toLowerCase();
|
|
116082
116494
|
if (answer !== "y" && answer !== "yes") {
|
|
116083
|
-
console.log(
|
|
116495
|
+
console.log(import_picocolors46.default.dim("Aborted. Nothing uploaded."));
|
|
116084
116496
|
return 0;
|
|
116085
116497
|
}
|
|
116086
116498
|
} finally {
|
|
@@ -116093,18 +116505,18 @@ async function runCloudSync(opts = {}) {
|
|
|
116093
116505
|
try {
|
|
116094
116506
|
const refs = await uploadInvestigationToCloud(session.client, cfg, c4.report);
|
|
116095
116507
|
created++;
|
|
116096
|
-
console.log(`${
|
|
116508
|
+
console.log(`${import_picocolors46.default.green("\u2713")} ${import_picocolors46.default.dim(c4.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
|
|
116097
116509
|
} catch (err2) {
|
|
116098
116510
|
failed++;
|
|
116099
|
-
console.error(`${
|
|
116511
|
+
console.error(`${import_picocolors46.default.red("\u2717")} ${import_picocolors46.default.dim(c4.id.slice(0, 8))} ${err2.message}`);
|
|
116100
116512
|
}
|
|
116101
116513
|
}
|
|
116102
116514
|
console.log("");
|
|
116103
116515
|
console.log(
|
|
116104
|
-
`${
|
|
116516
|
+
`${import_picocolors46.default.bold("Sync complete:")} ${import_picocolors46.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors46.default.red(`${failed} failed`) : "0 failed") + "."
|
|
116105
116517
|
);
|
|
116106
116518
|
console.log(
|
|
116107
|
-
|
|
116519
|
+
import_picocolors46.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
|
|
116108
116520
|
);
|
|
116109
116521
|
return failed > 0 ? 1 : 0;
|
|
116110
116522
|
}
|
|
@@ -116112,8 +116524,8 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116112
116524
|
if (!process.stdin.isTTY && !yes) return null;
|
|
116113
116525
|
if (ctx.organizations.length === 0) {
|
|
116114
116526
|
console.error(
|
|
116115
|
-
|
|
116116
|
-
Create one at ${
|
|
116527
|
+
import_picocolors46.default.red("You're not a member of any organization yet.") + `
|
|
116528
|
+
Create one at ${import_picocolors46.default.bold("https://cloud.horus.sh")} first.`
|
|
116117
116529
|
);
|
|
116118
116530
|
return null;
|
|
116119
116531
|
}
|
|
@@ -116138,9 +116550,9 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116138
116550
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
116139
116551
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
116140
116552
|
workspace = { id: ws.id, slug: ws.slug };
|
|
116141
|
-
console.log(
|
|
116553
|
+
console.log(import_picocolors46.default.dim(`Created workspace ${import_picocolors46.default.bold(ws.slug)}.`));
|
|
116142
116554
|
} else {
|
|
116143
|
-
console.log(`Workspaces in ${
|
|
116555
|
+
console.log(`Workspaces in ${import_picocolors46.default.bold(org.slug)}:`);
|
|
116144
116556
|
orgWorkspaces.forEach((w4, i3) => console.log(` ${i3 + 1}) ${w4.slug}`));
|
|
116145
116557
|
console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
|
|
116146
116558
|
const pick2 = Number(await ask2("Workspace number", "1"));
|
|
@@ -116148,7 +116560,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116148
116560
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
116149
116561
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
116150
116562
|
workspace = { id: ws.id, slug: ws.slug };
|
|
116151
|
-
console.log(
|
|
116563
|
+
console.log(import_picocolors46.default.dim(`Created workspace ${import_picocolors46.default.bold(ws.slug)}.`));
|
|
116152
116564
|
} else {
|
|
116153
116565
|
const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
|
|
116154
116566
|
workspace = { id: chosen.id, slug: chosen.slug };
|
|
@@ -116160,7 +116572,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116160
116572
|
remoteUrl: remote?.remoteUrl,
|
|
116161
116573
|
provider: remote ? inferProvider(remote.remoteUrl) : void 0
|
|
116162
116574
|
});
|
|
116163
|
-
console.log(
|
|
116575
|
+
console.log(import_picocolors46.default.dim(`Created project ${import_picocolors46.default.bold(project.slug)}.`));
|
|
116164
116576
|
return {
|
|
116165
116577
|
organization: { id: org.id, slug: org.slug },
|
|
116166
116578
|
workspace,
|
|
@@ -116176,7 +116588,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116176
116588
|
|
|
116177
116589
|
// ../../packages/cli/src/commands/telemetry.ts
|
|
116178
116590
|
init_cjs_shims();
|
|
116179
|
-
var
|
|
116591
|
+
var import_picocolors48 = __toESM(require_picocolors(), 1);
|
|
116180
116592
|
var import_node_crypto8 = require("crypto");
|
|
116181
116593
|
|
|
116182
116594
|
// ../../packages/cli/src/lib/telemetry/transport.ts
|
|
@@ -116237,7 +116649,7 @@ async function deleteRemoteTelemetry(installId, opts = {}) {
|
|
|
116237
116649
|
|
|
116238
116650
|
// ../../packages/cli/src/lib/telemetry/notice.ts
|
|
116239
116651
|
init_cjs_shims();
|
|
116240
|
-
var
|
|
116652
|
+
var import_picocolors47 = __toESM(require_picocolors(), 1);
|
|
116241
116653
|
var PRIVACY_URL = "https://horus.sh/privacy";
|
|
116242
116654
|
var SUPPRESS_FIRST_TOKEN = /* @__PURE__ */ new Set(["telemetry", "help"]);
|
|
116243
116655
|
var SUPPRESS_FLAGS = /* @__PURE__ */ new Set(["-V", "--version", "-h", "--help"]);
|
|
@@ -116249,11 +116661,11 @@ function shouldSuppressFor(argv) {
|
|
|
116249
116661
|
return firstToken != null && SUPPRESS_FIRST_TOKEN.has(firstToken);
|
|
116250
116662
|
}
|
|
116251
116663
|
function banner() {
|
|
116252
|
-
const b5 =
|
|
116253
|
-
const dim =
|
|
116664
|
+
const b5 = import_picocolors47.default.bold;
|
|
116665
|
+
const dim = import_picocolors47.default.dim;
|
|
116254
116666
|
return [
|
|
116255
116667
|
"",
|
|
116256
|
-
|
|
116668
|
+
import_picocolors47.default.cyan(b5("Horus collects anonymous usage data to improve the tool.")),
|
|
116257
116669
|
dim(" \u2022 On by default: command names, timings, success/failure, and where the"),
|
|
116258
116670
|
dim(" engine ran out of evidence. No file contents, queries, or secrets."),
|
|
116259
116671
|
dim(" \u2022 Off by default: your investigation inputs/outputs (opt in to help train"),
|
|
@@ -116261,7 +116673,7 @@ function banner() {
|
|
|
116261
116673
|
dim(" \u2022 If you use Horus Cloud, linked investigations sync to your workspace."),
|
|
116262
116674
|
"",
|
|
116263
116675
|
` ${dim("Opt out anytime:")} ${b5("horus telemetry disable")} ${dim("\xB7")} ${b5("HORUS_TELEMETRY=0")} ${dim("\xB7")} ${b5("DO_NOT_TRACK=1")}`,
|
|
116264
|
-
` ${dim("Details:")} ${
|
|
116676
|
+
` ${dim("Details:")} ${import_picocolors47.default.underline(PRIVACY_URL)}`,
|
|
116265
116677
|
""
|
|
116266
116678
|
].join("\n");
|
|
116267
116679
|
}
|
|
@@ -116284,45 +116696,45 @@ function maybeShowFirstRunNotice(argv = process.argv) {
|
|
|
116284
116696
|
|
|
116285
116697
|
// ../../packages/cli/src/commands/telemetry.ts
|
|
116286
116698
|
function onOff(enabled) {
|
|
116287
|
-
return enabled ?
|
|
116699
|
+
return enabled ? import_picocolors48.default.green("on") : import_picocolors48.default.dim("off");
|
|
116288
116700
|
}
|
|
116289
116701
|
function warnIfEnvOverrides() {
|
|
116290
116702
|
const decision = resolveConsent();
|
|
116291
116703
|
if (decision.forcedOff) {
|
|
116292
116704
|
console.log(
|
|
116293
|
-
|
|
116705
|
+
import_picocolors48.default.yellow(
|
|
116294
116706
|
`
|
|
116295
116707
|
Note: telemetry is currently forced off by your environment (${decision.reason}).`
|
|
116296
116708
|
)
|
|
116297
116709
|
);
|
|
116298
|
-
console.log(
|
|
116710
|
+
console.log(import_picocolors48.default.dim("Your saved preference will apply once that override is removed."));
|
|
116299
116711
|
} else if (decision.source === "ci") {
|
|
116300
116712
|
console.log(
|
|
116301
|
-
|
|
116713
|
+
import_picocolors48.default.yellow("\nNote: CI environment detected \u2014 telemetry stays off here regardless.")
|
|
116302
116714
|
);
|
|
116303
116715
|
}
|
|
116304
116716
|
}
|
|
116305
116717
|
async function runTelemetryStatus() {
|
|
116306
116718
|
const state = readTelemetryState();
|
|
116307
116719
|
const decision = resolveConsent({ state });
|
|
116308
|
-
console.log(
|
|
116720
|
+
console.log(import_picocolors48.default.bold("Horus telemetry"));
|
|
116309
116721
|
console.log(
|
|
116310
|
-
` Usage metadata (Tier A): ${onOff(decision.tierA)} ${
|
|
116722
|
+
` Usage metadata (Tier A): ${onOff(decision.tierA)} ${import_picocolors48.default.dim(`[${decision.source}]`)}`
|
|
116311
116723
|
);
|
|
116312
116724
|
console.log(` Content sharing (Tier B): ${onOff(decision.tierB)}`);
|
|
116313
|
-
console.log(
|
|
116725
|
+
console.log(import_picocolors48.default.dim(` Reason: ${decision.reason}`));
|
|
116314
116726
|
if (state) {
|
|
116315
|
-
console.log(
|
|
116727
|
+
console.log(import_picocolors48.default.dim(` Install ID: ${state.installId}`));
|
|
116316
116728
|
console.log(
|
|
116317
|
-
|
|
116729
|
+
import_picocolors48.default.dim(
|
|
116318
116730
|
` Saved: Tier A ${state.tierA.enabled ? "on" : "off"}, Tier B ${state.tierB.enabled ? "on" : "off"}`
|
|
116319
116731
|
)
|
|
116320
116732
|
);
|
|
116321
116733
|
} else {
|
|
116322
|
-
console.log(
|
|
116734
|
+
console.log(import_picocolors48.default.dim(" No saved preference yet (defaults apply)."));
|
|
116323
116735
|
}
|
|
116324
|
-
console.log(
|
|
116325
|
-
console.log(
|
|
116736
|
+
console.log(import_picocolors48.default.dim(` Config: ${telemetryPath()}`));
|
|
116737
|
+
console.log(import_picocolors48.default.dim(` Privacy: ${PRIVACY_URL}`));
|
|
116326
116738
|
if (!state || decision.source === "env" || decision.source === "ci") {
|
|
116327
116739
|
warnIfEnvOverrides();
|
|
116328
116740
|
}
|
|
@@ -116333,7 +116745,7 @@ async function runTelemetryEnable() {
|
|
|
116333
116745
|
s5.tierA.enabled = true;
|
|
116334
116746
|
if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
116335
116747
|
});
|
|
116336
|
-
console.log(
|
|
116748
|
+
console.log(import_picocolors48.default.green("Anonymous usage metadata (Tier A) is now ON. Thank you!"));
|
|
116337
116749
|
warnIfEnvOverrides();
|
|
116338
116750
|
return 0;
|
|
116339
116751
|
}
|
|
@@ -116343,7 +116755,7 @@ async function runTelemetryDisable() {
|
|
|
116343
116755
|
s5.tierB.enabled = false;
|
|
116344
116756
|
if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
116345
116757
|
});
|
|
116346
|
-
console.log(
|
|
116758
|
+
console.log(import_picocolors48.default.yellow("All telemetry is now OFF (Tier A and Tier B)."));
|
|
116347
116759
|
return 0;
|
|
116348
116760
|
}
|
|
116349
116761
|
async function runTelemetryEnableContent() {
|
|
@@ -116354,10 +116766,10 @@ async function runTelemetryEnableContent() {
|
|
|
116354
116766
|
if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
116355
116767
|
});
|
|
116356
116768
|
console.log(
|
|
116357
|
-
|
|
116769
|
+
import_picocolors48.default.green("Content sharing (Tier B) is now ON \u2014 redacted inputs/outputs help improve Horus.")
|
|
116358
116770
|
);
|
|
116359
|
-
console.log(
|
|
116360
|
-
console.log(
|
|
116771
|
+
console.log(import_picocolors48.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
|
|
116772
|
+
console.log(import_picocolors48.default.dim(`What this means: ${PRIVACY_URL}`));
|
|
116361
116773
|
warnIfEnvOverrides();
|
|
116362
116774
|
return 0;
|
|
116363
116775
|
}
|
|
@@ -116365,7 +116777,7 @@ async function runTelemetryDisableContent() {
|
|
|
116365
116777
|
updateTelemetryState((s5) => {
|
|
116366
116778
|
s5.tierB.enabled = false;
|
|
116367
116779
|
});
|
|
116368
|
-
console.log(
|
|
116780
|
+
console.log(import_picocolors48.default.yellow("Content sharing (Tier B) is now OFF. Usage metadata is unchanged."));
|
|
116369
116781
|
return 0;
|
|
116370
116782
|
}
|
|
116371
116783
|
async function runTelemetryResetId() {
|
|
@@ -116373,8 +116785,8 @@ async function runTelemetryResetId() {
|
|
|
116373
116785
|
updateTelemetryState((s5) => {
|
|
116374
116786
|
s5.installId = next;
|
|
116375
116787
|
});
|
|
116376
|
-
console.log(
|
|
116377
|
-
console.log(
|
|
116788
|
+
console.log(import_picocolors48.default.green("Install ID reset."));
|
|
116789
|
+
console.log(import_picocolors48.default.dim(` New install ID: ${next}`));
|
|
116378
116790
|
return 0;
|
|
116379
116791
|
}
|
|
116380
116792
|
async function runTelemetryDelete() {
|
|
@@ -116382,9 +116794,9 @@ async function runTelemetryDelete() {
|
|
|
116382
116794
|
if (installId) await deleteRemoteTelemetry(installId);
|
|
116383
116795
|
deleteTelemetryState();
|
|
116384
116796
|
clearSpool();
|
|
116385
|
-
console.log(
|
|
116797
|
+
console.log(import_picocolors48.default.green("Telemetry deleted \u2014 local state cleared and a server purge requested."));
|
|
116386
116798
|
console.log(
|
|
116387
|
-
|
|
116799
|
+
import_picocolors48.default.dim(
|
|
116388
116800
|
"Next run will treat this as a fresh install and re-show the notice. If the purge could not reach the server (offline), re-run this command when back online."
|
|
116389
116801
|
)
|
|
116390
116802
|
);
|
|
@@ -116427,35 +116839,35 @@ function installCommandTelemetry(program2) {
|
|
|
116427
116839
|
|
|
116428
116840
|
// ../../packages/cli/src/commands/feedback.ts
|
|
116429
116841
|
init_cjs_shims();
|
|
116430
|
-
var
|
|
116842
|
+
var import_picocolors49 = __toESM(require_picocolors(), 1);
|
|
116431
116843
|
async function runFeedback(investigationId, opts = {}) {
|
|
116432
116844
|
const id = investigationId?.trim();
|
|
116433
116845
|
if (!id) {
|
|
116434
|
-
console.error(
|
|
116846
|
+
console.error(import_picocolors49.default.red("Usage: horus feedback <investigationId> [--resolved yes|partly|no]"));
|
|
116435
116847
|
return 1;
|
|
116436
116848
|
}
|
|
116437
116849
|
if (opts.resolved !== void 0) {
|
|
116438
116850
|
const resolved = parseResolved(opts.resolved);
|
|
116439
116851
|
if (resolved === null) {
|
|
116440
|
-
console.error(
|
|
116852
|
+
console.error(import_picocolors49.default.red(`Invalid --resolved "${opts.resolved}". Use: yes | partly | no.`));
|
|
116441
116853
|
return 1;
|
|
116442
116854
|
}
|
|
116443
116855
|
let manualEstimateMinutes = null;
|
|
116444
116856
|
if (opts.manualEstimateMin !== void 0) {
|
|
116445
116857
|
const n4 = Number.parseInt(opts.manualEstimateMin, 10);
|
|
116446
116858
|
if (!Number.isFinite(n4) || n4 < 0) {
|
|
116447
|
-
console.error(
|
|
116859
|
+
console.error(import_picocolors49.default.red(`Invalid --manual-estimate-min "${opts.manualEstimateMin}" (expect a non-negative integer).`));
|
|
116448
116860
|
return 1;
|
|
116449
116861
|
}
|
|
116450
116862
|
manualEstimateMinutes = n4;
|
|
116451
116863
|
}
|
|
116452
116864
|
submitFeedback({ investigationId: id, resolved, manualEstimateMinutes, horusSeconds: null, source: "flag" });
|
|
116453
|
-
console.log(
|
|
116865
|
+
console.log(import_picocolors49.default.dim(`Feedback recorded (${resolved}) \u2014 thanks, this helps improve Horus.`));
|
|
116454
116866
|
return 0;
|
|
116455
116867
|
}
|
|
116456
116868
|
if (!process.stdin.isTTY) {
|
|
116457
116869
|
console.error(
|
|
116458
|
-
|
|
116870
|
+
import_picocolors49.default.yellow(
|
|
116459
116871
|
"horus feedback needs an interactive terminal \u2014 or pass --resolved yes|partly|no for non-interactive (agent) use."
|
|
116460
116872
|
)
|
|
116461
116873
|
);
|
|
@@ -116548,8 +116960,8 @@ Examples:
|
|
|
116548
116960
|
program2.command("stop").description("Stop the source-intelligence host for the current repo (or --all to stop every host)").option("--all", "stop all registered source-intelligence hosts").action(async (opts) => {
|
|
116549
116961
|
process.exitCode = await runStop(opts);
|
|
116550
116962
|
});
|
|
116551
|
-
program2.command("hosts").description("List registered source-intelligence hosts and their live status (port, repo, running/stopped)").action(async () => {
|
|
116552
|
-
process.exitCode = await runHosts();
|
|
116963
|
+
program2.command("hosts").description("List registered source-intelligence hosts and their live status (port, repo, running/stopped)").option("--reap", "stop orphaned source hosts that no registered repo owns").action(async (opts) => {
|
|
116964
|
+
process.exitCode = await runHosts({ reap: opts.reap });
|
|
116553
116965
|
});
|
|
116554
116966
|
program2.command("status").description("Show config, provider health, and project/environment matrix").option("-c, --config <path>", "path to horus.config.ts").option("--name <name>", "registered project name (resolves via the registry)").option("--project <name>", "project name (show only this project)").option("--env <name>", "environment name (e.g. production)").action(
|
|
116555
116967
|
async (opts) => {
|