@merittdev/horus 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1569 -708
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -23435,9 +23435,9 @@ var require_client_metadata = __commonJS({
|
|
|
23435
23435
|
throw new error_1.MongoInvalidArgumentError("Unable to include driverInfo name and version, metadata cannot exceed 512 bytes");
|
|
23436
23436
|
}
|
|
23437
23437
|
let runtimeInfo = getRuntimeInfo();
|
|
23438
|
-
for (const { platform = "" } of driverInfoList) {
|
|
23439
|
-
if (
|
|
23440
|
-
runtimeInfo = `${runtimeInfo}|${
|
|
23438
|
+
for (const { platform: platform2 = "" } of driverInfoList) {
|
|
23439
|
+
if (platform2.length > 0) {
|
|
23440
|
+
runtimeInfo = `${runtimeInfo}|${platform2}`;
|
|
23441
23441
|
}
|
|
23442
23442
|
}
|
|
23443
23443
|
if (!metadataDocument.ifItFitsItSits("platform", runtimeInfo)) {
|
|
@@ -43447,7 +43447,7 @@ var require_common3 = __commonJS({
|
|
|
43447
43447
|
function setup(env) {
|
|
43448
43448
|
createDebug.debug = createDebug;
|
|
43449
43449
|
createDebug.default = createDebug;
|
|
43450
|
-
createDebug.coerce =
|
|
43450
|
+
createDebug.coerce = coerce3;
|
|
43451
43451
|
createDebug.disable = disable;
|
|
43452
43452
|
createDebug.enable = enable;
|
|
43453
43453
|
createDebug.enabled = enabled;
|
|
@@ -43602,7 +43602,7 @@ var require_common3 = __commonJS({
|
|
|
43602
43602
|
}
|
|
43603
43603
|
return false;
|
|
43604
43604
|
}
|
|
43605
|
-
function
|
|
43605
|
+
function coerce3(val) {
|
|
43606
43606
|
if (val instanceof Error) {
|
|
43607
43607
|
return val.stack || val.message;
|
|
43608
43608
|
}
|
|
@@ -57239,9 +57239,8 @@ init_cjs_shims();
|
|
|
57239
57239
|
|
|
57240
57240
|
// ../../packages/core/src/version.ts
|
|
57241
57241
|
init_cjs_shims();
|
|
57242
|
-
var HORUS_VERSION = true ? "0.
|
|
57243
|
-
var
|
|
57244
|
-
var PINNED_SOURCE_VERSION = PINNED_AXON_VERSION;
|
|
57242
|
+
var HORUS_VERSION = true ? "0.3.0" : "dev";
|
|
57243
|
+
var PINNED_SOURCE_VERSION = "1.1.1";
|
|
57245
57244
|
|
|
57246
57245
|
// ../../packages/core/src/evidence.ts
|
|
57247
57246
|
init_cjs_shims();
|
|
@@ -61672,7 +61671,7 @@ var horusConfigSchema = external_exports.object({
|
|
|
61672
61671
|
projects: external_exports.array(projectSchema).default([]),
|
|
61673
61672
|
axon: external_exports.object({
|
|
61674
61673
|
/** Fail fast if the running Axon is not this version. */
|
|
61675
|
-
pinnedVersion: external_exports.string().default("1.
|
|
61674
|
+
pinnedVersion: external_exports.string().default("1.1.1")
|
|
61676
61675
|
}).default({}),
|
|
61677
61676
|
database: databaseSchema,
|
|
61678
61677
|
models: modelsSchema.default({}),
|
|
@@ -62052,6 +62051,73 @@ packages/core/src/git.ts @horus/core-team @horus/devex-team
|
|
|
62052
62051
|
`.trim();
|
|
62053
62052
|
var FIXTURE_CODEOWNERS_RULES = parseCodeowners(FIXTURE_CODEOWNERS);
|
|
62054
62053
|
|
|
62054
|
+
// ../../packages/core/src/redact.ts
|
|
62055
|
+
init_cjs_shims();
|
|
62056
|
+
var BASE_PATTERNS = [
|
|
62057
|
+
[/(authorization\s*[=:]\s*)(bearer\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
|
|
62058
|
+
[/(authorization\s*[=:]\s*)(basic\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
|
|
62059
|
+
[
|
|
62060
|
+
/("?(?:password|passwd|secret|token|api[_-]key|apikey|x-api-key)"?\s*[=:]\s*)"?[^"',\s)>]+/gi,
|
|
62061
|
+
"$1[REDACTED]"
|
|
62062
|
+
],
|
|
62063
|
+
[/((?:cookie|set-cookie)\s*[=:]\s*)[^\s"',;>]{4,}/gi, "$1[REDACTED]"],
|
|
62064
|
+
[
|
|
62065
|
+
/((?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis(?:s)?):\/\/)[^:@/\s]+:[^@\s]+@/gi,
|
|
62066
|
+
"$1[REDACTED]@"
|
|
62067
|
+
],
|
|
62068
|
+
[/\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, "[REDACTED-CARD]"]
|
|
62069
|
+
];
|
|
62070
|
+
var EXTENDED_PATTERNS = [
|
|
62071
|
+
// PEM private key blocks (run first — multi-line, highest risk).
|
|
62072
|
+
[
|
|
62073
|
+
/-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z ]+ )?PRIVATE KEY-----/g,
|
|
62074
|
+
"[REDACTED-PRIVATE-KEY]"
|
|
62075
|
+
],
|
|
62076
|
+
// JSON Web Tokens (three base64url segments starting with the `{"` header).
|
|
62077
|
+
[/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[REDACTED-JWT]"],
|
|
62078
|
+
// AWS access key ids.
|
|
62079
|
+
[/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, "[REDACTED-AWS-KEY]"],
|
|
62080
|
+
// Google API keys.
|
|
62081
|
+
[/\bAIza[0-9A-Za-z_-]{35}\b/g, "[REDACTED-GCP-KEY]"],
|
|
62082
|
+
// Slack tokens.
|
|
62083
|
+
[/\bxox[baprs]-[0-9A-Za-z-]{10,}\b/g, "[REDACTED-SLACK-TOKEN]"],
|
|
62084
|
+
// GitHub tokens.
|
|
62085
|
+
[/\bgh[pousr]_[0-9A-Za-z]{36,}\b/g, "[REDACTED-GH-TOKEN]"],
|
|
62086
|
+
// Emails.
|
|
62087
|
+
[/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, "[REDACTED-EMAIL]"],
|
|
62088
|
+
// IPv4 addresses.
|
|
62089
|
+
[/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, "[REDACTED-IP]"]
|
|
62090
|
+
];
|
|
62091
|
+
var RESIDUAL_HIGH_RISK = [
|
|
62092
|
+
/-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----/,
|
|
62093
|
+
/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/,
|
|
62094
|
+
/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/
|
|
62095
|
+
];
|
|
62096
|
+
function apply(patterns, input) {
|
|
62097
|
+
let out = input;
|
|
62098
|
+
for (const [pattern, replacement] of patterns) {
|
|
62099
|
+
out = out.replace(pattern, replacement);
|
|
62100
|
+
}
|
|
62101
|
+
return out;
|
|
62102
|
+
}
|
|
62103
|
+
function redactSecrets(input) {
|
|
62104
|
+
return apply(BASE_PATTERNS, input);
|
|
62105
|
+
}
|
|
62106
|
+
function redactContent(input) {
|
|
62107
|
+
return apply(EXTENDED_PATTERNS, apply(BASE_PATTERNS, input));
|
|
62108
|
+
}
|
|
62109
|
+
function redactOrDrop(input) {
|
|
62110
|
+
try {
|
|
62111
|
+
const value = redactContent(input);
|
|
62112
|
+
if (RESIDUAL_HIGH_RISK.some((p) => p.test(value))) {
|
|
62113
|
+
return { value: null, dropped: true };
|
|
62114
|
+
}
|
|
62115
|
+
return { value, dropped: false };
|
|
62116
|
+
} catch {
|
|
62117
|
+
return { value: null, dropped: true };
|
|
62118
|
+
}
|
|
62119
|
+
}
|
|
62120
|
+
|
|
62055
62121
|
// ../../packages/cli/src/commands/status.ts
|
|
62056
62122
|
init_cjs_shims();
|
|
62057
62123
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
@@ -63135,25 +63201,8 @@ function annotateAgainstBaseline(current, baseline) {
|
|
|
63135
63201
|
current.newSignatures = newSignatures;
|
|
63136
63202
|
return current;
|
|
63137
63203
|
}
|
|
63138
|
-
var REDACTION_PATTERNS = [
|
|
63139
|
-
// Authorization headers (Bearer / Basic / API-key tokens)
|
|
63140
|
-
[/(authorization\s*[=:]\s*)(bearer\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
|
|
63141
|
-
[/(authorization\s*[=:]\s*)(basic\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
|
|
63142
|
-
// Standalone password/token/secret keys in KV strings, query params, JSON
|
|
63143
|
-
[/("?(?:password|passwd|secret|token|api[_-]key|apikey|x-api-key)"?\s*[=:]\s*)"?[^"',\s)>]+/gi, "$1[REDACTED]"],
|
|
63144
|
-
// Cookie / set-cookie header values
|
|
63145
|
-
[/((?:cookie|set-cookie)\s*[=:]\s*)[^\s"',;>]{4,}/gi, "$1[REDACTED]"],
|
|
63146
|
-
// Database / service connection strings with embedded credentials
|
|
63147
|
-
[/((?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis(?:s)?):\/\/)[^:@/\s]+:[^@\s]+@/gi, "$1[REDACTED]@"],
|
|
63148
|
-
// 16-digit card-number-like sequences (PCIDSS)
|
|
63149
|
-
[/\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, "[REDACTED-CARD]"]
|
|
63150
|
-
];
|
|
63151
63204
|
function redactSensitiveString(s) {
|
|
63152
|
-
|
|
63153
|
-
for (const [pattern, replacement] of REDACTION_PATTERNS) {
|
|
63154
|
-
out = out.replace(pattern, replacement);
|
|
63155
|
-
}
|
|
63156
|
-
return out;
|
|
63205
|
+
return redactSecrets(s);
|
|
63157
63206
|
}
|
|
63158
63207
|
function redactErrorSignature(sig) {
|
|
63159
63208
|
if (sig.sampleMessage === void 0) return sig;
|
|
@@ -66196,7 +66245,7 @@ function sql(strings, ...params) {
|
|
|
66196
66245
|
return new SQL([new StringChunk(str3)]);
|
|
66197
66246
|
}
|
|
66198
66247
|
sql2.raw = raw;
|
|
66199
|
-
function
|
|
66248
|
+
function join13(chunks, separator) {
|
|
66200
66249
|
const result = [];
|
|
66201
66250
|
for (const [i, chunk] of chunks.entries()) {
|
|
66202
66251
|
if (i > 0 && separator !== void 0) {
|
|
@@ -66206,7 +66255,7 @@ function sql(strings, ...params) {
|
|
|
66206
66255
|
}
|
|
66207
66256
|
return new SQL(result);
|
|
66208
66257
|
}
|
|
66209
|
-
sql2.join =
|
|
66258
|
+
sql2.join = join13;
|
|
66210
66259
|
function identifier(value) {
|
|
66211
66260
|
return new Name(value);
|
|
66212
66261
|
}
|
|
@@ -69797,7 +69846,7 @@ var PgSelectQueryBuilderBase = class extends TypedQueryBuilder {
|
|
|
69797
69846
|
return (table, on) => {
|
|
69798
69847
|
const baseTableName = this.tableName;
|
|
69799
69848
|
const tableName = getTableLikeName(table);
|
|
69800
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
69849
|
+
if (typeof tableName === "string" && this.config.joins?.some((join13) => join13.alias === tableName)) {
|
|
69801
69850
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
69802
69851
|
}
|
|
69803
69852
|
if (!this.isPartialSelect) {
|
|
@@ -70809,7 +70858,7 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
70809
70858
|
createJoin(joinType) {
|
|
70810
70859
|
return (table, on) => {
|
|
70811
70860
|
const tableName = getTableLikeName(table);
|
|
70812
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
70861
|
+
if (typeof tableName === "string" && this.config.joins.some((join13) => join13.alias === tableName)) {
|
|
70813
70862
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
70814
70863
|
}
|
|
70815
70864
|
if (typeof on === "function") {
|
|
@@ -70905,10 +70954,10 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
70905
70954
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
70906
70955
|
fields[tableName] = fromFields;
|
|
70907
70956
|
}
|
|
70908
|
-
for (const
|
|
70909
|
-
const tableName2 = getTableLikeName(
|
|
70910
|
-
if (typeof tableName2 === "string" && !is(
|
|
70911
|
-
const fromFields = this.getTableLikeFields(
|
|
70957
|
+
for (const join13 of this.config.joins) {
|
|
70958
|
+
const tableName2 = getTableLikeName(join13.table);
|
|
70959
|
+
if (typeof tableName2 === "string" && !is(join13.table, SQL)) {
|
|
70960
|
+
const fromFields = this.getTableLikeFields(join13.table);
|
|
70912
70961
|
fields[tableName2] = fromFields;
|
|
70913
70962
|
}
|
|
70914
70963
|
}
|
|
@@ -77014,6 +77063,8 @@ function detectEntityFields(context, limit = 2) {
|
|
|
77014
77063
|
}
|
|
77015
77064
|
async function reseedFromLogs(hint, input, deps) {
|
|
77016
77065
|
if (!deps.logs || typeof deps.logs.searchLogs !== "function") return null;
|
|
77066
|
+
if (!deps.code) return null;
|
|
77067
|
+
const code = deps.code;
|
|
77017
77068
|
try {
|
|
77018
77069
|
const from = logWindowFrom(input.since);
|
|
77019
77070
|
const records = await deps.logs.searchLogs({
|
|
@@ -77026,7 +77077,7 @@ async function reseedFromLogs(hint, input, deps) {
|
|
|
77026
77077
|
if (records.length === 0) return null;
|
|
77027
77078
|
const candidates = [...new Set(records.map((r) => r.component).filter((c) => !!c))];
|
|
77028
77079
|
for (const name of candidates) {
|
|
77029
|
-
const syms = await
|
|
77080
|
+
const syms = await code.searchSymbols(name, 5);
|
|
77030
77081
|
if (syms.length > 0) {
|
|
77031
77082
|
const rankedSeeds = rankSeeds(syms, [...new Set(tokenize(name))]);
|
|
77032
77083
|
const first = records[0];
|
|
@@ -77077,20 +77128,21 @@ async function investigate(input, deps) {
|
|
|
77077
77128
|
evidence2.push(ev);
|
|
77078
77129
|
return ev;
|
|
77079
77130
|
}
|
|
77080
|
-
const
|
|
77131
|
+
const degradedNoSource = !code;
|
|
77132
|
+
const rawSeeds = code ? await code.searchSymbols(hint, 5) : [];
|
|
77081
77133
|
const hintTokens = [...new Set(tokenize(hint))];
|
|
77082
77134
|
const ranked = rankSeeds(rawSeeds, hintTokens);
|
|
77083
77135
|
let seeds = ranked.map((r) => r.symbol);
|
|
77084
77136
|
let top = seeds[0];
|
|
77085
77137
|
let logReseed = null;
|
|
77086
|
-
if (!top) {
|
|
77138
|
+
if (!top && !degradedNoSource) {
|
|
77087
77139
|
logReseed = await reseedFromLogs(hint, input, deps);
|
|
77088
77140
|
if (logReseed !== null) {
|
|
77089
77141
|
seeds = logReseed.seeds;
|
|
77090
77142
|
top = seeds[0];
|
|
77091
77143
|
}
|
|
77092
77144
|
}
|
|
77093
|
-
if (!top) {
|
|
77145
|
+
if (!top && !degradedNoSource) {
|
|
77094
77146
|
const report2 = {
|
|
77095
77147
|
id: globalThis.crypto.randomUUID(),
|
|
77096
77148
|
input,
|
|
@@ -77114,31 +77166,6 @@ async function investigate(input, deps) {
|
|
|
77114
77166
|
if (persistedId2) report2.id = persistedId2;
|
|
77115
77167
|
return report2;
|
|
77116
77168
|
}
|
|
77117
|
-
const label = top.name === "constructor" ? `${(top.id.split(":").pop() ?? "").replace(/\.constructor$/, "") || top.name} (constructor)` : top.name;
|
|
77118
|
-
const [ctx, impact, flows] = await Promise.all([
|
|
77119
|
-
code.context(top.id),
|
|
77120
|
-
code.impact(top.id, 2),
|
|
77121
|
-
code.flowsFor(top.id)
|
|
77122
|
-
]);
|
|
77123
|
-
const edges = await listQueueEdges(db, { project: input.repo });
|
|
77124
|
-
const symbolNames = /* @__PURE__ */ new Set([
|
|
77125
|
-
top.name,
|
|
77126
|
-
...ctx.callers.map((s) => s.name),
|
|
77127
|
-
...ctx.callees.map((s) => s.name)
|
|
77128
|
-
]);
|
|
77129
|
-
const queueHits = edges.filter((e) => {
|
|
77130
|
-
const bySymbol = e.producerSymbol !== null && symbolNames.has(e.producerSymbol) || e.workerSymbol !== null && symbolNames.has(e.workerSymbol);
|
|
77131
|
-
const byFile = e.producerFile === top.filePath || e.workerFile === top.filePath;
|
|
77132
|
-
return bySymbol || byFile;
|
|
77133
|
-
});
|
|
77134
|
-
let changes = null;
|
|
77135
|
-
if (input.since !== void 0 && looksDiffable(input.since)) {
|
|
77136
|
-
try {
|
|
77137
|
-
changes = await code.detectChanges({ base: input.since, compare: "HEAD" });
|
|
77138
|
-
} catch {
|
|
77139
|
-
changes = null;
|
|
77140
|
-
}
|
|
77141
|
-
}
|
|
77142
77169
|
let recentChanges;
|
|
77143
77170
|
if (deps.repoPath && input.since) {
|
|
77144
77171
|
try {
|
|
@@ -77146,62 +77173,102 @@ async function investigate(input, deps) {
|
|
|
77146
77173
|
} catch {
|
|
77147
77174
|
}
|
|
77148
77175
|
}
|
|
77149
|
-
|
|
77150
|
-
|
|
77151
|
-
|
|
77152
|
-
|
|
77153
|
-
|
|
77154
|
-
|
|
77155
|
-
|
|
77176
|
+
let label = input.service ?? hint;
|
|
77177
|
+
let seedLoc = "";
|
|
77178
|
+
let ctx = null;
|
|
77179
|
+
let impact = null;
|
|
77180
|
+
let flows = [];
|
|
77181
|
+
let seedEv = null;
|
|
77182
|
+
let impactEv = null;
|
|
77156
77183
|
const flowEvIds = [];
|
|
77157
|
-
|
|
77158
|
-
const ev = mkEv(
|
|
77159
|
-
"flow",
|
|
77160
|
-
`Flow "${flow.name}" (${flow.steps.length} step(s))`,
|
|
77161
|
-
{ flowId: flow.id, name: flow.name, steps: flow.steps.map((s) => s.name) },
|
|
77162
|
-
{ symbolId: top.id, file: top.filePath }
|
|
77163
|
-
);
|
|
77164
|
-
flowEvIds.push(ev.id);
|
|
77165
|
-
}
|
|
77166
|
-
const impactEv = mkEv(
|
|
77167
|
-
"impact",
|
|
77168
|
-
`Impact of ${top.name}: ${impact.affected} affected symbol(s)`,
|
|
77169
|
-
{ affected: impact.affected },
|
|
77170
|
-
{ symbolId: top.id, file: top.filePath }
|
|
77171
|
-
);
|
|
77184
|
+
let queueHits = [];
|
|
77172
77185
|
const queueEvByName = /* @__PURE__ */ new Map();
|
|
77173
|
-
|
|
77174
|
-
const producer = edge.producerSymbol ?? "unknown-producer";
|
|
77175
|
-
const worker = edge.workerSymbol ?? "unknown-worker";
|
|
77176
|
-
const ev = mkEv(
|
|
77177
|
-
"queue-edge",
|
|
77178
|
-
`Queue "${edge.queueName}": ${producer} -> ${worker}`,
|
|
77179
|
-
{
|
|
77180
|
-
queueName: edge.queueName,
|
|
77181
|
-
producerSymbol: edge.producerSymbol,
|
|
77182
|
-
producerFile: edge.producerFile,
|
|
77183
|
-
workerSymbol: edge.workerSymbol,
|
|
77184
|
-
workerFile: edge.workerFile,
|
|
77185
|
-
source: edge.source
|
|
77186
|
-
},
|
|
77187
|
-
{ queueName: edge.queueName }
|
|
77188
|
-
);
|
|
77189
|
-
const list = queueEvByName.get(edge.queueName) ?? [];
|
|
77190
|
-
list.push(ev.id);
|
|
77191
|
-
queueEvByName.set(edge.queueName, list);
|
|
77192
|
-
}
|
|
77186
|
+
let changes = null;
|
|
77193
77187
|
let changeEvId = null;
|
|
77194
|
-
if (
|
|
77195
|
-
|
|
77196
|
-
|
|
77197
|
-
|
|
77198
|
-
|
|
77199
|
-
|
|
77200
|
-
|
|
77201
|
-
|
|
77202
|
-
|
|
77188
|
+
if (top && code) {
|
|
77189
|
+
label = top.name === "constructor" ? `${(top.id.split(":").pop() ?? "").replace(/\.constructor$/, "") || top.name} (constructor)` : top.name;
|
|
77190
|
+
[ctx, impact, flows] = await Promise.all([
|
|
77191
|
+
code.context(top.id),
|
|
77192
|
+
code.impact(top.id, 2),
|
|
77193
|
+
code.flowsFor(top.id)
|
|
77194
|
+
]);
|
|
77195
|
+
let edges = [];
|
|
77196
|
+
try {
|
|
77197
|
+
edges = await listQueueEdges(db, { project: input.repo });
|
|
77198
|
+
} catch {
|
|
77199
|
+
edges = [];
|
|
77200
|
+
}
|
|
77201
|
+
const symbolNames = /* @__PURE__ */ new Set([
|
|
77202
|
+
top.name,
|
|
77203
|
+
...ctx.callers.map((s) => s.name),
|
|
77204
|
+
...ctx.callees.map((s) => s.name)
|
|
77205
|
+
]);
|
|
77206
|
+
queueHits = edges.filter((e) => {
|
|
77207
|
+
const bySymbol = e.producerSymbol !== null && symbolNames.has(e.producerSymbol) || e.workerSymbol !== null && symbolNames.has(e.workerSymbol);
|
|
77208
|
+
const byFile = e.producerFile === top.filePath || e.workerFile === top.filePath;
|
|
77209
|
+
return bySymbol || byFile;
|
|
77210
|
+
});
|
|
77211
|
+
if (input.since !== void 0 && looksDiffable(input.since)) {
|
|
77212
|
+
try {
|
|
77213
|
+
changes = await code.detectChanges({ base: input.since, compare: "HEAD" });
|
|
77214
|
+
} catch {
|
|
77215
|
+
changes = null;
|
|
77216
|
+
}
|
|
77217
|
+
}
|
|
77218
|
+
seedLoc = formatSymbolLocation(top.filePath, top.startLine, top.endLine);
|
|
77219
|
+
seedEv = mkEv(
|
|
77220
|
+
"symbol",
|
|
77221
|
+
`Seed symbol ${top.name} (${seedLoc})`,
|
|
77222
|
+
{ symbol: top, snippet: ctx.snippet ?? null },
|
|
77223
|
+
top.startLine !== void 0 && top.startLine > 0 ? { symbolId: top.id, file: top.filePath, line: top.startLine } : { symbolId: top.id, file: top.filePath }
|
|
77203
77224
|
);
|
|
77204
|
-
|
|
77225
|
+
for (const flow of flows) {
|
|
77226
|
+
const ev = mkEv(
|
|
77227
|
+
"flow",
|
|
77228
|
+
`Flow "${flow.name}" (${flow.steps.length} step(s))`,
|
|
77229
|
+
{ flowId: flow.id, name: flow.name, steps: flow.steps.map((s) => s.name) },
|
|
77230
|
+
{ symbolId: top.id, file: top.filePath }
|
|
77231
|
+
);
|
|
77232
|
+
flowEvIds.push(ev.id);
|
|
77233
|
+
}
|
|
77234
|
+
impactEv = mkEv(
|
|
77235
|
+
"impact",
|
|
77236
|
+
`Impact of ${top.name}: ${impact.affected} affected symbol(s)`,
|
|
77237
|
+
{ affected: impact.affected },
|
|
77238
|
+
{ symbolId: top.id, file: top.filePath }
|
|
77239
|
+
);
|
|
77240
|
+
for (const edge of queueHits) {
|
|
77241
|
+
const producer = edge.producerSymbol ?? "unknown-producer";
|
|
77242
|
+
const worker = edge.workerSymbol ?? "unknown-worker";
|
|
77243
|
+
const ev = mkEv(
|
|
77244
|
+
"queue-edge",
|
|
77245
|
+
`Queue "${edge.queueName}": ${producer} -> ${worker}`,
|
|
77246
|
+
{
|
|
77247
|
+
queueName: edge.queueName,
|
|
77248
|
+
producerSymbol: edge.producerSymbol,
|
|
77249
|
+
producerFile: edge.producerFile,
|
|
77250
|
+
workerSymbol: edge.workerSymbol,
|
|
77251
|
+
workerFile: edge.workerFile,
|
|
77252
|
+
source: edge.source
|
|
77253
|
+
},
|
|
77254
|
+
{ queueName: edge.queueName }
|
|
77255
|
+
);
|
|
77256
|
+
const list = queueEvByName.get(edge.queueName) ?? [];
|
|
77257
|
+
list.push(ev.id);
|
|
77258
|
+
queueEvByName.set(edge.queueName, list);
|
|
77259
|
+
}
|
|
77260
|
+
if (changes) {
|
|
77261
|
+
const addedN = changes.added.length;
|
|
77262
|
+
const removedN = changes.removed.length;
|
|
77263
|
+
const modifiedN = changes.modified.length;
|
|
77264
|
+
const ev = mkEv(
|
|
77265
|
+
"commit",
|
|
77266
|
+
`Change range ${input.since}..HEAD: +${addedN} -${removedN} ~${modifiedN} symbol(s)`,
|
|
77267
|
+
{ added: addedN, removed: removedN, modified: modifiedN },
|
|
77268
|
+
{}
|
|
77269
|
+
);
|
|
77270
|
+
changeEvId = ev.id;
|
|
77271
|
+
}
|
|
77205
77272
|
}
|
|
77206
77273
|
if (changes === null && recentChanges !== void 0 && recentChanges.commits.length > 0) {
|
|
77207
77274
|
const { commits, changedFiles, totalInsertions, totalDeletions } = recentChanges;
|
|
@@ -77235,10 +77302,8 @@ async function investigate(input, deps) {
|
|
|
77235
77302
|
const from = logWindowFrom(input.since);
|
|
77236
77303
|
analysis = await deps.logs.analyzeErrors({ service: input.service, from });
|
|
77237
77304
|
logsCollected = true;
|
|
77238
|
-
const seedBase = top.filePath.split("/").pop() ?? "";
|
|
77239
|
-
const logSeedTerms = [
|
|
77240
|
-
.../* @__PURE__ */ new Set([...tokenize(hint), ...tokenize(top.name), ...tokenize(seedBase)])
|
|
77241
|
-
];
|
|
77305
|
+
const seedBase = top ? top.filePath.split("/").pop() ?? "" : "";
|
|
77306
|
+
const logSeedTerms = top ? [.../* @__PURE__ */ new Set([...tokenize(hint), ...tokenize(top.name), ...tokenize(seedBase)])] : [...new Set(tokenize(hint))];
|
|
77242
77307
|
for (const s of analysis.signatures.slice(0, 15)) {
|
|
77243
77308
|
const { relevanceClass, relevanceReason } = classifyLogRelevance(
|
|
77244
77309
|
s.key,
|
|
@@ -77486,10 +77551,10 @@ async function investigate(input, deps) {
|
|
|
77486
77551
|
}
|
|
77487
77552
|
}
|
|
77488
77553
|
let ownershipEstimate = null;
|
|
77489
|
-
if (deps.repoPath) {
|
|
77554
|
+
if (deps.repoPath && top && code) {
|
|
77490
77555
|
try {
|
|
77491
77556
|
ownershipEstimate = await estimateOwnership(top.name, {
|
|
77492
|
-
code
|
|
77557
|
+
code,
|
|
77493
77558
|
repoPath: deps.repoPath,
|
|
77494
77559
|
symbol: top
|
|
77495
77560
|
});
|
|
@@ -77515,14 +77580,16 @@ async function investigate(input, deps) {
|
|
|
77515
77580
|
const validated = validateHypotheses(hyps, evidence2);
|
|
77516
77581
|
const causeChains = buildCauseChains(validated, evidence2, graph, label);
|
|
77517
77582
|
const findings2 = [];
|
|
77518
|
-
|
|
77519
|
-
|
|
77520
|
-
|
|
77521
|
-
|
|
77522
|
-
|
|
77523
|
-
|
|
77524
|
-
|
|
77525
|
-
|
|
77583
|
+
if (top && seedEv) {
|
|
77584
|
+
findings2.push({
|
|
77585
|
+
kind: "observation",
|
|
77586
|
+
title: `Seed resolves to ${label} at ${seedLoc}`,
|
|
77587
|
+
detail: top.signature ?? void 0,
|
|
77588
|
+
confidence: 1,
|
|
77589
|
+
evidenceIds: [seedEv.id]
|
|
77590
|
+
});
|
|
77591
|
+
}
|
|
77592
|
+
if (logReseed !== null && seedEv) {
|
|
77526
77593
|
findings2.push({
|
|
77527
77594
|
kind: "observation",
|
|
77528
77595
|
title: logReseed.note,
|
|
@@ -77531,7 +77598,7 @@ async function investigate(input, deps) {
|
|
|
77531
77598
|
evidenceIds: [seedEv.id]
|
|
77532
77599
|
});
|
|
77533
77600
|
}
|
|
77534
|
-
if (ranked.length > 1) {
|
|
77601
|
+
if (ranked.length > 1 && seedEv) {
|
|
77535
77602
|
const candidates = ranked.slice(0, 4).map((r) => `${r.symbol.name} [${r.role}]`).join(", ");
|
|
77536
77603
|
findings2.push({
|
|
77537
77604
|
kind: "observation",
|
|
@@ -77550,7 +77617,7 @@ async function investigate(input, deps) {
|
|
|
77550
77617
|
evidenceIds: flowEvIds
|
|
77551
77618
|
});
|
|
77552
77619
|
}
|
|
77553
|
-
if (impact.affected > 0) {
|
|
77620
|
+
if (top && impactEv && impact && impact.affected > 0) {
|
|
77554
77621
|
findings2.push({
|
|
77555
77622
|
kind: "observation",
|
|
77556
77623
|
title: `Changing ${top.name} impacts ${impact.affected} symbol(s) (blast radius)`,
|
|
@@ -77701,7 +77768,7 @@ async function investigate(input, deps) {
|
|
|
77701
77768
|
});
|
|
77702
77769
|
}
|
|
77703
77770
|
const causeInputs = [];
|
|
77704
|
-
const blastRadius = impact
|
|
77771
|
+
const blastRadius = impact?.affected ?? 0;
|
|
77705
77772
|
if (queueRuntimeState !== null) {
|
|
77706
77773
|
for (const q of queueRuntimeState.queues) {
|
|
77707
77774
|
const isStarved = q.waiting >= 10 && q.active === 0;
|
|
@@ -77730,26 +77797,25 @@ async function investigate(input, deps) {
|
|
|
77730
77797
|
id: `cause:queue-path:${queueName}`,
|
|
77731
77798
|
title: `The ${queueName} processing path (${producer} -> ${worker}) is implicated`,
|
|
77732
77799
|
category: "queue-path",
|
|
77733
|
-
sourceEvidenceIds: [...evIds, impactEv.id],
|
|
77800
|
+
sourceEvidenceIds: [...evIds, ...impactEv ? [impactEv.id] : []],
|
|
77734
77801
|
baseScore: 0.35,
|
|
77735
77802
|
metadata: { blastRadius }
|
|
77736
77803
|
});
|
|
77737
77804
|
}
|
|
77738
77805
|
if (changeEvId) {
|
|
77739
77806
|
const gitChangedFiles = recentChanges?.changedFiles ?? [];
|
|
77740
|
-
const
|
|
77741
|
-
|
|
77742
|
-
);
|
|
77807
|
+
const seedFile = top?.filePath;
|
|
77808
|
+
const seedInChanges = seedFile !== void 0 && gitChangedFiles.length > 0 && gitChangedFiles.some((f) => f === seedFile || seedFile.endsWith(f) || f.endsWith(seedFile));
|
|
77743
77809
|
causeInputs.push({
|
|
77744
77810
|
id: "cause:deployment-regression",
|
|
77745
|
-
title: `Recent change to ${top.name} in ${input.since}..HEAD may have introduced the regression`,
|
|
77811
|
+
title: top ? `Recent change to ${top.name} in ${input.since}..HEAD may have introduced the regression` : `Recent changes in ${input.since}..HEAD may have introduced the regression`,
|
|
77746
77812
|
category: "deployment-regression",
|
|
77747
|
-
sourceEvidenceIds: [changeEvId, seedEv.id],
|
|
77813
|
+
sourceEvidenceIds: [changeEvId, ...seedEv ? [seedEv.id] : []],
|
|
77748
77814
|
baseScore: clamp015((seedInChanges ? 0.45 : 0.25) + (queueHits.length > 0 ? 0.05 : 0)),
|
|
77749
77815
|
metadata: { blastRadius }
|
|
77750
77816
|
});
|
|
77751
77817
|
}
|
|
77752
|
-
if (impact.affected > 0) {
|
|
77818
|
+
if (top && impactEv && seedEv && impact && impact.affected > 0) {
|
|
77753
77819
|
causeInputs.push({
|
|
77754
77820
|
id: "cause:blast-radius",
|
|
77755
77821
|
title: `${top.name} sits on a high-fan-out path (${impact.affected} affected) and may propagate the fault`,
|
|
@@ -77812,13 +77878,20 @@ async function investigate(input, deps) {
|
|
|
77812
77878
|
);
|
|
77813
77879
|
const seedResolved = seeds.length > 0 ? 1 : 0;
|
|
77814
77880
|
const confidence = clamp015(0.5 * evidenceConfidence + 0.5 * seedResolved);
|
|
77815
|
-
const area = ctx
|
|
77881
|
+
const area = ctx?.community?.name ?? top?.filePath ?? (input.service ?? "runtime evidence");
|
|
77816
77882
|
const topCause = rankedCauses[0];
|
|
77817
|
-
const
|
|
77883
|
+
const banner2 = degradedNoSource ? "Runtime-only (source intelligence unavailable). " : "";
|
|
77884
|
+
const scope = top ? `resolved to ${label} (${area})` : `over runtime evidence (${area})`;
|
|
77885
|
+
const summary = topCause ? `${banner2}Investigation of "${hint}" ${scope}. Top suspected cause: ${topCause.title}.` : `${banner2}Investigation of "${hint}" ${scope}. No dominant suspected cause emerged from the available ${degradedNoSource ? "runtime" : "structural"} evidence.`;
|
|
77818
77886
|
const nextActions = buildNextActions(top, ctx, impact, queueHits, changes, input);
|
|
77887
|
+
if (degradedNoSource) {
|
|
77888
|
+
nextActions.unshift(
|
|
77889
|
+
"Source intelligence was unavailable \u2014 run `horus index` to enable code-aware analysis, then re-run for a full investigation."
|
|
77890
|
+
);
|
|
77891
|
+
}
|
|
77819
77892
|
if (ownershipEstimate?.likelyMaintainer) {
|
|
77820
77893
|
nextActions.unshift(
|
|
77821
|
-
`Route to likely maintainer: ${ownershipEstimate.likelyMaintainer} (${Math.round(ownershipEstimate.maintainerShare * 100)}% of commits to ${ownershipEstimate.file ?? top
|
|
77894
|
+
`Route to likely maintainer: ${ownershipEstimate.likelyMaintainer} (${Math.round(ownershipEstimate.maintainerShare * 100)}% of commits to ${ownershipEstimate.file ?? top?.filePath ?? "the implicated file"})`
|
|
77822
77895
|
);
|
|
77823
77896
|
}
|
|
77824
77897
|
const report = {
|
|
@@ -77839,7 +77912,8 @@ async function investigate(input, deps) {
|
|
|
77839
77912
|
nextActions,
|
|
77840
77913
|
ownership: ownershipEstimate,
|
|
77841
77914
|
causeChains: causeChains.length > 0 ? causeChains : void 0,
|
|
77842
|
-
...recentChanges !== void 0 ? { recentChanges } : {}
|
|
77915
|
+
...recentChanges !== void 0 ? { recentChanges } : {},
|
|
77916
|
+
...degradedNoSource ? { degraded: { sourceIntelligence: false, reason: "source-intelligence host unreachable" } } : {}
|
|
77843
77917
|
};
|
|
77844
77918
|
const connectorFlags = deps.connectors ? {
|
|
77845
77919
|
...deps.connectors,
|
|
@@ -77869,6 +77943,7 @@ async function investigate(input, deps) {
|
|
|
77869
77943
|
report.sourceStatus = buildRuntimeSourceStatus(evidence2, connectorFlags);
|
|
77870
77944
|
const persistedId = await persist(db, input, report);
|
|
77871
77945
|
if (persistedId) report.id = persistedId;
|
|
77946
|
+
report.persisted = persistedId !== null;
|
|
77872
77947
|
if (persistedId !== null) {
|
|
77873
77948
|
const tags = deriveTags(report);
|
|
77874
77949
|
report.similarIncidents = await recallSimilar(db, tags, persistedId, input.repo ?? null);
|
|
@@ -77892,16 +77967,20 @@ function buildNextActions(top, _ctx, impact, queueHits, changes, input) {
|
|
|
77892
77967
|
actions.push(`Inspect logs for worker ${worker} on queue ${edge.queueName}`);
|
|
77893
77968
|
actions.push(`Check depth/failures of queue ${edge.queueName}`);
|
|
77894
77969
|
}
|
|
77895
|
-
if (impact.affected > 0) {
|
|
77970
|
+
if (top && impact && impact.affected > 0) {
|
|
77896
77971
|
actions.push(`Review impact set of ${top.name} (${impact.affected} affected symbol(s))`);
|
|
77897
77972
|
}
|
|
77898
|
-
if (
|
|
77899
|
-
|
|
77900
|
-
|
|
77901
|
-
|
|
77973
|
+
if (top) {
|
|
77974
|
+
if (changes && input.since !== void 0) {
|
|
77975
|
+
actions.push(`Diff recent commits touching ${top.filePath} in ${input.since}..HEAD`);
|
|
77976
|
+
} else {
|
|
77977
|
+
actions.push(`Diff recent commits touching ${top.filePath}`);
|
|
77978
|
+
}
|
|
77902
77979
|
}
|
|
77903
77980
|
if (actions.length === 0) {
|
|
77904
|
-
actions.push(
|
|
77981
|
+
actions.push(
|
|
77982
|
+
top ? `Inspect the source of ${top.name} at ${top.filePath}` : input.service ? `Inspect runtime logs and metrics for service "${input.service}"` : "Inspect runtime logs and metrics for the affected service"
|
|
77983
|
+
);
|
|
77905
77984
|
}
|
|
77906
77985
|
return actions;
|
|
77907
77986
|
}
|
|
@@ -80959,22 +81038,22 @@ function extractQueueGraph(input) {
|
|
|
80959
81038
|
}
|
|
80960
81039
|
}
|
|
80961
81040
|
const producers = [];
|
|
80962
|
-
for (const
|
|
80963
|
-
for (const m of
|
|
81041
|
+
for (const pc47 of input.producerClasses) {
|
|
81042
|
+
for (const m of pc47.content.matchAll(INJECT_QUEUE_RE)) {
|
|
80964
81043
|
const queue = m[1] ?? "";
|
|
80965
|
-
if (queue) producers.push({ queue, symbol:
|
|
81044
|
+
if (queue) producers.push({ queue, symbol: pc47.name, file: pc47.filePath });
|
|
80966
81045
|
}
|
|
80967
|
-
for (const m of
|
|
81046
|
+
for (const m of pc47.content.matchAll(NEW_BULL_RE)) {
|
|
80968
81047
|
if (m[2] !== "Queue") continue;
|
|
80969
81048
|
const queue = resolveArg(m[3] ?? "", constMap);
|
|
80970
81049
|
if (queue) {
|
|
80971
|
-
producers.push({ queue, symbol: (m[1] ??
|
|
81050
|
+
producers.push({ queue, symbol: (m[1] ?? pc47.name) || baseName(pc47.filePath), file: pc47.filePath });
|
|
80972
81051
|
}
|
|
80973
81052
|
}
|
|
80974
|
-
for (const m of
|
|
81053
|
+
for (const m of pc47.content.matchAll(QUEUE_NAME_CONST_RE)) {
|
|
80975
81054
|
const ident = m[1] ?? "";
|
|
80976
81055
|
const queue = m[2] ?? "";
|
|
80977
|
-
if (queue) producers.push({ queue, symbol: ident || baseName(
|
|
81056
|
+
if (queue) producers.push({ queue, symbol: ident || baseName(pc47.filePath), file: pc47.filePath });
|
|
80978
81057
|
}
|
|
80979
81058
|
}
|
|
80980
81059
|
const workers = [];
|
|
@@ -90044,21 +90123,21 @@ var Protocol = class {
|
|
|
90044
90123
|
* the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer
|
|
90045
90124
|
* simply propagates the error.
|
|
90046
90125
|
*/
|
|
90047
|
-
async _enqueueTaskMessage(taskId, message,
|
|
90126
|
+
async _enqueueTaskMessage(taskId, message, sessionId2) {
|
|
90048
90127
|
if (!this._taskStore || !this._taskMessageQueue) {
|
|
90049
90128
|
throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
|
|
90050
90129
|
}
|
|
90051
90130
|
const maxQueueSize = this._options?.maxTaskQueueSize;
|
|
90052
|
-
await this._taskMessageQueue.enqueue(taskId, message,
|
|
90131
|
+
await this._taskMessageQueue.enqueue(taskId, message, sessionId2, maxQueueSize);
|
|
90053
90132
|
}
|
|
90054
90133
|
/**
|
|
90055
90134
|
* Clears the message queue for a task and rejects any pending request resolvers.
|
|
90056
90135
|
* @param taskId The task ID whose queue should be cleared
|
|
90057
90136
|
* @param sessionId Optional session ID for binding the operation to a specific session
|
|
90058
90137
|
*/
|
|
90059
|
-
async _clearTaskQueue(taskId,
|
|
90138
|
+
async _clearTaskQueue(taskId, sessionId2) {
|
|
90060
90139
|
if (this._taskMessageQueue) {
|
|
90061
|
-
const messages2 = await this._taskMessageQueue.dequeueAll(taskId,
|
|
90140
|
+
const messages2 = await this._taskMessageQueue.dequeueAll(taskId, sessionId2);
|
|
90062
90141
|
for (const message of messages2) {
|
|
90063
90142
|
if (message.type === "request" && isJSONRPCRequest(message.message)) {
|
|
90064
90143
|
const requestId = message.message.id;
|
|
@@ -90101,7 +90180,7 @@ var Protocol = class {
|
|
|
90101
90180
|
}, { once: true });
|
|
90102
90181
|
});
|
|
90103
90182
|
}
|
|
90104
|
-
requestTaskStore(request,
|
|
90183
|
+
requestTaskStore(request, sessionId2) {
|
|
90105
90184
|
const taskStore = this._taskStore;
|
|
90106
90185
|
if (!taskStore) {
|
|
90107
90186
|
throw new Error("No task store configured");
|
|
@@ -90114,18 +90193,18 @@ var Protocol = class {
|
|
|
90114
90193
|
return await taskStore.createTask(taskParams, request.id, {
|
|
90115
90194
|
method: request.method,
|
|
90116
90195
|
params: request.params
|
|
90117
|
-
},
|
|
90196
|
+
}, sessionId2);
|
|
90118
90197
|
},
|
|
90119
90198
|
getTask: async (taskId) => {
|
|
90120
|
-
const task = await taskStore.getTask(taskId,
|
|
90199
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
90121
90200
|
if (!task) {
|
|
90122
90201
|
throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
|
|
90123
90202
|
}
|
|
90124
90203
|
return task;
|
|
90125
90204
|
},
|
|
90126
90205
|
storeTaskResult: async (taskId, status, result) => {
|
|
90127
|
-
await taskStore.storeTaskResult(taskId, status, result,
|
|
90128
|
-
const task = await taskStore.getTask(taskId,
|
|
90206
|
+
await taskStore.storeTaskResult(taskId, status, result, sessionId2);
|
|
90207
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
90129
90208
|
if (task) {
|
|
90130
90209
|
const notification = TaskStatusNotificationSchema.parse({
|
|
90131
90210
|
method: "notifications/tasks/status",
|
|
@@ -90138,18 +90217,18 @@ var Protocol = class {
|
|
|
90138
90217
|
}
|
|
90139
90218
|
},
|
|
90140
90219
|
getTaskResult: (taskId) => {
|
|
90141
|
-
return taskStore.getTaskResult(taskId,
|
|
90220
|
+
return taskStore.getTaskResult(taskId, sessionId2);
|
|
90142
90221
|
},
|
|
90143
90222
|
updateTaskStatus: async (taskId, status, statusMessage) => {
|
|
90144
|
-
const task = await taskStore.getTask(taskId,
|
|
90223
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
90145
90224
|
if (!task) {
|
|
90146
90225
|
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
|
|
90147
90226
|
}
|
|
90148
90227
|
if (isTerminal(task.status)) {
|
|
90149
90228
|
throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
|
|
90150
90229
|
}
|
|
90151
|
-
await taskStore.updateTaskStatus(taskId, status, statusMessage,
|
|
90152
|
-
const updatedTask = await taskStore.getTask(taskId,
|
|
90230
|
+
await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId2);
|
|
90231
|
+
const updatedTask = await taskStore.getTask(taskId, sessionId2);
|
|
90153
90232
|
if (updatedTask) {
|
|
90154
90233
|
const notification = TaskStatusNotificationSchema.parse({
|
|
90155
90234
|
method: "notifications/tasks/status",
|
|
@@ -90162,7 +90241,7 @@ var Protocol = class {
|
|
|
90162
90241
|
}
|
|
90163
90242
|
},
|
|
90164
90243
|
listTasks: (cursor) => {
|
|
90165
|
-
return taskStore.listTasks(cursor,
|
|
90244
|
+
return taskStore.listTasks(cursor, sessionId2);
|
|
90166
90245
|
}
|
|
90167
90246
|
};
|
|
90168
90247
|
}
|
|
@@ -90516,8 +90595,8 @@ var Server = class extends Protocol {
|
|
|
90516
90595
|
this._serverInfo = _serverInfo;
|
|
90517
90596
|
this._loggingLevels = /* @__PURE__ */ new Map();
|
|
90518
90597
|
this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index2) => [level, index2]));
|
|
90519
|
-
this.isMessageIgnored = (level,
|
|
90520
|
-
const currentLevel = this._loggingLevels.get(
|
|
90598
|
+
this.isMessageIgnored = (level, sessionId2) => {
|
|
90599
|
+
const currentLevel = this._loggingLevels.get(sessionId2);
|
|
90521
90600
|
return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
|
|
90522
90601
|
};
|
|
90523
90602
|
this._capabilities = options?.capabilities ?? {};
|
|
@@ -90860,9 +90939,9 @@ var Server = class extends Protocol {
|
|
|
90860
90939
|
* @param params
|
|
90861
90940
|
* @param sessionId optional for stateless and backward compatibility
|
|
90862
90941
|
*/
|
|
90863
|
-
async sendLoggingMessage(params,
|
|
90942
|
+
async sendLoggingMessage(params, sessionId2) {
|
|
90864
90943
|
if (this._capabilities.logging) {
|
|
90865
|
-
if (!this.isMessageIgnored(params.level,
|
|
90944
|
+
if (!this.isMessageIgnored(params.level, sessionId2)) {
|
|
90866
90945
|
return this.notification({ method: "notifications/message", params });
|
|
90867
90946
|
}
|
|
90868
90947
|
}
|
|
@@ -91667,8 +91746,8 @@ var McpServer = class {
|
|
|
91667
91746
|
* @param params
|
|
91668
91747
|
* @param sessionId optional for stateless and backward compatibility
|
|
91669
91748
|
*/
|
|
91670
|
-
async sendLoggingMessage(params,
|
|
91671
|
-
return this.server.sendLoggingMessage(params,
|
|
91749
|
+
async sendLoggingMessage(params, sessionId2) {
|
|
91750
|
+
return this.server.sendLoggingMessage(params, sessionId2);
|
|
91672
91751
|
}
|
|
91673
91752
|
/**
|
|
91674
91753
|
* Sends a resource list changed event to the client, if connected.
|
|
@@ -93719,7 +93798,7 @@ function formatAge(ms) {
|
|
|
93719
93798
|
|
|
93720
93799
|
// ../../packages/cli/src/commands/investigate.ts
|
|
93721
93800
|
init_cjs_shims();
|
|
93722
|
-
var
|
|
93801
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
93723
93802
|
|
|
93724
93803
|
// ../../packages/cli/src/lib/cloud/investigation-sync.ts
|
|
93725
93804
|
init_cjs_shims();
|
|
@@ -93796,6 +93875,379 @@ async function listCloudInvestigations(client, cfg) {
|
|
|
93796
93875
|
return result.investigations;
|
|
93797
93876
|
}
|
|
93798
93877
|
|
|
93878
|
+
// ../../packages/cli/src/lib/telemetry/client.ts
|
|
93879
|
+
init_cjs_shims();
|
|
93880
|
+
var import_node_crypto6 = require("crypto");
|
|
93881
|
+
var import_node_os3 = require("os");
|
|
93882
|
+
|
|
93883
|
+
// ../../packages/cli/src/lib/telemetry/consent.ts
|
|
93884
|
+
init_cjs_shims();
|
|
93885
|
+
|
|
93886
|
+
// ../../packages/cli/src/lib/telemetry/store.ts
|
|
93887
|
+
init_cjs_shims();
|
|
93888
|
+
var import_node_fs13 = require("fs");
|
|
93889
|
+
var import_node_crypto5 = require("crypto");
|
|
93890
|
+
|
|
93891
|
+
// ../../packages/cli/src/lib/telemetry/paths.ts
|
|
93892
|
+
init_cjs_shims();
|
|
93893
|
+
var import_node_path14 = require("path");
|
|
93894
|
+
var TELEMETRY_FILE = "telemetry.json";
|
|
93895
|
+
var TELEMETRY_QUEUE_DIR = "telemetry-queue";
|
|
93896
|
+
function telemetryPath() {
|
|
93897
|
+
return (0, import_node_path14.join)(horusHome(), TELEMETRY_FILE);
|
|
93898
|
+
}
|
|
93899
|
+
function telemetryQueueDir() {
|
|
93900
|
+
return (0, import_node_path14.join)(horusHome(), TELEMETRY_QUEUE_DIR);
|
|
93901
|
+
}
|
|
93902
|
+
|
|
93903
|
+
// ../../packages/cli/src/lib/telemetry/store.ts
|
|
93904
|
+
var TELEMETRY_SCHEMA_VERSION = 1;
|
|
93905
|
+
function nowIso() {
|
|
93906
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
93907
|
+
}
|
|
93908
|
+
function defaultTelemetryState(installId = (0, import_node_crypto5.randomUUID)()) {
|
|
93909
|
+
const now = nowIso();
|
|
93910
|
+
return {
|
|
93911
|
+
schemaVersion: TELEMETRY_SCHEMA_VERSION,
|
|
93912
|
+
installId,
|
|
93913
|
+
tierA: { enabled: true, noticeShownAt: null },
|
|
93914
|
+
tierB: { enabled: false, enabledAt: null },
|
|
93915
|
+
createdAt: now,
|
|
93916
|
+
updatedAt: now
|
|
93917
|
+
};
|
|
93918
|
+
}
|
|
93919
|
+
function coerce2(raw) {
|
|
93920
|
+
if (!raw || typeof raw !== "object") return null;
|
|
93921
|
+
const r = raw;
|
|
93922
|
+
if (typeof r.installId !== "string" || r.installId.length === 0) return null;
|
|
93923
|
+
const tierA = r.tierA ?? {};
|
|
93924
|
+
const tierB = r.tierB ?? {};
|
|
93925
|
+
return {
|
|
93926
|
+
schemaVersion: typeof r.schemaVersion === "number" ? r.schemaVersion : TELEMETRY_SCHEMA_VERSION,
|
|
93927
|
+
installId: r.installId,
|
|
93928
|
+
tierA: {
|
|
93929
|
+
enabled: typeof tierA.enabled === "boolean" ? tierA.enabled : true,
|
|
93930
|
+
noticeShownAt: typeof tierA.noticeShownAt === "string" ? tierA.noticeShownAt : null
|
|
93931
|
+
},
|
|
93932
|
+
tierB: {
|
|
93933
|
+
enabled: typeof tierB.enabled === "boolean" ? tierB.enabled : false,
|
|
93934
|
+
enabledAt: typeof tierB.enabledAt === "string" ? tierB.enabledAt : null
|
|
93935
|
+
},
|
|
93936
|
+
createdAt: typeof r.createdAt === "string" ? r.createdAt : nowIso(),
|
|
93937
|
+
updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : nowIso()
|
|
93938
|
+
};
|
|
93939
|
+
}
|
|
93940
|
+
function readTelemetryState() {
|
|
93941
|
+
const p = telemetryPath();
|
|
93942
|
+
if (!(0, import_node_fs13.existsSync)(p)) return null;
|
|
93943
|
+
try {
|
|
93944
|
+
return coerce2(JSON.parse((0, import_node_fs13.readFileSync)(p, "utf8")));
|
|
93945
|
+
} catch {
|
|
93946
|
+
return null;
|
|
93947
|
+
}
|
|
93948
|
+
}
|
|
93949
|
+
function writeTelemetryState(state) {
|
|
93950
|
+
(0, import_node_fs13.mkdirSync)(horusHome(), { recursive: true });
|
|
93951
|
+
const p = telemetryPath();
|
|
93952
|
+
const tmp = `${p}.${process.pid}.tmp`;
|
|
93953
|
+
(0, import_node_fs13.writeFileSync)(tmp, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
|
|
93954
|
+
try {
|
|
93955
|
+
(0, import_node_fs13.renameSync)(tmp, p);
|
|
93956
|
+
} catch (err) {
|
|
93957
|
+
try {
|
|
93958
|
+
(0, import_node_fs13.rmSync)(tmp);
|
|
93959
|
+
} catch {
|
|
93960
|
+
}
|
|
93961
|
+
throw err;
|
|
93962
|
+
}
|
|
93963
|
+
try {
|
|
93964
|
+
(0, import_node_fs13.chmodSync)(p, 384);
|
|
93965
|
+
} catch {
|
|
93966
|
+
}
|
|
93967
|
+
}
|
|
93968
|
+
function loadOrInitTelemetryState() {
|
|
93969
|
+
const existing = readTelemetryState();
|
|
93970
|
+
if (existing) return existing;
|
|
93971
|
+
const created = defaultTelemetryState();
|
|
93972
|
+
writeTelemetryState(created);
|
|
93973
|
+
return created;
|
|
93974
|
+
}
|
|
93975
|
+
function updateTelemetryState(mutate) {
|
|
93976
|
+
const state = loadOrInitTelemetryState();
|
|
93977
|
+
mutate(state);
|
|
93978
|
+
state.updatedAt = nowIso();
|
|
93979
|
+
writeTelemetryState(state);
|
|
93980
|
+
return state;
|
|
93981
|
+
}
|
|
93982
|
+
function deleteTelemetryState() {
|
|
93983
|
+
const p = telemetryPath();
|
|
93984
|
+
if ((0, import_node_fs13.existsSync)(p)) (0, import_node_fs13.rmSync)(p);
|
|
93985
|
+
}
|
|
93986
|
+
|
|
93987
|
+
// ../../packages/cli/src/lib/telemetry/consent.ts
|
|
93988
|
+
var OFF_VALUES = /* @__PURE__ */ new Set(["0", "false", "off", "no", "disable", "disabled"]);
|
|
93989
|
+
var ON_VALUES = /* @__PURE__ */ new Set(["1", "true", "on", "yes", "enable", "enabled", "all"]);
|
|
93990
|
+
var CI_ENV_VARS = [
|
|
93991
|
+
"CI",
|
|
93992
|
+
"CONTINUOUS_INTEGRATION",
|
|
93993
|
+
"GITHUB_ACTIONS",
|
|
93994
|
+
"GITLAB_CI",
|
|
93995
|
+
"BUILDKITE",
|
|
93996
|
+
"CIRCLECI",
|
|
93997
|
+
"TRAVIS",
|
|
93998
|
+
"JENKINS_URL",
|
|
93999
|
+
"TEAMCITY_VERSION"
|
|
94000
|
+
];
|
|
94001
|
+
function doNotTrack(env) {
|
|
94002
|
+
const v = env.DO_NOT_TRACK;
|
|
94003
|
+
if (v == null || v === "") return false;
|
|
94004
|
+
return !OFF_VALUES.has(v.trim().toLowerCase());
|
|
94005
|
+
}
|
|
94006
|
+
function parseEnvFlag(value) {
|
|
94007
|
+
if (value == null || value === "") return void 0;
|
|
94008
|
+
const v = value.trim().toLowerCase();
|
|
94009
|
+
if (OFF_VALUES.has(v)) return "off";
|
|
94010
|
+
if (ON_VALUES.has(v)) return "on";
|
|
94011
|
+
return void 0;
|
|
94012
|
+
}
|
|
94013
|
+
function isCI(env = process.env) {
|
|
94014
|
+
return CI_ENV_VARS.some((key) => {
|
|
94015
|
+
const v = env[key];
|
|
94016
|
+
return v != null && v !== "" && !OFF_VALUES.has(v.trim().toLowerCase());
|
|
94017
|
+
});
|
|
94018
|
+
}
|
|
94019
|
+
function off(source, reason) {
|
|
94020
|
+
return { tierA: false, tierB: false, forcedOff: source === "env", source, reason };
|
|
94021
|
+
}
|
|
94022
|
+
function resolveConsent(opts = {}) {
|
|
94023
|
+
const env = opts.env ?? process.env;
|
|
94024
|
+
if (doNotTrack(env)) return off("env", "DO_NOT_TRACK is set");
|
|
94025
|
+
const envFlag = parseEnvFlag(env.HORUS_TELEMETRY);
|
|
94026
|
+
if (envFlag === "off") return off("env", "HORUS_TELEMETRY disables telemetry");
|
|
94027
|
+
const state = opts.state !== void 0 ? opts.state : readTelemetryState();
|
|
94028
|
+
const storedTierA = state?.tierA.enabled ?? true;
|
|
94029
|
+
const storedTierB = state?.tierB.enabled ?? false;
|
|
94030
|
+
if (isCI(env) && envFlag !== "on") {
|
|
94031
|
+
return {
|
|
94032
|
+
tierA: false,
|
|
94033
|
+
tierB: false,
|
|
94034
|
+
forcedOff: false,
|
|
94035
|
+
source: "ci",
|
|
94036
|
+
reason: "CI environment detected \u2014 telemetry off by default"
|
|
94037
|
+
};
|
|
94038
|
+
}
|
|
94039
|
+
if (envFlag === "on") {
|
|
94040
|
+
return {
|
|
94041
|
+
tierA: true,
|
|
94042
|
+
tierB: storedTierB,
|
|
94043
|
+
forcedOff: false,
|
|
94044
|
+
source: "env",
|
|
94045
|
+
reason: "HORUS_TELEMETRY enables usage metadata"
|
|
94046
|
+
};
|
|
94047
|
+
}
|
|
94048
|
+
return {
|
|
94049
|
+
tierA: storedTierA,
|
|
94050
|
+
tierB: storedTierB,
|
|
94051
|
+
forcedOff: false,
|
|
94052
|
+
source: state ? "state" : "default",
|
|
94053
|
+
reason: state ? "from ~/.horus/telemetry.json" : "default settings (no saved preference)"
|
|
94054
|
+
};
|
|
94055
|
+
}
|
|
94056
|
+
function isContentSharingEnabled(env = process.env) {
|
|
94057
|
+
return resolveConsent({ env }).tierB;
|
|
94058
|
+
}
|
|
94059
|
+
|
|
94060
|
+
// ../../packages/cli/src/lib/telemetry/spool.ts
|
|
94061
|
+
init_cjs_shims();
|
|
94062
|
+
var import_node_fs14 = require("fs");
|
|
94063
|
+
var import_node_path15 = require("path");
|
|
94064
|
+
var SPOOL_FILE = "events.jsonl";
|
|
94065
|
+
var MAX_SPOOL_BYTES = 256 * 1024;
|
|
94066
|
+
function spoolFilePath() {
|
|
94067
|
+
return (0, import_node_path15.join)(telemetryQueueDir(), SPOOL_FILE);
|
|
94068
|
+
}
|
|
94069
|
+
function spoolEvent(event) {
|
|
94070
|
+
try {
|
|
94071
|
+
(0, import_node_fs14.mkdirSync)(telemetryQueueDir(), { recursive: true });
|
|
94072
|
+
const file = spoolFilePath();
|
|
94073
|
+
let size2 = 0;
|
|
94074
|
+
try {
|
|
94075
|
+
size2 = (0, import_node_fs14.statSync)(file).size;
|
|
94076
|
+
} catch {
|
|
94077
|
+
}
|
|
94078
|
+
if (size2 >= MAX_SPOOL_BYTES) return;
|
|
94079
|
+
(0, import_node_fs14.appendFileSync)(file, JSON.stringify(event) + "\n", { mode: 384 });
|
|
94080
|
+
} catch {
|
|
94081
|
+
}
|
|
94082
|
+
}
|
|
94083
|
+
function readSpooledEvents() {
|
|
94084
|
+
try {
|
|
94085
|
+
const file = spoolFilePath();
|
|
94086
|
+
if (!(0, import_node_fs14.existsSync)(file)) return [];
|
|
94087
|
+
return (0, import_node_fs14.readFileSync)(file, "utf8").split("\n").filter((line2) => line2.trim().length > 0).map((line2) => {
|
|
94088
|
+
try {
|
|
94089
|
+
return JSON.parse(line2);
|
|
94090
|
+
} catch {
|
|
94091
|
+
return null;
|
|
94092
|
+
}
|
|
94093
|
+
}).filter((e) => e !== null);
|
|
94094
|
+
} catch {
|
|
94095
|
+
return [];
|
|
94096
|
+
}
|
|
94097
|
+
}
|
|
94098
|
+
function clearSpool() {
|
|
94099
|
+
try {
|
|
94100
|
+
const file = spoolFilePath();
|
|
94101
|
+
if ((0, import_node_fs14.existsSync)(file)) (0, import_node_fs14.rmSync)(file);
|
|
94102
|
+
} catch {
|
|
94103
|
+
}
|
|
94104
|
+
}
|
|
94105
|
+
|
|
94106
|
+
// ../../packages/cli/src/lib/telemetry/events.ts
|
|
94107
|
+
init_cjs_shims();
|
|
94108
|
+
var TELEMETRY_EVENT_SCHEMA_VERSION = 1;
|
|
94109
|
+
var TIER_B_EVENT_TYPES = /* @__PURE__ */ new Set(["investigation.content"]);
|
|
94110
|
+
function tierForEventType(type) {
|
|
94111
|
+
return TIER_B_EVENT_TYPES.has(type) ? "B" : "A";
|
|
94112
|
+
}
|
|
94113
|
+
function extractFlagNames(argv) {
|
|
94114
|
+
const names = /* @__PURE__ */ new Set();
|
|
94115
|
+
for (const token of argv.slice(2)) {
|
|
94116
|
+
if (token.startsWith("--") && token.length > 2) {
|
|
94117
|
+
const name = token.slice(2).split("=")[0];
|
|
94118
|
+
if (name) names.add(name);
|
|
94119
|
+
}
|
|
94120
|
+
}
|
|
94121
|
+
return [...names];
|
|
94122
|
+
}
|
|
94123
|
+
|
|
94124
|
+
// ../../packages/cli/src/lib/telemetry/client.ts
|
|
94125
|
+
var sessionId = null;
|
|
94126
|
+
function getSessionId() {
|
|
94127
|
+
if (!sessionId) sessionId = (0, import_node_crypto6.randomUUID)();
|
|
94128
|
+
return sessionId;
|
|
94129
|
+
}
|
|
94130
|
+
function track(input) {
|
|
94131
|
+
try {
|
|
94132
|
+
const state = readTelemetryState();
|
|
94133
|
+
if (!state) return;
|
|
94134
|
+
const tier = tierForEventType(input.type);
|
|
94135
|
+
const decision = resolveConsent({ state });
|
|
94136
|
+
if (tier === "A" ? !decision.tierA : !decision.tierB) return;
|
|
94137
|
+
const event = {
|
|
94138
|
+
schemaVersion: TELEMETRY_EVENT_SCHEMA_VERSION,
|
|
94139
|
+
tier,
|
|
94140
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
94141
|
+
installId: state.installId,
|
|
94142
|
+
sessionId: getSessionId(),
|
|
94143
|
+
cliVersion: HORUS_VERSION,
|
|
94144
|
+
os: (0, import_node_os3.platform)(),
|
|
94145
|
+
arch: (0, import_node_os3.arch)(),
|
|
94146
|
+
...input
|
|
94147
|
+
};
|
|
94148
|
+
spoolEvent(event);
|
|
94149
|
+
} catch {
|
|
94150
|
+
}
|
|
94151
|
+
}
|
|
94152
|
+
|
|
94153
|
+
// ../../packages/cli/src/lib/telemetry/feedback.ts
|
|
94154
|
+
init_cjs_shims();
|
|
94155
|
+
var import_promises = require("readline/promises");
|
|
94156
|
+
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
94157
|
+
var MANUAL_MINUTES = {
|
|
94158
|
+
"1": 5,
|
|
94159
|
+
// < 5 min
|
|
94160
|
+
"2": 30,
|
|
94161
|
+
// ~30 min
|
|
94162
|
+
"3": 120,
|
|
94163
|
+
// ~2 h
|
|
94164
|
+
"4": 480,
|
|
94165
|
+
// half-day+
|
|
94166
|
+
"5": null
|
|
94167
|
+
// unsure
|
|
94168
|
+
};
|
|
94169
|
+
function parseResolved(raw) {
|
|
94170
|
+
const v = raw.trim().toLowerCase();
|
|
94171
|
+
if (v.startsWith("y")) return "yes";
|
|
94172
|
+
if (v.startsWith("p")) return "partly";
|
|
94173
|
+
if (v.startsWith("n")) return "no";
|
|
94174
|
+
return null;
|
|
94175
|
+
}
|
|
94176
|
+
function parseManualEstimate(raw) {
|
|
94177
|
+
return MANUAL_MINUTES[raw.trim()] ?? null;
|
|
94178
|
+
}
|
|
94179
|
+
function submitFeedback(input) {
|
|
94180
|
+
track({ type: "feedback.submitted", ...input });
|
|
94181
|
+
}
|
|
94182
|
+
async function runFeedbackPrompt(investigationId, horusSeconds) {
|
|
94183
|
+
const rl = (0, import_promises.createInterface)({ input: process.stdin, output: process.stdout });
|
|
94184
|
+
try {
|
|
94185
|
+
console.log(import_picocolors8.default.dim("\nQuick feedback to improve Horus (press Enter to skip):"));
|
|
94186
|
+
const a1 = await rl.question(
|
|
94187
|
+
import_picocolors8.default.bold(" Did this point you at the cause? ") + import_picocolors8.default.dim("[y / partly / n] ")
|
|
94188
|
+
);
|
|
94189
|
+
if (a1.trim() === "") return false;
|
|
94190
|
+
const a2 = await rl.question(
|
|
94191
|
+
import_picocolors8.default.bold(" Manually, finding this would have taken? ") + import_picocolors8.default.dim("[1:<5m 2:~30m 3:~2h 4:half-day+ 5:unsure] ")
|
|
94192
|
+
);
|
|
94193
|
+
submitFeedback({
|
|
94194
|
+
investigationId,
|
|
94195
|
+
resolved: parseResolved(a1),
|
|
94196
|
+
manualEstimateMinutes: parseManualEstimate(a2),
|
|
94197
|
+
horusSeconds
|
|
94198
|
+
});
|
|
94199
|
+
console.log(import_picocolors8.default.dim(" Thanks \u2014 recorded."));
|
|
94200
|
+
return true;
|
|
94201
|
+
} finally {
|
|
94202
|
+
rl.close();
|
|
94203
|
+
}
|
|
94204
|
+
}
|
|
94205
|
+
async function maybePromptFeedback(opts) {
|
|
94206
|
+
try {
|
|
94207
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
94208
|
+
if (!resolveConsent().tierA) return;
|
|
94209
|
+
const rate = opts.sampleRate ?? 0.25;
|
|
94210
|
+
const rand = (opts.random ?? Math.random)();
|
|
94211
|
+
if (rand > rate) return;
|
|
94212
|
+
await runFeedbackPrompt(opts.investigationId, opts.horusSeconds ?? null);
|
|
94213
|
+
} catch {
|
|
94214
|
+
}
|
|
94215
|
+
}
|
|
94216
|
+
|
|
94217
|
+
// ../../packages/cli/src/lib/ensure-host.ts
|
|
94218
|
+
init_cjs_shims();
|
|
94219
|
+
function parseHostPort(hostUrl) {
|
|
94220
|
+
try {
|
|
94221
|
+
const port = parseInt(new URL(hostUrl).port, 10);
|
|
94222
|
+
return Number.isNaN(port) ? null : port;
|
|
94223
|
+
} catch {
|
|
94224
|
+
return null;
|
|
94225
|
+
}
|
|
94226
|
+
}
|
|
94227
|
+
function ensureHostReasonHint(reason) {
|
|
94228
|
+
switch (reason) {
|
|
94229
|
+
case "source-unavailable":
|
|
94230
|
+
return "horus-source is not installed \u2014 install it: pip install horus-source";
|
|
94231
|
+
case "not-analyzed":
|
|
94232
|
+
return "this repo has not been indexed yet \u2014 run: horus index";
|
|
94233
|
+
case "bad-url":
|
|
94234
|
+
return "the configured source host URL is not a valid URL";
|
|
94235
|
+
default:
|
|
94236
|
+
return "the host did not become healthy \u2014 run: horus index";
|
|
94237
|
+
}
|
|
94238
|
+
}
|
|
94239
|
+
async function ensureSourceHost(root, hostUrl, opts = {}) {
|
|
94240
|
+
if (await isHostHealthy(hostUrl)) return { ok: true, hostUrl };
|
|
94241
|
+
if (!await sourceAvailable()) return { ok: false, reason: "source-unavailable" };
|
|
94242
|
+
if (!isAnalyzed(root)) return { ok: false, reason: "not-analyzed" };
|
|
94243
|
+
const port = parseHostPort(hostUrl);
|
|
94244
|
+
if (port === null) return { ok: false, reason: "bad-url" };
|
|
94245
|
+
startHost(root, port);
|
|
94246
|
+
if (await waitForHost(hostUrl, opts.timeoutMs ?? 2e4)) return { ok: true, hostUrl };
|
|
94247
|
+
removeSpawnedHostRecord(root);
|
|
94248
|
+
return { ok: false, reason: "unhealthy" };
|
|
94249
|
+
}
|
|
94250
|
+
|
|
93799
94251
|
// ../../packages/cli/src/commands/investigate.ts
|
|
93800
94252
|
function extractEvidenceExcerpt(e) {
|
|
93801
94253
|
if (!e.payload || typeof e.payload !== "object") return void 0;
|
|
@@ -93854,37 +94306,37 @@ function renderStoredAIJudgment(judgment, write = (l) => console.log(l)) {
|
|
|
93854
94306
|
const sep = "\u2500".repeat(60);
|
|
93855
94307
|
write(`
|
|
93856
94308
|
${sep}`);
|
|
93857
|
-
write(
|
|
94309
|
+
write(import_picocolors9.default.bold("AI Judgment") + import_picocolors9.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
|
|
93858
94310
|
write(sep);
|
|
93859
94311
|
if (judgment.hypothesisJudgments && judgment.hypothesisJudgments.length > 0) {
|
|
93860
|
-
write(
|
|
94312
|
+
write(import_picocolors9.default.bold("Hypothesis verdicts:"));
|
|
93861
94313
|
for (const j of judgment.hypothesisJudgments) {
|
|
93862
|
-
const verdictColor = j.verdict === "supported" ?
|
|
93863
|
-
write(` ${verdictColor(`[${j.verdict}]`)} ${j.category}` +
|
|
93864
|
-
write(
|
|
94314
|
+
const verdictColor = j.verdict === "supported" ? import_picocolors9.default.green : j.verdict === "weakened" ? import_picocolors9.default.yellow : j.verdict === "eliminated" ? import_picocolors9.default.red : import_picocolors9.default.dim;
|
|
94315
|
+
write(` ${verdictColor(`[${j.verdict}]`)} ${j.category}` + import_picocolors9.default.dim(` (${(j.confidence * 100).toFixed(0)}%)`));
|
|
94316
|
+
write(import_picocolors9.default.dim(` ${j.rationale}`));
|
|
93865
94317
|
}
|
|
93866
94318
|
write("");
|
|
93867
94319
|
}
|
|
93868
94320
|
if (judgment.rootCauseAssessment) {
|
|
93869
94321
|
const rca = judgment.rootCauseAssessment;
|
|
93870
|
-
const uncertaintyColor = rca.uncertainty === "low" ?
|
|
93871
|
-
write(
|
|
94322
|
+
const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors9.default.green : rca.uncertainty === "medium" ? import_picocolors9.default.yellow : import_picocolors9.default.red;
|
|
94323
|
+
write(import_picocolors9.default.bold("Root cause assessment:") + import_picocolors9.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
|
|
93872
94324
|
write(rca.summary);
|
|
93873
94325
|
if (rca.citedEvidenceIds.length > 0) {
|
|
93874
|
-
write(
|
|
94326
|
+
write(import_picocolors9.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
|
|
93875
94327
|
}
|
|
93876
94328
|
write("");
|
|
93877
94329
|
}
|
|
93878
|
-
write(
|
|
93879
|
-
write(
|
|
94330
|
+
write(import_picocolors9.default.bold("What:") + " " + judgment.what);
|
|
94331
|
+
write(import_picocolors9.default.bold("Why:") + " " + judgment.why);
|
|
93880
94332
|
if (judgment.whereNext.length > 0) {
|
|
93881
|
-
write(
|
|
94333
|
+
write(import_picocolors9.default.bold("Next steps:"));
|
|
93882
94334
|
for (const step of judgment.whereNext) {
|
|
93883
94335
|
write(` \u2022 ${step}`);
|
|
93884
94336
|
}
|
|
93885
94337
|
}
|
|
93886
94338
|
if (judgment.citations.length > 0) {
|
|
93887
|
-
write(
|
|
94339
|
+
write(import_picocolors9.default.dim(`
|
|
93888
94340
|
Cited evidence: ${judgment.citations.map((c) => c.evidenceId).join(", ")}`));
|
|
93889
94341
|
}
|
|
93890
94342
|
}
|
|
@@ -93902,6 +94354,7 @@ function classifyAIFailure(firstError) {
|
|
|
93902
94354
|
return firstError;
|
|
93903
94355
|
}
|
|
93904
94356
|
async function runInvestigate(hint, opts) {
|
|
94357
|
+
const startedAtMs = Date.now();
|
|
93905
94358
|
try {
|
|
93906
94359
|
const config2 = await loadConfig(opts.config, { name: opts.name });
|
|
93907
94360
|
const projectName = opts.project ?? opts.repo;
|
|
@@ -93909,7 +94362,7 @@ async function runInvestigate(hint, opts) {
|
|
|
93909
94362
|
try {
|
|
93910
94363
|
renv = resolveEnvironment(config2, { project: projectName, env: opts.env });
|
|
93911
94364
|
} catch (err) {
|
|
93912
|
-
console.error(
|
|
94365
|
+
console.error(import_picocolors9.default.red(err.message));
|
|
93913
94366
|
return 1;
|
|
93914
94367
|
}
|
|
93915
94368
|
const repoRoot = repoRootOrCwd(renv.path);
|
|
@@ -93918,30 +94371,41 @@ async function runInvestigate(hint, opts) {
|
|
|
93918
94371
|
const cloudSession = cloudActive ? authedClient() : null;
|
|
93919
94372
|
if (cloudActive && !cloudSession) {
|
|
93920
94373
|
console.error(
|
|
93921
|
-
|
|
93922
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
94374
|
+
import_picocolors9.default.yellow(
|
|
94375
|
+
`This repo is linked to Horus Cloud but you are not logged in \u2014 running locally only. Run ${import_picocolors9.default.bold("horus login")} to also sync results to the cloud.`
|
|
93923
94376
|
)
|
|
93924
94377
|
);
|
|
93925
|
-
return 1;
|
|
93926
94378
|
}
|
|
93927
94379
|
const code = codeForEnv(renv);
|
|
93928
94380
|
if (!code) {
|
|
93929
94381
|
console.error(
|
|
93930
|
-
|
|
94382
|
+
import_picocolors9.default.red(
|
|
93931
94383
|
`No source-intelligence connector configured for project "${renv.project}" / env "${renv.env}".`
|
|
93932
94384
|
)
|
|
93933
94385
|
);
|
|
93934
94386
|
return 1;
|
|
93935
94387
|
}
|
|
93936
|
-
const
|
|
93937
|
-
|
|
93938
|
-
|
|
94388
|
+
const sourceUrl = renv.repositories[0]?.sourceHostUrl ?? renv.repositories[0]?.axonHostUrl;
|
|
94389
|
+
let health = await code.health();
|
|
94390
|
+
if (!health.ok && sourceUrl) {
|
|
93939
94391
|
console.error(
|
|
93940
|
-
|
|
93941
|
-
|
|
94392
|
+
import_picocolors9.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`)
|
|
94393
|
+
);
|
|
94394
|
+
const healed = await ensureSourceHost(renv.path, sourceUrl);
|
|
94395
|
+
if (healed.ok) {
|
|
94396
|
+
console.error(import_picocolors9.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
|
|
94397
|
+
health = await code.health();
|
|
94398
|
+
} else {
|
|
94399
|
+
console.error(import_picocolors9.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
|
|
94400
|
+
}
|
|
94401
|
+
}
|
|
94402
|
+
const runtimeOnly = !health.ok;
|
|
94403
|
+
if (runtimeOnly) {
|
|
94404
|
+
console.error(
|
|
94405
|
+
import_picocolors9.default.yellow(
|
|
94406
|
+
`Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors9.default.bold("horus index")} for a full (code-aware) investigation.`
|
|
93942
94407
|
)
|
|
93943
94408
|
);
|
|
93944
|
-
return 1;
|
|
93945
94409
|
}
|
|
93946
94410
|
const logs = logsForEnv(renv);
|
|
93947
94411
|
const mongo = mongoForEnv(renv);
|
|
@@ -93954,7 +94418,9 @@ async function runInvestigate(hint, opts) {
|
|
|
93954
94418
|
const report = await investigate(
|
|
93955
94419
|
{ hint, repo: renv.project, since: opts.since, service },
|
|
93956
94420
|
{
|
|
93957
|
-
|
|
94421
|
+
// Runtime-only degrade (HOR-319 layer-2): pass no source provider so the engine
|
|
94422
|
+
// skips seed resolution + structural evidence and builds from runtime evidence.
|
|
94423
|
+
code: runtimeOnly ? null : code,
|
|
93958
94424
|
db,
|
|
93959
94425
|
logs,
|
|
93960
94426
|
mongo,
|
|
@@ -93982,7 +94448,7 @@ async function runInvestigate(hint, opts) {
|
|
|
93982
94448
|
model,
|
|
93983
94449
|
...ai.anthropicApiKey !== void 0 ? { apiKey: ai.anthropicApiKey } : {}
|
|
93984
94450
|
});
|
|
93985
|
-
console.log(
|
|
94451
|
+
console.log(import_picocolors9.default.dim(`[ai] model: ${model}`));
|
|
93986
94452
|
const narrativeInput = buildNarrativeInput(report);
|
|
93987
94453
|
const { output, fromProvider, degraded, validationErrors } = await renderNarrative(
|
|
93988
94454
|
narrativeInput,
|
|
@@ -93998,34 +94464,79 @@ async function runInvestigate(hint, opts) {
|
|
|
93998
94464
|
renderStoredAIJudgment(stored);
|
|
93999
94465
|
} else if (degraded) {
|
|
94000
94466
|
if (validationErrors && validationErrors.length > 0) {
|
|
94001
|
-
console.error(
|
|
94467
|
+
console.error(import_picocolors9.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
|
|
94002
94468
|
}
|
|
94003
94469
|
console.log("");
|
|
94004
|
-
console.log(
|
|
94005
|
-
console.log(
|
|
94470
|
+
console.log(import_picocolors9.default.dim("\u2500".repeat(60)));
|
|
94471
|
+
console.log(import_picocolors9.default.bold("AI narrative ") + import_picocolors9.default.yellow("(unstructured \u2014 not validated)"));
|
|
94006
94472
|
if (output.what?.trim()) {
|
|
94007
|
-
console.log(
|
|
94473
|
+
console.log(import_picocolors9.default.bold("\nWhat:"), output.what.trim());
|
|
94008
94474
|
}
|
|
94009
94475
|
if (output.why?.trim()) {
|
|
94010
|
-
console.log(
|
|
94476
|
+
console.log(import_picocolors9.default.bold("\nWhy:"), output.why.trim());
|
|
94011
94477
|
}
|
|
94012
94478
|
if (output.whereNext.length > 0) {
|
|
94013
|
-
console.log(
|
|
94479
|
+
console.log(import_picocolors9.default.bold("\nNext:"));
|
|
94014
94480
|
for (const a of output.whereNext) console.log(` - ${a}`);
|
|
94015
94481
|
}
|
|
94016
94482
|
} else {
|
|
94017
94483
|
const reason = classifyAIFailure(validationErrors?.[0]);
|
|
94018
|
-
console.error(
|
|
94484
|
+
console.error(import_picocolors9.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
|
|
94485
|
+
}
|
|
94486
|
+
}
|
|
94487
|
+
track({
|
|
94488
|
+
type: "investigation.completed",
|
|
94489
|
+
investigationId: report.id,
|
|
94490
|
+
confidence: typeof report.confidence === "number" ? report.confidence : null,
|
|
94491
|
+
evidenceCount: report.evidence?.length ?? 0,
|
|
94492
|
+
findingCount: report.findings?.length ?? 0,
|
|
94493
|
+
suspectedCauseCount: report.suspectedCauses?.length ?? 0,
|
|
94494
|
+
degraded: Boolean(report.degraded),
|
|
94495
|
+
gapCount: report.gapAnalysis?.gaps?.length ?? 0,
|
|
94496
|
+
hasAi: Boolean(report.aiJudgment)
|
|
94497
|
+
});
|
|
94498
|
+
if (isContentSharingEnabled()) {
|
|
94499
|
+
const hint2 = redactOrDrop(report.input.hint ?? "");
|
|
94500
|
+
if (!hint2.dropped) {
|
|
94501
|
+
track({
|
|
94502
|
+
type: "investigation.content",
|
|
94503
|
+
investigationId: report.id,
|
|
94504
|
+
hint: hint2.value ?? "",
|
|
94505
|
+
summary: redactContent(report.summary ?? ""),
|
|
94506
|
+
findingTitles: (report.findings ?? []).slice(0, 20).map((f) => redactContent(f.title ?? "")),
|
|
94507
|
+
suspectedCauseTitles: (report.suspectedCauses ?? []).slice(0, 20).map((c) => redactContent(c.title ?? "")),
|
|
94508
|
+
confidence: typeof report.confidence === "number" ? report.confidence : null
|
|
94509
|
+
});
|
|
94019
94510
|
}
|
|
94020
94511
|
}
|
|
94021
94512
|
if (cloudActive && cloudSession && cloudCfg) {
|
|
94022
94513
|
try {
|
|
94023
94514
|
const refs = await uploadInvestigationToCloud(cloudSession.client, cloudCfg, report);
|
|
94024
|
-
console.log(
|
|
94515
|
+
console.log(import_picocolors9.default.dim(`[cloud] investigation saved: ${refs.investigationId}`));
|
|
94025
94516
|
} catch (err) {
|
|
94026
94517
|
return reportCloudError(err);
|
|
94027
94518
|
}
|
|
94028
94519
|
}
|
|
94520
|
+
if (format !== "json") {
|
|
94521
|
+
if (report.persisted === false) {
|
|
94522
|
+
console.error(
|
|
94523
|
+
import_picocolors9.default.yellow(`
|
|
94524
|
+
\u26A0 Results were not saved \u2014 the investigation database was unreachable.`)
|
|
94525
|
+
);
|
|
94526
|
+
console.error(
|
|
94527
|
+
import_picocolors9.default.dim(
|
|
94528
|
+
` This report is display-only; \`horus ask\` won't find it. Start the database and re-run to persist.`
|
|
94529
|
+
)
|
|
94530
|
+
);
|
|
94531
|
+
} else {
|
|
94532
|
+
console.log(import_picocolors9.default.dim(`
|
|
94533
|
+
Ask a follow-up: horus ask ${report.id} "<question>"`));
|
|
94534
|
+
}
|
|
94535
|
+
await maybePromptFeedback({
|
|
94536
|
+
investigationId: report.id,
|
|
94537
|
+
horusSeconds: (Date.now() - startedAtMs) / 1e3
|
|
94538
|
+
});
|
|
94539
|
+
}
|
|
94029
94540
|
} finally {
|
|
94030
94541
|
await sql2.end();
|
|
94031
94542
|
if (mongo) await mongo.close();
|
|
@@ -94033,14 +94544,16 @@ async function runInvestigate(hint, opts) {
|
|
|
94033
94544
|
}
|
|
94034
94545
|
return 0;
|
|
94035
94546
|
} catch (err) {
|
|
94036
|
-
|
|
94547
|
+
const msg = err?.message || String(err);
|
|
94548
|
+
console.error(import_picocolors9.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
|
|
94549
|
+
if (process.env.HORUS_DEBUG) console.error(import_picocolors9.default.dim(err?.stack ?? String(err)));
|
|
94037
94550
|
return 1;
|
|
94038
94551
|
}
|
|
94039
94552
|
}
|
|
94040
94553
|
|
|
94041
94554
|
// ../../packages/cli/src/commands/changes.ts
|
|
94042
94555
|
init_cjs_shims();
|
|
94043
|
-
var
|
|
94556
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
94044
94557
|
var CHANGES_AI_CONTRACT = `Provide a clearly separated AI change-impact review with:
|
|
94045
94558
|
|
|
94046
94559
|
Highest-risk changes
|
|
@@ -94065,7 +94578,7 @@ async function runChanges(base2, compare, opts) {
|
|
|
94065
94578
|
const health = await code.health();
|
|
94066
94579
|
if (!health.ok) {
|
|
94067
94580
|
console.error(
|
|
94068
|
-
|
|
94581
|
+
import_picocolors10.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
94069
94582
|
);
|
|
94070
94583
|
return 1;
|
|
94071
94584
|
}
|
|
@@ -94087,20 +94600,20 @@ async function runChanges(base2, compare, opts) {
|
|
|
94087
94600
|
});
|
|
94088
94601
|
console.log("\n" + renderInterpretation(result));
|
|
94089
94602
|
if (!result.ok) {
|
|
94090
|
-
console.error(
|
|
94603
|
+
console.error(import_picocolors10.default.yellow(`[ai] ${result.warning}`));
|
|
94091
94604
|
}
|
|
94092
94605
|
}
|
|
94093
94606
|
}
|
|
94094
94607
|
return 0;
|
|
94095
94608
|
} catch (err) {
|
|
94096
|
-
console.error(
|
|
94609
|
+
console.error(import_picocolors10.default.red(err.message));
|
|
94097
94610
|
return 1;
|
|
94098
94611
|
}
|
|
94099
94612
|
}
|
|
94100
94613
|
|
|
94101
94614
|
// ../../packages/cli/src/commands/timeline.ts
|
|
94102
94615
|
init_cjs_shims();
|
|
94103
|
-
var
|
|
94616
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
94104
94617
|
var DEFAULT_SINCE = "7 days ago";
|
|
94105
94618
|
function resolveTimelineWindow(opts) {
|
|
94106
94619
|
const usingDefault = !opts.all && opts.since === void 0;
|
|
@@ -94130,7 +94643,7 @@ async function runTimeline(service, opts) {
|
|
|
94130
94643
|
try {
|
|
94131
94644
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
94132
94645
|
} catch (err) {
|
|
94133
|
-
console.error(
|
|
94646
|
+
console.error(import_picocolors11.default.red(err.message));
|
|
94134
94647
|
return 1;
|
|
94135
94648
|
}
|
|
94136
94649
|
const { code } = createConnectors(config2);
|
|
@@ -94145,9 +94658,9 @@ async function runTimeline(service, opts) {
|
|
|
94145
94658
|
console.log(renderChangeTimeline(t));
|
|
94146
94659
|
if (usingDefaultWindow) {
|
|
94147
94660
|
console.log(
|
|
94148
|
-
|
|
94661
|
+
import_picocolors11.default.dim(
|
|
94149
94662
|
`
|
|
94150
|
-
Showing the last 7 days (default). Widen with ${
|
|
94663
|
+
Showing the last 7 days (default). Widen with ${import_picocolors11.default.bold('--since "30 days ago"')}, pin a range with ${import_picocolors11.default.bold("--since <when> --until <when>")}, or see everything with ${import_picocolors11.default.bold("--all")}.`
|
|
94151
94664
|
)
|
|
94152
94665
|
);
|
|
94153
94666
|
}
|
|
@@ -94164,20 +94677,20 @@ async function runTimeline(service, opts) {
|
|
|
94164
94677
|
});
|
|
94165
94678
|
console.log("\n" + renderInterpretation(result));
|
|
94166
94679
|
if (!result.ok) {
|
|
94167
|
-
console.error(
|
|
94680
|
+
console.error(import_picocolors11.default.yellow(`[ai] ${result.warning}`));
|
|
94168
94681
|
}
|
|
94169
94682
|
}
|
|
94170
94683
|
}
|
|
94171
94684
|
return 0;
|
|
94172
94685
|
} catch (err) {
|
|
94173
|
-
console.error(
|
|
94686
|
+
console.error(import_picocolors11.default.red(err.message));
|
|
94174
94687
|
return 1;
|
|
94175
94688
|
}
|
|
94176
94689
|
}
|
|
94177
94690
|
|
|
94178
94691
|
// ../../packages/cli/src/commands/what-changed.ts
|
|
94179
94692
|
init_cjs_shims();
|
|
94180
|
-
var
|
|
94693
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
94181
94694
|
var DEFAULT_SINCE2 = "7 days ago";
|
|
94182
94695
|
var WHAT_CHANGED_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
94183
94696
|
|
|
@@ -94201,7 +94714,7 @@ async function runWhatChanged(service, opts) {
|
|
|
94201
94714
|
try {
|
|
94202
94715
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
94203
94716
|
} catch (err) {
|
|
94204
|
-
console.error(
|
|
94717
|
+
console.error(import_picocolors12.default.red(err.message));
|
|
94205
94718
|
return 1;
|
|
94206
94719
|
}
|
|
94207
94720
|
const { code } = createConnectors(config2);
|
|
@@ -94224,19 +94737,19 @@ async function runWhatChanged(service, opts) {
|
|
|
94224
94737
|
});
|
|
94225
94738
|
console.log("\n" + renderInterpretation(result));
|
|
94226
94739
|
if (!result.ok) {
|
|
94227
|
-
console.error(
|
|
94740
|
+
console.error(import_picocolors12.default.yellow(`[ai] ${result.warning}`));
|
|
94228
94741
|
}
|
|
94229
94742
|
}
|
|
94230
94743
|
return 0;
|
|
94231
94744
|
} catch (err) {
|
|
94232
|
-
console.error(
|
|
94745
|
+
console.error(import_picocolors12.default.red(err.message));
|
|
94233
94746
|
return 1;
|
|
94234
94747
|
}
|
|
94235
94748
|
}
|
|
94236
94749
|
|
|
94237
94750
|
// ../../packages/cli/src/commands/architecture.ts
|
|
94238
94751
|
init_cjs_shims();
|
|
94239
|
-
var
|
|
94752
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
94240
94753
|
var ARCHITECTURE_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
94241
94754
|
|
|
94242
94755
|
System summary
|
|
@@ -94265,7 +94778,7 @@ async function runArchitecture(opts) {
|
|
|
94265
94778
|
const health = await code.health();
|
|
94266
94779
|
if (!health.ok) {
|
|
94267
94780
|
console.error(
|
|
94268
|
-
|
|
94781
|
+
import_picocolors13.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
94269
94782
|
);
|
|
94270
94783
|
return 1;
|
|
94271
94784
|
}
|
|
@@ -94293,7 +94806,7 @@ async function runArchitecture(opts) {
|
|
|
94293
94806
|
});
|
|
94294
94807
|
console.log("\n" + renderInterpretation(result));
|
|
94295
94808
|
if (!result.ok) {
|
|
94296
|
-
console.error(
|
|
94809
|
+
console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
|
|
94297
94810
|
}
|
|
94298
94811
|
}
|
|
94299
94812
|
}
|
|
@@ -94302,14 +94815,14 @@ async function runArchitecture(opts) {
|
|
|
94302
94815
|
}
|
|
94303
94816
|
return 0;
|
|
94304
94817
|
} catch (err) {
|
|
94305
|
-
console.error(
|
|
94818
|
+
console.error(import_picocolors13.default.red(err.message));
|
|
94306
94819
|
return 1;
|
|
94307
94820
|
}
|
|
94308
94821
|
}
|
|
94309
94822
|
|
|
94310
94823
|
// ../../packages/cli/src/commands/blast-radius.ts
|
|
94311
94824
|
init_cjs_shims();
|
|
94312
|
-
var
|
|
94825
|
+
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
94313
94826
|
var BLAST_RADIUS_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
94314
94827
|
|
|
94315
94828
|
Evidence used
|
|
@@ -94336,7 +94849,7 @@ async function runBlastRadius(query, opts) {
|
|
|
94336
94849
|
const { code } = createConnectors(config2);
|
|
94337
94850
|
const health = await code.health();
|
|
94338
94851
|
if (!health.ok) {
|
|
94339
|
-
console.error(
|
|
94852
|
+
console.error(import_picocolors14.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
|
|
94340
94853
|
return 1;
|
|
94341
94854
|
}
|
|
94342
94855
|
let project;
|
|
@@ -94349,12 +94862,12 @@ async function runBlastRadius(query, opts) {
|
|
|
94349
94862
|
const r = await analyzeBlastRadius(query, { code, db, project }, opts.depth ?? 3);
|
|
94350
94863
|
if (!r) {
|
|
94351
94864
|
console.log(`No symbol found for: ${query}`);
|
|
94352
|
-
console.log(
|
|
94865
|
+
console.log(import_picocolors14.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
|
|
94353
94866
|
return 1;
|
|
94354
94867
|
}
|
|
94355
94868
|
if (r.seed.name.toLowerCase() !== query.toLowerCase()) {
|
|
94356
94869
|
console.log(
|
|
94357
|
-
|
|
94870
|
+
import_picocolors14.default.yellow(` No exact match for "${query}"`) + import_picocolors14.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
|
|
94358
94871
|
);
|
|
94359
94872
|
}
|
|
94360
94873
|
if (opts.json) {
|
|
@@ -94374,7 +94887,7 @@ async function runBlastRadius(query, opts) {
|
|
|
94374
94887
|
});
|
|
94375
94888
|
console.log("\n" + renderInterpretation(result));
|
|
94376
94889
|
if (!result.ok) {
|
|
94377
|
-
console.error(
|
|
94890
|
+
console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
|
|
94378
94891
|
}
|
|
94379
94892
|
}
|
|
94380
94893
|
}
|
|
@@ -94383,28 +94896,28 @@ async function runBlastRadius(query, opts) {
|
|
|
94383
94896
|
}
|
|
94384
94897
|
return 0;
|
|
94385
94898
|
} catch (err) {
|
|
94386
|
-
console.error(
|
|
94899
|
+
console.error(import_picocolors14.default.red(err.message));
|
|
94387
94900
|
return 1;
|
|
94388
94901
|
}
|
|
94389
94902
|
}
|
|
94390
94903
|
|
|
94391
94904
|
// ../../packages/cli/src/commands/repos.ts
|
|
94392
94905
|
init_cjs_shims();
|
|
94393
|
-
var
|
|
94906
|
+
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
94394
94907
|
async function runRepos(opts) {
|
|
94395
94908
|
const config2 = await loadConfig(opts.config);
|
|
94396
94909
|
const providers = repoProviders(config2);
|
|
94397
94910
|
if (providers.length === 0) {
|
|
94398
|
-
console.log(
|
|
94911
|
+
console.log(import_picocolors15.default.dim("No repositories configured."));
|
|
94399
94912
|
return 0;
|
|
94400
94913
|
}
|
|
94401
94914
|
const health = await reposHealth(providers);
|
|
94402
94915
|
for (const h of health) {
|
|
94403
|
-
const dot = h.reachable ?
|
|
94404
|
-
const name =
|
|
94405
|
-
const hostUrl =
|
|
94406
|
-
const path =
|
|
94407
|
-
const detail = h.detail ?
|
|
94916
|
+
const dot = h.reachable ? import_picocolors15.default.green("\u25CF") : import_picocolors15.default.red("\u25CF");
|
|
94917
|
+
const name = import_picocolors15.default.bold(h.repo.padEnd(20));
|
|
94918
|
+
const hostUrl = import_picocolors15.default.cyan(h.hostUrl.padEnd(30));
|
|
94919
|
+
const path = import_picocolors15.default.dim(h.path);
|
|
94920
|
+
const detail = h.detail ? import_picocolors15.default.dim(`(${h.detail})`) : "";
|
|
94408
94921
|
console.log(` ${dot} ${name} ${hostUrl} ${path} ${detail}`);
|
|
94409
94922
|
}
|
|
94410
94923
|
return 0;
|
|
@@ -94412,7 +94925,7 @@ async function runRepos(opts) {
|
|
|
94412
94925
|
|
|
94413
94926
|
// ../../packages/cli/src/commands/search.ts
|
|
94414
94927
|
init_cjs_shims();
|
|
94415
|
-
var
|
|
94928
|
+
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
94416
94929
|
async function runSearch(query, opts) {
|
|
94417
94930
|
const config2 = await loadConfig(opts.config);
|
|
94418
94931
|
const providers = repoProviders(config2);
|
|
@@ -94423,16 +94936,16 @@ async function runSearch(query, opts) {
|
|
|
94423
94936
|
}
|
|
94424
94937
|
let totalMatches = 0;
|
|
94425
94938
|
for (const result of results) {
|
|
94426
|
-
const statusDot = result.reachable ?
|
|
94939
|
+
const statusDot = result.reachable ? import_picocolors16.default.green("\u25CF") : import_picocolors16.default.red("\u25CF");
|
|
94427
94940
|
console.log("");
|
|
94428
|
-
console.log(`${statusDot} ${
|
|
94941
|
+
console.log(`${statusDot} ${import_picocolors16.default.bold("## " + result.repo)} ${import_picocolors16.default.dim(result.hostUrl)}`);
|
|
94429
94942
|
if (!result.reachable) {
|
|
94430
|
-
console.log(
|
|
94943
|
+
console.log(import_picocolors16.default.dim(" (unreachable)"));
|
|
94431
94944
|
} else if (result.symbols.length === 0) {
|
|
94432
|
-
console.log(
|
|
94945
|
+
console.log(import_picocolors16.default.dim(" (no matches)"));
|
|
94433
94946
|
} else {
|
|
94434
94947
|
for (const sym of result.symbols) {
|
|
94435
|
-
console.log(` - ${
|
|
94948
|
+
console.log(` - ${import_picocolors16.default.bold(sym.name)} ${import_picocolors16.default.dim(sym.filePath)}`);
|
|
94436
94949
|
}
|
|
94437
94950
|
totalMatches += result.symbols.length;
|
|
94438
94951
|
}
|
|
@@ -94440,7 +94953,7 @@ async function runSearch(query, opts) {
|
|
|
94440
94953
|
console.log("");
|
|
94441
94954
|
const reachableCount = results.filter((r) => r.reachable).length;
|
|
94442
94955
|
console.log(
|
|
94443
|
-
|
|
94956
|
+
import_picocolors16.default.dim(
|
|
94444
94957
|
`${totalMatches} match(es) across ${reachableCount}/${results.length} reachable repo(s) for query: "${query}"`
|
|
94445
94958
|
)
|
|
94446
94959
|
);
|
|
@@ -94449,7 +94962,7 @@ async function runSearch(query, opts) {
|
|
|
94449
94962
|
|
|
94450
94963
|
// ../../packages/cli/src/commands/investigations.ts
|
|
94451
94964
|
init_cjs_shims();
|
|
94452
|
-
var
|
|
94965
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
94453
94966
|
|
|
94454
94967
|
// ../../packages/cli/src/lib/format.ts
|
|
94455
94968
|
init_cjs_shims();
|
|
@@ -94492,8 +95005,8 @@ async function runInvestigations(opts) {
|
|
|
94492
95005
|
const session = authedClient();
|
|
94493
95006
|
if (!session) {
|
|
94494
95007
|
console.error(
|
|
94495
|
-
|
|
94496
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
95008
|
+
import_picocolors17.default.red(
|
|
95009
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors17.default.bold("horus login")} first.`
|
|
94497
95010
|
)
|
|
94498
95011
|
);
|
|
94499
95012
|
return 1;
|
|
@@ -94534,18 +95047,18 @@ async function runInvestigations(opts) {
|
|
|
94534
95047
|
|
|
94535
95048
|
// ../../packages/cli/src/commands/replay.ts
|
|
94536
95049
|
init_cjs_shims();
|
|
94537
|
-
var
|
|
95050
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
94538
95051
|
async function runReplay(id, opts) {
|
|
94539
95052
|
const { db, sql: sql2 } = createDb(await resolveDbUrl(opts.config));
|
|
94540
95053
|
try {
|
|
94541
95054
|
const row = await getInvestigation(db, id);
|
|
94542
95055
|
if (!row) {
|
|
94543
|
-
console.error(
|
|
95056
|
+
console.error(import_picocolors18.default.red("No investigation found: " + id));
|
|
94544
95057
|
return 1;
|
|
94545
95058
|
}
|
|
94546
95059
|
if (!row.report) {
|
|
94547
95060
|
console.error(
|
|
94548
|
-
|
|
95061
|
+
import_picocolors18.default.red(
|
|
94549
95062
|
"Investigation " + id + " has no stored report (run a newer investigation)."
|
|
94550
95063
|
)
|
|
94551
95064
|
);
|
|
@@ -94558,7 +95071,7 @@ async function runReplay(id, opts) {
|
|
|
94558
95071
|
if (opts.ai && fmt !== "json") {
|
|
94559
95072
|
if (report.aiJudgment && !opts.refreshAi) {
|
|
94560
95073
|
renderStoredAIJudgment(report.aiJudgment);
|
|
94561
|
-
console.log(
|
|
95074
|
+
console.log(import_picocolors18.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
|
|
94562
95075
|
} else {
|
|
94563
95076
|
const narrativeInput = buildNarrativeInput(report);
|
|
94564
95077
|
const { provider } = await buildNarrativeProvider({
|
|
@@ -94571,17 +95084,17 @@ async function runReplay(id, opts) {
|
|
|
94571
95084
|
);
|
|
94572
95085
|
if (!fromProvider && degraded) {
|
|
94573
95086
|
if (validationErrors?.length) {
|
|
94574
|
-
console.error(
|
|
95087
|
+
console.error(import_picocolors18.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
|
|
94575
95088
|
}
|
|
94576
95089
|
console.log("");
|
|
94577
|
-
console.log(
|
|
94578
|
-
console.log(
|
|
94579
|
-
if (output.what?.trim()) console.log(
|
|
94580
|
-
if (output.why?.trim()) console.log(
|
|
95090
|
+
console.log(import_picocolors18.default.dim("\u2500".repeat(60)));
|
|
95091
|
+
console.log(import_picocolors18.default.bold("AI narrative ") + import_picocolors18.default.yellow("(unstructured \u2014 not validated)"));
|
|
95092
|
+
if (output.what?.trim()) console.log(import_picocolors18.default.bold("\nWhat:"), output.what.trim());
|
|
95093
|
+
if (output.why?.trim()) console.log(import_picocolors18.default.bold("\nWhy:"), output.why.trim());
|
|
94581
95094
|
} else if (!fromProvider) {
|
|
94582
|
-
console.error(
|
|
95095
|
+
console.error(import_picocolors18.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
|
|
94583
95096
|
if (validationErrors?.length) {
|
|
94584
|
-
console.error(
|
|
95097
|
+
console.error(import_picocolors18.default.dim(` ${validationErrors[0]}`));
|
|
94585
95098
|
}
|
|
94586
95099
|
} else {
|
|
94587
95100
|
const stored = narrativeOutputToStoredJudgment(output, "anthropic");
|
|
@@ -94602,7 +95115,7 @@ async function runReplay(id, opts) {
|
|
|
94602
95115
|
|
|
94603
95116
|
// ../../packages/cli/src/commands/owner.ts
|
|
94604
95117
|
init_cjs_shims();
|
|
94605
|
-
var
|
|
95118
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
94606
95119
|
async function runOwner(query, opts) {
|
|
94607
95120
|
try {
|
|
94608
95121
|
const config2 = await loadConfig(opts.config);
|
|
@@ -94610,7 +95123,7 @@ async function runOwner(query, opts) {
|
|
|
94610
95123
|
try {
|
|
94611
95124
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
94612
95125
|
} catch (err) {
|
|
94613
|
-
console.error(
|
|
95126
|
+
console.error(import_picocolors19.default.red(err.message));
|
|
94614
95127
|
return 1;
|
|
94615
95128
|
}
|
|
94616
95129
|
const code = codeForRepo(config2, renv.project);
|
|
@@ -94618,16 +95131,16 @@ async function runOwner(query, opts) {
|
|
|
94618
95131
|
console.log(opts.json ? ownershipToJSON(o) : renderOwnership(o));
|
|
94619
95132
|
return 0;
|
|
94620
95133
|
} catch (err) {
|
|
94621
|
-
console.error(
|
|
95134
|
+
console.error(import_picocolors19.default.red(err.message));
|
|
94622
95135
|
return 1;
|
|
94623
95136
|
}
|
|
94624
95137
|
}
|
|
94625
95138
|
|
|
94626
95139
|
// ../../packages/cli/src/commands/postmortem.ts
|
|
94627
95140
|
init_cjs_shims();
|
|
94628
|
-
var
|
|
94629
|
-
var
|
|
94630
|
-
var
|
|
95141
|
+
var import_node_fs15 = require("fs");
|
|
95142
|
+
var import_node_path16 = require("path");
|
|
95143
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
94631
95144
|
async function runPostmortem(id, opts) {
|
|
94632
95145
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
94633
95146
|
let sql2;
|
|
@@ -94641,11 +95154,11 @@ async function runPostmortem(id, opts) {
|
|
|
94641
95154
|
const { db } = conn;
|
|
94642
95155
|
const row = await getInvestigation(db, id);
|
|
94643
95156
|
if (!row) {
|
|
94644
|
-
log(
|
|
95157
|
+
log(import_picocolors20.default.red("No investigation found: " + id));
|
|
94645
95158
|
return 1;
|
|
94646
95159
|
}
|
|
94647
95160
|
if (!row.report) {
|
|
94648
|
-
log(
|
|
95161
|
+
log(import_picocolors20.default.red("Investigation " + id + " has no stored report."));
|
|
94649
95162
|
return 1;
|
|
94650
95163
|
}
|
|
94651
95164
|
report = migrateReport(row.report);
|
|
@@ -94737,25 +95250,25 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
94737
95250
|
}
|
|
94738
95251
|
}
|
|
94739
95252
|
if (opts.output) {
|
|
94740
|
-
const outputPath = (0,
|
|
94741
|
-
if ((0,
|
|
94742
|
-
log(
|
|
94743
|
-
log(
|
|
95253
|
+
const outputPath = (0, import_node_path16.resolve)(opts.output);
|
|
95254
|
+
if ((0, import_node_fs15.existsSync)(outputPath) && !opts.force) {
|
|
95255
|
+
log(import_picocolors20.default.red("\u2717") + " " + outputPath + " already exists");
|
|
95256
|
+
log(import_picocolors20.default.dim(" pass --force to overwrite"));
|
|
94744
95257
|
return 1;
|
|
94745
95258
|
}
|
|
94746
95259
|
try {
|
|
94747
|
-
(0,
|
|
95260
|
+
(0, import_node_fs15.writeFileSync)(outputPath, content, "utf8");
|
|
94748
95261
|
} catch (err) {
|
|
94749
|
-
log(
|
|
95262
|
+
log(import_picocolors20.default.red("\u2717 Could not write " + outputPath + ": ") + err.message);
|
|
94750
95263
|
return 1;
|
|
94751
95264
|
}
|
|
94752
|
-
log(
|
|
95265
|
+
log(import_picocolors20.default.green("\u2713") + " Saved postmortem to " + outputPath);
|
|
94753
95266
|
} else {
|
|
94754
95267
|
log(content);
|
|
94755
95268
|
}
|
|
94756
95269
|
} catch (err) {
|
|
94757
95270
|
const message = err instanceof Error ? err.message : String(err);
|
|
94758
|
-
log(
|
|
95271
|
+
log(import_picocolors20.default.red("postmortem failed: " + message));
|
|
94759
95272
|
return 1;
|
|
94760
95273
|
} finally {
|
|
94761
95274
|
if (sql2) {
|
|
@@ -94767,7 +95280,7 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
94767
95280
|
|
|
94768
95281
|
// ../../packages/cli/src/commands/score.ts
|
|
94769
95282
|
init_cjs_shims();
|
|
94770
|
-
var
|
|
95283
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
94771
95284
|
var SCORE_AI_CONTRACT = `Provide a clearly separated AI score explanation with:
|
|
94772
95285
|
|
|
94773
95286
|
Why this scored this way
|
|
@@ -94786,11 +95299,11 @@ async function runScore(id, opts) {
|
|
|
94786
95299
|
try {
|
|
94787
95300
|
const row = await getInvestigation(db, id);
|
|
94788
95301
|
if (!row) {
|
|
94789
|
-
console.error(
|
|
95302
|
+
console.error(import_picocolors21.default.red("No investigation found: " + id));
|
|
94790
95303
|
return 1;
|
|
94791
95304
|
}
|
|
94792
95305
|
if (!row.report) {
|
|
94793
|
-
console.error(
|
|
95306
|
+
console.error(import_picocolors21.default.red("Investigation " + id + " has no stored report."));
|
|
94794
95307
|
return 1;
|
|
94795
95308
|
}
|
|
94796
95309
|
const s = scoreInvestigation(migrateReport(row.report));
|
|
@@ -94811,7 +95324,7 @@ async function runScore(id, opts) {
|
|
|
94811
95324
|
});
|
|
94812
95325
|
console.log("\n" + renderInterpretation(result));
|
|
94813
95326
|
if (!result.ok) {
|
|
94814
|
-
console.error(
|
|
95327
|
+
console.error(import_picocolors21.default.yellow(`[ai] ${result.warning}`));
|
|
94815
95328
|
}
|
|
94816
95329
|
}
|
|
94817
95330
|
}
|
|
@@ -94851,7 +95364,7 @@ async function runScores(opts) {
|
|
|
94851
95364
|
|
|
94852
95365
|
// ../../packages/cli/src/commands/ask.ts
|
|
94853
95366
|
init_cjs_shims();
|
|
94854
|
-
var
|
|
95367
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
94855
95368
|
function renderAnswer(report, directive, opts) {
|
|
94856
95369
|
const answer = answerQuestion(report, directive);
|
|
94857
95370
|
if (answer) {
|
|
@@ -94863,78 +95376,88 @@ function renderAnswer(report, directive, opts) {
|
|
|
94863
95376
|
if (!opts.json && report.aiJudgment) {
|
|
94864
95377
|
const j = report.aiJudgment;
|
|
94865
95378
|
console.log("");
|
|
94866
|
-
console.log(
|
|
94867
|
-
console.log(
|
|
95379
|
+
console.log(import_picocolors22.default.dim("\u2500".repeat(60)));
|
|
95380
|
+
console.log(import_picocolors22.default.dim(`Stored AI judgment (${j.provider}, ${j.generatedAt}):`));
|
|
94868
95381
|
if (j.rootCauseAssessment) {
|
|
94869
|
-
console.log(
|
|
94870
|
-
console.log(
|
|
95382
|
+
console.log(import_picocolors22.default.bold("Root cause (AI):"), j.rootCauseAssessment.summary);
|
|
95383
|
+
console.log(import_picocolors22.default.dim(`Uncertainty: ${j.rootCauseAssessment.uncertainty}`));
|
|
94871
95384
|
} else {
|
|
94872
|
-
console.log(
|
|
95385
|
+
console.log(import_picocolors22.default.bold("AI Why:"), j.why);
|
|
94873
95386
|
}
|
|
94874
95387
|
}
|
|
94875
95388
|
return 0;
|
|
94876
95389
|
}
|
|
95390
|
+
async function lookupLocalInvestigation(id, configPath) {
|
|
95391
|
+
const { db, sql: sql2 } = createDb(await resolveDbUrl(configPath));
|
|
95392
|
+
try {
|
|
95393
|
+
const row = await getInvestigation(db, id);
|
|
95394
|
+
if (!row) return { kind: "not-found" };
|
|
95395
|
+
if (!row.report) return { kind: "no-report" };
|
|
95396
|
+
return { kind: "found", report: migrateReport(row.report) };
|
|
95397
|
+
} catch (err) {
|
|
95398
|
+
const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
95399
|
+
if (code === "22P02") return { kind: "not-found" };
|
|
95400
|
+
return { kind: "error", message: err instanceof Error ? err.message : String(err) };
|
|
95401
|
+
} finally {
|
|
95402
|
+
await sql2.end();
|
|
95403
|
+
}
|
|
95404
|
+
}
|
|
94877
95405
|
async function runAsk(id, directive, opts) {
|
|
94878
95406
|
const repoRoot = repoRootOrCwd();
|
|
94879
95407
|
const cloudCfg = readCloudConfig(repoRoot);
|
|
94880
95408
|
if (isCloudActive(cloudCfg)) {
|
|
95409
|
+
const local2 = await lookupLocalInvestigation(id, opts.config);
|
|
95410
|
+
if (local2.kind === "found") return renderAnswer(local2.report, directive, opts);
|
|
94881
95411
|
const session = authedClient();
|
|
94882
95412
|
if (!session) {
|
|
94883
95413
|
console.error(
|
|
94884
|
-
|
|
94885
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
95414
|
+
import_picocolors22.default.red(
|
|
95415
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors22.default.bold("horus login")} first.`
|
|
94886
95416
|
)
|
|
94887
95417
|
);
|
|
94888
95418
|
return 1;
|
|
94889
95419
|
}
|
|
94890
95420
|
try {
|
|
94891
|
-
const report = await fetchInvestigationReportFromCloud(
|
|
94892
|
-
|
|
94893
|
-
|
|
94894
|
-
|
|
95421
|
+
const report = await fetchInvestigationReportFromCloud(session.client, cloudCfg, id);
|
|
95422
|
+
if (report) return renderAnswer(report, directive, opts);
|
|
95423
|
+
console.error(
|
|
95424
|
+
import_picocolors22.default.red(
|
|
95425
|
+
`Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors22.default.bold("horus investigate")} first.`
|
|
95426
|
+
)
|
|
94895
95427
|
);
|
|
94896
|
-
|
|
95428
|
+
return 1;
|
|
95429
|
+
} catch (err) {
|
|
95430
|
+
if (err instanceof CloudError && err.status === 404) {
|
|
94897
95431
|
console.error(
|
|
94898
|
-
|
|
94899
|
-
`
|
|
95432
|
+
import_picocolors22.default.red(
|
|
95433
|
+
`No investigation found for "${id}" locally or in Horus Cloud. Use the id printed at the top of \`horus investigate\` output.`
|
|
94900
95434
|
)
|
|
94901
95435
|
);
|
|
94902
95436
|
return 1;
|
|
94903
95437
|
}
|
|
94904
|
-
return renderAnswer(report, directive, opts);
|
|
94905
|
-
} catch (err) {
|
|
94906
95438
|
return reportCloudError(err);
|
|
94907
95439
|
}
|
|
94908
95440
|
}
|
|
94909
|
-
const
|
|
94910
|
-
|
|
94911
|
-
|
|
94912
|
-
|
|
94913
|
-
|
|
95441
|
+
const local = await lookupLocalInvestigation(id, opts.config);
|
|
95442
|
+
switch (local.kind) {
|
|
95443
|
+
case "found":
|
|
95444
|
+
return renderAnswer(local.report, directive, opts);
|
|
95445
|
+
case "no-report":
|
|
95446
|
+
console.error(import_picocolors22.default.red("Investigation " + id + " has no stored report."));
|
|
94914
95447
|
return 1;
|
|
94915
|
-
|
|
94916
|
-
|
|
94917
|
-
console.error(import_picocolors21.default.red("Investigation " + id + " has no stored report."));
|
|
95448
|
+
case "error":
|
|
95449
|
+
console.error(import_picocolors22.default.red(local.message));
|
|
94918
95450
|
return 1;
|
|
94919
|
-
|
|
94920
|
-
|
|
94921
|
-
|
|
94922
|
-
} catch (err) {
|
|
94923
|
-
const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
|
|
94924
|
-
if (code === "22P02") {
|
|
94925
|
-
console.error(import_picocolors21.default.red("No investigation found: " + id));
|
|
95451
|
+
case "not-found":
|
|
95452
|
+
default:
|
|
95453
|
+
console.error(import_picocolors22.default.red("No investigation found: " + id));
|
|
94926
95454
|
return 1;
|
|
94927
|
-
}
|
|
94928
|
-
console.error(import_picocolors21.default.red(err instanceof Error ? err.message : String(err)));
|
|
94929
|
-
return 1;
|
|
94930
|
-
} finally {
|
|
94931
|
-
await sql2.end();
|
|
94932
95455
|
}
|
|
94933
95456
|
}
|
|
94934
95457
|
|
|
94935
95458
|
// ../../packages/cli/src/commands/onboard.ts
|
|
94936
95459
|
init_cjs_shims();
|
|
94937
|
-
var
|
|
95460
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
94938
95461
|
var ONBOARD_AI_CONTRACT = `Provide a clearly separated AI onboarding guide with:
|
|
94939
95462
|
|
|
94940
95463
|
Start here
|
|
@@ -94962,7 +95485,7 @@ async function runOnboard(area, opts) {
|
|
|
94962
95485
|
try {
|
|
94963
95486
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
94964
95487
|
} catch (err) {
|
|
94965
|
-
console.error(
|
|
95488
|
+
console.error(import_picocolors23.default.red(err.message));
|
|
94966
95489
|
return 1;
|
|
94967
95490
|
}
|
|
94968
95491
|
const repo = { name: renv.project, path: renv.path };
|
|
@@ -94970,7 +95493,7 @@ async function runOnboard(area, opts) {
|
|
|
94970
95493
|
const health = await code.health();
|
|
94971
95494
|
if (!health.ok) {
|
|
94972
95495
|
console.error(
|
|
94973
|
-
|
|
95496
|
+
import_picocolors23.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
94974
95497
|
);
|
|
94975
95498
|
return 1;
|
|
94976
95499
|
}
|
|
@@ -94997,7 +95520,7 @@ async function runOnboard(area, opts) {
|
|
|
94997
95520
|
});
|
|
94998
95521
|
console.log("\n" + renderInterpretation(result));
|
|
94999
95522
|
if (!result.ok) {
|
|
95000
|
-
console.error(
|
|
95523
|
+
console.error(import_picocolors23.default.yellow(`[ai] ${result.warning}`));
|
|
95001
95524
|
}
|
|
95002
95525
|
}
|
|
95003
95526
|
}
|
|
@@ -95006,14 +95529,14 @@ async function runOnboard(area, opts) {
|
|
|
95006
95529
|
}
|
|
95007
95530
|
return 0;
|
|
95008
95531
|
} catch (err) {
|
|
95009
|
-
console.error(
|
|
95532
|
+
console.error(import_picocolors23.default.red(err.message));
|
|
95010
95533
|
return 1;
|
|
95011
95534
|
}
|
|
95012
95535
|
}
|
|
95013
95536
|
|
|
95014
95537
|
// ../../packages/cli/src/commands/simulate.ts
|
|
95015
95538
|
init_cjs_shims();
|
|
95016
|
-
var
|
|
95539
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
95017
95540
|
async function runSimulate(scenarioId, opts) {
|
|
95018
95541
|
try {
|
|
95019
95542
|
if (scenarioId === void 0) {
|
|
@@ -95023,7 +95546,7 @@ async function runSimulate(scenarioId, opts) {
|
|
|
95023
95546
|
const scenario = getScenario(scenarioId);
|
|
95024
95547
|
if (scenario === null) {
|
|
95025
95548
|
console.error(
|
|
95026
|
-
|
|
95549
|
+
import_picocolors24.default.red(
|
|
95027
95550
|
`Unknown scenario: "${scenarioId}". Run horus simulate to list available scenarios.`
|
|
95028
95551
|
)
|
|
95029
95552
|
);
|
|
@@ -95034,14 +95557,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
95034
95557
|
try {
|
|
95035
95558
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
95036
95559
|
} catch (err) {
|
|
95037
|
-
console.error(
|
|
95560
|
+
console.error(import_picocolors24.default.red(err.message));
|
|
95038
95561
|
return 1;
|
|
95039
95562
|
}
|
|
95040
95563
|
const code = codeForRepo(config2, renv.project);
|
|
95041
95564
|
const health = await code.health();
|
|
95042
95565
|
if (!health.ok) {
|
|
95043
95566
|
console.error(
|
|
95044
|
-
|
|
95567
|
+
import_picocolors24.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
95045
95568
|
);
|
|
95046
95569
|
return 1;
|
|
95047
95570
|
}
|
|
@@ -95058,14 +95581,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
95058
95581
|
}
|
|
95059
95582
|
return 0;
|
|
95060
95583
|
} catch (err) {
|
|
95061
|
-
console.error(
|
|
95584
|
+
console.error(import_picocolors24.default.red(err.message));
|
|
95062
95585
|
return 1;
|
|
95063
95586
|
}
|
|
95064
95587
|
}
|
|
95065
95588
|
|
|
95066
95589
|
// ../../packages/cli/src/commands/logs.ts
|
|
95067
95590
|
init_cjs_shims();
|
|
95068
|
-
var
|
|
95591
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
95069
95592
|
var LOGS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
95070
95593
|
|
|
95071
95594
|
Evidence used
|
|
@@ -95098,9 +95621,17 @@ function resolveRawLevel(opts) {
|
|
|
95098
95621
|
return opts.level ?? "error";
|
|
95099
95622
|
}
|
|
95100
95623
|
function levelColor(level, text2) {
|
|
95101
|
-
if (level === "error" || level === "fatal") return
|
|
95102
|
-
if (level === "warn") return
|
|
95103
|
-
return
|
|
95624
|
+
if (level === "error" || level === "fatal") return import_picocolors25.default.red(text2);
|
|
95625
|
+
if (level === "warn") return import_picocolors25.default.yellow(text2);
|
|
95626
|
+
return import_picocolors25.default.dim(text2);
|
|
95627
|
+
}
|
|
95628
|
+
function matchServiceLabel(requested, candidates) {
|
|
95629
|
+
if (candidates.includes(requested)) return { kind: "unique", matched: requested };
|
|
95630
|
+
const needle = requested.toLowerCase();
|
|
95631
|
+
const subs = candidates.filter((c) => c.toLowerCase().includes(needle));
|
|
95632
|
+
if (subs.length === 1 && subs[0] !== void 0) return { kind: "unique", matched: subs[0] };
|
|
95633
|
+
if (subs.length > 1) return { kind: "ambiguous", candidates: subs };
|
|
95634
|
+
return { kind: "none" };
|
|
95104
95635
|
}
|
|
95105
95636
|
async function runLogs(service, opts) {
|
|
95106
95637
|
try {
|
|
@@ -95109,13 +95640,13 @@ async function runLogs(service, opts) {
|
|
|
95109
95640
|
try {
|
|
95110
95641
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
95111
95642
|
} catch (err) {
|
|
95112
|
-
console.error(
|
|
95643
|
+
console.error(import_picocolors25.default.red(err.message));
|
|
95113
95644
|
return 1;
|
|
95114
95645
|
}
|
|
95115
95646
|
const logs = logsForEnv(renv);
|
|
95116
95647
|
if (logs === null) {
|
|
95117
95648
|
console.error(
|
|
95118
|
-
|
|
95649
|
+
import_picocolors25.default.red(
|
|
95119
95650
|
`No Elasticsearch connector configured for ${renv.project}/${renv.env} (set ES_URL / ES_USERNAME / ES_PASSWORD)`
|
|
95120
95651
|
)
|
|
95121
95652
|
);
|
|
@@ -95123,7 +95654,7 @@ async function runLogs(service, opts) {
|
|
|
95123
95654
|
}
|
|
95124
95655
|
const health = await logs.health();
|
|
95125
95656
|
if (!health.ok) {
|
|
95126
|
-
console.error(
|
|
95657
|
+
console.error(import_picocolors25.default.red(`Elasticsearch unreachable: ${health.detail}`));
|
|
95127
95658
|
return 1;
|
|
95128
95659
|
}
|
|
95129
95660
|
const resolvedService = service ?? renv.connectors.elasticsearch?.serviceName;
|
|
@@ -95136,21 +95667,21 @@ async function runLogs(service, opts) {
|
|
|
95136
95667
|
requiresEventCode: aggregatesEventCode
|
|
95137
95668
|
});
|
|
95138
95669
|
for (const w of compat.issues.filter((i) => i.severity === "warning")) {
|
|
95139
|
-
console.warn(
|
|
95670
|
+
console.warn(import_picocolors25.default.yellow(`[warn] ${w.field}: ${w.message}`));
|
|
95140
95671
|
}
|
|
95141
95672
|
const errors2 = compat.issues.filter((i) => i.severity === "error");
|
|
95142
95673
|
if (errors2.length > 0) {
|
|
95143
|
-
console.error(
|
|
95674
|
+
console.error(import_picocolors25.default.red("Elasticsearch field mapping is incompatible with the index:"));
|
|
95144
95675
|
for (const e of errors2) {
|
|
95145
|
-
console.error(
|
|
95676
|
+
console.error(import_picocolors25.default.red(` ${e.field}: ${e.message}`));
|
|
95146
95677
|
}
|
|
95147
95678
|
console.error(
|
|
95148
|
-
|
|
95679
|
+
import_picocolors25.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
|
|
95149
95680
|
);
|
|
95150
95681
|
return 1;
|
|
95151
95682
|
}
|
|
95152
95683
|
} catch {
|
|
95153
|
-
console.warn(
|
|
95684
|
+
console.warn(import_picocolors25.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
|
|
95154
95685
|
}
|
|
95155
95686
|
const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
|
|
95156
95687
|
const fromDisplay = from.slice(0, 16).replace("T", " ");
|
|
@@ -95201,7 +95732,7 @@ async function runLogs(service, opts) {
|
|
|
95201
95732
|
return 0;
|
|
95202
95733
|
}
|
|
95203
95734
|
if (records.length === 0) {
|
|
95204
|
-
console.log(
|
|
95735
|
+
console.log(import_picocolors25.default.dim("No logs matched."));
|
|
95205
95736
|
return 0;
|
|
95206
95737
|
}
|
|
95207
95738
|
for (const r of records) {
|
|
@@ -95211,7 +95742,7 @@ async function runLogs(service, opts) {
|
|
|
95211
95742
|
const line2 = `${ts} ${lvl} ${comp} ${r.message.slice(0, 120)}`;
|
|
95212
95743
|
console.log(levelColor(r.level, line2));
|
|
95213
95744
|
for (const { key, value } of extractContextFields(r)) {
|
|
95214
|
-
console.log(
|
|
95745
|
+
console.log(import_picocolors25.default.dim(` ${key}: ${value.slice(0, 200)}`));
|
|
95215
95746
|
}
|
|
95216
95747
|
}
|
|
95217
95748
|
return 0;
|
|
@@ -95250,22 +95781,22 @@ async function runLogs(service, opts) {
|
|
|
95250
95781
|
return 0;
|
|
95251
95782
|
}
|
|
95252
95783
|
console.log(
|
|
95253
|
-
|
|
95784
|
+
import_picocolors25.default.bold(`Top ${field}`) + import_picocolors25.default.dim(
|
|
95254
95785
|
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
95255
95786
|
)
|
|
95256
95787
|
);
|
|
95257
|
-
console.log(
|
|
95788
|
+
console.log(import_picocolors25.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
95258
95789
|
console.log("");
|
|
95259
95790
|
if (buckets.length === 0) {
|
|
95260
|
-
console.log(
|
|
95261
|
-
console.log(
|
|
95791
|
+
console.log(import_picocolors25.default.dim(` No error logs with field "${field}" in the window.`));
|
|
95792
|
+
console.log(import_picocolors25.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
|
|
95262
95793
|
return 0;
|
|
95263
95794
|
}
|
|
95264
95795
|
for (const b2 of buckets) {
|
|
95265
|
-
console.log(` ${
|
|
95796
|
+
console.log(` ${import_picocolors25.default.red(String(b2.count).padStart(6))} ${b2.key}`);
|
|
95266
95797
|
}
|
|
95267
95798
|
console.log("");
|
|
95268
|
-
console.log(
|
|
95799
|
+
console.log(import_picocolors25.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
|
|
95269
95800
|
return 0;
|
|
95270
95801
|
}
|
|
95271
95802
|
let analysis = await logs.analyzeErrors({
|
|
@@ -95279,9 +95810,32 @@ async function runLogs(service, opts) {
|
|
|
95279
95810
|
if (analysis.signatures.length === 0 && resolvedService !== void 0) {
|
|
95280
95811
|
const broader = await logs.analyzeErrors({ from, text: opts.grep, broadText: opts.grep !== void 0 });
|
|
95281
95812
|
if (broader.signatures.length > 0) {
|
|
95282
|
-
|
|
95283
|
-
|
|
95284
|
-
|
|
95813
|
+
const match = matchServiceLabel(resolvedService, broader.affectedServices);
|
|
95814
|
+
if (match.kind === "unique" && match.matched !== void 0 && match.matched !== resolvedService) {
|
|
95815
|
+
const scoped = await logs.analyzeErrors({
|
|
95816
|
+
service: match.matched,
|
|
95817
|
+
from,
|
|
95818
|
+
text: opts.grep,
|
|
95819
|
+
broadText: opts.grep !== void 0
|
|
95820
|
+
});
|
|
95821
|
+
if (scoped.signatures.length > 0) {
|
|
95822
|
+
analysis = scoped;
|
|
95823
|
+
scopeService = match.matched;
|
|
95824
|
+
broadeningNote = `No exact match for service "${resolvedService}" \u2014 matched "${match.matched}"`;
|
|
95825
|
+
} else {
|
|
95826
|
+
analysis = broader;
|
|
95827
|
+
scopeService = void 0;
|
|
95828
|
+
broadeningNote = `No errors found for service "${resolvedService}" \u2014 showing all services`;
|
|
95829
|
+
}
|
|
95830
|
+
} else if (match.kind === "ambiguous" && match.candidates !== void 0) {
|
|
95831
|
+
analysis = broader;
|
|
95832
|
+
scopeService = void 0;
|
|
95833
|
+
broadeningNote = `"${resolvedService}" matches multiple services: ${match.candidates.join(", ")} \u2014 showing all; re-run with a full label`;
|
|
95834
|
+
} else {
|
|
95835
|
+
analysis = broader;
|
|
95836
|
+
scopeService = void 0;
|
|
95837
|
+
broadeningNote = `No errors found for service "${resolvedService}" \u2014 showing all services`;
|
|
95838
|
+
}
|
|
95285
95839
|
}
|
|
95286
95840
|
}
|
|
95287
95841
|
if (opts.json) {
|
|
@@ -95309,46 +95863,46 @@ async function runLogs(service, opts) {
|
|
|
95309
95863
|
return 0;
|
|
95310
95864
|
}
|
|
95311
95865
|
console.log(
|
|
95312
|
-
|
|
95866
|
+
import_picocolors25.default.bold(`Error analysis`) + import_picocolors25.default.dim(
|
|
95313
95867
|
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
95314
95868
|
)
|
|
95315
95869
|
);
|
|
95316
|
-
console.log(
|
|
95870
|
+
console.log(import_picocolors25.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
95317
95871
|
if (broadeningNote) {
|
|
95318
|
-
console.log(
|
|
95872
|
+
console.log(import_picocolors25.default.yellow(` ${broadeningNote}`));
|
|
95319
95873
|
}
|
|
95320
95874
|
console.log(
|
|
95321
|
-
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${
|
|
95875
|
+
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors25.default.yellow(String(analysis.newSignatures.length))} new`
|
|
95322
95876
|
);
|
|
95323
95877
|
console.log("");
|
|
95324
95878
|
if (analysis.signatures.length === 0) {
|
|
95325
|
-
console.log(
|
|
95879
|
+
console.log(import_picocolors25.default.dim(" No error-level logs in the window."));
|
|
95326
95880
|
console.log(
|
|
95327
|
-
|
|
95881
|
+
import_picocolors25.default.dim(
|
|
95328
95882
|
` Searched: ${scopeService ? `service "${scopeService}"` : "all services"} \xB7 index: ${indexPattern} \xB7 from: ${fromDisplay} UTC`
|
|
95329
95883
|
)
|
|
95330
95884
|
);
|
|
95331
|
-
console.log(
|
|
95885
|
+
console.log(import_picocolors25.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
|
|
95332
95886
|
return 0;
|
|
95333
95887
|
}
|
|
95334
95888
|
for (const s of analysis.signatures) {
|
|
95335
95889
|
const tags = [];
|
|
95336
|
-
if (s.isNew) tags.push(
|
|
95890
|
+
if (s.isNew) tags.push(import_picocolors25.default.red("NEW"));
|
|
95337
95891
|
else if (s.ratio !== void 0 && Number.isFinite(s.ratio) && s.ratio >= 1.5) {
|
|
95338
|
-
tags.push(
|
|
95892
|
+
tags.push(import_picocolors25.default.yellow(`spike x${s.ratio.toFixed(1)}`));
|
|
95339
95893
|
}
|
|
95340
|
-
const svc = s.services.length > 0 ?
|
|
95894
|
+
const svc = s.services.length > 0 ? import_picocolors25.default.dim(` \xB7 ${s.services.slice(0, 3).join(", ")}`) : "";
|
|
95341
95895
|
const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
|
|
95342
95896
|
console.log(
|
|
95343
|
-
` ${
|
|
95897
|
+
` ${import_picocolors25.default.red(String(s.count).padStart(6))} ${import_picocolors25.default.bold(s.key.padEnd(14).slice(0, 14))} ` + import_picocolors25.default.dim(`first ${shortTs(s.firstSeen)} \xB7 last ${shortTs(s.lastSeen)}`) + svc + tagStr
|
|
95344
95898
|
);
|
|
95345
95899
|
}
|
|
95346
95900
|
if (analysis.affectedServices.length > 0) {
|
|
95347
95901
|
console.log("");
|
|
95348
|
-
console.log(` ${
|
|
95902
|
+
console.log(` ${import_picocolors25.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
|
|
95349
95903
|
}
|
|
95350
95904
|
console.log("");
|
|
95351
|
-
console.log(
|
|
95905
|
+
console.log(import_picocolors25.default.dim(" (use --raw to see individual log lines)"));
|
|
95352
95906
|
if (opts.ai) {
|
|
95353
95907
|
const result = await renderAiInterpretation({
|
|
95354
95908
|
command: "logs",
|
|
@@ -95367,19 +95921,19 @@ async function runLogs(service, opts) {
|
|
|
95367
95921
|
});
|
|
95368
95922
|
console.log("\n" + renderInterpretation(result));
|
|
95369
95923
|
if (!result.ok) {
|
|
95370
|
-
console.error(
|
|
95924
|
+
console.error(import_picocolors25.default.yellow(`[ai] ${result.warning}`));
|
|
95371
95925
|
}
|
|
95372
95926
|
}
|
|
95373
95927
|
return 0;
|
|
95374
95928
|
} catch (err) {
|
|
95375
|
-
console.error(
|
|
95929
|
+
console.error(import_picocolors25.default.red(err.message));
|
|
95376
95930
|
return 1;
|
|
95377
95931
|
}
|
|
95378
95932
|
}
|
|
95379
95933
|
|
|
95380
95934
|
// ../../packages/cli/src/commands/metrics.ts
|
|
95381
95935
|
init_cjs_shims();
|
|
95382
|
-
var
|
|
95936
|
+
var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
95383
95937
|
var METRICS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
95384
95938
|
|
|
95385
95939
|
Evidence used
|
|
@@ -95424,10 +95978,10 @@ function anomalyColor(anomaly) {
|
|
|
95424
95978
|
case "latency-spike":
|
|
95425
95979
|
case "error-rate-change":
|
|
95426
95980
|
case "throughput-drop":
|
|
95427
|
-
return
|
|
95981
|
+
return import_picocolors26.default.red;
|
|
95428
95982
|
case "queue-growth":
|
|
95429
95983
|
case "change":
|
|
95430
|
-
return
|
|
95984
|
+
return import_picocolors26.default.yellow;
|
|
95431
95985
|
default:
|
|
95432
95986
|
return (s) => s;
|
|
95433
95987
|
}
|
|
@@ -95454,7 +96008,7 @@ async function runMetrics(hint, opts) {
|
|
|
95454
96008
|
const metrics = metricsProviderFromConfig(config2);
|
|
95455
96009
|
if (metrics === null) {
|
|
95456
96010
|
console.error(
|
|
95457
|
-
|
|
96011
|
+
import_picocolors26.default.red(
|
|
95458
96012
|
"Grafana not configured \u2014 set GRAFANA_URL + GRAFANA_USER + GRAFANA_PASSWORD"
|
|
95459
96013
|
)
|
|
95460
96014
|
);
|
|
@@ -95462,7 +96016,7 @@ async function runMetrics(hint, opts) {
|
|
|
95462
96016
|
}
|
|
95463
96017
|
const health = await metrics.health();
|
|
95464
96018
|
if (!health.ok) {
|
|
95465
|
-
console.error(
|
|
96019
|
+
console.error(import_picocolors26.default.red(`Grafana unreachable: ${health.detail}`));
|
|
95466
96020
|
return 1;
|
|
95467
96021
|
}
|
|
95468
96022
|
const stepNum = opts.step !== void 0 ? Number(opts.step) : void 0;
|
|
@@ -95491,14 +96045,14 @@ async function runMetrics(hint, opts) {
|
|
|
95491
96045
|
return 0;
|
|
95492
96046
|
}
|
|
95493
96047
|
if (series.length === 0) {
|
|
95494
|
-
console.log(
|
|
96048
|
+
console.log(import_picocolors26.default.dim("No series returned."));
|
|
95495
96049
|
return 0;
|
|
95496
96050
|
}
|
|
95497
96051
|
for (const s of series) {
|
|
95498
96052
|
const summary = summarize(s);
|
|
95499
96053
|
const label = labelStr(s.labels);
|
|
95500
96054
|
console.log(
|
|
95501
|
-
` ${
|
|
96055
|
+
` ${import_picocolors26.default.cyan(label || "(no labels)")} last=${import_picocolors26.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
|
|
95502
96056
|
);
|
|
95503
96057
|
}
|
|
95504
96058
|
return 0;
|
|
@@ -95513,36 +96067,36 @@ async function runMetrics(hint, opts) {
|
|
|
95513
96067
|
if (findings2.length === 0) {
|
|
95514
96068
|
if (hint !== void 0) {
|
|
95515
96069
|
const tokens = extractHintTokens(hint);
|
|
95516
|
-
console.log(
|
|
96070
|
+
console.log(import_picocolors26.default.dim(`No panels matched hint "${hint}".`));
|
|
95517
96071
|
if (tokens.length > 0) {
|
|
95518
|
-
console.log(
|
|
96072
|
+
console.log(import_picocolors26.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
|
|
95519
96073
|
}
|
|
95520
96074
|
console.log(
|
|
95521
|
-
|
|
95522
|
-
` Tip: run ${
|
|
96075
|
+
import_picocolors26.default.dim(
|
|
96076
|
+
` Tip: run ${import_picocolors26.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
|
|
95523
96077
|
)
|
|
95524
96078
|
);
|
|
95525
96079
|
} else {
|
|
95526
|
-
console.log(
|
|
96080
|
+
console.log(import_picocolors26.default.dim("No panels found in configured Grafana dashboards."));
|
|
95527
96081
|
}
|
|
95528
96082
|
return 0;
|
|
95529
96083
|
}
|
|
95530
96084
|
const hintSuffix = hint !== void 0 ? ` (hint: "${hint}")` : "";
|
|
95531
96085
|
const matchSources = hint !== void 0 ? [...new Set(findings2.map((f) => f.matchSource).filter(Boolean))] : [];
|
|
95532
|
-
const matchSuffix = matchSources.length > 0 ?
|
|
96086
|
+
const matchSuffix = matchSources.length > 0 ? import_picocolors26.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
|
|
95533
96087
|
console.log(
|
|
95534
|
-
|
|
96088
|
+
import_picocolors26.default.bold(
|
|
95535
96089
|
`Grafana metrics${hintSuffix} \u2014 ${findings2.length} series across panels`
|
|
95536
96090
|
) + matchSuffix
|
|
95537
96091
|
);
|
|
95538
96092
|
console.log(
|
|
95539
|
-
|
|
96093
|
+
import_picocolors26.default.dim(
|
|
95540
96094
|
` window: ${new Date(from * 1e3).toISOString()} \u2013 ${new Date(to * 1e3).toISOString()}`
|
|
95541
96095
|
)
|
|
95542
96096
|
);
|
|
95543
96097
|
console.log("");
|
|
95544
96098
|
if (flagged.length === 0) {
|
|
95545
|
-
console.log(
|
|
96099
|
+
console.log(import_picocolors26.default.green(" No anomalies detected."));
|
|
95546
96100
|
} else {
|
|
95547
96101
|
for (const f of flagged) {
|
|
95548
96102
|
const color = anomalyColor(f.anomaly);
|
|
@@ -95551,14 +96105,14 @@ async function runMetrics(hint, opts) {
|
|
|
95551
96105
|
const lblStr = labelStr(f.labels);
|
|
95552
96106
|
console.log(
|
|
95553
96107
|
color(
|
|
95554
|
-
` ${label.padEnd(22)} ${f.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f.baselineAvg)} -> ${fmtNum(f.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${
|
|
96108
|
+
` ${label.padEnd(22)} ${f.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f.baselineAvg)} -> ${fmtNum(f.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors26.default.dim(lblStr)}` : "")
|
|
95555
96109
|
)
|
|
95556
96110
|
);
|
|
95557
96111
|
}
|
|
95558
96112
|
}
|
|
95559
96113
|
if (ok.length > 0) {
|
|
95560
96114
|
console.log("");
|
|
95561
|
-
console.log(
|
|
96115
|
+
console.log(import_picocolors26.default.dim(` ${ok.length} panel series with no anomaly.`));
|
|
95562
96116
|
}
|
|
95563
96117
|
if (opts.ai) {
|
|
95564
96118
|
const result = await renderAiInterpretation({
|
|
@@ -95573,19 +96127,19 @@ async function runMetrics(hint, opts) {
|
|
|
95573
96127
|
});
|
|
95574
96128
|
console.log("\n" + renderInterpretation(result));
|
|
95575
96129
|
if (!result.ok) {
|
|
95576
|
-
console.error(
|
|
96130
|
+
console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
|
|
95577
96131
|
}
|
|
95578
96132
|
}
|
|
95579
96133
|
return 0;
|
|
95580
96134
|
} catch (err) {
|
|
95581
|
-
console.error(
|
|
96135
|
+
console.error(import_picocolors26.default.red(err.message));
|
|
95582
96136
|
return 1;
|
|
95583
96137
|
}
|
|
95584
96138
|
}
|
|
95585
96139
|
|
|
95586
96140
|
// ../../packages/cli/src/commands/state.ts
|
|
95587
96141
|
init_cjs_shims();
|
|
95588
|
-
var
|
|
96142
|
+
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
95589
96143
|
var STATE_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
95590
96144
|
|
|
95591
96145
|
Evidence used
|
|
@@ -95611,13 +96165,13 @@ async function runState(opts) {
|
|
|
95611
96165
|
try {
|
|
95612
96166
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
95613
96167
|
} catch (err) {
|
|
95614
|
-
console.error(
|
|
96168
|
+
console.error(import_picocolors27.default.red(err.message));
|
|
95615
96169
|
return 1;
|
|
95616
96170
|
}
|
|
95617
96171
|
const mongo = mongoForEnv(renv);
|
|
95618
96172
|
if (mongo === null) {
|
|
95619
96173
|
console.error(
|
|
95620
|
-
|
|
96174
|
+
import_picocolors27.default.red(
|
|
95621
96175
|
`No MongoDB connector configured for ${renv.project}/${renv.env} (set the project's Mongo URL env var).`
|
|
95622
96176
|
)
|
|
95623
96177
|
);
|
|
@@ -95625,7 +96179,7 @@ async function runState(opts) {
|
|
|
95625
96179
|
}
|
|
95626
96180
|
const health = await mongo.health();
|
|
95627
96181
|
if (!health.ok) {
|
|
95628
|
-
console.error(
|
|
96182
|
+
console.error(import_picocolors27.default.red(`MongoDB unreachable: ${health.detail}`));
|
|
95629
96183
|
await mongo.close();
|
|
95630
96184
|
return 1;
|
|
95631
96185
|
}
|
|
@@ -95651,38 +96205,38 @@ async function runState(opts) {
|
|
|
95651
96205
|
);
|
|
95652
96206
|
return 0;
|
|
95653
96207
|
}
|
|
95654
|
-
const discoveryNote = analysis.autoDiscovered ?
|
|
96208
|
+
const discoveryNote = analysis.autoDiscovered ? import_picocolors27.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
|
|
95655
96209
|
console.log(
|
|
95656
|
-
|
|
96210
|
+
import_picocolors27.default.bold("State analysis") + import_picocolors27.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
|
|
95657
96211
|
);
|
|
95658
96212
|
console.log("");
|
|
95659
96213
|
let signals = 0;
|
|
95660
96214
|
for (const c of analysis.collections) {
|
|
95661
96215
|
const flags = [];
|
|
95662
96216
|
if (c.isStale === true) {
|
|
95663
|
-
flags.push(
|
|
96217
|
+
flags.push(import_picocolors27.default.yellow(`stale (last ${c.dateField}, ~${Math.round(c.ageHours ?? 0)}h)`));
|
|
95664
96218
|
}
|
|
95665
96219
|
for (const a of c.anomalies) {
|
|
95666
|
-
flags.push(
|
|
96220
|
+
flags.push(import_picocolors27.default.red(`${a.count}\xD7 "${a.value}"`));
|
|
95667
96221
|
signals += 1;
|
|
95668
96222
|
}
|
|
95669
96223
|
if (c.isStale === true) signals += 1;
|
|
95670
|
-
const head = ` ${
|
|
95671
|
-
console.log(flags.length > 0 ? `${head} ${flags.join(" ")}` :
|
|
96224
|
+
const head = ` ${import_picocolors27.default.bold(c.collection.padEnd(18).slice(0, 18))} ${import_picocolors27.default.dim(String(c.count).padStart(8) + " docs")}`;
|
|
96225
|
+
console.log(flags.length > 0 ? `${head} ${flags.join(" ")}` : import_picocolors27.default.dim(head));
|
|
95672
96226
|
}
|
|
95673
96227
|
console.log("");
|
|
95674
96228
|
if (analysis.collections.length === 0) {
|
|
95675
96229
|
console.log(
|
|
95676
|
-
|
|
96230
|
+
import_picocolors27.default.yellow(" No collections discovered in this database.") + import_picocolors27.default.dim(" Verify the database name in your MongoDB connector config.")
|
|
95677
96231
|
);
|
|
95678
96232
|
} else {
|
|
95679
96233
|
const scope = analysis.autoDiscovered ? "collection(s)" : "allowlisted collection(s)";
|
|
95680
96234
|
console.log(
|
|
95681
|
-
signals > 0 ? ` ${
|
|
96235
|
+
signals > 0 ? ` ${import_picocolors27.default.bold(String(signals))} state signal(s) across ${analysis.collections.length} ${scope}` : import_picocolors27.default.dim(` No state anomalies across ${analysis.collections.length} ${scope}.`)
|
|
95682
96236
|
);
|
|
95683
96237
|
if (analysis.autoDiscovered) {
|
|
95684
96238
|
console.log(
|
|
95685
|
-
|
|
96239
|
+
import_picocolors27.default.dim(
|
|
95686
96240
|
` Tip: add a "collections" list to your MongoDB connector config to restrict analysis.`
|
|
95687
96241
|
)
|
|
95688
96242
|
);
|
|
@@ -95700,7 +96254,7 @@ async function runState(opts) {
|
|
|
95700
96254
|
});
|
|
95701
96255
|
console.log("\n" + renderInterpretation(result));
|
|
95702
96256
|
if (!result.ok) {
|
|
95703
|
-
console.error(
|
|
96257
|
+
console.error(import_picocolors27.default.yellow(`[ai] ${result.warning}`));
|
|
95704
96258
|
}
|
|
95705
96259
|
}
|
|
95706
96260
|
return 0;
|
|
@@ -95708,20 +96262,20 @@ async function runState(opts) {
|
|
|
95708
96262
|
await mongo.close();
|
|
95709
96263
|
}
|
|
95710
96264
|
} catch (err) {
|
|
95711
|
-
console.error(
|
|
96265
|
+
console.error(import_picocolors27.default.red(err.message));
|
|
95712
96266
|
return 1;
|
|
95713
96267
|
}
|
|
95714
96268
|
}
|
|
95715
96269
|
|
|
95716
96270
|
// ../../packages/cli/src/commands/init.ts
|
|
95717
96271
|
init_cjs_shims();
|
|
95718
|
-
var
|
|
95719
|
-
var
|
|
96272
|
+
var import_picocolors28 = __toESM(require_picocolors(), 1);
|
|
96273
|
+
var import_node_path17 = require("path");
|
|
95720
96274
|
async function runInit(opts) {
|
|
95721
96275
|
try {
|
|
95722
96276
|
const cwd = process.cwd();
|
|
95723
|
-
const root = opts.path ? (0,
|
|
95724
|
-
const name = opts.name ?? (0,
|
|
96277
|
+
const root = opts.path ? (0, import_node_path17.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
|
|
96278
|
+
const name = opts.name ?? (0, import_node_path17.basename)(root);
|
|
95725
96279
|
const envName = opts.env ?? "production";
|
|
95726
96280
|
const repo = { name, path: root };
|
|
95727
96281
|
if (opts.source) repo["source"] = { hostUrl: opts.source };
|
|
@@ -95736,65 +96290,65 @@ async function runInit(opts) {
|
|
|
95736
96290
|
const configPath = writeLocalConfig(root, file);
|
|
95737
96291
|
registerProject(name, root, configPath);
|
|
95738
96292
|
ensureProjectGitignore(root);
|
|
95739
|
-
console.log(`${
|
|
95740
|
-
console.log(
|
|
95741
|
-
console.log(
|
|
96293
|
+
console.log(`${import_picocolors28.default.green("\u2713")} Initialized Horus project ${import_picocolors28.default.bold(name)}`);
|
|
96294
|
+
console.log(import_picocolors28.default.dim(` config: ${configPath}`));
|
|
96295
|
+
console.log(import_picocolors28.default.dim(` registered: horus investigate --name ${name} "<hint>"`));
|
|
95742
96296
|
if (!opts.source) {
|
|
95743
96297
|
console.log(
|
|
95744
|
-
|
|
96298
|
+
import_picocolors28.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
|
|
95745
96299
|
);
|
|
95746
96300
|
}
|
|
95747
96301
|
console.log(
|
|
95748
|
-
|
|
96302
|
+
import_picocolors28.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
|
|
95749
96303
|
);
|
|
95750
96304
|
console.log(
|
|
95751
|
-
|
|
96305
|
+
import_picocolors28.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
|
|
95752
96306
|
);
|
|
95753
96307
|
return 0;
|
|
95754
96308
|
} catch (err) {
|
|
95755
|
-
console.error(
|
|
96309
|
+
console.error(import_picocolors28.default.red(err.message));
|
|
95756
96310
|
return 1;
|
|
95757
96311
|
}
|
|
95758
96312
|
}
|
|
95759
96313
|
|
|
95760
96314
|
// ../../packages/cli/src/commands/projects.ts
|
|
95761
96315
|
init_cjs_shims();
|
|
95762
|
-
var
|
|
96316
|
+
var import_picocolors29 = __toESM(require_picocolors(), 1);
|
|
95763
96317
|
async function runProjects() {
|
|
95764
96318
|
const reg = readRegistry();
|
|
95765
96319
|
const names = Object.keys(reg.projects).sort();
|
|
95766
96320
|
if (names.length === 0) {
|
|
95767
96321
|
console.log(
|
|
95768
|
-
|
|
96322
|
+
import_picocolors29.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
|
|
95769
96323
|
);
|
|
95770
96324
|
return 0;
|
|
95771
96325
|
}
|
|
95772
|
-
console.log(
|
|
96326
|
+
console.log(import_picocolors29.default.bold("Registered Horus projects"));
|
|
95773
96327
|
console.log("");
|
|
95774
96328
|
for (const name of names) {
|
|
95775
96329
|
const entry2 = reg.projects[name];
|
|
95776
96330
|
if (entry2 === void 0) continue;
|
|
95777
|
-
console.log(` ${
|
|
96331
|
+
console.log(` ${import_picocolors29.default.bold(name.padEnd(22))} ${import_picocolors29.default.dim(entry2.root)}`);
|
|
95778
96332
|
}
|
|
95779
96333
|
console.log("");
|
|
95780
|
-
console.log(
|
|
96334
|
+
console.log(import_picocolors29.default.dim(' horus investigate --name <name> "<hint>"'));
|
|
95781
96335
|
return 0;
|
|
95782
96336
|
}
|
|
95783
96337
|
|
|
95784
96338
|
// ../../packages/cli/src/commands/setup.ts
|
|
95785
96339
|
init_cjs_shims();
|
|
95786
|
-
var
|
|
96340
|
+
var import_picocolors30 = __toESM(require_picocolors(), 1);
|
|
95787
96341
|
var DEFAULT_DB_URL3 = "postgresql://horus:horus@localhost:5433/horus";
|
|
95788
96342
|
async function runSetup(opts) {
|
|
95789
96343
|
const write = opts.write ?? ((line2) => console.log(line2));
|
|
95790
96344
|
let ok = true;
|
|
95791
|
-
write(
|
|
96345
|
+
write(import_picocolors30.default.bold("\nHorus setup\n"));
|
|
95792
96346
|
const backendVersion = await getSourceVersion();
|
|
95793
96347
|
if (backendVersion === null) {
|
|
95794
96348
|
ok = false;
|
|
95795
|
-
write(` ${
|
|
96349
|
+
write(` ${import_picocolors30.default.red("\u25CF")} Horus source-intelligence backend not found`);
|
|
95796
96350
|
write(
|
|
95797
|
-
|
|
96351
|
+
import_picocolors30.default.dim(
|
|
95798
96352
|
` install it (Python 3.11+ required):
|
|
95799
96353
|
pip install horus-source
|
|
95800
96354
|
ensure ~/.local/bin is on your PATH`
|
|
@@ -95803,17 +96357,17 @@ async function runSetup(opts) {
|
|
|
95803
96357
|
} else if (backendVersion !== PINNED_SOURCE_VERSION) {
|
|
95804
96358
|
ok = false;
|
|
95805
96359
|
write(
|
|
95806
|
-
` ${
|
|
96360
|
+
` ${import_picocolors30.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors30.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
|
|
95807
96361
|
);
|
|
95808
96362
|
write(
|
|
95809
|
-
|
|
96363
|
+
import_picocolors30.default.dim(
|
|
95810
96364
|
` update it:
|
|
95811
96365
|
pip install --upgrade horus-source`
|
|
95812
96366
|
)
|
|
95813
96367
|
);
|
|
95814
96368
|
} else {
|
|
95815
96369
|
write(
|
|
95816
|
-
` ${
|
|
96370
|
+
` ${import_picocolors30.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors30.default.dim(`(${backendVersion})`)
|
|
95817
96371
|
);
|
|
95818
96372
|
}
|
|
95819
96373
|
let dbUrl = process.env["DATABASE_URL"] ?? DEFAULT_DB_URL3;
|
|
@@ -95825,11 +96379,11 @@ async function runSetup(opts) {
|
|
|
95825
96379
|
}
|
|
95826
96380
|
const db = await checkDatabase(dbUrl);
|
|
95827
96381
|
if (db.reachable) {
|
|
95828
|
-
write(` ${
|
|
96382
|
+
write(` ${import_picocolors30.default.green("\u25CF")} Postgres reachable ${import_picocolors30.default.dim(`(${db.schemaDetail})`)}`);
|
|
95829
96383
|
if (!db.schemaReady) {
|
|
95830
96384
|
ok = false;
|
|
95831
96385
|
write(
|
|
95832
|
-
|
|
96386
|
+
import_picocolors30.default.dim(
|
|
95833
96387
|
` schema not applied \u2014 run migrations:
|
|
95834
96388
|
from the Horus repo: pnpm db migrate
|
|
95835
96389
|
or apply the schema directly: https://github.com/meritt-dev/horus/tree/master/packages/db/drizzle`
|
|
@@ -95838,9 +96392,9 @@ async function runSetup(opts) {
|
|
|
95838
96392
|
}
|
|
95839
96393
|
} else {
|
|
95840
96394
|
ok = false;
|
|
95841
|
-
write(` ${
|
|
96395
|
+
write(` ${import_picocolors30.default.red("\u25CF")} Postgres unreachable`);
|
|
95842
96396
|
write(
|
|
95843
|
-
|
|
96397
|
+
import_picocolors30.default.dim(
|
|
95844
96398
|
` start a local instance:
|
|
95845
96399
|
docker run -d --name horus-db \\
|
|
95846
96400
|
-e POSTGRES_USER=horus -e POSTGRES_PASSWORD=horus -e POSTGRES_DB=horus \\
|
|
@@ -95865,10 +96419,10 @@ async function runSetup(opts) {
|
|
|
95865
96419
|
if (!health.ok) {
|
|
95866
96420
|
ok = false;
|
|
95867
96421
|
write(
|
|
95868
|
-
` ${
|
|
96422
|
+
` ${import_picocolors30.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors30.default.bold(repo.name)} ` + import_picocolors30.default.dim(`(${repoHostUrl})`)
|
|
95869
96423
|
);
|
|
95870
96424
|
write(
|
|
95871
|
-
|
|
96425
|
+
import_picocolors30.default.dim(
|
|
95872
96426
|
` start the source intelligence host:
|
|
95873
96427
|
cd ${repo.path}
|
|
95874
96428
|
horus index`
|
|
@@ -95879,10 +96433,10 @@ async function runSetup(opts) {
|
|
|
95879
96433
|
if (count === 0) {
|
|
95880
96434
|
ok = false;
|
|
95881
96435
|
write(
|
|
95882
|
-
` ${
|
|
96436
|
+
` ${import_picocolors30.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors30.default.bold(repo.name)} is not indexed`
|
|
95883
96437
|
);
|
|
95884
96438
|
write(
|
|
95885
|
-
|
|
96439
|
+
import_picocolors30.default.dim(
|
|
95886
96440
|
` index the repo:
|
|
95887
96441
|
cd ${repo.path}
|
|
95888
96442
|
horus index`
|
|
@@ -95890,7 +96444,7 @@ async function runSetup(opts) {
|
|
|
95890
96444
|
);
|
|
95891
96445
|
} else {
|
|
95892
96446
|
write(
|
|
95893
|
-
` ${
|
|
96447
|
+
` ${import_picocolors30.default.green("\u25CF")} ${import_picocolors30.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors30.default.dim(`(${repoHostUrl})`)
|
|
95894
96448
|
);
|
|
95895
96449
|
}
|
|
95896
96450
|
}
|
|
@@ -95900,10 +96454,10 @@ async function runSetup(opts) {
|
|
|
95900
96454
|
write("");
|
|
95901
96455
|
if (ok) {
|
|
95902
96456
|
write(
|
|
95903
|
-
|
|
96457
|
+
import_picocolors30.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
|
|
95904
96458
|
);
|
|
95905
96459
|
} else {
|
|
95906
|
-
write(
|
|
96460
|
+
write(import_picocolors30.default.yellow("Resolve the items above, then re-run `horus setup`."));
|
|
95907
96461
|
}
|
|
95908
96462
|
return ok ? 0 : 1;
|
|
95909
96463
|
}
|
|
@@ -95912,12 +96466,12 @@ async function runSetup(opts) {
|
|
|
95912
96466
|
init_cjs_shims();
|
|
95913
96467
|
var import_node_readline = require("readline");
|
|
95914
96468
|
var import_node_child_process6 = require("child_process");
|
|
95915
|
-
var
|
|
96469
|
+
var import_picocolors33 = __toESM(require_picocolors(), 1);
|
|
95916
96470
|
|
|
95917
96471
|
// ../../packages/cli/src/lib/tty-selector.ts
|
|
95918
96472
|
init_cjs_shims();
|
|
95919
96473
|
var import_node_tty = require("tty");
|
|
95920
|
-
var
|
|
96474
|
+
var import_picocolors31 = __toESM(require_picocolors(), 1);
|
|
95921
96475
|
var ExitPromptError = class extends Error {
|
|
95922
96476
|
constructor(message = "Prompt was cancelled") {
|
|
95923
96477
|
super(message);
|
|
@@ -95973,7 +96527,7 @@ async function checkboxSearch(opts) {
|
|
|
95973
96527
|
clear();
|
|
95974
96528
|
const lines = [];
|
|
95975
96529
|
lines.push(
|
|
95976
|
-
`? ${
|
|
96530
|
+
`? ${import_picocolors31.default.bold(message)} ${import_picocolors31.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
|
|
95977
96531
|
);
|
|
95978
96532
|
const visible = filtered.slice(visibleOffset, visibleOffset + pageSize);
|
|
95979
96533
|
for (let i = 0; i < visible.length; i++) {
|
|
@@ -95981,29 +96535,29 @@ async function checkboxSearch(opts) {
|
|
|
95981
96535
|
const choice = visible[i];
|
|
95982
96536
|
const isCursor = idx === cursor;
|
|
95983
96537
|
const isSelected = selected.has(choice);
|
|
95984
|
-
const marker = isSelected ?
|
|
96538
|
+
const marker = isSelected ? import_picocolors31.default.green("\u25CF") : " ";
|
|
95985
96539
|
const prefix = `[${marker}]`;
|
|
95986
|
-
const label = isCursor ?
|
|
95987
|
-
const pointer = isCursor ?
|
|
96540
|
+
const label = isCursor ? import_picocolors31.default.cyan(choice) : choice;
|
|
96541
|
+
const pointer = isCursor ? import_picocolors31.default.cyan("\u276F") : " ";
|
|
95988
96542
|
lines.push(` ${pointer} ${prefix} ${label}`);
|
|
95989
96543
|
}
|
|
95990
96544
|
if (filtered.length === 0) {
|
|
95991
|
-
lines.push(
|
|
96545
|
+
lines.push(import_picocolors31.default.dim(" No matches"));
|
|
95992
96546
|
}
|
|
95993
96547
|
const statusParts = [
|
|
95994
96548
|
`${selected.size} selected`,
|
|
95995
96549
|
`${filtered.length}/${choices.length} matches`
|
|
95996
96550
|
];
|
|
95997
96551
|
if (filter) statusParts.push(`filter: ${filter}`);
|
|
95998
|
-
lines.push(
|
|
96552
|
+
lines.push(import_picocolors31.default.dim(` ${statusParts.join(" \xB7 ")}`));
|
|
95999
96553
|
output.write(lines.join("\n"));
|
|
96000
96554
|
lastRenderLines = lines.length;
|
|
96001
96555
|
}
|
|
96002
96556
|
function finish(values2) {
|
|
96003
96557
|
cleanup();
|
|
96004
|
-
const summary = values2.length > 0 ? values2.join(", ") :
|
|
96558
|
+
const summary = values2.length > 0 ? values2.join(", ") : import_picocolors31.default.dim("none");
|
|
96005
96559
|
output.write(`
|
|
96006
|
-
? ${
|
|
96560
|
+
? ${import_picocolors31.default.bold(message)} ${summary}
|
|
96007
96561
|
`);
|
|
96008
96562
|
resolve8(values2);
|
|
96009
96563
|
}
|
|
@@ -96095,7 +96649,7 @@ async function checkboxSearch(opts) {
|
|
|
96095
96649
|
// ../../packages/cli/src/commands/connect-ai.ts
|
|
96096
96650
|
init_cjs_shims();
|
|
96097
96651
|
var import_node_child_process5 = require("child_process");
|
|
96098
|
-
var
|
|
96652
|
+
var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
96099
96653
|
function cliInstalled(id) {
|
|
96100
96654
|
const r = (0, import_node_child_process5.spawnSync)(id, ["--version"], { stdio: "ignore", timeout: 2e3 });
|
|
96101
96655
|
return !r.error;
|
|
@@ -96123,13 +96677,13 @@ async function runConnectAi(opts) {
|
|
|
96123
96677
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
96124
96678
|
const configPath = discoverLocalConfig(cwd) ?? localConfigPath(root);
|
|
96125
96679
|
const installed = LOCAL_PROVIDER_IDS.filter((id) => cliInstalled(id));
|
|
96126
|
-
console.log(
|
|
96680
|
+
console.log(import_picocolors32.default.bold("\nAI providers"));
|
|
96127
96681
|
console.log(
|
|
96128
|
-
|
|
96682
|
+
import_picocolors32.default.dim(
|
|
96129
96683
|
` Local CLIs on PATH: ${installed.length > 0 ? installed.join(", ") : "none detected"}`
|
|
96130
96684
|
)
|
|
96131
96685
|
);
|
|
96132
|
-
console.log(
|
|
96686
|
+
console.log(import_picocolors32.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
|
|
96133
96687
|
let provider = opts.provider;
|
|
96134
96688
|
if (provider === void 0) {
|
|
96135
96689
|
if (isInteractive()) {
|
|
@@ -96140,7 +96694,7 @@ async function runConnectAi(opts) {
|
|
|
96140
96694
|
}
|
|
96141
96695
|
}
|
|
96142
96696
|
if (!AI_PROVIDERS.includes(provider)) {
|
|
96143
|
-
console.error(
|
|
96697
|
+
console.error(import_picocolors32.default.red(`Unknown AI provider: ${provider}`) + import_picocolors32.default.dim(`
|
|
96144
96698
|
supported: ${AI_PROVIDERS.join(", ")}`));
|
|
96145
96699
|
return 1;
|
|
96146
96700
|
}
|
|
@@ -96153,7 +96707,7 @@ async function runConnectAi(opts) {
|
|
|
96153
96707
|
}
|
|
96154
96708
|
if (!apiKey) {
|
|
96155
96709
|
console.error(
|
|
96156
|
-
|
|
96710
|
+
import_picocolors32.default.red("No API key provided.") + import_picocolors32.default.dim("\n Pass --api-key sk-ant-\u2026 or set ANTHROPIC_API_KEY and re-run.")
|
|
96157
96711
|
);
|
|
96158
96712
|
return 1;
|
|
96159
96713
|
}
|
|
@@ -96161,28 +96715,28 @@ async function runConnectAi(opts) {
|
|
|
96161
96715
|
const probe2 = await probeAnthropic(apiKey);
|
|
96162
96716
|
if (!probe2.ok) {
|
|
96163
96717
|
console.error(`
|
|
96164
|
-
${
|
|
96165
|
-
console.error(
|
|
96718
|
+
${import_picocolors32.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
|
|
96719
|
+
console.error(import_picocolors32.default.dim(" Fix the key and retry, or pass --no-test to skip."));
|
|
96166
96720
|
return 1;
|
|
96167
96721
|
}
|
|
96168
96722
|
console.log(`
|
|
96169
|
-
${
|
|
96723
|
+
${import_picocolors32.default.green("\u2713")} anthropic ${import_picocolors32.default.dim(`(${probe2.detail})`)}`);
|
|
96170
96724
|
}
|
|
96171
96725
|
if (opts.model) aiBlock["anthropic"] = { model: opts.model };
|
|
96172
96726
|
storedKey = apiKey;
|
|
96173
96727
|
} else {
|
|
96174
96728
|
if (!installed.includes(provider)) {
|
|
96175
96729
|
console.error(
|
|
96176
|
-
|
|
96730
|
+
import_picocolors32.default.red(`${provider} CLI not found on PATH.`) + import_picocolors32.default.dim(`
|
|
96177
96731
|
Install it first, or choose anthropic.`)
|
|
96178
96732
|
);
|
|
96179
96733
|
return 1;
|
|
96180
96734
|
}
|
|
96181
96735
|
const desc2 = DEFAULT_LOCAL_PROVIDER_REGISTRY.get(provider);
|
|
96182
96736
|
console.log(`
|
|
96183
|
-
${
|
|
96737
|
+
${import_picocolors32.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors32.default.dim("detected on PATH")}`);
|
|
96184
96738
|
console.log(
|
|
96185
|
-
|
|
96739
|
+
import_picocolors32.default.dim(
|
|
96186
96740
|
" Note: `investigate --ai` currently uses the Anthropic API for narrative.\n Local-CLI narrative generation is recorded as your preference for future use."
|
|
96187
96741
|
)
|
|
96188
96742
|
);
|
|
@@ -96200,15 +96754,15 @@ ${import_picocolors31.default.green("\u2713")} ${desc2?.displayName ?? provider}
|
|
|
96200
96754
|
});
|
|
96201
96755
|
}
|
|
96202
96756
|
console.log(`
|
|
96203
|
-
${
|
|
96204
|
-
console.log(
|
|
96757
|
+
${import_picocolors32.default.green("\u2713")} ${import_picocolors32.default.bold("ai")} provider saved \u2192 ${import_picocolors32.default.dim(configPath)}`);
|
|
96758
|
+
console.log(import_picocolors32.default.dim(` provider: ${provider}`));
|
|
96205
96759
|
if (storedKey && secretsPathStr) {
|
|
96206
|
-
console.log(
|
|
96760
|
+
console.log(import_picocolors32.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
|
|
96207
96761
|
}
|
|
96208
|
-
console.log(
|
|
96762
|
+
console.log(import_picocolors32.default.dim(' run: horus investigate "<hint>" --ai'));
|
|
96209
96763
|
return 0;
|
|
96210
96764
|
} catch (err) {
|
|
96211
|
-
console.error(
|
|
96765
|
+
console.error(import_picocolors32.default.red(err instanceof Error ? err.message : String(err)));
|
|
96212
96766
|
return 1;
|
|
96213
96767
|
}
|
|
96214
96768
|
}
|
|
@@ -96240,7 +96794,7 @@ async function runConnect(type, opts) {
|
|
|
96240
96794
|
}
|
|
96241
96795
|
if (!SUPPORTED.includes(type)) {
|
|
96242
96796
|
console.error(
|
|
96243
|
-
|
|
96797
|
+
import_picocolors33.default.red(`Unknown connector type: ${type}`) + import_picocolors33.default.dim(`
|
|
96244
96798
|
supported: ${SUPPORTED.join(", ")}, ai`)
|
|
96245
96799
|
);
|
|
96246
96800
|
return 1;
|
|
@@ -96271,20 +96825,20 @@ async function runConnect(type, opts) {
|
|
|
96271
96825
|
if (!probeResult.ok) {
|
|
96272
96826
|
console.error(
|
|
96273
96827
|
`
|
|
96274
|
-
${
|
|
96828
|
+
${import_picocolors33.default.red(`\u2717 Could not reach ${connectorType}:`)} ${probeResult.detail}` + import_picocolors33.default.dim("\n Fix the connection and retry, or pass --no-test to skip.")
|
|
96275
96829
|
);
|
|
96276
96830
|
return 1;
|
|
96277
96831
|
}
|
|
96278
96832
|
console.log(
|
|
96279
96833
|
`
|
|
96280
|
-
${
|
|
96834
|
+
${import_picocolors33.default.green("\u2713")} ${connectorType} reachable ${import_picocolors33.default.dim(`(${probeResult.detail})`)}`
|
|
96281
96835
|
);
|
|
96282
96836
|
}
|
|
96283
96837
|
const hasLiteralCredentials = filled.url !== void 0 || filled.password !== void 0 || filled.username !== void 0;
|
|
96284
96838
|
if (hasLiteralCredentials) {
|
|
96285
96839
|
if (isGitTracked(configPath, root)) {
|
|
96286
96840
|
console.error(
|
|
96287
|
-
|
|
96841
|
+
import_picocolors33.default.red(".horus/config.json is already tracked by Git.") + "\nStoring credentials here would expose them in the repository.\n" + import_picocolors33.default.dim(
|
|
96288
96842
|
" 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."
|
|
96289
96843
|
)
|
|
96290
96844
|
);
|
|
@@ -96294,18 +96848,18 @@ ${import_picocolors32.default.green("\u2713")} ${connectorType} reachable ${impo
|
|
|
96294
96848
|
}
|
|
96295
96849
|
patchLocalConnector(configPath, connectorType, patch, filled.env);
|
|
96296
96850
|
console.log(
|
|
96297
|
-
`${
|
|
96851
|
+
`${import_picocolors33.default.green("\u2713")} ${import_picocolors33.default.bold(connectorType)} connector saved \u2192 ${import_picocolors33.default.dim(configPath)}`
|
|
96298
96852
|
);
|
|
96299
96853
|
printSummary(connectorType, filled);
|
|
96300
|
-
console.log(
|
|
96854
|
+
console.log(import_picocolors33.default.dim(`
|
|
96301
96855
|
run: horus investigate "<hint>"`));
|
|
96302
96856
|
return 0;
|
|
96303
96857
|
} catch (err) {
|
|
96304
96858
|
if (err instanceof ExitPromptError) {
|
|
96305
|
-
console.error(
|
|
96859
|
+
console.error(import_picocolors33.default.red("Cancelled."));
|
|
96306
96860
|
return 1;
|
|
96307
96861
|
}
|
|
96308
|
-
console.error(
|
|
96862
|
+
console.error(import_picocolors33.default.red(err.message));
|
|
96309
96863
|
return 1;
|
|
96310
96864
|
}
|
|
96311
96865
|
}
|
|
@@ -96314,7 +96868,7 @@ async function fillInteractive(type, opts) {
|
|
|
96314
96868
|
if (!needsInteraction) return opts;
|
|
96315
96869
|
console.log(
|
|
96316
96870
|
`
|
|
96317
|
-
${
|
|
96871
|
+
${import_picocolors33.default.bold(`Connect ${type}`)} ${import_picocolors33.default.dim("(press Enter to skip optional fields)")}
|
|
96318
96872
|
`
|
|
96319
96873
|
);
|
|
96320
96874
|
const filled = { ...opts };
|
|
@@ -96369,7 +96923,7 @@ ${import_picocolors32.default.bold(`Connect ${type}`)} ${import_picocolors32.def
|
|
|
96369
96923
|
break;
|
|
96370
96924
|
case "redis": {
|
|
96371
96925
|
console.log(
|
|
96372
|
-
|
|
96926
|
+
import_picocolors33.default.dim(
|
|
96373
96927
|
" Tip: embed credentials directly in the URL \u2014 redis://:password@host:6379\n or enter the URL and password separately below."
|
|
96374
96928
|
)
|
|
96375
96929
|
);
|
|
@@ -96407,8 +96961,8 @@ function missingRequired(type, opts) {
|
|
|
96407
96961
|
}
|
|
96408
96962
|
function ask(label, placeholder = "", required2 = true) {
|
|
96409
96963
|
return new Promise((resolve8) => {
|
|
96410
|
-
const hint = placeholder ?
|
|
96411
|
-
const suffix = required2 ? "" :
|
|
96964
|
+
const hint = placeholder ? import_picocolors33.default.dim(` (${placeholder})`) : "";
|
|
96965
|
+
const suffix = required2 ? "" : import_picocolors33.default.dim(" [optional]");
|
|
96412
96966
|
process.stdout.write(` ${label}${suffix}${hint}: `);
|
|
96413
96967
|
const rl = (0, import_node_readline.createInterface)({
|
|
96414
96968
|
input: process.stdin,
|
|
@@ -96425,7 +96979,7 @@ function askPassword(label) {
|
|
|
96425
96979
|
return new Promise((resolve8) => {
|
|
96426
96980
|
const stdin = process.stdin;
|
|
96427
96981
|
if (typeof stdin.setRawMode === "function") {
|
|
96428
|
-
process.stdout.write(` ${label}${
|
|
96982
|
+
process.stdout.write(` ${label}${import_picocolors33.default.dim(" [optional]")}: `);
|
|
96429
96983
|
stdin.setRawMode(true);
|
|
96430
96984
|
stdin.resume();
|
|
96431
96985
|
let value = "";
|
|
@@ -96525,15 +97079,15 @@ function describeProbe(p) {
|
|
|
96525
97079
|
return `${p.keyCount} keys \xB7 ${p.suggestedRoles.join("/")}${examples ? ` \xB7 ${examples}` : ""}`;
|
|
96526
97080
|
}
|
|
96527
97081
|
async function discoverAndSelectDbs(url, bullmqPrefix) {
|
|
96528
|
-
console.log(
|
|
97082
|
+
console.log(import_picocolors33.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
|
|
96529
97083
|
const probes = await probeRedisDatabases(url, { bullmqPrefix });
|
|
96530
97084
|
const nonEmpty = probes.filter((p) => p.reachable && p.keyCount > 0);
|
|
96531
97085
|
if (nonEmpty.length === 0) {
|
|
96532
|
-
console.log(
|
|
97086
|
+
console.log(import_picocolors33.default.dim(" No populated DBs found."));
|
|
96533
97087
|
return [];
|
|
96534
97088
|
}
|
|
96535
97089
|
for (const p of nonEmpty) {
|
|
96536
|
-
console.log(` ${
|
|
97090
|
+
console.log(` ${import_picocolors33.default.bold(`DB ${p.db}`)} ${import_picocolors33.default.dim("\xB7 " + describeProbe(p))}`);
|
|
96537
97091
|
}
|
|
96538
97092
|
const byLabel = /* @__PURE__ */ new Map();
|
|
96539
97093
|
const choices = nonEmpty.map((p) => {
|
|
@@ -96644,7 +97198,7 @@ function printSummary(type, opts) {
|
|
|
96644
97198
|
} else if (opts.dashboard) {
|
|
96645
97199
|
lines.push(` dashboard: ${opts.dashboard}`);
|
|
96646
97200
|
}
|
|
96647
|
-
if (lines.length > 0) console.log(
|
|
97201
|
+
if (lines.length > 0) console.log(import_picocolors33.default.dim(lines.join("\n")));
|
|
96648
97202
|
}
|
|
96649
97203
|
function isGitTracked(filePath, cwd) {
|
|
96650
97204
|
try {
|
|
@@ -96683,11 +97237,11 @@ async function askIndexSelection(indices) {
|
|
|
96683
97237
|
const shown = indices.slice(0, MAX_DISPLAY);
|
|
96684
97238
|
console.log("\n Available Elasticsearch indexes/data streams:");
|
|
96685
97239
|
shown.forEach((name, i) => {
|
|
96686
|
-
console.log(` ${
|
|
97240
|
+
console.log(` ${import_picocolors33.default.dim(`[${i + 1}]`)} ${name}`);
|
|
96687
97241
|
});
|
|
96688
97242
|
if (indices.length > MAX_DISPLAY) {
|
|
96689
97243
|
console.log(
|
|
96690
|
-
|
|
97244
|
+
import_picocolors33.default.dim(
|
|
96691
97245
|
` \u2026 and ${indices.length - MAX_DISPLAY} more (type a pattern manually to match all)`
|
|
96692
97246
|
)
|
|
96693
97247
|
);
|
|
@@ -96731,11 +97285,11 @@ async function askDashboardSelection(dashboards) {
|
|
|
96731
97285
|
const shown = dashboards.slice(0, MAX_DISPLAY);
|
|
96732
97286
|
console.log("\n Available Grafana dashboards:");
|
|
96733
97287
|
shown.forEach((d, i) => {
|
|
96734
|
-
const folder = d.folderTitle ?
|
|
96735
|
-
console.log(` ${
|
|
97288
|
+
const folder = d.folderTitle ? import_picocolors33.default.dim(` (${d.folderTitle})`) : "";
|
|
97289
|
+
console.log(` ${import_picocolors33.default.dim(`[${i + 1}]`)} ${d.title}${folder}`);
|
|
96736
97290
|
});
|
|
96737
97291
|
if (dashboards.length > MAX_DISPLAY) {
|
|
96738
|
-
console.log(
|
|
97292
|
+
console.log(import_picocolors33.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
|
|
96739
97293
|
}
|
|
96740
97294
|
const input = (await ask(
|
|
96741
97295
|
` Select dashboards to use (e.g. 1,2 or Enter to type uid manually)`,
|
|
@@ -96786,12 +97340,12 @@ function redactUrl(raw) {
|
|
|
96786
97340
|
// ../../packages/cli/src/commands/stop.ts
|
|
96787
97341
|
init_cjs_shims();
|
|
96788
97342
|
var import_node_child_process7 = require("child_process");
|
|
96789
|
-
var
|
|
97343
|
+
var import_node_fs16 = require("fs");
|
|
96790
97344
|
var import_node_util4 = require("util");
|
|
96791
|
-
var
|
|
96792
|
-
var
|
|
97345
|
+
var import_node_path18 = require("path");
|
|
97346
|
+
var import_picocolors34 = __toESM(require_picocolors(), 1);
|
|
96793
97347
|
var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
|
|
96794
|
-
var unlinkAsync = (0, import_node_util4.promisify)(
|
|
97348
|
+
var unlinkAsync = (0, import_node_util4.promisify)(import_node_fs16.unlink);
|
|
96795
97349
|
var SPAWNED_HOST_FILE2 = "spawned-host.json";
|
|
96796
97350
|
var START_TIME_TOLERANCE_S = 60;
|
|
96797
97351
|
var STOP_WAIT_MS = 5e3;
|
|
@@ -96806,30 +97360,30 @@ async function runStop(opts) {
|
|
|
96806
97360
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
96807
97361
|
const hostUrl = readSourceHostUrl(root);
|
|
96808
97362
|
if (!hostUrl) {
|
|
96809
|
-
console.log(
|
|
97363
|
+
console.log(import_picocolors34.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
|
|
96810
97364
|
return 0;
|
|
96811
97365
|
}
|
|
96812
97366
|
return await stopHost(root, hostUrl);
|
|
96813
97367
|
} catch (err) {
|
|
96814
|
-
console.error(
|
|
97368
|
+
console.error(import_picocolors34.default.red(err.message));
|
|
96815
97369
|
return 1;
|
|
96816
97370
|
}
|
|
96817
97371
|
}
|
|
96818
97372
|
async function stopHost(root, hostUrl) {
|
|
96819
97373
|
const alive = await isHostHealthy(hostUrl);
|
|
96820
97374
|
if (!alive) {
|
|
96821
|
-
console.log(
|
|
97375
|
+
console.log(import_picocolors34.default.dim(`Host ${hostUrl} is already stopped.`));
|
|
96822
97376
|
return 0;
|
|
96823
97377
|
}
|
|
96824
97378
|
const port = extractPort(hostUrl);
|
|
96825
97379
|
if (port === null) {
|
|
96826
|
-
console.error(
|
|
97380
|
+
console.error(import_picocolors34.default.red(`Cannot determine port from host URL: ${hostUrl}`));
|
|
96827
97381
|
return 1;
|
|
96828
97382
|
}
|
|
96829
97383
|
const spawned = readSpawnedHost(root);
|
|
96830
97384
|
if (spawned === null) {
|
|
96831
97385
|
console.error(
|
|
96832
|
-
|
|
97386
|
+
import_picocolors34.default.red(
|
|
96833
97387
|
`No ownership record found (.horus/${SPAWNED_HOST_FILE2} absent). Horus will not stop a host it did not spawn.`
|
|
96834
97388
|
)
|
|
96835
97389
|
);
|
|
@@ -96837,12 +97391,12 @@ async function stopHost(root, hostUrl) {
|
|
|
96837
97391
|
}
|
|
96838
97392
|
const recordError = validateSpawnedRecord(spawned);
|
|
96839
97393
|
if (recordError !== null) {
|
|
96840
|
-
console.error(
|
|
97394
|
+
console.error(import_picocolors34.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
|
|
96841
97395
|
return 1;
|
|
96842
97396
|
}
|
|
96843
97397
|
if (spawned.port !== port) {
|
|
96844
97398
|
console.error(
|
|
96845
|
-
|
|
97399
|
+
import_picocolors34.default.red(
|
|
96846
97400
|
`Ownership record port (${spawned.port}) does not match host URL port (${port}). Record may be stale.`
|
|
96847
97401
|
)
|
|
96848
97402
|
);
|
|
@@ -96850,7 +97404,7 @@ async function stopHost(root, hostUrl) {
|
|
|
96850
97404
|
}
|
|
96851
97405
|
if (spawned.root !== root) {
|
|
96852
97406
|
console.error(
|
|
96853
|
-
|
|
97407
|
+
import_picocolors34.default.red(
|
|
96854
97408
|
`Ownership record root (${spawned.root}) does not match resolved root (${root}). Record may be stale.`
|
|
96855
97409
|
)
|
|
96856
97410
|
);
|
|
@@ -96858,9 +97412,9 @@ async function stopHost(root, hostUrl) {
|
|
|
96858
97412
|
}
|
|
96859
97413
|
const info = await getProcessInfo(spawned.pid);
|
|
96860
97414
|
if (info === null) {
|
|
96861
|
-
console.log(
|
|
97415
|
+
console.log(import_picocolors34.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
|
|
96862
97416
|
try {
|
|
96863
|
-
await unlinkAsync((0,
|
|
97417
|
+
await unlinkAsync((0, import_node_path18.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
|
|
96864
97418
|
} catch {
|
|
96865
97419
|
}
|
|
96866
97420
|
return 0;
|
|
@@ -96871,7 +97425,7 @@ async function stopHost(root, hostUrl) {
|
|
|
96871
97425
|
);
|
|
96872
97426
|
if (!hostPortRe.test(info.args)) {
|
|
96873
97427
|
console.error(
|
|
96874
|
-
|
|
97428
|
+
import_picocolors34.default.red(
|
|
96875
97429
|
`Pid ${spawned.pid} args do not match "horus-source host --port ${portStr}". Got: "${info.args.slice(0, 120)}". Aborting for safety.`
|
|
96876
97430
|
)
|
|
96877
97431
|
);
|
|
@@ -96880,12 +97434,12 @@ async function stopHost(root, hostUrl) {
|
|
|
96880
97434
|
const startTs = new Date(spawned.startedAt).getTime();
|
|
96881
97435
|
const recordedAgeS = Math.round((Date.now() - startTs) / 1e3);
|
|
96882
97436
|
if (!Number.isFinite(info.etimeSeconds)) {
|
|
96883
|
-
console.error(
|
|
97437
|
+
console.error(import_picocolors34.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
|
|
96884
97438
|
return 1;
|
|
96885
97439
|
}
|
|
96886
97440
|
if (Math.abs(info.etimeSeconds - recordedAgeS) > START_TIME_TOLERANCE_S) {
|
|
96887
97441
|
console.error(
|
|
96888
|
-
|
|
97442
|
+
import_picocolors34.default.red(
|
|
96889
97443
|
`Pid ${spawned.pid} age mismatch: record says ~${recordedAgeS}s, process reports ${info.etimeSeconds}s elapsed. Possible PID reuse \u2014 aborting for safety.`
|
|
96890
97444
|
)
|
|
96891
97445
|
);
|
|
@@ -96897,9 +97451,9 @@ async function stopHost(root, hostUrl) {
|
|
|
96897
97451
|
signaled = true;
|
|
96898
97452
|
} catch (err) {
|
|
96899
97453
|
if (err.code === "ESRCH") {
|
|
96900
|
-
console.log(
|
|
97454
|
+
console.log(import_picocolors34.default.dim(`Process pid ${spawned.pid} exited before signal \u2014 already stopped.`));
|
|
96901
97455
|
} else {
|
|
96902
|
-
console.error(
|
|
97456
|
+
console.error(import_picocolors34.default.red(`Failed to signal pid ${spawned.pid}: ${err.message}`));
|
|
96903
97457
|
return 1;
|
|
96904
97458
|
}
|
|
96905
97459
|
}
|
|
@@ -96915,18 +97469,18 @@ async function stopHost(root, hostUrl) {
|
|
|
96915
97469
|
}
|
|
96916
97470
|
if (!exited) {
|
|
96917
97471
|
console.error(
|
|
96918
|
-
|
|
97472
|
+
import_picocolors34.default.red(
|
|
96919
97473
|
`Host pid ${spawned.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`
|
|
96920
97474
|
)
|
|
96921
97475
|
);
|
|
96922
97476
|
return 1;
|
|
96923
97477
|
}
|
|
96924
97478
|
console.log(
|
|
96925
|
-
`${
|
|
97479
|
+
`${import_picocolors34.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors34.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
|
|
96926
97480
|
);
|
|
96927
97481
|
}
|
|
96928
97482
|
try {
|
|
96929
|
-
await unlinkAsync((0,
|
|
97483
|
+
await unlinkAsync((0, import_node_path18.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
|
|
96930
97484
|
} catch {
|
|
96931
97485
|
}
|
|
96932
97486
|
return 0;
|
|
@@ -96935,7 +97489,7 @@ async function stopAll() {
|
|
|
96935
97489
|
const registry2 = readRegistry();
|
|
96936
97490
|
const projects2 = Object.entries(registry2.projects);
|
|
96937
97491
|
if (projects2.length === 0) {
|
|
96938
|
-
console.log(
|
|
97492
|
+
console.log(import_picocolors34.default.dim("No registered projects."));
|
|
96939
97493
|
return 0;
|
|
96940
97494
|
}
|
|
96941
97495
|
let stopped = 0;
|
|
@@ -96945,17 +97499,17 @@ async function stopAll() {
|
|
|
96945
97499
|
if (!hostUrl) continue;
|
|
96946
97500
|
const alive = await isHostHealthy(hostUrl);
|
|
96947
97501
|
if (!alive) continue;
|
|
96948
|
-
console.log(` Stopping ${
|
|
97502
|
+
console.log(` Stopping ${import_picocolors34.default.bold(name)} ${import_picocolors34.default.dim(`(${hostUrl})`)}`);
|
|
96949
97503
|
const code = await stopHost(entry2.root, hostUrl);
|
|
96950
97504
|
if (code === 0) stopped++;
|
|
96951
97505
|
else failed++;
|
|
96952
97506
|
}
|
|
96953
97507
|
if (stopped === 0 && failed === 0) {
|
|
96954
|
-
console.log(
|
|
97508
|
+
console.log(import_picocolors34.default.dim("No running source-intelligence hosts found."));
|
|
96955
97509
|
} else {
|
|
96956
97510
|
console.log(
|
|
96957
97511
|
`
|
|
96958
|
-
Stopped ${stopped} host(s)${failed > 0 ?
|
|
97512
|
+
Stopped ${stopped} host(s)${failed > 0 ? import_picocolors34.default.red(`, ${failed} failed`) : ""}.`
|
|
96959
97513
|
);
|
|
96960
97514
|
}
|
|
96961
97515
|
return failed > 0 ? 1 : 0;
|
|
@@ -97007,12 +97561,12 @@ function extractPort(hostUrl) {
|
|
|
97007
97561
|
|
|
97008
97562
|
// ../../packages/cli/src/commands/hosts.ts
|
|
97009
97563
|
init_cjs_shims();
|
|
97010
|
-
var
|
|
97564
|
+
var import_picocolors35 = __toESM(require_picocolors(), 1);
|
|
97011
97565
|
async function runHosts() {
|
|
97012
97566
|
const registry2 = readRegistry();
|
|
97013
97567
|
const projects2 = Object.entries(registry2.projects);
|
|
97014
97568
|
if (projects2.length === 0) {
|
|
97015
|
-
console.log(
|
|
97569
|
+
console.log(import_picocolors35.default.dim("No registered projects. Run `horus index` in a repo first."));
|
|
97016
97570
|
return 0;
|
|
97017
97571
|
}
|
|
97018
97572
|
const rows = [];
|
|
@@ -97029,29 +97583,29 @@ async function runHosts() {
|
|
|
97029
97583
|
});
|
|
97030
97584
|
const anyHost = rows.some((r) => r.hostUrl !== null);
|
|
97031
97585
|
if (!anyHost) {
|
|
97032
|
-
console.log(
|
|
97586
|
+
console.log(import_picocolors35.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
|
|
97033
97587
|
return 0;
|
|
97034
97588
|
}
|
|
97035
97589
|
console.log("");
|
|
97036
97590
|
for (const row of rows) {
|
|
97037
97591
|
if (row.hostUrl === null) continue;
|
|
97038
|
-
const status = row.healthy ?
|
|
97592
|
+
const status = row.healthy ? import_picocolors35.default.green("\u25CF running") : import_picocolors35.default.red("\u25CF stopped");
|
|
97039
97593
|
const port = extractPort2(row.hostUrl) ?? "?";
|
|
97040
97594
|
console.log(
|
|
97041
|
-
` ${status} ${
|
|
97595
|
+
` ${status} ${import_picocolors35.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors35.default.dim(row.root)}`
|
|
97042
97596
|
);
|
|
97043
97597
|
}
|
|
97044
97598
|
console.log("");
|
|
97045
97599
|
const noHost = rows.filter((r) => r.hostUrl === null);
|
|
97046
97600
|
if (noHost.length > 0) {
|
|
97047
97601
|
for (const row of noHost) {
|
|
97048
|
-
console.log(` ${
|
|
97602
|
+
console.log(` ${import_picocolors35.default.dim("\u25CB no host")} ${import_picocolors35.default.dim(row.name)}`);
|
|
97049
97603
|
}
|
|
97050
97604
|
console.log("");
|
|
97051
97605
|
}
|
|
97052
97606
|
const running = rows.filter((r) => r.healthy).length;
|
|
97053
97607
|
console.log(
|
|
97054
|
-
|
|
97608
|
+
import_picocolors35.default.dim(
|
|
97055
97609
|
`${running} running \xB7 horus stop to reap \xB7 horus stop --all to stop everything`
|
|
97056
97610
|
)
|
|
97057
97611
|
);
|
|
@@ -97068,12 +97622,12 @@ function extractPort2(hostUrl) {
|
|
|
97068
97622
|
|
|
97069
97623
|
// ../../packages/cli/src/commands/doctor.ts
|
|
97070
97624
|
init_cjs_shims();
|
|
97071
|
-
var
|
|
97625
|
+
var import_picocolors36 = __toESM(require_picocolors(), 1);
|
|
97072
97626
|
var DEFAULT_DB_URL4 = "postgresql://horus:horus@localhost:5433/horus";
|
|
97073
97627
|
function mark2(status) {
|
|
97074
|
-
if (status === "pass") return
|
|
97075
|
-
if (status === "warn") return
|
|
97076
|
-
return
|
|
97628
|
+
if (status === "pass") return import_picocolors36.default.green("\u2713");
|
|
97629
|
+
if (status === "warn") return import_picocolors36.default.yellow("~");
|
|
97630
|
+
return import_picocolors36.default.red("\u2717");
|
|
97077
97631
|
}
|
|
97078
97632
|
async function runDoctor(opts) {
|
|
97079
97633
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -97300,11 +97854,11 @@ async function runDoctor(opts) {
|
|
|
97300
97854
|
write(JSON.stringify(output, null, 2));
|
|
97301
97855
|
return hasFailure ? 1 : 0;
|
|
97302
97856
|
}
|
|
97303
|
-
write(
|
|
97857
|
+
write(import_picocolors36.default.bold("\nHorus readiness check\n"));
|
|
97304
97858
|
for (const check2 of checks) {
|
|
97305
|
-
write(` ${mark2(check2.status)} ${
|
|
97859
|
+
write(` ${mark2(check2.status)} ${import_picocolors36.default.bold(check2.label.padEnd(26))} ${import_picocolors36.default.dim(check2.detail)}`);
|
|
97306
97860
|
if (check2.next) {
|
|
97307
|
-
write(` ${
|
|
97861
|
+
write(` ${import_picocolors36.default.dim("\u2192 " + check2.next)}`);
|
|
97308
97862
|
}
|
|
97309
97863
|
}
|
|
97310
97864
|
write("");
|
|
@@ -97314,16 +97868,16 @@ async function runDoctor(opts) {
|
|
|
97314
97868
|
// ../../packages/cli/src/commands/providers-doctor.ts
|
|
97315
97869
|
init_cjs_shims();
|
|
97316
97870
|
var import_node_child_process8 = require("child_process");
|
|
97317
|
-
var
|
|
97871
|
+
var import_picocolors37 = __toESM(require_picocolors(), 1);
|
|
97318
97872
|
function statusMark(status) {
|
|
97319
|
-
if (status === "ready") return
|
|
97320
|
-
if (status === "installed") return
|
|
97321
|
-
return
|
|
97873
|
+
if (status === "ready") return import_picocolors37.default.green("\u2713");
|
|
97874
|
+
if (status === "installed") return import_picocolors37.default.yellow("~");
|
|
97875
|
+
return import_picocolors37.default.red("\u2717");
|
|
97322
97876
|
}
|
|
97323
97877
|
function statusLabel(status) {
|
|
97324
|
-
if (status === "ready") return
|
|
97325
|
-
if (status === "installed") return
|
|
97326
|
-
return
|
|
97878
|
+
if (status === "ready") return import_picocolors37.default.green("ready");
|
|
97879
|
+
if (status === "installed") return import_picocolors37.default.yellow("installed (not configured)");
|
|
97880
|
+
return import_picocolors37.default.dim("not found on PATH");
|
|
97327
97881
|
}
|
|
97328
97882
|
function detectBinary(id) {
|
|
97329
97883
|
const result = (0, import_node_child_process8.spawnSync)(id, ["--version"], { stdio: "pipe", timeout: 2e3 });
|
|
@@ -97340,7 +97894,7 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97340
97894
|
const registry2 = opts?.registry ?? DEFAULT_LOCAL_PROVIDER_REGISTRY;
|
|
97341
97895
|
const write = opts?.write ?? ((line2) => console.log(line2));
|
|
97342
97896
|
const results = buildProviderResults(registry2, opts?._detect);
|
|
97343
|
-
write(
|
|
97897
|
+
write(import_picocolors37.default.bold("\nLocal AI providers\n"));
|
|
97344
97898
|
for (const result of results) {
|
|
97345
97899
|
const descriptor = registry2.get(result.id);
|
|
97346
97900
|
const name = descriptor?.displayName ?? result.id;
|
|
@@ -97348,11 +97902,11 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97348
97902
|
` ${statusMark(result.status)} ${result.id.padEnd(8)} ${name.padEnd(22)} ${statusLabel(result.status)}`
|
|
97349
97903
|
);
|
|
97350
97904
|
if (result.status !== "ready" && result.detail) {
|
|
97351
|
-
write(` ${
|
|
97905
|
+
write(` ${import_picocolors37.default.dim("\u2192 " + result.detail)}`);
|
|
97352
97906
|
}
|
|
97353
97907
|
}
|
|
97354
97908
|
write("");
|
|
97355
|
-
write(
|
|
97909
|
+
write(import_picocolors37.default.bold("Cloud AI providers\n"));
|
|
97356
97910
|
let source = null;
|
|
97357
97911
|
if (opts?._anthropicKey !== void 0) {
|
|
97358
97912
|
source = opts._anthropicKey ? "env" : null;
|
|
@@ -97367,18 +97921,18 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97367
97921
|
}
|
|
97368
97922
|
if (source === "config") {
|
|
97369
97923
|
write(
|
|
97370
|
-
` ${
|
|
97924
|
+
` ${import_picocolors37.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.green("configured (local .horus config)")}`
|
|
97371
97925
|
);
|
|
97372
97926
|
} else if (source === "env") {
|
|
97373
97927
|
write(
|
|
97374
|
-
` ${
|
|
97928
|
+
` ${import_picocolors37.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.green("configured (ANTHROPIC_API_KEY env)")}`
|
|
97375
97929
|
);
|
|
97376
97930
|
} else {
|
|
97377
97931
|
write(
|
|
97378
|
-
` ${
|
|
97932
|
+
` ${import_picocolors37.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.dim("not configured")}`
|
|
97379
97933
|
);
|
|
97380
97934
|
write(
|
|
97381
|
-
` ${
|
|
97935
|
+
` ${import_picocolors37.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
|
|
97382
97936
|
);
|
|
97383
97937
|
}
|
|
97384
97938
|
write("");
|
|
@@ -97387,9 +97941,9 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97387
97941
|
|
|
97388
97942
|
// ../../packages/cli/src/commands/generate-config.ts
|
|
97389
97943
|
init_cjs_shims();
|
|
97390
|
-
var
|
|
97391
|
-
var
|
|
97392
|
-
var
|
|
97944
|
+
var import_node_fs17 = require("fs");
|
|
97945
|
+
var import_node_path19 = require("path");
|
|
97946
|
+
var import_picocolors38 = __toESM(require_picocolors(), 1);
|
|
97393
97947
|
function configTemplate(name, repoPath) {
|
|
97394
97948
|
return `export default {
|
|
97395
97949
|
database: {
|
|
@@ -97437,39 +97991,39 @@ async function runGenerateConfig(opts) {
|
|
|
97437
97991
|
const defaults = localConfigPath2 != null ? projectDefaults(localConfigPath2) : null;
|
|
97438
97992
|
const hasLocalConfig = defaults != null;
|
|
97439
97993
|
const defaultOut = hasLocalConfig ? "horus.config.example.js" : "horus.config.js";
|
|
97440
|
-
const outPath = (0,
|
|
97994
|
+
const outPath = (0, import_node_path19.resolve)(cwd, opts.out ?? defaultOut);
|
|
97441
97995
|
const name = opts.name ?? defaults?.name ?? "my-project";
|
|
97442
97996
|
const repoPath = opts.repo ?? defaults?.repoPath ?? `/path/to/${name}`;
|
|
97443
|
-
if ((0,
|
|
97444
|
-
log(`${
|
|
97445
|
-
log(
|
|
97997
|
+
if ((0, import_node_fs17.existsSync)(outPath) && !opts.force) {
|
|
97998
|
+
log(`${import_picocolors38.default.red("\u2717")} ${outPath} already exists`);
|
|
97999
|
+
log(import_picocolors38.default.dim(" pass --force to overwrite"));
|
|
97446
98000
|
return 1;
|
|
97447
98001
|
}
|
|
97448
98002
|
try {
|
|
97449
|
-
(0,
|
|
97450
|
-
(0,
|
|
98003
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path19.dirname)(outPath), { recursive: true });
|
|
98004
|
+
(0, import_node_fs17.writeFileSync)(outPath, configTemplate(name, repoPath), "utf8");
|
|
97451
98005
|
} catch (err) {
|
|
97452
|
-
log(`${
|
|
98006
|
+
log(`${import_picocolors38.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
|
|
97453
98007
|
return 1;
|
|
97454
98008
|
}
|
|
97455
|
-
log(`${
|
|
97456
|
-
log(
|
|
97457
|
-
log(
|
|
98009
|
+
log(`${import_picocolors38.default.green("\u2713")} Created ${outPath}`);
|
|
98010
|
+
log(import_picocolors38.default.dim(` project: ${name}`));
|
|
98011
|
+
log(import_picocolors38.default.dim(` repo: ${repoPath}`));
|
|
97458
98012
|
if (hasLocalConfig && localConfigPath2 != null) {
|
|
97459
98013
|
log("");
|
|
97460
|
-
log(
|
|
98014
|
+
log(import_picocolors38.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
|
|
97461
98015
|
log(" \u2022 .horus/config.json \u2014 project config used by `horus investigate` from this repo");
|
|
97462
98016
|
log(" \u2022 horus.config.js \u2014 standalone/global config used with `horus doctor --config <path>`");
|
|
97463
|
-
log(
|
|
98017
|
+
log(import_picocolors38.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
|
|
97464
98018
|
} else {
|
|
97465
|
-
log(
|
|
98019
|
+
log(import_picocolors38.default.dim(` next: horus doctor --config ${outPath}`));
|
|
97466
98020
|
}
|
|
97467
98021
|
return 0;
|
|
97468
98022
|
}
|
|
97469
98023
|
|
|
97470
98024
|
// ../../packages/cli/src/commands/readiness.ts
|
|
97471
98025
|
init_cjs_shims();
|
|
97472
|
-
var
|
|
98026
|
+
var import_picocolors39 = __toESM(require_picocolors(), 1);
|
|
97473
98027
|
var READINESS_AI_CONTRACT = `Provide a clearly separated AI readiness summary with:
|
|
97474
98028
|
|
|
97475
98029
|
Overall assessment
|
|
@@ -97492,9 +98046,9 @@ Confidence / gaps
|
|
|
97492
98046
|
- Which checks Horus could verify vs. which require manual confirmation`;
|
|
97493
98047
|
var DEFAULT_DB_URL5 = "postgresql://horus:horus@localhost:5433/horus";
|
|
97494
98048
|
function mark3(status) {
|
|
97495
|
-
if (status === "pass") return
|
|
97496
|
-
if (status === "warn") return
|
|
97497
|
-
return
|
|
98049
|
+
if (status === "pass") return import_picocolors39.default.green("\u2713");
|
|
98050
|
+
if (status === "warn") return import_picocolors39.default.yellow("~");
|
|
98051
|
+
return import_picocolors39.default.red("\u2717");
|
|
97498
98052
|
}
|
|
97499
98053
|
async function runReadiness(opts) {
|
|
97500
98054
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -97649,20 +98203,20 @@ async function runReadiness(opts) {
|
|
|
97649
98203
|
}
|
|
97650
98204
|
const blockingChecks = checks.filter((c) => c.blocking);
|
|
97651
98205
|
const optionalChecks = checks.filter((c) => !c.blocking);
|
|
97652
|
-
write(
|
|
97653
|
-
write(
|
|
98206
|
+
write(import_picocolors39.default.bold("\nHorus release readiness\n"));
|
|
98207
|
+
write(import_picocolors39.default.bold(" Blocking"));
|
|
97654
98208
|
for (const check2 of blockingChecks) {
|
|
97655
|
-
write(` ${mark3(check2.status)} ${
|
|
98209
|
+
write(` ${mark3(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(22))} ${import_picocolors39.default.dim(check2.detail)}`);
|
|
97656
98210
|
if (check2.next) {
|
|
97657
|
-
write(` ${
|
|
98211
|
+
write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
|
|
97658
98212
|
}
|
|
97659
98213
|
}
|
|
97660
98214
|
write("");
|
|
97661
|
-
write(
|
|
98215
|
+
write(import_picocolors39.default.bold(" Optional"));
|
|
97662
98216
|
for (const check2 of optionalChecks) {
|
|
97663
|
-
write(` ${mark3(check2.status)} ${
|
|
98217
|
+
write(` ${mark3(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(22))} ${import_picocolors39.default.dim(check2.detail)}`);
|
|
97664
98218
|
if (check2.next) {
|
|
97665
|
-
write(` ${
|
|
98219
|
+
write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
|
|
97666
98220
|
}
|
|
97667
98221
|
}
|
|
97668
98222
|
write("");
|
|
@@ -97670,21 +98224,21 @@ async function runReadiness(opts) {
|
|
|
97670
98224
|
const optionalWarns = optionalChecks.filter((c) => c.status === "warn").length;
|
|
97671
98225
|
if (blockingFails.length === 0) {
|
|
97672
98226
|
if (optionalWarns === 0) {
|
|
97673
|
-
write(
|
|
98227
|
+
write(import_picocolors39.default.green(" Ready for demo/release."));
|
|
97674
98228
|
} else {
|
|
97675
98229
|
write(
|
|
97676
|
-
|
|
98230
|
+
import_picocolors39.default.yellow(
|
|
97677
98231
|
` Ready for a basic demo. ${optionalWarns} optional item(s) not configured \u2014 investigation evidence will be limited.`
|
|
97678
98232
|
)
|
|
97679
98233
|
);
|
|
97680
98234
|
}
|
|
97681
98235
|
} else {
|
|
97682
98236
|
write(
|
|
97683
|
-
|
|
98237
|
+
import_picocolors39.default.red(
|
|
97684
98238
|
` Not ready. ${blockingFails.length} blocking item(s) must be resolved before demo/release.`
|
|
97685
98239
|
)
|
|
97686
98240
|
);
|
|
97687
|
-
write(
|
|
98241
|
+
write(import_picocolors39.default.dim(" Re-run `horus readiness` after resolving the items above."));
|
|
97688
98242
|
}
|
|
97689
98243
|
write("");
|
|
97690
98244
|
if (opts?.ai) {
|
|
@@ -97699,7 +98253,7 @@ async function runReadiness(opts) {
|
|
|
97699
98253
|
});
|
|
97700
98254
|
console.log("\n" + renderInterpretation(result));
|
|
97701
98255
|
if (!result.ok) {
|
|
97702
|
-
console.error(
|
|
98256
|
+
console.error(import_picocolors39.default.yellow(`[ai] ${result.warning}`));
|
|
97703
98257
|
}
|
|
97704
98258
|
}
|
|
97705
98259
|
return blockingFails.length > 0 ? 1 : 0;
|
|
@@ -97707,33 +98261,33 @@ async function runReadiness(opts) {
|
|
|
97707
98261
|
|
|
97708
98262
|
// ../../packages/cli/src/commands/skill.ts
|
|
97709
98263
|
init_cjs_shims();
|
|
97710
|
-
var
|
|
97711
|
-
var
|
|
97712
|
-
var
|
|
97713
|
-
var
|
|
97714
|
-
var
|
|
98264
|
+
var import_node_fs18 = require("fs");
|
|
98265
|
+
var import_node_path20 = require("path");
|
|
98266
|
+
var import_node_os4 = require("os");
|
|
98267
|
+
var import_promises2 = require("readline/promises");
|
|
98268
|
+
var import_picocolors40 = __toESM(require_picocolors(), 1);
|
|
97715
98269
|
var SUPPORTED_TARGETS = ["claude", "codex", "gemini", "cursor", "generic"];
|
|
97716
98270
|
function isValidTarget(target) {
|
|
97717
98271
|
return SUPPORTED_TARGETS.includes(target);
|
|
97718
98272
|
}
|
|
97719
98273
|
function getLegacyClaudeSkillPath(opts) {
|
|
97720
|
-
const base2 = opts.global ? (0,
|
|
97721
|
-
return (0,
|
|
98274
|
+
const base2 = opts.global ? (0, import_node_os4.homedir)() : opts.cwd ?? process.cwd();
|
|
98275
|
+
return (0, import_node_path20.join)(base2, ".claude", "skills", "horus.md");
|
|
97722
98276
|
}
|
|
97723
98277
|
function getSkillInstallPath(target, opts) {
|
|
97724
|
-
const base2 = opts.global ? (0,
|
|
98278
|
+
const base2 = opts.global ? (0, import_node_os4.homedir)() : opts.cwd ?? process.cwd();
|
|
97725
98279
|
switch (target) {
|
|
97726
98280
|
case "claude":
|
|
97727
|
-
return (0,
|
|
98281
|
+
return (0, import_node_path20.join)(base2, ".claude", "skills", "horus", "SKILL.md");
|
|
97728
98282
|
case "gemini":
|
|
97729
|
-
return opts.global ? (0,
|
|
98283
|
+
return opts.global ? (0, import_node_path20.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path20.join)(base2, "GEMINI.md");
|
|
97730
98284
|
case "cursor":
|
|
97731
|
-
return opts.global ? (0,
|
|
98285
|
+
return opts.global ? (0, import_node_path20.join)(base2, ".cursorrules") : (0, import_node_path20.join)(base2, ".cursor", "rules", "horus.mdc");
|
|
97732
98286
|
case "codex":
|
|
97733
|
-
return opts.global ? (0,
|
|
98287
|
+
return opts.global ? (0, import_node_path20.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path20.join)(base2, "AGENTS.md");
|
|
97734
98288
|
case "generic":
|
|
97735
98289
|
default:
|
|
97736
|
-
return (0,
|
|
98290
|
+
return (0, import_node_path20.join)(base2, ".horus", "skills", `horus-${target}.md`);
|
|
97737
98291
|
}
|
|
97738
98292
|
}
|
|
97739
98293
|
var BASE_SKILL = `# Horus Grounded Investigation Skill
|
|
@@ -98018,7 +98572,7 @@ async function confirm(message) {
|
|
|
98018
98572
|
if (!process.stdin.isTTY) {
|
|
98019
98573
|
return false;
|
|
98020
98574
|
}
|
|
98021
|
-
const rl = (0,
|
|
98575
|
+
const rl = (0, import_promises2.createInterface)({ input: process.stdin, output: process.stdout });
|
|
98022
98576
|
try {
|
|
98023
98577
|
const answer = await rl.question(`${message} [y/N] `);
|
|
98024
98578
|
return /^y(es)?$/i.test(answer.trim());
|
|
@@ -98029,66 +98583,66 @@ async function confirm(message) {
|
|
|
98029
98583
|
async function runSkillInstall(target, opts) {
|
|
98030
98584
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
98031
98585
|
if (!isValidTarget(target)) {
|
|
98032
|
-
log(`${
|
|
98033
|
-
log(
|
|
98586
|
+
log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
|
|
98587
|
+
log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
98034
98588
|
return 1;
|
|
98035
98589
|
}
|
|
98036
98590
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
98037
98591
|
const content = generateSkillContent(target);
|
|
98038
98592
|
if (target === "claude") {
|
|
98039
98593
|
const legacyPath = getLegacyClaudeSkillPath({ global: opts.global, cwd: opts.cwd });
|
|
98040
|
-
if ((0,
|
|
98594
|
+
if ((0, import_node_fs18.existsSync)(legacyPath)) {
|
|
98041
98595
|
if (!opts.force) {
|
|
98042
98596
|
const confirmed = await (opts.confirm ?? confirm)(
|
|
98043
98597
|
`Found old flat skill file at ${legacyPath}. Replace it with the new directory layout?`
|
|
98044
98598
|
);
|
|
98045
98599
|
if (!confirmed) {
|
|
98046
|
-
log(`${
|
|
98047
|
-
log(
|
|
98600
|
+
log(`${import_picocolors40.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
|
|
98601
|
+
log(import_picocolors40.default.dim(" Pass --force to replace without prompting."));
|
|
98048
98602
|
return 1;
|
|
98049
98603
|
}
|
|
98050
98604
|
}
|
|
98051
98605
|
try {
|
|
98052
|
-
(0,
|
|
98053
|
-
log(`${
|
|
98606
|
+
(0, import_node_fs18.rmSync)(legacyPath);
|
|
98607
|
+
log(`${import_picocolors40.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
|
|
98054
98608
|
} catch (err) {
|
|
98055
|
-
log(`${
|
|
98609
|
+
log(`${import_picocolors40.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err.message}`);
|
|
98056
98610
|
return 1;
|
|
98057
98611
|
}
|
|
98058
98612
|
}
|
|
98059
98613
|
}
|
|
98060
|
-
if ((0,
|
|
98061
|
-
log(`${
|
|
98062
|
-
log(
|
|
98614
|
+
if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
|
|
98615
|
+
log(`${import_picocolors40.default.red("\u2717")} ${outPath} already exists`);
|
|
98616
|
+
log(import_picocolors40.default.dim(" pass --force to overwrite"));
|
|
98063
98617
|
return 1;
|
|
98064
98618
|
}
|
|
98065
98619
|
try {
|
|
98066
|
-
(0,
|
|
98067
|
-
(0,
|
|
98620
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
|
|
98621
|
+
(0, import_node_fs18.writeFileSync)(outPath, content, "utf8");
|
|
98068
98622
|
} catch (err) {
|
|
98069
|
-
log(`${
|
|
98623
|
+
log(`${import_picocolors40.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
|
|
98070
98624
|
return 1;
|
|
98071
98625
|
}
|
|
98072
|
-
log(`${
|
|
98626
|
+
log(`${import_picocolors40.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
|
|
98073
98627
|
if (target === "claude") {
|
|
98074
|
-
log(
|
|
98075
|
-
log(
|
|
98628
|
+
log(import_picocolors40.default.dim(" Claude Code will load this skill automatically."));
|
|
98629
|
+
log(import_picocolors40.default.dim(" Invoke it with: /horus"));
|
|
98076
98630
|
} else if (target === "gemini") {
|
|
98077
|
-
log(
|
|
98631
|
+
log(import_picocolors40.default.dim(" Gemini CLI will load this context file automatically."));
|
|
98078
98632
|
} else if (target === "cursor") {
|
|
98079
|
-
log(
|
|
98633
|
+
log(import_picocolors40.default.dim(" Cursor will load this rule automatically."));
|
|
98080
98634
|
} else if (target === "codex") {
|
|
98081
|
-
log(
|
|
98635
|
+
log(import_picocolors40.default.dim(" Codex CLI will load this file automatically."));
|
|
98082
98636
|
} else {
|
|
98083
|
-
log(
|
|
98637
|
+
log(import_picocolors40.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
|
|
98084
98638
|
}
|
|
98085
98639
|
return 0;
|
|
98086
98640
|
}
|
|
98087
98641
|
async function runSkillPrint(target, opts) {
|
|
98088
98642
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
98089
98643
|
if (!isValidTarget(target)) {
|
|
98090
|
-
log(`${
|
|
98091
|
-
log(
|
|
98644
|
+
log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
|
|
98645
|
+
log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
98092
98646
|
return 1;
|
|
98093
98647
|
}
|
|
98094
98648
|
process.stdout.write(generateSkillContent(target));
|
|
@@ -98097,8 +98651,8 @@ async function runSkillPrint(target, opts) {
|
|
|
98097
98651
|
async function runSkillPath(target, opts) {
|
|
98098
98652
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
98099
98653
|
if (!isValidTarget(target)) {
|
|
98100
|
-
log(`${
|
|
98101
|
-
log(
|
|
98654
|
+
log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
|
|
98655
|
+
log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
98102
98656
|
return 1;
|
|
98103
98657
|
}
|
|
98104
98658
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
@@ -98108,11 +98662,11 @@ async function runSkillPath(target, opts) {
|
|
|
98108
98662
|
|
|
98109
98663
|
// ../../packages/cli/src/commands/update.ts
|
|
98110
98664
|
init_cjs_shims();
|
|
98111
|
-
var
|
|
98112
|
-
var
|
|
98113
|
-
var
|
|
98114
|
-
var
|
|
98115
|
-
var
|
|
98665
|
+
var import_node_fs19 = require("fs");
|
|
98666
|
+
var import_node_path21 = require("path");
|
|
98667
|
+
var import_node_os5 = require("os");
|
|
98668
|
+
var import_node_crypto7 = require("crypto");
|
|
98669
|
+
var import_picocolors41 = __toESM(require_picocolors(), 1);
|
|
98116
98670
|
var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
|
|
98117
98671
|
function parseVersion(v) {
|
|
98118
98672
|
const m = v.replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
@@ -98137,13 +98691,13 @@ async function fetchLatestRelease(_fetch = fetch) {
|
|
|
98137
98691
|
return res.json();
|
|
98138
98692
|
}
|
|
98139
98693
|
function sha256hex(buf) {
|
|
98140
|
-
return (0,
|
|
98694
|
+
return (0, import_node_crypto7.createHash)("sha256").update(buf).digest("hex");
|
|
98141
98695
|
}
|
|
98142
98696
|
function resolveBinaryPath() {
|
|
98143
98697
|
const raw = process.argv[1];
|
|
98144
98698
|
if (!raw) throw new Error("Cannot determine binary path from process.argv[1]");
|
|
98145
98699
|
try {
|
|
98146
|
-
return (0,
|
|
98700
|
+
return (0, import_node_fs19.realpathSync)(raw);
|
|
98147
98701
|
} catch {
|
|
98148
98702
|
return raw;
|
|
98149
98703
|
}
|
|
@@ -98153,33 +98707,33 @@ async function runUpdate(opts) {
|
|
|
98153
98707
|
const _fetch = opts._fetch ?? fetch;
|
|
98154
98708
|
const currentVersion = opts._currentVersion ?? HORUS_VERSION;
|
|
98155
98709
|
write(`
|
|
98156
|
-
${
|
|
98710
|
+
${import_picocolors41.default.bold("Horus update")}
|
|
98157
98711
|
`);
|
|
98158
|
-
write(` Current version: ${
|
|
98712
|
+
write(` Current version: ${import_picocolors41.default.cyan(currentVersion)}`);
|
|
98159
98713
|
let release;
|
|
98160
98714
|
try {
|
|
98161
98715
|
release = await fetchLatestRelease(_fetch);
|
|
98162
98716
|
} catch (e) {
|
|
98163
|
-
write(` ${
|
|
98717
|
+
write(` ${import_picocolors41.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
|
|
98164
98718
|
return 1;
|
|
98165
98719
|
}
|
|
98166
98720
|
const latest = release.tag_name.replace(/^v/, "");
|
|
98167
|
-
write(` Latest version: ${
|
|
98721
|
+
write(` Latest version: ${import_picocolors41.default.cyan(latest)}`);
|
|
98168
98722
|
const newer = isNewer(latest, currentVersion);
|
|
98169
98723
|
if (opts.check) {
|
|
98170
98724
|
if (newer) {
|
|
98171
98725
|
write(`
|
|
98172
|
-
${
|
|
98173
|
-
write(` Run ${
|
|
98726
|
+
${import_picocolors41.default.yellow("\u2192")} ${import_picocolors41.default.bold(latest)} is available.`);
|
|
98727
|
+
write(` Run ${import_picocolors41.default.bold("horus update")} to upgrade.`);
|
|
98174
98728
|
} else {
|
|
98175
98729
|
write(`
|
|
98176
|
-
${
|
|
98730
|
+
${import_picocolors41.default.green("\u2713")} Already on the latest version.`);
|
|
98177
98731
|
}
|
|
98178
98732
|
return 0;
|
|
98179
98733
|
}
|
|
98180
98734
|
if (!newer && !opts.force) {
|
|
98181
98735
|
write(`
|
|
98182
|
-
${
|
|
98736
|
+
${import_picocolors41.default.green("\u2713")} Already on the latest version.`);
|
|
98183
98737
|
return 0;
|
|
98184
98738
|
}
|
|
98185
98739
|
const tag = release.tag_name;
|
|
@@ -98188,8 +98742,8 @@ ${import_picocolors40.default.bold("Horus update")}
|
|
|
98188
98742
|
const binaryAsset = release.assets.find((a) => a.name === assetName);
|
|
98189
98743
|
const checksumAsset = release.assets.find((a) => a.name === checksumName);
|
|
98190
98744
|
if (!binaryAsset) {
|
|
98191
|
-
write(` ${
|
|
98192
|
-
write(` Fallback: ${
|
|
98745
|
+
write(` ${import_picocolors41.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
|
|
98746
|
+
write(` Fallback: ${import_picocolors41.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
|
|
98193
98747
|
return 1;
|
|
98194
98748
|
}
|
|
98195
98749
|
write(`
|
|
@@ -98199,7 +98753,7 @@ ${import_picocolors40.default.bold("Horus update")}
|
|
|
98199
98753
|
redirect: "follow"
|
|
98200
98754
|
});
|
|
98201
98755
|
if (!binRes.ok || !binRes.body) {
|
|
98202
|
-
write(` ${
|
|
98756
|
+
write(` ${import_picocolors41.default.red("\u2717")} Download failed: ${binRes.status}`);
|
|
98203
98757
|
return 1;
|
|
98204
98758
|
}
|
|
98205
98759
|
const binBuf = Buffer.from(await binRes.arrayBuffer());
|
|
@@ -98213,75 +98767,75 @@ ${import_picocolors40.default.bold("Horus update")}
|
|
|
98213
98767
|
const expectedHash = csText.trim().split(/\s+/)[0];
|
|
98214
98768
|
const actualHash = sha256hex(binBuf);
|
|
98215
98769
|
if (expectedHash !== actualHash) {
|
|
98216
|
-
write(` ${
|
|
98770
|
+
write(` ${import_picocolors41.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
|
|
98217
98771
|
write(` Expected: ${expectedHash}`);
|
|
98218
98772
|
write(` Got: ${actualHash}`);
|
|
98219
98773
|
return 1;
|
|
98220
98774
|
}
|
|
98221
|
-
write(` ${
|
|
98775
|
+
write(` ${import_picocolors41.default.green("\u2713")} Checksum verified.`);
|
|
98222
98776
|
}
|
|
98223
98777
|
}
|
|
98224
98778
|
let binaryPath;
|
|
98225
98779
|
try {
|
|
98226
98780
|
binaryPath = resolveBinaryPath();
|
|
98227
98781
|
} catch (e) {
|
|
98228
|
-
write(` ${
|
|
98782
|
+
write(` ${import_picocolors41.default.red("\u2717")} ${e.message}`);
|
|
98229
98783
|
return 1;
|
|
98230
98784
|
}
|
|
98231
|
-
if (!(0,
|
|
98232
|
-
write(` ${
|
|
98785
|
+
if (!(0, import_node_fs19.existsSync)(binaryPath)) {
|
|
98786
|
+
write(` ${import_picocolors41.default.red("\u2717")} Binary not found at ${binaryPath}`);
|
|
98233
98787
|
return 1;
|
|
98234
98788
|
}
|
|
98235
|
-
const tmpPath = (0,
|
|
98789
|
+
const tmpPath = (0, import_node_path21.join)((0, import_node_os5.tmpdir)(), `horus-update-${tag}-${process.pid}`);
|
|
98236
98790
|
const backupPath = `${binaryPath}.bak`;
|
|
98237
98791
|
try {
|
|
98238
|
-
(0,
|
|
98792
|
+
(0, import_node_fs19.writeFileSync)(tmpPath, binBuf, { mode: 493 });
|
|
98239
98793
|
} catch (e) {
|
|
98240
|
-
write(` ${
|
|
98794
|
+
write(` ${import_picocolors41.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
|
|
98241
98795
|
return 1;
|
|
98242
98796
|
}
|
|
98243
98797
|
try {
|
|
98244
|
-
(0,
|
|
98798
|
+
(0, import_node_fs19.renameSync)(binaryPath, backupPath);
|
|
98245
98799
|
try {
|
|
98246
|
-
(0,
|
|
98247
|
-
(0,
|
|
98800
|
+
(0, import_node_fs19.renameSync)(tmpPath, binaryPath);
|
|
98801
|
+
(0, import_node_fs19.chmodSync)(binaryPath, 493);
|
|
98248
98802
|
} catch (e) {
|
|
98249
98803
|
try {
|
|
98250
|
-
(0,
|
|
98804
|
+
(0, import_node_fs19.renameSync)(backupPath, binaryPath);
|
|
98251
98805
|
} catch {
|
|
98252
98806
|
}
|
|
98253
98807
|
throw e;
|
|
98254
98808
|
}
|
|
98255
98809
|
try {
|
|
98256
|
-
(0,
|
|
98810
|
+
(0, import_node_fs19.unlinkSync)(backupPath);
|
|
98257
98811
|
} catch {
|
|
98258
98812
|
}
|
|
98259
98813
|
} catch (e) {
|
|
98260
98814
|
try {
|
|
98261
|
-
(0,
|
|
98815
|
+
(0, import_node_fs19.unlinkSync)(tmpPath);
|
|
98262
98816
|
} catch {
|
|
98263
98817
|
}
|
|
98264
98818
|
const msg = e.message ?? String(e);
|
|
98265
98819
|
const needsSudo = e.code === "EACCES" || e.code === "EPERM";
|
|
98266
|
-
write(` ${
|
|
98820
|
+
write(` ${import_picocolors41.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
|
|
98267
98821
|
if (needsSudo) {
|
|
98268
98822
|
write(`
|
|
98269
98823
|
Try with sudo:`);
|
|
98270
98824
|
write(
|
|
98271
|
-
` ${
|
|
98825
|
+
` ${import_picocolors41.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
|
|
98272
98826
|
);
|
|
98273
98827
|
}
|
|
98274
98828
|
return 1;
|
|
98275
98829
|
}
|
|
98276
|
-
write(` ${
|
|
98277
|
-
write(` ${
|
|
98830
|
+
write(` ${import_picocolors41.default.green("\u2713")} Updated: ${import_picocolors41.default.bold(currentVersion)} \u2192 ${import_picocolors41.default.bold(latest)}`);
|
|
98831
|
+
write(` ${import_picocolors41.default.dim(binaryPath)}`);
|
|
98278
98832
|
return 0;
|
|
98279
98833
|
}
|
|
98280
98834
|
|
|
98281
98835
|
// ../../packages/cli/src/commands/login.ts
|
|
98282
98836
|
init_cjs_shims();
|
|
98283
98837
|
var import_node_child_process9 = require("child_process");
|
|
98284
|
-
var
|
|
98838
|
+
var import_picocolors42 = __toESM(require_picocolors(), 1);
|
|
98285
98839
|
var sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
98286
98840
|
function openBrowser(url) {
|
|
98287
98841
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
@@ -98306,19 +98860,19 @@ async function loginWithToken(apiBaseUrl, token) {
|
|
|
98306
98860
|
}
|
|
98307
98861
|
});
|
|
98308
98862
|
console.log(
|
|
98309
|
-
`${
|
|
98863
|
+
`${import_picocolors42.default.green("\u2713")} Logged in as ${import_picocolors42.default.bold(me.user.primaryEmail)}. Run ${import_picocolors42.default.bold("horus cloud link")} to connect this repo.`
|
|
98310
98864
|
);
|
|
98311
98865
|
return 0;
|
|
98312
98866
|
} catch (err) {
|
|
98313
98867
|
if (err instanceof CloudOfflineError) {
|
|
98314
|
-
console.error(
|
|
98868
|
+
console.error(import_picocolors42.default.red(err.message));
|
|
98315
98869
|
return 1;
|
|
98316
98870
|
}
|
|
98317
98871
|
if (err instanceof CloudError && err.status === 401) {
|
|
98318
|
-
console.error(
|
|
98872
|
+
console.error(import_picocolors42.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
|
|
98319
98873
|
return 1;
|
|
98320
98874
|
}
|
|
98321
|
-
console.error(
|
|
98875
|
+
console.error(import_picocolors42.default.red(`Login failed: ${err.message}`));
|
|
98322
98876
|
return 1;
|
|
98323
98877
|
}
|
|
98324
98878
|
}
|
|
@@ -98329,19 +98883,19 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
98329
98883
|
session = await client.startCliSession();
|
|
98330
98884
|
} catch (err) {
|
|
98331
98885
|
if (err instanceof CloudOfflineError) {
|
|
98332
|
-
console.error(
|
|
98886
|
+
console.error(import_picocolors42.default.red(err.message));
|
|
98333
98887
|
return 1;
|
|
98334
98888
|
}
|
|
98335
|
-
console.error(
|
|
98889
|
+
console.error(import_picocolors42.default.red(`Could not start login: ${err.message}`));
|
|
98336
98890
|
return 1;
|
|
98337
98891
|
}
|
|
98338
98892
|
console.log("");
|
|
98339
98893
|
console.log(` To sign in, open this URL in your browser:`);
|
|
98340
|
-
console.log(` ${
|
|
98894
|
+
console.log(` ${import_picocolors42.default.cyan(import_picocolors42.default.bold(session.verificationUri))}`);
|
|
98341
98895
|
console.log("");
|
|
98342
|
-
console.log(` and enter the code: ${
|
|
98896
|
+
console.log(` and enter the code: ${import_picocolors42.default.bold(session.userCode)}`);
|
|
98343
98897
|
console.log("");
|
|
98344
|
-
console.log(
|
|
98898
|
+
console.log(import_picocolors42.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
|
|
98345
98899
|
openBrowser(session.verificationUriComplete);
|
|
98346
98900
|
const deadline = Date.now() + session.expiresIn * 1e3;
|
|
98347
98901
|
let intervalMs = Math.max(session.interval, 1) * 1e3;
|
|
@@ -98352,7 +98906,7 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
98352
98906
|
poll = await client.pollCliSession(session.deviceCode);
|
|
98353
98907
|
} catch (err) {
|
|
98354
98908
|
if (err instanceof CloudOfflineError) {
|
|
98355
|
-
console.error(
|
|
98909
|
+
console.error(import_picocolors42.default.red(`
|
|
98356
98910
|
${err.message}`));
|
|
98357
98911
|
return 1;
|
|
98358
98912
|
}
|
|
@@ -98365,14 +98919,14 @@ ${err.message}`));
|
|
|
98365
98919
|
intervalMs += 2e3;
|
|
98366
98920
|
break;
|
|
98367
98921
|
case "denied":
|
|
98368
|
-
console.error(
|
|
98922
|
+
console.error(import_picocolors42.default.red("\nLogin was denied in the browser."));
|
|
98369
98923
|
return 1;
|
|
98370
98924
|
case "expired":
|
|
98371
|
-
console.error(
|
|
98925
|
+
console.error(import_picocolors42.default.red("\nThis login request expired. Run `horus login` again."));
|
|
98372
98926
|
return 1;
|
|
98373
98927
|
case "approved":
|
|
98374
98928
|
if (!poll.token || !poll.account) {
|
|
98375
|
-
console.error(
|
|
98929
|
+
console.error(import_picocolors42.default.red("\nLogin approved but no token was returned. Try again."));
|
|
98376
98930
|
return 1;
|
|
98377
98931
|
}
|
|
98378
98932
|
writeAuth({
|
|
@@ -98382,12 +98936,12 @@ ${err.message}`));
|
|
|
98382
98936
|
});
|
|
98383
98937
|
console.log(
|
|
98384
98938
|
`
|
|
98385
|
-
${
|
|
98939
|
+
${import_picocolors42.default.green("\u2713")} Logged in as ${import_picocolors42.default.bold(poll.account.email)}. Run ${import_picocolors42.default.bold("horus cloud link")} to connect this repo.`
|
|
98386
98940
|
);
|
|
98387
98941
|
return 0;
|
|
98388
98942
|
}
|
|
98389
98943
|
}
|
|
98390
|
-
console.error(
|
|
98944
|
+
console.error(import_picocolors42.default.red("\nTimed out waiting for approval. Run `horus login` again."));
|
|
98391
98945
|
return 1;
|
|
98392
98946
|
}
|
|
98393
98947
|
async function runLogin(opts = {}) {
|
|
@@ -98401,7 +98955,7 @@ async function runLogin(opts = {}) {
|
|
|
98401
98955
|
async function runLogout() {
|
|
98402
98956
|
const auth = readAuth();
|
|
98403
98957
|
if (!auth) {
|
|
98404
|
-
console.log(
|
|
98958
|
+
console.log(import_picocolors42.default.dim("Not logged in."));
|
|
98405
98959
|
return 0;
|
|
98406
98960
|
}
|
|
98407
98961
|
const session = authedClient();
|
|
@@ -98415,14 +98969,14 @@ async function runLogout() {
|
|
|
98415
98969
|
}
|
|
98416
98970
|
}
|
|
98417
98971
|
clearAuth();
|
|
98418
|
-
console.log(`${
|
|
98972
|
+
console.log(`${import_picocolors42.default.green("\u2713")} Logged out. Local mode still works.`);
|
|
98419
98973
|
return 0;
|
|
98420
98974
|
}
|
|
98421
98975
|
|
|
98422
98976
|
// ../../packages/cli/src/commands/cloud.ts
|
|
98423
98977
|
init_cjs_shims();
|
|
98424
|
-
var
|
|
98425
|
-
var
|
|
98978
|
+
var import_picocolors43 = __toESM(require_picocolors(), 1);
|
|
98979
|
+
var import_promises3 = require("readline/promises");
|
|
98426
98980
|
|
|
98427
98981
|
// ../../packages/cli/src/lib/cloud/git.ts
|
|
98428
98982
|
init_cjs_shims();
|
|
@@ -98466,7 +99020,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98466
99020
|
const root = repoRootOrCwd(opts.cwd);
|
|
98467
99021
|
const session = authedClient();
|
|
98468
99022
|
if (!session) {
|
|
98469
|
-
console.error(
|
|
99023
|
+
console.error(import_picocolors43.default.red(`Not logged in. Run ${import_picocolors43.default.bold("horus login")} first.`));
|
|
98470
99024
|
return 1;
|
|
98471
99025
|
}
|
|
98472
99026
|
let ctx;
|
|
@@ -98476,7 +99030,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98476
99030
|
return reportCloudError(err);
|
|
98477
99031
|
}
|
|
98478
99032
|
const remote = detectGitRemote(root);
|
|
98479
|
-
if (remote) console.log(
|
|
99033
|
+
if (remote) console.log(import_picocolors43.default.dim(`Detected git remote: ${remote.remoteUrl}`));
|
|
98480
99034
|
let resolved = null;
|
|
98481
99035
|
let target = opts.project;
|
|
98482
99036
|
if (!target) {
|
|
@@ -98492,7 +99046,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98492
99046
|
const org = ctx.organizations.find((o) => o.id === match.organizationId);
|
|
98493
99047
|
if (ws && org) {
|
|
98494
99048
|
target = `${org.slug}/${ws.slug}/${match.slug}`;
|
|
98495
|
-
console.log(
|
|
99049
|
+
console.log(import_picocolors43.default.dim(`Matched existing project by remote: ${import_picocolors43.default.bold(target)}`));
|
|
98496
99050
|
}
|
|
98497
99051
|
}
|
|
98498
99052
|
}
|
|
@@ -98501,8 +99055,8 @@ async function runCloudLink(opts = {}) {
|
|
|
98501
99055
|
if (created === "aborted") return 1;
|
|
98502
99056
|
if (!created) {
|
|
98503
99057
|
console.error(
|
|
98504
|
-
|
|
98505
|
-
Run ${
|
|
99058
|
+
import_picocolors43.default.red("No project to link.") + `
|
|
99059
|
+
Run ${import_picocolors43.default.bold("horus cloud link")} in a TTY to create one, pass ${import_picocolors43.default.bold("--project <org/workspace/project>")}, or create a project at ${import_picocolors43.default.bold("https://cloud.horus.sh")}.`
|
|
98506
99060
|
);
|
|
98507
99061
|
return 1;
|
|
98508
99062
|
}
|
|
@@ -98512,8 +99066,8 @@ async function runCloudLink(opts = {}) {
|
|
|
98512
99066
|
resolved = resolveTriple(ctx, target);
|
|
98513
99067
|
if (!resolved) {
|
|
98514
99068
|
console.error(
|
|
98515
|
-
|
|
98516
|
-
Run ${
|
|
99069
|
+
import_picocolors43.default.red(`You don't have access to ${import_picocolors43.default.bold(target)}.`) + `
|
|
99070
|
+
Run ${import_picocolors43.default.bold("horus context list")} to see available projects.`
|
|
98517
99071
|
);
|
|
98518
99072
|
return 1;
|
|
98519
99073
|
}
|
|
@@ -98526,10 +99080,10 @@ async function runCloudLink(opts = {}) {
|
|
|
98526
99080
|
project: resolved.project
|
|
98527
99081
|
});
|
|
98528
99082
|
console.log(
|
|
98529
|
-
`${
|
|
99083
|
+
`${import_picocolors43.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors43.default.bold(label)} ${import_picocolors43.default.dim("(cloud)")}.`
|
|
98530
99084
|
);
|
|
98531
99085
|
console.log(
|
|
98532
|
-
|
|
99086
|
+
import_picocolors43.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors43.default.bold("horus context use local")} to switch back.`)
|
|
98533
99087
|
);
|
|
98534
99088
|
return 0;
|
|
98535
99089
|
}
|
|
@@ -98537,11 +99091,11 @@ async function runCloudUnlink(opts = {}) {
|
|
|
98537
99091
|
const root = repoRootOrCwd(opts.cwd);
|
|
98538
99092
|
const cfg = readCloudConfig(root);
|
|
98539
99093
|
if (!cfg || cfg.context !== "cloud") {
|
|
98540
|
-
console.log(
|
|
99094
|
+
console.log(import_picocolors43.default.dim("This repo isn't linked to a cloud project."));
|
|
98541
99095
|
return 0;
|
|
98542
99096
|
}
|
|
98543
99097
|
clearCloudConfig(root);
|
|
98544
|
-
console.log(`${
|
|
99098
|
+
console.log(`${import_picocolors43.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors43.default.bold("local")} mode.`);
|
|
98545
99099
|
return 0;
|
|
98546
99100
|
}
|
|
98547
99101
|
async function runCloudStatus(opts = {}) {
|
|
@@ -98549,18 +99103,18 @@ async function runCloudStatus(opts = {}) {
|
|
|
98549
99103
|
const cfg = readCloudConfig(root);
|
|
98550
99104
|
const session = authedClient();
|
|
98551
99105
|
if (!isCloudActive(cfg) || !cfg?.project) {
|
|
98552
|
-
console.log(`${
|
|
98553
|
-
console.log(`${
|
|
99106
|
+
console.log(`${import_picocolors43.default.bold("Context:")} local`);
|
|
99107
|
+
console.log(`${import_picocolors43.default.bold("Storage:")} local Postgres (~/.horus)`);
|
|
98554
99108
|
console.log(
|
|
98555
|
-
|
|
99109
|
+
import_picocolors43.default.dim(`Tip: ${import_picocolors43.default.bold("horus login")} then ${import_picocolors43.default.bold("horus cloud link")} to share investigations with your team.`)
|
|
98556
99110
|
);
|
|
98557
99111
|
return 0;
|
|
98558
99112
|
}
|
|
98559
99113
|
const triple = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
98560
|
-
console.log(`${
|
|
99114
|
+
console.log(`${import_picocolors43.default.bold("Context:")} ${triple} ${import_picocolors43.default.dim("(cloud)")}`);
|
|
98561
99115
|
let projectLine = `${cfg.project.slug}`;
|
|
98562
99116
|
let metaLines = [];
|
|
98563
|
-
let account =
|
|
99117
|
+
let account = import_picocolors43.default.dim("not logged in");
|
|
98564
99118
|
if (session) {
|
|
98565
99119
|
account = session.auth.account.email;
|
|
98566
99120
|
try {
|
|
@@ -98575,9 +99129,9 @@ async function runCloudStatus(opts = {}) {
|
|
|
98575
99129
|
} catch {
|
|
98576
99130
|
}
|
|
98577
99131
|
}
|
|
98578
|
-
console.log(`${
|
|
99132
|
+
console.log(`${import_picocolors43.default.bold("Project:")} ${projectLine}`);
|
|
98579
99133
|
for (const line2 of metaLines) console.log(line2);
|
|
98580
|
-
console.log(`${
|
|
99134
|
+
console.log(`${import_picocolors43.default.bold("Account:")} ${account}`);
|
|
98581
99135
|
return 0;
|
|
98582
99136
|
}
|
|
98583
99137
|
async function runCloudSync(opts = {}) {
|
|
@@ -98585,13 +99139,13 @@ async function runCloudSync(opts = {}) {
|
|
|
98585
99139
|
const cfg = readCloudConfig(root);
|
|
98586
99140
|
if (!isCloudActive(cfg) || !cfg.project) {
|
|
98587
99141
|
console.error(
|
|
98588
|
-
|
|
99142
|
+
import_picocolors43.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors43.default.bold("horus cloud link")} first.`
|
|
98589
99143
|
);
|
|
98590
99144
|
return 1;
|
|
98591
99145
|
}
|
|
98592
99146
|
const session = authedClient();
|
|
98593
99147
|
if (!session) {
|
|
98594
|
-
console.error(
|
|
99148
|
+
console.error(import_picocolors43.default.red(`Not logged in. Run ${import_picocolors43.default.bold("horus login")} first.`));
|
|
98595
99149
|
return 1;
|
|
98596
99150
|
}
|
|
98597
99151
|
const { db, sql: sql2 } = createDb(await resolveDbUrl(opts.config));
|
|
@@ -98612,34 +99166,34 @@ async function runCloudSync(opts = {}) {
|
|
|
98612
99166
|
const target = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
98613
99167
|
if (uploadable.length === 0) {
|
|
98614
99168
|
console.log(
|
|
98615
|
-
|
|
99169
|
+
import_picocolors43.default.dim(
|
|
98616
99170
|
`No local investigations with reports to upload.` + (skippedNoReport ? ` (${skippedNoReport} without a report skipped)` : "")
|
|
98617
99171
|
)
|
|
98618
99172
|
);
|
|
98619
99173
|
return 0;
|
|
98620
99174
|
}
|
|
98621
|
-
console.log(
|
|
99175
|
+
console.log(import_picocolors43.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors43.default.dim("(cloud)")}:`));
|
|
98622
99176
|
for (const c of uploadable.slice(0, 20)) {
|
|
98623
|
-
console.log(` ${
|
|
99177
|
+
console.log(` ${import_picocolors43.default.dim(c.id.slice(0, 8))} ${(c.title ?? "untitled").slice(0, 70)}`);
|
|
98624
99178
|
}
|
|
98625
|
-
if (uploadable.length > 20) console.log(
|
|
98626
|
-
if (skippedNoReport) console.log(
|
|
99179
|
+
if (uploadable.length > 20) console.log(import_picocolors43.default.dim(` \u2026and ${uploadable.length - 20} more`));
|
|
99180
|
+
if (skippedNoReport) console.log(import_picocolors43.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
|
|
98627
99181
|
if (opts.dryRun) {
|
|
98628
|
-
console.log(
|
|
99182
|
+
console.log(import_picocolors43.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
|
|
98629
99183
|
return 0;
|
|
98630
99184
|
}
|
|
98631
99185
|
if (!opts.yes) {
|
|
98632
99186
|
if (!process.stdin.isTTY) {
|
|
98633
99187
|
console.error(
|
|
98634
|
-
|
|
99188
|
+
import_picocolors43.default.yellow(`Re-run with ${import_picocolors43.default.bold("--yes")} to upload, or ${import_picocolors43.default.bold("--dry-run")} to preview only.`)
|
|
98635
99189
|
);
|
|
98636
99190
|
return 1;
|
|
98637
99191
|
}
|
|
98638
|
-
const rl = (0,
|
|
99192
|
+
const rl = (0, import_promises3.createInterface)({ input: process.stdin, output: process.stdout });
|
|
98639
99193
|
try {
|
|
98640
99194
|
const answer = (await rl.question(`Upload ${uploadable.length} investigation(s) to ${target}? (y/N) `)).trim().toLowerCase();
|
|
98641
99195
|
if (answer !== "y" && answer !== "yes") {
|
|
98642
|
-
console.log(
|
|
99196
|
+
console.log(import_picocolors43.default.dim("Aborted. Nothing uploaded."));
|
|
98643
99197
|
return 0;
|
|
98644
99198
|
}
|
|
98645
99199
|
} finally {
|
|
@@ -98652,18 +99206,18 @@ async function runCloudSync(opts = {}) {
|
|
|
98652
99206
|
try {
|
|
98653
99207
|
const refs = await uploadInvestigationToCloud(session.client, cfg, c.report);
|
|
98654
99208
|
created++;
|
|
98655
|
-
console.log(`${
|
|
99209
|
+
console.log(`${import_picocolors43.default.green("\u2713")} ${import_picocolors43.default.dim(c.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
|
|
98656
99210
|
} catch (err) {
|
|
98657
99211
|
failed++;
|
|
98658
|
-
console.error(`${
|
|
99212
|
+
console.error(`${import_picocolors43.default.red("\u2717")} ${import_picocolors43.default.dim(c.id.slice(0, 8))} ${err.message}`);
|
|
98659
99213
|
}
|
|
98660
99214
|
}
|
|
98661
99215
|
console.log("");
|
|
98662
99216
|
console.log(
|
|
98663
|
-
`${
|
|
99217
|
+
`${import_picocolors43.default.bold("Sync complete:")} ${import_picocolors43.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors43.default.red(`${failed} failed`) : "0 failed") + "."
|
|
98664
99218
|
);
|
|
98665
99219
|
console.log(
|
|
98666
|
-
|
|
99220
|
+
import_picocolors43.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
|
|
98667
99221
|
);
|
|
98668
99222
|
return failed > 0 ? 1 : 0;
|
|
98669
99223
|
}
|
|
@@ -98671,12 +99225,12 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98671
99225
|
if (!process.stdin.isTTY && !yes) return null;
|
|
98672
99226
|
if (ctx.organizations.length === 0) {
|
|
98673
99227
|
console.error(
|
|
98674
|
-
|
|
98675
|
-
Create one at ${
|
|
99228
|
+
import_picocolors43.default.red("You're not a member of any organization yet.") + `
|
|
99229
|
+
Create one at ${import_picocolors43.default.bold("https://cloud.horus.sh")} first.`
|
|
98676
99230
|
);
|
|
98677
99231
|
return null;
|
|
98678
99232
|
}
|
|
98679
|
-
const rl = (0,
|
|
99233
|
+
const rl = (0, import_promises3.createInterface)({ input: process.stdin, output: process.stdout });
|
|
98680
99234
|
const ask2 = async (q, def) => {
|
|
98681
99235
|
if (yes && def !== void 0) return def;
|
|
98682
99236
|
const a = (await rl.question(def ? `${q} (${def}): ` : `${q}: `)).trim();
|
|
@@ -98697,9 +99251,9 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98697
99251
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
98698
99252
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
98699
99253
|
workspace = { id: ws.id, slug: ws.slug };
|
|
98700
|
-
console.log(
|
|
99254
|
+
console.log(import_picocolors43.default.dim(`Created workspace ${import_picocolors43.default.bold(ws.slug)}.`));
|
|
98701
99255
|
} else {
|
|
98702
|
-
console.log(`Workspaces in ${
|
|
99256
|
+
console.log(`Workspaces in ${import_picocolors43.default.bold(org.slug)}:`);
|
|
98703
99257
|
orgWorkspaces.forEach((w, i) => console.log(` ${i + 1}) ${w.slug}`));
|
|
98704
99258
|
console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
|
|
98705
99259
|
const pick2 = Number(await ask2("Workspace number", "1"));
|
|
@@ -98707,7 +99261,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98707
99261
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
98708
99262
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
98709
99263
|
workspace = { id: ws.id, slug: ws.slug };
|
|
98710
|
-
console.log(
|
|
99264
|
+
console.log(import_picocolors43.default.dim(`Created workspace ${import_picocolors43.default.bold(ws.slug)}.`));
|
|
98711
99265
|
} else {
|
|
98712
99266
|
const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
|
|
98713
99267
|
workspace = { id: chosen.id, slug: chosen.slug };
|
|
@@ -98719,7 +99273,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98719
99273
|
remoteUrl: remote?.remoteUrl,
|
|
98720
99274
|
provider: remote ? inferProvider(remote.remoteUrl) : void 0
|
|
98721
99275
|
});
|
|
98722
|
-
console.log(
|
|
99276
|
+
console.log(import_picocolors43.default.dim(`Created project ${import_picocolors43.default.bold(project.slug)}.`));
|
|
98723
99277
|
return {
|
|
98724
99278
|
organization: { id: org.id, slug: org.slug },
|
|
98725
99279
|
workspace,
|
|
@@ -98733,6 +99287,274 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98733
99287
|
}
|
|
98734
99288
|
}
|
|
98735
99289
|
|
|
99290
|
+
// ../../packages/cli/src/commands/telemetry.ts
|
|
99291
|
+
init_cjs_shims();
|
|
99292
|
+
var import_picocolors45 = __toESM(require_picocolors(), 1);
|
|
99293
|
+
var import_node_crypto8 = require("crypto");
|
|
99294
|
+
|
|
99295
|
+
// ../../packages/cli/src/lib/telemetry/transport.ts
|
|
99296
|
+
init_cjs_shims();
|
|
99297
|
+
var CHUNK = 100;
|
|
99298
|
+
var DEFAULT_TIMEOUT_MS = 1500;
|
|
99299
|
+
function telemetryBaseUrl() {
|
|
99300
|
+
return process.env.HORUS_TELEMETRY_URL ?? process.env.HORUS_API_BASE_URL ?? readAuth()?.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
99301
|
+
}
|
|
99302
|
+
async function postBatch(url, events, signal) {
|
|
99303
|
+
const res = await fetch(url, {
|
|
99304
|
+
method: "POST",
|
|
99305
|
+
headers: { "content-type": "application/json" },
|
|
99306
|
+
body: JSON.stringify({ events }),
|
|
99307
|
+
signal
|
|
99308
|
+
});
|
|
99309
|
+
return res.ok;
|
|
99310
|
+
}
|
|
99311
|
+
async function flushTelemetry(opts = {}) {
|
|
99312
|
+
try {
|
|
99313
|
+
if (!resolveConsent().tierA) return;
|
|
99314
|
+
const events = readSpooledEvents();
|
|
99315
|
+
if (events.length === 0) return;
|
|
99316
|
+
const url = `${telemetryBaseUrl().replace(/\/$/, "")}/v1/telemetry`;
|
|
99317
|
+
const controller = new AbortController();
|
|
99318
|
+
const timer2 = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
99319
|
+
try {
|
|
99320
|
+
for (let i = 0; i < events.length; i += CHUNK) {
|
|
99321
|
+
const ok = await postBatch(url, events.slice(i, i + CHUNK), controller.signal);
|
|
99322
|
+
if (!ok) return;
|
|
99323
|
+
}
|
|
99324
|
+
clearSpool();
|
|
99325
|
+
} finally {
|
|
99326
|
+
clearTimeout(timer2);
|
|
99327
|
+
}
|
|
99328
|
+
} catch {
|
|
99329
|
+
}
|
|
99330
|
+
}
|
|
99331
|
+
async function deleteRemoteTelemetry(installId, opts = {}) {
|
|
99332
|
+
try {
|
|
99333
|
+
if (!installId) return;
|
|
99334
|
+
const url = `${telemetryBaseUrl().replace(/\/$/, "")}/v1/telemetry/delete`;
|
|
99335
|
+
const controller = new AbortController();
|
|
99336
|
+
const timer2 = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
99337
|
+
try {
|
|
99338
|
+
await fetch(url, {
|
|
99339
|
+
method: "POST",
|
|
99340
|
+
headers: { "content-type": "application/json" },
|
|
99341
|
+
body: JSON.stringify({ installId }),
|
|
99342
|
+
signal: controller.signal
|
|
99343
|
+
});
|
|
99344
|
+
} finally {
|
|
99345
|
+
clearTimeout(timer2);
|
|
99346
|
+
}
|
|
99347
|
+
} catch {
|
|
99348
|
+
}
|
|
99349
|
+
}
|
|
99350
|
+
|
|
99351
|
+
// ../../packages/cli/src/lib/telemetry/notice.ts
|
|
99352
|
+
init_cjs_shims();
|
|
99353
|
+
var import_picocolors44 = __toESM(require_picocolors(), 1);
|
|
99354
|
+
var PRIVACY_URL = "https://horus.sh/privacy";
|
|
99355
|
+
var SUPPRESS_FIRST_TOKEN = /* @__PURE__ */ new Set(["telemetry", "help"]);
|
|
99356
|
+
var SUPPRESS_FLAGS = /* @__PURE__ */ new Set(["-V", "--version", "-h", "--help"]);
|
|
99357
|
+
function shouldSuppressFor(argv) {
|
|
99358
|
+
const args = argv.slice(2);
|
|
99359
|
+
if (args.length === 0) return true;
|
|
99360
|
+
if (args.some((a) => SUPPRESS_FLAGS.has(a))) return true;
|
|
99361
|
+
const firstToken = args.find((a) => !a.startsWith("-"));
|
|
99362
|
+
return firstToken != null && SUPPRESS_FIRST_TOKEN.has(firstToken);
|
|
99363
|
+
}
|
|
99364
|
+
function banner() {
|
|
99365
|
+
const b2 = import_picocolors44.default.bold;
|
|
99366
|
+
const dim = import_picocolors44.default.dim;
|
|
99367
|
+
return [
|
|
99368
|
+
"",
|
|
99369
|
+
import_picocolors44.default.cyan(b2("Horus collects anonymous usage data to improve the tool.")),
|
|
99370
|
+
dim(" \u2022 On by default: command names, timings, success/failure, and where the"),
|
|
99371
|
+
dim(" engine ran out of evidence. No file contents, queries, or secrets."),
|
|
99372
|
+
dim(" \u2022 Off by default: your investigation inputs/outputs (opt in to help train"),
|
|
99373
|
+
` ${dim("Horus)")} ${dim("\u2014 run")} ${b2("horus telemetry enable-content")}`,
|
|
99374
|
+
dim(" \u2022 If you use Horus Cloud, linked investigations sync to your workspace."),
|
|
99375
|
+
"",
|
|
99376
|
+
` ${dim("Opt out anytime:")} ${b2("horus telemetry disable")} ${dim("\xB7")} ${b2("HORUS_TELEMETRY=0")} ${dim("\xB7")} ${b2("DO_NOT_TRACK=1")}`,
|
|
99377
|
+
` ${dim("Details:")} ${import_picocolors44.default.underline(PRIVACY_URL)}`,
|
|
99378
|
+
""
|
|
99379
|
+
].join("\n");
|
|
99380
|
+
}
|
|
99381
|
+
function maybeShowFirstRunNotice(argv = process.argv) {
|
|
99382
|
+
try {
|
|
99383
|
+
const env = process.env;
|
|
99384
|
+
if (resolveConsent({ env }).forcedOff) return;
|
|
99385
|
+
if (isCI(env)) return;
|
|
99386
|
+
const state = loadOrInitTelemetryState();
|
|
99387
|
+
if (state.tierA.noticeShownAt) return;
|
|
99388
|
+
if (shouldSuppressFor(argv)) return;
|
|
99389
|
+
if (!process.stderr.isTTY) return;
|
|
99390
|
+
process.stderr.write(banner() + "\n");
|
|
99391
|
+
updateTelemetryState((s) => {
|
|
99392
|
+
s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99393
|
+
});
|
|
99394
|
+
} catch {
|
|
99395
|
+
}
|
|
99396
|
+
}
|
|
99397
|
+
|
|
99398
|
+
// ../../packages/cli/src/commands/telemetry.ts
|
|
99399
|
+
function onOff(enabled) {
|
|
99400
|
+
return enabled ? import_picocolors45.default.green("on") : import_picocolors45.default.dim("off");
|
|
99401
|
+
}
|
|
99402
|
+
function warnIfEnvOverrides() {
|
|
99403
|
+
const decision = resolveConsent();
|
|
99404
|
+
if (decision.forcedOff) {
|
|
99405
|
+
console.log(
|
|
99406
|
+
import_picocolors45.default.yellow(
|
|
99407
|
+
`
|
|
99408
|
+
Note: telemetry is currently forced off by your environment (${decision.reason}).`
|
|
99409
|
+
)
|
|
99410
|
+
);
|
|
99411
|
+
console.log(import_picocolors45.default.dim("Your saved preference will apply once that override is removed."));
|
|
99412
|
+
} else if (decision.source === "ci") {
|
|
99413
|
+
console.log(
|
|
99414
|
+
import_picocolors45.default.yellow("\nNote: CI environment detected \u2014 telemetry stays off here regardless.")
|
|
99415
|
+
);
|
|
99416
|
+
}
|
|
99417
|
+
}
|
|
99418
|
+
async function runTelemetryStatus() {
|
|
99419
|
+
const state = readTelemetryState();
|
|
99420
|
+
const decision = resolveConsent({ state });
|
|
99421
|
+
console.log(import_picocolors45.default.bold("Horus telemetry"));
|
|
99422
|
+
console.log(
|
|
99423
|
+
` Usage metadata (Tier A): ${onOff(decision.tierA)} ${import_picocolors45.default.dim(`[${decision.source}]`)}`
|
|
99424
|
+
);
|
|
99425
|
+
console.log(` Content sharing (Tier B): ${onOff(decision.tierB)}`);
|
|
99426
|
+
console.log(import_picocolors45.default.dim(` Reason: ${decision.reason}`));
|
|
99427
|
+
if (state) {
|
|
99428
|
+
console.log(import_picocolors45.default.dim(` Install ID: ${state.installId}`));
|
|
99429
|
+
console.log(
|
|
99430
|
+
import_picocolors45.default.dim(
|
|
99431
|
+
` Saved: Tier A ${state.tierA.enabled ? "on" : "off"}, Tier B ${state.tierB.enabled ? "on" : "off"}`
|
|
99432
|
+
)
|
|
99433
|
+
);
|
|
99434
|
+
} else {
|
|
99435
|
+
console.log(import_picocolors45.default.dim(" No saved preference yet (defaults apply)."));
|
|
99436
|
+
}
|
|
99437
|
+
console.log(import_picocolors45.default.dim(` Config: ${telemetryPath()}`));
|
|
99438
|
+
console.log(import_picocolors45.default.dim(` Privacy: ${PRIVACY_URL}`));
|
|
99439
|
+
if (!state || decision.source === "env" || decision.source === "ci") {
|
|
99440
|
+
warnIfEnvOverrides();
|
|
99441
|
+
}
|
|
99442
|
+
return 0;
|
|
99443
|
+
}
|
|
99444
|
+
async function runTelemetryEnable() {
|
|
99445
|
+
updateTelemetryState((s) => {
|
|
99446
|
+
s.tierA.enabled = true;
|
|
99447
|
+
if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99448
|
+
});
|
|
99449
|
+
console.log(import_picocolors45.default.green("Anonymous usage metadata (Tier A) is now ON. Thank you!"));
|
|
99450
|
+
warnIfEnvOverrides();
|
|
99451
|
+
return 0;
|
|
99452
|
+
}
|
|
99453
|
+
async function runTelemetryDisable() {
|
|
99454
|
+
updateTelemetryState((s) => {
|
|
99455
|
+
s.tierA.enabled = false;
|
|
99456
|
+
s.tierB.enabled = false;
|
|
99457
|
+
if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99458
|
+
});
|
|
99459
|
+
console.log(import_picocolors45.default.yellow("All telemetry is now OFF (Tier A and Tier B)."));
|
|
99460
|
+
return 0;
|
|
99461
|
+
}
|
|
99462
|
+
async function runTelemetryEnableContent() {
|
|
99463
|
+
updateTelemetryState((s) => {
|
|
99464
|
+
s.tierA.enabled = true;
|
|
99465
|
+
s.tierB.enabled = true;
|
|
99466
|
+
s.tierB.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99467
|
+
if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99468
|
+
});
|
|
99469
|
+
console.log(
|
|
99470
|
+
import_picocolors45.default.green("Content sharing (Tier B) is now ON \u2014 redacted inputs/outputs help improve Horus.")
|
|
99471
|
+
);
|
|
99472
|
+
console.log(import_picocolors45.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
|
|
99473
|
+
console.log(import_picocolors45.default.dim(`What this means: ${PRIVACY_URL}`));
|
|
99474
|
+
warnIfEnvOverrides();
|
|
99475
|
+
return 0;
|
|
99476
|
+
}
|
|
99477
|
+
async function runTelemetryDisableContent() {
|
|
99478
|
+
updateTelemetryState((s) => {
|
|
99479
|
+
s.tierB.enabled = false;
|
|
99480
|
+
});
|
|
99481
|
+
console.log(import_picocolors45.default.yellow("Content sharing (Tier B) is now OFF. Usage metadata is unchanged."));
|
|
99482
|
+
return 0;
|
|
99483
|
+
}
|
|
99484
|
+
async function runTelemetryResetId() {
|
|
99485
|
+
const next = (0, import_node_crypto8.randomUUID)();
|
|
99486
|
+
updateTelemetryState((s) => {
|
|
99487
|
+
s.installId = next;
|
|
99488
|
+
});
|
|
99489
|
+
console.log(import_picocolors45.default.green("Install ID reset."));
|
|
99490
|
+
console.log(import_picocolors45.default.dim(` New install ID: ${next}`));
|
|
99491
|
+
return 0;
|
|
99492
|
+
}
|
|
99493
|
+
async function runTelemetryDelete() {
|
|
99494
|
+
const installId = readTelemetryState()?.installId;
|
|
99495
|
+
if (installId) await deleteRemoteTelemetry(installId);
|
|
99496
|
+
deleteTelemetryState();
|
|
99497
|
+
clearSpool();
|
|
99498
|
+
console.log(import_picocolors45.default.green("Telemetry deleted \u2014 local state cleared and a server purge requested."));
|
|
99499
|
+
console.log(
|
|
99500
|
+
import_picocolors45.default.dim(
|
|
99501
|
+
"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."
|
|
99502
|
+
)
|
|
99503
|
+
);
|
|
99504
|
+
return 0;
|
|
99505
|
+
}
|
|
99506
|
+
|
|
99507
|
+
// ../../packages/cli/src/lib/telemetry/command-hooks.ts
|
|
99508
|
+
init_cjs_shims();
|
|
99509
|
+
function commandPath(cmd) {
|
|
99510
|
+
const parts = [];
|
|
99511
|
+
let current = cmd;
|
|
99512
|
+
while (current && current.parent) {
|
|
99513
|
+
parts.unshift(current.name());
|
|
99514
|
+
current = current.parent;
|
|
99515
|
+
}
|
|
99516
|
+
return parts.join(" ") || cmd.name();
|
|
99517
|
+
}
|
|
99518
|
+
function installCommandTelemetry(program2) {
|
|
99519
|
+
let startedAt = 0;
|
|
99520
|
+
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
99521
|
+
startedAt = Date.now();
|
|
99522
|
+
track({
|
|
99523
|
+
type: "command.invoked",
|
|
99524
|
+
command: commandPath(actionCommand),
|
|
99525
|
+
flags: extractFlagNames(process.argv)
|
|
99526
|
+
});
|
|
99527
|
+
});
|
|
99528
|
+
program2.hook("postAction", (_thisCommand, actionCommand) => {
|
|
99529
|
+
const exitCode = typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
99530
|
+
track({
|
|
99531
|
+
type: "command.completed",
|
|
99532
|
+
command: commandPath(actionCommand),
|
|
99533
|
+
flags: extractFlagNames(process.argv),
|
|
99534
|
+
durationMs: startedAt ? Date.now() - startedAt : 0,
|
|
99535
|
+
exitCode,
|
|
99536
|
+
ok: exitCode === 0
|
|
99537
|
+
});
|
|
99538
|
+
});
|
|
99539
|
+
}
|
|
99540
|
+
|
|
99541
|
+
// ../../packages/cli/src/commands/feedback.ts
|
|
99542
|
+
init_cjs_shims();
|
|
99543
|
+
var import_picocolors46 = __toESM(require_picocolors(), 1);
|
|
99544
|
+
async function runFeedback(investigationId) {
|
|
99545
|
+
if (!process.stdin.isTTY) {
|
|
99546
|
+
console.error(import_picocolors46.default.yellow("horus feedback needs an interactive terminal."));
|
|
99547
|
+
return 1;
|
|
99548
|
+
}
|
|
99549
|
+
const id = investigationId?.trim();
|
|
99550
|
+
if (!id) {
|
|
99551
|
+
console.error(import_picocolors46.default.red("Usage: horus feedback <investigationId>"));
|
|
99552
|
+
return 1;
|
|
99553
|
+
}
|
|
99554
|
+
await runFeedbackPrompt(id, null);
|
|
99555
|
+
return 0;
|
|
99556
|
+
}
|
|
99557
|
+
|
|
98736
99558
|
// ../../packages/cli/src/index.ts
|
|
98737
99559
|
function buildProgram() {
|
|
98738
99560
|
const program2 = new Command();
|
|
@@ -99154,11 +99976,50 @@ Examples:
|
|
|
99154
99976
|
horus cloud sync --dry-run # preview what would upload
|
|
99155
99977
|
horus cloud sync # confirm, then upload
|
|
99156
99978
|
horus cloud sync --yes # upload without prompting`);
|
|
99979
|
+
program2.command("feedback [investigationId]").description("Leave quick impact feedback on an investigation (helps improve Horus)").action(async (investigationId) => {
|
|
99980
|
+
process.exitCode = await runFeedback(investigationId);
|
|
99981
|
+
});
|
|
99982
|
+
const telemetry = program2.command("telemetry").description("Manage anonymous usage telemetry that helps improve Horus");
|
|
99983
|
+
telemetry.command("status").description("Show the effective telemetry decision, saved preferences, and install ID").action(async () => {
|
|
99984
|
+
process.exitCode = await runTelemetryStatus();
|
|
99985
|
+
});
|
|
99986
|
+
telemetry.command("enable").description("Turn on anonymous usage metadata (Tier A)").action(async () => {
|
|
99987
|
+
process.exitCode = await runTelemetryEnable();
|
|
99988
|
+
});
|
|
99989
|
+
telemetry.command("disable").description("Turn off all telemetry (Tier A and Tier B)").action(async () => {
|
|
99990
|
+
process.exitCode = await runTelemetryDisable();
|
|
99991
|
+
});
|
|
99992
|
+
telemetry.command("enable-content").description("Opt in to sharing redacted investigation inputs/outputs (Tier B)").action(async () => {
|
|
99993
|
+
process.exitCode = await runTelemetryEnableContent();
|
|
99994
|
+
});
|
|
99995
|
+
telemetry.command("disable-content").description("Stop sharing inputs/outputs; keep usage metadata (Tier B off)").action(async () => {
|
|
99996
|
+
process.exitCode = await runTelemetryDisableContent();
|
|
99997
|
+
});
|
|
99998
|
+
telemetry.command("reset-id").description("Generate a new random install ID").action(async () => {
|
|
99999
|
+
process.exitCode = await runTelemetryResetId();
|
|
100000
|
+
});
|
|
100001
|
+
telemetry.command("delete").description("Delete local telemetry state (install ID + saved preferences)").action(async () => {
|
|
100002
|
+
process.exitCode = await runTelemetryDelete();
|
|
100003
|
+
});
|
|
100004
|
+
telemetry.addHelpText(
|
|
100005
|
+
"after",
|
|
100006
|
+
`
|
|
100007
|
+
Defaults: usage metadata ON (with this notice), content sharing OFF.
|
|
100008
|
+
Hard opt-out via env: HORUS_TELEMETRY=0 or DO_NOT_TRACK=1.
|
|
100009
|
+
|
|
100010
|
+
Examples:
|
|
100011
|
+
horus telemetry status
|
|
100012
|
+
horus telemetry disable
|
|
100013
|
+
horus telemetry enable-content`
|
|
100014
|
+
);
|
|
99157
100015
|
return program2;
|
|
99158
100016
|
}
|
|
99159
100017
|
async function run(argv = process.argv) {
|
|
100018
|
+
maybeShowFirstRunNotice(argv);
|
|
99160
100019
|
const program2 = buildProgram();
|
|
100020
|
+
installCommandTelemetry(program2);
|
|
99161
100021
|
await program2.parseAsync(argv);
|
|
100022
|
+
await flushTelemetry();
|
|
99162
100023
|
}
|
|
99163
100024
|
|
|
99164
100025
|
// src/index.ts
|