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