@merittdev/horus 0.8.17 → 0.8.19
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 +1153 -702
- 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.19" : "dev";
|
|
65884
|
+
var PINNED_SOURCE_VERSION = "1.5.5";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
65887
65887
|
init_cjs_shims();
|
|
@@ -71008,11 +71008,22 @@ function isAnalyzed(root) {
|
|
|
71008
71008
|
async function analyzeRepo(root) {
|
|
71009
71009
|
const bin = await resolveSourceBin();
|
|
71010
71010
|
if (!bin) throw new Error("horus-source not found on PATH. Install it: curl -fsSL https://horus.sh/install.sh | bash");
|
|
71011
|
-
|
|
71012
|
-
|
|
71013
|
-
|
|
71014
|
-
|
|
71015
|
-
|
|
71011
|
+
try {
|
|
71012
|
+
await exec(bin, ["analyze", "."], {
|
|
71013
|
+
cwd: root,
|
|
71014
|
+
timeout: 9e5,
|
|
71015
|
+
maxBuffer: 64 * 1024 * 1024
|
|
71016
|
+
});
|
|
71017
|
+
} catch (err2) {
|
|
71018
|
+
const e = err2;
|
|
71019
|
+
const tail = (e.stderr ?? "").trim().slice(-800);
|
|
71020
|
+
if (e.killed || e.signal === "SIGTERM" || e.code === "ETIMEDOUT") {
|
|
71021
|
+
throw new Error(
|
|
71022
|
+
`horus-source analyze timed out after 900s (large repo / slow embeddings phase)` + (tail ? ` \u2014 last output: ${tail}` : "")
|
|
71023
|
+
);
|
|
71024
|
+
}
|
|
71025
|
+
throw new Error(tail ? `horus-source analyze failed: ${tail}` : e.message ?? "horus-source analyze failed");
|
|
71026
|
+
}
|
|
71016
71027
|
}
|
|
71017
71028
|
function readSourceHostUrl(root) {
|
|
71018
71029
|
const p5 = (0, import_node_path3.join)(root, ".horus", "source", "host.json");
|
|
@@ -71105,6 +71116,34 @@ function removeSpawnedHostRecord(root) {
|
|
|
71105
71116
|
} catch {
|
|
71106
71117
|
}
|
|
71107
71118
|
}
|
|
71119
|
+
async function killSpawnedHost(root) {
|
|
71120
|
+
const pids = /* @__PURE__ */ new Set();
|
|
71121
|
+
const spawned = readSpawnedHost(root);
|
|
71122
|
+
if (spawned?.pid) pids.add(spawned.pid);
|
|
71123
|
+
const backend = readSourceHostPid(root);
|
|
71124
|
+
if (backend?.pid) pids.add(backend.pid);
|
|
71125
|
+
for (const pid of pids) {
|
|
71126
|
+
for (const signal of ["SIGTERM", "SIGKILL"]) {
|
|
71127
|
+
try {
|
|
71128
|
+
process.kill(pid, signal);
|
|
71129
|
+
} catch {
|
|
71130
|
+
break;
|
|
71131
|
+
}
|
|
71132
|
+
let dead = false;
|
|
71133
|
+
for (let i3 = 0; i3 < 15; i3 += 1) {
|
|
71134
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
71135
|
+
try {
|
|
71136
|
+
process.kill(pid, 0);
|
|
71137
|
+
} catch {
|
|
71138
|
+
dead = true;
|
|
71139
|
+
break;
|
|
71140
|
+
}
|
|
71141
|
+
}
|
|
71142
|
+
if (dead) break;
|
|
71143
|
+
}
|
|
71144
|
+
}
|
|
71145
|
+
removeSpawnedHostRecord(root);
|
|
71146
|
+
}
|
|
71108
71147
|
function reconcileSpawnedHostPid(root, port) {
|
|
71109
71148
|
try {
|
|
71110
71149
|
const backend = readSourceHostPid(root);
|
|
@@ -75560,7 +75599,7 @@ function sql(strings, ...params) {
|
|
|
75560
75599
|
return new SQL([new StringChunk(str3)]);
|
|
75561
75600
|
}
|
|
75562
75601
|
sql2.raw = raw;
|
|
75563
|
-
function
|
|
75602
|
+
function join15(chunks, separator) {
|
|
75564
75603
|
const result = [];
|
|
75565
75604
|
for (const [i3, chunk] of chunks.entries()) {
|
|
75566
75605
|
if (i3 > 0 && separator !== void 0) {
|
|
@@ -75570,7 +75609,7 @@ function sql(strings, ...params) {
|
|
|
75570
75609
|
}
|
|
75571
75610
|
return new SQL(result);
|
|
75572
75611
|
}
|
|
75573
|
-
sql2.join =
|
|
75612
|
+
sql2.join = join15;
|
|
75574
75613
|
function identifier(value) {
|
|
75575
75614
|
return new Name(value);
|
|
75576
75615
|
}
|
|
@@ -79161,7 +79200,7 @@ var PgSelectQueryBuilderBase = class extends TypedQueryBuilder {
|
|
|
79161
79200
|
return (table, on2) => {
|
|
79162
79201
|
const baseTableName = this.tableName;
|
|
79163
79202
|
const tableName = getTableLikeName(table);
|
|
79164
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
79203
|
+
if (typeof tableName === "string" && this.config.joins?.some((join15) => join15.alias === tableName)) {
|
|
79165
79204
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
79166
79205
|
}
|
|
79167
79206
|
if (!this.isPartialSelect) {
|
|
@@ -80173,7 +80212,7 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
80173
80212
|
createJoin(joinType) {
|
|
80174
80213
|
return (table, on2) => {
|
|
80175
80214
|
const tableName = getTableLikeName(table);
|
|
80176
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
80215
|
+
if (typeof tableName === "string" && this.config.joins.some((join15) => join15.alias === tableName)) {
|
|
80177
80216
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
80178
80217
|
}
|
|
80179
80218
|
if (typeof on2 === "function") {
|
|
@@ -80269,10 +80308,10 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
80269
80308
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
80270
80309
|
fields[tableName] = fromFields;
|
|
80271
80310
|
}
|
|
80272
|
-
for (const
|
|
80273
|
-
const tableName2 = getTableLikeName(
|
|
80274
|
-
if (typeof tableName2 === "string" && !is(
|
|
80275
|
-
const fromFields = this.getTableLikeFields(
|
|
80311
|
+
for (const join15 of this.config.joins) {
|
|
80312
|
+
const tableName2 = getTableLikeName(join15.table);
|
|
80313
|
+
if (typeof tableName2 === "string" && !is(join15.table, SQL)) {
|
|
80314
|
+
const fromFields = this.getTableLikeFields(join15.table);
|
|
80276
80315
|
fields[tableName2] = fromFields;
|
|
80277
80316
|
}
|
|
80278
80317
|
}
|
|
@@ -90401,6 +90440,10 @@ function reportToMarkdown(r) {
|
|
|
90401
90440
|
|
|
90402
90441
|
// ../../packages/engine/src/ownership.ts
|
|
90403
90442
|
init_cjs_shims();
|
|
90443
|
+
var BOT_AUTHOR_RE = /\[bot\]|^(?:github-actions|dependabot|renovate(?:-bot)?|greenkeeper|snyk-bot|mergify|codecov|semantic-release-bot|allcontributors)\b/i;
|
|
90444
|
+
function isBotAuthor(author) {
|
|
90445
|
+
return BOT_AUTHOR_RE.test((author ?? "").trim());
|
|
90446
|
+
}
|
|
90404
90447
|
var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
90405
90448
|
"ts",
|
|
90406
90449
|
"tsx",
|
|
@@ -90507,10 +90550,12 @@ async function estimateOwnership(query, deps) {
|
|
|
90507
90550
|
};
|
|
90508
90551
|
}
|
|
90509
90552
|
const contributors = await gitFileContributors(deps.repoPath, file);
|
|
90510
|
-
const
|
|
90511
|
-
const
|
|
90553
|
+
const humans = contributors.filter((c4) => !isBotAuthor(c4.author));
|
|
90554
|
+
const ranked = humans.length > 0 ? humans : contributors;
|
|
90555
|
+
const total = ranked.reduce((n4, c4) => n4 + c4.commits, 0);
|
|
90556
|
+
const lead = ranked[0] ?? null;
|
|
90512
90557
|
const maintainerShare = total > 0 && lead !== null ? lead.commits / total : 0;
|
|
90513
|
-
const byRecent = [...
|
|
90558
|
+
const byRecent = [...ranked].sort(
|
|
90514
90559
|
(a2, b5) => b5.lastDate < a2.lastDate ? -1 : b5.lastDate > a2.lastDate ? 1 : 0
|
|
90515
90560
|
);
|
|
90516
90561
|
const mostActiveRecent = byRecent[0]?.author ?? null;
|
|
@@ -91250,6 +91295,30 @@ function factorRequestContext(sourceEvidenceIds, graph, request) {
|
|
|
91250
91295
|
reason: `Cause directly implicates the investigated service (${request.service})`
|
|
91251
91296
|
};
|
|
91252
91297
|
}
|
|
91298
|
+
function factorIncidentHistory(sourceEvidenceIds, graph, priorIncidents) {
|
|
91299
|
+
if (priorIncidents.size === 0) return null;
|
|
91300
|
+
const idSet = new Set(sourceEvidenceIds);
|
|
91301
|
+
const codeNodes = graph.nodes.filter(
|
|
91302
|
+
(n4) => (n4.type === "symbol" || n4.type === "file") && n4.evidenceIds.some((eid) => idSet.has(eid))
|
|
91303
|
+
);
|
|
91304
|
+
const matched = [];
|
|
91305
|
+
let totalPrior = 0;
|
|
91306
|
+
for (const n4 of codeNodes) {
|
|
91307
|
+
const count = priorIncidents.get(n4.id);
|
|
91308
|
+
if (count && count > 0) {
|
|
91309
|
+
matched.push(n4);
|
|
91310
|
+
totalPrior += count;
|
|
91311
|
+
}
|
|
91312
|
+
}
|
|
91313
|
+
if (matched.length === 0) return null;
|
|
91314
|
+
const delta = +Math.min(matched.length * 0.04, 0.1).toFixed(3);
|
|
91315
|
+
const names = matched.slice(0, 3).map((n4) => n4.label);
|
|
91316
|
+
return {
|
|
91317
|
+
factor: "incident-history",
|
|
91318
|
+
delta,
|
|
91319
|
+
reason: `Implicated code has a prior-incident history (${totalPrior} prior investigation(s) on ${names.join(", ")}${matched.length > 3 ? ", \u2026" : ""})`
|
|
91320
|
+
};
|
|
91321
|
+
}
|
|
91253
91322
|
function scoreCause(input, ctx) {
|
|
91254
91323
|
const now = ctx.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
91255
91324
|
const idSet = new Set(input.sourceEvidenceIds);
|
|
@@ -91263,7 +91332,8 @@ function scoreCause(input, ctx) {
|
|
|
91263
91332
|
factorSignalStrength(attached),
|
|
91264
91333
|
factorFindingUncertainty(input.sourceEvidenceIds, ctx.findings ?? []),
|
|
91265
91334
|
ctx.providerReliability ? factorProviderReliability(attached, ctx.providerReliability) : null,
|
|
91266
|
-
ctx.request ? factorRequestContext(input.sourceEvidenceIds, ctx.graph, ctx.request) : null
|
|
91335
|
+
ctx.request ? factorRequestContext(input.sourceEvidenceIds, ctx.graph, ctx.request) : null,
|
|
91336
|
+
ctx.priorIncidents ? factorIncidentHistory(input.sourceEvidenceIds, ctx.graph, ctx.priorIncidents) : null
|
|
91267
91337
|
];
|
|
91268
91338
|
const explanations = rawFactors.filter((f5) => f5 !== null);
|
|
91269
91339
|
const totalDelta = explanations.reduce((sum, e) => sum + e.delta, 0);
|
|
@@ -92005,6 +92075,11 @@ function scoreSeed(s5, index2, hintTokens, changedFiles, hintHasCode) {
|
|
|
92005
92075
|
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)) {
|
|
92006
92076
|
score -= 4;
|
|
92007
92077
|
}
|
|
92078
|
+
if (/(^|\/)(examples?|samples?|demos?|fixtures?|sandbox|playground|docs|docs_src|tutorials?)(\/|$)/i.test(
|
|
92079
|
+
s5.filePath
|
|
92080
|
+
)) {
|
|
92081
|
+
score -= 4;
|
|
92082
|
+
}
|
|
92008
92083
|
score += Math.max(0, 5 - index2) * (hintHasCode ? 0.8 : 0.1);
|
|
92009
92084
|
if (s5.score !== void 0) score += s5.score * (hintHasCode ? 8 : 1.5);
|
|
92010
92085
|
if (/\.[jt]sx?$/i.test(s5.name)) score -= 5;
|
|
@@ -92020,13 +92095,24 @@ function scoreSeed(s5, index2, hintTokens, changedFiles, hintHasCode) {
|
|
|
92020
92095
|
}
|
|
92021
92096
|
return score;
|
|
92022
92097
|
}
|
|
92098
|
+
function isDeprioritizedSeedPath(filePath) {
|
|
92099
|
+
return /(^|\/)(tests?|__tests__|spec)\//i.test(filePath) || /(\.|_)(test|spec)\.[jt]sx?$/i.test(filePath) || /(^|\/)test_[^/]*\.py$/i.test(filePath) || /_test\.py$/i.test(filePath) || /(^|\/)(examples?|samples?|demos?|fixtures?|sandbox|playground|docs|docs_src|tutorials?)(\/|$)/i.test(
|
|
92100
|
+
filePath
|
|
92101
|
+
);
|
|
92102
|
+
}
|
|
92023
92103
|
function rankSeeds(seeds, hintTokens, changedFiles, hintHasCode) {
|
|
92024
|
-
|
|
92104
|
+
const scored = seeds.map((symbol, i3) => ({
|
|
92025
92105
|
symbol,
|
|
92026
92106
|
score: scoreSeed(symbol, i3, hintTokens, changedFiles, hintHasCode),
|
|
92027
92107
|
role: seedRole(symbol),
|
|
92028
|
-
i: i3
|
|
92029
|
-
|
|
92108
|
+
i: i3,
|
|
92109
|
+
deprio: isDeprioritizedSeedPath(symbol.filePath)
|
|
92110
|
+
}));
|
|
92111
|
+
const hasReal = scored.some((s5) => !s5.deprio);
|
|
92112
|
+
if (hasReal) {
|
|
92113
|
+
for (const s5 of scored) if (s5.deprio) s5.score -= 1e3;
|
|
92114
|
+
}
|
|
92115
|
+
return scored.sort((a2, b5) => b5.score === a2.score ? a2.i - b5.i : b5.score - a2.score).map(({ symbol, score, role }) => ({ symbol, score, role }));
|
|
92030
92116
|
}
|
|
92031
92117
|
|
|
92032
92118
|
// ../../packages/engine/src/normalize.ts
|
|
@@ -92681,12 +92767,12 @@ async function investigate(input, deps) {
|
|
|
92681
92767
|
const fp = (s5.filePath ?? "").replace(/^\.?\/+/, "");
|
|
92682
92768
|
return fp === scopePrefix || fp.startsWith(scopePrefix + "/");
|
|
92683
92769
|
};
|
|
92684
|
-
const seedSearchLimit = scopePrefix ?
|
|
92685
|
-
const rawSeeds = code ? (await code.searchSymbols(hint, seedSearchLimit)).filter(inScope)
|
|
92770
|
+
const seedSearchLimit = scopePrefix ? 30 : 20;
|
|
92771
|
+
const rawSeeds = code ? (await code.searchSymbols(hint, seedSearchLimit)).filter(inScope) : [];
|
|
92686
92772
|
const hintTokens = [...new Set(tokenize(hint))];
|
|
92687
92773
|
const hintHasCode = /\b(?=[A-Z0-9_]*[0-9_])[A-Z][A-Z0-9_]{3,}\b/.test(hint);
|
|
92688
92774
|
const ranked = rankSeeds(rawSeeds, hintTokens, changedFilePaths, hintHasCode);
|
|
92689
|
-
let seeds = ranked.map((r) => r.symbol);
|
|
92775
|
+
let seeds = ranked.map((r) => r.symbol).slice(0, 5);
|
|
92690
92776
|
let top = seeds[0];
|
|
92691
92777
|
if (top && code && isTypeLikeName(top.name)) {
|
|
92692
92778
|
const base2 = executableBaseName(top.name);
|
|
@@ -93481,11 +93567,12 @@ async function investigate(input, deps) {
|
|
|
93481
93567
|
const causeChains = buildCauseChains(validated, evidence2, graph, label);
|
|
93482
93568
|
const findings2 = [];
|
|
93483
93569
|
if (top && seedEv) {
|
|
93570
|
+
const seedConfidence = seedIsLowConfidence ? Math.min(0.4, top.score ?? 0.3) : Math.min(0.95, Math.max(0.5, top.score ?? 0.75));
|
|
93484
93571
|
findings2.push({
|
|
93485
93572
|
kind: "observation",
|
|
93486
93573
|
title: `Seed resolves to ${label} at ${seedLoc}`,
|
|
93487
93574
|
detail: top.signature ?? void 0,
|
|
93488
|
-
confidence:
|
|
93575
|
+
confidence: +seedConfidence.toFixed(2),
|
|
93489
93576
|
evidenceIds: [seedEv.id]
|
|
93490
93577
|
});
|
|
93491
93578
|
}
|
|
@@ -93867,12 +93954,31 @@ async function investigate(input, deps) {
|
|
|
93867
93954
|
...deps.queue != null ? { queue: 0.9 } : {},
|
|
93868
93955
|
...deps.metrics != null ? { metrics: 0.75 } : {}
|
|
93869
93956
|
};
|
|
93957
|
+
const priorIncidents = /* @__PURE__ */ new Map();
|
|
93958
|
+
try {
|
|
93959
|
+
const priors = await listInvestigationsWithReports(deps.db, 50);
|
|
93960
|
+
for (const row of priors) {
|
|
93961
|
+
const rep = row.report;
|
|
93962
|
+
if (!rep || !Array.isArray(rep.evidence) || rep.evidence.length === 0) continue;
|
|
93963
|
+
if (typeof rep.confidence === "number" && rep.confidence < 0.5) continue;
|
|
93964
|
+
const priorGraph = buildGraph(rep.evidence);
|
|
93965
|
+
const seen = /* @__PURE__ */ new Set();
|
|
93966
|
+
for (const n4 of priorGraph.nodes) {
|
|
93967
|
+
if ((n4.type === "symbol" || n4.type === "file") && !seen.has(n4.id)) {
|
|
93968
|
+
seen.add(n4.id);
|
|
93969
|
+
priorIncidents.set(n4.id, (priorIncidents.get(n4.id) ?? 0) + 1);
|
|
93970
|
+
}
|
|
93971
|
+
}
|
|
93972
|
+
}
|
|
93973
|
+
} catch {
|
|
93974
|
+
}
|
|
93870
93975
|
const rankedCauses = rankCauses(causeInputs, {
|
|
93871
93976
|
evidence: evidence2,
|
|
93872
93977
|
graph,
|
|
93873
93978
|
findings: findings2,
|
|
93874
93979
|
providerReliability,
|
|
93875
|
-
request: { hint: input.hint, service: input.service }
|
|
93980
|
+
request: { hint: input.hint, service: input.service },
|
|
93981
|
+
...priorIncidents.size > 0 ? { priorIncidents } : {}
|
|
93876
93982
|
});
|
|
93877
93983
|
const evidenceConfidence = computeWeightedEvidenceConfidence(
|
|
93878
93984
|
evidence2,
|
|
@@ -94442,8 +94548,53 @@ var EXTERNAL_MARKERS = [
|
|
|
94442
94548
|
"postgres",
|
|
94443
94549
|
"mariadb",
|
|
94444
94550
|
"mysql2",
|
|
94445
|
-
"mssql"
|
|
94551
|
+
"mssql",
|
|
94552
|
+
// Python async DB drivers (HOR-379): the markers above missed several backends real Python
|
|
94553
|
+
// DB libs implement (e.g. `databases` caught sqlalchemy/postgres/psycopg but missed asyncpg,
|
|
94554
|
+
// aiomysql/asyncmy, aiopg, aiosqlite). Their names don't substring-match the sync drivers, so
|
|
94555
|
+
// they need explicit markers. 'sqlite' also covers sqlite3/aiosqlite.
|
|
94556
|
+
"sqlite",
|
|
94557
|
+
"asyncpg",
|
|
94558
|
+
"aiomysql",
|
|
94559
|
+
"asyncmy",
|
|
94560
|
+
"aiopg",
|
|
94561
|
+
"pymysql"
|
|
94446
94562
|
];
|
|
94563
|
+
var TESTY_TOKENS = /* @__PURE__ */ new Set([
|
|
94564
|
+
"test",
|
|
94565
|
+
"tests",
|
|
94566
|
+
"spec",
|
|
94567
|
+
"specs",
|
|
94568
|
+
"example",
|
|
94569
|
+
"examples",
|
|
94570
|
+
"sample",
|
|
94571
|
+
"samples",
|
|
94572
|
+
"demo",
|
|
94573
|
+
"demos",
|
|
94574
|
+
"docs",
|
|
94575
|
+
"doc",
|
|
94576
|
+
"tutorial",
|
|
94577
|
+
"tutorials",
|
|
94578
|
+
"fixture",
|
|
94579
|
+
"fixtures",
|
|
94580
|
+
"e2e"
|
|
94581
|
+
]);
|
|
94582
|
+
function isTestyCommunity(name2) {
|
|
94583
|
+
return name2.toLowerCase().split(/[+\s/_.-]+/).some((t2) => TESTY_TOKENS.has(t2));
|
|
94584
|
+
}
|
|
94585
|
+
function cleanSubsystemName(name2) {
|
|
94586
|
+
let n4 = name2.replace(/^[_.]+/, "").trim();
|
|
94587
|
+
const parts2 = n4.split("+");
|
|
94588
|
+
if (parts2.length === 2) {
|
|
94589
|
+
const [a2, b5] = parts2;
|
|
94590
|
+
if (a2 && b5 && a2.toLowerCase() === b5.toLowerCase()) n4 = a2;
|
|
94591
|
+
}
|
|
94592
|
+
return n4 || name2;
|
|
94593
|
+
}
|
|
94594
|
+
var TEST_EXAMPLE_PATH_RE = /(^|\/)(tests?|__tests__|examples?|samples?|demos?|docs|docs_src|tutorials?|fixtures?|spec|specs)(\/|$)/i;
|
|
94595
|
+
function isTestOrExamplePath(p5) {
|
|
94596
|
+
return TEST_EXAMPLE_PATH_RE.test(p5);
|
|
94597
|
+
}
|
|
94447
94598
|
async function discoverArchitecture(deps) {
|
|
94448
94599
|
const nodeStats = await (async () => {
|
|
94449
94600
|
try {
|
|
@@ -94463,10 +94614,17 @@ async function discoverArchitecture(deps) {
|
|
|
94463
94614
|
const result = await deps.code.cypher(
|
|
94464
94615
|
'MATCH (m)-[r:CodeRelation]->(c:Community) WHERE r.rel_type = "member_of" RETURN c.name, count(m) ORDER BY count(m) DESC'
|
|
94465
94616
|
);
|
|
94466
|
-
|
|
94467
|
-
name: String(row[0] ?? ""),
|
|
94617
|
+
const rows = result.rows.map((row) => ({
|
|
94618
|
+
name: cleanSubsystemName(String(row[0] ?? "")),
|
|
94468
94619
|
members: Number(row[1] ?? 0)
|
|
94469
94620
|
}));
|
|
94621
|
+
rows.sort((a2, b5) => {
|
|
94622
|
+
const at4 = isTestyCommunity(a2.name) ? 1 : 0;
|
|
94623
|
+
const bt2 = isTestyCommunity(b5.name) ? 1 : 0;
|
|
94624
|
+
if (at4 !== bt2) return at4 - bt2;
|
|
94625
|
+
return b5.members - a2.members;
|
|
94626
|
+
});
|
|
94627
|
+
return rows.slice(0, 12);
|
|
94470
94628
|
} catch {
|
|
94471
94629
|
return [];
|
|
94472
94630
|
}
|
|
@@ -94478,20 +94636,22 @@ async function discoverArchitecture(deps) {
|
|
|
94478
94636
|
for (const edge of edges) {
|
|
94479
94637
|
const key = edge.queueName;
|
|
94480
94638
|
if (!byQueue.has(key)) {
|
|
94481
|
-
byQueue.set(key, { producers: /* @__PURE__ */ new
|
|
94639
|
+
byQueue.set(key, { producers: /* @__PURE__ */ new Map(), workers: /* @__PURE__ */ new Map() });
|
|
94482
94640
|
}
|
|
94483
94641
|
const entry2 = byQueue.get(key);
|
|
94484
94642
|
if (edge.producerSymbol != null && edge.producerSymbol !== "") {
|
|
94485
|
-
|
|
94643
|
+
const file = edge.producerFile ?? null;
|
|
94644
|
+
entry2.producers.set(`${edge.producerSymbol}\0${file ?? ""}`, { symbol: edge.producerSymbol, file });
|
|
94486
94645
|
}
|
|
94487
94646
|
if (edge.workerSymbol != null && edge.workerSymbol !== "") {
|
|
94488
|
-
|
|
94647
|
+
const file = edge.workerFile ?? null;
|
|
94648
|
+
entry2.workers.set(`${edge.workerSymbol}\0${file ?? ""}`, { symbol: edge.workerSymbol, file });
|
|
94489
94649
|
}
|
|
94490
94650
|
}
|
|
94491
94651
|
return Array.from(byQueue.entries()).map(([queueName, { producers, workers }]) => ({
|
|
94492
94652
|
queueName,
|
|
94493
|
-
producers: Array.from(producers),
|
|
94494
|
-
workers: Array.from(workers)
|
|
94653
|
+
producers: Array.from(producers.values()),
|
|
94654
|
+
workers: Array.from(workers.values())
|
|
94495
94655
|
}));
|
|
94496
94656
|
} catch {
|
|
94497
94657
|
return [];
|
|
@@ -94527,16 +94687,20 @@ async function discoverArchitecture(deps) {
|
|
|
94527
94687
|
return 0;
|
|
94528
94688
|
}
|
|
94529
94689
|
})();
|
|
94690
|
+
const ownPackage = deps.project?.toLowerCase();
|
|
94530
94691
|
const externalSystems = await (async () => {
|
|
94531
94692
|
try {
|
|
94532
94693
|
const results = await Promise.all(
|
|
94533
94694
|
EXTERNAL_MARKERS.map(async (marker) => {
|
|
94695
|
+
if (ownPackage && marker.toLowerCase() === ownPackage) {
|
|
94696
|
+
return { name: marker, files: 0 };
|
|
94697
|
+
}
|
|
94534
94698
|
try {
|
|
94535
94699
|
const result = await deps.code.cypher(
|
|
94536
|
-
`MATCH (n:File) WHERE n.content CONTAINS "${marker}" RETURN
|
|
94700
|
+
`MATCH (n:File) WHERE n.content CONTAINS "${marker}" RETURN n.file_path LIMIT 500`
|
|
94537
94701
|
);
|
|
94538
|
-
const
|
|
94539
|
-
return { name: marker, files
|
|
94702
|
+
const files = result.rows.map((r) => String(r[0] ?? "")).filter((p5) => p5 !== "" && !isTestOrExamplePath(p5)).length;
|
|
94703
|
+
return { name: marker, files };
|
|
94540
94704
|
} catch {
|
|
94541
94705
|
return { name: marker, files: 0 };
|
|
94542
94706
|
}
|
|
@@ -94563,6 +94727,10 @@ async function discoverArchitecture(deps) {
|
|
|
94563
94727
|
|
|
94564
94728
|
// ../../packages/engine/src/render-architecture.ts
|
|
94565
94729
|
init_cjs_shims();
|
|
94730
|
+
function fmtEndpoints(endpoints2) {
|
|
94731
|
+
if (endpoints2.length === 0) return "(unknown)";
|
|
94732
|
+
return endpoints2.map((e) => e.file ? `${e.symbol} (${e.file.split("/").pop()})` : e.symbol).join(", ");
|
|
94733
|
+
}
|
|
94566
94734
|
function renderArchitecture(m5) {
|
|
94567
94735
|
const lines = [];
|
|
94568
94736
|
lines.push("# Architecture (discovered)");
|
|
@@ -94574,7 +94742,8 @@ function renderArchitecture(m5) {
|
|
|
94574
94742
|
lines.push("(none)");
|
|
94575
94743
|
} else {
|
|
94576
94744
|
for (const s5 of m5.subsystems) {
|
|
94577
|
-
|
|
94745
|
+
const tag = isTestyCommunity(s5.name) ? " (tests)" : "";
|
|
94746
|
+
lines.push(`- ${s5.name} \u2014 ${s5.members} members${tag}`);
|
|
94578
94747
|
}
|
|
94579
94748
|
}
|
|
94580
94749
|
lines.push("");
|
|
@@ -94583,9 +94752,7 @@ function renderArchitecture(m5) {
|
|
|
94583
94752
|
lines.push("(none)");
|
|
94584
94753
|
} else {
|
|
94585
94754
|
for (const b5 of m5.asyncBoundaries) {
|
|
94586
|
-
|
|
94587
|
-
const workers = b5.workers.length > 0 ? b5.workers.join(", ") : "(unknown)";
|
|
94588
|
-
lines.push(`- ${b5.queueName}: ${producers} -> ${workers}`);
|
|
94755
|
+
lines.push(`- ${b5.queueName}: ${fmtEndpoints(b5.producers)} -> ${fmtEndpoints(b5.workers)}`);
|
|
94589
94756
|
}
|
|
94590
94757
|
}
|
|
94591
94758
|
lines.push("");
|
|
@@ -95750,7 +95917,7 @@ function filterArchitecture(architecture, tokens) {
|
|
|
95750
95917
|
...architecture,
|
|
95751
95918
|
subsystems: architecture.subsystems.filter((s5) => matchesArea(s5.name, tokens)),
|
|
95752
95919
|
asyncBoundaries: architecture.asyncBoundaries.filter(
|
|
95753
|
-
(b5) => matchesArea(b5.queueName, tokens) || b5.producers.some((p5) => matchesArea(p5, tokens)) || b5.workers.some((w4) => matchesArea(w4, tokens))
|
|
95920
|
+
(b5) => matchesArea(b5.queueName, tokens) || b5.producers.some((p5) => matchesArea(p5.symbol, tokens)) || b5.workers.some((w4) => matchesArea(w4.symbol, tokens))
|
|
95754
95921
|
),
|
|
95755
95922
|
keyFlows: architecture.keyFlows.filter((f5) => matchesArea(f5, tokens)),
|
|
95756
95923
|
externalSystems: architecture.externalSystems.filter((e) => matchesArea(e.name, tokens))
|
|
@@ -95864,9 +96031,8 @@ function renderOnboarding(g4) {
|
|
|
95864
96031
|
if (g4.architecture.asyncBoundaries.length > 0) {
|
|
95865
96032
|
lines.push("Async boundaries:");
|
|
95866
96033
|
for (const b5 of g4.architecture.asyncBoundaries) {
|
|
95867
|
-
const
|
|
95868
|
-
|
|
95869
|
-
lines.push(`- ${b5.queueName}: ${producers} -> ${workers}`);
|
|
96034
|
+
const fmt = (xs) => xs.length > 0 ? xs.map((x4) => x4.file ? `${x4.symbol} (${x4.file.split("/").pop()})` : x4.symbol).join(", ") : "(unknown)";
|
|
96035
|
+
lines.push(`- ${b5.queueName}: ${fmt(b5.producers)} -> ${fmt(b5.workers)}`);
|
|
95870
96036
|
}
|
|
95871
96037
|
} else {
|
|
95872
96038
|
lines.push("No async boundaries detected.");
|
|
@@ -97212,27 +97378,27 @@ function extractQueueGraph(input) {
|
|
|
97212
97378
|
}
|
|
97213
97379
|
}
|
|
97214
97380
|
const producers = [];
|
|
97215
|
-
for (const
|
|
97216
|
-
const loopVarEnums = loopVarEnumsFor(
|
|
97217
|
-
for (const m5 of
|
|
97381
|
+
for (const pc50 of input.producerClasses) {
|
|
97382
|
+
const loopVarEnums = loopVarEnumsFor(pc50.content);
|
|
97383
|
+
for (const m5 of pc50.content.matchAll(INJECT_QUEUE_RE)) {
|
|
97218
97384
|
const queue = m5[1] ?? "";
|
|
97219
|
-
if (queue) producers.push({ queue, symbol:
|
|
97385
|
+
if (queue) producers.push({ queue, symbol: pc50.name, file: pc50.filePath });
|
|
97220
97386
|
}
|
|
97221
|
-
for (const m5 of
|
|
97387
|
+
for (const m5 of pc50.content.matchAll(NEW_BULL_RE)) {
|
|
97222
97388
|
if (m5[2] !== "Queue") continue;
|
|
97223
97389
|
const queues2 = resolveArg(m5[3] ?? "", constMap, enums, loopVarEnums);
|
|
97224
97390
|
for (const queue of queues2) {
|
|
97225
97391
|
producers.push({
|
|
97226
97392
|
queue,
|
|
97227
|
-
symbol: (m5[1] ??
|
|
97228
|
-
file:
|
|
97393
|
+
symbol: (m5[1] ?? pc50.name) || baseName(pc50.filePath),
|
|
97394
|
+
file: pc50.filePath
|
|
97229
97395
|
});
|
|
97230
97396
|
}
|
|
97231
97397
|
}
|
|
97232
|
-
for (const m5 of
|
|
97398
|
+
for (const m5 of pc50.content.matchAll(QUEUE_NAME_CONST_RE)) {
|
|
97233
97399
|
const ident = m5[1] ?? "";
|
|
97234
97400
|
const queue = m5[2] ?? "";
|
|
97235
|
-
if (queue) producers.push({ queue, symbol: ident || baseName(
|
|
97401
|
+
if (queue) producers.push({ queue, symbol: ident || baseName(pc50.filePath), file: pc50.filePath });
|
|
97236
97402
|
}
|
|
97237
97403
|
}
|
|
97238
97404
|
const workers = [];
|
|
@@ -97299,6 +97465,60 @@ function extractQueueGraph(input) {
|
|
|
97299
97465
|
}
|
|
97300
97466
|
return { queues, producers, workers, edges };
|
|
97301
97467
|
}
|
|
97468
|
+
var CELERY_TASK_DEF_RE = /@(?:[\w]+\.)*(?:shared_task|db_periodic_task|periodic_task|db_task|task)\b[^\n]*(?:\n[ \t]*@[^\n]*)*\n[ \t]*(?:async[ \t]+)?def[ \t]+(\w+)/g;
|
|
97469
|
+
var CELERY_ENQUEUE_RE = /([A-Za-z_]\w*)\s*\.\s*(?:delay|apply_async|schedule)\s*\(/g;
|
|
97470
|
+
function extractCeleryQueueGraph(nodes) {
|
|
97471
|
+
const producers = [];
|
|
97472
|
+
const workers = [];
|
|
97473
|
+
const FILE_NODE_RE = /\.(py|pyi|js|jsx|ts|tsx|mjs|cjs)$/i;
|
|
97474
|
+
for (const n4 of nodes) {
|
|
97475
|
+
for (const m5 of n4.content.matchAll(CELERY_TASK_DEF_RE)) {
|
|
97476
|
+
const task = m5[1];
|
|
97477
|
+
if (task) workers.push({ queue: task, symbol: task, file: n4.filePath });
|
|
97478
|
+
}
|
|
97479
|
+
if (FILE_NODE_RE.test(n4.name)) continue;
|
|
97480
|
+
for (const m5 of n4.content.matchAll(CELERY_ENQUEUE_RE)) {
|
|
97481
|
+
const task = m5[1];
|
|
97482
|
+
if (task) producers.push({ queue: task, symbol: n4.name || baseName(n4.filePath), file: n4.filePath });
|
|
97483
|
+
}
|
|
97484
|
+
}
|
|
97485
|
+
const taskQueues = new Set(workers.map((w4) => w4.queue));
|
|
97486
|
+
const queues = [...taskQueues].sort();
|
|
97487
|
+
const edges = [];
|
|
97488
|
+
for (const q2 of queues) {
|
|
97489
|
+
const P5 = dedupeBySymbolFile(producers.filter((p5) => p5.queue === q2));
|
|
97490
|
+
const W5 = dedupeBySymbolFile(workers.filter((w4) => w4.queue === q2));
|
|
97491
|
+
if (P5.length) {
|
|
97492
|
+
for (const p5 of P5) {
|
|
97493
|
+
for (const w4 of W5) {
|
|
97494
|
+
edges.push({
|
|
97495
|
+
queueName: q2,
|
|
97496
|
+
producerSymbol: p5.symbol,
|
|
97497
|
+
producerFile: p5.file,
|
|
97498
|
+
workerSymbol: w4.symbol,
|
|
97499
|
+
workerFile: w4.file
|
|
97500
|
+
});
|
|
97501
|
+
}
|
|
97502
|
+
}
|
|
97503
|
+
} else {
|
|
97504
|
+
for (const w4 of W5) {
|
|
97505
|
+
edges.push({
|
|
97506
|
+
queueName: q2,
|
|
97507
|
+
producerSymbol: null,
|
|
97508
|
+
producerFile: null,
|
|
97509
|
+
workerSymbol: w4.symbol,
|
|
97510
|
+
workerFile: w4.file
|
|
97511
|
+
});
|
|
97512
|
+
}
|
|
97513
|
+
}
|
|
97514
|
+
}
|
|
97515
|
+
return {
|
|
97516
|
+
queues,
|
|
97517
|
+
producers: producers.filter((p5) => taskQueues.has(p5.queue)),
|
|
97518
|
+
workers,
|
|
97519
|
+
edges
|
|
97520
|
+
};
|
|
97521
|
+
}
|
|
97302
97522
|
function dedupeBySymbolFile(rows) {
|
|
97303
97523
|
const seen = /* @__PURE__ */ new Set();
|
|
97304
97524
|
const out2 = [];
|
|
@@ -97332,7 +97552,16 @@ async function stitch(client, db, opts = {}) {
|
|
|
97332
97552
|
content: String(r[2] ?? "")
|
|
97333
97553
|
}));
|
|
97334
97554
|
const graph = extractQueueGraph({ producerClasses, workerFiles });
|
|
97335
|
-
const
|
|
97555
|
+
const celeryRows = (await client.cypher(
|
|
97556
|
+
'MATCH (n) WHERE n.content CONTAINS ".delay(" OR n.content CONTAINS ".apply_async(" OR n.content CONTAINS ".schedule(" OR n.content CONTAINS "@shared_task" OR n.content CONTAINS "@task" OR n.content CONTAINS ".task" OR n.content CONTAINS "periodic_task" OR n.content CONTAINS "db_task" RETURN n.name, n.file_path, n.content'
|
|
97557
|
+
)).rows;
|
|
97558
|
+
const celeryNodes = celeryRows.map((r) => ({
|
|
97559
|
+
name: String(r[0] ?? ""),
|
|
97560
|
+
filePath: String(r[1] ?? ""),
|
|
97561
|
+
content: String(r[2] ?? "")
|
|
97562
|
+
}));
|
|
97563
|
+
const celery = extractCeleryQueueGraph(celeryNodes);
|
|
97564
|
+
const edges = [...graph.edges, ...celery.edges].map((e) => ({
|
|
97336
97565
|
queueName: e.queueName,
|
|
97337
97566
|
producerSymbol: e.producerSymbol,
|
|
97338
97567
|
producerFile: e.producerFile,
|
|
@@ -97343,9 +97572,9 @@ async function stitch(client, db, opts = {}) {
|
|
|
97343
97572
|
}));
|
|
97344
97573
|
await replaceQueueEdges(db, edges, { project: opts.project });
|
|
97345
97574
|
return {
|
|
97346
|
-
queues: graph.queues.length,
|
|
97347
|
-
producers: graph.producers.length,
|
|
97348
|
-
workers: graph.workers.length,
|
|
97575
|
+
queues: graph.queues.length + celery.queues.length,
|
|
97576
|
+
producers: graph.producers.length + celery.producers.length,
|
|
97577
|
+
workers: graph.workers.length + celery.workers.length,
|
|
97349
97578
|
edges: edges.length
|
|
97350
97579
|
};
|
|
97351
97580
|
}
|
|
@@ -97533,6 +97762,7 @@ async function runIndex(opts) {
|
|
|
97533
97762
|
console.log(import_picocolors3.default.dim(" already analyzed"));
|
|
97534
97763
|
}
|
|
97535
97764
|
const priorPort = parseHostPort(configuredHost ?? "") ?? parseHostPort(readSourceHostUrl(root) ?? "");
|
|
97765
|
+
await killSpawnedHost(root);
|
|
97536
97766
|
const MAX_PORT_ATTEMPTS = 4;
|
|
97537
97767
|
let started = false;
|
|
97538
97768
|
for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS && !started; attempt += 1) {
|
|
@@ -97557,7 +97787,7 @@ async function runIndex(opts) {
|
|
|
97557
97787
|
reconcileSpawnedHostPid(root, port);
|
|
97558
97788
|
started = true;
|
|
97559
97789
|
} else {
|
|
97560
|
-
|
|
97790
|
+
await killSpawnedHost(root);
|
|
97561
97791
|
}
|
|
97562
97792
|
}
|
|
97563
97793
|
if (!started) {
|
|
@@ -110080,7 +110310,7 @@ function formatAge(ms) {
|
|
|
110080
110310
|
|
|
110081
110311
|
// ../../packages/cli/src/commands/investigate.ts
|
|
110082
110312
|
init_cjs_shims();
|
|
110083
|
-
var
|
|
110313
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
110084
110314
|
|
|
110085
110315
|
// ../../packages/cli/src/lib/cloud/investigation-sync.ts
|
|
110086
110316
|
init_cjs_shims();
|
|
@@ -110498,9 +110728,110 @@ async function maybePromptFeedback(opts) {
|
|
|
110498
110728
|
}
|
|
110499
110729
|
}
|
|
110500
110730
|
|
|
110501
|
-
// ../../packages/cli/src/lib/
|
|
110731
|
+
// ../../packages/cli/src/lib/freshness.ts
|
|
110502
110732
|
init_cjs_shims();
|
|
110733
|
+
var import_node_child_process5 = require("child_process");
|
|
110734
|
+
var import_node_fs15 = require("fs");
|
|
110735
|
+
var import_node_path16 = require("path");
|
|
110736
|
+
var import_node_util4 = require("util");
|
|
110503
110737
|
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
110738
|
+
var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process5.execFile);
|
|
110739
|
+
async function commitsSince(repoRoot, sinceIso) {
|
|
110740
|
+
try {
|
|
110741
|
+
const { stdout } = await execFileAsync(
|
|
110742
|
+
"git",
|
|
110743
|
+
["-C", repoRoot, "rev-list", "--count", `--since=${sinceIso}`, "HEAD"],
|
|
110744
|
+
{ timeout: 5e3 }
|
|
110745
|
+
);
|
|
110746
|
+
const n4 = parseInt(stdout.trim(), 10);
|
|
110747
|
+
return Number.isFinite(n4) ? n4 : null;
|
|
110748
|
+
} catch {
|
|
110749
|
+
return null;
|
|
110750
|
+
}
|
|
110751
|
+
}
|
|
110752
|
+
var RUNTIME_SOURCES = /* @__PURE__ */ new Set(["logs", "metrics", "state", "queue"]);
|
|
110753
|
+
var STALE_INDEX_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
110754
|
+
function readIndexMeta(repoRoot) {
|
|
110755
|
+
try {
|
|
110756
|
+
const raw = (0, import_node_fs15.readFileSync)((0, import_node_path16.join)(repoRoot, ".horus", "source", "meta.json"), "utf8");
|
|
110757
|
+
const j3 = JSON.parse(raw);
|
|
110758
|
+
return {
|
|
110759
|
+
lastIndexedAt: typeof j3.last_indexed_at === "string" ? j3.last_indexed_at : void 0,
|
|
110760
|
+
version: typeof j3.version === "string" ? j3.version : void 0,
|
|
110761
|
+
stats: j3.stats ?? void 0
|
|
110762
|
+
};
|
|
110763
|
+
} catch {
|
|
110764
|
+
return null;
|
|
110765
|
+
}
|
|
110766
|
+
}
|
|
110767
|
+
function humanAge(ms) {
|
|
110768
|
+
const s5 = Math.max(0, Math.floor(ms / 1e3));
|
|
110769
|
+
if (s5 < 60) return `${s5}s`;
|
|
110770
|
+
const m5 = Math.floor(s5 / 60);
|
|
110771
|
+
if (m5 < 60) return `${m5}m`;
|
|
110772
|
+
const h3 = Math.floor(m5 / 60);
|
|
110773
|
+
if (h3 < 24) return `${h3}h`;
|
|
110774
|
+
return `${Math.floor(h3 / 24)}d`;
|
|
110775
|
+
}
|
|
110776
|
+
function computeFreshness(opts) {
|
|
110777
|
+
const now = Date.parse(opts.nowIso);
|
|
110778
|
+
const meta = opts.meta !== void 0 ? opts.meta : readIndexMeta(opts.repoRoot);
|
|
110779
|
+
const commitsSinceIndex = opts.commitsSinceIndex ?? null;
|
|
110780
|
+
const caveats = [];
|
|
110781
|
+
let indexAgeMs = null;
|
|
110782
|
+
let indexAgeLabel = "index age unknown";
|
|
110783
|
+
let indexStale = false;
|
|
110784
|
+
const indexedAt = meta?.lastIndexedAt ? Date.parse(meta.lastIndexedAt) : NaN;
|
|
110785
|
+
if (!Number.isNaN(indexedAt)) {
|
|
110786
|
+
indexAgeMs = Math.max(0, now - indexedAt);
|
|
110787
|
+
indexAgeLabel = `indexed ${humanAge(indexAgeMs)} ago`;
|
|
110788
|
+
indexStale = indexAgeMs > STALE_INDEX_MS;
|
|
110789
|
+
if (indexStale) {
|
|
110790
|
+
caveats.push(
|
|
110791
|
+
`code index is ${humanAge(indexAgeMs)} old \u2014 re-run \`horus index\` so analysis reflects current code`
|
|
110792
|
+
);
|
|
110793
|
+
}
|
|
110794
|
+
} else {
|
|
110795
|
+
caveats.push("code index age unknown (no .horus/source/meta.json) \u2014 freshness unverified");
|
|
110796
|
+
}
|
|
110797
|
+
if (typeof commitsSinceIndex === "number" && commitsSinceIndex > 0) {
|
|
110798
|
+
caveats.push(
|
|
110799
|
+
`${commitsSinceIndex} commit(s) since the last index \u2014 re-run \`horus index\` so analysis reflects current code`
|
|
110800
|
+
);
|
|
110801
|
+
}
|
|
110802
|
+
const runtime = opts.evidence.filter((e) => e.source && RUNTIME_SOURCES.has(e.source));
|
|
110803
|
+
const runtimeSources = [...new Set(runtime.map((e) => e.source))].sort();
|
|
110804
|
+
const stamps = runtime.map((e) => e.timestamp ? Date.parse(e.timestamp) : NaN).filter((n4) => !Number.isNaN(n4)).sort((a2, b5) => a2 - b5);
|
|
110805
|
+
const runtimeWindow = stamps.length > 0 ? { fromIso: new Date(stamps[0]).toISOString(), toIso: new Date(stamps[stamps.length - 1]).toISOString() } : null;
|
|
110806
|
+
if (runtime.length === 0) {
|
|
110807
|
+
caveats.push(
|
|
110808
|
+
"no runtime evidence \u2014 source-only investigation; connect logs/metrics/queues for deeper grounding"
|
|
110809
|
+
);
|
|
110810
|
+
}
|
|
110811
|
+
return {
|
|
110812
|
+
indexAgeMs,
|
|
110813
|
+
indexAgeLabel,
|
|
110814
|
+
indexStale,
|
|
110815
|
+
commitsSinceIndex,
|
|
110816
|
+
runtimeWindow,
|
|
110817
|
+
runtimeSources,
|
|
110818
|
+
caveats
|
|
110819
|
+
};
|
|
110820
|
+
}
|
|
110821
|
+
function renderFreshness(f5) {
|
|
110822
|
+
const lines = [];
|
|
110823
|
+
const behind = f5.commitsSinceIndex && f5.commitsSinceIndex > 0 ? ` (${f5.commitsSinceIndex} behind)` : "";
|
|
110824
|
+
const label = f5.indexAgeLabel + behind;
|
|
110825
|
+
const idx = f5.indexStale || behind ? import_picocolors9.default.yellow(label) : label;
|
|
110826
|
+
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";
|
|
110827
|
+
lines.push(import_picocolors9.default.dim(`Freshness: code ${idx} \xB7 ${runtime}`));
|
|
110828
|
+
for (const c4 of f5.caveats) lines.push(import_picocolors9.default.yellow(` \u26A0 ${c4}`));
|
|
110829
|
+
return lines.join("\n");
|
|
110830
|
+
}
|
|
110831
|
+
|
|
110832
|
+
// ../../packages/cli/src/lib/investigation-runner.ts
|
|
110833
|
+
init_cjs_shims();
|
|
110834
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
110504
110835
|
async function buildInvestigationContext(renv, opts) {
|
|
110505
110836
|
const log = opts.log ?? ((l2) => console.error(l2));
|
|
110506
110837
|
const code = codeForEnv(renv);
|
|
@@ -110512,20 +110843,20 @@ async function buildInvestigationContext(renv, opts) {
|
|
|
110512
110843
|
const sourceUrl = renv.repositories[0]?.sourceHostUrl;
|
|
110513
110844
|
let health = await code.health();
|
|
110514
110845
|
if (!health.ok && sourceUrl) {
|
|
110515
|
-
log(
|
|
110846
|
+
log(import_picocolors10.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`));
|
|
110516
110847
|
const healed = await ensureSourceHost(renv.path, sourceUrl);
|
|
110517
110848
|
if (healed.ok) {
|
|
110518
|
-
log(
|
|
110849
|
+
log(import_picocolors10.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
|
|
110519
110850
|
health = await code.health();
|
|
110520
110851
|
} else {
|
|
110521
|
-
log(
|
|
110852
|
+
log(import_picocolors10.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
|
|
110522
110853
|
}
|
|
110523
110854
|
}
|
|
110524
110855
|
const runtimeOnly = !health.ok;
|
|
110525
110856
|
if (runtimeOnly) {
|
|
110526
110857
|
log(
|
|
110527
|
-
|
|
110528
|
-
`Proceeding in runtime-only mode \u2014 no source intelligence. Run ${
|
|
110858
|
+
import_picocolors10.default.yellow(
|
|
110859
|
+
`Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors10.default.bold("horus index")} for a full (code-aware) investigation.`
|
|
110529
110860
|
)
|
|
110530
110861
|
);
|
|
110531
110862
|
}
|
|
@@ -110680,37 +111011,37 @@ function renderStoredAIJudgment(judgment, write = (l2) => console.log(l2)) {
|
|
|
110680
111011
|
const sep = "\u2500".repeat(60);
|
|
110681
111012
|
write(`
|
|
110682
111013
|
${sep}`);
|
|
110683
|
-
write(
|
|
111014
|
+
write(import_picocolors11.default.bold("AI Judgment") + import_picocolors11.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
|
|
110684
111015
|
write(sep);
|
|
110685
111016
|
if (judgment.hypothesisJudgments && judgment.hypothesisJudgments.length > 0) {
|
|
110686
|
-
write(
|
|
111017
|
+
write(import_picocolors11.default.bold("Hypothesis verdicts:"));
|
|
110687
111018
|
for (const j3 of judgment.hypothesisJudgments) {
|
|
110688
|
-
const verdictColor = j3.verdict === "supported" ?
|
|
110689
|
-
write(` ${verdictColor(`[${j3.verdict}]`)} ${j3.category}` +
|
|
110690
|
-
write(
|
|
111019
|
+
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;
|
|
111020
|
+
write(` ${verdictColor(`[${j3.verdict}]`)} ${j3.category}` + import_picocolors11.default.dim(` (${(j3.confidence * 100).toFixed(0)}%)`));
|
|
111021
|
+
write(import_picocolors11.default.dim(` ${j3.rationale}`));
|
|
110691
111022
|
}
|
|
110692
111023
|
write("");
|
|
110693
111024
|
}
|
|
110694
111025
|
if (judgment.rootCauseAssessment) {
|
|
110695
111026
|
const rca = judgment.rootCauseAssessment;
|
|
110696
|
-
const uncertaintyColor = rca.uncertainty === "low" ?
|
|
110697
|
-
write(
|
|
111027
|
+
const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors11.default.green : rca.uncertainty === "medium" ? import_picocolors11.default.yellow : import_picocolors11.default.red;
|
|
111028
|
+
write(import_picocolors11.default.bold("Root cause assessment:") + import_picocolors11.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
|
|
110698
111029
|
write(rca.summary);
|
|
110699
111030
|
if (rca.citedEvidenceIds.length > 0) {
|
|
110700
|
-
write(
|
|
111031
|
+
write(import_picocolors11.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
|
|
110701
111032
|
}
|
|
110702
111033
|
write("");
|
|
110703
111034
|
}
|
|
110704
|
-
write(
|
|
110705
|
-
write(
|
|
111035
|
+
write(import_picocolors11.default.bold("What:") + " " + judgment.what);
|
|
111036
|
+
write(import_picocolors11.default.bold("Why:") + " " + judgment.why);
|
|
110706
111037
|
if (judgment.whereNext.length > 0) {
|
|
110707
|
-
write(
|
|
111038
|
+
write(import_picocolors11.default.bold("Next steps:"));
|
|
110708
111039
|
for (const step of judgment.whereNext) {
|
|
110709
111040
|
write(` \u2022 ${step}`);
|
|
110710
111041
|
}
|
|
110711
111042
|
}
|
|
110712
111043
|
if (judgment.citations.length > 0) {
|
|
110713
|
-
write(
|
|
111044
|
+
write(import_picocolors11.default.dim(`
|
|
110714
111045
|
Cited evidence: ${judgment.citations.map((c4) => c4.evidenceId).join(", ")}`));
|
|
110715
111046
|
}
|
|
110716
111047
|
}
|
|
@@ -110736,7 +111067,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110736
111067
|
try {
|
|
110737
111068
|
renv = resolveEnvironment(config2, { project: projectName, env: opts.env });
|
|
110738
111069
|
} catch (err2) {
|
|
110739
|
-
console.error(
|
|
111070
|
+
console.error(import_picocolors11.default.red(err2.message));
|
|
110740
111071
|
return 1;
|
|
110741
111072
|
}
|
|
110742
111073
|
const repoRoot = repoRootOrCwd(renv.path);
|
|
@@ -110745,8 +111076,8 @@ async function runInvestigate(hint, opts) {
|
|
|
110745
111076
|
const cloudSession = cloudActive ? authedClient() : null;
|
|
110746
111077
|
if (cloudActive && !cloudSession) {
|
|
110747
111078
|
console.error(
|
|
110748
|
-
|
|
110749
|
-
`This repo is linked to Horus Cloud but you are not logged in \u2014 running locally only. Run ${
|
|
111079
|
+
import_picocolors11.default.yellow(
|
|
111080
|
+
`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.`
|
|
110750
111081
|
)
|
|
110751
111082
|
);
|
|
110752
111083
|
}
|
|
@@ -110757,7 +111088,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110757
111088
|
...opts.service !== void 0 ? { service: opts.service } : {}
|
|
110758
111089
|
});
|
|
110759
111090
|
} catch (err2) {
|
|
110760
|
-
console.error(
|
|
111091
|
+
console.error(import_picocolors11.default.red(err2.message));
|
|
110761
111092
|
return 1;
|
|
110762
111093
|
}
|
|
110763
111094
|
try {
|
|
@@ -110774,8 +111105,24 @@ async function runInvestigate(hint, opts) {
|
|
|
110774
111105
|
);
|
|
110775
111106
|
const db = ctx.dbHandle.db;
|
|
110776
111107
|
const format = opts.json ? "json" : opts.format ?? "text";
|
|
110777
|
-
const
|
|
110778
|
-
|
|
111108
|
+
const fmeta = readIndexMeta(repoRoot);
|
|
111109
|
+
const commitsSinceIndex = fmeta?.lastIndexedAt ? await commitsSince(repoRoot, fmeta.lastIndexedAt) : null;
|
|
111110
|
+
const freshness = computeFreshness({
|
|
111111
|
+
repoRoot,
|
|
111112
|
+
evidence: report.evidence,
|
|
111113
|
+
nowIso: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111114
|
+
meta: fmeta,
|
|
111115
|
+
commitsSinceIndex
|
|
111116
|
+
});
|
|
111117
|
+
if (format === "json") {
|
|
111118
|
+
const obj = JSON.parse(reportToJSON(report));
|
|
111119
|
+
obj.freshness = freshness;
|
|
111120
|
+
console.log(JSON.stringify(obj, null, 2));
|
|
111121
|
+
} else {
|
|
111122
|
+
console.log(format === "markdown" || format === "md" ? reportToMarkdown(report) : renderReport(report));
|
|
111123
|
+
console.log("");
|
|
111124
|
+
console.log(renderFreshness(freshness));
|
|
111125
|
+
}
|
|
110779
111126
|
if (opts.ai && format !== "json") {
|
|
110780
111127
|
const ai = resolveAiSettings(config2);
|
|
110781
111128
|
const model = opts.aiModel ?? ai.model ?? "claude-opus-4-8";
|
|
@@ -110783,7 +111130,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110783
111130
|
model,
|
|
110784
111131
|
...ai.anthropicApiKey !== void 0 ? { apiKey: ai.anthropicApiKey } : {}
|
|
110785
111132
|
});
|
|
110786
|
-
console.log(
|
|
111133
|
+
console.log(import_picocolors11.default.dim(`[ai] model: ${model}`));
|
|
110787
111134
|
const narrativeInput = buildNarrativeInput(report);
|
|
110788
111135
|
const { output, fromProvider, degraded, validationErrors } = await renderNarrative(
|
|
110789
111136
|
narrativeInput,
|
|
@@ -110799,24 +111146,24 @@ async function runInvestigate(hint, opts) {
|
|
|
110799
111146
|
renderStoredAIJudgment(stored);
|
|
110800
111147
|
} else if (degraded) {
|
|
110801
111148
|
if (validationErrors && validationErrors.length > 0) {
|
|
110802
|
-
console.error(
|
|
111149
|
+
console.error(import_picocolors11.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
|
|
110803
111150
|
}
|
|
110804
111151
|
console.log("");
|
|
110805
|
-
console.log(
|
|
110806
|
-
console.log(
|
|
111152
|
+
console.log(import_picocolors11.default.dim("\u2500".repeat(60)));
|
|
111153
|
+
console.log(import_picocolors11.default.bold("AI narrative ") + import_picocolors11.default.yellow("(unstructured \u2014 not validated)"));
|
|
110807
111154
|
if (output.what?.trim()) {
|
|
110808
|
-
console.log(
|
|
111155
|
+
console.log(import_picocolors11.default.bold("\nWhat:"), output.what.trim());
|
|
110809
111156
|
}
|
|
110810
111157
|
if (output.why?.trim()) {
|
|
110811
|
-
console.log(
|
|
111158
|
+
console.log(import_picocolors11.default.bold("\nWhy:"), output.why.trim());
|
|
110812
111159
|
}
|
|
110813
111160
|
if (output.whereNext.length > 0) {
|
|
110814
|
-
console.log(
|
|
111161
|
+
console.log(import_picocolors11.default.bold("\nNext:"));
|
|
110815
111162
|
for (const a2 of output.whereNext) console.log(` - ${a2}`);
|
|
110816
111163
|
}
|
|
110817
111164
|
} else {
|
|
110818
111165
|
const reason = classifyAIFailure(validationErrors?.[0]);
|
|
110819
|
-
console.error(
|
|
111166
|
+
console.error(import_picocolors11.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
|
|
110820
111167
|
}
|
|
110821
111168
|
}
|
|
110822
111169
|
track({
|
|
@@ -110847,7 +111194,7 @@ async function runInvestigate(hint, opts) {
|
|
|
110847
111194
|
if (cloudActive && cloudSession && cloudCfg) {
|
|
110848
111195
|
try {
|
|
110849
111196
|
const refs = await uploadInvestigationToCloud(cloudSession.client, cloudCfg, report);
|
|
110850
|
-
const cloudNote =
|
|
111197
|
+
const cloudNote = import_picocolors11.default.dim(`[cloud] investigation saved: ${refs.investigationId}`);
|
|
110851
111198
|
if (format === "json") console.error(cloudNote);
|
|
110852
111199
|
else console.log(cloudNote);
|
|
110853
111200
|
} catch (err2) {
|
|
@@ -110857,16 +111204,16 @@ async function runInvestigate(hint, opts) {
|
|
|
110857
111204
|
if (format !== "json") {
|
|
110858
111205
|
if (report.persisted === false) {
|
|
110859
111206
|
console.error(
|
|
110860
|
-
|
|
111207
|
+
import_picocolors11.default.yellow(`
|
|
110861
111208
|
\u26A0 Results were not saved \u2014 the investigation database was unreachable.`)
|
|
110862
111209
|
);
|
|
110863
111210
|
console.error(
|
|
110864
|
-
|
|
111211
|
+
import_picocolors11.default.dim(
|
|
110865
111212
|
` This report is display-only; \`horus ask\` won't find it. Start the database and re-run to persist.`
|
|
110866
111213
|
)
|
|
110867
111214
|
);
|
|
110868
111215
|
} else {
|
|
110869
|
-
console.log(
|
|
111216
|
+
console.log(import_picocolors11.default.dim(`
|
|
110870
111217
|
Ask a follow-up: horus ask ${report.id} "<question>"`));
|
|
110871
111218
|
}
|
|
110872
111219
|
await maybePromptFeedback({
|
|
@@ -110880,15 +111227,15 @@ Ask a follow-up: horus ask ${report.id} "<question>"`));
|
|
|
110880
111227
|
return 0;
|
|
110881
111228
|
} catch (err2) {
|
|
110882
111229
|
const msg = err2?.message || String(err2);
|
|
110883
|
-
console.error(
|
|
110884
|
-
if (process.env.HORUS_DEBUG) console.error(
|
|
111230
|
+
console.error(import_picocolors11.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
|
|
111231
|
+
if (process.env.HORUS_DEBUG) console.error(import_picocolors11.default.dim(err2?.stack ?? String(err2)));
|
|
110885
111232
|
return 1;
|
|
110886
111233
|
}
|
|
110887
111234
|
}
|
|
110888
111235
|
|
|
110889
111236
|
// ../../packages/cli/src/commands/watch.ts
|
|
110890
111237
|
init_cjs_shims();
|
|
110891
|
-
var
|
|
111238
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
110892
111239
|
function detectNewSentryIncidents(issues, seen) {
|
|
110893
111240
|
const fresh = [];
|
|
110894
111241
|
for (const issue2 of issues) {
|
|
@@ -110970,21 +111317,21 @@ async function runWatch(opts) {
|
|
|
110970
111317
|
try {
|
|
110971
111318
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
110972
111319
|
} catch (err2) {
|
|
110973
|
-
console.error(
|
|
111320
|
+
console.error(import_picocolors12.default.red(err2.message));
|
|
110974
111321
|
return 1;
|
|
110975
111322
|
}
|
|
110976
111323
|
let ctx;
|
|
110977
111324
|
try {
|
|
110978
111325
|
ctx = await buildInvestigationContext(renv, { databaseUrl: config2.database.url });
|
|
110979
111326
|
} catch (err2) {
|
|
110980
|
-
console.error(
|
|
111327
|
+
console.error(import_picocolors12.default.red(err2.message));
|
|
110981
111328
|
return 1;
|
|
110982
111329
|
}
|
|
110983
111330
|
const requested = opts.source ?? "auto";
|
|
110984
111331
|
const source = resolveSource(requested, ctx);
|
|
110985
111332
|
if (source === null) {
|
|
110986
111333
|
console.error(
|
|
110987
|
-
|
|
111334
|
+
import_picocolors12.default.red(
|
|
110988
111335
|
requested === "auto" ? `No Sentry or Elasticsearch connector configured for "${renv.project}" / "${renv.env}".` : `No ${requested} connector configured for "${renv.project}" / "${renv.env}".`
|
|
110989
111336
|
)
|
|
110990
111337
|
);
|
|
@@ -110994,7 +111341,7 @@ async function runWatch(opts) {
|
|
|
110994
111341
|
const intervalSec = Math.max(1, Number(opts.interval) || 60);
|
|
110995
111342
|
const seen = /* @__PURE__ */ new Set();
|
|
110996
111343
|
console.error(
|
|
110997
|
-
|
|
111344
|
+
import_picocolors12.default.dim(
|
|
110998
111345
|
`[watch] source: ${source} \xB7 project: ${renv.project} \xB7 env: ${renv.env}` + (opts.once ? " \xB7 mode: once" : ` \xB7 interval: ${intervalSec}s`)
|
|
110999
111346
|
)
|
|
111000
111347
|
);
|
|
@@ -111003,24 +111350,24 @@ async function runWatch(opts) {
|
|
|
111003
111350
|
try {
|
|
111004
111351
|
incidents = source === "sentry" ? await pollSentry(ctx.sentry, seen) : await pollElasticsearch(ctx.logs, seen, ctx.service);
|
|
111005
111352
|
} catch (err2) {
|
|
111006
|
-
console.error(
|
|
111353
|
+
console.error(import_picocolors12.default.yellow(`[watch] poll failed \u2014 ${err2.message}`));
|
|
111007
111354
|
return;
|
|
111008
111355
|
}
|
|
111009
111356
|
if (incidents.length === 0) {
|
|
111010
|
-
console.error(
|
|
111357
|
+
console.error(import_picocolors12.default.dim(`[watch] no new incidents`));
|
|
111011
111358
|
return;
|
|
111012
111359
|
}
|
|
111013
111360
|
for (const incident of incidents) {
|
|
111014
111361
|
try {
|
|
111015
111362
|
const report = await runOneInvestigation({ hint: incident.hint }, ctx);
|
|
111016
111363
|
const { cause, confidence } = headlineFor(report);
|
|
111017
|
-
const persisted = report.persisted === false ?
|
|
111364
|
+
const persisted = report.persisted === false ? import_picocolors12.default.yellow(" (not persisted)") : "";
|
|
111018
111365
|
console.log(
|
|
111019
|
-
`${
|
|
111366
|
+
`${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}`
|
|
111020
111367
|
);
|
|
111021
111368
|
} catch (err2) {
|
|
111022
111369
|
console.error(
|
|
111023
|
-
|
|
111370
|
+
import_picocolors12.default.yellow(`[watch] investigation failed for "${incident.ref}" \u2014 ${err2.message}`)
|
|
111024
111371
|
);
|
|
111025
111372
|
}
|
|
111026
111373
|
}
|
|
@@ -111035,7 +111382,7 @@ async function runWatch(opts) {
|
|
|
111035
111382
|
const onSignal = () => {
|
|
111036
111383
|
if (stopping) return;
|
|
111037
111384
|
stopping = true;
|
|
111038
|
-
console.error(
|
|
111385
|
+
console.error(import_picocolors12.default.dim(`
|
|
111039
111386
|
[watch] shutting down\u2026`));
|
|
111040
111387
|
if (wake) wake();
|
|
111041
111388
|
};
|
|
@@ -111062,7 +111409,7 @@ async function runWatch(opts) {
|
|
|
111062
111409
|
|
|
111063
111410
|
// ../../packages/cli/src/commands/changes.ts
|
|
111064
111411
|
init_cjs_shims();
|
|
111065
|
-
var
|
|
111412
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
111066
111413
|
var CHANGES_AI_CONTRACT = `Provide a clearly separated AI change-impact review with:
|
|
111067
111414
|
|
|
111068
111415
|
Highest-risk changes
|
|
@@ -111087,7 +111434,7 @@ async function runChanges(base2, compare, opts) {
|
|
|
111087
111434
|
const health = await code.health();
|
|
111088
111435
|
if (!health.ok) {
|
|
111089
111436
|
console.error(
|
|
111090
|
-
|
|
111437
|
+
import_picocolors13.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
111091
111438
|
);
|
|
111092
111439
|
return 1;
|
|
111093
111440
|
}
|
|
@@ -111109,20 +111456,20 @@ async function runChanges(base2, compare, opts) {
|
|
|
111109
111456
|
});
|
|
111110
111457
|
console.log("\n" + renderInterpretation(result));
|
|
111111
111458
|
if (!result.ok) {
|
|
111112
|
-
console.error(
|
|
111459
|
+
console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
|
|
111113
111460
|
}
|
|
111114
111461
|
}
|
|
111115
111462
|
}
|
|
111116
111463
|
return 0;
|
|
111117
111464
|
} catch (err2) {
|
|
111118
|
-
console.error(
|
|
111465
|
+
console.error(import_picocolors13.default.red(err2.message));
|
|
111119
111466
|
return 1;
|
|
111120
111467
|
}
|
|
111121
111468
|
}
|
|
111122
111469
|
|
|
111123
111470
|
// ../../packages/cli/src/commands/timeline.ts
|
|
111124
111471
|
init_cjs_shims();
|
|
111125
|
-
var
|
|
111472
|
+
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
111126
111473
|
var DEFAULT_SINCE = "7 days ago";
|
|
111127
111474
|
function resolveTimelineWindow(opts) {
|
|
111128
111475
|
const usingDefault = !opts.all && opts.since === void 0;
|
|
@@ -111152,7 +111499,7 @@ async function runTimeline(service, opts) {
|
|
|
111152
111499
|
try {
|
|
111153
111500
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
111154
111501
|
} catch (err2) {
|
|
111155
|
-
console.error(
|
|
111502
|
+
console.error(import_picocolors14.default.red(err2.message));
|
|
111156
111503
|
return 1;
|
|
111157
111504
|
}
|
|
111158
111505
|
const { code } = createConnectors(config2);
|
|
@@ -111167,9 +111514,9 @@ async function runTimeline(service, opts) {
|
|
|
111167
111514
|
console.log(renderChangeTimeline(t2));
|
|
111168
111515
|
if (usingDefaultWindow) {
|
|
111169
111516
|
console.log(
|
|
111170
|
-
|
|
111517
|
+
import_picocolors14.default.dim(
|
|
111171
111518
|
`
|
|
111172
|
-
Showing the last 7 days (default). Widen with ${
|
|
111519
|
+
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")}.`
|
|
111173
111520
|
)
|
|
111174
111521
|
);
|
|
111175
111522
|
}
|
|
@@ -111186,21 +111533,26 @@ async function runTimeline(service, opts) {
|
|
|
111186
111533
|
});
|
|
111187
111534
|
console.log("\n" + renderInterpretation(result));
|
|
111188
111535
|
if (!result.ok) {
|
|
111189
|
-
console.error(
|
|
111536
|
+
console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
|
|
111190
111537
|
}
|
|
111191
111538
|
}
|
|
111192
111539
|
}
|
|
111193
111540
|
return 0;
|
|
111194
111541
|
} catch (err2) {
|
|
111195
|
-
console.error(
|
|
111542
|
+
console.error(import_picocolors14.default.red(err2.message));
|
|
111196
111543
|
return 1;
|
|
111197
111544
|
}
|
|
111198
111545
|
}
|
|
111199
111546
|
|
|
111200
111547
|
// ../../packages/cli/src/commands/what-changed.ts
|
|
111201
111548
|
init_cjs_shims();
|
|
111202
|
-
var
|
|
111549
|
+
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
111203
111550
|
var DEFAULT_SINCE2 = "7 days ago";
|
|
111551
|
+
function looksLikeGitRef(s5) {
|
|
111552
|
+
const t2 = s5.trim();
|
|
111553
|
+
if (/\s/.test(t2) || /^\d{4}-\d{2}-\d{2}/.test(t2)) return false;
|
|
111554
|
+
return /[~^]/.test(t2) || /^HEAD\b/.test(t2) || /^[0-9a-f]{7,40}$/i.test(t2) || /^v?\d+\.\d+/.test(t2);
|
|
111555
|
+
}
|
|
111204
111556
|
var WHAT_CHANGED_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
111205
111557
|
|
|
111206
111558
|
Evidence used
|
|
@@ -111223,11 +111575,18 @@ async function runWhatChanged(service, opts) {
|
|
|
111223
111575
|
try {
|
|
111224
111576
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
111225
111577
|
} catch (err2) {
|
|
111226
|
-
console.error(
|
|
111578
|
+
console.error(import_picocolors15.default.red(err2.message));
|
|
111227
111579
|
return 1;
|
|
111228
111580
|
}
|
|
111229
111581
|
const { code } = createConnectors(config2);
|
|
111230
111582
|
const since = opts.since ?? DEFAULT_SINCE2;
|
|
111583
|
+
if (opts.since !== void 0 && looksLikeGitRef(opts.since) && !await resolvesToCommit(renv.path, opts.since)) {
|
|
111584
|
+
console.error(
|
|
111585
|
+
import_picocolors15.default.yellow(
|
|
111586
|
+
`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.`
|
|
111587
|
+
)
|
|
111588
|
+
);
|
|
111589
|
+
}
|
|
111231
111590
|
const r = await whatChanged(
|
|
111232
111591
|
{ repoPath: renv.path, since, until: opts.until, service },
|
|
111233
111592
|
{ code }
|
|
@@ -111246,7 +111605,7 @@ async function runWhatChanged(service, opts) {
|
|
|
111246
111605
|
});
|
|
111247
111606
|
console.log("\n" + renderInterpretation(result));
|
|
111248
111607
|
if (!result.ok) {
|
|
111249
|
-
console.error(
|
|
111608
|
+
console.error(import_picocolors15.default.yellow(`[ai] ${result.warning}`));
|
|
111250
111609
|
}
|
|
111251
111610
|
}
|
|
111252
111611
|
if (opts.push) {
|
|
@@ -111254,19 +111613,19 @@ async function runWhatChanged(service, opts) {
|
|
|
111254
111613
|
}
|
|
111255
111614
|
return 0;
|
|
111256
111615
|
} catch (err2) {
|
|
111257
|
-
console.error(
|
|
111616
|
+
console.error(import_picocolors15.default.red(err2.message));
|
|
111258
111617
|
return 1;
|
|
111259
111618
|
}
|
|
111260
111619
|
}
|
|
111261
111620
|
async function pushChangeReport(r, repoCwd) {
|
|
111262
111621
|
const session = authedClient();
|
|
111263
111622
|
if (!session) {
|
|
111264
|
-
console.error(
|
|
111623
|
+
console.error(import_picocolors15.default.yellow("[cloud] not logged in \u2014 run `horus login` to push reports."));
|
|
111265
111624
|
return;
|
|
111266
111625
|
}
|
|
111267
111626
|
const cfg = readCloudConfig(repoRootOrCwd(repoCwd));
|
|
111268
111627
|
if (!isCloudActive(cfg)) {
|
|
111269
|
-
console.error(
|
|
111628
|
+
console.error(import_picocolors15.default.yellow("[cloud] repo not linked \u2014 run `horus cloud link` to push reports."));
|
|
111270
111629
|
return;
|
|
111271
111630
|
}
|
|
111272
111631
|
const projectId = cfg.project?.id;
|
|
@@ -111312,15 +111671,15 @@ async function pushChangeReport(r, repoCwd) {
|
|
|
111312
111671
|
queueTopologyTouched: r.queueTopology.touched,
|
|
111313
111672
|
payload
|
|
111314
111673
|
});
|
|
111315
|
-
console.error(
|
|
111674
|
+
console.error(import_picocolors15.default.dim("[cloud] pushed change report."));
|
|
111316
111675
|
} catch (err2) {
|
|
111317
|
-
console.error(
|
|
111676
|
+
console.error(import_picocolors15.default.yellow(`[cloud] push failed: ${err2.message}`));
|
|
111318
111677
|
}
|
|
111319
111678
|
}
|
|
111320
111679
|
|
|
111321
111680
|
// ../../packages/cli/src/commands/architecture.ts
|
|
111322
111681
|
init_cjs_shims();
|
|
111323
|
-
var
|
|
111682
|
+
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
111324
111683
|
var ARCHITECTURE_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
111325
111684
|
|
|
111326
111685
|
System summary
|
|
@@ -111349,7 +111708,7 @@ async function runArchitecture(opts) {
|
|
|
111349
111708
|
const health = await code.health();
|
|
111350
111709
|
if (!health.ok) {
|
|
111351
111710
|
console.error(
|
|
111352
|
-
|
|
111711
|
+
import_picocolors16.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
111353
111712
|
);
|
|
111354
111713
|
return 1;
|
|
111355
111714
|
}
|
|
@@ -111377,7 +111736,7 @@ async function runArchitecture(opts) {
|
|
|
111377
111736
|
});
|
|
111378
111737
|
console.log("\n" + renderInterpretation(result));
|
|
111379
111738
|
if (!result.ok) {
|
|
111380
|
-
console.error(
|
|
111739
|
+
console.error(import_picocolors16.default.yellow(`[ai] ${result.warning}`));
|
|
111381
111740
|
}
|
|
111382
111741
|
}
|
|
111383
111742
|
}
|
|
@@ -111386,14 +111745,14 @@ async function runArchitecture(opts) {
|
|
|
111386
111745
|
}
|
|
111387
111746
|
return 0;
|
|
111388
111747
|
} catch (err2) {
|
|
111389
|
-
console.error(
|
|
111748
|
+
console.error(import_picocolors16.default.red(err2.message));
|
|
111390
111749
|
return 1;
|
|
111391
111750
|
}
|
|
111392
111751
|
}
|
|
111393
111752
|
|
|
111394
111753
|
// ../../packages/cli/src/commands/blast-radius.ts
|
|
111395
111754
|
init_cjs_shims();
|
|
111396
|
-
var
|
|
111755
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
111397
111756
|
var BLAST_RADIUS_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
111398
111757
|
|
|
111399
111758
|
Evidence used
|
|
@@ -111420,7 +111779,7 @@ async function runBlastRadius(query, opts) {
|
|
|
111420
111779
|
const { code } = createConnectors(config2);
|
|
111421
111780
|
const health = await code.health();
|
|
111422
111781
|
if (!health.ok) {
|
|
111423
|
-
console.error(
|
|
111782
|
+
console.error(import_picocolors17.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
|
|
111424
111783
|
return 1;
|
|
111425
111784
|
}
|
|
111426
111785
|
let project;
|
|
@@ -111433,12 +111792,12 @@ async function runBlastRadius(query, opts) {
|
|
|
111433
111792
|
const r = await analyzeBlastRadius(query, { code, db, project }, opts.depth ?? 3);
|
|
111434
111793
|
if (!r) {
|
|
111435
111794
|
console.log(`No symbol found for: ${query}`);
|
|
111436
|
-
console.log(
|
|
111795
|
+
console.log(import_picocolors17.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
|
|
111437
111796
|
return 1;
|
|
111438
111797
|
}
|
|
111439
111798
|
if (r.seed.name.toLowerCase() !== query.toLowerCase()) {
|
|
111440
111799
|
console.log(
|
|
111441
|
-
|
|
111800
|
+
import_picocolors17.default.yellow(` No exact match for "${query}"`) + import_picocolors17.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
|
|
111442
111801
|
);
|
|
111443
111802
|
}
|
|
111444
111803
|
if (opts.json) {
|
|
@@ -111458,7 +111817,7 @@ async function runBlastRadius(query, opts) {
|
|
|
111458
111817
|
});
|
|
111459
111818
|
console.log("\n" + renderInterpretation(result));
|
|
111460
111819
|
if (!result.ok) {
|
|
111461
|
-
console.error(
|
|
111820
|
+
console.error(import_picocolors17.default.yellow(`[ai] ${result.warning}`));
|
|
111462
111821
|
}
|
|
111463
111822
|
}
|
|
111464
111823
|
}
|
|
@@ -111467,28 +111826,28 @@ async function runBlastRadius(query, opts) {
|
|
|
111467
111826
|
}
|
|
111468
111827
|
return 0;
|
|
111469
111828
|
} catch (err2) {
|
|
111470
|
-
console.error(
|
|
111829
|
+
console.error(import_picocolors17.default.red(err2.message));
|
|
111471
111830
|
return 1;
|
|
111472
111831
|
}
|
|
111473
111832
|
}
|
|
111474
111833
|
|
|
111475
111834
|
// ../../packages/cli/src/commands/repos.ts
|
|
111476
111835
|
init_cjs_shims();
|
|
111477
|
-
var
|
|
111836
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
111478
111837
|
async function runRepos(opts) {
|
|
111479
111838
|
const config2 = await loadConfig(opts.config);
|
|
111480
111839
|
const providers = repoProviders(config2);
|
|
111481
111840
|
if (providers.length === 0) {
|
|
111482
|
-
console.log(
|
|
111841
|
+
console.log(import_picocolors18.default.dim("No repositories configured."));
|
|
111483
111842
|
return 0;
|
|
111484
111843
|
}
|
|
111485
111844
|
const health = await reposHealth(providers);
|
|
111486
111845
|
for (const h3 of health) {
|
|
111487
|
-
const dot = h3.reachable ?
|
|
111488
|
-
const name2 =
|
|
111489
|
-
const hostUrl =
|
|
111490
|
-
const path =
|
|
111491
|
-
const detail = h3.detail ?
|
|
111846
|
+
const dot = h3.reachable ? import_picocolors18.default.green("\u25CF") : import_picocolors18.default.red("\u25CF");
|
|
111847
|
+
const name2 = import_picocolors18.default.bold(h3.repo.padEnd(20));
|
|
111848
|
+
const hostUrl = import_picocolors18.default.cyan(h3.hostUrl.padEnd(30));
|
|
111849
|
+
const path = import_picocolors18.default.dim(h3.path);
|
|
111850
|
+
const detail = h3.detail ? import_picocolors18.default.dim(`(${h3.detail})`) : "";
|
|
111492
111851
|
console.log(` ${dot} ${name2} ${hostUrl} ${path} ${detail}`);
|
|
111493
111852
|
}
|
|
111494
111853
|
return 0;
|
|
@@ -111496,7 +111855,7 @@ async function runRepos(opts) {
|
|
|
111496
111855
|
|
|
111497
111856
|
// ../../packages/cli/src/commands/search.ts
|
|
111498
111857
|
init_cjs_shims();
|
|
111499
|
-
var
|
|
111858
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
111500
111859
|
async function runSearch(query, opts) {
|
|
111501
111860
|
const config2 = await loadConfig(opts.config);
|
|
111502
111861
|
const providers = repoProviders(config2);
|
|
@@ -111507,16 +111866,16 @@ async function runSearch(query, opts) {
|
|
|
111507
111866
|
}
|
|
111508
111867
|
let totalMatches = 0;
|
|
111509
111868
|
for (const result of results) {
|
|
111510
|
-
const statusDot = result.reachable ?
|
|
111869
|
+
const statusDot = result.reachable ? import_picocolors19.default.green("\u25CF") : import_picocolors19.default.red("\u25CF");
|
|
111511
111870
|
console.log("");
|
|
111512
|
-
console.log(`${statusDot} ${
|
|
111871
|
+
console.log(`${statusDot} ${import_picocolors19.default.bold("## " + result.repo)} ${import_picocolors19.default.dim(result.hostUrl)}`);
|
|
111513
111872
|
if (!result.reachable) {
|
|
111514
|
-
console.log(
|
|
111873
|
+
console.log(import_picocolors19.default.dim(" (unreachable)"));
|
|
111515
111874
|
} else if (result.symbols.length === 0) {
|
|
111516
|
-
console.log(
|
|
111875
|
+
console.log(import_picocolors19.default.dim(" (no matches)"));
|
|
111517
111876
|
} else {
|
|
111518
111877
|
for (const sym of result.symbols) {
|
|
111519
|
-
console.log(` - ${
|
|
111878
|
+
console.log(` - ${import_picocolors19.default.bold(sym.name)} ${import_picocolors19.default.dim(sym.filePath)}`);
|
|
111520
111879
|
}
|
|
111521
111880
|
totalMatches += result.symbols.length;
|
|
111522
111881
|
}
|
|
@@ -111524,7 +111883,7 @@ async function runSearch(query, opts) {
|
|
|
111524
111883
|
console.log("");
|
|
111525
111884
|
const reachableCount = results.filter((r) => r.reachable).length;
|
|
111526
111885
|
console.log(
|
|
111527
|
-
|
|
111886
|
+
import_picocolors19.default.dim(
|
|
111528
111887
|
`${totalMatches} match(es) across ${reachableCount}/${results.length} reachable repo(s) for query: "${query}"`
|
|
111529
111888
|
)
|
|
111530
111889
|
);
|
|
@@ -111533,7 +111892,7 @@ async function runSearch(query, opts) {
|
|
|
111533
111892
|
|
|
111534
111893
|
// ../../packages/cli/src/commands/investigations.ts
|
|
111535
111894
|
init_cjs_shims();
|
|
111536
|
-
var
|
|
111895
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
111537
111896
|
|
|
111538
111897
|
// ../../packages/cli/src/lib/format.ts
|
|
111539
111898
|
init_cjs_shims();
|
|
@@ -111576,8 +111935,8 @@ async function runInvestigations(opts) {
|
|
|
111576
111935
|
const session = authedClient();
|
|
111577
111936
|
if (!session) {
|
|
111578
111937
|
console.error(
|
|
111579
|
-
|
|
111580
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
111938
|
+
import_picocolors20.default.red(
|
|
111939
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors20.default.bold("horus login")} first.`
|
|
111581
111940
|
)
|
|
111582
111941
|
);
|
|
111583
111942
|
return 1;
|
|
@@ -111618,18 +111977,18 @@ async function runInvestigations(opts) {
|
|
|
111618
111977
|
|
|
111619
111978
|
// ../../packages/cli/src/commands/replay.ts
|
|
111620
111979
|
init_cjs_shims();
|
|
111621
|
-
var
|
|
111980
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
111622
111981
|
async function runReplay(id, opts) {
|
|
111623
111982
|
const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
|
|
111624
111983
|
try {
|
|
111625
111984
|
const row = await getInvestigation(db, id);
|
|
111626
111985
|
if (!row) {
|
|
111627
|
-
console.error(
|
|
111986
|
+
console.error(import_picocolors21.default.red("No investigation found: " + id));
|
|
111628
111987
|
return 1;
|
|
111629
111988
|
}
|
|
111630
111989
|
if (!row.report) {
|
|
111631
111990
|
console.error(
|
|
111632
|
-
|
|
111991
|
+
import_picocolors21.default.red(
|
|
111633
111992
|
"Investigation " + id + " has no stored report (run a newer investigation)."
|
|
111634
111993
|
)
|
|
111635
111994
|
);
|
|
@@ -111642,7 +112001,7 @@ async function runReplay(id, opts) {
|
|
|
111642
112001
|
if (opts.ai && fmt !== "json") {
|
|
111643
112002
|
if (report.aiJudgment && !opts.refreshAi) {
|
|
111644
112003
|
renderStoredAIJudgment(report.aiJudgment);
|
|
111645
|
-
console.log(
|
|
112004
|
+
console.log(import_picocolors21.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
|
|
111646
112005
|
} else {
|
|
111647
112006
|
const narrativeInput = buildNarrativeInput(report);
|
|
111648
112007
|
const { provider } = await buildNarrativeProvider({
|
|
@@ -111655,17 +112014,17 @@ async function runReplay(id, opts) {
|
|
|
111655
112014
|
);
|
|
111656
112015
|
if (!fromProvider && degraded) {
|
|
111657
112016
|
if (validationErrors?.length) {
|
|
111658
|
-
console.error(
|
|
112017
|
+
console.error(import_picocolors21.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
|
|
111659
112018
|
}
|
|
111660
112019
|
console.log("");
|
|
111661
|
-
console.log(
|
|
111662
|
-
console.log(
|
|
111663
|
-
if (output.what?.trim()) console.log(
|
|
111664
|
-
if (output.why?.trim()) console.log(
|
|
112020
|
+
console.log(import_picocolors21.default.dim("\u2500".repeat(60)));
|
|
112021
|
+
console.log(import_picocolors21.default.bold("AI narrative ") + import_picocolors21.default.yellow("(unstructured \u2014 not validated)"));
|
|
112022
|
+
if (output.what?.trim()) console.log(import_picocolors21.default.bold("\nWhat:"), output.what.trim());
|
|
112023
|
+
if (output.why?.trim()) console.log(import_picocolors21.default.bold("\nWhy:"), output.why.trim());
|
|
111665
112024
|
} else if (!fromProvider) {
|
|
111666
|
-
console.error(
|
|
112025
|
+
console.error(import_picocolors21.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
|
|
111667
112026
|
if (validationErrors?.length) {
|
|
111668
|
-
console.error(
|
|
112027
|
+
console.error(import_picocolors21.default.dim(` ${validationErrors[0]}`));
|
|
111669
112028
|
}
|
|
111670
112029
|
} else {
|
|
111671
112030
|
const stored = narrativeOutputToStoredJudgment(output, "anthropic");
|
|
@@ -111686,7 +112045,7 @@ async function runReplay(id, opts) {
|
|
|
111686
112045
|
|
|
111687
112046
|
// ../../packages/cli/src/commands/owner.ts
|
|
111688
112047
|
init_cjs_shims();
|
|
111689
|
-
var
|
|
112048
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
111690
112049
|
async function runOwner(query, opts) {
|
|
111691
112050
|
try {
|
|
111692
112051
|
const config2 = await loadConfig(opts.config);
|
|
@@ -111694,7 +112053,7 @@ async function runOwner(query, opts) {
|
|
|
111694
112053
|
try {
|
|
111695
112054
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
111696
112055
|
} catch (err2) {
|
|
111697
|
-
console.error(
|
|
112056
|
+
console.error(import_picocolors22.default.red(err2.message));
|
|
111698
112057
|
return 1;
|
|
111699
112058
|
}
|
|
111700
112059
|
const code = codeForRepo(config2, renv.project);
|
|
@@ -111702,16 +112061,16 @@ async function runOwner(query, opts) {
|
|
|
111702
112061
|
console.log(opts.json ? ownershipToJSON(o5) : renderOwnership(o5));
|
|
111703
112062
|
return 0;
|
|
111704
112063
|
} catch (err2) {
|
|
111705
|
-
console.error(
|
|
112064
|
+
console.error(import_picocolors22.default.red(err2.message));
|
|
111706
112065
|
return 1;
|
|
111707
112066
|
}
|
|
111708
112067
|
}
|
|
111709
112068
|
|
|
111710
112069
|
// ../../packages/cli/src/commands/postmortem.ts
|
|
111711
112070
|
init_cjs_shims();
|
|
111712
|
-
var
|
|
111713
|
-
var
|
|
111714
|
-
var
|
|
112071
|
+
var import_node_fs16 = require("fs");
|
|
112072
|
+
var import_node_path17 = require("path");
|
|
112073
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
111715
112074
|
async function runPostmortem(id, opts) {
|
|
111716
112075
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
111717
112076
|
let sql2;
|
|
@@ -111725,11 +112084,11 @@ async function runPostmortem(id, opts) {
|
|
|
111725
112084
|
const { db } = conn;
|
|
111726
112085
|
const row = await getInvestigation(db, id);
|
|
111727
112086
|
if (!row) {
|
|
111728
|
-
log(
|
|
112087
|
+
log(import_picocolors23.default.red("No investigation found: " + id));
|
|
111729
112088
|
return 1;
|
|
111730
112089
|
}
|
|
111731
112090
|
if (!row.report) {
|
|
111732
|
-
log(
|
|
112091
|
+
log(import_picocolors23.default.red("Investigation " + id + " has no stored report."));
|
|
111733
112092
|
return 1;
|
|
111734
112093
|
}
|
|
111735
112094
|
report = migrateReport(row.report);
|
|
@@ -111821,25 +112180,25 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
111821
112180
|
}
|
|
111822
112181
|
}
|
|
111823
112182
|
if (opts.output) {
|
|
111824
|
-
const outputPath = (0,
|
|
111825
|
-
if ((0,
|
|
111826
|
-
log(
|
|
111827
|
-
log(
|
|
112183
|
+
const outputPath = (0, import_node_path17.resolve)(opts.output);
|
|
112184
|
+
if ((0, import_node_fs16.existsSync)(outputPath) && !opts.force) {
|
|
112185
|
+
log(import_picocolors23.default.red("\u2717") + " " + outputPath + " already exists");
|
|
112186
|
+
log(import_picocolors23.default.dim(" pass --force to overwrite"));
|
|
111828
112187
|
return 1;
|
|
111829
112188
|
}
|
|
111830
112189
|
try {
|
|
111831
|
-
(0,
|
|
112190
|
+
(0, import_node_fs16.writeFileSync)(outputPath, content, "utf8");
|
|
111832
112191
|
} catch (err2) {
|
|
111833
|
-
log(
|
|
112192
|
+
log(import_picocolors23.default.red("\u2717 Could not write " + outputPath + ": ") + err2.message);
|
|
111834
112193
|
return 1;
|
|
111835
112194
|
}
|
|
111836
|
-
log(
|
|
112195
|
+
log(import_picocolors23.default.green("\u2713") + " Saved postmortem to " + outputPath);
|
|
111837
112196
|
} else {
|
|
111838
112197
|
log(content);
|
|
111839
112198
|
}
|
|
111840
112199
|
} catch (err2) {
|
|
111841
112200
|
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
111842
|
-
log(
|
|
112201
|
+
log(import_picocolors23.default.red("postmortem failed: " + message));
|
|
111843
112202
|
return 1;
|
|
111844
112203
|
} finally {
|
|
111845
112204
|
if (sql2) {
|
|
@@ -111851,7 +112210,7 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
111851
112210
|
|
|
111852
112211
|
// ../../packages/cli/src/commands/score.ts
|
|
111853
112212
|
init_cjs_shims();
|
|
111854
|
-
var
|
|
112213
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
111855
112214
|
var SCORE_AI_CONTRACT = `Provide a clearly separated AI score explanation with:
|
|
111856
112215
|
|
|
111857
112216
|
Why this scored this way
|
|
@@ -111870,11 +112229,11 @@ async function runScore(id, opts) {
|
|
|
111870
112229
|
try {
|
|
111871
112230
|
const row = await getInvestigation(db, id);
|
|
111872
112231
|
if (!row) {
|
|
111873
|
-
console.error(
|
|
112232
|
+
console.error(import_picocolors24.default.red("No investigation found: " + id));
|
|
111874
112233
|
return 1;
|
|
111875
112234
|
}
|
|
111876
112235
|
if (!row.report) {
|
|
111877
|
-
console.error(
|
|
112236
|
+
console.error(import_picocolors24.default.red("Investigation " + id + " has no stored report."));
|
|
111878
112237
|
return 1;
|
|
111879
112238
|
}
|
|
111880
112239
|
const s5 = scoreInvestigation(migrateReport(row.report));
|
|
@@ -111895,7 +112254,7 @@ async function runScore(id, opts) {
|
|
|
111895
112254
|
});
|
|
111896
112255
|
console.log("\n" + renderInterpretation(result));
|
|
111897
112256
|
if (!result.ok) {
|
|
111898
|
-
console.error(
|
|
112257
|
+
console.error(import_picocolors24.default.yellow(`[ai] ${result.warning}`));
|
|
111899
112258
|
}
|
|
111900
112259
|
}
|
|
111901
112260
|
}
|
|
@@ -111935,7 +112294,7 @@ async function runScores(opts) {
|
|
|
111935
112294
|
|
|
111936
112295
|
// ../../packages/cli/src/commands/ask.ts
|
|
111937
112296
|
init_cjs_shims();
|
|
111938
|
-
var
|
|
112297
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
111939
112298
|
function renderAnswer(report, directive, opts) {
|
|
111940
112299
|
const answer = answerQuestion(report, directive);
|
|
111941
112300
|
if (answer) {
|
|
@@ -111947,13 +112306,13 @@ function renderAnswer(report, directive, opts) {
|
|
|
111947
112306
|
if (!opts.json && report.aiJudgment) {
|
|
111948
112307
|
const j3 = report.aiJudgment;
|
|
111949
112308
|
console.log("");
|
|
111950
|
-
console.log(
|
|
111951
|
-
console.log(
|
|
112309
|
+
console.log(import_picocolors25.default.dim("\u2500".repeat(60)));
|
|
112310
|
+
console.log(import_picocolors25.default.dim(`Stored AI judgment (${j3.provider}, ${j3.generatedAt}):`));
|
|
111952
112311
|
if (j3.rootCauseAssessment) {
|
|
111953
|
-
console.log(
|
|
111954
|
-
console.log(
|
|
112312
|
+
console.log(import_picocolors25.default.bold("Root cause (AI):"), j3.rootCauseAssessment.summary);
|
|
112313
|
+
console.log(import_picocolors25.default.dim(`Uncertainty: ${j3.rootCauseAssessment.uncertainty}`));
|
|
111955
112314
|
} else {
|
|
111956
|
-
console.log(
|
|
112315
|
+
console.log(import_picocolors25.default.bold("AI Why:"), j3.why);
|
|
111957
112316
|
}
|
|
111958
112317
|
}
|
|
111959
112318
|
return 0;
|
|
@@ -111982,8 +112341,8 @@ async function runAsk(id, directive, opts) {
|
|
|
111982
112341
|
const session = authedClient();
|
|
111983
112342
|
if (!session) {
|
|
111984
112343
|
console.error(
|
|
111985
|
-
|
|
111986
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
112344
|
+
import_picocolors25.default.red(
|
|
112345
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors25.default.bold("horus login")} first.`
|
|
111987
112346
|
)
|
|
111988
112347
|
);
|
|
111989
112348
|
return 1;
|
|
@@ -111992,15 +112351,15 @@ async function runAsk(id, directive, opts) {
|
|
|
111992
112351
|
const report = await fetchInvestigationReportFromCloud(session.client, cloudCfg, id);
|
|
111993
112352
|
if (report) return renderAnswer(report, directive, opts);
|
|
111994
112353
|
console.error(
|
|
111995
|
-
|
|
111996
|
-
`Investigation ${id} has no saved report locally or in Horus Cloud. Run ${
|
|
112354
|
+
import_picocolors25.default.red(
|
|
112355
|
+
`Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors25.default.bold("horus investigate")} first.`
|
|
111997
112356
|
)
|
|
111998
112357
|
);
|
|
111999
112358
|
return 1;
|
|
112000
112359
|
} catch (err2) {
|
|
112001
112360
|
if (err2 instanceof CloudError && err2.status === 404) {
|
|
112002
112361
|
console.error(
|
|
112003
|
-
|
|
112362
|
+
import_picocolors25.default.red(
|
|
112004
112363
|
`No investigation found for "${id}" locally or in Horus Cloud. Use the id printed at the top of \`horus investigate\` output.`
|
|
112005
112364
|
)
|
|
112006
112365
|
);
|
|
@@ -112014,21 +112373,21 @@ async function runAsk(id, directive, opts) {
|
|
|
112014
112373
|
case "found":
|
|
112015
112374
|
return renderAnswer(local.report, directive, opts);
|
|
112016
112375
|
case "no-report":
|
|
112017
|
-
console.error(
|
|
112376
|
+
console.error(import_picocolors25.default.red("Investigation " + id + " has no stored report."));
|
|
112018
112377
|
return 1;
|
|
112019
112378
|
case "error":
|
|
112020
|
-
console.error(
|
|
112379
|
+
console.error(import_picocolors25.default.red(local.message));
|
|
112021
112380
|
return 1;
|
|
112022
112381
|
case "not-found":
|
|
112023
112382
|
default:
|
|
112024
|
-
console.error(
|
|
112383
|
+
console.error(import_picocolors25.default.red("No investigation found: " + id));
|
|
112025
112384
|
return 1;
|
|
112026
112385
|
}
|
|
112027
112386
|
}
|
|
112028
112387
|
|
|
112029
112388
|
// ../../packages/cli/src/commands/onboard.ts
|
|
112030
112389
|
init_cjs_shims();
|
|
112031
|
-
var
|
|
112390
|
+
var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
112032
112391
|
var ONBOARD_AI_CONTRACT = `Provide a clearly separated AI onboarding guide with:
|
|
112033
112392
|
|
|
112034
112393
|
Start here
|
|
@@ -112056,7 +112415,7 @@ async function runOnboard(area, opts) {
|
|
|
112056
112415
|
try {
|
|
112057
112416
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
112058
112417
|
} catch (err2) {
|
|
112059
|
-
console.error(
|
|
112418
|
+
console.error(import_picocolors26.default.red(err2.message));
|
|
112060
112419
|
return 1;
|
|
112061
112420
|
}
|
|
112062
112421
|
const repo = { name: renv.project, path: renv.path };
|
|
@@ -112064,7 +112423,7 @@ async function runOnboard(area, opts) {
|
|
|
112064
112423
|
const health = await code.health();
|
|
112065
112424
|
if (!health.ok) {
|
|
112066
112425
|
console.error(
|
|
112067
|
-
|
|
112426
|
+
import_picocolors26.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
112068
112427
|
);
|
|
112069
112428
|
return 1;
|
|
112070
112429
|
}
|
|
@@ -112091,7 +112450,7 @@ async function runOnboard(area, opts) {
|
|
|
112091
112450
|
});
|
|
112092
112451
|
console.log("\n" + renderInterpretation(result));
|
|
112093
112452
|
if (!result.ok) {
|
|
112094
|
-
console.error(
|
|
112453
|
+
console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
|
|
112095
112454
|
}
|
|
112096
112455
|
}
|
|
112097
112456
|
}
|
|
@@ -112100,14 +112459,14 @@ async function runOnboard(area, opts) {
|
|
|
112100
112459
|
}
|
|
112101
112460
|
return 0;
|
|
112102
112461
|
} catch (err2) {
|
|
112103
|
-
console.error(
|
|
112462
|
+
console.error(import_picocolors26.default.red(err2.message));
|
|
112104
112463
|
return 1;
|
|
112105
112464
|
}
|
|
112106
112465
|
}
|
|
112107
112466
|
|
|
112108
112467
|
// ../../packages/cli/src/commands/simulate.ts
|
|
112109
112468
|
init_cjs_shims();
|
|
112110
|
-
var
|
|
112469
|
+
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
112111
112470
|
async function runSimulate(scenarioId, opts) {
|
|
112112
112471
|
try {
|
|
112113
112472
|
if (scenarioId === void 0) {
|
|
@@ -112117,7 +112476,7 @@ async function runSimulate(scenarioId, opts) {
|
|
|
112117
112476
|
const scenario = getScenario(scenarioId);
|
|
112118
112477
|
if (scenario === null) {
|
|
112119
112478
|
console.error(
|
|
112120
|
-
|
|
112479
|
+
import_picocolors27.default.red(
|
|
112121
112480
|
`Unknown scenario: "${scenarioId}". Run horus simulate to list available scenarios.`
|
|
112122
112481
|
)
|
|
112123
112482
|
);
|
|
@@ -112128,14 +112487,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
112128
112487
|
try {
|
|
112129
112488
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
112130
112489
|
} catch (err2) {
|
|
112131
|
-
console.error(
|
|
112490
|
+
console.error(import_picocolors27.default.red(err2.message));
|
|
112132
112491
|
return 1;
|
|
112133
112492
|
}
|
|
112134
112493
|
const code = codeForRepo(config2, renv.project);
|
|
112135
112494
|
const health = await code.health();
|
|
112136
112495
|
if (!health.ok) {
|
|
112137
112496
|
console.error(
|
|
112138
|
-
|
|
112497
|
+
import_picocolors27.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
112139
112498
|
);
|
|
112140
112499
|
return 1;
|
|
112141
112500
|
}
|
|
@@ -112152,14 +112511,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
112152
112511
|
}
|
|
112153
112512
|
return 0;
|
|
112154
112513
|
} catch (err2) {
|
|
112155
|
-
console.error(
|
|
112514
|
+
console.error(import_picocolors27.default.red(err2.message));
|
|
112156
112515
|
return 1;
|
|
112157
112516
|
}
|
|
112158
112517
|
}
|
|
112159
112518
|
|
|
112160
112519
|
// ../../packages/cli/src/commands/logs.ts
|
|
112161
112520
|
init_cjs_shims();
|
|
112162
|
-
var
|
|
112521
|
+
var import_picocolors28 = __toESM(require_picocolors(), 1);
|
|
112163
112522
|
var LOGS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
112164
112523
|
|
|
112165
112524
|
Evidence used
|
|
@@ -112205,9 +112564,9 @@ function parseWhere(raw) {
|
|
|
112205
112564
|
}
|
|
112206
112565
|
var whereLabel = (where) => where.map((w4) => `${w4.field}=${w4.value}`).join(", ");
|
|
112207
112566
|
function levelColor(level, text2) {
|
|
112208
|
-
if (level === "error" || level === "fatal") return
|
|
112209
|
-
if (level === "warn") return
|
|
112210
|
-
return
|
|
112567
|
+
if (level === "error" || level === "fatal") return import_picocolors28.default.red(text2);
|
|
112568
|
+
if (level === "warn") return import_picocolors28.default.yellow(text2);
|
|
112569
|
+
return import_picocolors28.default.dim(text2);
|
|
112211
112570
|
}
|
|
112212
112571
|
function matchServiceLabel(requested, candidates) {
|
|
112213
112572
|
if (candidates.includes(requested)) return { kind: "unique", matched: requested };
|
|
@@ -112223,7 +112582,7 @@ async function runLogs(service, opts) {
|
|
|
112223
112582
|
try {
|
|
112224
112583
|
where = parseWhere(opts.where);
|
|
112225
112584
|
} catch (err2) {
|
|
112226
|
-
console.error(
|
|
112585
|
+
console.error(import_picocolors28.default.red(err2.message));
|
|
112227
112586
|
return 1;
|
|
112228
112587
|
}
|
|
112229
112588
|
const hasWhere = where.length > 0;
|
|
@@ -112233,13 +112592,13 @@ async function runLogs(service, opts) {
|
|
|
112233
112592
|
try {
|
|
112234
112593
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
112235
112594
|
} catch (err2) {
|
|
112236
|
-
console.error(
|
|
112595
|
+
console.error(import_picocolors28.default.red(err2.message));
|
|
112237
112596
|
return 1;
|
|
112238
112597
|
}
|
|
112239
112598
|
const logs = logsForEnv(renv);
|
|
112240
112599
|
if (logs === null) {
|
|
112241
112600
|
console.error(
|
|
112242
|
-
|
|
112601
|
+
import_picocolors28.default.red(
|
|
112243
112602
|
`No Elasticsearch connector configured for ${renv.project}/${renv.env} (set ES_URL / ES_USERNAME / ES_PASSWORD)`
|
|
112244
112603
|
)
|
|
112245
112604
|
);
|
|
@@ -112247,7 +112606,7 @@ async function runLogs(service, opts) {
|
|
|
112247
112606
|
}
|
|
112248
112607
|
const health = await logs.health();
|
|
112249
112608
|
if (!health.ok) {
|
|
112250
|
-
console.error(
|
|
112609
|
+
console.error(import_picocolors28.default.red(`Elasticsearch unreachable: ${health.detail}`));
|
|
112251
112610
|
return 1;
|
|
112252
112611
|
}
|
|
112253
112612
|
const resolvedService = service ?? renv.connectors.elasticsearch?.serviceName;
|
|
@@ -112260,21 +112619,21 @@ async function runLogs(service, opts) {
|
|
|
112260
112619
|
requiresEventCode: aggregatesEventCode
|
|
112261
112620
|
});
|
|
112262
112621
|
for (const w4 of compat.issues.filter((i3) => i3.severity === "warning")) {
|
|
112263
|
-
console.warn(
|
|
112622
|
+
console.warn(import_picocolors28.default.yellow(`[warn] ${w4.field}: ${w4.message}`));
|
|
112264
112623
|
}
|
|
112265
112624
|
const errors2 = compat.issues.filter((i3) => i3.severity === "error");
|
|
112266
112625
|
if (errors2.length > 0) {
|
|
112267
|
-
console.error(
|
|
112626
|
+
console.error(import_picocolors28.default.red("Elasticsearch field mapping is incompatible with the index:"));
|
|
112268
112627
|
for (const e of errors2) {
|
|
112269
|
-
console.error(
|
|
112628
|
+
console.error(import_picocolors28.default.red(` ${e.field}: ${e.message}`));
|
|
112270
112629
|
}
|
|
112271
112630
|
console.error(
|
|
112272
|
-
|
|
112631
|
+
import_picocolors28.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
|
|
112273
112632
|
);
|
|
112274
112633
|
return 1;
|
|
112275
112634
|
}
|
|
112276
112635
|
} catch {
|
|
112277
|
-
console.warn(
|
|
112636
|
+
console.warn(import_picocolors28.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
|
|
112278
112637
|
}
|
|
112279
112638
|
const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
|
|
112280
112639
|
const fromDisplay = from.slice(0, 16).replace("T", " ");
|
|
@@ -112330,12 +112689,12 @@ async function runLogs(service, opts) {
|
|
|
112330
112689
|
return 0;
|
|
112331
112690
|
}
|
|
112332
112691
|
console.error(
|
|
112333
|
-
|
|
112692
|
+
import_picocolors28.default.dim(
|
|
112334
112693
|
`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)" : "")
|
|
112335
112694
|
)
|
|
112336
112695
|
);
|
|
112337
112696
|
if (records.length === 0) {
|
|
112338
|
-
console.log(
|
|
112697
|
+
console.log(import_picocolors28.default.dim("No logs matched."));
|
|
112339
112698
|
return 0;
|
|
112340
112699
|
}
|
|
112341
112700
|
for (const r of records) {
|
|
@@ -112345,12 +112704,12 @@ async function runLogs(service, opts) {
|
|
|
112345
112704
|
const line2 = `${ts} ${lvl} ${comp} ${r.message.slice(0, 120)}`;
|
|
112346
112705
|
console.log(levelColor(r.level, line2));
|
|
112347
112706
|
for (const { key, value } of extractContextFields(r)) {
|
|
112348
|
-
console.log(
|
|
112707
|
+
console.log(import_picocolors28.default.dim(` ${key}: ${value.slice(0, 200)}`));
|
|
112349
112708
|
}
|
|
112350
112709
|
}
|
|
112351
112710
|
if (truncated) {
|
|
112352
112711
|
console.error(
|
|
112353
|
-
|
|
112712
|
+
import_picocolors28.default.yellow(
|
|
112354
112713
|
`[warn] showing the first ${limit} record(s) \u2014 results may be truncated. Narrow with --where/--since/--level or raise --limit (max 1000).`
|
|
112355
112714
|
)
|
|
112356
112715
|
);
|
|
@@ -112393,22 +112752,22 @@ async function runLogs(service, opts) {
|
|
|
112393
112752
|
return 0;
|
|
112394
112753
|
}
|
|
112395
112754
|
console.log(
|
|
112396
|
-
|
|
112755
|
+
import_picocolors28.default.bold(`Top ${field}`) + import_picocolors28.default.dim(
|
|
112397
112756
|
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112398
112757
|
)
|
|
112399
112758
|
);
|
|
112400
|
-
console.log(
|
|
112759
|
+
console.log(import_picocolors28.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
112401
112760
|
console.log("");
|
|
112402
112761
|
if (buckets.length === 0) {
|
|
112403
|
-
console.log(
|
|
112404
|
-
console.log(
|
|
112762
|
+
console.log(import_picocolors28.default.dim(` No error logs with field "${field}" in the window.`));
|
|
112763
|
+
console.log(import_picocolors28.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
|
|
112405
112764
|
return 0;
|
|
112406
112765
|
}
|
|
112407
112766
|
for (const b5 of buckets) {
|
|
112408
|
-
console.log(` ${
|
|
112767
|
+
console.log(` ${import_picocolors28.default.red(String(b5.count).padStart(6))} ${b5.key}`);
|
|
112409
112768
|
}
|
|
112410
112769
|
console.log("");
|
|
112411
|
-
console.log(
|
|
112770
|
+
console.log(import_picocolors28.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
|
|
112412
112771
|
return 0;
|
|
112413
112772
|
}
|
|
112414
112773
|
let analysis = await logs.analyzeErrors({
|
|
@@ -112478,46 +112837,46 @@ async function runLogs(service, opts) {
|
|
|
112478
112837
|
return 0;
|
|
112479
112838
|
}
|
|
112480
112839
|
console.log(
|
|
112481
|
-
|
|
112840
|
+
import_picocolors28.default.bold(`Error analysis`) + import_picocolors28.default.dim(
|
|
112482
112841
|
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112483
112842
|
)
|
|
112484
112843
|
);
|
|
112485
|
-
console.log(
|
|
112844
|
+
console.log(import_picocolors28.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
112486
112845
|
if (broadeningNote) {
|
|
112487
|
-
console.log(
|
|
112846
|
+
console.log(import_picocolors28.default.yellow(` ${broadeningNote}`));
|
|
112488
112847
|
}
|
|
112489
112848
|
console.log(
|
|
112490
|
-
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${
|
|
112849
|
+
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors28.default.yellow(String(analysis.newSignatures.length))} new`
|
|
112491
112850
|
);
|
|
112492
112851
|
console.log("");
|
|
112493
112852
|
if (analysis.signatures.length === 0) {
|
|
112494
|
-
console.log(
|
|
112853
|
+
console.log(import_picocolors28.default.dim(" No error-level logs in the window."));
|
|
112495
112854
|
console.log(
|
|
112496
|
-
|
|
112855
|
+
import_picocolors28.default.dim(
|
|
112497
112856
|
` Searched: ${scopeService ? `service "${scopeService}"` : "all services"} \xB7 index: ${indexPattern} \xB7 from: ${fromDisplay} UTC`
|
|
112498
112857
|
)
|
|
112499
112858
|
);
|
|
112500
|
-
console.log(
|
|
112859
|
+
console.log(import_picocolors28.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
|
|
112501
112860
|
return 0;
|
|
112502
112861
|
}
|
|
112503
112862
|
for (const s5 of analysis.signatures) {
|
|
112504
112863
|
const tags = [];
|
|
112505
|
-
if (s5.isNew) tags.push(
|
|
112864
|
+
if (s5.isNew) tags.push(import_picocolors28.default.red("NEW"));
|
|
112506
112865
|
else if (s5.ratio !== void 0 && Number.isFinite(s5.ratio) && s5.ratio >= 1.5) {
|
|
112507
|
-
tags.push(
|
|
112866
|
+
tags.push(import_picocolors28.default.yellow(`spike x${s5.ratio.toFixed(1)}`));
|
|
112508
112867
|
}
|
|
112509
|
-
const svc = s5.services.length > 0 ?
|
|
112868
|
+
const svc = s5.services.length > 0 ? import_picocolors28.default.dim(` \xB7 ${s5.services.slice(0, 3).join(", ")}`) : "";
|
|
112510
112869
|
const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
|
|
112511
112870
|
console.log(
|
|
112512
|
-
` ${
|
|
112871
|
+
` ${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
|
|
112513
112872
|
);
|
|
112514
112873
|
}
|
|
112515
112874
|
if (analysis.affectedServices.length > 0) {
|
|
112516
112875
|
console.log("");
|
|
112517
|
-
console.log(` ${
|
|
112876
|
+
console.log(` ${import_picocolors28.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
|
|
112518
112877
|
}
|
|
112519
112878
|
console.log("");
|
|
112520
|
-
console.log(
|
|
112879
|
+
console.log(import_picocolors28.default.dim(" (use --raw to see individual log lines)"));
|
|
112521
112880
|
if (opts.ai) {
|
|
112522
112881
|
const result = await renderAiInterpretation({
|
|
112523
112882
|
command: "logs",
|
|
@@ -112536,19 +112895,19 @@ async function runLogs(service, opts) {
|
|
|
112536
112895
|
});
|
|
112537
112896
|
console.log("\n" + renderInterpretation(result));
|
|
112538
112897
|
if (!result.ok) {
|
|
112539
|
-
console.error(
|
|
112898
|
+
console.error(import_picocolors28.default.yellow(`[ai] ${result.warning}`));
|
|
112540
112899
|
}
|
|
112541
112900
|
}
|
|
112542
112901
|
return 0;
|
|
112543
112902
|
} catch (err2) {
|
|
112544
|
-
console.error(
|
|
112903
|
+
console.error(import_picocolors28.default.red(err2.message));
|
|
112545
112904
|
return 1;
|
|
112546
112905
|
}
|
|
112547
112906
|
}
|
|
112548
112907
|
|
|
112549
112908
|
// ../../packages/cli/src/commands/metrics.ts
|
|
112550
112909
|
init_cjs_shims();
|
|
112551
|
-
var
|
|
112910
|
+
var import_picocolors29 = __toESM(require_picocolors(), 1);
|
|
112552
112911
|
var METRICS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
112553
112912
|
|
|
112554
112913
|
Evidence used
|
|
@@ -112593,10 +112952,10 @@ function anomalyColor(anomaly) {
|
|
|
112593
112952
|
case "latency-spike":
|
|
112594
112953
|
case "error-rate-change":
|
|
112595
112954
|
case "throughput-drop":
|
|
112596
|
-
return
|
|
112955
|
+
return import_picocolors29.default.red;
|
|
112597
112956
|
case "queue-growth":
|
|
112598
112957
|
case "change":
|
|
112599
|
-
return
|
|
112958
|
+
return import_picocolors29.default.yellow;
|
|
112600
112959
|
default:
|
|
112601
112960
|
return (s5) => s5;
|
|
112602
112961
|
}
|
|
@@ -112623,7 +112982,7 @@ async function runMetrics(hint, opts) {
|
|
|
112623
112982
|
const metrics = metricsProviderFromConfig(config2);
|
|
112624
112983
|
if (metrics === null) {
|
|
112625
112984
|
console.error(
|
|
112626
|
-
|
|
112985
|
+
import_picocolors29.default.red(
|
|
112627
112986
|
"Grafana not configured \u2014 set GRAFANA_URL + GRAFANA_USER + GRAFANA_PASSWORD"
|
|
112628
112987
|
)
|
|
112629
112988
|
);
|
|
@@ -112631,7 +112990,7 @@ async function runMetrics(hint, opts) {
|
|
|
112631
112990
|
}
|
|
112632
112991
|
const health = await metrics.health();
|
|
112633
112992
|
if (!health.ok) {
|
|
112634
|
-
console.error(
|
|
112993
|
+
console.error(import_picocolors29.default.red(`Grafana unreachable: ${health.detail}`));
|
|
112635
112994
|
return 1;
|
|
112636
112995
|
}
|
|
112637
112996
|
const stepNum = opts.step !== void 0 ? Number(opts.step) : void 0;
|
|
@@ -112660,14 +113019,14 @@ async function runMetrics(hint, opts) {
|
|
|
112660
113019
|
return 0;
|
|
112661
113020
|
}
|
|
112662
113021
|
if (series.length === 0) {
|
|
112663
|
-
console.log(
|
|
113022
|
+
console.log(import_picocolors29.default.dim("No series returned."));
|
|
112664
113023
|
return 0;
|
|
112665
113024
|
}
|
|
112666
113025
|
for (const s5 of series) {
|
|
112667
113026
|
const summary = summarize(s5);
|
|
112668
113027
|
const label = labelStr(s5.labels);
|
|
112669
113028
|
console.log(
|
|
112670
|
-
` ${
|
|
113029
|
+
` ${import_picocolors29.default.cyan(label || "(no labels)")} last=${import_picocolors29.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
|
|
112671
113030
|
);
|
|
112672
113031
|
}
|
|
112673
113032
|
return 0;
|
|
@@ -112682,36 +113041,36 @@ async function runMetrics(hint, opts) {
|
|
|
112682
113041
|
if (findings2.length === 0) {
|
|
112683
113042
|
if (hint !== void 0) {
|
|
112684
113043
|
const tokens = extractHintTokens(hint);
|
|
112685
|
-
console.log(
|
|
113044
|
+
console.log(import_picocolors29.default.dim(`No panels matched hint "${hint}".`));
|
|
112686
113045
|
if (tokens.length > 0) {
|
|
112687
|
-
console.log(
|
|
113046
|
+
console.log(import_picocolors29.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
|
|
112688
113047
|
}
|
|
112689
113048
|
console.log(
|
|
112690
|
-
|
|
112691
|
-
` Tip: run ${
|
|
113049
|
+
import_picocolors29.default.dim(
|
|
113050
|
+
` Tip: run ${import_picocolors29.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
|
|
112692
113051
|
)
|
|
112693
113052
|
);
|
|
112694
113053
|
} else {
|
|
112695
|
-
console.log(
|
|
113054
|
+
console.log(import_picocolors29.default.dim("No panels found in configured Grafana dashboards."));
|
|
112696
113055
|
}
|
|
112697
113056
|
return 0;
|
|
112698
113057
|
}
|
|
112699
113058
|
const hintSuffix = hint !== void 0 ? ` (hint: "${hint}")` : "";
|
|
112700
113059
|
const matchSources = hint !== void 0 ? [...new Set(findings2.map((f5) => f5.matchSource).filter(Boolean))] : [];
|
|
112701
|
-
const matchSuffix = matchSources.length > 0 ?
|
|
113060
|
+
const matchSuffix = matchSources.length > 0 ? import_picocolors29.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
|
|
112702
113061
|
console.log(
|
|
112703
|
-
|
|
113062
|
+
import_picocolors29.default.bold(
|
|
112704
113063
|
`Grafana metrics${hintSuffix} \u2014 ${findings2.length} series across panels`
|
|
112705
113064
|
) + matchSuffix
|
|
112706
113065
|
);
|
|
112707
113066
|
console.log(
|
|
112708
|
-
|
|
113067
|
+
import_picocolors29.default.dim(
|
|
112709
113068
|
` window: ${new Date(from * 1e3).toISOString()} \u2013 ${new Date(to * 1e3).toISOString()}`
|
|
112710
113069
|
)
|
|
112711
113070
|
);
|
|
112712
113071
|
console.log("");
|
|
112713
113072
|
if (flagged.length === 0) {
|
|
112714
|
-
console.log(
|
|
113073
|
+
console.log(import_picocolors29.default.green(" No anomalies detected."));
|
|
112715
113074
|
} else {
|
|
112716
113075
|
for (const f5 of flagged) {
|
|
112717
113076
|
const color = anomalyColor(f5.anomaly);
|
|
@@ -112720,14 +113079,14 @@ async function runMetrics(hint, opts) {
|
|
|
112720
113079
|
const lblStr = labelStr(f5.labels);
|
|
112721
113080
|
console.log(
|
|
112722
113081
|
color(
|
|
112723
|
-
` ${label.padEnd(22)} ${f5.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f5.baselineAvg)} -> ${fmtNum(f5.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${
|
|
113082
|
+
` ${label.padEnd(22)} ${f5.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f5.baselineAvg)} -> ${fmtNum(f5.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors29.default.dim(lblStr)}` : "")
|
|
112724
113083
|
)
|
|
112725
113084
|
);
|
|
112726
113085
|
}
|
|
112727
113086
|
}
|
|
112728
113087
|
if (ok.length > 0) {
|
|
112729
113088
|
console.log("");
|
|
112730
|
-
console.log(
|
|
113089
|
+
console.log(import_picocolors29.default.dim(` ${ok.length} panel series with no anomaly.`));
|
|
112731
113090
|
}
|
|
112732
113091
|
if (opts.ai) {
|
|
112733
113092
|
const result = await renderAiInterpretation({
|
|
@@ -112742,19 +113101,19 @@ async function runMetrics(hint, opts) {
|
|
|
112742
113101
|
});
|
|
112743
113102
|
console.log("\n" + renderInterpretation(result));
|
|
112744
113103
|
if (!result.ok) {
|
|
112745
|
-
console.error(
|
|
113104
|
+
console.error(import_picocolors29.default.yellow(`[ai] ${result.warning}`));
|
|
112746
113105
|
}
|
|
112747
113106
|
}
|
|
112748
113107
|
return 0;
|
|
112749
113108
|
} catch (err2) {
|
|
112750
|
-
console.error(
|
|
113109
|
+
console.error(import_picocolors29.default.red(err2.message));
|
|
112751
113110
|
return 1;
|
|
112752
113111
|
}
|
|
112753
113112
|
}
|
|
112754
113113
|
|
|
112755
113114
|
// ../../packages/cli/src/commands/state.ts
|
|
112756
113115
|
init_cjs_shims();
|
|
112757
|
-
var
|
|
113116
|
+
var import_picocolors30 = __toESM(require_picocolors(), 1);
|
|
112758
113117
|
var STATE_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
112759
113118
|
|
|
112760
113119
|
Evidence used
|
|
@@ -112780,13 +113139,13 @@ async function runState(opts) {
|
|
|
112780
113139
|
try {
|
|
112781
113140
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
112782
113141
|
} catch (err2) {
|
|
112783
|
-
console.error(
|
|
113142
|
+
console.error(import_picocolors30.default.red(err2.message));
|
|
112784
113143
|
return 1;
|
|
112785
113144
|
}
|
|
112786
113145
|
const mongo = mongoForEnv(renv);
|
|
112787
113146
|
if (mongo === null) {
|
|
112788
113147
|
console.error(
|
|
112789
|
-
|
|
113148
|
+
import_picocolors30.default.red(
|
|
112790
113149
|
`No MongoDB connector configured for ${renv.project}/${renv.env} (set the project's Mongo URL env var).`
|
|
112791
113150
|
)
|
|
112792
113151
|
);
|
|
@@ -112794,7 +113153,7 @@ async function runState(opts) {
|
|
|
112794
113153
|
}
|
|
112795
113154
|
const health = await mongo.health();
|
|
112796
113155
|
if (!health.ok) {
|
|
112797
|
-
console.error(
|
|
113156
|
+
console.error(import_picocolors30.default.red(`MongoDB unreachable: ${health.detail}`));
|
|
112798
113157
|
await mongo.close();
|
|
112799
113158
|
return 1;
|
|
112800
113159
|
}
|
|
@@ -112820,38 +113179,38 @@ async function runState(opts) {
|
|
|
112820
113179
|
);
|
|
112821
113180
|
return 0;
|
|
112822
113181
|
}
|
|
112823
|
-
const discoveryNote = analysis.autoDiscovered ?
|
|
113182
|
+
const discoveryNote = analysis.autoDiscovered ? import_picocolors30.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
|
|
112824
113183
|
console.log(
|
|
112825
|
-
|
|
113184
|
+
import_picocolors30.default.bold("State analysis") + import_picocolors30.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
|
|
112826
113185
|
);
|
|
112827
113186
|
console.log("");
|
|
112828
113187
|
let signals = 0;
|
|
112829
113188
|
for (const c4 of analysis.collections) {
|
|
112830
113189
|
const flags2 = [];
|
|
112831
113190
|
if (c4.isStale === true) {
|
|
112832
|
-
flags2.push(
|
|
113191
|
+
flags2.push(import_picocolors30.default.yellow(`stale (last ${c4.dateField}, ~${Math.round(c4.ageHours ?? 0)}h)`));
|
|
112833
113192
|
}
|
|
112834
113193
|
for (const a2 of c4.anomalies) {
|
|
112835
|
-
flags2.push(
|
|
113194
|
+
flags2.push(import_picocolors30.default.red(`${a2.count}\xD7 "${a2.value}"`));
|
|
112836
113195
|
signals += 1;
|
|
112837
113196
|
}
|
|
112838
113197
|
if (c4.isStale === true) signals += 1;
|
|
112839
|
-
const head = ` ${
|
|
112840
|
-
console.log(flags2.length > 0 ? `${head} ${flags2.join(" ")}` :
|
|
113198
|
+
const head = ` ${import_picocolors30.default.bold(c4.collection.padEnd(18).slice(0, 18))} ${import_picocolors30.default.dim(String(c4.count).padStart(8) + " docs")}`;
|
|
113199
|
+
console.log(flags2.length > 0 ? `${head} ${flags2.join(" ")}` : import_picocolors30.default.dim(head));
|
|
112841
113200
|
}
|
|
112842
113201
|
console.log("");
|
|
112843
113202
|
if (analysis.collections.length === 0) {
|
|
112844
113203
|
console.log(
|
|
112845
|
-
|
|
113204
|
+
import_picocolors30.default.yellow(" No collections discovered in this database.") + import_picocolors30.default.dim(" Verify the database name in your MongoDB connector config.")
|
|
112846
113205
|
);
|
|
112847
113206
|
} else {
|
|
112848
113207
|
const scope = analysis.autoDiscovered ? "collection(s)" : "allowlisted collection(s)";
|
|
112849
113208
|
console.log(
|
|
112850
|
-
signals > 0 ? ` ${
|
|
113209
|
+
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}.`)
|
|
112851
113210
|
);
|
|
112852
113211
|
if (analysis.autoDiscovered) {
|
|
112853
113212
|
console.log(
|
|
112854
|
-
|
|
113213
|
+
import_picocolors30.default.dim(
|
|
112855
113214
|
` Tip: add a "collections" list to your MongoDB connector config to restrict analysis.`
|
|
112856
113215
|
)
|
|
112857
113216
|
);
|
|
@@ -112869,7 +113228,7 @@ async function runState(opts) {
|
|
|
112869
113228
|
});
|
|
112870
113229
|
console.log("\n" + renderInterpretation(result));
|
|
112871
113230
|
if (!result.ok) {
|
|
112872
|
-
console.error(
|
|
113231
|
+
console.error(import_picocolors30.default.yellow(`[ai] ${result.warning}`));
|
|
112873
113232
|
}
|
|
112874
113233
|
}
|
|
112875
113234
|
return 0;
|
|
@@ -112877,20 +113236,20 @@ async function runState(opts) {
|
|
|
112877
113236
|
await mongo.close();
|
|
112878
113237
|
}
|
|
112879
113238
|
} catch (err2) {
|
|
112880
|
-
console.error(
|
|
113239
|
+
console.error(import_picocolors30.default.red(err2.message));
|
|
112881
113240
|
return 1;
|
|
112882
113241
|
}
|
|
112883
113242
|
}
|
|
112884
113243
|
|
|
112885
113244
|
// ../../packages/cli/src/commands/init.ts
|
|
112886
113245
|
init_cjs_shims();
|
|
112887
|
-
var
|
|
112888
|
-
var
|
|
113246
|
+
var import_picocolors31 = __toESM(require_picocolors(), 1);
|
|
113247
|
+
var import_node_path18 = require("path");
|
|
112889
113248
|
async function runInit(opts) {
|
|
112890
113249
|
try {
|
|
112891
113250
|
const cwd = process.cwd();
|
|
112892
|
-
const root = opts.path ? (0,
|
|
112893
|
-
const name2 = opts.name ?? (0,
|
|
113251
|
+
const root = opts.path ? (0, import_node_path18.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
|
|
113252
|
+
const name2 = opts.name ?? (0, import_node_path18.basename)(root);
|
|
112894
113253
|
const envName = opts.env ?? "production";
|
|
112895
113254
|
const repo = { name: name2, path: root };
|
|
112896
113255
|
if (opts.source) repo["source"] = { hostUrl: opts.source };
|
|
@@ -112905,65 +113264,65 @@ async function runInit(opts) {
|
|
|
112905
113264
|
const configPath = writeLocalConfig(root, file);
|
|
112906
113265
|
registerProject(name2, root, configPath);
|
|
112907
113266
|
ensureProjectGitignore(root);
|
|
112908
|
-
console.log(`${
|
|
112909
|
-
console.log(
|
|
112910
|
-
console.log(
|
|
113267
|
+
console.log(`${import_picocolors31.default.green("\u2713")} Initialized Horus project ${import_picocolors31.default.bold(name2)}`);
|
|
113268
|
+
console.log(import_picocolors31.default.dim(` config: ${configPath}`));
|
|
113269
|
+
console.log(import_picocolors31.default.dim(` registered: horus investigate --name ${name2} "<hint>"`));
|
|
112911
113270
|
if (!opts.source) {
|
|
112912
113271
|
console.log(
|
|
112913
|
-
|
|
113272
|
+
import_picocolors31.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
|
|
112914
113273
|
);
|
|
112915
113274
|
}
|
|
112916
113275
|
console.log(
|
|
112917
|
-
|
|
113276
|
+
import_picocolors31.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
|
|
112918
113277
|
);
|
|
112919
113278
|
console.log(
|
|
112920
|
-
|
|
113279
|
+
import_picocolors31.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
|
|
112921
113280
|
);
|
|
112922
113281
|
return 0;
|
|
112923
113282
|
} catch (err2) {
|
|
112924
|
-
console.error(
|
|
113283
|
+
console.error(import_picocolors31.default.red(err2.message));
|
|
112925
113284
|
return 1;
|
|
112926
113285
|
}
|
|
112927
113286
|
}
|
|
112928
113287
|
|
|
112929
113288
|
// ../../packages/cli/src/commands/projects.ts
|
|
112930
113289
|
init_cjs_shims();
|
|
112931
|
-
var
|
|
113290
|
+
var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
112932
113291
|
async function runProjects() {
|
|
112933
113292
|
const reg = readRegistry();
|
|
112934
113293
|
const names = Object.keys(reg.projects).sort();
|
|
112935
113294
|
if (names.length === 0) {
|
|
112936
113295
|
console.log(
|
|
112937
|
-
|
|
113296
|
+
import_picocolors32.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
|
|
112938
113297
|
);
|
|
112939
113298
|
return 0;
|
|
112940
113299
|
}
|
|
112941
|
-
console.log(
|
|
113300
|
+
console.log(import_picocolors32.default.bold("Registered Horus projects"));
|
|
112942
113301
|
console.log("");
|
|
112943
113302
|
for (const name2 of names) {
|
|
112944
113303
|
const entry2 = reg.projects[name2];
|
|
112945
113304
|
if (entry2 === void 0) continue;
|
|
112946
|
-
console.log(` ${
|
|
113305
|
+
console.log(` ${import_picocolors32.default.bold(name2.padEnd(22))} ${import_picocolors32.default.dim(entry2.root)}`);
|
|
112947
113306
|
}
|
|
112948
113307
|
console.log("");
|
|
112949
|
-
console.log(
|
|
113308
|
+
console.log(import_picocolors32.default.dim(' horus investigate --name <name> "<hint>"'));
|
|
112950
113309
|
return 0;
|
|
112951
113310
|
}
|
|
112952
113311
|
|
|
112953
113312
|
// ../../packages/cli/src/commands/setup.ts
|
|
112954
113313
|
init_cjs_shims();
|
|
112955
|
-
var
|
|
113314
|
+
var import_picocolors33 = __toESM(require_picocolors(), 1);
|
|
112956
113315
|
var DEFAULT_DB_URL3 = "postgresql://horus:horus@localhost:5433/horus";
|
|
112957
113316
|
async function runSetup(opts) {
|
|
112958
113317
|
const write = opts.write ?? ((line2) => console.log(line2));
|
|
112959
113318
|
let ok = true;
|
|
112960
|
-
write(
|
|
113319
|
+
write(import_picocolors33.default.bold("\nHorus setup\n"));
|
|
112961
113320
|
const backendVersion = await getSourceVersion();
|
|
112962
113321
|
if (backendVersion === null) {
|
|
112963
113322
|
ok = false;
|
|
112964
|
-
write(` ${
|
|
113323
|
+
write(` ${import_picocolors33.default.red("\u25CF")} Horus source-intelligence backend not found`);
|
|
112965
113324
|
write(
|
|
112966
|
-
|
|
113325
|
+
import_picocolors33.default.dim(
|
|
112967
113326
|
` install it (Python 3.11+ required):
|
|
112968
113327
|
curl -fsSL https://horus.sh/install.sh | bash
|
|
112969
113328
|
ensure ~/.local/bin is on your PATH`
|
|
@@ -112972,17 +113331,17 @@ async function runSetup(opts) {
|
|
|
112972
113331
|
} else if (backendVersion !== PINNED_SOURCE_VERSION) {
|
|
112973
113332
|
ok = false;
|
|
112974
113333
|
write(
|
|
112975
|
-
` ${
|
|
113334
|
+
` ${import_picocolors33.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors33.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
|
|
112976
113335
|
);
|
|
112977
113336
|
write(
|
|
112978
|
-
|
|
113337
|
+
import_picocolors33.default.dim(
|
|
112979
113338
|
` update it:
|
|
112980
113339
|
horus update`
|
|
112981
113340
|
)
|
|
112982
113341
|
);
|
|
112983
113342
|
} else {
|
|
112984
113343
|
write(
|
|
112985
|
-
` ${
|
|
113344
|
+
` ${import_picocolors33.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors33.default.dim(`(${backendVersion})`)
|
|
112986
113345
|
);
|
|
112987
113346
|
}
|
|
112988
113347
|
let dbUrl = process.env["DATABASE_URL"] ?? DEFAULT_DB_URL3;
|
|
@@ -112994,11 +113353,11 @@ async function runSetup(opts) {
|
|
|
112994
113353
|
}
|
|
112995
113354
|
const db = await checkDatabase(dbUrl);
|
|
112996
113355
|
if (db.reachable) {
|
|
112997
|
-
write(` ${
|
|
113356
|
+
write(` ${import_picocolors33.default.green("\u25CF")} Postgres reachable ${import_picocolors33.default.dim(`(${db.schemaDetail})`)}`);
|
|
112998
113357
|
if (!db.schemaReady) {
|
|
112999
113358
|
ok = false;
|
|
113000
113359
|
write(
|
|
113001
|
-
|
|
113360
|
+
import_picocolors33.default.dim(
|
|
113002
113361
|
` schema not applied \u2014 run migrations:
|
|
113003
113362
|
from the Horus repo: pnpm db migrate
|
|
113004
113363
|
or apply the schema directly: https://github.com/meritt-dev/horus/tree/master/packages/db/drizzle`
|
|
@@ -113007,9 +113366,9 @@ async function runSetup(opts) {
|
|
|
113007
113366
|
}
|
|
113008
113367
|
} else {
|
|
113009
113368
|
ok = false;
|
|
113010
|
-
write(` ${
|
|
113369
|
+
write(` ${import_picocolors33.default.red("\u25CF")} Postgres unreachable`);
|
|
113011
113370
|
write(
|
|
113012
|
-
|
|
113371
|
+
import_picocolors33.default.dim(
|
|
113013
113372
|
` start a local instance:
|
|
113014
113373
|
docker run -d --name horus-db \\
|
|
113015
113374
|
-e POSTGRES_USER=horus -e POSTGRES_PASSWORD=horus -e POSTGRES_DB=horus \\
|
|
@@ -113034,10 +113393,10 @@ async function runSetup(opts) {
|
|
|
113034
113393
|
if (!health.ok) {
|
|
113035
113394
|
ok = false;
|
|
113036
113395
|
write(
|
|
113037
|
-
` ${
|
|
113396
|
+
` ${import_picocolors33.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors33.default.bold(repo.name)} ` + import_picocolors33.default.dim(`(${repoHostUrl})`)
|
|
113038
113397
|
);
|
|
113039
113398
|
write(
|
|
113040
|
-
|
|
113399
|
+
import_picocolors33.default.dim(
|
|
113041
113400
|
` start the source intelligence host:
|
|
113042
113401
|
cd ${repo.path}
|
|
113043
113402
|
horus index`
|
|
@@ -113048,10 +113407,10 @@ async function runSetup(opts) {
|
|
|
113048
113407
|
if (count === 0) {
|
|
113049
113408
|
ok = false;
|
|
113050
113409
|
write(
|
|
113051
|
-
` ${
|
|
113410
|
+
` ${import_picocolors33.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors33.default.bold(repo.name)} is not indexed`
|
|
113052
113411
|
);
|
|
113053
113412
|
write(
|
|
113054
|
-
|
|
113413
|
+
import_picocolors33.default.dim(
|
|
113055
113414
|
` index the repo:
|
|
113056
113415
|
cd ${repo.path}
|
|
113057
113416
|
horus index`
|
|
@@ -113059,7 +113418,7 @@ async function runSetup(opts) {
|
|
|
113059
113418
|
);
|
|
113060
113419
|
} else {
|
|
113061
113420
|
write(
|
|
113062
|
-
` ${
|
|
113421
|
+
` ${import_picocolors33.default.green("\u25CF")} ${import_picocolors33.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors33.default.dim(`(${repoHostUrl})`)
|
|
113063
113422
|
);
|
|
113064
113423
|
}
|
|
113065
113424
|
}
|
|
@@ -113069,10 +113428,10 @@ async function runSetup(opts) {
|
|
|
113069
113428
|
write("");
|
|
113070
113429
|
if (ok) {
|
|
113071
113430
|
write(
|
|
113072
|
-
|
|
113431
|
+
import_picocolors33.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
|
|
113073
113432
|
);
|
|
113074
113433
|
} else {
|
|
113075
|
-
write(
|
|
113434
|
+
write(import_picocolors33.default.yellow("Resolve the items above, then re-run `horus setup`."));
|
|
113076
113435
|
}
|
|
113077
113436
|
return ok ? 0 : 1;
|
|
113078
113437
|
}
|
|
@@ -113080,13 +113439,13 @@ async function runSetup(opts) {
|
|
|
113080
113439
|
// ../../packages/cli/src/commands/connect.ts
|
|
113081
113440
|
init_cjs_shims();
|
|
113082
113441
|
var import_node_readline = require("readline");
|
|
113083
|
-
var
|
|
113084
|
-
var
|
|
113442
|
+
var import_node_child_process7 = require("child_process");
|
|
113443
|
+
var import_picocolors36 = __toESM(require_picocolors(), 1);
|
|
113085
113444
|
|
|
113086
113445
|
// ../../packages/cli/src/lib/tty-selector.ts
|
|
113087
113446
|
init_cjs_shims();
|
|
113088
113447
|
var import_node_tty = require("tty");
|
|
113089
|
-
var
|
|
113448
|
+
var import_picocolors34 = __toESM(require_picocolors(), 1);
|
|
113090
113449
|
var ExitPromptError = class extends Error {
|
|
113091
113450
|
constructor(message = "Prompt was cancelled") {
|
|
113092
113451
|
super(message);
|
|
@@ -113142,7 +113501,7 @@ async function checkboxSearch(opts) {
|
|
|
113142
113501
|
clear();
|
|
113143
113502
|
const lines = [];
|
|
113144
113503
|
lines.push(
|
|
113145
|
-
`? ${
|
|
113504
|
+
`? ${import_picocolors34.default.bold(message)} ${import_picocolors34.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
|
|
113146
113505
|
);
|
|
113147
113506
|
const visible = filtered.slice(visibleOffset, visibleOffset + pageSize);
|
|
113148
113507
|
for (let i3 = 0; i3 < visible.length; i3++) {
|
|
@@ -113150,29 +113509,29 @@ async function checkboxSearch(opts) {
|
|
|
113150
113509
|
const choice = visible[i3];
|
|
113151
113510
|
const isCursor = idx === cursor;
|
|
113152
113511
|
const isSelected = selected.has(choice);
|
|
113153
|
-
const marker = isSelected ?
|
|
113512
|
+
const marker = isSelected ? import_picocolors34.default.green("\u25CF") : " ";
|
|
113154
113513
|
const prefix = `[${marker}]`;
|
|
113155
|
-
const label = isCursor ?
|
|
113156
|
-
const pointer = isCursor ?
|
|
113514
|
+
const label = isCursor ? import_picocolors34.default.cyan(choice) : choice;
|
|
113515
|
+
const pointer = isCursor ? import_picocolors34.default.cyan("\u276F") : " ";
|
|
113157
113516
|
lines.push(` ${pointer} ${prefix} ${label}`);
|
|
113158
113517
|
}
|
|
113159
113518
|
if (filtered.length === 0) {
|
|
113160
|
-
lines.push(
|
|
113519
|
+
lines.push(import_picocolors34.default.dim(" No matches"));
|
|
113161
113520
|
}
|
|
113162
113521
|
const statusParts = [
|
|
113163
113522
|
`${selected.size} selected`,
|
|
113164
113523
|
`${filtered.length}/${choices.length} matches`
|
|
113165
113524
|
];
|
|
113166
113525
|
if (filter) statusParts.push(`filter: ${filter}`);
|
|
113167
|
-
lines.push(
|
|
113526
|
+
lines.push(import_picocolors34.default.dim(` ${statusParts.join(" \xB7 ")}`));
|
|
113168
113527
|
output.write(lines.join("\n"));
|
|
113169
113528
|
lastRenderLines = lines.length;
|
|
113170
113529
|
}
|
|
113171
113530
|
function finish(values2) {
|
|
113172
113531
|
cleanup();
|
|
113173
|
-
const summary = values2.length > 0 ? values2.join(", ") :
|
|
113532
|
+
const summary = values2.length > 0 ? values2.join(", ") : import_picocolors34.default.dim("none");
|
|
113174
113533
|
output.write(`
|
|
113175
|
-
? ${
|
|
113534
|
+
? ${import_picocolors34.default.bold(message)} ${summary}
|
|
113176
113535
|
`);
|
|
113177
113536
|
resolve9(values2);
|
|
113178
113537
|
}
|
|
@@ -113263,10 +113622,10 @@ async function checkboxSearch(opts) {
|
|
|
113263
113622
|
|
|
113264
113623
|
// ../../packages/cli/src/commands/connect-ai.ts
|
|
113265
113624
|
init_cjs_shims();
|
|
113266
|
-
var
|
|
113267
|
-
var
|
|
113625
|
+
var import_node_child_process6 = require("child_process");
|
|
113626
|
+
var import_picocolors35 = __toESM(require_picocolors(), 1);
|
|
113268
113627
|
function cliInstalled(id) {
|
|
113269
|
-
const r = (0,
|
|
113628
|
+
const r = (0, import_node_child_process6.spawnSync)(id, ["--version"], { stdio: "ignore", timeout: 2e3 });
|
|
113270
113629
|
return !r.error;
|
|
113271
113630
|
}
|
|
113272
113631
|
async function probeAnthropic(apiKey) {
|
|
@@ -113292,13 +113651,13 @@ async function runConnectAi(opts) {
|
|
|
113292
113651
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
113293
113652
|
const configPath = discoverLocalConfig(cwd) ?? localConfigPath(root);
|
|
113294
113653
|
const installed = LOCAL_PROVIDER_IDS.filter((id) => cliInstalled(id));
|
|
113295
|
-
console.log(
|
|
113654
|
+
console.log(import_picocolors35.default.bold("\nAI providers"));
|
|
113296
113655
|
console.log(
|
|
113297
|
-
|
|
113656
|
+
import_picocolors35.default.dim(
|
|
113298
113657
|
` Local CLIs on PATH: ${installed.length > 0 ? installed.join(", ") : "none detected"}`
|
|
113299
113658
|
)
|
|
113300
113659
|
);
|
|
113301
|
-
console.log(
|
|
113660
|
+
console.log(import_picocolors35.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
|
|
113302
113661
|
let provider = opts.provider;
|
|
113303
113662
|
if (provider === void 0) {
|
|
113304
113663
|
if (isInteractive()) {
|
|
@@ -113309,7 +113668,7 @@ async function runConnectAi(opts) {
|
|
|
113309
113668
|
}
|
|
113310
113669
|
}
|
|
113311
113670
|
if (!AI_PROVIDERS.includes(provider)) {
|
|
113312
|
-
console.error(
|
|
113671
|
+
console.error(import_picocolors35.default.red(`Unknown AI provider: ${provider}`) + import_picocolors35.default.dim(`
|
|
113313
113672
|
supported: ${AI_PROVIDERS.join(", ")}`));
|
|
113314
113673
|
return 1;
|
|
113315
113674
|
}
|
|
@@ -113322,7 +113681,7 @@ async function runConnectAi(opts) {
|
|
|
113322
113681
|
}
|
|
113323
113682
|
if (!apiKey) {
|
|
113324
113683
|
console.error(
|
|
113325
|
-
|
|
113684
|
+
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.")
|
|
113326
113685
|
);
|
|
113327
113686
|
return 1;
|
|
113328
113687
|
}
|
|
@@ -113330,28 +113689,28 @@ async function runConnectAi(opts) {
|
|
|
113330
113689
|
const probe2 = await probeAnthropic(apiKey);
|
|
113331
113690
|
if (!probe2.ok) {
|
|
113332
113691
|
console.error(`
|
|
113333
|
-
${
|
|
113334
|
-
console.error(
|
|
113692
|
+
${import_picocolors35.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
|
|
113693
|
+
console.error(import_picocolors35.default.dim(" Fix the key and retry, or pass --no-test to skip."));
|
|
113335
113694
|
return 1;
|
|
113336
113695
|
}
|
|
113337
113696
|
console.log(`
|
|
113338
|
-
${
|
|
113697
|
+
${import_picocolors35.default.green("\u2713")} anthropic ${import_picocolors35.default.dim(`(${probe2.detail})`)}`);
|
|
113339
113698
|
}
|
|
113340
113699
|
if (opts.model) aiBlock["anthropic"] = { model: opts.model };
|
|
113341
113700
|
storedKey = apiKey;
|
|
113342
113701
|
} else {
|
|
113343
113702
|
if (!installed.includes(provider)) {
|
|
113344
113703
|
console.error(
|
|
113345
|
-
|
|
113704
|
+
import_picocolors35.default.red(`${provider} CLI not found on PATH.`) + import_picocolors35.default.dim(`
|
|
113346
113705
|
Install it first, or choose anthropic.`)
|
|
113347
113706
|
);
|
|
113348
113707
|
return 1;
|
|
113349
113708
|
}
|
|
113350
113709
|
const desc2 = DEFAULT_LOCAL_PROVIDER_REGISTRY.get(provider);
|
|
113351
113710
|
console.log(`
|
|
113352
|
-
${
|
|
113711
|
+
${import_picocolors35.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors35.default.dim("detected on PATH")}`);
|
|
113353
113712
|
console.log(
|
|
113354
|
-
|
|
113713
|
+
import_picocolors35.default.dim(
|
|
113355
113714
|
" Note: `investigate --ai` currently uses the Anthropic API for narrative.\n Local-CLI narrative generation is recorded as your preference for future use."
|
|
113356
113715
|
)
|
|
113357
113716
|
);
|
|
@@ -113369,15 +113728,15 @@ ${import_picocolors34.default.green("\u2713")} ${desc2?.displayName ?? provider}
|
|
|
113369
113728
|
});
|
|
113370
113729
|
}
|
|
113371
113730
|
console.log(`
|
|
113372
|
-
${
|
|
113373
|
-
console.log(
|
|
113731
|
+
${import_picocolors35.default.green("\u2713")} ${import_picocolors35.default.bold("ai")} provider saved \u2192 ${import_picocolors35.default.dim(configPath)}`);
|
|
113732
|
+
console.log(import_picocolors35.default.dim(` provider: ${provider}`));
|
|
113374
113733
|
if (storedKey && secretsPathStr) {
|
|
113375
|
-
console.log(
|
|
113734
|
+
console.log(import_picocolors35.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
|
|
113376
113735
|
}
|
|
113377
|
-
console.log(
|
|
113736
|
+
console.log(import_picocolors35.default.dim(' run: horus investigate "<hint>" --ai'));
|
|
113378
113737
|
return 0;
|
|
113379
113738
|
} catch (err2) {
|
|
113380
|
-
console.error(
|
|
113739
|
+
console.error(import_picocolors35.default.red(err2 instanceof Error ? err2.message : String(err2)));
|
|
113381
113740
|
return 1;
|
|
113382
113741
|
}
|
|
113383
113742
|
}
|
|
@@ -113409,7 +113768,7 @@ async function runConnect(type, opts) {
|
|
|
113409
113768
|
}
|
|
113410
113769
|
if (!SUPPORTED.includes(type)) {
|
|
113411
113770
|
console.error(
|
|
113412
|
-
|
|
113771
|
+
import_picocolors36.default.red(`Unknown connector type: ${type}`) + import_picocolors36.default.dim(`
|
|
113413
113772
|
supported: ${SUPPORTED.join(", ")}, ai`)
|
|
113414
113773
|
);
|
|
113415
113774
|
return 1;
|
|
@@ -113446,20 +113805,20 @@ async function runConnect(type, opts) {
|
|
|
113446
113805
|
if (!probeResult.ok) {
|
|
113447
113806
|
console.error(
|
|
113448
113807
|
`
|
|
113449
|
-
${
|
|
113808
|
+
${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.")
|
|
113450
113809
|
);
|
|
113451
113810
|
return 1;
|
|
113452
113811
|
}
|
|
113453
113812
|
console.log(
|
|
113454
113813
|
`
|
|
113455
|
-
${
|
|
113814
|
+
${import_picocolors36.default.green("\u2713")} ${connectorType} reachable ${import_picocolors36.default.dim(`(${probeResult.detail})`)}`
|
|
113456
113815
|
);
|
|
113457
113816
|
}
|
|
113458
113817
|
const hasLiteralCredentials = filled.url !== void 0 || filled.password !== void 0 || filled.username !== void 0 || filled.authToken !== void 0;
|
|
113459
113818
|
if (hasLiteralCredentials) {
|
|
113460
113819
|
if (isGitTracked(configPath, root)) {
|
|
113461
113820
|
console.error(
|
|
113462
|
-
|
|
113821
|
+
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(
|
|
113463
113822
|
" 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."
|
|
113464
113823
|
)
|
|
113465
113824
|
);
|
|
@@ -113469,18 +113828,18 @@ ${import_picocolors35.default.green("\u2713")} ${connectorType} reachable ${impo
|
|
|
113469
113828
|
}
|
|
113470
113829
|
patchLocalConnector(configPath, connectorType, patch, filled.env);
|
|
113471
113830
|
console.log(
|
|
113472
|
-
`${
|
|
113831
|
+
`${import_picocolors36.default.green("\u2713")} ${import_picocolors36.default.bold(connectorType)} connector saved \u2192 ${import_picocolors36.default.dim(configPath)}`
|
|
113473
113832
|
);
|
|
113474
113833
|
printSummary(connectorType, filled);
|
|
113475
|
-
console.log(
|
|
113834
|
+
console.log(import_picocolors36.default.dim(`
|
|
113476
113835
|
run: horus investigate "<hint>"`));
|
|
113477
113836
|
return 0;
|
|
113478
113837
|
} catch (err2) {
|
|
113479
113838
|
if (err2 instanceof ExitPromptError) {
|
|
113480
|
-
console.error(
|
|
113839
|
+
console.error(import_picocolors36.default.red("Cancelled."));
|
|
113481
113840
|
return 1;
|
|
113482
113841
|
}
|
|
113483
|
-
console.error(
|
|
113842
|
+
console.error(import_picocolors36.default.red(err2.message));
|
|
113484
113843
|
return 1;
|
|
113485
113844
|
}
|
|
113486
113845
|
}
|
|
@@ -113489,7 +113848,7 @@ async function fillInteractive(type, opts) {
|
|
|
113489
113848
|
if (!needsInteraction) return opts;
|
|
113490
113849
|
console.log(
|
|
113491
113850
|
`
|
|
113492
|
-
${
|
|
113851
|
+
${import_picocolors36.default.bold(`Connect ${type}`)} ${import_picocolors36.default.dim("(press Enter to skip optional fields)")}
|
|
113493
113852
|
`
|
|
113494
113853
|
);
|
|
113495
113854
|
const filled = { ...opts };
|
|
@@ -113555,7 +113914,7 @@ ${import_picocolors35.default.bold(`Connect ${type}`)} ${import_picocolors35.def
|
|
|
113555
113914
|
break;
|
|
113556
113915
|
case "redis": {
|
|
113557
113916
|
console.log(
|
|
113558
|
-
|
|
113917
|
+
import_picocolors36.default.dim(
|
|
113559
113918
|
" Tip: embed credentials directly in the URL \u2014 redis://:password@host:6379\n or enter the URL and password separately below."
|
|
113560
113919
|
)
|
|
113561
113920
|
);
|
|
@@ -113597,8 +113956,8 @@ function missingRequired(type, opts) {
|
|
|
113597
113956
|
}
|
|
113598
113957
|
function ask(label, placeholder = "", required2 = true) {
|
|
113599
113958
|
return new Promise((resolve9) => {
|
|
113600
|
-
const hint = placeholder ?
|
|
113601
|
-
const suffix = required2 ? "" :
|
|
113959
|
+
const hint = placeholder ? import_picocolors36.default.dim(` (${placeholder})`) : "";
|
|
113960
|
+
const suffix = required2 ? "" : import_picocolors36.default.dim(" [optional]");
|
|
113602
113961
|
process.stdout.write(` ${label}${suffix}${hint}: `);
|
|
113603
113962
|
const rl = (0, import_node_readline.createInterface)({
|
|
113604
113963
|
input: process.stdin,
|
|
@@ -113615,7 +113974,7 @@ function askPassword(label) {
|
|
|
113615
113974
|
return new Promise((resolve9) => {
|
|
113616
113975
|
const stdin = process.stdin;
|
|
113617
113976
|
if (typeof stdin.setRawMode === "function") {
|
|
113618
|
-
process.stdout.write(` ${label}${
|
|
113977
|
+
process.stdout.write(` ${label}${import_picocolors36.default.dim(" [optional]")}: `);
|
|
113619
113978
|
stdin.setRawMode(true);
|
|
113620
113979
|
stdin.resume();
|
|
113621
113980
|
let value = "";
|
|
@@ -113732,15 +114091,15 @@ function describeProbe(p5) {
|
|
|
113732
114091
|
return `${p5.keyCount} keys \xB7 ${p5.suggestedRoles.join("/")}${examples ? ` \xB7 ${examples}` : ""}`;
|
|
113733
114092
|
}
|
|
113734
114093
|
async function discoverAndSelectDbs(url, bullmqPrefix) {
|
|
113735
|
-
console.log(
|
|
114094
|
+
console.log(import_picocolors36.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
|
|
113736
114095
|
const probes = await probeRedisDatabases(url, { bullmqPrefix });
|
|
113737
114096
|
const nonEmpty = probes.filter((p5) => p5.reachable && p5.keyCount > 0);
|
|
113738
114097
|
if (nonEmpty.length === 0) {
|
|
113739
|
-
console.log(
|
|
114098
|
+
console.log(import_picocolors36.default.dim(" No populated DBs found."));
|
|
113740
114099
|
return [];
|
|
113741
114100
|
}
|
|
113742
114101
|
for (const p5 of nonEmpty) {
|
|
113743
|
-
console.log(` ${
|
|
114102
|
+
console.log(` ${import_picocolors36.default.bold(`DB ${p5.db}`)} ${import_picocolors36.default.dim("\xB7 " + describeProbe(p5))}`);
|
|
113744
114103
|
}
|
|
113745
114104
|
const byLabel = /* @__PURE__ */ new Map();
|
|
113746
114105
|
const choices = nonEmpty.map((p5) => {
|
|
@@ -113880,11 +114239,11 @@ function printSummary(type, opts) {
|
|
|
113880
114239
|
} else if (opts.dashboard) {
|
|
113881
114240
|
lines.push(` dashboard: ${opts.dashboard}`);
|
|
113882
114241
|
}
|
|
113883
|
-
if (lines.length > 0) console.log(
|
|
114242
|
+
if (lines.length > 0) console.log(import_picocolors36.default.dim(lines.join("\n")));
|
|
113884
114243
|
}
|
|
113885
114244
|
function isGitTracked(filePath, cwd) {
|
|
113886
114245
|
try {
|
|
113887
|
-
(0,
|
|
114246
|
+
(0, import_node_child_process7.execFileSync)("git", ["ls-files", "--error-unmatch", filePath], {
|
|
113888
114247
|
cwd,
|
|
113889
114248
|
stdio: "pipe"
|
|
113890
114249
|
});
|
|
@@ -113919,11 +114278,11 @@ async function askIndexSelection(indices) {
|
|
|
113919
114278
|
const shown = indices.slice(0, MAX_DISPLAY);
|
|
113920
114279
|
console.log("\n Available Elasticsearch indexes/data streams:");
|
|
113921
114280
|
shown.forEach((name2, i3) => {
|
|
113922
|
-
console.log(` ${
|
|
114281
|
+
console.log(` ${import_picocolors36.default.dim(`[${i3 + 1}]`)} ${name2}`);
|
|
113923
114282
|
});
|
|
113924
114283
|
if (indices.length > MAX_DISPLAY) {
|
|
113925
114284
|
console.log(
|
|
113926
|
-
|
|
114285
|
+
import_picocolors36.default.dim(
|
|
113927
114286
|
` \u2026 and ${indices.length - MAX_DISPLAY} more (type a pattern manually to match all)`
|
|
113928
114287
|
)
|
|
113929
114288
|
);
|
|
@@ -113967,11 +114326,11 @@ async function askDashboardSelection(dashboards) {
|
|
|
113967
114326
|
const shown = dashboards.slice(0, MAX_DISPLAY);
|
|
113968
114327
|
console.log("\n Available Grafana dashboards:");
|
|
113969
114328
|
shown.forEach((d3, i3) => {
|
|
113970
|
-
const folder = d3.folderTitle ?
|
|
113971
|
-
console.log(` ${
|
|
114329
|
+
const folder = d3.folderTitle ? import_picocolors36.default.dim(` (${d3.folderTitle})`) : "";
|
|
114330
|
+
console.log(` ${import_picocolors36.default.dim(`[${i3 + 1}]`)} ${d3.title}${folder}`);
|
|
113972
114331
|
});
|
|
113973
114332
|
if (dashboards.length > MAX_DISPLAY) {
|
|
113974
|
-
console.log(
|
|
114333
|
+
console.log(import_picocolors36.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
|
|
113975
114334
|
}
|
|
113976
114335
|
const input = (await ask(
|
|
113977
114336
|
` Select dashboards to use (e.g. 1,2 or Enter to type uid manually)`,
|
|
@@ -114021,16 +114380,17 @@ function redactUrl(raw) {
|
|
|
114021
114380
|
|
|
114022
114381
|
// ../../packages/cli/src/commands/stop.ts
|
|
114023
114382
|
init_cjs_shims();
|
|
114024
|
-
var
|
|
114025
|
-
var
|
|
114026
|
-
var
|
|
114027
|
-
var
|
|
114028
|
-
var
|
|
114029
|
-
var
|
|
114030
|
-
var unlinkAsync = (0,
|
|
114383
|
+
var import_node_child_process8 = require("child_process");
|
|
114384
|
+
var import_node_fs17 = require("fs");
|
|
114385
|
+
var import_node_util5 = require("util");
|
|
114386
|
+
var import_node_path19 = require("path");
|
|
114387
|
+
var import_picocolors37 = __toESM(require_picocolors(), 1);
|
|
114388
|
+
var execFileAsync2 = (0, import_node_util5.promisify)(import_node_child_process8.execFile);
|
|
114389
|
+
var unlinkAsync = (0, import_node_util5.promisify)(import_node_fs17.unlink);
|
|
114031
114390
|
var SPAWNED_HOST_FILE2 = "spawned-host.json";
|
|
114032
114391
|
var START_TIME_TOLERANCE_S = 60;
|
|
114033
114392
|
var STOP_WAIT_MS = 5e3;
|
|
114393
|
+
var STOP_KILL_WAIT_MS = 3e3;
|
|
114034
114394
|
var STOP_POLL_MS = 200;
|
|
114035
114395
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
114036
114396
|
async function runStop(opts) {
|
|
@@ -114042,19 +114402,19 @@ async function runStop(opts) {
|
|
|
114042
114402
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
114043
114403
|
const hostUrl = readSourceHostUrl(root);
|
|
114044
114404
|
if (!hostUrl) {
|
|
114045
|
-
console.log(
|
|
114405
|
+
console.log(import_picocolors37.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
|
|
114046
114406
|
return 0;
|
|
114047
114407
|
}
|
|
114048
114408
|
return await stopHost(root, hostUrl);
|
|
114049
114409
|
} catch (err2) {
|
|
114050
|
-
console.error(
|
|
114410
|
+
console.error(import_picocolors37.default.red(err2.message));
|
|
114051
114411
|
return 1;
|
|
114052
114412
|
}
|
|
114053
114413
|
}
|
|
114054
114414
|
async function stopHost(root, hostUrl) {
|
|
114055
114415
|
const port = extractPort(hostUrl);
|
|
114056
114416
|
if (port === null) {
|
|
114057
|
-
console.error(
|
|
114417
|
+
console.error(import_picocolors37.default.red(`Cannot determine port from host URL: ${hostUrl}`));
|
|
114058
114418
|
return 1;
|
|
114059
114419
|
}
|
|
114060
114420
|
const healthy = await isHostHealthy(hostUrl);
|
|
@@ -114064,12 +114424,12 @@ async function stopHost(root, hostUrl) {
|
|
|
114064
114424
|
if (!wrapperAlive) {
|
|
114065
114425
|
const res = await stopBackendServerPid(root, port);
|
|
114066
114426
|
if (res === "failed") return 1;
|
|
114067
|
-
if (res !== "stopped") console.log(
|
|
114427
|
+
if (res !== "stopped") console.log(import_picocolors37.default.dim(`Host ${hostUrl} is already stopped.`));
|
|
114068
114428
|
await cleanupSpawnedRecord(root);
|
|
114069
114429
|
return 0;
|
|
114070
114430
|
}
|
|
114071
114431
|
console.log(
|
|
114072
|
-
|
|
114432
|
+
import_picocolors37.default.dim(
|
|
114073
114433
|
`Host ${hostUrl} is unreachable but pid ${owned.pid} is still running \u2014 terminating a stuck host.`
|
|
114074
114434
|
)
|
|
114075
114435
|
);
|
|
@@ -114077,7 +114437,7 @@ async function stopHost(root, hostUrl) {
|
|
|
114077
114437
|
const spawned = readSpawnedHost(root);
|
|
114078
114438
|
if (spawned === null) {
|
|
114079
114439
|
console.error(
|
|
114080
|
-
|
|
114440
|
+
import_picocolors37.default.red(
|
|
114081
114441
|
`No ownership record found (.horus/${SPAWNED_HOST_FILE2} absent). Horus will not stop a host it did not spawn.`
|
|
114082
114442
|
)
|
|
114083
114443
|
);
|
|
@@ -114085,12 +114445,12 @@ async function stopHost(root, hostUrl) {
|
|
|
114085
114445
|
}
|
|
114086
114446
|
const recordError = validateSpawnedRecord(spawned);
|
|
114087
114447
|
if (recordError !== null) {
|
|
114088
|
-
console.error(
|
|
114448
|
+
console.error(import_picocolors37.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
|
|
114089
114449
|
return 1;
|
|
114090
114450
|
}
|
|
114091
114451
|
if (spawned.port !== port) {
|
|
114092
114452
|
console.error(
|
|
114093
|
-
|
|
114453
|
+
import_picocolors37.default.red(
|
|
114094
114454
|
`Ownership record port (${spawned.port}) does not match host URL port (${port}). Record may be stale.`
|
|
114095
114455
|
)
|
|
114096
114456
|
);
|
|
@@ -114098,7 +114458,7 @@ async function stopHost(root, hostUrl) {
|
|
|
114098
114458
|
}
|
|
114099
114459
|
if (spawned.root !== root) {
|
|
114100
114460
|
console.error(
|
|
114101
|
-
|
|
114461
|
+
import_picocolors37.default.red(
|
|
114102
114462
|
`Ownership record root (${spawned.root}) does not match resolved root (${root}). Record may be stale.`
|
|
114103
114463
|
)
|
|
114104
114464
|
);
|
|
@@ -114109,14 +114469,14 @@ async function stopHost(root, hostUrl) {
|
|
|
114109
114469
|
const res = await stopBackendServerPid(root, port);
|
|
114110
114470
|
if (res === "failed") return 1;
|
|
114111
114471
|
if (res !== "stopped") {
|
|
114112
|
-
console.log(
|
|
114472
|
+
console.log(import_picocolors37.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
|
|
114113
114473
|
}
|
|
114114
114474
|
await cleanupSpawnedRecord(root);
|
|
114115
114475
|
return 0;
|
|
114116
114476
|
}
|
|
114117
114477
|
if (!hostArgvRegex(port).test(info2.args)) {
|
|
114118
114478
|
console.error(
|
|
114119
|
-
|
|
114479
|
+
import_picocolors37.default.red(
|
|
114120
114480
|
`Pid ${spawned.pid} args do not match "horus-source host --port ${port}". Got: "${info2.args.slice(0, 120)}". Aborting for safety.`
|
|
114121
114481
|
)
|
|
114122
114482
|
);
|
|
@@ -114125,51 +114485,33 @@ async function stopHost(root, hostUrl) {
|
|
|
114125
114485
|
const startTs = new Date(spawned.startedAt).getTime();
|
|
114126
114486
|
const recordedAgeS = Math.round((Date.now() - startTs) / 1e3);
|
|
114127
114487
|
if (!Number.isFinite(info2.etimeSeconds)) {
|
|
114128
|
-
console.error(
|
|
114488
|
+
console.error(import_picocolors37.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
|
|
114129
114489
|
return 1;
|
|
114130
114490
|
}
|
|
114131
114491
|
if (Math.abs(info2.etimeSeconds - recordedAgeS) > START_TIME_TOLERANCE_S) {
|
|
114132
114492
|
console.error(
|
|
114133
|
-
|
|
114493
|
+
import_picocolors37.default.red(
|
|
114134
114494
|
`Pid ${spawned.pid} age mismatch: record says ~${recordedAgeS}s, process reports ${info2.etimeSeconds}s elapsed. Possible PID reuse \u2014 aborting for safety.`
|
|
114135
114495
|
)
|
|
114136
114496
|
);
|
|
114137
114497
|
return 1;
|
|
114138
114498
|
}
|
|
114139
|
-
let
|
|
114499
|
+
let terminated;
|
|
114140
114500
|
try {
|
|
114141
|
-
|
|
114142
|
-
signaled = true;
|
|
114501
|
+
terminated = await terminatePid(spawned.pid);
|
|
114143
114502
|
} catch (err2) {
|
|
114144
|
-
|
|
114145
|
-
|
|
114146
|
-
} else {
|
|
114147
|
-
console.error(import_picocolors36.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
|
|
114148
|
-
return 1;
|
|
114149
|
-
}
|
|
114503
|
+
console.error(import_picocolors37.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
|
|
114504
|
+
return 1;
|
|
114150
114505
|
}
|
|
114151
|
-
if (
|
|
114152
|
-
|
|
114153
|
-
|
|
114154
|
-
while (Date.now() < deadline) {
|
|
114155
|
-
await sleep(STOP_POLL_MS);
|
|
114156
|
-
if (await getProcessInfo(spawned.pid) === null) {
|
|
114157
|
-
exited = true;
|
|
114158
|
-
break;
|
|
114159
|
-
}
|
|
114160
|
-
}
|
|
114161
|
-
if (!exited) {
|
|
114162
|
-
console.error(
|
|
114163
|
-
import_picocolors36.default.red(
|
|
114164
|
-
`Host pid ${spawned.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`
|
|
114165
|
-
)
|
|
114166
|
-
);
|
|
114167
|
-
return 1;
|
|
114168
|
-
}
|
|
114169
|
-
console.log(
|
|
114170
|
-
`${import_picocolors36.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors36.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
|
|
114506
|
+
if (!terminated) {
|
|
114507
|
+
console.error(
|
|
114508
|
+
import_picocolors37.default.red(`Host pid ${spawned.pid} did not exit even after SIGKILL \u2014 could not reclaim the port.`)
|
|
114171
114509
|
);
|
|
114510
|
+
return 1;
|
|
114172
114511
|
}
|
|
114512
|
+
console.log(
|
|
114513
|
+
`${import_picocolors37.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors37.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
|
|
114514
|
+
);
|
|
114173
114515
|
if (await stopBackendServerPid(root, port) === "failed") return 1;
|
|
114174
114516
|
await cleanupSpawnedRecord(root);
|
|
114175
114517
|
return 0;
|
|
@@ -114181,7 +114523,7 @@ function hostArgvRegex(port) {
|
|
|
114181
114523
|
}
|
|
114182
114524
|
async function cleanupSpawnedRecord(root) {
|
|
114183
114525
|
try {
|
|
114184
|
-
await unlinkAsync((0,
|
|
114526
|
+
await unlinkAsync((0, import_node_path19.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
|
|
114185
114527
|
} catch {
|
|
114186
114528
|
}
|
|
114187
114529
|
}
|
|
@@ -114193,40 +114535,36 @@ async function stopBackendServerPid(root, port) {
|
|
|
114193
114535
|
const info2 = await getProcessInfo(rec.pid);
|
|
114194
114536
|
if (info2 === null) return "none";
|
|
114195
114537
|
if (!hostArgvRegex(port).test(info2.args)) {
|
|
114196
|
-
console.
|
|
114197
|
-
|
|
114198
|
-
`
|
|
114538
|
+
console.log(
|
|
114539
|
+
import_picocolors37.default.dim(
|
|
114540
|
+
`Host pid ${rec.pid} is now an unrelated process (stale source/host.json / PID reuse) \u2014 host already gone.`
|
|
114199
114541
|
)
|
|
114200
114542
|
);
|
|
114201
|
-
return "
|
|
114543
|
+
return "none";
|
|
114202
114544
|
}
|
|
114545
|
+
let terminated;
|
|
114203
114546
|
try {
|
|
114204
|
-
|
|
114547
|
+
terminated = await terminatePid(rec.pid);
|
|
114205
114548
|
} catch (err2) {
|
|
114206
|
-
|
|
114207
|
-
console.error(import_picocolors36.default.red(`Failed to signal pid ${rec.pid}: ${err2.message}`));
|
|
114549
|
+
console.error(import_picocolors37.default.red(`Failed to signal pid ${rec.pid}: ${err2.message}`));
|
|
114208
114550
|
return "failed";
|
|
114209
114551
|
}
|
|
114210
|
-
|
|
114211
|
-
|
|
114212
|
-
|
|
114213
|
-
|
|
114214
|
-
|
|
114215
|
-
`${import_picocolors36.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors36.default.dim(`(pid ${rec.pid}, port ${port})`) + ` for ${root}`
|
|
114216
|
-
);
|
|
114217
|
-
return "stopped";
|
|
114218
|
-
}
|
|
114552
|
+
if (!terminated) {
|
|
114553
|
+
console.error(
|
|
114554
|
+
import_picocolors37.default.red(`Host pid ${rec.pid} did not exit even after SIGKILL \u2014 could not reclaim the port.`)
|
|
114555
|
+
);
|
|
114556
|
+
return "failed";
|
|
114219
114557
|
}
|
|
114220
|
-
console.
|
|
114221
|
-
|
|
114558
|
+
console.log(
|
|
114559
|
+
`${import_picocolors37.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors37.default.dim(`(pid ${rec.pid}, port ${port})`) + ` for ${root}`
|
|
114222
114560
|
);
|
|
114223
|
-
return "
|
|
114561
|
+
return "stopped";
|
|
114224
114562
|
}
|
|
114225
114563
|
async function stopAll() {
|
|
114226
114564
|
const registry2 = readRegistry();
|
|
114227
114565
|
const projects2 = Object.entries(registry2.projects);
|
|
114228
114566
|
if (projects2.length === 0) {
|
|
114229
|
-
console.log(
|
|
114567
|
+
console.log(import_picocolors37.default.dim("No registered projects."));
|
|
114230
114568
|
return 0;
|
|
114231
114569
|
}
|
|
114232
114570
|
let stopped = 0;
|
|
@@ -114238,17 +114576,17 @@ async function stopAll() {
|
|
|
114238
114576
|
if (!healthy && readSpawnedHost(entry2.root) === null && readSourceHostPid(entry2.root) === null) {
|
|
114239
114577
|
continue;
|
|
114240
114578
|
}
|
|
114241
|
-
console.log(` Stopping ${
|
|
114579
|
+
console.log(` Stopping ${import_picocolors37.default.bold(name2)} ${import_picocolors37.default.dim(`(${hostUrl})`)}`);
|
|
114242
114580
|
const code = await stopHost(entry2.root, hostUrl);
|
|
114243
114581
|
if (code === 0) stopped++;
|
|
114244
114582
|
else failed++;
|
|
114245
114583
|
}
|
|
114246
114584
|
if (stopped === 0 && failed === 0) {
|
|
114247
|
-
console.log(
|
|
114585
|
+
console.log(import_picocolors37.default.dim("No running source-intelligence hosts found."));
|
|
114248
114586
|
} else {
|
|
114249
114587
|
console.log(
|
|
114250
114588
|
`
|
|
114251
|
-
Stopped ${stopped} host(s)${failed > 0 ?
|
|
114589
|
+
Stopped ${stopped} host(s)${failed > 0 ? import_picocolors37.default.red(`, ${failed} failed`) : ""}.`
|
|
114252
114590
|
);
|
|
114253
114591
|
}
|
|
114254
114592
|
return failed > 0 ? 1 : 0;
|
|
@@ -114275,10 +114613,30 @@ function validateSpawnedRecord(r) {
|
|
|
114275
114613
|
}
|
|
114276
114614
|
return null;
|
|
114277
114615
|
}
|
|
114616
|
+
async function terminatePid(pid) {
|
|
114617
|
+
const escalation = [
|
|
114618
|
+
{ signal: "SIGTERM", waitMs: STOP_WAIT_MS },
|
|
114619
|
+
{ signal: "SIGKILL", waitMs: STOP_KILL_WAIT_MS }
|
|
114620
|
+
];
|
|
114621
|
+
for (const { signal, waitMs } of escalation) {
|
|
114622
|
+
try {
|
|
114623
|
+
process.kill(pid, signal);
|
|
114624
|
+
} catch (err2) {
|
|
114625
|
+
if (err2.code === "ESRCH") return true;
|
|
114626
|
+
throw err2;
|
|
114627
|
+
}
|
|
114628
|
+
const deadline = Date.now() + waitMs;
|
|
114629
|
+
while (Date.now() < deadline) {
|
|
114630
|
+
await sleep(STOP_POLL_MS);
|
|
114631
|
+
if (await getProcessInfo(pid) === null) return true;
|
|
114632
|
+
}
|
|
114633
|
+
}
|
|
114634
|
+
return await getProcessInfo(pid) === null;
|
|
114635
|
+
}
|
|
114278
114636
|
async function getProcessInfo(pid) {
|
|
114279
114637
|
let args2;
|
|
114280
114638
|
try {
|
|
114281
|
-
const argsResult = await
|
|
114639
|
+
const argsResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "args="], {
|
|
114282
114640
|
timeout: 3e3
|
|
114283
114641
|
});
|
|
114284
114642
|
args2 = argsResult.stdout.trim();
|
|
@@ -114288,7 +114646,7 @@ async function getProcessInfo(pid) {
|
|
|
114288
114646
|
if (!args2) return null;
|
|
114289
114647
|
let etimeSeconds = Number.NaN;
|
|
114290
114648
|
try {
|
|
114291
|
-
const etimeResult = await
|
|
114649
|
+
const etimeResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "etime="], {
|
|
114292
114650
|
timeout: 3e3
|
|
114293
114651
|
});
|
|
114294
114652
|
etimeSeconds = parseEtimeSeconds(etimeResult.stdout.trim());
|
|
@@ -114316,14 +114674,77 @@ function extractPort(hostUrl) {
|
|
|
114316
114674
|
|
|
114317
114675
|
// ../../packages/cli/src/commands/hosts.ts
|
|
114318
114676
|
init_cjs_shims();
|
|
114319
|
-
var
|
|
114320
|
-
|
|
114677
|
+
var import_picocolors38 = __toESM(require_picocolors(), 1);
|
|
114678
|
+
|
|
114679
|
+
// ../../packages/cli/src/lib/host-reaper.ts
|
|
114680
|
+
init_cjs_shims();
|
|
114681
|
+
var import_node_child_process9 = require("child_process");
|
|
114682
|
+
var import_node_util6 = require("util");
|
|
114683
|
+
var execFileAsync3 = (0, import_node_util6.promisify)(import_node_child_process9.execFile);
|
|
114684
|
+
var HOST_ARGV = /(?:^|\s)(?:\S*\/)?horus-source\s+host\s+--port(?:=|\s+)(\d+)(?=\s|$)/;
|
|
114685
|
+
function parseSourceHosts(psStdout) {
|
|
114686
|
+
const hosts = [];
|
|
114687
|
+
for (const line2 of psStdout.split("\n")) {
|
|
114688
|
+
const trimmed = line2.trim();
|
|
114689
|
+
if (!trimmed) continue;
|
|
114690
|
+
const sp = trimmed.indexOf(" ");
|
|
114691
|
+
if (sp < 0) continue;
|
|
114692
|
+
const pid = Number(trimmed.slice(0, sp));
|
|
114693
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
114694
|
+
const m5 = HOST_ARGV.exec(trimmed.slice(sp + 1));
|
|
114695
|
+
if (!m5) continue;
|
|
114696
|
+
hosts.push({ pid, port: Number(m5[1]) });
|
|
114697
|
+
}
|
|
114698
|
+
return hosts;
|
|
114699
|
+
}
|
|
114700
|
+
function selectOrphans(running, claimedPids) {
|
|
114701
|
+
return running.filter((h3) => !claimedPids.has(h3.pid));
|
|
114702
|
+
}
|
|
114703
|
+
async function listRunningSourceHosts() {
|
|
114704
|
+
try {
|
|
114705
|
+
const { stdout } = await execFileAsync3("ps", ["-axww", "-o", "pid=,args="], {
|
|
114706
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
114707
|
+
timeout: 5e3
|
|
114708
|
+
});
|
|
114709
|
+
return parseSourceHosts(stdout);
|
|
114710
|
+
} catch {
|
|
114711
|
+
return [];
|
|
114712
|
+
}
|
|
114713
|
+
}
|
|
114714
|
+
function isAlive(pid) {
|
|
114715
|
+
try {
|
|
114716
|
+
process.kill(pid, 0);
|
|
114717
|
+
return true;
|
|
114718
|
+
} catch (err2) {
|
|
114719
|
+
return err2.code === "EPERM";
|
|
114720
|
+
}
|
|
114721
|
+
}
|
|
114722
|
+
async function reapPid(pid, opts = {}) {
|
|
114723
|
+
const termMs = opts.termMs ?? 4e3;
|
|
114724
|
+
const killMs = opts.killMs ?? 3e3;
|
|
114725
|
+
const pollMs = opts.pollMs ?? 200;
|
|
114726
|
+
for (const [signal, wait] of [
|
|
114727
|
+
["SIGTERM", termMs],
|
|
114728
|
+
["SIGKILL", killMs]
|
|
114729
|
+
]) {
|
|
114730
|
+
try {
|
|
114731
|
+
process.kill(pid, signal);
|
|
114732
|
+
} catch (err2) {
|
|
114733
|
+
if (err2.code === "ESRCH") return true;
|
|
114734
|
+
}
|
|
114735
|
+
const deadline = Date.now() + wait;
|
|
114736
|
+
while (Date.now() < deadline) {
|
|
114737
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
114738
|
+
if (!isAlive(pid)) return true;
|
|
114739
|
+
}
|
|
114740
|
+
}
|
|
114741
|
+
return !isAlive(pid);
|
|
114742
|
+
}
|
|
114743
|
+
|
|
114744
|
+
// ../../packages/cli/src/commands/hosts.ts
|
|
114745
|
+
async function runHosts(opts = {}) {
|
|
114321
114746
|
const registry2 = readRegistry();
|
|
114322
114747
|
const projects2 = Object.entries(registry2.projects);
|
|
114323
|
-
if (projects2.length === 0) {
|
|
114324
|
-
console.log(import_picocolors37.default.dim("No registered projects. Run `horus index` in a repo first."));
|
|
114325
|
-
return 0;
|
|
114326
|
-
}
|
|
114327
114748
|
const rows = [];
|
|
114328
114749
|
await Promise.all(
|
|
114329
114750
|
projects2.map(async ([name2, entry2]) => {
|
|
@@ -114332,39 +114753,69 @@ async function runHosts() {
|
|
|
114332
114753
|
rows.push({ name: name2, hostUrl: hostUrl ?? null, healthy, root: entry2.root });
|
|
114333
114754
|
})
|
|
114334
114755
|
);
|
|
114335
|
-
rows.sort(
|
|
114336
|
-
|
|
114337
|
-
|
|
114338
|
-
});
|
|
114339
|
-
const anyHost = rows.some((r) => r.hostUrl !== null);
|
|
114340
|
-
if (!anyHost) {
|
|
114341
|
-
console.log(import_picocolors37.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
|
|
114342
|
-
return 0;
|
|
114343
|
-
}
|
|
114756
|
+
rows.sort(
|
|
114757
|
+
(a2, b5) => a2.healthy !== b5.healthy ? a2.healthy ? -1 : 1 : a2.name.localeCompare(b5.name)
|
|
114758
|
+
);
|
|
114344
114759
|
console.log("");
|
|
114760
|
+
let printedAny = false;
|
|
114345
114761
|
for (const row of rows) {
|
|
114346
114762
|
if (row.hostUrl === null) continue;
|
|
114347
|
-
|
|
114763
|
+
printedAny = true;
|
|
114764
|
+
const status = row.healthy ? import_picocolors38.default.green("\u25CF running") : import_picocolors38.default.red("\u25CF stopped");
|
|
114348
114765
|
const port = extractPort2(row.hostUrl) ?? "?";
|
|
114349
114766
|
console.log(
|
|
114350
|
-
` ${status} ${
|
|
114767
|
+
` ${status} ${import_picocolors38.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors38.default.dim(row.root)}`
|
|
114351
114768
|
);
|
|
114352
114769
|
}
|
|
114353
|
-
console.log("");
|
|
114354
114770
|
const noHost = rows.filter((r) => r.hostUrl === null);
|
|
114355
|
-
|
|
114356
|
-
|
|
114357
|
-
|
|
114771
|
+
for (const row of noHost) console.log(` ${import_picocolors38.default.dim("\u25CB no host")} ${import_picocolors38.default.dim(row.name)}`);
|
|
114772
|
+
if (printedAny || noHost.length > 0) console.log("");
|
|
114773
|
+
const claimedPids = /* @__PURE__ */ new Set();
|
|
114774
|
+
for (const [, entry2] of projects2) {
|
|
114775
|
+
const hp = readSourceHostPid(entry2.root);
|
|
114776
|
+
if (hp?.pid) claimedPids.add(hp.pid);
|
|
114777
|
+
const sp = readSpawnedHost(entry2.root);
|
|
114778
|
+
if (sp?.pid) claimedPids.add(sp.pid);
|
|
114779
|
+
}
|
|
114780
|
+
const orphans = selectOrphans(await listRunningSourceHosts(), claimedPids);
|
|
114781
|
+
let reapFailed = 0;
|
|
114782
|
+
if (orphans.length > 0) {
|
|
114783
|
+
console.log(
|
|
114784
|
+
import_picocolors38.default.yellow(` \u26A0 ${orphans.length} orphaned source host(s) \u2014 no registered repo owns them:`)
|
|
114785
|
+
);
|
|
114786
|
+
for (const o5 of orphans) console.log(import_picocolors38.default.dim(` pid ${o5.pid} port ${o5.port}`));
|
|
114787
|
+
if (opts.reap) {
|
|
114788
|
+
let reaped = 0;
|
|
114789
|
+
for (const o5 of orphans) {
|
|
114790
|
+
if (await reapPid(o5.pid)) {
|
|
114791
|
+
reaped++;
|
|
114792
|
+
console.log(` ${import_picocolors38.default.green("\u2713")} reaped pid ${o5.pid} ${import_picocolors38.default.dim(`(port ${o5.port})`)}`);
|
|
114793
|
+
} else {
|
|
114794
|
+
reapFailed++;
|
|
114795
|
+
console.log(import_picocolors38.default.red(` \u2717 could not stop pid ${o5.pid} (port ${o5.port})`));
|
|
114796
|
+
}
|
|
114797
|
+
}
|
|
114798
|
+
console.log(import_picocolors38.default.dim(` Reaped ${reaped}/${orphans.length} orphan(s).`));
|
|
114799
|
+
} else {
|
|
114800
|
+
console.log(import_picocolors38.default.dim(" Run `horus hosts --reap` to stop them."));
|
|
114358
114801
|
}
|
|
114359
114802
|
console.log("");
|
|
114360
114803
|
}
|
|
114804
|
+
if (projects2.length === 0 && orphans.length === 0) {
|
|
114805
|
+
console.log(import_picocolors38.default.dim("No registered projects. Run `horus index` in a repo first."));
|
|
114806
|
+
return 0;
|
|
114807
|
+
}
|
|
114808
|
+
if (!printedAny && orphans.length === 0) {
|
|
114809
|
+
console.log(import_picocolors38.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
|
|
114810
|
+
return 0;
|
|
114811
|
+
}
|
|
114361
114812
|
const running = rows.filter((r) => r.healthy).length;
|
|
114362
114813
|
console.log(
|
|
114363
|
-
|
|
114364
|
-
`${running} running \xB7 horus
|
|
114814
|
+
import_picocolors38.default.dim(
|
|
114815
|
+
`${running} running${orphans.length ? ` \xB7 ${orphans.length} orphan(s)` : ""} \xB7 horus hosts --reap to clear orphans \xB7 horus stop --all to stop everything`
|
|
114365
114816
|
)
|
|
114366
114817
|
);
|
|
114367
|
-
return 0;
|
|
114818
|
+
return reapFailed > 0 ? 1 : 0;
|
|
114368
114819
|
}
|
|
114369
114820
|
function extractPort2(hostUrl) {
|
|
114370
114821
|
try {
|
|
@@ -114377,12 +114828,12 @@ function extractPort2(hostUrl) {
|
|
|
114377
114828
|
|
|
114378
114829
|
// ../../packages/cli/src/commands/doctor.ts
|
|
114379
114830
|
init_cjs_shims();
|
|
114380
|
-
var
|
|
114831
|
+
var import_picocolors39 = __toESM(require_picocolors(), 1);
|
|
114381
114832
|
var DEFAULT_DB_URL4 = "postgresql://horus:horus@localhost:5433/horus";
|
|
114382
114833
|
function mark2(status) {
|
|
114383
|
-
if (status === "pass") return
|
|
114384
|
-
if (status === "warn") return
|
|
114385
|
-
return
|
|
114834
|
+
if (status === "pass") return import_picocolors39.default.green("\u2713");
|
|
114835
|
+
if (status === "warn") return import_picocolors39.default.yellow("~");
|
|
114836
|
+
return import_picocolors39.default.red("\u2717");
|
|
114386
114837
|
}
|
|
114387
114838
|
async function runDoctor(opts) {
|
|
114388
114839
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -114662,11 +115113,11 @@ async function runDoctor(opts) {
|
|
|
114662
115113
|
write(JSON.stringify(output, null, 2));
|
|
114663
115114
|
return hasFailure ? 1 : 0;
|
|
114664
115115
|
}
|
|
114665
|
-
write(
|
|
115116
|
+
write(import_picocolors39.default.bold("\nHorus readiness check\n"));
|
|
114666
115117
|
for (const check2 of checks) {
|
|
114667
|
-
write(` ${mark2(check2.status)} ${
|
|
115118
|
+
write(` ${mark2(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(26))} ${import_picocolors39.default.dim(check2.detail)}`);
|
|
114668
115119
|
if (check2.next) {
|
|
114669
|
-
write(` ${
|
|
115120
|
+
write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
|
|
114670
115121
|
}
|
|
114671
115122
|
}
|
|
114672
115123
|
write("");
|
|
@@ -114675,20 +115126,20 @@ async function runDoctor(opts) {
|
|
|
114675
115126
|
|
|
114676
115127
|
// ../../packages/cli/src/commands/providers-doctor.ts
|
|
114677
115128
|
init_cjs_shims();
|
|
114678
|
-
var
|
|
114679
|
-
var
|
|
115129
|
+
var import_node_child_process10 = require("child_process");
|
|
115130
|
+
var import_picocolors40 = __toESM(require_picocolors(), 1);
|
|
114680
115131
|
function statusMark(status) {
|
|
114681
|
-
if (status === "ready") return
|
|
114682
|
-
if (status === "installed") return
|
|
114683
|
-
return
|
|
115132
|
+
if (status === "ready") return import_picocolors40.default.green("\u2713");
|
|
115133
|
+
if (status === "installed") return import_picocolors40.default.yellow("~");
|
|
115134
|
+
return import_picocolors40.default.red("\u2717");
|
|
114684
115135
|
}
|
|
114685
115136
|
function statusLabel(status) {
|
|
114686
|
-
if (status === "ready") return
|
|
114687
|
-
if (status === "installed") return
|
|
114688
|
-
return
|
|
115137
|
+
if (status === "ready") return import_picocolors40.default.green("ready");
|
|
115138
|
+
if (status === "installed") return import_picocolors40.default.yellow("installed (not configured)");
|
|
115139
|
+
return import_picocolors40.default.dim("not found on PATH");
|
|
114689
115140
|
}
|
|
114690
115141
|
function detectBinary(id) {
|
|
114691
|
-
const result = (0,
|
|
115142
|
+
const result = (0, import_node_child_process10.spawnSync)(id, ["--version"], { stdio: "pipe", timeout: 2e3 });
|
|
114692
115143
|
if (result.error) {
|
|
114693
115144
|
return { id, status: "unavailable", detail: `${id}: command not found` };
|
|
114694
115145
|
}
|
|
@@ -114702,7 +115153,7 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114702
115153
|
const registry2 = opts?.registry ?? DEFAULT_LOCAL_PROVIDER_REGISTRY;
|
|
114703
115154
|
const write = opts?.write ?? ((line2) => console.log(line2));
|
|
114704
115155
|
const results = buildProviderResults(registry2, opts?._detect);
|
|
114705
|
-
write(
|
|
115156
|
+
write(import_picocolors40.default.bold("\nLocal AI providers\n"));
|
|
114706
115157
|
for (const result of results) {
|
|
114707
115158
|
const descriptor = registry2.get(result.id);
|
|
114708
115159
|
const name2 = descriptor?.displayName ?? result.id;
|
|
@@ -114710,11 +115161,11 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114710
115161
|
` ${statusMark(result.status)} ${result.id.padEnd(8)} ${name2.padEnd(22)} ${statusLabel(result.status)}`
|
|
114711
115162
|
);
|
|
114712
115163
|
if (result.status !== "ready" && result.detail) {
|
|
114713
|
-
write(` ${
|
|
115164
|
+
write(` ${import_picocolors40.default.dim("\u2192 " + result.detail)}`);
|
|
114714
115165
|
}
|
|
114715
115166
|
}
|
|
114716
115167
|
write("");
|
|
114717
|
-
write(
|
|
115168
|
+
write(import_picocolors40.default.bold("Cloud AI providers\n"));
|
|
114718
115169
|
let source = null;
|
|
114719
115170
|
if (opts?._anthropicKey !== void 0) {
|
|
114720
115171
|
source = opts._anthropicKey ? "env" : null;
|
|
@@ -114729,18 +115180,18 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114729
115180
|
}
|
|
114730
115181
|
if (source === "config") {
|
|
114731
115182
|
write(
|
|
114732
|
-
` ${
|
|
115183
|
+
` ${import_picocolors40.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.green("configured (local .horus config)")}`
|
|
114733
115184
|
);
|
|
114734
115185
|
} else if (source === "env") {
|
|
114735
115186
|
write(
|
|
114736
|
-
` ${
|
|
115187
|
+
` ${import_picocolors40.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.green("configured (ANTHROPIC_API_KEY env)")}`
|
|
114737
115188
|
);
|
|
114738
115189
|
} else {
|
|
114739
115190
|
write(
|
|
114740
|
-
` ${
|
|
115191
|
+
` ${import_picocolors40.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.dim("not configured")}`
|
|
114741
115192
|
);
|
|
114742
115193
|
write(
|
|
114743
|
-
` ${
|
|
115194
|
+
` ${import_picocolors40.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
|
|
114744
115195
|
);
|
|
114745
115196
|
}
|
|
114746
115197
|
write("");
|
|
@@ -114749,9 +115200,9 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
114749
115200
|
|
|
114750
115201
|
// ../../packages/cli/src/commands/generate-config.ts
|
|
114751
115202
|
init_cjs_shims();
|
|
114752
|
-
var
|
|
114753
|
-
var
|
|
114754
|
-
var
|
|
115203
|
+
var import_node_fs18 = require("fs");
|
|
115204
|
+
var import_node_path20 = require("path");
|
|
115205
|
+
var import_picocolors41 = __toESM(require_picocolors(), 1);
|
|
114755
115206
|
function configTemplate(name2, repoPath) {
|
|
114756
115207
|
return `export default {
|
|
114757
115208
|
database: {
|
|
@@ -114799,39 +115250,39 @@ async function runGenerateConfig(opts) {
|
|
|
114799
115250
|
const defaults2 = localConfigPath2 != null ? projectDefaults(localConfigPath2) : null;
|
|
114800
115251
|
const hasLocalConfig = defaults2 != null;
|
|
114801
115252
|
const defaultOut = hasLocalConfig ? "horus.config.example.js" : "horus.config.js";
|
|
114802
|
-
const outPath = (0,
|
|
115253
|
+
const outPath = (0, import_node_path20.resolve)(cwd, opts.out ?? defaultOut);
|
|
114803
115254
|
const name2 = opts.name ?? defaults2?.name ?? "my-project";
|
|
114804
115255
|
const repoPath = opts.repo ?? defaults2?.repoPath ?? `/path/to/${name2}`;
|
|
114805
|
-
if ((0,
|
|
114806
|
-
log(`${
|
|
114807
|
-
log(
|
|
115256
|
+
if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
|
|
115257
|
+
log(`${import_picocolors41.default.red("\u2717")} ${outPath} already exists`);
|
|
115258
|
+
log(import_picocolors41.default.dim(" pass --force to overwrite"));
|
|
114808
115259
|
return 1;
|
|
114809
115260
|
}
|
|
114810
115261
|
try {
|
|
114811
|
-
(0,
|
|
114812
|
-
(0,
|
|
115262
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
|
|
115263
|
+
(0, import_node_fs18.writeFileSync)(outPath, configTemplate(name2, repoPath), "utf8");
|
|
114813
115264
|
} catch (err2) {
|
|
114814
|
-
log(`${
|
|
115265
|
+
log(`${import_picocolors41.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
|
|
114815
115266
|
return 1;
|
|
114816
115267
|
}
|
|
114817
|
-
log(`${
|
|
114818
|
-
log(
|
|
114819
|
-
log(
|
|
115268
|
+
log(`${import_picocolors41.default.green("\u2713")} Created ${outPath}`);
|
|
115269
|
+
log(import_picocolors41.default.dim(` project: ${name2}`));
|
|
115270
|
+
log(import_picocolors41.default.dim(` repo: ${repoPath}`));
|
|
114820
115271
|
if (hasLocalConfig && localConfigPath2 != null) {
|
|
114821
115272
|
log("");
|
|
114822
|
-
log(
|
|
115273
|
+
log(import_picocolors41.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
|
|
114823
115274
|
log(" \u2022 .horus/config.json \u2014 project config used by `horus investigate` from this repo");
|
|
114824
115275
|
log(" \u2022 horus.config.js \u2014 standalone/global config used with `horus doctor --config <path>`");
|
|
114825
|
-
log(
|
|
115276
|
+
log(import_picocolors41.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
|
|
114826
115277
|
} else {
|
|
114827
|
-
log(
|
|
115278
|
+
log(import_picocolors41.default.dim(` next: horus doctor --config ${outPath}`));
|
|
114828
115279
|
}
|
|
114829
115280
|
return 0;
|
|
114830
115281
|
}
|
|
114831
115282
|
|
|
114832
115283
|
// ../../packages/cli/src/commands/readiness.ts
|
|
114833
115284
|
init_cjs_shims();
|
|
114834
|
-
var
|
|
115285
|
+
var import_picocolors42 = __toESM(require_picocolors(), 1);
|
|
114835
115286
|
var READINESS_AI_CONTRACT = `Provide a clearly separated AI readiness summary with:
|
|
114836
115287
|
|
|
114837
115288
|
Overall assessment
|
|
@@ -114854,9 +115305,9 @@ Confidence / gaps
|
|
|
114854
115305
|
- Which checks Horus could verify vs. which require manual confirmation`;
|
|
114855
115306
|
var DEFAULT_DB_URL5 = "postgresql://horus:horus@localhost:5433/horus";
|
|
114856
115307
|
function mark3(status) {
|
|
114857
|
-
if (status === "pass") return
|
|
114858
|
-
if (status === "warn") return
|
|
114859
|
-
return
|
|
115308
|
+
if (status === "pass") return import_picocolors42.default.green("\u2713");
|
|
115309
|
+
if (status === "warn") return import_picocolors42.default.yellow("~");
|
|
115310
|
+
return import_picocolors42.default.red("\u2717");
|
|
114860
115311
|
}
|
|
114861
115312
|
async function runReadiness(opts) {
|
|
114862
115313
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -115033,20 +115484,20 @@ async function runReadiness(opts) {
|
|
|
115033
115484
|
}
|
|
115034
115485
|
const blockingChecks = checks.filter((c4) => c4.blocking);
|
|
115035
115486
|
const optionalChecks = checks.filter((c4) => !c4.blocking);
|
|
115036
|
-
write(
|
|
115037
|
-
write(
|
|
115487
|
+
write(import_picocolors42.default.bold("\nHorus release readiness\n"));
|
|
115488
|
+
write(import_picocolors42.default.bold(" Blocking"));
|
|
115038
115489
|
for (const check2 of blockingChecks) {
|
|
115039
|
-
write(` ${mark3(check2.status)} ${
|
|
115490
|
+
write(` ${mark3(check2.status)} ${import_picocolors42.default.bold(check2.label.padEnd(22))} ${import_picocolors42.default.dim(check2.detail)}`);
|
|
115040
115491
|
if (check2.next) {
|
|
115041
|
-
write(` ${
|
|
115492
|
+
write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
|
|
115042
115493
|
}
|
|
115043
115494
|
}
|
|
115044
115495
|
write("");
|
|
115045
|
-
write(
|
|
115496
|
+
write(import_picocolors42.default.bold(" Optional"));
|
|
115046
115497
|
for (const check2 of optionalChecks) {
|
|
115047
|
-
write(` ${mark3(check2.status)} ${
|
|
115498
|
+
write(` ${mark3(check2.status)} ${import_picocolors42.default.bold(check2.label.padEnd(22))} ${import_picocolors42.default.dim(check2.detail)}`);
|
|
115048
115499
|
if (check2.next) {
|
|
115049
|
-
write(` ${
|
|
115500
|
+
write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
|
|
115050
115501
|
}
|
|
115051
115502
|
}
|
|
115052
115503
|
write("");
|
|
@@ -115054,21 +115505,21 @@ async function runReadiness(opts) {
|
|
|
115054
115505
|
const optionalWarns = optionalChecks.filter((c4) => c4.status === "warn").length;
|
|
115055
115506
|
if (blockingFails.length === 0) {
|
|
115056
115507
|
if (optionalWarns === 0) {
|
|
115057
|
-
write(
|
|
115508
|
+
write(import_picocolors42.default.green(" Ready for demo/release."));
|
|
115058
115509
|
} else {
|
|
115059
115510
|
write(
|
|
115060
|
-
|
|
115511
|
+
import_picocolors42.default.yellow(
|
|
115061
115512
|
` Ready for a basic demo. ${optionalWarns} optional item(s) not configured \u2014 investigation evidence will be limited.`
|
|
115062
115513
|
)
|
|
115063
115514
|
);
|
|
115064
115515
|
}
|
|
115065
115516
|
} else {
|
|
115066
115517
|
write(
|
|
115067
|
-
|
|
115518
|
+
import_picocolors42.default.red(
|
|
115068
115519
|
` Not ready. ${blockingFails.length} blocking item(s) must be resolved before demo/release.`
|
|
115069
115520
|
)
|
|
115070
115521
|
);
|
|
115071
|
-
write(
|
|
115522
|
+
write(import_picocolors42.default.dim(" Re-run `horus readiness` after resolving the items above."));
|
|
115072
115523
|
}
|
|
115073
115524
|
write("");
|
|
115074
115525
|
if (opts?.ai) {
|
|
@@ -115083,7 +115534,7 @@ async function runReadiness(opts) {
|
|
|
115083
115534
|
});
|
|
115084
115535
|
console.log("\n" + renderInterpretation(result));
|
|
115085
115536
|
if (!result.ok) {
|
|
115086
|
-
console.error(
|
|
115537
|
+
console.error(import_picocolors42.default.yellow(`[ai] ${result.warning}`));
|
|
115087
115538
|
}
|
|
115088
115539
|
}
|
|
115089
115540
|
return blockingFails.length > 0 ? 1 : 0;
|
|
@@ -115091,33 +115542,33 @@ async function runReadiness(opts) {
|
|
|
115091
115542
|
|
|
115092
115543
|
// ../../packages/cli/src/commands/skill.ts
|
|
115093
115544
|
init_cjs_shims();
|
|
115094
|
-
var
|
|
115095
|
-
var
|
|
115545
|
+
var import_node_fs19 = require("fs");
|
|
115546
|
+
var import_node_path21 = require("path");
|
|
115096
115547
|
var import_node_os5 = require("os");
|
|
115097
115548
|
var import_promises2 = require("readline/promises");
|
|
115098
|
-
var
|
|
115549
|
+
var import_picocolors43 = __toESM(require_picocolors(), 1);
|
|
115099
115550
|
var SUPPORTED_TARGETS = ["claude", "codex", "gemini", "cursor", "generic"];
|
|
115100
115551
|
function isValidTarget(target) {
|
|
115101
115552
|
return SUPPORTED_TARGETS.includes(target);
|
|
115102
115553
|
}
|
|
115103
115554
|
function getLegacyClaudeSkillPath(opts) {
|
|
115104
115555
|
const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
|
|
115105
|
-
return (0,
|
|
115556
|
+
return (0, import_node_path21.join)(base2, ".claude", "skills", "horus.md");
|
|
115106
115557
|
}
|
|
115107
115558
|
function getSkillInstallPath(target, opts) {
|
|
115108
115559
|
const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
|
|
115109
115560
|
switch (target) {
|
|
115110
115561
|
case "claude":
|
|
115111
|
-
return (0,
|
|
115562
|
+
return (0, import_node_path21.join)(base2, ".claude", "skills", "horus", "SKILL.md");
|
|
115112
115563
|
case "gemini":
|
|
115113
|
-
return opts.global ? (0,
|
|
115564
|
+
return opts.global ? (0, import_node_path21.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path21.join)(base2, "GEMINI.md");
|
|
115114
115565
|
case "cursor":
|
|
115115
|
-
return opts.global ? (0,
|
|
115566
|
+
return opts.global ? (0, import_node_path21.join)(base2, ".cursorrules") : (0, import_node_path21.join)(base2, ".cursor", "rules", "horus.mdc");
|
|
115116
115567
|
case "codex":
|
|
115117
|
-
return opts.global ? (0,
|
|
115568
|
+
return opts.global ? (0, import_node_path21.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path21.join)(base2, "AGENTS.md");
|
|
115118
115569
|
case "generic":
|
|
115119
115570
|
default:
|
|
115120
|
-
return (0,
|
|
115571
|
+
return (0, import_node_path21.join)(base2, ".horus", "skills", `horus-${target}.md`);
|
|
115121
115572
|
}
|
|
115122
115573
|
}
|
|
115123
115574
|
var BASE_SKILL = `# Horus Grounded Investigation Skill
|
|
@@ -115434,66 +115885,66 @@ async function confirm(message) {
|
|
|
115434
115885
|
async function runSkillInstall(target, opts) {
|
|
115435
115886
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
115436
115887
|
if (!isValidTarget(target)) {
|
|
115437
|
-
log(`${
|
|
115438
|
-
log(
|
|
115888
|
+
log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
|
|
115889
|
+
log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
115439
115890
|
return 1;
|
|
115440
115891
|
}
|
|
115441
115892
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
115442
115893
|
const content = generateSkillContent(target);
|
|
115443
115894
|
if (target === "claude") {
|
|
115444
115895
|
const legacyPath = getLegacyClaudeSkillPath({ global: opts.global, cwd: opts.cwd });
|
|
115445
|
-
if ((0,
|
|
115896
|
+
if ((0, import_node_fs19.existsSync)(legacyPath)) {
|
|
115446
115897
|
if (!opts.force) {
|
|
115447
115898
|
const confirmed = await (opts.confirm ?? confirm)(
|
|
115448
115899
|
`Found old flat skill file at ${legacyPath}. Replace it with the new directory layout?`
|
|
115449
115900
|
);
|
|
115450
115901
|
if (!confirmed) {
|
|
115451
|
-
log(`${
|
|
115452
|
-
log(
|
|
115902
|
+
log(`${import_picocolors43.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
|
|
115903
|
+
log(import_picocolors43.default.dim(" Pass --force to replace without prompting."));
|
|
115453
115904
|
return 1;
|
|
115454
115905
|
}
|
|
115455
115906
|
}
|
|
115456
115907
|
try {
|
|
115457
|
-
(0,
|
|
115458
|
-
log(`${
|
|
115908
|
+
(0, import_node_fs19.rmSync)(legacyPath);
|
|
115909
|
+
log(`${import_picocolors43.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
|
|
115459
115910
|
} catch (err2) {
|
|
115460
|
-
log(`${
|
|
115911
|
+
log(`${import_picocolors43.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err2.message}`);
|
|
115461
115912
|
return 1;
|
|
115462
115913
|
}
|
|
115463
115914
|
}
|
|
115464
115915
|
}
|
|
115465
|
-
if ((0,
|
|
115466
|
-
log(`${
|
|
115467
|
-
log(
|
|
115916
|
+
if ((0, import_node_fs19.existsSync)(outPath) && !opts.force) {
|
|
115917
|
+
log(`${import_picocolors43.default.red("\u2717")} ${outPath} already exists`);
|
|
115918
|
+
log(import_picocolors43.default.dim(" pass --force to overwrite"));
|
|
115468
115919
|
return 1;
|
|
115469
115920
|
}
|
|
115470
115921
|
try {
|
|
115471
|
-
(0,
|
|
115472
|
-
(0,
|
|
115922
|
+
(0, import_node_fs19.mkdirSync)((0, import_node_path21.dirname)(outPath), { recursive: true });
|
|
115923
|
+
(0, import_node_fs19.writeFileSync)(outPath, content, "utf8");
|
|
115473
115924
|
} catch (err2) {
|
|
115474
|
-
log(`${
|
|
115925
|
+
log(`${import_picocolors43.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
|
|
115475
115926
|
return 1;
|
|
115476
115927
|
}
|
|
115477
|
-
log(`${
|
|
115928
|
+
log(`${import_picocolors43.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
|
|
115478
115929
|
if (target === "claude") {
|
|
115479
|
-
log(
|
|
115480
|
-
log(
|
|
115930
|
+
log(import_picocolors43.default.dim(" Claude Code will load this skill automatically."));
|
|
115931
|
+
log(import_picocolors43.default.dim(" Invoke it with: /horus"));
|
|
115481
115932
|
} else if (target === "gemini") {
|
|
115482
|
-
log(
|
|
115933
|
+
log(import_picocolors43.default.dim(" Gemini CLI will load this context file automatically."));
|
|
115483
115934
|
} else if (target === "cursor") {
|
|
115484
|
-
log(
|
|
115935
|
+
log(import_picocolors43.default.dim(" Cursor will load this rule automatically."));
|
|
115485
115936
|
} else if (target === "codex") {
|
|
115486
|
-
log(
|
|
115937
|
+
log(import_picocolors43.default.dim(" Codex CLI will load this file automatically."));
|
|
115487
115938
|
} else {
|
|
115488
|
-
log(
|
|
115939
|
+
log(import_picocolors43.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
|
|
115489
115940
|
}
|
|
115490
115941
|
return 0;
|
|
115491
115942
|
}
|
|
115492
115943
|
async function runSkillPrint(target, opts) {
|
|
115493
115944
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
115494
115945
|
if (!isValidTarget(target)) {
|
|
115495
|
-
log(`${
|
|
115496
|
-
log(
|
|
115946
|
+
log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
|
|
115947
|
+
log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
115497
115948
|
return 1;
|
|
115498
115949
|
}
|
|
115499
115950
|
process.stdout.write(generateSkillContent(target));
|
|
@@ -115502,8 +115953,8 @@ async function runSkillPrint(target, opts) {
|
|
|
115502
115953
|
async function runSkillPath(target, opts) {
|
|
115503
115954
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
115504
115955
|
if (!isValidTarget(target)) {
|
|
115505
|
-
log(`${
|
|
115506
|
-
log(
|
|
115956
|
+
log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
|
|
115957
|
+
log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
115507
115958
|
return 1;
|
|
115508
115959
|
}
|
|
115509
115960
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
@@ -115513,14 +115964,14 @@ async function runSkillPath(target, opts) {
|
|
|
115513
115964
|
|
|
115514
115965
|
// ../../packages/cli/src/commands/update.ts
|
|
115515
115966
|
init_cjs_shims();
|
|
115516
|
-
var
|
|
115517
|
-
var
|
|
115967
|
+
var import_node_fs20 = require("fs");
|
|
115968
|
+
var import_node_path22 = require("path");
|
|
115518
115969
|
var import_node_os6 = require("os");
|
|
115519
115970
|
var import_node_crypto7 = require("crypto");
|
|
115520
|
-
var
|
|
115521
|
-
var
|
|
115522
|
-
var
|
|
115523
|
-
var
|
|
115971
|
+
var import_node_child_process11 = require("child_process");
|
|
115972
|
+
var import_node_util7 = require("util");
|
|
115973
|
+
var import_picocolors44 = __toESM(require_picocolors(), 1);
|
|
115974
|
+
var execFileAsync4 = (0, import_node_util7.promisify)(import_node_child_process11.execFile);
|
|
115524
115975
|
async function ensureBackendPinned(write) {
|
|
115525
115976
|
let installed = null;
|
|
115526
115977
|
try {
|
|
@@ -115529,18 +115980,18 @@ async function ensureBackendPinned(write) {
|
|
|
115529
115980
|
installed = null;
|
|
115530
115981
|
}
|
|
115531
115982
|
if (installed === PINNED_SOURCE_VERSION) {
|
|
115532
|
-
write(` ${
|
|
115983
|
+
write(` ${import_picocolors44.default.green("\u2713")} Source backend already on pinned ${PINNED_SOURCE_VERSION}.`);
|
|
115533
115984
|
return;
|
|
115534
115985
|
}
|
|
115535
115986
|
if (installed === null) {
|
|
115536
115987
|
write(
|
|
115537
|
-
` ${
|
|
115988
|
+
` ${import_picocolors44.default.dim("Source backend not installed \u2014 install it: curl -fsSL https://horus.sh/install.sh | bash")}`
|
|
115538
115989
|
);
|
|
115539
115990
|
return;
|
|
115540
115991
|
}
|
|
115541
115992
|
write(` Upgrading source backend ${installed} \u2192 ${PINNED_SOURCE_VERSION}\u2026`);
|
|
115542
115993
|
try {
|
|
115543
|
-
await
|
|
115994
|
+
await execFileAsync4(
|
|
115544
115995
|
"uv",
|
|
115545
115996
|
// --refresh: a CLI update commonly lands seconds after a backend release, so uv's
|
|
115546
115997
|
// cached PyPI index may not list the just-published version yet and the install
|
|
@@ -115549,10 +116000,10 @@ async function ensureBackendPinned(write) {
|
|
|
115549
116000
|
["tool", "install", "--force", "--refresh", `horus-source==${PINNED_SOURCE_VERSION}`],
|
|
115550
116001
|
{ timeout: 3e5 }
|
|
115551
116002
|
);
|
|
115552
|
-
write(` ${
|
|
116003
|
+
write(` ${import_picocolors44.default.green("\u2713")} Source backend upgraded to ${PINNED_SOURCE_VERSION}.`);
|
|
115553
116004
|
} catch {
|
|
115554
|
-
write(` ${
|
|
115555
|
-
write(` ${
|
|
116005
|
+
write(` ${import_picocolors44.default.yellow("!")} Couldn't auto-upgrade the source backend. Re-run the installer:`);
|
|
116006
|
+
write(` ${import_picocolors44.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
|
|
115556
116007
|
}
|
|
115557
116008
|
}
|
|
115558
116009
|
var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
|
|
@@ -115585,7 +116036,7 @@ function resolveBinaryPath() {
|
|
|
115585
116036
|
const raw = process.argv[1];
|
|
115586
116037
|
if (!raw) throw new Error("Cannot determine binary path from process.argv[1]");
|
|
115587
116038
|
try {
|
|
115588
|
-
return (0,
|
|
116039
|
+
return (0, import_node_fs20.realpathSync)(raw);
|
|
115589
116040
|
} catch {
|
|
115590
116041
|
return raw;
|
|
115591
116042
|
}
|
|
@@ -115595,33 +116046,33 @@ async function runUpdate(opts) {
|
|
|
115595
116046
|
const _fetch = opts._fetch ?? fetch;
|
|
115596
116047
|
const currentVersion = opts._currentVersion ?? HORUS_VERSION;
|
|
115597
116048
|
write(`
|
|
115598
|
-
${
|
|
116049
|
+
${import_picocolors44.default.bold("Horus update")}
|
|
115599
116050
|
`);
|
|
115600
|
-
write(` Current version: ${
|
|
116051
|
+
write(` Current version: ${import_picocolors44.default.cyan(currentVersion)}`);
|
|
115601
116052
|
let release;
|
|
115602
116053
|
try {
|
|
115603
116054
|
release = await fetchLatestRelease(_fetch);
|
|
115604
116055
|
} catch (e) {
|
|
115605
|
-
write(` ${
|
|
116056
|
+
write(` ${import_picocolors44.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
|
|
115606
116057
|
return 1;
|
|
115607
116058
|
}
|
|
115608
116059
|
const latest = release.tag_name.replace(/^v/, "");
|
|
115609
|
-
write(` Latest version: ${
|
|
116060
|
+
write(` Latest version: ${import_picocolors44.default.cyan(latest)}`);
|
|
115610
116061
|
const newer = isNewer(latest, currentVersion);
|
|
115611
116062
|
if (opts.check) {
|
|
115612
116063
|
if (newer) {
|
|
115613
116064
|
write(`
|
|
115614
|
-
${
|
|
115615
|
-
write(` Run ${
|
|
116065
|
+
${import_picocolors44.default.yellow("\u2192")} ${import_picocolors44.default.bold(latest)} is available.`);
|
|
116066
|
+
write(` Run ${import_picocolors44.default.bold("horus update")} to upgrade.`);
|
|
115616
116067
|
} else {
|
|
115617
116068
|
write(`
|
|
115618
|
-
${
|
|
116069
|
+
${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
|
|
115619
116070
|
}
|
|
115620
116071
|
return 0;
|
|
115621
116072
|
}
|
|
115622
116073
|
if (!newer && !opts.force) {
|
|
115623
116074
|
write(`
|
|
115624
|
-
${
|
|
116075
|
+
${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
|
|
115625
116076
|
await ensureBackendPinned(write);
|
|
115626
116077
|
return 0;
|
|
115627
116078
|
}
|
|
@@ -115631,8 +116082,8 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115631
116082
|
const binaryAsset = release.assets.find((a2) => a2.name === assetName);
|
|
115632
116083
|
const checksumAsset = release.assets.find((a2) => a2.name === checksumName);
|
|
115633
116084
|
if (!binaryAsset) {
|
|
115634
|
-
write(` ${
|
|
115635
|
-
write(` Fallback: ${
|
|
116085
|
+
write(` ${import_picocolors44.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
|
|
116086
|
+
write(` Fallback: ${import_picocolors44.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
|
|
115636
116087
|
return 1;
|
|
115637
116088
|
}
|
|
115638
116089
|
write(`
|
|
@@ -115642,7 +116093,7 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115642
116093
|
redirect: "follow"
|
|
115643
116094
|
});
|
|
115644
116095
|
if (!binRes.ok || !binRes.body) {
|
|
115645
|
-
write(` ${
|
|
116096
|
+
write(` ${import_picocolors44.default.red("\u2717")} Download failed: ${binRes.status}`);
|
|
115646
116097
|
return 1;
|
|
115647
116098
|
}
|
|
115648
116099
|
const binBuf = Buffer.from(await binRes.arrayBuffer());
|
|
@@ -115656,82 +116107,82 @@ ${import_picocolors43.default.bold("Horus update")}
|
|
|
115656
116107
|
const expectedHash = csText.trim().split(/\s+/)[0];
|
|
115657
116108
|
const actualHash = sha256hex(binBuf);
|
|
115658
116109
|
if (expectedHash !== actualHash) {
|
|
115659
|
-
write(` ${
|
|
116110
|
+
write(` ${import_picocolors44.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
|
|
115660
116111
|
write(` Expected: ${expectedHash}`);
|
|
115661
116112
|
write(` Got: ${actualHash}`);
|
|
115662
116113
|
return 1;
|
|
115663
116114
|
}
|
|
115664
|
-
write(` ${
|
|
116115
|
+
write(` ${import_picocolors44.default.green("\u2713")} Checksum verified.`);
|
|
115665
116116
|
}
|
|
115666
116117
|
}
|
|
115667
116118
|
let binaryPath;
|
|
115668
116119
|
try {
|
|
115669
116120
|
binaryPath = resolveBinaryPath();
|
|
115670
116121
|
} catch (e) {
|
|
115671
|
-
write(` ${
|
|
116122
|
+
write(` ${import_picocolors44.default.red("\u2717")} ${e.message}`);
|
|
115672
116123
|
return 1;
|
|
115673
116124
|
}
|
|
115674
|
-
if (!(0,
|
|
115675
|
-
write(` ${
|
|
116125
|
+
if (!(0, import_node_fs20.existsSync)(binaryPath)) {
|
|
116126
|
+
write(` ${import_picocolors44.default.red("\u2717")} Binary not found at ${binaryPath}`);
|
|
115676
116127
|
return 1;
|
|
115677
116128
|
}
|
|
115678
|
-
const tmpPath = (0,
|
|
116129
|
+
const tmpPath = (0, import_node_path22.join)((0, import_node_os6.tmpdir)(), `horus-update-${tag}-${process.pid}`);
|
|
115679
116130
|
const backupPath = `${binaryPath}.bak`;
|
|
115680
116131
|
try {
|
|
115681
|
-
(0,
|
|
116132
|
+
(0, import_node_fs20.writeFileSync)(tmpPath, binBuf, { mode: 493 });
|
|
115682
116133
|
} catch (e) {
|
|
115683
|
-
write(` ${
|
|
116134
|
+
write(` ${import_picocolors44.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
|
|
115684
116135
|
return 1;
|
|
115685
116136
|
}
|
|
115686
116137
|
try {
|
|
115687
|
-
(0,
|
|
116138
|
+
(0, import_node_fs20.renameSync)(binaryPath, backupPath);
|
|
115688
116139
|
try {
|
|
115689
|
-
(0,
|
|
115690
|
-
(0,
|
|
116140
|
+
(0, import_node_fs20.renameSync)(tmpPath, binaryPath);
|
|
116141
|
+
(0, import_node_fs20.chmodSync)(binaryPath, 493);
|
|
115691
116142
|
} catch (e) {
|
|
115692
116143
|
try {
|
|
115693
|
-
(0,
|
|
116144
|
+
(0, import_node_fs20.renameSync)(backupPath, binaryPath);
|
|
115694
116145
|
} catch {
|
|
115695
116146
|
}
|
|
115696
116147
|
throw e;
|
|
115697
116148
|
}
|
|
115698
116149
|
try {
|
|
115699
|
-
(0,
|
|
116150
|
+
(0, import_node_fs20.unlinkSync)(backupPath);
|
|
115700
116151
|
} catch {
|
|
115701
116152
|
}
|
|
115702
116153
|
} catch (e) {
|
|
115703
116154
|
try {
|
|
115704
|
-
(0,
|
|
116155
|
+
(0, import_node_fs20.unlinkSync)(tmpPath);
|
|
115705
116156
|
} catch {
|
|
115706
116157
|
}
|
|
115707
116158
|
const msg = e.message ?? String(e);
|
|
115708
116159
|
const needsSudo = e.code === "EACCES" || e.code === "EPERM";
|
|
115709
|
-
write(` ${
|
|
116160
|
+
write(` ${import_picocolors44.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
|
|
115710
116161
|
if (needsSudo) {
|
|
115711
116162
|
write(`
|
|
115712
116163
|
Try with sudo:`);
|
|
115713
116164
|
write(
|
|
115714
|
-
` ${
|
|
116165
|
+
` ${import_picocolors44.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
|
|
115715
116166
|
);
|
|
115716
116167
|
}
|
|
115717
116168
|
return 1;
|
|
115718
116169
|
}
|
|
115719
|
-
write(` ${
|
|
115720
|
-
write(` ${
|
|
116170
|
+
write(` ${import_picocolors44.default.green("\u2713")} Updated: ${import_picocolors44.default.bold(currentVersion)} \u2192 ${import_picocolors44.default.bold(latest)}`);
|
|
116171
|
+
write(` ${import_picocolors44.default.dim(binaryPath)}`);
|
|
115721
116172
|
await ensureBackendPinned(write);
|
|
115722
116173
|
return 0;
|
|
115723
116174
|
}
|
|
115724
116175
|
|
|
115725
116176
|
// ../../packages/cli/src/commands/login.ts
|
|
115726
116177
|
init_cjs_shims();
|
|
115727
|
-
var
|
|
115728
|
-
var
|
|
116178
|
+
var import_node_child_process12 = require("child_process");
|
|
116179
|
+
var import_picocolors45 = __toESM(require_picocolors(), 1);
|
|
115729
116180
|
var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
115730
116181
|
function openBrowser(url) {
|
|
115731
116182
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
115732
116183
|
const args2 = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
115733
116184
|
try {
|
|
115734
|
-
(0,
|
|
116185
|
+
(0, import_node_child_process12.execFile)(cmd, args2, () => {
|
|
115735
116186
|
});
|
|
115736
116187
|
} catch {
|
|
115737
116188
|
}
|
|
@@ -115750,19 +116201,19 @@ async function loginWithToken(apiBaseUrl, token) {
|
|
|
115750
116201
|
}
|
|
115751
116202
|
});
|
|
115752
116203
|
console.log(
|
|
115753
|
-
`${
|
|
116204
|
+
`${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.`
|
|
115754
116205
|
);
|
|
115755
116206
|
return 0;
|
|
115756
116207
|
} catch (err2) {
|
|
115757
116208
|
if (err2 instanceof CloudOfflineError) {
|
|
115758
|
-
console.error(
|
|
116209
|
+
console.error(import_picocolors45.default.red(err2.message));
|
|
115759
116210
|
return 1;
|
|
115760
116211
|
}
|
|
115761
116212
|
if (err2 instanceof CloudError && err2.status === 401) {
|
|
115762
|
-
console.error(
|
|
116213
|
+
console.error(import_picocolors45.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
|
|
115763
116214
|
return 1;
|
|
115764
116215
|
}
|
|
115765
|
-
console.error(
|
|
116216
|
+
console.error(import_picocolors45.default.red(`Login failed: ${err2.message}`));
|
|
115766
116217
|
return 1;
|
|
115767
116218
|
}
|
|
115768
116219
|
}
|
|
@@ -115773,19 +116224,19 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
115773
116224
|
session = await client.startCliSession();
|
|
115774
116225
|
} catch (err2) {
|
|
115775
116226
|
if (err2 instanceof CloudOfflineError) {
|
|
115776
|
-
console.error(
|
|
116227
|
+
console.error(import_picocolors45.default.red(err2.message));
|
|
115777
116228
|
return 1;
|
|
115778
116229
|
}
|
|
115779
|
-
console.error(
|
|
116230
|
+
console.error(import_picocolors45.default.red(`Could not start login: ${err2.message}`));
|
|
115780
116231
|
return 1;
|
|
115781
116232
|
}
|
|
115782
116233
|
console.log("");
|
|
115783
116234
|
console.log(` To sign in, open this URL in your browser:`);
|
|
115784
|
-
console.log(` ${
|
|
116235
|
+
console.log(` ${import_picocolors45.default.cyan(import_picocolors45.default.bold(session.verificationUri))}`);
|
|
115785
116236
|
console.log("");
|
|
115786
|
-
console.log(` and enter the code: ${
|
|
116237
|
+
console.log(` and enter the code: ${import_picocolors45.default.bold(session.userCode)}`);
|
|
115787
116238
|
console.log("");
|
|
115788
|
-
console.log(
|
|
116239
|
+
console.log(import_picocolors45.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
|
|
115789
116240
|
openBrowser(session.verificationUriComplete);
|
|
115790
116241
|
const deadline = Date.now() + session.expiresIn * 1e3;
|
|
115791
116242
|
let intervalMs = Math.max(session.interval, 1) * 1e3;
|
|
@@ -115796,7 +116247,7 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
115796
116247
|
poll = await client.pollCliSession(session.deviceCode);
|
|
115797
116248
|
} catch (err2) {
|
|
115798
116249
|
if (err2 instanceof CloudOfflineError) {
|
|
115799
|
-
console.error(
|
|
116250
|
+
console.error(import_picocolors45.default.red(`
|
|
115800
116251
|
${err2.message}`));
|
|
115801
116252
|
return 1;
|
|
115802
116253
|
}
|
|
@@ -115809,14 +116260,14 @@ ${err2.message}`));
|
|
|
115809
116260
|
intervalMs += 2e3;
|
|
115810
116261
|
break;
|
|
115811
116262
|
case "denied":
|
|
115812
|
-
console.error(
|
|
116263
|
+
console.error(import_picocolors45.default.red("\nLogin was denied in the browser."));
|
|
115813
116264
|
return 1;
|
|
115814
116265
|
case "expired":
|
|
115815
|
-
console.error(
|
|
116266
|
+
console.error(import_picocolors45.default.red("\nThis login request expired. Run `horus login` again."));
|
|
115816
116267
|
return 1;
|
|
115817
116268
|
case "approved":
|
|
115818
116269
|
if (!poll.token || !poll.account) {
|
|
115819
|
-
console.error(
|
|
116270
|
+
console.error(import_picocolors45.default.red("\nLogin approved but no token was returned. Try again."));
|
|
115820
116271
|
return 1;
|
|
115821
116272
|
}
|
|
115822
116273
|
writeAuth({
|
|
@@ -115826,12 +116277,12 @@ ${err2.message}`));
|
|
|
115826
116277
|
});
|
|
115827
116278
|
console.log(
|
|
115828
116279
|
`
|
|
115829
|
-
${
|
|
116280
|
+
${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.`
|
|
115830
116281
|
);
|
|
115831
116282
|
return 0;
|
|
115832
116283
|
}
|
|
115833
116284
|
}
|
|
115834
|
-
console.error(
|
|
116285
|
+
console.error(import_picocolors45.default.red("\nTimed out waiting for approval. Run `horus login` again."));
|
|
115835
116286
|
return 1;
|
|
115836
116287
|
}
|
|
115837
116288
|
async function runLogin(opts = {}) {
|
|
@@ -115845,7 +116296,7 @@ async function runLogin(opts = {}) {
|
|
|
115845
116296
|
async function runLogout() {
|
|
115846
116297
|
const auth = readAuth();
|
|
115847
116298
|
if (!auth) {
|
|
115848
|
-
console.log(
|
|
116299
|
+
console.log(import_picocolors45.default.dim("Not logged in."));
|
|
115849
116300
|
return 0;
|
|
115850
116301
|
}
|
|
115851
116302
|
const session = authedClient();
|
|
@@ -115859,18 +116310,18 @@ async function runLogout() {
|
|
|
115859
116310
|
}
|
|
115860
116311
|
}
|
|
115861
116312
|
clearAuth();
|
|
115862
|
-
console.log(`${
|
|
116313
|
+
console.log(`${import_picocolors45.default.green("\u2713")} Logged out. Local mode still works.`);
|
|
115863
116314
|
return 0;
|
|
115864
116315
|
}
|
|
115865
116316
|
|
|
115866
116317
|
// ../../packages/cli/src/commands/cloud.ts
|
|
115867
116318
|
init_cjs_shims();
|
|
115868
|
-
var
|
|
116319
|
+
var import_picocolors46 = __toESM(require_picocolors(), 1);
|
|
115869
116320
|
var import_promises3 = require("readline/promises");
|
|
115870
116321
|
|
|
115871
116322
|
// ../../packages/cli/src/lib/cloud/git.ts
|
|
115872
116323
|
init_cjs_shims();
|
|
115873
|
-
var
|
|
116324
|
+
var import_node_child_process13 = require("child_process");
|
|
115874
116325
|
function parseRepoName(remoteUrl) {
|
|
115875
116326
|
const cleaned = remoteUrl.trim().replace(/\.git$/, "");
|
|
115876
116327
|
const lastSegment = cleaned.split(/[/:]/).filter(Boolean).pop() ?? "";
|
|
@@ -115894,7 +116345,7 @@ function inferProvider(remoteUrl) {
|
|
|
115894
116345
|
}
|
|
115895
116346
|
function detectGitRemote(root) {
|
|
115896
116347
|
try {
|
|
115897
|
-
const remoteUrl = (0,
|
|
116348
|
+
const remoteUrl = (0, import_node_child_process13.execFileSync)("git", ["-C", root, "remote", "get-url", "origin"], {
|
|
115898
116349
|
encoding: "utf8",
|
|
115899
116350
|
stdio: ["ignore", "pipe", "ignore"]
|
|
115900
116351
|
}).trim();
|
|
@@ -115910,7 +116361,7 @@ async function runCloudLink(opts = {}) {
|
|
|
115910
116361
|
const root = repoRootOrCwd(opts.cwd);
|
|
115911
116362
|
const session = authedClient();
|
|
115912
116363
|
if (!session) {
|
|
115913
|
-
console.error(
|
|
116364
|
+
console.error(import_picocolors46.default.red(`Not logged in. Run ${import_picocolors46.default.bold("horus login")} first.`));
|
|
115914
116365
|
return 1;
|
|
115915
116366
|
}
|
|
115916
116367
|
let ctx;
|
|
@@ -115920,7 +116371,7 @@ async function runCloudLink(opts = {}) {
|
|
|
115920
116371
|
return reportCloudError(err2);
|
|
115921
116372
|
}
|
|
115922
116373
|
const remote = detectGitRemote(root);
|
|
115923
|
-
if (remote) console.log(
|
|
116374
|
+
if (remote) console.log(import_picocolors46.default.dim(`Detected git remote: ${remote.remoteUrl}`));
|
|
115924
116375
|
let resolved = null;
|
|
115925
116376
|
let target = opts.project;
|
|
115926
116377
|
if (!target) {
|
|
@@ -115936,7 +116387,7 @@ async function runCloudLink(opts = {}) {
|
|
|
115936
116387
|
const org = ctx.organizations.find((o5) => o5.id === match.organizationId);
|
|
115937
116388
|
if (ws && org) {
|
|
115938
116389
|
target = `${org.slug}/${ws.slug}/${match.slug}`;
|
|
115939
|
-
console.log(
|
|
116390
|
+
console.log(import_picocolors46.default.dim(`Matched existing project by remote: ${import_picocolors46.default.bold(target)}`));
|
|
115940
116391
|
}
|
|
115941
116392
|
}
|
|
115942
116393
|
}
|
|
@@ -115945,8 +116396,8 @@ async function runCloudLink(opts = {}) {
|
|
|
115945
116396
|
if (created === "aborted") return 1;
|
|
115946
116397
|
if (!created) {
|
|
115947
116398
|
console.error(
|
|
115948
|
-
|
|
115949
|
-
Run ${
|
|
116399
|
+
import_picocolors46.default.red("No project to link.") + `
|
|
116400
|
+
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")}.`
|
|
115950
116401
|
);
|
|
115951
116402
|
return 1;
|
|
115952
116403
|
}
|
|
@@ -115956,8 +116407,8 @@ async function runCloudLink(opts = {}) {
|
|
|
115956
116407
|
resolved = resolveTriple(ctx, target);
|
|
115957
116408
|
if (!resolved) {
|
|
115958
116409
|
console.error(
|
|
115959
|
-
|
|
115960
|
-
Run ${
|
|
116410
|
+
import_picocolors46.default.red(`You don't have access to ${import_picocolors46.default.bold(target)}.`) + `
|
|
116411
|
+
Run ${import_picocolors46.default.bold("horus context list")} to see available projects.`
|
|
115961
116412
|
);
|
|
115962
116413
|
return 1;
|
|
115963
116414
|
}
|
|
@@ -115970,10 +116421,10 @@ async function runCloudLink(opts = {}) {
|
|
|
115970
116421
|
project: resolved.project
|
|
115971
116422
|
});
|
|
115972
116423
|
console.log(
|
|
115973
|
-
`${
|
|
116424
|
+
`${import_picocolors46.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors46.default.bold(label)} ${import_picocolors46.default.dim("(cloud)")}.`
|
|
115974
116425
|
);
|
|
115975
116426
|
console.log(
|
|
115976
|
-
|
|
116427
|
+
import_picocolors46.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors46.default.bold("horus context use local")} to switch back.`)
|
|
115977
116428
|
);
|
|
115978
116429
|
return 0;
|
|
115979
116430
|
}
|
|
@@ -115981,11 +116432,11 @@ async function runCloudUnlink(opts = {}) {
|
|
|
115981
116432
|
const root = repoRootOrCwd(opts.cwd);
|
|
115982
116433
|
const cfg = readCloudConfig(root);
|
|
115983
116434
|
if (!cfg || cfg.context !== "cloud") {
|
|
115984
|
-
console.log(
|
|
116435
|
+
console.log(import_picocolors46.default.dim("This repo isn't linked to a cloud project."));
|
|
115985
116436
|
return 0;
|
|
115986
116437
|
}
|
|
115987
116438
|
clearCloudConfig(root);
|
|
115988
|
-
console.log(`${
|
|
116439
|
+
console.log(`${import_picocolors46.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors46.default.bold("local")} mode.`);
|
|
115989
116440
|
return 0;
|
|
115990
116441
|
}
|
|
115991
116442
|
async function runCloudStatus(opts = {}) {
|
|
@@ -115993,18 +116444,18 @@ async function runCloudStatus(opts = {}) {
|
|
|
115993
116444
|
const cfg = readCloudConfig(root);
|
|
115994
116445
|
const session = authedClient();
|
|
115995
116446
|
if (!isCloudActive(cfg) || !cfg?.project) {
|
|
115996
|
-
console.log(`${
|
|
115997
|
-
console.log(`${
|
|
116447
|
+
console.log(`${import_picocolors46.default.bold("Context:")} local`);
|
|
116448
|
+
console.log(`${import_picocolors46.default.bold("Storage:")} local Postgres (~/.horus)`);
|
|
115998
116449
|
console.log(
|
|
115999
|
-
|
|
116450
|
+
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.`)
|
|
116000
116451
|
);
|
|
116001
116452
|
return 0;
|
|
116002
116453
|
}
|
|
116003
116454
|
const triple = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
116004
|
-
console.log(`${
|
|
116455
|
+
console.log(`${import_picocolors46.default.bold("Context:")} ${triple} ${import_picocolors46.default.dim("(cloud)")}`);
|
|
116005
116456
|
let projectLine = `${cfg.project.slug}`;
|
|
116006
116457
|
let metaLines = [];
|
|
116007
|
-
let account =
|
|
116458
|
+
let account = import_picocolors46.default.dim("not logged in");
|
|
116008
116459
|
if (session) {
|
|
116009
116460
|
account = session.auth.account.email;
|
|
116010
116461
|
try {
|
|
@@ -116019,9 +116470,9 @@ async function runCloudStatus(opts = {}) {
|
|
|
116019
116470
|
} catch {
|
|
116020
116471
|
}
|
|
116021
116472
|
}
|
|
116022
|
-
console.log(`${
|
|
116473
|
+
console.log(`${import_picocolors46.default.bold("Project:")} ${projectLine}`);
|
|
116023
116474
|
for (const line2 of metaLines) console.log(line2);
|
|
116024
|
-
console.log(`${
|
|
116475
|
+
console.log(`${import_picocolors46.default.bold("Account:")} ${account}`);
|
|
116025
116476
|
return 0;
|
|
116026
116477
|
}
|
|
116027
116478
|
async function runCloudSync(opts = {}) {
|
|
@@ -116029,13 +116480,13 @@ async function runCloudSync(opts = {}) {
|
|
|
116029
116480
|
const cfg = readCloudConfig(root);
|
|
116030
116481
|
if (!isCloudActive(cfg) || !cfg.project) {
|
|
116031
116482
|
console.error(
|
|
116032
|
-
|
|
116483
|
+
import_picocolors46.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors46.default.bold("horus cloud link")} first.`
|
|
116033
116484
|
);
|
|
116034
116485
|
return 1;
|
|
116035
116486
|
}
|
|
116036
116487
|
const session = authedClient();
|
|
116037
116488
|
if (!session) {
|
|
116038
|
-
console.error(
|
|
116489
|
+
console.error(import_picocolors46.default.red(`Not logged in. Run ${import_picocolors46.default.bold("horus login")} first.`));
|
|
116039
116490
|
return 1;
|
|
116040
116491
|
}
|
|
116041
116492
|
const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
|
|
@@ -116056,26 +116507,26 @@ async function runCloudSync(opts = {}) {
|
|
|
116056
116507
|
const target = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
116057
116508
|
if (uploadable.length === 0) {
|
|
116058
116509
|
console.log(
|
|
116059
|
-
|
|
116510
|
+
import_picocolors46.default.dim(
|
|
116060
116511
|
`No local investigations with reports to upload.` + (skippedNoReport ? ` (${skippedNoReport} without a report skipped)` : "")
|
|
116061
116512
|
)
|
|
116062
116513
|
);
|
|
116063
116514
|
return 0;
|
|
116064
116515
|
}
|
|
116065
|
-
console.log(
|
|
116516
|
+
console.log(import_picocolors46.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors46.default.dim("(cloud)")}:`));
|
|
116066
116517
|
for (const c4 of uploadable.slice(0, 20)) {
|
|
116067
|
-
console.log(` ${
|
|
116518
|
+
console.log(` ${import_picocolors46.default.dim(c4.id.slice(0, 8))} ${(c4.title ?? "untitled").slice(0, 70)}`);
|
|
116068
116519
|
}
|
|
116069
|
-
if (uploadable.length > 20) console.log(
|
|
116070
|
-
if (skippedNoReport) console.log(
|
|
116520
|
+
if (uploadable.length > 20) console.log(import_picocolors46.default.dim(` \u2026and ${uploadable.length - 20} more`));
|
|
116521
|
+
if (skippedNoReport) console.log(import_picocolors46.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
|
|
116071
116522
|
if (opts.dryRun) {
|
|
116072
|
-
console.log(
|
|
116523
|
+
console.log(import_picocolors46.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
|
|
116073
116524
|
return 0;
|
|
116074
116525
|
}
|
|
116075
116526
|
if (!opts.yes) {
|
|
116076
116527
|
if (!process.stdin.isTTY) {
|
|
116077
116528
|
console.error(
|
|
116078
|
-
|
|
116529
|
+
import_picocolors46.default.yellow(`Re-run with ${import_picocolors46.default.bold("--yes")} to upload, or ${import_picocolors46.default.bold("--dry-run")} to preview only.`)
|
|
116079
116530
|
);
|
|
116080
116531
|
return 1;
|
|
116081
116532
|
}
|
|
@@ -116083,7 +116534,7 @@ async function runCloudSync(opts = {}) {
|
|
|
116083
116534
|
try {
|
|
116084
116535
|
const answer = (await rl.question(`Upload ${uploadable.length} investigation(s) to ${target}? (y/N) `)).trim().toLowerCase();
|
|
116085
116536
|
if (answer !== "y" && answer !== "yes") {
|
|
116086
|
-
console.log(
|
|
116537
|
+
console.log(import_picocolors46.default.dim("Aborted. Nothing uploaded."));
|
|
116087
116538
|
return 0;
|
|
116088
116539
|
}
|
|
116089
116540
|
} finally {
|
|
@@ -116096,18 +116547,18 @@ async function runCloudSync(opts = {}) {
|
|
|
116096
116547
|
try {
|
|
116097
116548
|
const refs = await uploadInvestigationToCloud(session.client, cfg, c4.report);
|
|
116098
116549
|
created++;
|
|
116099
|
-
console.log(`${
|
|
116550
|
+
console.log(`${import_picocolors46.default.green("\u2713")} ${import_picocolors46.default.dim(c4.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
|
|
116100
116551
|
} catch (err2) {
|
|
116101
116552
|
failed++;
|
|
116102
|
-
console.error(`${
|
|
116553
|
+
console.error(`${import_picocolors46.default.red("\u2717")} ${import_picocolors46.default.dim(c4.id.slice(0, 8))} ${err2.message}`);
|
|
116103
116554
|
}
|
|
116104
116555
|
}
|
|
116105
116556
|
console.log("");
|
|
116106
116557
|
console.log(
|
|
116107
|
-
`${
|
|
116558
|
+
`${import_picocolors46.default.bold("Sync complete:")} ${import_picocolors46.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors46.default.red(`${failed} failed`) : "0 failed") + "."
|
|
116108
116559
|
);
|
|
116109
116560
|
console.log(
|
|
116110
|
-
|
|
116561
|
+
import_picocolors46.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
|
|
116111
116562
|
);
|
|
116112
116563
|
return failed > 0 ? 1 : 0;
|
|
116113
116564
|
}
|
|
@@ -116115,8 +116566,8 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116115
116566
|
if (!process.stdin.isTTY && !yes) return null;
|
|
116116
116567
|
if (ctx.organizations.length === 0) {
|
|
116117
116568
|
console.error(
|
|
116118
|
-
|
|
116119
|
-
Create one at ${
|
|
116569
|
+
import_picocolors46.default.red("You're not a member of any organization yet.") + `
|
|
116570
|
+
Create one at ${import_picocolors46.default.bold("https://cloud.horus.sh")} first.`
|
|
116120
116571
|
);
|
|
116121
116572
|
return null;
|
|
116122
116573
|
}
|
|
@@ -116141,9 +116592,9 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116141
116592
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
116142
116593
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
116143
116594
|
workspace = { id: ws.id, slug: ws.slug };
|
|
116144
|
-
console.log(
|
|
116595
|
+
console.log(import_picocolors46.default.dim(`Created workspace ${import_picocolors46.default.bold(ws.slug)}.`));
|
|
116145
116596
|
} else {
|
|
116146
|
-
console.log(`Workspaces in ${
|
|
116597
|
+
console.log(`Workspaces in ${import_picocolors46.default.bold(org.slug)}:`);
|
|
116147
116598
|
orgWorkspaces.forEach((w4, i3) => console.log(` ${i3 + 1}) ${w4.slug}`));
|
|
116148
116599
|
console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
|
|
116149
116600
|
const pick2 = Number(await ask2("Workspace number", "1"));
|
|
@@ -116151,7 +116602,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116151
116602
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
116152
116603
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
116153
116604
|
workspace = { id: ws.id, slug: ws.slug };
|
|
116154
|
-
console.log(
|
|
116605
|
+
console.log(import_picocolors46.default.dim(`Created workspace ${import_picocolors46.default.bold(ws.slug)}.`));
|
|
116155
116606
|
} else {
|
|
116156
116607
|
const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
|
|
116157
116608
|
workspace = { id: chosen.id, slug: chosen.slug };
|
|
@@ -116163,7 +116614,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116163
116614
|
remoteUrl: remote?.remoteUrl,
|
|
116164
116615
|
provider: remote ? inferProvider(remote.remoteUrl) : void 0
|
|
116165
116616
|
});
|
|
116166
|
-
console.log(
|
|
116617
|
+
console.log(import_picocolors46.default.dim(`Created project ${import_picocolors46.default.bold(project.slug)}.`));
|
|
116167
116618
|
return {
|
|
116168
116619
|
organization: { id: org.id, slug: org.slug },
|
|
116169
116620
|
workspace,
|
|
@@ -116179,7 +116630,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
116179
116630
|
|
|
116180
116631
|
// ../../packages/cli/src/commands/telemetry.ts
|
|
116181
116632
|
init_cjs_shims();
|
|
116182
|
-
var
|
|
116633
|
+
var import_picocolors48 = __toESM(require_picocolors(), 1);
|
|
116183
116634
|
var import_node_crypto8 = require("crypto");
|
|
116184
116635
|
|
|
116185
116636
|
// ../../packages/cli/src/lib/telemetry/transport.ts
|
|
@@ -116240,7 +116691,7 @@ async function deleteRemoteTelemetry(installId, opts = {}) {
|
|
|
116240
116691
|
|
|
116241
116692
|
// ../../packages/cli/src/lib/telemetry/notice.ts
|
|
116242
116693
|
init_cjs_shims();
|
|
116243
|
-
var
|
|
116694
|
+
var import_picocolors47 = __toESM(require_picocolors(), 1);
|
|
116244
116695
|
var PRIVACY_URL = "https://horus.sh/privacy";
|
|
116245
116696
|
var SUPPRESS_FIRST_TOKEN = /* @__PURE__ */ new Set(["telemetry", "help"]);
|
|
116246
116697
|
var SUPPRESS_FLAGS = /* @__PURE__ */ new Set(["-V", "--version", "-h", "--help"]);
|
|
@@ -116252,11 +116703,11 @@ function shouldSuppressFor(argv) {
|
|
|
116252
116703
|
return firstToken != null && SUPPRESS_FIRST_TOKEN.has(firstToken);
|
|
116253
116704
|
}
|
|
116254
116705
|
function banner() {
|
|
116255
|
-
const b5 =
|
|
116256
|
-
const dim =
|
|
116706
|
+
const b5 = import_picocolors47.default.bold;
|
|
116707
|
+
const dim = import_picocolors47.default.dim;
|
|
116257
116708
|
return [
|
|
116258
116709
|
"",
|
|
116259
|
-
|
|
116710
|
+
import_picocolors47.default.cyan(b5("Horus collects anonymous usage data to improve the tool.")),
|
|
116260
116711
|
dim(" \u2022 On by default: command names, timings, success/failure, and where the"),
|
|
116261
116712
|
dim(" engine ran out of evidence. No file contents, queries, or secrets."),
|
|
116262
116713
|
dim(" \u2022 Off by default: your investigation inputs/outputs (opt in to help train"),
|
|
@@ -116264,7 +116715,7 @@ function banner() {
|
|
|
116264
116715
|
dim(" \u2022 If you use Horus Cloud, linked investigations sync to your workspace."),
|
|
116265
116716
|
"",
|
|
116266
116717
|
` ${dim("Opt out anytime:")} ${b5("horus telemetry disable")} ${dim("\xB7")} ${b5("HORUS_TELEMETRY=0")} ${dim("\xB7")} ${b5("DO_NOT_TRACK=1")}`,
|
|
116267
|
-
` ${dim("Details:")} ${
|
|
116718
|
+
` ${dim("Details:")} ${import_picocolors47.default.underline(PRIVACY_URL)}`,
|
|
116268
116719
|
""
|
|
116269
116720
|
].join("\n");
|
|
116270
116721
|
}
|
|
@@ -116287,45 +116738,45 @@ function maybeShowFirstRunNotice(argv = process.argv) {
|
|
|
116287
116738
|
|
|
116288
116739
|
// ../../packages/cli/src/commands/telemetry.ts
|
|
116289
116740
|
function onOff(enabled) {
|
|
116290
|
-
return enabled ?
|
|
116741
|
+
return enabled ? import_picocolors48.default.green("on") : import_picocolors48.default.dim("off");
|
|
116291
116742
|
}
|
|
116292
116743
|
function warnIfEnvOverrides() {
|
|
116293
116744
|
const decision = resolveConsent();
|
|
116294
116745
|
if (decision.forcedOff) {
|
|
116295
116746
|
console.log(
|
|
116296
|
-
|
|
116747
|
+
import_picocolors48.default.yellow(
|
|
116297
116748
|
`
|
|
116298
116749
|
Note: telemetry is currently forced off by your environment (${decision.reason}).`
|
|
116299
116750
|
)
|
|
116300
116751
|
);
|
|
116301
|
-
console.log(
|
|
116752
|
+
console.log(import_picocolors48.default.dim("Your saved preference will apply once that override is removed."));
|
|
116302
116753
|
} else if (decision.source === "ci") {
|
|
116303
116754
|
console.log(
|
|
116304
|
-
|
|
116755
|
+
import_picocolors48.default.yellow("\nNote: CI environment detected \u2014 telemetry stays off here regardless.")
|
|
116305
116756
|
);
|
|
116306
116757
|
}
|
|
116307
116758
|
}
|
|
116308
116759
|
async function runTelemetryStatus() {
|
|
116309
116760
|
const state = readTelemetryState();
|
|
116310
116761
|
const decision = resolveConsent({ state });
|
|
116311
|
-
console.log(
|
|
116762
|
+
console.log(import_picocolors48.default.bold("Horus telemetry"));
|
|
116312
116763
|
console.log(
|
|
116313
|
-
` Usage metadata (Tier A): ${onOff(decision.tierA)} ${
|
|
116764
|
+
` Usage metadata (Tier A): ${onOff(decision.tierA)} ${import_picocolors48.default.dim(`[${decision.source}]`)}`
|
|
116314
116765
|
);
|
|
116315
116766
|
console.log(` Content sharing (Tier B): ${onOff(decision.tierB)}`);
|
|
116316
|
-
console.log(
|
|
116767
|
+
console.log(import_picocolors48.default.dim(` Reason: ${decision.reason}`));
|
|
116317
116768
|
if (state) {
|
|
116318
|
-
console.log(
|
|
116769
|
+
console.log(import_picocolors48.default.dim(` Install ID: ${state.installId}`));
|
|
116319
116770
|
console.log(
|
|
116320
|
-
|
|
116771
|
+
import_picocolors48.default.dim(
|
|
116321
116772
|
` Saved: Tier A ${state.tierA.enabled ? "on" : "off"}, Tier B ${state.tierB.enabled ? "on" : "off"}`
|
|
116322
116773
|
)
|
|
116323
116774
|
);
|
|
116324
116775
|
} else {
|
|
116325
|
-
console.log(
|
|
116776
|
+
console.log(import_picocolors48.default.dim(" No saved preference yet (defaults apply)."));
|
|
116326
116777
|
}
|
|
116327
|
-
console.log(
|
|
116328
|
-
console.log(
|
|
116778
|
+
console.log(import_picocolors48.default.dim(` Config: ${telemetryPath()}`));
|
|
116779
|
+
console.log(import_picocolors48.default.dim(` Privacy: ${PRIVACY_URL}`));
|
|
116329
116780
|
if (!state || decision.source === "env" || decision.source === "ci") {
|
|
116330
116781
|
warnIfEnvOverrides();
|
|
116331
116782
|
}
|
|
@@ -116336,7 +116787,7 @@ async function runTelemetryEnable() {
|
|
|
116336
116787
|
s5.tierA.enabled = true;
|
|
116337
116788
|
if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
116338
116789
|
});
|
|
116339
|
-
console.log(
|
|
116790
|
+
console.log(import_picocolors48.default.green("Anonymous usage metadata (Tier A) is now ON. Thank you!"));
|
|
116340
116791
|
warnIfEnvOverrides();
|
|
116341
116792
|
return 0;
|
|
116342
116793
|
}
|
|
@@ -116346,7 +116797,7 @@ async function runTelemetryDisable() {
|
|
|
116346
116797
|
s5.tierB.enabled = false;
|
|
116347
116798
|
if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
116348
116799
|
});
|
|
116349
|
-
console.log(
|
|
116800
|
+
console.log(import_picocolors48.default.yellow("All telemetry is now OFF (Tier A and Tier B)."));
|
|
116350
116801
|
return 0;
|
|
116351
116802
|
}
|
|
116352
116803
|
async function runTelemetryEnableContent() {
|
|
@@ -116357,10 +116808,10 @@ async function runTelemetryEnableContent() {
|
|
|
116357
116808
|
if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
116358
116809
|
});
|
|
116359
116810
|
console.log(
|
|
116360
|
-
|
|
116811
|
+
import_picocolors48.default.green("Content sharing (Tier B) is now ON \u2014 redacted inputs/outputs help improve Horus.")
|
|
116361
116812
|
);
|
|
116362
|
-
console.log(
|
|
116363
|
-
console.log(
|
|
116813
|
+
console.log(import_picocolors48.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
|
|
116814
|
+
console.log(import_picocolors48.default.dim(`What this means: ${PRIVACY_URL}`));
|
|
116364
116815
|
warnIfEnvOverrides();
|
|
116365
116816
|
return 0;
|
|
116366
116817
|
}
|
|
@@ -116368,7 +116819,7 @@ async function runTelemetryDisableContent() {
|
|
|
116368
116819
|
updateTelemetryState((s5) => {
|
|
116369
116820
|
s5.tierB.enabled = false;
|
|
116370
116821
|
});
|
|
116371
|
-
console.log(
|
|
116822
|
+
console.log(import_picocolors48.default.yellow("Content sharing (Tier B) is now OFF. Usage metadata is unchanged."));
|
|
116372
116823
|
return 0;
|
|
116373
116824
|
}
|
|
116374
116825
|
async function runTelemetryResetId() {
|
|
@@ -116376,8 +116827,8 @@ async function runTelemetryResetId() {
|
|
|
116376
116827
|
updateTelemetryState((s5) => {
|
|
116377
116828
|
s5.installId = next;
|
|
116378
116829
|
});
|
|
116379
|
-
console.log(
|
|
116380
|
-
console.log(
|
|
116830
|
+
console.log(import_picocolors48.default.green("Install ID reset."));
|
|
116831
|
+
console.log(import_picocolors48.default.dim(` New install ID: ${next}`));
|
|
116381
116832
|
return 0;
|
|
116382
116833
|
}
|
|
116383
116834
|
async function runTelemetryDelete() {
|
|
@@ -116385,9 +116836,9 @@ async function runTelemetryDelete() {
|
|
|
116385
116836
|
if (installId) await deleteRemoteTelemetry(installId);
|
|
116386
116837
|
deleteTelemetryState();
|
|
116387
116838
|
clearSpool();
|
|
116388
|
-
console.log(
|
|
116839
|
+
console.log(import_picocolors48.default.green("Telemetry deleted \u2014 local state cleared and a server purge requested."));
|
|
116389
116840
|
console.log(
|
|
116390
|
-
|
|
116841
|
+
import_picocolors48.default.dim(
|
|
116391
116842
|
"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."
|
|
116392
116843
|
)
|
|
116393
116844
|
);
|
|
@@ -116430,35 +116881,35 @@ function installCommandTelemetry(program2) {
|
|
|
116430
116881
|
|
|
116431
116882
|
// ../../packages/cli/src/commands/feedback.ts
|
|
116432
116883
|
init_cjs_shims();
|
|
116433
|
-
var
|
|
116884
|
+
var import_picocolors49 = __toESM(require_picocolors(), 1);
|
|
116434
116885
|
async function runFeedback(investigationId, opts = {}) {
|
|
116435
116886
|
const id = investigationId?.trim();
|
|
116436
116887
|
if (!id) {
|
|
116437
|
-
console.error(
|
|
116888
|
+
console.error(import_picocolors49.default.red("Usage: horus feedback <investigationId> [--resolved yes|partly|no]"));
|
|
116438
116889
|
return 1;
|
|
116439
116890
|
}
|
|
116440
116891
|
if (opts.resolved !== void 0) {
|
|
116441
116892
|
const resolved = parseResolved(opts.resolved);
|
|
116442
116893
|
if (resolved === null) {
|
|
116443
|
-
console.error(
|
|
116894
|
+
console.error(import_picocolors49.default.red(`Invalid --resolved "${opts.resolved}". Use: yes | partly | no.`));
|
|
116444
116895
|
return 1;
|
|
116445
116896
|
}
|
|
116446
116897
|
let manualEstimateMinutes = null;
|
|
116447
116898
|
if (opts.manualEstimateMin !== void 0) {
|
|
116448
116899
|
const n4 = Number.parseInt(opts.manualEstimateMin, 10);
|
|
116449
116900
|
if (!Number.isFinite(n4) || n4 < 0) {
|
|
116450
|
-
console.error(
|
|
116901
|
+
console.error(import_picocolors49.default.red(`Invalid --manual-estimate-min "${opts.manualEstimateMin}" (expect a non-negative integer).`));
|
|
116451
116902
|
return 1;
|
|
116452
116903
|
}
|
|
116453
116904
|
manualEstimateMinutes = n4;
|
|
116454
116905
|
}
|
|
116455
116906
|
submitFeedback({ investigationId: id, resolved, manualEstimateMinutes, horusSeconds: null, source: "flag" });
|
|
116456
|
-
console.log(
|
|
116907
|
+
console.log(import_picocolors49.default.dim(`Feedback recorded (${resolved}) \u2014 thanks, this helps improve Horus.`));
|
|
116457
116908
|
return 0;
|
|
116458
116909
|
}
|
|
116459
116910
|
if (!process.stdin.isTTY) {
|
|
116460
116911
|
console.error(
|
|
116461
|
-
|
|
116912
|
+
import_picocolors49.default.yellow(
|
|
116462
116913
|
"horus feedback needs an interactive terminal \u2014 or pass --resolved yes|partly|no for non-interactive (agent) use."
|
|
116463
116914
|
)
|
|
116464
116915
|
);
|
|
@@ -116551,8 +117002,8 @@ Examples:
|
|
|
116551
117002
|
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) => {
|
|
116552
117003
|
process.exitCode = await runStop(opts);
|
|
116553
117004
|
});
|
|
116554
|
-
program2.command("hosts").description("List registered source-intelligence hosts and their live status (port, repo, running/stopped)").action(async () => {
|
|
116555
|
-
process.exitCode = await runHosts();
|
|
117005
|
+
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) => {
|
|
117006
|
+
process.exitCode = await runHosts({ reap: opts.reap });
|
|
116556
117007
|
});
|
|
116557
117008
|
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(
|
|
116558
117009
|
async (opts) => {
|