@merittdev/horus 0.2.3 → 0.3.1
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 +1366 -569
- 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.1" : "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
|
}
|
|
@@ -77143,7 +77192,12 @@ async function investigate(input, deps) {
|
|
|
77143
77192
|
code.impact(top.id, 2),
|
|
77144
77193
|
code.flowsFor(top.id)
|
|
77145
77194
|
]);
|
|
77146
|
-
|
|
77195
|
+
let edges = [];
|
|
77196
|
+
try {
|
|
77197
|
+
edges = await listQueueEdges(db, { project: input.repo });
|
|
77198
|
+
} catch {
|
|
77199
|
+
edges = [];
|
|
77200
|
+
}
|
|
77147
77201
|
const symbolNames = /* @__PURE__ */ new Set([
|
|
77148
77202
|
top.name,
|
|
77149
77203
|
...ctx.callers.map((s) => s.name),
|
|
@@ -77826,9 +77880,9 @@ async function investigate(input, deps) {
|
|
|
77826
77880
|
const confidence = clamp015(0.5 * evidenceConfidence + 0.5 * seedResolved);
|
|
77827
77881
|
const area = ctx?.community?.name ?? top?.filePath ?? (input.service ?? "runtime evidence");
|
|
77828
77882
|
const topCause = rankedCauses[0];
|
|
77829
|
-
const
|
|
77883
|
+
const banner2 = degradedNoSource ? "Runtime-only (source intelligence unavailable). " : "";
|
|
77830
77884
|
const scope = top ? `resolved to ${label} (${area})` : `over runtime evidence (${area})`;
|
|
77831
|
-
const summary = topCause ? `${
|
|
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.`;
|
|
77832
77886
|
const nextActions = buildNextActions(top, ctx, impact, queueHits, changes, input);
|
|
77833
77887
|
if (degradedNoSource) {
|
|
77834
77888
|
nextActions.unshift(
|
|
@@ -77889,6 +77943,7 @@ async function investigate(input, deps) {
|
|
|
77889
77943
|
report.sourceStatus = buildRuntimeSourceStatus(evidence2, connectorFlags);
|
|
77890
77944
|
const persistedId = await persist(db, input, report);
|
|
77891
77945
|
if (persistedId) report.id = persistedId;
|
|
77946
|
+
report.persisted = persistedId !== null;
|
|
77892
77947
|
if (persistedId !== null) {
|
|
77893
77948
|
const tags = deriveTags(report);
|
|
77894
77949
|
report.similarIncidents = await recallSimilar(db, tags, persistedId, input.repo ?? null);
|
|
@@ -80983,22 +81038,22 @@ function extractQueueGraph(input) {
|
|
|
80983
81038
|
}
|
|
80984
81039
|
}
|
|
80985
81040
|
const producers = [];
|
|
80986
|
-
for (const
|
|
80987
|
-
for (const m of
|
|
81041
|
+
for (const pc47 of input.producerClasses) {
|
|
81042
|
+
for (const m of pc47.content.matchAll(INJECT_QUEUE_RE)) {
|
|
80988
81043
|
const queue = m[1] ?? "";
|
|
80989
|
-
if (queue) producers.push({ queue, symbol:
|
|
81044
|
+
if (queue) producers.push({ queue, symbol: pc47.name, file: pc47.filePath });
|
|
80990
81045
|
}
|
|
80991
|
-
for (const m of
|
|
81046
|
+
for (const m of pc47.content.matchAll(NEW_BULL_RE)) {
|
|
80992
81047
|
if (m[2] !== "Queue") continue;
|
|
80993
81048
|
const queue = resolveArg(m[3] ?? "", constMap);
|
|
80994
81049
|
if (queue) {
|
|
80995
|
-
producers.push({ queue, symbol: (m[1] ??
|
|
81050
|
+
producers.push({ queue, symbol: (m[1] ?? pc47.name) || baseName(pc47.filePath), file: pc47.filePath });
|
|
80996
81051
|
}
|
|
80997
81052
|
}
|
|
80998
|
-
for (const m of
|
|
81053
|
+
for (const m of pc47.content.matchAll(QUEUE_NAME_CONST_RE)) {
|
|
80999
81054
|
const ident = m[1] ?? "";
|
|
81000
81055
|
const queue = m[2] ?? "";
|
|
81001
|
-
if (queue) producers.push({ queue, symbol: ident || baseName(
|
|
81056
|
+
if (queue) producers.push({ queue, symbol: ident || baseName(pc47.filePath), file: pc47.filePath });
|
|
81002
81057
|
}
|
|
81003
81058
|
}
|
|
81004
81059
|
const workers = [];
|
|
@@ -90068,21 +90123,21 @@ var Protocol = class {
|
|
|
90068
90123
|
* the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer
|
|
90069
90124
|
* simply propagates the error.
|
|
90070
90125
|
*/
|
|
90071
|
-
async _enqueueTaskMessage(taskId, message,
|
|
90126
|
+
async _enqueueTaskMessage(taskId, message, sessionId2) {
|
|
90072
90127
|
if (!this._taskStore || !this._taskMessageQueue) {
|
|
90073
90128
|
throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
|
|
90074
90129
|
}
|
|
90075
90130
|
const maxQueueSize = this._options?.maxTaskQueueSize;
|
|
90076
|
-
await this._taskMessageQueue.enqueue(taskId, message,
|
|
90131
|
+
await this._taskMessageQueue.enqueue(taskId, message, sessionId2, maxQueueSize);
|
|
90077
90132
|
}
|
|
90078
90133
|
/**
|
|
90079
90134
|
* Clears the message queue for a task and rejects any pending request resolvers.
|
|
90080
90135
|
* @param taskId The task ID whose queue should be cleared
|
|
90081
90136
|
* @param sessionId Optional session ID for binding the operation to a specific session
|
|
90082
90137
|
*/
|
|
90083
|
-
async _clearTaskQueue(taskId,
|
|
90138
|
+
async _clearTaskQueue(taskId, sessionId2) {
|
|
90084
90139
|
if (this._taskMessageQueue) {
|
|
90085
|
-
const messages2 = await this._taskMessageQueue.dequeueAll(taskId,
|
|
90140
|
+
const messages2 = await this._taskMessageQueue.dequeueAll(taskId, sessionId2);
|
|
90086
90141
|
for (const message of messages2) {
|
|
90087
90142
|
if (message.type === "request" && isJSONRPCRequest(message.message)) {
|
|
90088
90143
|
const requestId = message.message.id;
|
|
@@ -90125,7 +90180,7 @@ var Protocol = class {
|
|
|
90125
90180
|
}, { once: true });
|
|
90126
90181
|
});
|
|
90127
90182
|
}
|
|
90128
|
-
requestTaskStore(request,
|
|
90183
|
+
requestTaskStore(request, sessionId2) {
|
|
90129
90184
|
const taskStore = this._taskStore;
|
|
90130
90185
|
if (!taskStore) {
|
|
90131
90186
|
throw new Error("No task store configured");
|
|
@@ -90138,18 +90193,18 @@ var Protocol = class {
|
|
|
90138
90193
|
return await taskStore.createTask(taskParams, request.id, {
|
|
90139
90194
|
method: request.method,
|
|
90140
90195
|
params: request.params
|
|
90141
|
-
},
|
|
90196
|
+
}, sessionId2);
|
|
90142
90197
|
},
|
|
90143
90198
|
getTask: async (taskId) => {
|
|
90144
|
-
const task = await taskStore.getTask(taskId,
|
|
90199
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
90145
90200
|
if (!task) {
|
|
90146
90201
|
throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
|
|
90147
90202
|
}
|
|
90148
90203
|
return task;
|
|
90149
90204
|
},
|
|
90150
90205
|
storeTaskResult: async (taskId, status, result) => {
|
|
90151
|
-
await taskStore.storeTaskResult(taskId, status, result,
|
|
90152
|
-
const task = await taskStore.getTask(taskId,
|
|
90206
|
+
await taskStore.storeTaskResult(taskId, status, result, sessionId2);
|
|
90207
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
90153
90208
|
if (task) {
|
|
90154
90209
|
const notification = TaskStatusNotificationSchema.parse({
|
|
90155
90210
|
method: "notifications/tasks/status",
|
|
@@ -90162,18 +90217,18 @@ var Protocol = class {
|
|
|
90162
90217
|
}
|
|
90163
90218
|
},
|
|
90164
90219
|
getTaskResult: (taskId) => {
|
|
90165
|
-
return taskStore.getTaskResult(taskId,
|
|
90220
|
+
return taskStore.getTaskResult(taskId, sessionId2);
|
|
90166
90221
|
},
|
|
90167
90222
|
updateTaskStatus: async (taskId, status, statusMessage) => {
|
|
90168
|
-
const task = await taskStore.getTask(taskId,
|
|
90223
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
90169
90224
|
if (!task) {
|
|
90170
90225
|
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
|
|
90171
90226
|
}
|
|
90172
90227
|
if (isTerminal(task.status)) {
|
|
90173
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.`);
|
|
90174
90229
|
}
|
|
90175
|
-
await taskStore.updateTaskStatus(taskId, status, statusMessage,
|
|
90176
|
-
const updatedTask = await taskStore.getTask(taskId,
|
|
90230
|
+
await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId2);
|
|
90231
|
+
const updatedTask = await taskStore.getTask(taskId, sessionId2);
|
|
90177
90232
|
if (updatedTask) {
|
|
90178
90233
|
const notification = TaskStatusNotificationSchema.parse({
|
|
90179
90234
|
method: "notifications/tasks/status",
|
|
@@ -90186,7 +90241,7 @@ var Protocol = class {
|
|
|
90186
90241
|
}
|
|
90187
90242
|
},
|
|
90188
90243
|
listTasks: (cursor) => {
|
|
90189
|
-
return taskStore.listTasks(cursor,
|
|
90244
|
+
return taskStore.listTasks(cursor, sessionId2);
|
|
90190
90245
|
}
|
|
90191
90246
|
};
|
|
90192
90247
|
}
|
|
@@ -90540,8 +90595,8 @@ var Server = class extends Protocol {
|
|
|
90540
90595
|
this._serverInfo = _serverInfo;
|
|
90541
90596
|
this._loggingLevels = /* @__PURE__ */ new Map();
|
|
90542
90597
|
this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index2) => [level, index2]));
|
|
90543
|
-
this.isMessageIgnored = (level,
|
|
90544
|
-
const currentLevel = this._loggingLevels.get(
|
|
90598
|
+
this.isMessageIgnored = (level, sessionId2) => {
|
|
90599
|
+
const currentLevel = this._loggingLevels.get(sessionId2);
|
|
90545
90600
|
return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
|
|
90546
90601
|
};
|
|
90547
90602
|
this._capabilities = options?.capabilities ?? {};
|
|
@@ -90884,9 +90939,9 @@ var Server = class extends Protocol {
|
|
|
90884
90939
|
* @param params
|
|
90885
90940
|
* @param sessionId optional for stateless and backward compatibility
|
|
90886
90941
|
*/
|
|
90887
|
-
async sendLoggingMessage(params,
|
|
90942
|
+
async sendLoggingMessage(params, sessionId2) {
|
|
90888
90943
|
if (this._capabilities.logging) {
|
|
90889
|
-
if (!this.isMessageIgnored(params.level,
|
|
90944
|
+
if (!this.isMessageIgnored(params.level, sessionId2)) {
|
|
90890
90945
|
return this.notification({ method: "notifications/message", params });
|
|
90891
90946
|
}
|
|
90892
90947
|
}
|
|
@@ -91691,8 +91746,8 @@ var McpServer = class {
|
|
|
91691
91746
|
* @param params
|
|
91692
91747
|
* @param sessionId optional for stateless and backward compatibility
|
|
91693
91748
|
*/
|
|
91694
|
-
async sendLoggingMessage(params,
|
|
91695
|
-
return this.server.sendLoggingMessage(params,
|
|
91749
|
+
async sendLoggingMessage(params, sessionId2) {
|
|
91750
|
+
return this.server.sendLoggingMessage(params, sessionId2);
|
|
91696
91751
|
}
|
|
91697
91752
|
/**
|
|
91698
91753
|
* Sends a resource list changed event to the client, if connected.
|
|
@@ -93743,7 +93798,7 @@ function formatAge(ms) {
|
|
|
93743
93798
|
|
|
93744
93799
|
// ../../packages/cli/src/commands/investigate.ts
|
|
93745
93800
|
init_cjs_shims();
|
|
93746
|
-
var
|
|
93801
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
93747
93802
|
|
|
93748
93803
|
// ../../packages/cli/src/lib/cloud/investigation-sync.ts
|
|
93749
93804
|
init_cjs_shims();
|
|
@@ -93820,6 +93875,346 @@ async function listCloudInvestigations(client, cfg) {
|
|
|
93820
93875
|
return result.investigations;
|
|
93821
93876
|
}
|
|
93822
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
|
+
source: "prompt"
|
|
94199
|
+
});
|
|
94200
|
+
console.log(import_picocolors8.default.dim(" Thanks \u2014 recorded."));
|
|
94201
|
+
return true;
|
|
94202
|
+
} finally {
|
|
94203
|
+
rl.close();
|
|
94204
|
+
}
|
|
94205
|
+
}
|
|
94206
|
+
async function maybePromptFeedback(opts) {
|
|
94207
|
+
try {
|
|
94208
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
94209
|
+
if (!resolveConsent().tierA) return;
|
|
94210
|
+
const rate = opts.sampleRate ?? 0.25;
|
|
94211
|
+
const rand = (opts.random ?? Math.random)();
|
|
94212
|
+
if (rand > rate) return;
|
|
94213
|
+
await runFeedbackPrompt(opts.investigationId, opts.horusSeconds ?? null);
|
|
94214
|
+
} catch {
|
|
94215
|
+
}
|
|
94216
|
+
}
|
|
94217
|
+
|
|
93823
94218
|
// ../../packages/cli/src/lib/ensure-host.ts
|
|
93824
94219
|
init_cjs_shims();
|
|
93825
94220
|
function parseHostPort(hostUrl) {
|
|
@@ -93912,37 +94307,37 @@ function renderStoredAIJudgment(judgment, write = (l) => console.log(l)) {
|
|
|
93912
94307
|
const sep = "\u2500".repeat(60);
|
|
93913
94308
|
write(`
|
|
93914
94309
|
${sep}`);
|
|
93915
|
-
write(
|
|
94310
|
+
write(import_picocolors9.default.bold("AI Judgment") + import_picocolors9.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
|
|
93916
94311
|
write(sep);
|
|
93917
94312
|
if (judgment.hypothesisJudgments && judgment.hypothesisJudgments.length > 0) {
|
|
93918
|
-
write(
|
|
94313
|
+
write(import_picocolors9.default.bold("Hypothesis verdicts:"));
|
|
93919
94314
|
for (const j of judgment.hypothesisJudgments) {
|
|
93920
|
-
const verdictColor = j.verdict === "supported" ?
|
|
93921
|
-
write(` ${verdictColor(`[${j.verdict}]`)} ${j.category}` +
|
|
93922
|
-
write(
|
|
94315
|
+
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;
|
|
94316
|
+
write(` ${verdictColor(`[${j.verdict}]`)} ${j.category}` + import_picocolors9.default.dim(` (${(j.confidence * 100).toFixed(0)}%)`));
|
|
94317
|
+
write(import_picocolors9.default.dim(` ${j.rationale}`));
|
|
93923
94318
|
}
|
|
93924
94319
|
write("");
|
|
93925
94320
|
}
|
|
93926
94321
|
if (judgment.rootCauseAssessment) {
|
|
93927
94322
|
const rca = judgment.rootCauseAssessment;
|
|
93928
|
-
const uncertaintyColor = rca.uncertainty === "low" ?
|
|
93929
|
-
write(
|
|
94323
|
+
const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors9.default.green : rca.uncertainty === "medium" ? import_picocolors9.default.yellow : import_picocolors9.default.red;
|
|
94324
|
+
write(import_picocolors9.default.bold("Root cause assessment:") + import_picocolors9.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
|
|
93930
94325
|
write(rca.summary);
|
|
93931
94326
|
if (rca.citedEvidenceIds.length > 0) {
|
|
93932
|
-
write(
|
|
94327
|
+
write(import_picocolors9.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
|
|
93933
94328
|
}
|
|
93934
94329
|
write("");
|
|
93935
94330
|
}
|
|
93936
|
-
write(
|
|
93937
|
-
write(
|
|
94331
|
+
write(import_picocolors9.default.bold("What:") + " " + judgment.what);
|
|
94332
|
+
write(import_picocolors9.default.bold("Why:") + " " + judgment.why);
|
|
93938
94333
|
if (judgment.whereNext.length > 0) {
|
|
93939
|
-
write(
|
|
94334
|
+
write(import_picocolors9.default.bold("Next steps:"));
|
|
93940
94335
|
for (const step of judgment.whereNext) {
|
|
93941
94336
|
write(` \u2022 ${step}`);
|
|
93942
94337
|
}
|
|
93943
94338
|
}
|
|
93944
94339
|
if (judgment.citations.length > 0) {
|
|
93945
|
-
write(
|
|
94340
|
+
write(import_picocolors9.default.dim(`
|
|
93946
94341
|
Cited evidence: ${judgment.citations.map((c) => c.evidenceId).join(", ")}`));
|
|
93947
94342
|
}
|
|
93948
94343
|
}
|
|
@@ -93960,6 +94355,7 @@ function classifyAIFailure(firstError) {
|
|
|
93960
94355
|
return firstError;
|
|
93961
94356
|
}
|
|
93962
94357
|
async function runInvestigate(hint, opts) {
|
|
94358
|
+
const startedAtMs = Date.now();
|
|
93963
94359
|
try {
|
|
93964
94360
|
const config2 = await loadConfig(opts.config, { name: opts.name });
|
|
93965
94361
|
const projectName = opts.project ?? opts.repo;
|
|
@@ -93967,7 +94363,7 @@ async function runInvestigate(hint, opts) {
|
|
|
93967
94363
|
try {
|
|
93968
94364
|
renv = resolveEnvironment(config2, { project: projectName, env: opts.env });
|
|
93969
94365
|
} catch (err) {
|
|
93970
|
-
console.error(
|
|
94366
|
+
console.error(import_picocolors9.default.red(err.message));
|
|
93971
94367
|
return 1;
|
|
93972
94368
|
}
|
|
93973
94369
|
const repoRoot = repoRootOrCwd(renv.path);
|
|
@@ -93976,16 +94372,15 @@ async function runInvestigate(hint, opts) {
|
|
|
93976
94372
|
const cloudSession = cloudActive ? authedClient() : null;
|
|
93977
94373
|
if (cloudActive && !cloudSession) {
|
|
93978
94374
|
console.error(
|
|
93979
|
-
|
|
93980
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
94375
|
+
import_picocolors9.default.yellow(
|
|
94376
|
+
`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.`
|
|
93981
94377
|
)
|
|
93982
94378
|
);
|
|
93983
|
-
return 1;
|
|
93984
94379
|
}
|
|
93985
94380
|
const code = codeForEnv(renv);
|
|
93986
94381
|
if (!code) {
|
|
93987
94382
|
console.error(
|
|
93988
|
-
|
|
94383
|
+
import_picocolors9.default.red(
|
|
93989
94384
|
`No source-intelligence connector configured for project "${renv.project}" / env "${renv.env}".`
|
|
93990
94385
|
)
|
|
93991
94386
|
);
|
|
@@ -93995,21 +94390,21 @@ async function runInvestigate(hint, opts) {
|
|
|
93995
94390
|
let health = await code.health();
|
|
93996
94391
|
if (!health.ok && sourceUrl) {
|
|
93997
94392
|
console.error(
|
|
93998
|
-
|
|
94393
|
+
import_picocolors9.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`)
|
|
93999
94394
|
);
|
|
94000
94395
|
const healed = await ensureSourceHost(renv.path, sourceUrl);
|
|
94001
94396
|
if (healed.ok) {
|
|
94002
|
-
console.error(
|
|
94397
|
+
console.error(import_picocolors9.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
|
|
94003
94398
|
health = await code.health();
|
|
94004
94399
|
} else {
|
|
94005
|
-
console.error(
|
|
94400
|
+
console.error(import_picocolors9.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
|
|
94006
94401
|
}
|
|
94007
94402
|
}
|
|
94008
94403
|
const runtimeOnly = !health.ok;
|
|
94009
94404
|
if (runtimeOnly) {
|
|
94010
94405
|
console.error(
|
|
94011
|
-
|
|
94012
|
-
`Proceeding in runtime-only mode \u2014 no source intelligence. Run ${
|
|
94406
|
+
import_picocolors9.default.yellow(
|
|
94407
|
+
`Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors9.default.bold("horus index")} for a full (code-aware) investigation.`
|
|
94013
94408
|
)
|
|
94014
94409
|
);
|
|
94015
94410
|
}
|
|
@@ -94054,7 +94449,7 @@ async function runInvestigate(hint, opts) {
|
|
|
94054
94449
|
model,
|
|
94055
94450
|
...ai.anthropicApiKey !== void 0 ? { apiKey: ai.anthropicApiKey } : {}
|
|
94056
94451
|
});
|
|
94057
|
-
console.log(
|
|
94452
|
+
console.log(import_picocolors9.default.dim(`[ai] model: ${model}`));
|
|
94058
94453
|
const narrativeInput = buildNarrativeInput(report);
|
|
94059
94454
|
const { output, fromProvider, degraded, validationErrors } = await renderNarrative(
|
|
94060
94455
|
narrativeInput,
|
|
@@ -94070,37 +94465,78 @@ async function runInvestigate(hint, opts) {
|
|
|
94070
94465
|
renderStoredAIJudgment(stored);
|
|
94071
94466
|
} else if (degraded) {
|
|
94072
94467
|
if (validationErrors && validationErrors.length > 0) {
|
|
94073
|
-
console.error(
|
|
94468
|
+
console.error(import_picocolors9.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
|
|
94074
94469
|
}
|
|
94075
94470
|
console.log("");
|
|
94076
|
-
console.log(
|
|
94077
|
-
console.log(
|
|
94471
|
+
console.log(import_picocolors9.default.dim("\u2500".repeat(60)));
|
|
94472
|
+
console.log(import_picocolors9.default.bold("AI narrative ") + import_picocolors9.default.yellow("(unstructured \u2014 not validated)"));
|
|
94078
94473
|
if (output.what?.trim()) {
|
|
94079
|
-
console.log(
|
|
94474
|
+
console.log(import_picocolors9.default.bold("\nWhat:"), output.what.trim());
|
|
94080
94475
|
}
|
|
94081
94476
|
if (output.why?.trim()) {
|
|
94082
|
-
console.log(
|
|
94477
|
+
console.log(import_picocolors9.default.bold("\nWhy:"), output.why.trim());
|
|
94083
94478
|
}
|
|
94084
94479
|
if (output.whereNext.length > 0) {
|
|
94085
|
-
console.log(
|
|
94480
|
+
console.log(import_picocolors9.default.bold("\nNext:"));
|
|
94086
94481
|
for (const a of output.whereNext) console.log(` - ${a}`);
|
|
94087
94482
|
}
|
|
94088
94483
|
} else {
|
|
94089
94484
|
const reason = classifyAIFailure(validationErrors?.[0]);
|
|
94090
|
-
console.error(
|
|
94485
|
+
console.error(import_picocolors9.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
|
|
94486
|
+
}
|
|
94487
|
+
}
|
|
94488
|
+
track({
|
|
94489
|
+
type: "investigation.completed",
|
|
94490
|
+
investigationId: report.id,
|
|
94491
|
+
confidence: typeof report.confidence === "number" ? report.confidence : null,
|
|
94492
|
+
evidenceCount: report.evidence?.length ?? 0,
|
|
94493
|
+
findingCount: report.findings?.length ?? 0,
|
|
94494
|
+
suspectedCauseCount: report.suspectedCauses?.length ?? 0,
|
|
94495
|
+
degraded: Boolean(report.degraded),
|
|
94496
|
+
gapCount: report.gapAnalysis?.gaps?.length ?? 0,
|
|
94497
|
+
hasAi: Boolean(report.aiJudgment)
|
|
94498
|
+
});
|
|
94499
|
+
if (isContentSharingEnabled()) {
|
|
94500
|
+
const hint2 = redactOrDrop(report.input.hint ?? "");
|
|
94501
|
+
if (!hint2.dropped) {
|
|
94502
|
+
track({
|
|
94503
|
+
type: "investigation.content",
|
|
94504
|
+
investigationId: report.id,
|
|
94505
|
+
hint: hint2.value ?? "",
|
|
94506
|
+
summary: redactContent(report.summary ?? ""),
|
|
94507
|
+
findingTitles: (report.findings ?? []).slice(0, 20).map((f) => redactContent(f.title ?? "")),
|
|
94508
|
+
suspectedCauseTitles: (report.suspectedCauses ?? []).slice(0, 20).map((c) => redactContent(c.title ?? "")),
|
|
94509
|
+
confidence: typeof report.confidence === "number" ? report.confidence : null
|
|
94510
|
+
});
|
|
94091
94511
|
}
|
|
94092
94512
|
}
|
|
94093
94513
|
if (cloudActive && cloudSession && cloudCfg) {
|
|
94094
94514
|
try {
|
|
94095
94515
|
const refs = await uploadInvestigationToCloud(cloudSession.client, cloudCfg, report);
|
|
94096
|
-
console.log(
|
|
94516
|
+
console.log(import_picocolors9.default.dim(`[cloud] investigation saved: ${refs.investigationId}`));
|
|
94097
94517
|
} catch (err) {
|
|
94098
94518
|
return reportCloudError(err);
|
|
94099
94519
|
}
|
|
94100
94520
|
}
|
|
94101
94521
|
if (format !== "json") {
|
|
94102
|
-
|
|
94522
|
+
if (report.persisted === false) {
|
|
94523
|
+
console.error(
|
|
94524
|
+
import_picocolors9.default.yellow(`
|
|
94525
|
+
\u26A0 Results were not saved \u2014 the investigation database was unreachable.`)
|
|
94526
|
+
);
|
|
94527
|
+
console.error(
|
|
94528
|
+
import_picocolors9.default.dim(
|
|
94529
|
+
` This report is display-only; \`horus ask\` won't find it. Start the database and re-run to persist.`
|
|
94530
|
+
)
|
|
94531
|
+
);
|
|
94532
|
+
} else {
|
|
94533
|
+
console.log(import_picocolors9.default.dim(`
|
|
94103
94534
|
Ask a follow-up: horus ask ${report.id} "<question>"`));
|
|
94535
|
+
}
|
|
94536
|
+
await maybePromptFeedback({
|
|
94537
|
+
investigationId: report.id,
|
|
94538
|
+
horusSeconds: (Date.now() - startedAtMs) / 1e3
|
|
94539
|
+
});
|
|
94104
94540
|
}
|
|
94105
94541
|
} finally {
|
|
94106
94542
|
await sql2.end();
|
|
@@ -94109,14 +94545,16 @@ Ask a follow-up: horus ask ${report.id} "<question>"`));
|
|
|
94109
94545
|
}
|
|
94110
94546
|
return 0;
|
|
94111
94547
|
} catch (err) {
|
|
94112
|
-
|
|
94548
|
+
const msg = err?.message || String(err);
|
|
94549
|
+
console.error(import_picocolors9.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
|
|
94550
|
+
if (process.env.HORUS_DEBUG) console.error(import_picocolors9.default.dim(err?.stack ?? String(err)));
|
|
94113
94551
|
return 1;
|
|
94114
94552
|
}
|
|
94115
94553
|
}
|
|
94116
94554
|
|
|
94117
94555
|
// ../../packages/cli/src/commands/changes.ts
|
|
94118
94556
|
init_cjs_shims();
|
|
94119
|
-
var
|
|
94557
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
94120
94558
|
var CHANGES_AI_CONTRACT = `Provide a clearly separated AI change-impact review with:
|
|
94121
94559
|
|
|
94122
94560
|
Highest-risk changes
|
|
@@ -94141,7 +94579,7 @@ async function runChanges(base2, compare, opts) {
|
|
|
94141
94579
|
const health = await code.health();
|
|
94142
94580
|
if (!health.ok) {
|
|
94143
94581
|
console.error(
|
|
94144
|
-
|
|
94582
|
+
import_picocolors10.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
94145
94583
|
);
|
|
94146
94584
|
return 1;
|
|
94147
94585
|
}
|
|
@@ -94163,20 +94601,20 @@ async function runChanges(base2, compare, opts) {
|
|
|
94163
94601
|
});
|
|
94164
94602
|
console.log("\n" + renderInterpretation(result));
|
|
94165
94603
|
if (!result.ok) {
|
|
94166
|
-
console.error(
|
|
94604
|
+
console.error(import_picocolors10.default.yellow(`[ai] ${result.warning}`));
|
|
94167
94605
|
}
|
|
94168
94606
|
}
|
|
94169
94607
|
}
|
|
94170
94608
|
return 0;
|
|
94171
94609
|
} catch (err) {
|
|
94172
|
-
console.error(
|
|
94610
|
+
console.error(import_picocolors10.default.red(err.message));
|
|
94173
94611
|
return 1;
|
|
94174
94612
|
}
|
|
94175
94613
|
}
|
|
94176
94614
|
|
|
94177
94615
|
// ../../packages/cli/src/commands/timeline.ts
|
|
94178
94616
|
init_cjs_shims();
|
|
94179
|
-
var
|
|
94617
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
94180
94618
|
var DEFAULT_SINCE = "7 days ago";
|
|
94181
94619
|
function resolveTimelineWindow(opts) {
|
|
94182
94620
|
const usingDefault = !opts.all && opts.since === void 0;
|
|
@@ -94206,7 +94644,7 @@ async function runTimeline(service, opts) {
|
|
|
94206
94644
|
try {
|
|
94207
94645
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
94208
94646
|
} catch (err) {
|
|
94209
|
-
console.error(
|
|
94647
|
+
console.error(import_picocolors11.default.red(err.message));
|
|
94210
94648
|
return 1;
|
|
94211
94649
|
}
|
|
94212
94650
|
const { code } = createConnectors(config2);
|
|
@@ -94221,9 +94659,9 @@ async function runTimeline(service, opts) {
|
|
|
94221
94659
|
console.log(renderChangeTimeline(t));
|
|
94222
94660
|
if (usingDefaultWindow) {
|
|
94223
94661
|
console.log(
|
|
94224
|
-
|
|
94662
|
+
import_picocolors11.default.dim(
|
|
94225
94663
|
`
|
|
94226
|
-
Showing the last 7 days (default). Widen with ${
|
|
94664
|
+
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")}.`
|
|
94227
94665
|
)
|
|
94228
94666
|
);
|
|
94229
94667
|
}
|
|
@@ -94240,20 +94678,20 @@ async function runTimeline(service, opts) {
|
|
|
94240
94678
|
});
|
|
94241
94679
|
console.log("\n" + renderInterpretation(result));
|
|
94242
94680
|
if (!result.ok) {
|
|
94243
|
-
console.error(
|
|
94681
|
+
console.error(import_picocolors11.default.yellow(`[ai] ${result.warning}`));
|
|
94244
94682
|
}
|
|
94245
94683
|
}
|
|
94246
94684
|
}
|
|
94247
94685
|
return 0;
|
|
94248
94686
|
} catch (err) {
|
|
94249
|
-
console.error(
|
|
94687
|
+
console.error(import_picocolors11.default.red(err.message));
|
|
94250
94688
|
return 1;
|
|
94251
94689
|
}
|
|
94252
94690
|
}
|
|
94253
94691
|
|
|
94254
94692
|
// ../../packages/cli/src/commands/what-changed.ts
|
|
94255
94693
|
init_cjs_shims();
|
|
94256
|
-
var
|
|
94694
|
+
var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
94257
94695
|
var DEFAULT_SINCE2 = "7 days ago";
|
|
94258
94696
|
var WHAT_CHANGED_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
94259
94697
|
|
|
@@ -94277,7 +94715,7 @@ async function runWhatChanged(service, opts) {
|
|
|
94277
94715
|
try {
|
|
94278
94716
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
94279
94717
|
} catch (err) {
|
|
94280
|
-
console.error(
|
|
94718
|
+
console.error(import_picocolors12.default.red(err.message));
|
|
94281
94719
|
return 1;
|
|
94282
94720
|
}
|
|
94283
94721
|
const { code } = createConnectors(config2);
|
|
@@ -94300,19 +94738,19 @@ async function runWhatChanged(service, opts) {
|
|
|
94300
94738
|
});
|
|
94301
94739
|
console.log("\n" + renderInterpretation(result));
|
|
94302
94740
|
if (!result.ok) {
|
|
94303
|
-
console.error(
|
|
94741
|
+
console.error(import_picocolors12.default.yellow(`[ai] ${result.warning}`));
|
|
94304
94742
|
}
|
|
94305
94743
|
}
|
|
94306
94744
|
return 0;
|
|
94307
94745
|
} catch (err) {
|
|
94308
|
-
console.error(
|
|
94746
|
+
console.error(import_picocolors12.default.red(err.message));
|
|
94309
94747
|
return 1;
|
|
94310
94748
|
}
|
|
94311
94749
|
}
|
|
94312
94750
|
|
|
94313
94751
|
// ../../packages/cli/src/commands/architecture.ts
|
|
94314
94752
|
init_cjs_shims();
|
|
94315
|
-
var
|
|
94753
|
+
var import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
94316
94754
|
var ARCHITECTURE_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
94317
94755
|
|
|
94318
94756
|
System summary
|
|
@@ -94341,7 +94779,7 @@ async function runArchitecture(opts) {
|
|
|
94341
94779
|
const health = await code.health();
|
|
94342
94780
|
if (!health.ok) {
|
|
94343
94781
|
console.error(
|
|
94344
|
-
|
|
94782
|
+
import_picocolors13.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
94345
94783
|
);
|
|
94346
94784
|
return 1;
|
|
94347
94785
|
}
|
|
@@ -94369,7 +94807,7 @@ async function runArchitecture(opts) {
|
|
|
94369
94807
|
});
|
|
94370
94808
|
console.log("\n" + renderInterpretation(result));
|
|
94371
94809
|
if (!result.ok) {
|
|
94372
|
-
console.error(
|
|
94810
|
+
console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
|
|
94373
94811
|
}
|
|
94374
94812
|
}
|
|
94375
94813
|
}
|
|
@@ -94378,14 +94816,14 @@ async function runArchitecture(opts) {
|
|
|
94378
94816
|
}
|
|
94379
94817
|
return 0;
|
|
94380
94818
|
} catch (err) {
|
|
94381
|
-
console.error(
|
|
94819
|
+
console.error(import_picocolors13.default.red(err.message));
|
|
94382
94820
|
return 1;
|
|
94383
94821
|
}
|
|
94384
94822
|
}
|
|
94385
94823
|
|
|
94386
94824
|
// ../../packages/cli/src/commands/blast-radius.ts
|
|
94387
94825
|
init_cjs_shims();
|
|
94388
|
-
var
|
|
94826
|
+
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
94389
94827
|
var BLAST_RADIUS_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
|
|
94390
94828
|
|
|
94391
94829
|
Evidence used
|
|
@@ -94412,7 +94850,7 @@ async function runBlastRadius(query, opts) {
|
|
|
94412
94850
|
const { code } = createConnectors(config2);
|
|
94413
94851
|
const health = await code.health();
|
|
94414
94852
|
if (!health.ok) {
|
|
94415
|
-
console.error(
|
|
94853
|
+
console.error(import_picocolors14.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
|
|
94416
94854
|
return 1;
|
|
94417
94855
|
}
|
|
94418
94856
|
let project;
|
|
@@ -94425,12 +94863,12 @@ async function runBlastRadius(query, opts) {
|
|
|
94425
94863
|
const r = await analyzeBlastRadius(query, { code, db, project }, opts.depth ?? 3);
|
|
94426
94864
|
if (!r) {
|
|
94427
94865
|
console.log(`No symbol found for: ${query}`);
|
|
94428
|
-
console.log(
|
|
94866
|
+
console.log(import_picocolors14.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
|
|
94429
94867
|
return 1;
|
|
94430
94868
|
}
|
|
94431
94869
|
if (r.seed.name.toLowerCase() !== query.toLowerCase()) {
|
|
94432
94870
|
console.log(
|
|
94433
|
-
|
|
94871
|
+
import_picocolors14.default.yellow(` No exact match for "${query}"`) + import_picocolors14.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
|
|
94434
94872
|
);
|
|
94435
94873
|
}
|
|
94436
94874
|
if (opts.json) {
|
|
@@ -94450,7 +94888,7 @@ async function runBlastRadius(query, opts) {
|
|
|
94450
94888
|
});
|
|
94451
94889
|
console.log("\n" + renderInterpretation(result));
|
|
94452
94890
|
if (!result.ok) {
|
|
94453
|
-
console.error(
|
|
94891
|
+
console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
|
|
94454
94892
|
}
|
|
94455
94893
|
}
|
|
94456
94894
|
}
|
|
@@ -94459,28 +94897,28 @@ async function runBlastRadius(query, opts) {
|
|
|
94459
94897
|
}
|
|
94460
94898
|
return 0;
|
|
94461
94899
|
} catch (err) {
|
|
94462
|
-
console.error(
|
|
94900
|
+
console.error(import_picocolors14.default.red(err.message));
|
|
94463
94901
|
return 1;
|
|
94464
94902
|
}
|
|
94465
94903
|
}
|
|
94466
94904
|
|
|
94467
94905
|
// ../../packages/cli/src/commands/repos.ts
|
|
94468
94906
|
init_cjs_shims();
|
|
94469
|
-
var
|
|
94907
|
+
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
94470
94908
|
async function runRepos(opts) {
|
|
94471
94909
|
const config2 = await loadConfig(opts.config);
|
|
94472
94910
|
const providers = repoProviders(config2);
|
|
94473
94911
|
if (providers.length === 0) {
|
|
94474
|
-
console.log(
|
|
94912
|
+
console.log(import_picocolors15.default.dim("No repositories configured."));
|
|
94475
94913
|
return 0;
|
|
94476
94914
|
}
|
|
94477
94915
|
const health = await reposHealth(providers);
|
|
94478
94916
|
for (const h of health) {
|
|
94479
|
-
const dot = h.reachable ?
|
|
94480
|
-
const name =
|
|
94481
|
-
const hostUrl =
|
|
94482
|
-
const path =
|
|
94483
|
-
const detail = h.detail ?
|
|
94917
|
+
const dot = h.reachable ? import_picocolors15.default.green("\u25CF") : import_picocolors15.default.red("\u25CF");
|
|
94918
|
+
const name = import_picocolors15.default.bold(h.repo.padEnd(20));
|
|
94919
|
+
const hostUrl = import_picocolors15.default.cyan(h.hostUrl.padEnd(30));
|
|
94920
|
+
const path = import_picocolors15.default.dim(h.path);
|
|
94921
|
+
const detail = h.detail ? import_picocolors15.default.dim(`(${h.detail})`) : "";
|
|
94484
94922
|
console.log(` ${dot} ${name} ${hostUrl} ${path} ${detail}`);
|
|
94485
94923
|
}
|
|
94486
94924
|
return 0;
|
|
@@ -94488,7 +94926,7 @@ async function runRepos(opts) {
|
|
|
94488
94926
|
|
|
94489
94927
|
// ../../packages/cli/src/commands/search.ts
|
|
94490
94928
|
init_cjs_shims();
|
|
94491
|
-
var
|
|
94929
|
+
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
94492
94930
|
async function runSearch(query, opts) {
|
|
94493
94931
|
const config2 = await loadConfig(opts.config);
|
|
94494
94932
|
const providers = repoProviders(config2);
|
|
@@ -94499,16 +94937,16 @@ async function runSearch(query, opts) {
|
|
|
94499
94937
|
}
|
|
94500
94938
|
let totalMatches = 0;
|
|
94501
94939
|
for (const result of results) {
|
|
94502
|
-
const statusDot = result.reachable ?
|
|
94940
|
+
const statusDot = result.reachable ? import_picocolors16.default.green("\u25CF") : import_picocolors16.default.red("\u25CF");
|
|
94503
94941
|
console.log("");
|
|
94504
|
-
console.log(`${statusDot} ${
|
|
94942
|
+
console.log(`${statusDot} ${import_picocolors16.default.bold("## " + result.repo)} ${import_picocolors16.default.dim(result.hostUrl)}`);
|
|
94505
94943
|
if (!result.reachable) {
|
|
94506
|
-
console.log(
|
|
94944
|
+
console.log(import_picocolors16.default.dim(" (unreachable)"));
|
|
94507
94945
|
} else if (result.symbols.length === 0) {
|
|
94508
|
-
console.log(
|
|
94946
|
+
console.log(import_picocolors16.default.dim(" (no matches)"));
|
|
94509
94947
|
} else {
|
|
94510
94948
|
for (const sym of result.symbols) {
|
|
94511
|
-
console.log(` - ${
|
|
94949
|
+
console.log(` - ${import_picocolors16.default.bold(sym.name)} ${import_picocolors16.default.dim(sym.filePath)}`);
|
|
94512
94950
|
}
|
|
94513
94951
|
totalMatches += result.symbols.length;
|
|
94514
94952
|
}
|
|
@@ -94516,7 +94954,7 @@ async function runSearch(query, opts) {
|
|
|
94516
94954
|
console.log("");
|
|
94517
94955
|
const reachableCount = results.filter((r) => r.reachable).length;
|
|
94518
94956
|
console.log(
|
|
94519
|
-
|
|
94957
|
+
import_picocolors16.default.dim(
|
|
94520
94958
|
`${totalMatches} match(es) across ${reachableCount}/${results.length} reachable repo(s) for query: "${query}"`
|
|
94521
94959
|
)
|
|
94522
94960
|
);
|
|
@@ -94525,7 +94963,7 @@ async function runSearch(query, opts) {
|
|
|
94525
94963
|
|
|
94526
94964
|
// ../../packages/cli/src/commands/investigations.ts
|
|
94527
94965
|
init_cjs_shims();
|
|
94528
|
-
var
|
|
94966
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
94529
94967
|
|
|
94530
94968
|
// ../../packages/cli/src/lib/format.ts
|
|
94531
94969
|
init_cjs_shims();
|
|
@@ -94568,8 +95006,8 @@ async function runInvestigations(opts) {
|
|
|
94568
95006
|
const session = authedClient();
|
|
94569
95007
|
if (!session) {
|
|
94570
95008
|
console.error(
|
|
94571
|
-
|
|
94572
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
95009
|
+
import_picocolors17.default.red(
|
|
95010
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors17.default.bold("horus login")} first.`
|
|
94573
95011
|
)
|
|
94574
95012
|
);
|
|
94575
95013
|
return 1;
|
|
@@ -94610,18 +95048,18 @@ async function runInvestigations(opts) {
|
|
|
94610
95048
|
|
|
94611
95049
|
// ../../packages/cli/src/commands/replay.ts
|
|
94612
95050
|
init_cjs_shims();
|
|
94613
|
-
var
|
|
95051
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
94614
95052
|
async function runReplay(id, opts) {
|
|
94615
95053
|
const { db, sql: sql2 } = createDb(await resolveDbUrl(opts.config));
|
|
94616
95054
|
try {
|
|
94617
95055
|
const row = await getInvestigation(db, id);
|
|
94618
95056
|
if (!row) {
|
|
94619
|
-
console.error(
|
|
95057
|
+
console.error(import_picocolors18.default.red("No investigation found: " + id));
|
|
94620
95058
|
return 1;
|
|
94621
95059
|
}
|
|
94622
95060
|
if (!row.report) {
|
|
94623
95061
|
console.error(
|
|
94624
|
-
|
|
95062
|
+
import_picocolors18.default.red(
|
|
94625
95063
|
"Investigation " + id + " has no stored report (run a newer investigation)."
|
|
94626
95064
|
)
|
|
94627
95065
|
);
|
|
@@ -94634,7 +95072,7 @@ async function runReplay(id, opts) {
|
|
|
94634
95072
|
if (opts.ai && fmt !== "json") {
|
|
94635
95073
|
if (report.aiJudgment && !opts.refreshAi) {
|
|
94636
95074
|
renderStoredAIJudgment(report.aiJudgment);
|
|
94637
|
-
console.log(
|
|
95075
|
+
console.log(import_picocolors18.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
|
|
94638
95076
|
} else {
|
|
94639
95077
|
const narrativeInput = buildNarrativeInput(report);
|
|
94640
95078
|
const { provider } = await buildNarrativeProvider({
|
|
@@ -94647,17 +95085,17 @@ async function runReplay(id, opts) {
|
|
|
94647
95085
|
);
|
|
94648
95086
|
if (!fromProvider && degraded) {
|
|
94649
95087
|
if (validationErrors?.length) {
|
|
94650
|
-
console.error(
|
|
95088
|
+
console.error(import_picocolors18.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
|
|
94651
95089
|
}
|
|
94652
95090
|
console.log("");
|
|
94653
|
-
console.log(
|
|
94654
|
-
console.log(
|
|
94655
|
-
if (output.what?.trim()) console.log(
|
|
94656
|
-
if (output.why?.trim()) console.log(
|
|
95091
|
+
console.log(import_picocolors18.default.dim("\u2500".repeat(60)));
|
|
95092
|
+
console.log(import_picocolors18.default.bold("AI narrative ") + import_picocolors18.default.yellow("(unstructured \u2014 not validated)"));
|
|
95093
|
+
if (output.what?.trim()) console.log(import_picocolors18.default.bold("\nWhat:"), output.what.trim());
|
|
95094
|
+
if (output.why?.trim()) console.log(import_picocolors18.default.bold("\nWhy:"), output.why.trim());
|
|
94657
95095
|
} else if (!fromProvider) {
|
|
94658
|
-
console.error(
|
|
95096
|
+
console.error(import_picocolors18.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
|
|
94659
95097
|
if (validationErrors?.length) {
|
|
94660
|
-
console.error(
|
|
95098
|
+
console.error(import_picocolors18.default.dim(` ${validationErrors[0]}`));
|
|
94661
95099
|
}
|
|
94662
95100
|
} else {
|
|
94663
95101
|
const stored = narrativeOutputToStoredJudgment(output, "anthropic");
|
|
@@ -94678,7 +95116,7 @@ async function runReplay(id, opts) {
|
|
|
94678
95116
|
|
|
94679
95117
|
// ../../packages/cli/src/commands/owner.ts
|
|
94680
95118
|
init_cjs_shims();
|
|
94681
|
-
var
|
|
95119
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
94682
95120
|
async function runOwner(query, opts) {
|
|
94683
95121
|
try {
|
|
94684
95122
|
const config2 = await loadConfig(opts.config);
|
|
@@ -94686,7 +95124,7 @@ async function runOwner(query, opts) {
|
|
|
94686
95124
|
try {
|
|
94687
95125
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
94688
95126
|
} catch (err) {
|
|
94689
|
-
console.error(
|
|
95127
|
+
console.error(import_picocolors19.default.red(err.message));
|
|
94690
95128
|
return 1;
|
|
94691
95129
|
}
|
|
94692
95130
|
const code = codeForRepo(config2, renv.project);
|
|
@@ -94694,16 +95132,16 @@ async function runOwner(query, opts) {
|
|
|
94694
95132
|
console.log(opts.json ? ownershipToJSON(o) : renderOwnership(o));
|
|
94695
95133
|
return 0;
|
|
94696
95134
|
} catch (err) {
|
|
94697
|
-
console.error(
|
|
95135
|
+
console.error(import_picocolors19.default.red(err.message));
|
|
94698
95136
|
return 1;
|
|
94699
95137
|
}
|
|
94700
95138
|
}
|
|
94701
95139
|
|
|
94702
95140
|
// ../../packages/cli/src/commands/postmortem.ts
|
|
94703
95141
|
init_cjs_shims();
|
|
94704
|
-
var
|
|
94705
|
-
var
|
|
94706
|
-
var
|
|
95142
|
+
var import_node_fs15 = require("fs");
|
|
95143
|
+
var import_node_path16 = require("path");
|
|
95144
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
94707
95145
|
async function runPostmortem(id, opts) {
|
|
94708
95146
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
94709
95147
|
let sql2;
|
|
@@ -94717,11 +95155,11 @@ async function runPostmortem(id, opts) {
|
|
|
94717
95155
|
const { db } = conn;
|
|
94718
95156
|
const row = await getInvestigation(db, id);
|
|
94719
95157
|
if (!row) {
|
|
94720
|
-
log(
|
|
95158
|
+
log(import_picocolors20.default.red("No investigation found: " + id));
|
|
94721
95159
|
return 1;
|
|
94722
95160
|
}
|
|
94723
95161
|
if (!row.report) {
|
|
94724
|
-
log(
|
|
95162
|
+
log(import_picocolors20.default.red("Investigation " + id + " has no stored report."));
|
|
94725
95163
|
return 1;
|
|
94726
95164
|
}
|
|
94727
95165
|
report = migrateReport(row.report);
|
|
@@ -94813,25 +95251,25 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
94813
95251
|
}
|
|
94814
95252
|
}
|
|
94815
95253
|
if (opts.output) {
|
|
94816
|
-
const outputPath = (0,
|
|
94817
|
-
if ((0,
|
|
94818
|
-
log(
|
|
94819
|
-
log(
|
|
95254
|
+
const outputPath = (0, import_node_path16.resolve)(opts.output);
|
|
95255
|
+
if ((0, import_node_fs15.existsSync)(outputPath) && !opts.force) {
|
|
95256
|
+
log(import_picocolors20.default.red("\u2717") + " " + outputPath + " already exists");
|
|
95257
|
+
log(import_picocolors20.default.dim(" pass --force to overwrite"));
|
|
94820
95258
|
return 1;
|
|
94821
95259
|
}
|
|
94822
95260
|
try {
|
|
94823
|
-
(0,
|
|
95261
|
+
(0, import_node_fs15.writeFileSync)(outputPath, content, "utf8");
|
|
94824
95262
|
} catch (err) {
|
|
94825
|
-
log(
|
|
95263
|
+
log(import_picocolors20.default.red("\u2717 Could not write " + outputPath + ": ") + err.message);
|
|
94826
95264
|
return 1;
|
|
94827
95265
|
}
|
|
94828
|
-
log(
|
|
95266
|
+
log(import_picocolors20.default.green("\u2713") + " Saved postmortem to " + outputPath);
|
|
94829
95267
|
} else {
|
|
94830
95268
|
log(content);
|
|
94831
95269
|
}
|
|
94832
95270
|
} catch (err) {
|
|
94833
95271
|
const message = err instanceof Error ? err.message : String(err);
|
|
94834
|
-
log(
|
|
95272
|
+
log(import_picocolors20.default.red("postmortem failed: " + message));
|
|
94835
95273
|
return 1;
|
|
94836
95274
|
} finally {
|
|
94837
95275
|
if (sql2) {
|
|
@@ -94843,7 +95281,7 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
|
|
|
94843
95281
|
|
|
94844
95282
|
// ../../packages/cli/src/commands/score.ts
|
|
94845
95283
|
init_cjs_shims();
|
|
94846
|
-
var
|
|
95284
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
94847
95285
|
var SCORE_AI_CONTRACT = `Provide a clearly separated AI score explanation with:
|
|
94848
95286
|
|
|
94849
95287
|
Why this scored this way
|
|
@@ -94862,11 +95300,11 @@ async function runScore(id, opts) {
|
|
|
94862
95300
|
try {
|
|
94863
95301
|
const row = await getInvestigation(db, id);
|
|
94864
95302
|
if (!row) {
|
|
94865
|
-
console.error(
|
|
95303
|
+
console.error(import_picocolors21.default.red("No investigation found: " + id));
|
|
94866
95304
|
return 1;
|
|
94867
95305
|
}
|
|
94868
95306
|
if (!row.report) {
|
|
94869
|
-
console.error(
|
|
95307
|
+
console.error(import_picocolors21.default.red("Investigation " + id + " has no stored report."));
|
|
94870
95308
|
return 1;
|
|
94871
95309
|
}
|
|
94872
95310
|
const s = scoreInvestigation(migrateReport(row.report));
|
|
@@ -94887,7 +95325,7 @@ async function runScore(id, opts) {
|
|
|
94887
95325
|
});
|
|
94888
95326
|
console.log("\n" + renderInterpretation(result));
|
|
94889
95327
|
if (!result.ok) {
|
|
94890
|
-
console.error(
|
|
95328
|
+
console.error(import_picocolors21.default.yellow(`[ai] ${result.warning}`));
|
|
94891
95329
|
}
|
|
94892
95330
|
}
|
|
94893
95331
|
}
|
|
@@ -94927,7 +95365,7 @@ async function runScores(opts) {
|
|
|
94927
95365
|
|
|
94928
95366
|
// ../../packages/cli/src/commands/ask.ts
|
|
94929
95367
|
init_cjs_shims();
|
|
94930
|
-
var
|
|
95368
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
94931
95369
|
function renderAnswer(report, directive, opts) {
|
|
94932
95370
|
const answer = answerQuestion(report, directive);
|
|
94933
95371
|
if (answer) {
|
|
@@ -94939,13 +95377,13 @@ function renderAnswer(report, directive, opts) {
|
|
|
94939
95377
|
if (!opts.json && report.aiJudgment) {
|
|
94940
95378
|
const j = report.aiJudgment;
|
|
94941
95379
|
console.log("");
|
|
94942
|
-
console.log(
|
|
94943
|
-
console.log(
|
|
95380
|
+
console.log(import_picocolors22.default.dim("\u2500".repeat(60)));
|
|
95381
|
+
console.log(import_picocolors22.default.dim(`Stored AI judgment (${j.provider}, ${j.generatedAt}):`));
|
|
94944
95382
|
if (j.rootCauseAssessment) {
|
|
94945
|
-
console.log(
|
|
94946
|
-
console.log(
|
|
95383
|
+
console.log(import_picocolors22.default.bold("Root cause (AI):"), j.rootCauseAssessment.summary);
|
|
95384
|
+
console.log(import_picocolors22.default.dim(`Uncertainty: ${j.rootCauseAssessment.uncertainty}`));
|
|
94947
95385
|
} else {
|
|
94948
|
-
console.log(
|
|
95386
|
+
console.log(import_picocolors22.default.bold("AI Why:"), j.why);
|
|
94949
95387
|
}
|
|
94950
95388
|
}
|
|
94951
95389
|
return 0;
|
|
@@ -94974,8 +95412,8 @@ async function runAsk(id, directive, opts) {
|
|
|
94974
95412
|
const session = authedClient();
|
|
94975
95413
|
if (!session) {
|
|
94976
95414
|
console.error(
|
|
94977
|
-
|
|
94978
|
-
`This repo is linked to Horus Cloud but you are not logged in. Run ${
|
|
95415
|
+
import_picocolors22.default.red(
|
|
95416
|
+
`This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors22.default.bold("horus login")} first.`
|
|
94979
95417
|
)
|
|
94980
95418
|
);
|
|
94981
95419
|
return 1;
|
|
@@ -94984,15 +95422,15 @@ async function runAsk(id, directive, opts) {
|
|
|
94984
95422
|
const report = await fetchInvestigationReportFromCloud(session.client, cloudCfg, id);
|
|
94985
95423
|
if (report) return renderAnswer(report, directive, opts);
|
|
94986
95424
|
console.error(
|
|
94987
|
-
|
|
94988
|
-
`Investigation ${id} has no saved report locally or in Horus Cloud. Run ${
|
|
95425
|
+
import_picocolors22.default.red(
|
|
95426
|
+
`Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors22.default.bold("horus investigate")} first.`
|
|
94989
95427
|
)
|
|
94990
95428
|
);
|
|
94991
95429
|
return 1;
|
|
94992
95430
|
} catch (err) {
|
|
94993
95431
|
if (err instanceof CloudError && err.status === 404) {
|
|
94994
95432
|
console.error(
|
|
94995
|
-
|
|
95433
|
+
import_picocolors22.default.red(
|
|
94996
95434
|
`No investigation found for "${id}" locally or in Horus Cloud. Use the id printed at the top of \`horus investigate\` output.`
|
|
94997
95435
|
)
|
|
94998
95436
|
);
|
|
@@ -95006,21 +95444,21 @@ async function runAsk(id, directive, opts) {
|
|
|
95006
95444
|
case "found":
|
|
95007
95445
|
return renderAnswer(local.report, directive, opts);
|
|
95008
95446
|
case "no-report":
|
|
95009
|
-
console.error(
|
|
95447
|
+
console.error(import_picocolors22.default.red("Investigation " + id + " has no stored report."));
|
|
95010
95448
|
return 1;
|
|
95011
95449
|
case "error":
|
|
95012
|
-
console.error(
|
|
95450
|
+
console.error(import_picocolors22.default.red(local.message));
|
|
95013
95451
|
return 1;
|
|
95014
95452
|
case "not-found":
|
|
95015
95453
|
default:
|
|
95016
|
-
console.error(
|
|
95454
|
+
console.error(import_picocolors22.default.red("No investigation found: " + id));
|
|
95017
95455
|
return 1;
|
|
95018
95456
|
}
|
|
95019
95457
|
}
|
|
95020
95458
|
|
|
95021
95459
|
// ../../packages/cli/src/commands/onboard.ts
|
|
95022
95460
|
init_cjs_shims();
|
|
95023
|
-
var
|
|
95461
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
95024
95462
|
var ONBOARD_AI_CONTRACT = `Provide a clearly separated AI onboarding guide with:
|
|
95025
95463
|
|
|
95026
95464
|
Start here
|
|
@@ -95048,7 +95486,7 @@ async function runOnboard(area, opts) {
|
|
|
95048
95486
|
try {
|
|
95049
95487
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
95050
95488
|
} catch (err) {
|
|
95051
|
-
console.error(
|
|
95489
|
+
console.error(import_picocolors23.default.red(err.message));
|
|
95052
95490
|
return 1;
|
|
95053
95491
|
}
|
|
95054
95492
|
const repo = { name: renv.project, path: renv.path };
|
|
@@ -95056,7 +95494,7 @@ async function runOnboard(area, opts) {
|
|
|
95056
95494
|
const health = await code.health();
|
|
95057
95495
|
if (!health.ok) {
|
|
95058
95496
|
console.error(
|
|
95059
|
-
|
|
95497
|
+
import_picocolors23.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
95060
95498
|
);
|
|
95061
95499
|
return 1;
|
|
95062
95500
|
}
|
|
@@ -95083,7 +95521,7 @@ async function runOnboard(area, opts) {
|
|
|
95083
95521
|
});
|
|
95084
95522
|
console.log("\n" + renderInterpretation(result));
|
|
95085
95523
|
if (!result.ok) {
|
|
95086
|
-
console.error(
|
|
95524
|
+
console.error(import_picocolors23.default.yellow(`[ai] ${result.warning}`));
|
|
95087
95525
|
}
|
|
95088
95526
|
}
|
|
95089
95527
|
}
|
|
@@ -95092,14 +95530,14 @@ async function runOnboard(area, opts) {
|
|
|
95092
95530
|
}
|
|
95093
95531
|
return 0;
|
|
95094
95532
|
} catch (err) {
|
|
95095
|
-
console.error(
|
|
95533
|
+
console.error(import_picocolors23.default.red(err.message));
|
|
95096
95534
|
return 1;
|
|
95097
95535
|
}
|
|
95098
95536
|
}
|
|
95099
95537
|
|
|
95100
95538
|
// ../../packages/cli/src/commands/simulate.ts
|
|
95101
95539
|
init_cjs_shims();
|
|
95102
|
-
var
|
|
95540
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
95103
95541
|
async function runSimulate(scenarioId, opts) {
|
|
95104
95542
|
try {
|
|
95105
95543
|
if (scenarioId === void 0) {
|
|
@@ -95109,7 +95547,7 @@ async function runSimulate(scenarioId, opts) {
|
|
|
95109
95547
|
const scenario = getScenario(scenarioId);
|
|
95110
95548
|
if (scenario === null) {
|
|
95111
95549
|
console.error(
|
|
95112
|
-
|
|
95550
|
+
import_picocolors24.default.red(
|
|
95113
95551
|
`Unknown scenario: "${scenarioId}". Run horus simulate to list available scenarios.`
|
|
95114
95552
|
)
|
|
95115
95553
|
);
|
|
@@ -95120,14 +95558,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
95120
95558
|
try {
|
|
95121
95559
|
renv = resolveEnvironment(config2, { project: opts.repo });
|
|
95122
95560
|
} catch (err) {
|
|
95123
|
-
console.error(
|
|
95561
|
+
console.error(import_picocolors24.default.red(err.message));
|
|
95124
95562
|
return 1;
|
|
95125
95563
|
}
|
|
95126
95564
|
const code = codeForRepo(config2, renv.project);
|
|
95127
95565
|
const health = await code.health();
|
|
95128
95566
|
if (!health.ok) {
|
|
95129
95567
|
console.error(
|
|
95130
|
-
|
|
95568
|
+
import_picocolors24.default.red("Source-intelligence host unreachable \u2014 run: horus index")
|
|
95131
95569
|
);
|
|
95132
95570
|
return 1;
|
|
95133
95571
|
}
|
|
@@ -95144,14 +95582,14 @@ async function runSimulate(scenarioId, opts) {
|
|
|
95144
95582
|
}
|
|
95145
95583
|
return 0;
|
|
95146
95584
|
} catch (err) {
|
|
95147
|
-
console.error(
|
|
95585
|
+
console.error(import_picocolors24.default.red(err.message));
|
|
95148
95586
|
return 1;
|
|
95149
95587
|
}
|
|
95150
95588
|
}
|
|
95151
95589
|
|
|
95152
95590
|
// ../../packages/cli/src/commands/logs.ts
|
|
95153
95591
|
init_cjs_shims();
|
|
95154
|
-
var
|
|
95592
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
95155
95593
|
var LOGS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
95156
95594
|
|
|
95157
95595
|
Evidence used
|
|
@@ -95184,9 +95622,9 @@ function resolveRawLevel(opts) {
|
|
|
95184
95622
|
return opts.level ?? "error";
|
|
95185
95623
|
}
|
|
95186
95624
|
function levelColor(level, text2) {
|
|
95187
|
-
if (level === "error" || level === "fatal") return
|
|
95188
|
-
if (level === "warn") return
|
|
95189
|
-
return
|
|
95625
|
+
if (level === "error" || level === "fatal") return import_picocolors25.default.red(text2);
|
|
95626
|
+
if (level === "warn") return import_picocolors25.default.yellow(text2);
|
|
95627
|
+
return import_picocolors25.default.dim(text2);
|
|
95190
95628
|
}
|
|
95191
95629
|
function matchServiceLabel(requested, candidates) {
|
|
95192
95630
|
if (candidates.includes(requested)) return { kind: "unique", matched: requested };
|
|
@@ -95203,13 +95641,13 @@ async function runLogs(service, opts) {
|
|
|
95203
95641
|
try {
|
|
95204
95642
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
95205
95643
|
} catch (err) {
|
|
95206
|
-
console.error(
|
|
95644
|
+
console.error(import_picocolors25.default.red(err.message));
|
|
95207
95645
|
return 1;
|
|
95208
95646
|
}
|
|
95209
95647
|
const logs = logsForEnv(renv);
|
|
95210
95648
|
if (logs === null) {
|
|
95211
95649
|
console.error(
|
|
95212
|
-
|
|
95650
|
+
import_picocolors25.default.red(
|
|
95213
95651
|
`No Elasticsearch connector configured for ${renv.project}/${renv.env} (set ES_URL / ES_USERNAME / ES_PASSWORD)`
|
|
95214
95652
|
)
|
|
95215
95653
|
);
|
|
@@ -95217,7 +95655,7 @@ async function runLogs(service, opts) {
|
|
|
95217
95655
|
}
|
|
95218
95656
|
const health = await logs.health();
|
|
95219
95657
|
if (!health.ok) {
|
|
95220
|
-
console.error(
|
|
95658
|
+
console.error(import_picocolors25.default.red(`Elasticsearch unreachable: ${health.detail}`));
|
|
95221
95659
|
return 1;
|
|
95222
95660
|
}
|
|
95223
95661
|
const resolvedService = service ?? renv.connectors.elasticsearch?.serviceName;
|
|
@@ -95230,21 +95668,21 @@ async function runLogs(service, opts) {
|
|
|
95230
95668
|
requiresEventCode: aggregatesEventCode
|
|
95231
95669
|
});
|
|
95232
95670
|
for (const w of compat.issues.filter((i) => i.severity === "warning")) {
|
|
95233
|
-
console.warn(
|
|
95671
|
+
console.warn(import_picocolors25.default.yellow(`[warn] ${w.field}: ${w.message}`));
|
|
95234
95672
|
}
|
|
95235
95673
|
const errors2 = compat.issues.filter((i) => i.severity === "error");
|
|
95236
95674
|
if (errors2.length > 0) {
|
|
95237
|
-
console.error(
|
|
95675
|
+
console.error(import_picocolors25.default.red("Elasticsearch field mapping is incompatible with the index:"));
|
|
95238
95676
|
for (const e of errors2) {
|
|
95239
|
-
console.error(
|
|
95677
|
+
console.error(import_picocolors25.default.red(` ${e.field}: ${e.message}`));
|
|
95240
95678
|
}
|
|
95241
95679
|
console.error(
|
|
95242
|
-
|
|
95680
|
+
import_picocolors25.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
|
|
95243
95681
|
);
|
|
95244
95682
|
return 1;
|
|
95245
95683
|
}
|
|
95246
95684
|
} catch {
|
|
95247
|
-
console.warn(
|
|
95685
|
+
console.warn(import_picocolors25.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
|
|
95248
95686
|
}
|
|
95249
95687
|
const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
|
|
95250
95688
|
const fromDisplay = from.slice(0, 16).replace("T", " ");
|
|
@@ -95295,7 +95733,7 @@ async function runLogs(service, opts) {
|
|
|
95295
95733
|
return 0;
|
|
95296
95734
|
}
|
|
95297
95735
|
if (records.length === 0) {
|
|
95298
|
-
console.log(
|
|
95736
|
+
console.log(import_picocolors25.default.dim("No logs matched."));
|
|
95299
95737
|
return 0;
|
|
95300
95738
|
}
|
|
95301
95739
|
for (const r of records) {
|
|
@@ -95305,7 +95743,7 @@ async function runLogs(service, opts) {
|
|
|
95305
95743
|
const line2 = `${ts} ${lvl} ${comp} ${r.message.slice(0, 120)}`;
|
|
95306
95744
|
console.log(levelColor(r.level, line2));
|
|
95307
95745
|
for (const { key, value } of extractContextFields(r)) {
|
|
95308
|
-
console.log(
|
|
95746
|
+
console.log(import_picocolors25.default.dim(` ${key}: ${value.slice(0, 200)}`));
|
|
95309
95747
|
}
|
|
95310
95748
|
}
|
|
95311
95749
|
return 0;
|
|
@@ -95344,22 +95782,22 @@ async function runLogs(service, opts) {
|
|
|
95344
95782
|
return 0;
|
|
95345
95783
|
}
|
|
95346
95784
|
console.log(
|
|
95347
|
-
|
|
95785
|
+
import_picocolors25.default.bold(`Top ${field}`) + import_picocolors25.default.dim(
|
|
95348
95786
|
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
95349
95787
|
)
|
|
95350
95788
|
);
|
|
95351
|
-
console.log(
|
|
95789
|
+
console.log(import_picocolors25.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
95352
95790
|
console.log("");
|
|
95353
95791
|
if (buckets.length === 0) {
|
|
95354
|
-
console.log(
|
|
95355
|
-
console.log(
|
|
95792
|
+
console.log(import_picocolors25.default.dim(` No error logs with field "${field}" in the window.`));
|
|
95793
|
+
console.log(import_picocolors25.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
|
|
95356
95794
|
return 0;
|
|
95357
95795
|
}
|
|
95358
95796
|
for (const b2 of buckets) {
|
|
95359
|
-
console.log(` ${
|
|
95797
|
+
console.log(` ${import_picocolors25.default.red(String(b2.count).padStart(6))} ${b2.key}`);
|
|
95360
95798
|
}
|
|
95361
95799
|
console.log("");
|
|
95362
|
-
console.log(
|
|
95800
|
+
console.log(import_picocolors25.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
|
|
95363
95801
|
return 0;
|
|
95364
95802
|
}
|
|
95365
95803
|
let analysis = await logs.analyzeErrors({
|
|
@@ -95426,46 +95864,46 @@ async function runLogs(service, opts) {
|
|
|
95426
95864
|
return 0;
|
|
95427
95865
|
}
|
|
95428
95866
|
console.log(
|
|
95429
|
-
|
|
95867
|
+
import_picocolors25.default.bold(`Error analysis`) + import_picocolors25.default.dim(
|
|
95430
95868
|
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
95431
95869
|
)
|
|
95432
95870
|
);
|
|
95433
|
-
console.log(
|
|
95871
|
+
console.log(import_picocolors25.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
95434
95872
|
if (broadeningNote) {
|
|
95435
|
-
console.log(
|
|
95873
|
+
console.log(import_picocolors25.default.yellow(` ${broadeningNote}`));
|
|
95436
95874
|
}
|
|
95437
95875
|
console.log(
|
|
95438
|
-
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${
|
|
95876
|
+
` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors25.default.yellow(String(analysis.newSignatures.length))} new`
|
|
95439
95877
|
);
|
|
95440
95878
|
console.log("");
|
|
95441
95879
|
if (analysis.signatures.length === 0) {
|
|
95442
|
-
console.log(
|
|
95880
|
+
console.log(import_picocolors25.default.dim(" No error-level logs in the window."));
|
|
95443
95881
|
console.log(
|
|
95444
|
-
|
|
95882
|
+
import_picocolors25.default.dim(
|
|
95445
95883
|
` Searched: ${scopeService ? `service "${scopeService}"` : "all services"} \xB7 index: ${indexPattern} \xB7 from: ${fromDisplay} UTC`
|
|
95446
95884
|
)
|
|
95447
95885
|
);
|
|
95448
|
-
console.log(
|
|
95886
|
+
console.log(import_picocolors25.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
|
|
95449
95887
|
return 0;
|
|
95450
95888
|
}
|
|
95451
95889
|
for (const s of analysis.signatures) {
|
|
95452
95890
|
const tags = [];
|
|
95453
|
-
if (s.isNew) tags.push(
|
|
95891
|
+
if (s.isNew) tags.push(import_picocolors25.default.red("NEW"));
|
|
95454
95892
|
else if (s.ratio !== void 0 && Number.isFinite(s.ratio) && s.ratio >= 1.5) {
|
|
95455
|
-
tags.push(
|
|
95893
|
+
tags.push(import_picocolors25.default.yellow(`spike x${s.ratio.toFixed(1)}`));
|
|
95456
95894
|
}
|
|
95457
|
-
const svc = s.services.length > 0 ?
|
|
95895
|
+
const svc = s.services.length > 0 ? import_picocolors25.default.dim(` \xB7 ${s.services.slice(0, 3).join(", ")}`) : "";
|
|
95458
95896
|
const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
|
|
95459
95897
|
console.log(
|
|
95460
|
-
` ${
|
|
95898
|
+
` ${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
|
|
95461
95899
|
);
|
|
95462
95900
|
}
|
|
95463
95901
|
if (analysis.affectedServices.length > 0) {
|
|
95464
95902
|
console.log("");
|
|
95465
|
-
console.log(` ${
|
|
95903
|
+
console.log(` ${import_picocolors25.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
|
|
95466
95904
|
}
|
|
95467
95905
|
console.log("");
|
|
95468
|
-
console.log(
|
|
95906
|
+
console.log(import_picocolors25.default.dim(" (use --raw to see individual log lines)"));
|
|
95469
95907
|
if (opts.ai) {
|
|
95470
95908
|
const result = await renderAiInterpretation({
|
|
95471
95909
|
command: "logs",
|
|
@@ -95484,19 +95922,19 @@ async function runLogs(service, opts) {
|
|
|
95484
95922
|
});
|
|
95485
95923
|
console.log("\n" + renderInterpretation(result));
|
|
95486
95924
|
if (!result.ok) {
|
|
95487
|
-
console.error(
|
|
95925
|
+
console.error(import_picocolors25.default.yellow(`[ai] ${result.warning}`));
|
|
95488
95926
|
}
|
|
95489
95927
|
}
|
|
95490
95928
|
return 0;
|
|
95491
95929
|
} catch (err) {
|
|
95492
|
-
console.error(
|
|
95930
|
+
console.error(import_picocolors25.default.red(err.message));
|
|
95493
95931
|
return 1;
|
|
95494
95932
|
}
|
|
95495
95933
|
}
|
|
95496
95934
|
|
|
95497
95935
|
// ../../packages/cli/src/commands/metrics.ts
|
|
95498
95936
|
init_cjs_shims();
|
|
95499
|
-
var
|
|
95937
|
+
var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
95500
95938
|
var METRICS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
95501
95939
|
|
|
95502
95940
|
Evidence used
|
|
@@ -95541,10 +95979,10 @@ function anomalyColor(anomaly) {
|
|
|
95541
95979
|
case "latency-spike":
|
|
95542
95980
|
case "error-rate-change":
|
|
95543
95981
|
case "throughput-drop":
|
|
95544
|
-
return
|
|
95982
|
+
return import_picocolors26.default.red;
|
|
95545
95983
|
case "queue-growth":
|
|
95546
95984
|
case "change":
|
|
95547
|
-
return
|
|
95985
|
+
return import_picocolors26.default.yellow;
|
|
95548
95986
|
default:
|
|
95549
95987
|
return (s) => s;
|
|
95550
95988
|
}
|
|
@@ -95571,7 +96009,7 @@ async function runMetrics(hint, opts) {
|
|
|
95571
96009
|
const metrics = metricsProviderFromConfig(config2);
|
|
95572
96010
|
if (metrics === null) {
|
|
95573
96011
|
console.error(
|
|
95574
|
-
|
|
96012
|
+
import_picocolors26.default.red(
|
|
95575
96013
|
"Grafana not configured \u2014 set GRAFANA_URL + GRAFANA_USER + GRAFANA_PASSWORD"
|
|
95576
96014
|
)
|
|
95577
96015
|
);
|
|
@@ -95579,7 +96017,7 @@ async function runMetrics(hint, opts) {
|
|
|
95579
96017
|
}
|
|
95580
96018
|
const health = await metrics.health();
|
|
95581
96019
|
if (!health.ok) {
|
|
95582
|
-
console.error(
|
|
96020
|
+
console.error(import_picocolors26.default.red(`Grafana unreachable: ${health.detail}`));
|
|
95583
96021
|
return 1;
|
|
95584
96022
|
}
|
|
95585
96023
|
const stepNum = opts.step !== void 0 ? Number(opts.step) : void 0;
|
|
@@ -95608,14 +96046,14 @@ async function runMetrics(hint, opts) {
|
|
|
95608
96046
|
return 0;
|
|
95609
96047
|
}
|
|
95610
96048
|
if (series.length === 0) {
|
|
95611
|
-
console.log(
|
|
96049
|
+
console.log(import_picocolors26.default.dim("No series returned."));
|
|
95612
96050
|
return 0;
|
|
95613
96051
|
}
|
|
95614
96052
|
for (const s of series) {
|
|
95615
96053
|
const summary = summarize(s);
|
|
95616
96054
|
const label = labelStr(s.labels);
|
|
95617
96055
|
console.log(
|
|
95618
|
-
` ${
|
|
96056
|
+
` ${import_picocolors26.default.cyan(label || "(no labels)")} last=${import_picocolors26.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
|
|
95619
96057
|
);
|
|
95620
96058
|
}
|
|
95621
96059
|
return 0;
|
|
@@ -95630,36 +96068,36 @@ async function runMetrics(hint, opts) {
|
|
|
95630
96068
|
if (findings2.length === 0) {
|
|
95631
96069
|
if (hint !== void 0) {
|
|
95632
96070
|
const tokens = extractHintTokens(hint);
|
|
95633
|
-
console.log(
|
|
96071
|
+
console.log(import_picocolors26.default.dim(`No panels matched hint "${hint}".`));
|
|
95634
96072
|
if (tokens.length > 0) {
|
|
95635
|
-
console.log(
|
|
96073
|
+
console.log(import_picocolors26.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
|
|
95636
96074
|
}
|
|
95637
96075
|
console.log(
|
|
95638
|
-
|
|
95639
|
-
` Tip: run ${
|
|
96076
|
+
import_picocolors26.default.dim(
|
|
96077
|
+
` Tip: run ${import_picocolors26.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
|
|
95640
96078
|
)
|
|
95641
96079
|
);
|
|
95642
96080
|
} else {
|
|
95643
|
-
console.log(
|
|
96081
|
+
console.log(import_picocolors26.default.dim("No panels found in configured Grafana dashboards."));
|
|
95644
96082
|
}
|
|
95645
96083
|
return 0;
|
|
95646
96084
|
}
|
|
95647
96085
|
const hintSuffix = hint !== void 0 ? ` (hint: "${hint}")` : "";
|
|
95648
96086
|
const matchSources = hint !== void 0 ? [...new Set(findings2.map((f) => f.matchSource).filter(Boolean))] : [];
|
|
95649
|
-
const matchSuffix = matchSources.length > 0 ?
|
|
96087
|
+
const matchSuffix = matchSources.length > 0 ? import_picocolors26.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
|
|
95650
96088
|
console.log(
|
|
95651
|
-
|
|
96089
|
+
import_picocolors26.default.bold(
|
|
95652
96090
|
`Grafana metrics${hintSuffix} \u2014 ${findings2.length} series across panels`
|
|
95653
96091
|
) + matchSuffix
|
|
95654
96092
|
);
|
|
95655
96093
|
console.log(
|
|
95656
|
-
|
|
96094
|
+
import_picocolors26.default.dim(
|
|
95657
96095
|
` window: ${new Date(from * 1e3).toISOString()} \u2013 ${new Date(to * 1e3).toISOString()}`
|
|
95658
96096
|
)
|
|
95659
96097
|
);
|
|
95660
96098
|
console.log("");
|
|
95661
96099
|
if (flagged.length === 0) {
|
|
95662
|
-
console.log(
|
|
96100
|
+
console.log(import_picocolors26.default.green(" No anomalies detected."));
|
|
95663
96101
|
} else {
|
|
95664
96102
|
for (const f of flagged) {
|
|
95665
96103
|
const color = anomalyColor(f.anomaly);
|
|
@@ -95668,14 +96106,14 @@ async function runMetrics(hint, opts) {
|
|
|
95668
96106
|
const lblStr = labelStr(f.labels);
|
|
95669
96107
|
console.log(
|
|
95670
96108
|
color(
|
|
95671
|
-
` ${label.padEnd(22)} ${f.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f.baselineAvg)} -> ${fmtNum(f.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${
|
|
96109
|
+
` ${label.padEnd(22)} ${f.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f.baselineAvg)} -> ${fmtNum(f.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors26.default.dim(lblStr)}` : "")
|
|
95672
96110
|
)
|
|
95673
96111
|
);
|
|
95674
96112
|
}
|
|
95675
96113
|
}
|
|
95676
96114
|
if (ok.length > 0) {
|
|
95677
96115
|
console.log("");
|
|
95678
|
-
console.log(
|
|
96116
|
+
console.log(import_picocolors26.default.dim(` ${ok.length} panel series with no anomaly.`));
|
|
95679
96117
|
}
|
|
95680
96118
|
if (opts.ai) {
|
|
95681
96119
|
const result = await renderAiInterpretation({
|
|
@@ -95690,19 +96128,19 @@ async function runMetrics(hint, opts) {
|
|
|
95690
96128
|
});
|
|
95691
96129
|
console.log("\n" + renderInterpretation(result));
|
|
95692
96130
|
if (!result.ok) {
|
|
95693
|
-
console.error(
|
|
96131
|
+
console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
|
|
95694
96132
|
}
|
|
95695
96133
|
}
|
|
95696
96134
|
return 0;
|
|
95697
96135
|
} catch (err) {
|
|
95698
|
-
console.error(
|
|
96136
|
+
console.error(import_picocolors26.default.red(err.message));
|
|
95699
96137
|
return 1;
|
|
95700
96138
|
}
|
|
95701
96139
|
}
|
|
95702
96140
|
|
|
95703
96141
|
// ../../packages/cli/src/commands/state.ts
|
|
95704
96142
|
init_cjs_shims();
|
|
95705
|
-
var
|
|
96143
|
+
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
95706
96144
|
var STATE_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
|
|
95707
96145
|
|
|
95708
96146
|
Evidence used
|
|
@@ -95728,13 +96166,13 @@ async function runState(opts) {
|
|
|
95728
96166
|
try {
|
|
95729
96167
|
renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
|
|
95730
96168
|
} catch (err) {
|
|
95731
|
-
console.error(
|
|
96169
|
+
console.error(import_picocolors27.default.red(err.message));
|
|
95732
96170
|
return 1;
|
|
95733
96171
|
}
|
|
95734
96172
|
const mongo = mongoForEnv(renv);
|
|
95735
96173
|
if (mongo === null) {
|
|
95736
96174
|
console.error(
|
|
95737
|
-
|
|
96175
|
+
import_picocolors27.default.red(
|
|
95738
96176
|
`No MongoDB connector configured for ${renv.project}/${renv.env} (set the project's Mongo URL env var).`
|
|
95739
96177
|
)
|
|
95740
96178
|
);
|
|
@@ -95742,7 +96180,7 @@ async function runState(opts) {
|
|
|
95742
96180
|
}
|
|
95743
96181
|
const health = await mongo.health();
|
|
95744
96182
|
if (!health.ok) {
|
|
95745
|
-
console.error(
|
|
96183
|
+
console.error(import_picocolors27.default.red(`MongoDB unreachable: ${health.detail}`));
|
|
95746
96184
|
await mongo.close();
|
|
95747
96185
|
return 1;
|
|
95748
96186
|
}
|
|
@@ -95768,38 +96206,38 @@ async function runState(opts) {
|
|
|
95768
96206
|
);
|
|
95769
96207
|
return 0;
|
|
95770
96208
|
}
|
|
95771
|
-
const discoveryNote = analysis.autoDiscovered ?
|
|
96209
|
+
const discoveryNote = analysis.autoDiscovered ? import_picocolors27.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
|
|
95772
96210
|
console.log(
|
|
95773
|
-
|
|
96211
|
+
import_picocolors27.default.bold("State analysis") + import_picocolors27.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
|
|
95774
96212
|
);
|
|
95775
96213
|
console.log("");
|
|
95776
96214
|
let signals = 0;
|
|
95777
96215
|
for (const c of analysis.collections) {
|
|
95778
96216
|
const flags = [];
|
|
95779
96217
|
if (c.isStale === true) {
|
|
95780
|
-
flags.push(
|
|
96218
|
+
flags.push(import_picocolors27.default.yellow(`stale (last ${c.dateField}, ~${Math.round(c.ageHours ?? 0)}h)`));
|
|
95781
96219
|
}
|
|
95782
96220
|
for (const a of c.anomalies) {
|
|
95783
|
-
flags.push(
|
|
96221
|
+
flags.push(import_picocolors27.default.red(`${a.count}\xD7 "${a.value}"`));
|
|
95784
96222
|
signals += 1;
|
|
95785
96223
|
}
|
|
95786
96224
|
if (c.isStale === true) signals += 1;
|
|
95787
|
-
const head = ` ${
|
|
95788
|
-
console.log(flags.length > 0 ? `${head} ${flags.join(" ")}` :
|
|
96225
|
+
const head = ` ${import_picocolors27.default.bold(c.collection.padEnd(18).slice(0, 18))} ${import_picocolors27.default.dim(String(c.count).padStart(8) + " docs")}`;
|
|
96226
|
+
console.log(flags.length > 0 ? `${head} ${flags.join(" ")}` : import_picocolors27.default.dim(head));
|
|
95789
96227
|
}
|
|
95790
96228
|
console.log("");
|
|
95791
96229
|
if (analysis.collections.length === 0) {
|
|
95792
96230
|
console.log(
|
|
95793
|
-
|
|
96231
|
+
import_picocolors27.default.yellow(" No collections discovered in this database.") + import_picocolors27.default.dim(" Verify the database name in your MongoDB connector config.")
|
|
95794
96232
|
);
|
|
95795
96233
|
} else {
|
|
95796
96234
|
const scope = analysis.autoDiscovered ? "collection(s)" : "allowlisted collection(s)";
|
|
95797
96235
|
console.log(
|
|
95798
|
-
signals > 0 ? ` ${
|
|
96236
|
+
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}.`)
|
|
95799
96237
|
);
|
|
95800
96238
|
if (analysis.autoDiscovered) {
|
|
95801
96239
|
console.log(
|
|
95802
|
-
|
|
96240
|
+
import_picocolors27.default.dim(
|
|
95803
96241
|
` Tip: add a "collections" list to your MongoDB connector config to restrict analysis.`
|
|
95804
96242
|
)
|
|
95805
96243
|
);
|
|
@@ -95817,7 +96255,7 @@ async function runState(opts) {
|
|
|
95817
96255
|
});
|
|
95818
96256
|
console.log("\n" + renderInterpretation(result));
|
|
95819
96257
|
if (!result.ok) {
|
|
95820
|
-
console.error(
|
|
96258
|
+
console.error(import_picocolors27.default.yellow(`[ai] ${result.warning}`));
|
|
95821
96259
|
}
|
|
95822
96260
|
}
|
|
95823
96261
|
return 0;
|
|
@@ -95825,20 +96263,20 @@ async function runState(opts) {
|
|
|
95825
96263
|
await mongo.close();
|
|
95826
96264
|
}
|
|
95827
96265
|
} catch (err) {
|
|
95828
|
-
console.error(
|
|
96266
|
+
console.error(import_picocolors27.default.red(err.message));
|
|
95829
96267
|
return 1;
|
|
95830
96268
|
}
|
|
95831
96269
|
}
|
|
95832
96270
|
|
|
95833
96271
|
// ../../packages/cli/src/commands/init.ts
|
|
95834
96272
|
init_cjs_shims();
|
|
95835
|
-
var
|
|
95836
|
-
var
|
|
96273
|
+
var import_picocolors28 = __toESM(require_picocolors(), 1);
|
|
96274
|
+
var import_node_path17 = require("path");
|
|
95837
96275
|
async function runInit(opts) {
|
|
95838
96276
|
try {
|
|
95839
96277
|
const cwd = process.cwd();
|
|
95840
|
-
const root = opts.path ? (0,
|
|
95841
|
-
const name = opts.name ?? (0,
|
|
96278
|
+
const root = opts.path ? (0, import_node_path17.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
|
|
96279
|
+
const name = opts.name ?? (0, import_node_path17.basename)(root);
|
|
95842
96280
|
const envName = opts.env ?? "production";
|
|
95843
96281
|
const repo = { name, path: root };
|
|
95844
96282
|
if (opts.source) repo["source"] = { hostUrl: opts.source };
|
|
@@ -95853,65 +96291,65 @@ async function runInit(opts) {
|
|
|
95853
96291
|
const configPath = writeLocalConfig(root, file);
|
|
95854
96292
|
registerProject(name, root, configPath);
|
|
95855
96293
|
ensureProjectGitignore(root);
|
|
95856
|
-
console.log(`${
|
|
95857
|
-
console.log(
|
|
95858
|
-
console.log(
|
|
96294
|
+
console.log(`${import_picocolors28.default.green("\u2713")} Initialized Horus project ${import_picocolors28.default.bold(name)}`);
|
|
96295
|
+
console.log(import_picocolors28.default.dim(` config: ${configPath}`));
|
|
96296
|
+
console.log(import_picocolors28.default.dim(` registered: horus investigate --name ${name} "<hint>"`));
|
|
95859
96297
|
if (!opts.source) {
|
|
95860
96298
|
console.log(
|
|
95861
|
-
|
|
96299
|
+
import_picocolors28.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
|
|
95862
96300
|
);
|
|
95863
96301
|
}
|
|
95864
96302
|
console.log(
|
|
95865
|
-
|
|
96303
|
+
import_picocolors28.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
|
|
95866
96304
|
);
|
|
95867
96305
|
console.log(
|
|
95868
|
-
|
|
96306
|
+
import_picocolors28.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
|
|
95869
96307
|
);
|
|
95870
96308
|
return 0;
|
|
95871
96309
|
} catch (err) {
|
|
95872
|
-
console.error(
|
|
96310
|
+
console.error(import_picocolors28.default.red(err.message));
|
|
95873
96311
|
return 1;
|
|
95874
96312
|
}
|
|
95875
96313
|
}
|
|
95876
96314
|
|
|
95877
96315
|
// ../../packages/cli/src/commands/projects.ts
|
|
95878
96316
|
init_cjs_shims();
|
|
95879
|
-
var
|
|
96317
|
+
var import_picocolors29 = __toESM(require_picocolors(), 1);
|
|
95880
96318
|
async function runProjects() {
|
|
95881
96319
|
const reg = readRegistry();
|
|
95882
96320
|
const names = Object.keys(reg.projects).sort();
|
|
95883
96321
|
if (names.length === 0) {
|
|
95884
96322
|
console.log(
|
|
95885
|
-
|
|
96323
|
+
import_picocolors29.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
|
|
95886
96324
|
);
|
|
95887
96325
|
return 0;
|
|
95888
96326
|
}
|
|
95889
|
-
console.log(
|
|
96327
|
+
console.log(import_picocolors29.default.bold("Registered Horus projects"));
|
|
95890
96328
|
console.log("");
|
|
95891
96329
|
for (const name of names) {
|
|
95892
96330
|
const entry2 = reg.projects[name];
|
|
95893
96331
|
if (entry2 === void 0) continue;
|
|
95894
|
-
console.log(` ${
|
|
96332
|
+
console.log(` ${import_picocolors29.default.bold(name.padEnd(22))} ${import_picocolors29.default.dim(entry2.root)}`);
|
|
95895
96333
|
}
|
|
95896
96334
|
console.log("");
|
|
95897
|
-
console.log(
|
|
96335
|
+
console.log(import_picocolors29.default.dim(' horus investigate --name <name> "<hint>"'));
|
|
95898
96336
|
return 0;
|
|
95899
96337
|
}
|
|
95900
96338
|
|
|
95901
96339
|
// ../../packages/cli/src/commands/setup.ts
|
|
95902
96340
|
init_cjs_shims();
|
|
95903
|
-
var
|
|
96341
|
+
var import_picocolors30 = __toESM(require_picocolors(), 1);
|
|
95904
96342
|
var DEFAULT_DB_URL3 = "postgresql://horus:horus@localhost:5433/horus";
|
|
95905
96343
|
async function runSetup(opts) {
|
|
95906
96344
|
const write = opts.write ?? ((line2) => console.log(line2));
|
|
95907
96345
|
let ok = true;
|
|
95908
|
-
write(
|
|
96346
|
+
write(import_picocolors30.default.bold("\nHorus setup\n"));
|
|
95909
96347
|
const backendVersion = await getSourceVersion();
|
|
95910
96348
|
if (backendVersion === null) {
|
|
95911
96349
|
ok = false;
|
|
95912
|
-
write(` ${
|
|
96350
|
+
write(` ${import_picocolors30.default.red("\u25CF")} Horus source-intelligence backend not found`);
|
|
95913
96351
|
write(
|
|
95914
|
-
|
|
96352
|
+
import_picocolors30.default.dim(
|
|
95915
96353
|
` install it (Python 3.11+ required):
|
|
95916
96354
|
pip install horus-source
|
|
95917
96355
|
ensure ~/.local/bin is on your PATH`
|
|
@@ -95920,17 +96358,17 @@ async function runSetup(opts) {
|
|
|
95920
96358
|
} else if (backendVersion !== PINNED_SOURCE_VERSION) {
|
|
95921
96359
|
ok = false;
|
|
95922
96360
|
write(
|
|
95923
|
-
` ${
|
|
96361
|
+
` ${import_picocolors30.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors30.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
|
|
95924
96362
|
);
|
|
95925
96363
|
write(
|
|
95926
|
-
|
|
96364
|
+
import_picocolors30.default.dim(
|
|
95927
96365
|
` update it:
|
|
95928
96366
|
pip install --upgrade horus-source`
|
|
95929
96367
|
)
|
|
95930
96368
|
);
|
|
95931
96369
|
} else {
|
|
95932
96370
|
write(
|
|
95933
|
-
` ${
|
|
96371
|
+
` ${import_picocolors30.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors30.default.dim(`(${backendVersion})`)
|
|
95934
96372
|
);
|
|
95935
96373
|
}
|
|
95936
96374
|
let dbUrl = process.env["DATABASE_URL"] ?? DEFAULT_DB_URL3;
|
|
@@ -95942,11 +96380,11 @@ async function runSetup(opts) {
|
|
|
95942
96380
|
}
|
|
95943
96381
|
const db = await checkDatabase(dbUrl);
|
|
95944
96382
|
if (db.reachable) {
|
|
95945
|
-
write(` ${
|
|
96383
|
+
write(` ${import_picocolors30.default.green("\u25CF")} Postgres reachable ${import_picocolors30.default.dim(`(${db.schemaDetail})`)}`);
|
|
95946
96384
|
if (!db.schemaReady) {
|
|
95947
96385
|
ok = false;
|
|
95948
96386
|
write(
|
|
95949
|
-
|
|
96387
|
+
import_picocolors30.default.dim(
|
|
95950
96388
|
` schema not applied \u2014 run migrations:
|
|
95951
96389
|
from the Horus repo: pnpm db migrate
|
|
95952
96390
|
or apply the schema directly: https://github.com/meritt-dev/horus/tree/master/packages/db/drizzle`
|
|
@@ -95955,9 +96393,9 @@ async function runSetup(opts) {
|
|
|
95955
96393
|
}
|
|
95956
96394
|
} else {
|
|
95957
96395
|
ok = false;
|
|
95958
|
-
write(` ${
|
|
96396
|
+
write(` ${import_picocolors30.default.red("\u25CF")} Postgres unreachable`);
|
|
95959
96397
|
write(
|
|
95960
|
-
|
|
96398
|
+
import_picocolors30.default.dim(
|
|
95961
96399
|
` start a local instance:
|
|
95962
96400
|
docker run -d --name horus-db \\
|
|
95963
96401
|
-e POSTGRES_USER=horus -e POSTGRES_PASSWORD=horus -e POSTGRES_DB=horus \\
|
|
@@ -95982,10 +96420,10 @@ async function runSetup(opts) {
|
|
|
95982
96420
|
if (!health.ok) {
|
|
95983
96421
|
ok = false;
|
|
95984
96422
|
write(
|
|
95985
|
-
` ${
|
|
96423
|
+
` ${import_picocolors30.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors30.default.bold(repo.name)} ` + import_picocolors30.default.dim(`(${repoHostUrl})`)
|
|
95986
96424
|
);
|
|
95987
96425
|
write(
|
|
95988
|
-
|
|
96426
|
+
import_picocolors30.default.dim(
|
|
95989
96427
|
` start the source intelligence host:
|
|
95990
96428
|
cd ${repo.path}
|
|
95991
96429
|
horus index`
|
|
@@ -95996,10 +96434,10 @@ async function runSetup(opts) {
|
|
|
95996
96434
|
if (count === 0) {
|
|
95997
96435
|
ok = false;
|
|
95998
96436
|
write(
|
|
95999
|
-
` ${
|
|
96437
|
+
` ${import_picocolors30.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors30.default.bold(repo.name)} is not indexed`
|
|
96000
96438
|
);
|
|
96001
96439
|
write(
|
|
96002
|
-
|
|
96440
|
+
import_picocolors30.default.dim(
|
|
96003
96441
|
` index the repo:
|
|
96004
96442
|
cd ${repo.path}
|
|
96005
96443
|
horus index`
|
|
@@ -96007,7 +96445,7 @@ async function runSetup(opts) {
|
|
|
96007
96445
|
);
|
|
96008
96446
|
} else {
|
|
96009
96447
|
write(
|
|
96010
|
-
` ${
|
|
96448
|
+
` ${import_picocolors30.default.green("\u25CF")} ${import_picocolors30.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors30.default.dim(`(${repoHostUrl})`)
|
|
96011
96449
|
);
|
|
96012
96450
|
}
|
|
96013
96451
|
}
|
|
@@ -96017,10 +96455,10 @@ async function runSetup(opts) {
|
|
|
96017
96455
|
write("");
|
|
96018
96456
|
if (ok) {
|
|
96019
96457
|
write(
|
|
96020
|
-
|
|
96458
|
+
import_picocolors30.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
|
|
96021
96459
|
);
|
|
96022
96460
|
} else {
|
|
96023
|
-
write(
|
|
96461
|
+
write(import_picocolors30.default.yellow("Resolve the items above, then re-run `horus setup`."));
|
|
96024
96462
|
}
|
|
96025
96463
|
return ok ? 0 : 1;
|
|
96026
96464
|
}
|
|
@@ -96029,12 +96467,12 @@ async function runSetup(opts) {
|
|
|
96029
96467
|
init_cjs_shims();
|
|
96030
96468
|
var import_node_readline = require("readline");
|
|
96031
96469
|
var import_node_child_process6 = require("child_process");
|
|
96032
|
-
var
|
|
96470
|
+
var import_picocolors33 = __toESM(require_picocolors(), 1);
|
|
96033
96471
|
|
|
96034
96472
|
// ../../packages/cli/src/lib/tty-selector.ts
|
|
96035
96473
|
init_cjs_shims();
|
|
96036
96474
|
var import_node_tty = require("tty");
|
|
96037
|
-
var
|
|
96475
|
+
var import_picocolors31 = __toESM(require_picocolors(), 1);
|
|
96038
96476
|
var ExitPromptError = class extends Error {
|
|
96039
96477
|
constructor(message = "Prompt was cancelled") {
|
|
96040
96478
|
super(message);
|
|
@@ -96090,7 +96528,7 @@ async function checkboxSearch(opts) {
|
|
|
96090
96528
|
clear();
|
|
96091
96529
|
const lines = [];
|
|
96092
96530
|
lines.push(
|
|
96093
|
-
`? ${
|
|
96531
|
+
`? ${import_picocolors31.default.bold(message)} ${import_picocolors31.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
|
|
96094
96532
|
);
|
|
96095
96533
|
const visible = filtered.slice(visibleOffset, visibleOffset + pageSize);
|
|
96096
96534
|
for (let i = 0; i < visible.length; i++) {
|
|
@@ -96098,29 +96536,29 @@ async function checkboxSearch(opts) {
|
|
|
96098
96536
|
const choice = visible[i];
|
|
96099
96537
|
const isCursor = idx === cursor;
|
|
96100
96538
|
const isSelected = selected.has(choice);
|
|
96101
|
-
const marker = isSelected ?
|
|
96539
|
+
const marker = isSelected ? import_picocolors31.default.green("\u25CF") : " ";
|
|
96102
96540
|
const prefix = `[${marker}]`;
|
|
96103
|
-
const label = isCursor ?
|
|
96104
|
-
const pointer = isCursor ?
|
|
96541
|
+
const label = isCursor ? import_picocolors31.default.cyan(choice) : choice;
|
|
96542
|
+
const pointer = isCursor ? import_picocolors31.default.cyan("\u276F") : " ";
|
|
96105
96543
|
lines.push(` ${pointer} ${prefix} ${label}`);
|
|
96106
96544
|
}
|
|
96107
96545
|
if (filtered.length === 0) {
|
|
96108
|
-
lines.push(
|
|
96546
|
+
lines.push(import_picocolors31.default.dim(" No matches"));
|
|
96109
96547
|
}
|
|
96110
96548
|
const statusParts = [
|
|
96111
96549
|
`${selected.size} selected`,
|
|
96112
96550
|
`${filtered.length}/${choices.length} matches`
|
|
96113
96551
|
];
|
|
96114
96552
|
if (filter) statusParts.push(`filter: ${filter}`);
|
|
96115
|
-
lines.push(
|
|
96553
|
+
lines.push(import_picocolors31.default.dim(` ${statusParts.join(" \xB7 ")}`));
|
|
96116
96554
|
output.write(lines.join("\n"));
|
|
96117
96555
|
lastRenderLines = lines.length;
|
|
96118
96556
|
}
|
|
96119
96557
|
function finish(values2) {
|
|
96120
96558
|
cleanup();
|
|
96121
|
-
const summary = values2.length > 0 ? values2.join(", ") :
|
|
96559
|
+
const summary = values2.length > 0 ? values2.join(", ") : import_picocolors31.default.dim("none");
|
|
96122
96560
|
output.write(`
|
|
96123
|
-
? ${
|
|
96561
|
+
? ${import_picocolors31.default.bold(message)} ${summary}
|
|
96124
96562
|
`);
|
|
96125
96563
|
resolve8(values2);
|
|
96126
96564
|
}
|
|
@@ -96212,7 +96650,7 @@ async function checkboxSearch(opts) {
|
|
|
96212
96650
|
// ../../packages/cli/src/commands/connect-ai.ts
|
|
96213
96651
|
init_cjs_shims();
|
|
96214
96652
|
var import_node_child_process5 = require("child_process");
|
|
96215
|
-
var
|
|
96653
|
+
var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
96216
96654
|
function cliInstalled(id) {
|
|
96217
96655
|
const r = (0, import_node_child_process5.spawnSync)(id, ["--version"], { stdio: "ignore", timeout: 2e3 });
|
|
96218
96656
|
return !r.error;
|
|
@@ -96240,13 +96678,13 @@ async function runConnectAi(opts) {
|
|
|
96240
96678
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
96241
96679
|
const configPath = discoverLocalConfig(cwd) ?? localConfigPath(root);
|
|
96242
96680
|
const installed = LOCAL_PROVIDER_IDS.filter((id) => cliInstalled(id));
|
|
96243
|
-
console.log(
|
|
96681
|
+
console.log(import_picocolors32.default.bold("\nAI providers"));
|
|
96244
96682
|
console.log(
|
|
96245
|
-
|
|
96683
|
+
import_picocolors32.default.dim(
|
|
96246
96684
|
` Local CLIs on PATH: ${installed.length > 0 ? installed.join(", ") : "none detected"}`
|
|
96247
96685
|
)
|
|
96248
96686
|
);
|
|
96249
|
-
console.log(
|
|
96687
|
+
console.log(import_picocolors32.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
|
|
96250
96688
|
let provider = opts.provider;
|
|
96251
96689
|
if (provider === void 0) {
|
|
96252
96690
|
if (isInteractive()) {
|
|
@@ -96257,7 +96695,7 @@ async function runConnectAi(opts) {
|
|
|
96257
96695
|
}
|
|
96258
96696
|
}
|
|
96259
96697
|
if (!AI_PROVIDERS.includes(provider)) {
|
|
96260
|
-
console.error(
|
|
96698
|
+
console.error(import_picocolors32.default.red(`Unknown AI provider: ${provider}`) + import_picocolors32.default.dim(`
|
|
96261
96699
|
supported: ${AI_PROVIDERS.join(", ")}`));
|
|
96262
96700
|
return 1;
|
|
96263
96701
|
}
|
|
@@ -96270,7 +96708,7 @@ async function runConnectAi(opts) {
|
|
|
96270
96708
|
}
|
|
96271
96709
|
if (!apiKey) {
|
|
96272
96710
|
console.error(
|
|
96273
|
-
|
|
96711
|
+
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.")
|
|
96274
96712
|
);
|
|
96275
96713
|
return 1;
|
|
96276
96714
|
}
|
|
@@ -96278,28 +96716,28 @@ async function runConnectAi(opts) {
|
|
|
96278
96716
|
const probe2 = await probeAnthropic(apiKey);
|
|
96279
96717
|
if (!probe2.ok) {
|
|
96280
96718
|
console.error(`
|
|
96281
|
-
${
|
|
96282
|
-
console.error(
|
|
96719
|
+
${import_picocolors32.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
|
|
96720
|
+
console.error(import_picocolors32.default.dim(" Fix the key and retry, or pass --no-test to skip."));
|
|
96283
96721
|
return 1;
|
|
96284
96722
|
}
|
|
96285
96723
|
console.log(`
|
|
96286
|
-
${
|
|
96724
|
+
${import_picocolors32.default.green("\u2713")} anthropic ${import_picocolors32.default.dim(`(${probe2.detail})`)}`);
|
|
96287
96725
|
}
|
|
96288
96726
|
if (opts.model) aiBlock["anthropic"] = { model: opts.model };
|
|
96289
96727
|
storedKey = apiKey;
|
|
96290
96728
|
} else {
|
|
96291
96729
|
if (!installed.includes(provider)) {
|
|
96292
96730
|
console.error(
|
|
96293
|
-
|
|
96731
|
+
import_picocolors32.default.red(`${provider} CLI not found on PATH.`) + import_picocolors32.default.dim(`
|
|
96294
96732
|
Install it first, or choose anthropic.`)
|
|
96295
96733
|
);
|
|
96296
96734
|
return 1;
|
|
96297
96735
|
}
|
|
96298
96736
|
const desc2 = DEFAULT_LOCAL_PROVIDER_REGISTRY.get(provider);
|
|
96299
96737
|
console.log(`
|
|
96300
|
-
${
|
|
96738
|
+
${import_picocolors32.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors32.default.dim("detected on PATH")}`);
|
|
96301
96739
|
console.log(
|
|
96302
|
-
|
|
96740
|
+
import_picocolors32.default.dim(
|
|
96303
96741
|
" Note: `investigate --ai` currently uses the Anthropic API for narrative.\n Local-CLI narrative generation is recorded as your preference for future use."
|
|
96304
96742
|
)
|
|
96305
96743
|
);
|
|
@@ -96317,15 +96755,15 @@ ${import_picocolors31.default.green("\u2713")} ${desc2?.displayName ?? provider}
|
|
|
96317
96755
|
});
|
|
96318
96756
|
}
|
|
96319
96757
|
console.log(`
|
|
96320
|
-
${
|
|
96321
|
-
console.log(
|
|
96758
|
+
${import_picocolors32.default.green("\u2713")} ${import_picocolors32.default.bold("ai")} provider saved \u2192 ${import_picocolors32.default.dim(configPath)}`);
|
|
96759
|
+
console.log(import_picocolors32.default.dim(` provider: ${provider}`));
|
|
96322
96760
|
if (storedKey && secretsPathStr) {
|
|
96323
|
-
console.log(
|
|
96761
|
+
console.log(import_picocolors32.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
|
|
96324
96762
|
}
|
|
96325
|
-
console.log(
|
|
96763
|
+
console.log(import_picocolors32.default.dim(' run: horus investigate "<hint>" --ai'));
|
|
96326
96764
|
return 0;
|
|
96327
96765
|
} catch (err) {
|
|
96328
|
-
console.error(
|
|
96766
|
+
console.error(import_picocolors32.default.red(err instanceof Error ? err.message : String(err)));
|
|
96329
96767
|
return 1;
|
|
96330
96768
|
}
|
|
96331
96769
|
}
|
|
@@ -96357,7 +96795,7 @@ async function runConnect(type, opts) {
|
|
|
96357
96795
|
}
|
|
96358
96796
|
if (!SUPPORTED.includes(type)) {
|
|
96359
96797
|
console.error(
|
|
96360
|
-
|
|
96798
|
+
import_picocolors33.default.red(`Unknown connector type: ${type}`) + import_picocolors33.default.dim(`
|
|
96361
96799
|
supported: ${SUPPORTED.join(", ")}, ai`)
|
|
96362
96800
|
);
|
|
96363
96801
|
return 1;
|
|
@@ -96388,20 +96826,20 @@ async function runConnect(type, opts) {
|
|
|
96388
96826
|
if (!probeResult.ok) {
|
|
96389
96827
|
console.error(
|
|
96390
96828
|
`
|
|
96391
|
-
${
|
|
96829
|
+
${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.")
|
|
96392
96830
|
);
|
|
96393
96831
|
return 1;
|
|
96394
96832
|
}
|
|
96395
96833
|
console.log(
|
|
96396
96834
|
`
|
|
96397
|
-
${
|
|
96835
|
+
${import_picocolors33.default.green("\u2713")} ${connectorType} reachable ${import_picocolors33.default.dim(`(${probeResult.detail})`)}`
|
|
96398
96836
|
);
|
|
96399
96837
|
}
|
|
96400
96838
|
const hasLiteralCredentials = filled.url !== void 0 || filled.password !== void 0 || filled.username !== void 0;
|
|
96401
96839
|
if (hasLiteralCredentials) {
|
|
96402
96840
|
if (isGitTracked(configPath, root)) {
|
|
96403
96841
|
console.error(
|
|
96404
|
-
|
|
96842
|
+
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(
|
|
96405
96843
|
" 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."
|
|
96406
96844
|
)
|
|
96407
96845
|
);
|
|
@@ -96411,18 +96849,18 @@ ${import_picocolors32.default.green("\u2713")} ${connectorType} reachable ${impo
|
|
|
96411
96849
|
}
|
|
96412
96850
|
patchLocalConnector(configPath, connectorType, patch, filled.env);
|
|
96413
96851
|
console.log(
|
|
96414
|
-
`${
|
|
96852
|
+
`${import_picocolors33.default.green("\u2713")} ${import_picocolors33.default.bold(connectorType)} connector saved \u2192 ${import_picocolors33.default.dim(configPath)}`
|
|
96415
96853
|
);
|
|
96416
96854
|
printSummary(connectorType, filled);
|
|
96417
|
-
console.log(
|
|
96855
|
+
console.log(import_picocolors33.default.dim(`
|
|
96418
96856
|
run: horus investigate "<hint>"`));
|
|
96419
96857
|
return 0;
|
|
96420
96858
|
} catch (err) {
|
|
96421
96859
|
if (err instanceof ExitPromptError) {
|
|
96422
|
-
console.error(
|
|
96860
|
+
console.error(import_picocolors33.default.red("Cancelled."));
|
|
96423
96861
|
return 1;
|
|
96424
96862
|
}
|
|
96425
|
-
console.error(
|
|
96863
|
+
console.error(import_picocolors33.default.red(err.message));
|
|
96426
96864
|
return 1;
|
|
96427
96865
|
}
|
|
96428
96866
|
}
|
|
@@ -96431,7 +96869,7 @@ async function fillInteractive(type, opts) {
|
|
|
96431
96869
|
if (!needsInteraction) return opts;
|
|
96432
96870
|
console.log(
|
|
96433
96871
|
`
|
|
96434
|
-
${
|
|
96872
|
+
${import_picocolors33.default.bold(`Connect ${type}`)} ${import_picocolors33.default.dim("(press Enter to skip optional fields)")}
|
|
96435
96873
|
`
|
|
96436
96874
|
);
|
|
96437
96875
|
const filled = { ...opts };
|
|
@@ -96486,7 +96924,7 @@ ${import_picocolors32.default.bold(`Connect ${type}`)} ${import_picocolors32.def
|
|
|
96486
96924
|
break;
|
|
96487
96925
|
case "redis": {
|
|
96488
96926
|
console.log(
|
|
96489
|
-
|
|
96927
|
+
import_picocolors33.default.dim(
|
|
96490
96928
|
" Tip: embed credentials directly in the URL \u2014 redis://:password@host:6379\n or enter the URL and password separately below."
|
|
96491
96929
|
)
|
|
96492
96930
|
);
|
|
@@ -96524,8 +96962,8 @@ function missingRequired(type, opts) {
|
|
|
96524
96962
|
}
|
|
96525
96963
|
function ask(label, placeholder = "", required2 = true) {
|
|
96526
96964
|
return new Promise((resolve8) => {
|
|
96527
|
-
const hint = placeholder ?
|
|
96528
|
-
const suffix = required2 ? "" :
|
|
96965
|
+
const hint = placeholder ? import_picocolors33.default.dim(` (${placeholder})`) : "";
|
|
96966
|
+
const suffix = required2 ? "" : import_picocolors33.default.dim(" [optional]");
|
|
96529
96967
|
process.stdout.write(` ${label}${suffix}${hint}: `);
|
|
96530
96968
|
const rl = (0, import_node_readline.createInterface)({
|
|
96531
96969
|
input: process.stdin,
|
|
@@ -96542,7 +96980,7 @@ function askPassword(label) {
|
|
|
96542
96980
|
return new Promise((resolve8) => {
|
|
96543
96981
|
const stdin = process.stdin;
|
|
96544
96982
|
if (typeof stdin.setRawMode === "function") {
|
|
96545
|
-
process.stdout.write(` ${label}${
|
|
96983
|
+
process.stdout.write(` ${label}${import_picocolors33.default.dim(" [optional]")}: `);
|
|
96546
96984
|
stdin.setRawMode(true);
|
|
96547
96985
|
stdin.resume();
|
|
96548
96986
|
let value = "";
|
|
@@ -96642,15 +97080,15 @@ function describeProbe(p) {
|
|
|
96642
97080
|
return `${p.keyCount} keys \xB7 ${p.suggestedRoles.join("/")}${examples ? ` \xB7 ${examples}` : ""}`;
|
|
96643
97081
|
}
|
|
96644
97082
|
async function discoverAndSelectDbs(url, bullmqPrefix) {
|
|
96645
|
-
console.log(
|
|
97083
|
+
console.log(import_picocolors33.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
|
|
96646
97084
|
const probes = await probeRedisDatabases(url, { bullmqPrefix });
|
|
96647
97085
|
const nonEmpty = probes.filter((p) => p.reachable && p.keyCount > 0);
|
|
96648
97086
|
if (nonEmpty.length === 0) {
|
|
96649
|
-
console.log(
|
|
97087
|
+
console.log(import_picocolors33.default.dim(" No populated DBs found."));
|
|
96650
97088
|
return [];
|
|
96651
97089
|
}
|
|
96652
97090
|
for (const p of nonEmpty) {
|
|
96653
|
-
console.log(` ${
|
|
97091
|
+
console.log(` ${import_picocolors33.default.bold(`DB ${p.db}`)} ${import_picocolors33.default.dim("\xB7 " + describeProbe(p))}`);
|
|
96654
97092
|
}
|
|
96655
97093
|
const byLabel = /* @__PURE__ */ new Map();
|
|
96656
97094
|
const choices = nonEmpty.map((p) => {
|
|
@@ -96761,7 +97199,7 @@ function printSummary(type, opts) {
|
|
|
96761
97199
|
} else if (opts.dashboard) {
|
|
96762
97200
|
lines.push(` dashboard: ${opts.dashboard}`);
|
|
96763
97201
|
}
|
|
96764
|
-
if (lines.length > 0) console.log(
|
|
97202
|
+
if (lines.length > 0) console.log(import_picocolors33.default.dim(lines.join("\n")));
|
|
96765
97203
|
}
|
|
96766
97204
|
function isGitTracked(filePath, cwd) {
|
|
96767
97205
|
try {
|
|
@@ -96800,11 +97238,11 @@ async function askIndexSelection(indices) {
|
|
|
96800
97238
|
const shown = indices.slice(0, MAX_DISPLAY);
|
|
96801
97239
|
console.log("\n Available Elasticsearch indexes/data streams:");
|
|
96802
97240
|
shown.forEach((name, i) => {
|
|
96803
|
-
console.log(` ${
|
|
97241
|
+
console.log(` ${import_picocolors33.default.dim(`[${i + 1}]`)} ${name}`);
|
|
96804
97242
|
});
|
|
96805
97243
|
if (indices.length > MAX_DISPLAY) {
|
|
96806
97244
|
console.log(
|
|
96807
|
-
|
|
97245
|
+
import_picocolors33.default.dim(
|
|
96808
97246
|
` \u2026 and ${indices.length - MAX_DISPLAY} more (type a pattern manually to match all)`
|
|
96809
97247
|
)
|
|
96810
97248
|
);
|
|
@@ -96848,11 +97286,11 @@ async function askDashboardSelection(dashboards) {
|
|
|
96848
97286
|
const shown = dashboards.slice(0, MAX_DISPLAY);
|
|
96849
97287
|
console.log("\n Available Grafana dashboards:");
|
|
96850
97288
|
shown.forEach((d, i) => {
|
|
96851
|
-
const folder = d.folderTitle ?
|
|
96852
|
-
console.log(` ${
|
|
97289
|
+
const folder = d.folderTitle ? import_picocolors33.default.dim(` (${d.folderTitle})`) : "";
|
|
97290
|
+
console.log(` ${import_picocolors33.default.dim(`[${i + 1}]`)} ${d.title}${folder}`);
|
|
96853
97291
|
});
|
|
96854
97292
|
if (dashboards.length > MAX_DISPLAY) {
|
|
96855
|
-
console.log(
|
|
97293
|
+
console.log(import_picocolors33.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
|
|
96856
97294
|
}
|
|
96857
97295
|
const input = (await ask(
|
|
96858
97296
|
` Select dashboards to use (e.g. 1,2 or Enter to type uid manually)`,
|
|
@@ -96903,12 +97341,12 @@ function redactUrl(raw) {
|
|
|
96903
97341
|
// ../../packages/cli/src/commands/stop.ts
|
|
96904
97342
|
init_cjs_shims();
|
|
96905
97343
|
var import_node_child_process7 = require("child_process");
|
|
96906
|
-
var
|
|
97344
|
+
var import_node_fs16 = require("fs");
|
|
96907
97345
|
var import_node_util4 = require("util");
|
|
96908
|
-
var
|
|
96909
|
-
var
|
|
97346
|
+
var import_node_path18 = require("path");
|
|
97347
|
+
var import_picocolors34 = __toESM(require_picocolors(), 1);
|
|
96910
97348
|
var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
|
|
96911
|
-
var unlinkAsync = (0, import_node_util4.promisify)(
|
|
97349
|
+
var unlinkAsync = (0, import_node_util4.promisify)(import_node_fs16.unlink);
|
|
96912
97350
|
var SPAWNED_HOST_FILE2 = "spawned-host.json";
|
|
96913
97351
|
var START_TIME_TOLERANCE_S = 60;
|
|
96914
97352
|
var STOP_WAIT_MS = 5e3;
|
|
@@ -96923,30 +97361,30 @@ async function runStop(opts) {
|
|
|
96923
97361
|
const root = findRepoRoot(cwd) ?? cwd;
|
|
96924
97362
|
const hostUrl = readSourceHostUrl(root);
|
|
96925
97363
|
if (!hostUrl) {
|
|
96926
|
-
console.log(
|
|
97364
|
+
console.log(import_picocolors34.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
|
|
96927
97365
|
return 0;
|
|
96928
97366
|
}
|
|
96929
97367
|
return await stopHost(root, hostUrl);
|
|
96930
97368
|
} catch (err) {
|
|
96931
|
-
console.error(
|
|
97369
|
+
console.error(import_picocolors34.default.red(err.message));
|
|
96932
97370
|
return 1;
|
|
96933
97371
|
}
|
|
96934
97372
|
}
|
|
96935
97373
|
async function stopHost(root, hostUrl) {
|
|
96936
97374
|
const alive = await isHostHealthy(hostUrl);
|
|
96937
97375
|
if (!alive) {
|
|
96938
|
-
console.log(
|
|
97376
|
+
console.log(import_picocolors34.default.dim(`Host ${hostUrl} is already stopped.`));
|
|
96939
97377
|
return 0;
|
|
96940
97378
|
}
|
|
96941
97379
|
const port = extractPort(hostUrl);
|
|
96942
97380
|
if (port === null) {
|
|
96943
|
-
console.error(
|
|
97381
|
+
console.error(import_picocolors34.default.red(`Cannot determine port from host URL: ${hostUrl}`));
|
|
96944
97382
|
return 1;
|
|
96945
97383
|
}
|
|
96946
97384
|
const spawned = readSpawnedHost(root);
|
|
96947
97385
|
if (spawned === null) {
|
|
96948
97386
|
console.error(
|
|
96949
|
-
|
|
97387
|
+
import_picocolors34.default.red(
|
|
96950
97388
|
`No ownership record found (.horus/${SPAWNED_HOST_FILE2} absent). Horus will not stop a host it did not spawn.`
|
|
96951
97389
|
)
|
|
96952
97390
|
);
|
|
@@ -96954,12 +97392,12 @@ async function stopHost(root, hostUrl) {
|
|
|
96954
97392
|
}
|
|
96955
97393
|
const recordError = validateSpawnedRecord(spawned);
|
|
96956
97394
|
if (recordError !== null) {
|
|
96957
|
-
console.error(
|
|
97395
|
+
console.error(import_picocolors34.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
|
|
96958
97396
|
return 1;
|
|
96959
97397
|
}
|
|
96960
97398
|
if (spawned.port !== port) {
|
|
96961
97399
|
console.error(
|
|
96962
|
-
|
|
97400
|
+
import_picocolors34.default.red(
|
|
96963
97401
|
`Ownership record port (${spawned.port}) does not match host URL port (${port}). Record may be stale.`
|
|
96964
97402
|
)
|
|
96965
97403
|
);
|
|
@@ -96967,7 +97405,7 @@ async function stopHost(root, hostUrl) {
|
|
|
96967
97405
|
}
|
|
96968
97406
|
if (spawned.root !== root) {
|
|
96969
97407
|
console.error(
|
|
96970
|
-
|
|
97408
|
+
import_picocolors34.default.red(
|
|
96971
97409
|
`Ownership record root (${spawned.root}) does not match resolved root (${root}). Record may be stale.`
|
|
96972
97410
|
)
|
|
96973
97411
|
);
|
|
@@ -96975,9 +97413,9 @@ async function stopHost(root, hostUrl) {
|
|
|
96975
97413
|
}
|
|
96976
97414
|
const info = await getProcessInfo(spawned.pid);
|
|
96977
97415
|
if (info === null) {
|
|
96978
|
-
console.log(
|
|
97416
|
+
console.log(import_picocolors34.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
|
|
96979
97417
|
try {
|
|
96980
|
-
await unlinkAsync((0,
|
|
97418
|
+
await unlinkAsync((0, import_node_path18.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
|
|
96981
97419
|
} catch {
|
|
96982
97420
|
}
|
|
96983
97421
|
return 0;
|
|
@@ -96988,7 +97426,7 @@ async function stopHost(root, hostUrl) {
|
|
|
96988
97426
|
);
|
|
96989
97427
|
if (!hostPortRe.test(info.args)) {
|
|
96990
97428
|
console.error(
|
|
96991
|
-
|
|
97429
|
+
import_picocolors34.default.red(
|
|
96992
97430
|
`Pid ${spawned.pid} args do not match "horus-source host --port ${portStr}". Got: "${info.args.slice(0, 120)}". Aborting for safety.`
|
|
96993
97431
|
)
|
|
96994
97432
|
);
|
|
@@ -96997,12 +97435,12 @@ async function stopHost(root, hostUrl) {
|
|
|
96997
97435
|
const startTs = new Date(spawned.startedAt).getTime();
|
|
96998
97436
|
const recordedAgeS = Math.round((Date.now() - startTs) / 1e3);
|
|
96999
97437
|
if (!Number.isFinite(info.etimeSeconds)) {
|
|
97000
|
-
console.error(
|
|
97438
|
+
console.error(import_picocolors34.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
|
|
97001
97439
|
return 1;
|
|
97002
97440
|
}
|
|
97003
97441
|
if (Math.abs(info.etimeSeconds - recordedAgeS) > START_TIME_TOLERANCE_S) {
|
|
97004
97442
|
console.error(
|
|
97005
|
-
|
|
97443
|
+
import_picocolors34.default.red(
|
|
97006
97444
|
`Pid ${spawned.pid} age mismatch: record says ~${recordedAgeS}s, process reports ${info.etimeSeconds}s elapsed. Possible PID reuse \u2014 aborting for safety.`
|
|
97007
97445
|
)
|
|
97008
97446
|
);
|
|
@@ -97014,9 +97452,9 @@ async function stopHost(root, hostUrl) {
|
|
|
97014
97452
|
signaled = true;
|
|
97015
97453
|
} catch (err) {
|
|
97016
97454
|
if (err.code === "ESRCH") {
|
|
97017
|
-
console.log(
|
|
97455
|
+
console.log(import_picocolors34.default.dim(`Process pid ${spawned.pid} exited before signal \u2014 already stopped.`));
|
|
97018
97456
|
} else {
|
|
97019
|
-
console.error(
|
|
97457
|
+
console.error(import_picocolors34.default.red(`Failed to signal pid ${spawned.pid}: ${err.message}`));
|
|
97020
97458
|
return 1;
|
|
97021
97459
|
}
|
|
97022
97460
|
}
|
|
@@ -97032,18 +97470,18 @@ async function stopHost(root, hostUrl) {
|
|
|
97032
97470
|
}
|
|
97033
97471
|
if (!exited) {
|
|
97034
97472
|
console.error(
|
|
97035
|
-
|
|
97473
|
+
import_picocolors34.default.red(
|
|
97036
97474
|
`Host pid ${spawned.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`
|
|
97037
97475
|
)
|
|
97038
97476
|
);
|
|
97039
97477
|
return 1;
|
|
97040
97478
|
}
|
|
97041
97479
|
console.log(
|
|
97042
|
-
`${
|
|
97480
|
+
`${import_picocolors34.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors34.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
|
|
97043
97481
|
);
|
|
97044
97482
|
}
|
|
97045
97483
|
try {
|
|
97046
|
-
await unlinkAsync((0,
|
|
97484
|
+
await unlinkAsync((0, import_node_path18.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
|
|
97047
97485
|
} catch {
|
|
97048
97486
|
}
|
|
97049
97487
|
return 0;
|
|
@@ -97052,7 +97490,7 @@ async function stopAll() {
|
|
|
97052
97490
|
const registry2 = readRegistry();
|
|
97053
97491
|
const projects2 = Object.entries(registry2.projects);
|
|
97054
97492
|
if (projects2.length === 0) {
|
|
97055
|
-
console.log(
|
|
97493
|
+
console.log(import_picocolors34.default.dim("No registered projects."));
|
|
97056
97494
|
return 0;
|
|
97057
97495
|
}
|
|
97058
97496
|
let stopped = 0;
|
|
@@ -97062,17 +97500,17 @@ async function stopAll() {
|
|
|
97062
97500
|
if (!hostUrl) continue;
|
|
97063
97501
|
const alive = await isHostHealthy(hostUrl);
|
|
97064
97502
|
if (!alive) continue;
|
|
97065
|
-
console.log(` Stopping ${
|
|
97503
|
+
console.log(` Stopping ${import_picocolors34.default.bold(name)} ${import_picocolors34.default.dim(`(${hostUrl})`)}`);
|
|
97066
97504
|
const code = await stopHost(entry2.root, hostUrl);
|
|
97067
97505
|
if (code === 0) stopped++;
|
|
97068
97506
|
else failed++;
|
|
97069
97507
|
}
|
|
97070
97508
|
if (stopped === 0 && failed === 0) {
|
|
97071
|
-
console.log(
|
|
97509
|
+
console.log(import_picocolors34.default.dim("No running source-intelligence hosts found."));
|
|
97072
97510
|
} else {
|
|
97073
97511
|
console.log(
|
|
97074
97512
|
`
|
|
97075
|
-
Stopped ${stopped} host(s)${failed > 0 ?
|
|
97513
|
+
Stopped ${stopped} host(s)${failed > 0 ? import_picocolors34.default.red(`, ${failed} failed`) : ""}.`
|
|
97076
97514
|
);
|
|
97077
97515
|
}
|
|
97078
97516
|
return failed > 0 ? 1 : 0;
|
|
@@ -97124,12 +97562,12 @@ function extractPort(hostUrl) {
|
|
|
97124
97562
|
|
|
97125
97563
|
// ../../packages/cli/src/commands/hosts.ts
|
|
97126
97564
|
init_cjs_shims();
|
|
97127
|
-
var
|
|
97565
|
+
var import_picocolors35 = __toESM(require_picocolors(), 1);
|
|
97128
97566
|
async function runHosts() {
|
|
97129
97567
|
const registry2 = readRegistry();
|
|
97130
97568
|
const projects2 = Object.entries(registry2.projects);
|
|
97131
97569
|
if (projects2.length === 0) {
|
|
97132
|
-
console.log(
|
|
97570
|
+
console.log(import_picocolors35.default.dim("No registered projects. Run `horus index` in a repo first."));
|
|
97133
97571
|
return 0;
|
|
97134
97572
|
}
|
|
97135
97573
|
const rows = [];
|
|
@@ -97146,29 +97584,29 @@ async function runHosts() {
|
|
|
97146
97584
|
});
|
|
97147
97585
|
const anyHost = rows.some((r) => r.hostUrl !== null);
|
|
97148
97586
|
if (!anyHost) {
|
|
97149
|
-
console.log(
|
|
97587
|
+
console.log(import_picocolors35.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
|
|
97150
97588
|
return 0;
|
|
97151
97589
|
}
|
|
97152
97590
|
console.log("");
|
|
97153
97591
|
for (const row of rows) {
|
|
97154
97592
|
if (row.hostUrl === null) continue;
|
|
97155
|
-
const status = row.healthy ?
|
|
97593
|
+
const status = row.healthy ? import_picocolors35.default.green("\u25CF running") : import_picocolors35.default.red("\u25CF stopped");
|
|
97156
97594
|
const port = extractPort2(row.hostUrl) ?? "?";
|
|
97157
97595
|
console.log(
|
|
97158
|
-
` ${status} ${
|
|
97596
|
+
` ${status} ${import_picocolors35.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors35.default.dim(row.root)}`
|
|
97159
97597
|
);
|
|
97160
97598
|
}
|
|
97161
97599
|
console.log("");
|
|
97162
97600
|
const noHost = rows.filter((r) => r.hostUrl === null);
|
|
97163
97601
|
if (noHost.length > 0) {
|
|
97164
97602
|
for (const row of noHost) {
|
|
97165
|
-
console.log(` ${
|
|
97603
|
+
console.log(` ${import_picocolors35.default.dim("\u25CB no host")} ${import_picocolors35.default.dim(row.name)}`);
|
|
97166
97604
|
}
|
|
97167
97605
|
console.log("");
|
|
97168
97606
|
}
|
|
97169
97607
|
const running = rows.filter((r) => r.healthy).length;
|
|
97170
97608
|
console.log(
|
|
97171
|
-
|
|
97609
|
+
import_picocolors35.default.dim(
|
|
97172
97610
|
`${running} running \xB7 horus stop to reap \xB7 horus stop --all to stop everything`
|
|
97173
97611
|
)
|
|
97174
97612
|
);
|
|
@@ -97185,12 +97623,12 @@ function extractPort2(hostUrl) {
|
|
|
97185
97623
|
|
|
97186
97624
|
// ../../packages/cli/src/commands/doctor.ts
|
|
97187
97625
|
init_cjs_shims();
|
|
97188
|
-
var
|
|
97626
|
+
var import_picocolors36 = __toESM(require_picocolors(), 1);
|
|
97189
97627
|
var DEFAULT_DB_URL4 = "postgresql://horus:horus@localhost:5433/horus";
|
|
97190
97628
|
function mark2(status) {
|
|
97191
|
-
if (status === "pass") return
|
|
97192
|
-
if (status === "warn") return
|
|
97193
|
-
return
|
|
97629
|
+
if (status === "pass") return import_picocolors36.default.green("\u2713");
|
|
97630
|
+
if (status === "warn") return import_picocolors36.default.yellow("~");
|
|
97631
|
+
return import_picocolors36.default.red("\u2717");
|
|
97194
97632
|
}
|
|
97195
97633
|
async function runDoctor(opts) {
|
|
97196
97634
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -97417,11 +97855,11 @@ async function runDoctor(opts) {
|
|
|
97417
97855
|
write(JSON.stringify(output, null, 2));
|
|
97418
97856
|
return hasFailure ? 1 : 0;
|
|
97419
97857
|
}
|
|
97420
|
-
write(
|
|
97858
|
+
write(import_picocolors36.default.bold("\nHorus readiness check\n"));
|
|
97421
97859
|
for (const check2 of checks) {
|
|
97422
|
-
write(` ${mark2(check2.status)} ${
|
|
97860
|
+
write(` ${mark2(check2.status)} ${import_picocolors36.default.bold(check2.label.padEnd(26))} ${import_picocolors36.default.dim(check2.detail)}`);
|
|
97423
97861
|
if (check2.next) {
|
|
97424
|
-
write(` ${
|
|
97862
|
+
write(` ${import_picocolors36.default.dim("\u2192 " + check2.next)}`);
|
|
97425
97863
|
}
|
|
97426
97864
|
}
|
|
97427
97865
|
write("");
|
|
@@ -97431,16 +97869,16 @@ async function runDoctor(opts) {
|
|
|
97431
97869
|
// ../../packages/cli/src/commands/providers-doctor.ts
|
|
97432
97870
|
init_cjs_shims();
|
|
97433
97871
|
var import_node_child_process8 = require("child_process");
|
|
97434
|
-
var
|
|
97872
|
+
var import_picocolors37 = __toESM(require_picocolors(), 1);
|
|
97435
97873
|
function statusMark(status) {
|
|
97436
|
-
if (status === "ready") return
|
|
97437
|
-
if (status === "installed") return
|
|
97438
|
-
return
|
|
97874
|
+
if (status === "ready") return import_picocolors37.default.green("\u2713");
|
|
97875
|
+
if (status === "installed") return import_picocolors37.default.yellow("~");
|
|
97876
|
+
return import_picocolors37.default.red("\u2717");
|
|
97439
97877
|
}
|
|
97440
97878
|
function statusLabel(status) {
|
|
97441
|
-
if (status === "ready") return
|
|
97442
|
-
if (status === "installed") return
|
|
97443
|
-
return
|
|
97879
|
+
if (status === "ready") return import_picocolors37.default.green("ready");
|
|
97880
|
+
if (status === "installed") return import_picocolors37.default.yellow("installed (not configured)");
|
|
97881
|
+
return import_picocolors37.default.dim("not found on PATH");
|
|
97444
97882
|
}
|
|
97445
97883
|
function detectBinary(id) {
|
|
97446
97884
|
const result = (0, import_node_child_process8.spawnSync)(id, ["--version"], { stdio: "pipe", timeout: 2e3 });
|
|
@@ -97457,7 +97895,7 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97457
97895
|
const registry2 = opts?.registry ?? DEFAULT_LOCAL_PROVIDER_REGISTRY;
|
|
97458
97896
|
const write = opts?.write ?? ((line2) => console.log(line2));
|
|
97459
97897
|
const results = buildProviderResults(registry2, opts?._detect);
|
|
97460
|
-
write(
|
|
97898
|
+
write(import_picocolors37.default.bold("\nLocal AI providers\n"));
|
|
97461
97899
|
for (const result of results) {
|
|
97462
97900
|
const descriptor = registry2.get(result.id);
|
|
97463
97901
|
const name = descriptor?.displayName ?? result.id;
|
|
@@ -97465,11 +97903,11 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97465
97903
|
` ${statusMark(result.status)} ${result.id.padEnd(8)} ${name.padEnd(22)} ${statusLabel(result.status)}`
|
|
97466
97904
|
);
|
|
97467
97905
|
if (result.status !== "ready" && result.detail) {
|
|
97468
|
-
write(` ${
|
|
97906
|
+
write(` ${import_picocolors37.default.dim("\u2192 " + result.detail)}`);
|
|
97469
97907
|
}
|
|
97470
97908
|
}
|
|
97471
97909
|
write("");
|
|
97472
|
-
write(
|
|
97910
|
+
write(import_picocolors37.default.bold("Cloud AI providers\n"));
|
|
97473
97911
|
let source = null;
|
|
97474
97912
|
if (opts?._anthropicKey !== void 0) {
|
|
97475
97913
|
source = opts._anthropicKey ? "env" : null;
|
|
@@ -97484,18 +97922,18 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97484
97922
|
}
|
|
97485
97923
|
if (source === "config") {
|
|
97486
97924
|
write(
|
|
97487
|
-
` ${
|
|
97925
|
+
` ${import_picocolors37.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.green("configured (local .horus config)")}`
|
|
97488
97926
|
);
|
|
97489
97927
|
} else if (source === "env") {
|
|
97490
97928
|
write(
|
|
97491
|
-
` ${
|
|
97929
|
+
` ${import_picocolors37.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.green("configured (ANTHROPIC_API_KEY env)")}`
|
|
97492
97930
|
);
|
|
97493
97931
|
} else {
|
|
97494
97932
|
write(
|
|
97495
|
-
` ${
|
|
97933
|
+
` ${import_picocolors37.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.dim("not configured")}`
|
|
97496
97934
|
);
|
|
97497
97935
|
write(
|
|
97498
|
-
` ${
|
|
97936
|
+
` ${import_picocolors37.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
|
|
97499
97937
|
);
|
|
97500
97938
|
}
|
|
97501
97939
|
write("");
|
|
@@ -97504,9 +97942,9 @@ async function runProvidersDoctorCommand(opts) {
|
|
|
97504
97942
|
|
|
97505
97943
|
// ../../packages/cli/src/commands/generate-config.ts
|
|
97506
97944
|
init_cjs_shims();
|
|
97507
|
-
var
|
|
97508
|
-
var
|
|
97509
|
-
var
|
|
97945
|
+
var import_node_fs17 = require("fs");
|
|
97946
|
+
var import_node_path19 = require("path");
|
|
97947
|
+
var import_picocolors38 = __toESM(require_picocolors(), 1);
|
|
97510
97948
|
function configTemplate(name, repoPath) {
|
|
97511
97949
|
return `export default {
|
|
97512
97950
|
database: {
|
|
@@ -97554,39 +97992,39 @@ async function runGenerateConfig(opts) {
|
|
|
97554
97992
|
const defaults = localConfigPath2 != null ? projectDefaults(localConfigPath2) : null;
|
|
97555
97993
|
const hasLocalConfig = defaults != null;
|
|
97556
97994
|
const defaultOut = hasLocalConfig ? "horus.config.example.js" : "horus.config.js";
|
|
97557
|
-
const outPath = (0,
|
|
97995
|
+
const outPath = (0, import_node_path19.resolve)(cwd, opts.out ?? defaultOut);
|
|
97558
97996
|
const name = opts.name ?? defaults?.name ?? "my-project";
|
|
97559
97997
|
const repoPath = opts.repo ?? defaults?.repoPath ?? `/path/to/${name}`;
|
|
97560
|
-
if ((0,
|
|
97561
|
-
log(`${
|
|
97562
|
-
log(
|
|
97998
|
+
if ((0, import_node_fs17.existsSync)(outPath) && !opts.force) {
|
|
97999
|
+
log(`${import_picocolors38.default.red("\u2717")} ${outPath} already exists`);
|
|
98000
|
+
log(import_picocolors38.default.dim(" pass --force to overwrite"));
|
|
97563
98001
|
return 1;
|
|
97564
98002
|
}
|
|
97565
98003
|
try {
|
|
97566
|
-
(0,
|
|
97567
|
-
(0,
|
|
98004
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path19.dirname)(outPath), { recursive: true });
|
|
98005
|
+
(0, import_node_fs17.writeFileSync)(outPath, configTemplate(name, repoPath), "utf8");
|
|
97568
98006
|
} catch (err) {
|
|
97569
|
-
log(`${
|
|
98007
|
+
log(`${import_picocolors38.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
|
|
97570
98008
|
return 1;
|
|
97571
98009
|
}
|
|
97572
|
-
log(`${
|
|
97573
|
-
log(
|
|
97574
|
-
log(
|
|
98010
|
+
log(`${import_picocolors38.default.green("\u2713")} Created ${outPath}`);
|
|
98011
|
+
log(import_picocolors38.default.dim(` project: ${name}`));
|
|
98012
|
+
log(import_picocolors38.default.dim(` repo: ${repoPath}`));
|
|
97575
98013
|
if (hasLocalConfig && localConfigPath2 != null) {
|
|
97576
98014
|
log("");
|
|
97577
|
-
log(
|
|
98015
|
+
log(import_picocolors38.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
|
|
97578
98016
|
log(" \u2022 .horus/config.json \u2014 project config used by `horus investigate` from this repo");
|
|
97579
98017
|
log(" \u2022 horus.config.js \u2014 standalone/global config used with `horus doctor --config <path>`");
|
|
97580
|
-
log(
|
|
98018
|
+
log(import_picocolors38.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
|
|
97581
98019
|
} else {
|
|
97582
|
-
log(
|
|
98020
|
+
log(import_picocolors38.default.dim(` next: horus doctor --config ${outPath}`));
|
|
97583
98021
|
}
|
|
97584
98022
|
return 0;
|
|
97585
98023
|
}
|
|
97586
98024
|
|
|
97587
98025
|
// ../../packages/cli/src/commands/readiness.ts
|
|
97588
98026
|
init_cjs_shims();
|
|
97589
|
-
var
|
|
98027
|
+
var import_picocolors39 = __toESM(require_picocolors(), 1);
|
|
97590
98028
|
var READINESS_AI_CONTRACT = `Provide a clearly separated AI readiness summary with:
|
|
97591
98029
|
|
|
97592
98030
|
Overall assessment
|
|
@@ -97609,9 +98047,9 @@ Confidence / gaps
|
|
|
97609
98047
|
- Which checks Horus could verify vs. which require manual confirmation`;
|
|
97610
98048
|
var DEFAULT_DB_URL5 = "postgresql://horus:horus@localhost:5433/horus";
|
|
97611
98049
|
function mark3(status) {
|
|
97612
|
-
if (status === "pass") return
|
|
97613
|
-
if (status === "warn") return
|
|
97614
|
-
return
|
|
98050
|
+
if (status === "pass") return import_picocolors39.default.green("\u2713");
|
|
98051
|
+
if (status === "warn") return import_picocolors39.default.yellow("~");
|
|
98052
|
+
return import_picocolors39.default.red("\u2717");
|
|
97615
98053
|
}
|
|
97616
98054
|
async function runReadiness(opts) {
|
|
97617
98055
|
const cwd = opts?.cwd ?? process.cwd();
|
|
@@ -97766,20 +98204,20 @@ async function runReadiness(opts) {
|
|
|
97766
98204
|
}
|
|
97767
98205
|
const blockingChecks = checks.filter((c) => c.blocking);
|
|
97768
98206
|
const optionalChecks = checks.filter((c) => !c.blocking);
|
|
97769
|
-
write(
|
|
97770
|
-
write(
|
|
98207
|
+
write(import_picocolors39.default.bold("\nHorus release readiness\n"));
|
|
98208
|
+
write(import_picocolors39.default.bold(" Blocking"));
|
|
97771
98209
|
for (const check2 of blockingChecks) {
|
|
97772
|
-
write(` ${mark3(check2.status)} ${
|
|
98210
|
+
write(` ${mark3(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(22))} ${import_picocolors39.default.dim(check2.detail)}`);
|
|
97773
98211
|
if (check2.next) {
|
|
97774
|
-
write(` ${
|
|
98212
|
+
write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
|
|
97775
98213
|
}
|
|
97776
98214
|
}
|
|
97777
98215
|
write("");
|
|
97778
|
-
write(
|
|
98216
|
+
write(import_picocolors39.default.bold(" Optional"));
|
|
97779
98217
|
for (const check2 of optionalChecks) {
|
|
97780
|
-
write(` ${mark3(check2.status)} ${
|
|
98218
|
+
write(` ${mark3(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(22))} ${import_picocolors39.default.dim(check2.detail)}`);
|
|
97781
98219
|
if (check2.next) {
|
|
97782
|
-
write(` ${
|
|
98220
|
+
write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
|
|
97783
98221
|
}
|
|
97784
98222
|
}
|
|
97785
98223
|
write("");
|
|
@@ -97787,21 +98225,21 @@ async function runReadiness(opts) {
|
|
|
97787
98225
|
const optionalWarns = optionalChecks.filter((c) => c.status === "warn").length;
|
|
97788
98226
|
if (blockingFails.length === 0) {
|
|
97789
98227
|
if (optionalWarns === 0) {
|
|
97790
|
-
write(
|
|
98228
|
+
write(import_picocolors39.default.green(" Ready for demo/release."));
|
|
97791
98229
|
} else {
|
|
97792
98230
|
write(
|
|
97793
|
-
|
|
98231
|
+
import_picocolors39.default.yellow(
|
|
97794
98232
|
` Ready for a basic demo. ${optionalWarns} optional item(s) not configured \u2014 investigation evidence will be limited.`
|
|
97795
98233
|
)
|
|
97796
98234
|
);
|
|
97797
98235
|
}
|
|
97798
98236
|
} else {
|
|
97799
98237
|
write(
|
|
97800
|
-
|
|
98238
|
+
import_picocolors39.default.red(
|
|
97801
98239
|
` Not ready. ${blockingFails.length} blocking item(s) must be resolved before demo/release.`
|
|
97802
98240
|
)
|
|
97803
98241
|
);
|
|
97804
|
-
write(
|
|
98242
|
+
write(import_picocolors39.default.dim(" Re-run `horus readiness` after resolving the items above."));
|
|
97805
98243
|
}
|
|
97806
98244
|
write("");
|
|
97807
98245
|
if (opts?.ai) {
|
|
@@ -97816,7 +98254,7 @@ async function runReadiness(opts) {
|
|
|
97816
98254
|
});
|
|
97817
98255
|
console.log("\n" + renderInterpretation(result));
|
|
97818
98256
|
if (!result.ok) {
|
|
97819
|
-
console.error(
|
|
98257
|
+
console.error(import_picocolors39.default.yellow(`[ai] ${result.warning}`));
|
|
97820
98258
|
}
|
|
97821
98259
|
}
|
|
97822
98260
|
return blockingFails.length > 0 ? 1 : 0;
|
|
@@ -97824,33 +98262,33 @@ async function runReadiness(opts) {
|
|
|
97824
98262
|
|
|
97825
98263
|
// ../../packages/cli/src/commands/skill.ts
|
|
97826
98264
|
init_cjs_shims();
|
|
97827
|
-
var
|
|
97828
|
-
var
|
|
97829
|
-
var
|
|
97830
|
-
var
|
|
97831
|
-
var
|
|
98265
|
+
var import_node_fs18 = require("fs");
|
|
98266
|
+
var import_node_path20 = require("path");
|
|
98267
|
+
var import_node_os4 = require("os");
|
|
98268
|
+
var import_promises2 = require("readline/promises");
|
|
98269
|
+
var import_picocolors40 = __toESM(require_picocolors(), 1);
|
|
97832
98270
|
var SUPPORTED_TARGETS = ["claude", "codex", "gemini", "cursor", "generic"];
|
|
97833
98271
|
function isValidTarget(target) {
|
|
97834
98272
|
return SUPPORTED_TARGETS.includes(target);
|
|
97835
98273
|
}
|
|
97836
98274
|
function getLegacyClaudeSkillPath(opts) {
|
|
97837
|
-
const base2 = opts.global ? (0,
|
|
97838
|
-
return (0,
|
|
98275
|
+
const base2 = opts.global ? (0, import_node_os4.homedir)() : opts.cwd ?? process.cwd();
|
|
98276
|
+
return (0, import_node_path20.join)(base2, ".claude", "skills", "horus.md");
|
|
97839
98277
|
}
|
|
97840
98278
|
function getSkillInstallPath(target, opts) {
|
|
97841
|
-
const base2 = opts.global ? (0,
|
|
98279
|
+
const base2 = opts.global ? (0, import_node_os4.homedir)() : opts.cwd ?? process.cwd();
|
|
97842
98280
|
switch (target) {
|
|
97843
98281
|
case "claude":
|
|
97844
|
-
return (0,
|
|
98282
|
+
return (0, import_node_path20.join)(base2, ".claude", "skills", "horus", "SKILL.md");
|
|
97845
98283
|
case "gemini":
|
|
97846
|
-
return opts.global ? (0,
|
|
98284
|
+
return opts.global ? (0, import_node_path20.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path20.join)(base2, "GEMINI.md");
|
|
97847
98285
|
case "cursor":
|
|
97848
|
-
return opts.global ? (0,
|
|
98286
|
+
return opts.global ? (0, import_node_path20.join)(base2, ".cursorrules") : (0, import_node_path20.join)(base2, ".cursor", "rules", "horus.mdc");
|
|
97849
98287
|
case "codex":
|
|
97850
|
-
return opts.global ? (0,
|
|
98288
|
+
return opts.global ? (0, import_node_path20.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path20.join)(base2, "AGENTS.md");
|
|
97851
98289
|
case "generic":
|
|
97852
98290
|
default:
|
|
97853
|
-
return (0,
|
|
98291
|
+
return (0, import_node_path20.join)(base2, ".horus", "skills", `horus-${target}.md`);
|
|
97854
98292
|
}
|
|
97855
98293
|
}
|
|
97856
98294
|
var BASE_SKILL = `# Horus Grounded Investigation Skill
|
|
@@ -97893,6 +98331,27 @@ horus ask <id> "what evidence contradicts the likely root cause?"
|
|
|
97893
98331
|
horus score <id>
|
|
97894
98332
|
\`\`\`
|
|
97895
98333
|
|
|
98334
|
+
## Report whether Horus helped (close the loop)
|
|
98335
|
+
|
|
98336
|
+
Once you have **acted on an investigation and know the outcome** \u2014 you verified
|
|
98337
|
+
whether Horus pointed at the real cause \u2014 record it. This is how Horus measures and
|
|
98338
|
+
improves its own accuracy, and without it the impact signal is blind to agent-driven
|
|
98339
|
+
runs (most of them).
|
|
98340
|
+
|
|
98341
|
+
\`\`\`bash
|
|
98342
|
+
horus feedback <id> --resolved yes # Horus pointed at the real cause
|
|
98343
|
+
horus feedback <id> --resolved partly # useful lead, but not the whole cause
|
|
98344
|
+
horus feedback <id> --resolved no # Horus missed the cause
|
|
98345
|
+
\`\`\`
|
|
98346
|
+
|
|
98347
|
+
It is non-interactive and safe to run unattended. Report **once** per investigation,
|
|
98348
|
+
**after** you confirm the outcome \u2014 never before, and never guess. If you can estimate
|
|
98349
|
+
how long the same debugging would have taken you manually, add it:
|
|
98350
|
+
|
|
98351
|
+
\`\`\`bash
|
|
98352
|
+
horus feedback <id> --resolved yes --manual-estimate-min 120
|
|
98353
|
+
\`\`\`
|
|
98354
|
+
|
|
97896
98355
|
## Runtime evidence
|
|
97897
98356
|
|
|
97898
98357
|
Use runtime commands when the question depends on live or recent runtime behavior:
|
|
@@ -98135,7 +98594,7 @@ async function confirm(message) {
|
|
|
98135
98594
|
if (!process.stdin.isTTY) {
|
|
98136
98595
|
return false;
|
|
98137
98596
|
}
|
|
98138
|
-
const rl = (0,
|
|
98597
|
+
const rl = (0, import_promises2.createInterface)({ input: process.stdin, output: process.stdout });
|
|
98139
98598
|
try {
|
|
98140
98599
|
const answer = await rl.question(`${message} [y/N] `);
|
|
98141
98600
|
return /^y(es)?$/i.test(answer.trim());
|
|
@@ -98146,66 +98605,66 @@ async function confirm(message) {
|
|
|
98146
98605
|
async function runSkillInstall(target, opts) {
|
|
98147
98606
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
98148
98607
|
if (!isValidTarget(target)) {
|
|
98149
|
-
log(`${
|
|
98150
|
-
log(
|
|
98608
|
+
log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
|
|
98609
|
+
log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
98151
98610
|
return 1;
|
|
98152
98611
|
}
|
|
98153
98612
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
98154
98613
|
const content = generateSkillContent(target);
|
|
98155
98614
|
if (target === "claude") {
|
|
98156
98615
|
const legacyPath = getLegacyClaudeSkillPath({ global: opts.global, cwd: opts.cwd });
|
|
98157
|
-
if ((0,
|
|
98616
|
+
if ((0, import_node_fs18.existsSync)(legacyPath)) {
|
|
98158
98617
|
if (!opts.force) {
|
|
98159
98618
|
const confirmed = await (opts.confirm ?? confirm)(
|
|
98160
98619
|
`Found old flat skill file at ${legacyPath}. Replace it with the new directory layout?`
|
|
98161
98620
|
);
|
|
98162
98621
|
if (!confirmed) {
|
|
98163
|
-
log(`${
|
|
98164
|
-
log(
|
|
98622
|
+
log(`${import_picocolors40.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
|
|
98623
|
+
log(import_picocolors40.default.dim(" Pass --force to replace without prompting."));
|
|
98165
98624
|
return 1;
|
|
98166
98625
|
}
|
|
98167
98626
|
}
|
|
98168
98627
|
try {
|
|
98169
|
-
(0,
|
|
98170
|
-
log(`${
|
|
98628
|
+
(0, import_node_fs18.rmSync)(legacyPath);
|
|
98629
|
+
log(`${import_picocolors40.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
|
|
98171
98630
|
} catch (err) {
|
|
98172
|
-
log(`${
|
|
98631
|
+
log(`${import_picocolors40.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err.message}`);
|
|
98173
98632
|
return 1;
|
|
98174
98633
|
}
|
|
98175
98634
|
}
|
|
98176
98635
|
}
|
|
98177
|
-
if ((0,
|
|
98178
|
-
log(`${
|
|
98179
|
-
log(
|
|
98636
|
+
if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
|
|
98637
|
+
log(`${import_picocolors40.default.red("\u2717")} ${outPath} already exists`);
|
|
98638
|
+
log(import_picocolors40.default.dim(" pass --force to overwrite"));
|
|
98180
98639
|
return 1;
|
|
98181
98640
|
}
|
|
98182
98641
|
try {
|
|
98183
|
-
(0,
|
|
98184
|
-
(0,
|
|
98642
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
|
|
98643
|
+
(0, import_node_fs18.writeFileSync)(outPath, content, "utf8");
|
|
98185
98644
|
} catch (err) {
|
|
98186
|
-
log(`${
|
|
98645
|
+
log(`${import_picocolors40.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
|
|
98187
98646
|
return 1;
|
|
98188
98647
|
}
|
|
98189
|
-
log(`${
|
|
98648
|
+
log(`${import_picocolors40.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
|
|
98190
98649
|
if (target === "claude") {
|
|
98191
|
-
log(
|
|
98192
|
-
log(
|
|
98650
|
+
log(import_picocolors40.default.dim(" Claude Code will load this skill automatically."));
|
|
98651
|
+
log(import_picocolors40.default.dim(" Invoke it with: /horus"));
|
|
98193
98652
|
} else if (target === "gemini") {
|
|
98194
|
-
log(
|
|
98653
|
+
log(import_picocolors40.default.dim(" Gemini CLI will load this context file automatically."));
|
|
98195
98654
|
} else if (target === "cursor") {
|
|
98196
|
-
log(
|
|
98655
|
+
log(import_picocolors40.default.dim(" Cursor will load this rule automatically."));
|
|
98197
98656
|
} else if (target === "codex") {
|
|
98198
|
-
log(
|
|
98657
|
+
log(import_picocolors40.default.dim(" Codex CLI will load this file automatically."));
|
|
98199
98658
|
} else {
|
|
98200
|
-
log(
|
|
98659
|
+
log(import_picocolors40.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
|
|
98201
98660
|
}
|
|
98202
98661
|
return 0;
|
|
98203
98662
|
}
|
|
98204
98663
|
async function runSkillPrint(target, opts) {
|
|
98205
98664
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
98206
98665
|
if (!isValidTarget(target)) {
|
|
98207
|
-
log(`${
|
|
98208
|
-
log(
|
|
98666
|
+
log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
|
|
98667
|
+
log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
98209
98668
|
return 1;
|
|
98210
98669
|
}
|
|
98211
98670
|
process.stdout.write(generateSkillContent(target));
|
|
@@ -98214,8 +98673,8 @@ async function runSkillPrint(target, opts) {
|
|
|
98214
98673
|
async function runSkillPath(target, opts) {
|
|
98215
98674
|
const log = opts.write ?? ((line2) => console.log(line2));
|
|
98216
98675
|
if (!isValidTarget(target)) {
|
|
98217
|
-
log(`${
|
|
98218
|
-
log(
|
|
98676
|
+
log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
|
|
98677
|
+
log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
|
|
98219
98678
|
return 1;
|
|
98220
98679
|
}
|
|
98221
98680
|
const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
|
|
@@ -98225,11 +98684,11 @@ async function runSkillPath(target, opts) {
|
|
|
98225
98684
|
|
|
98226
98685
|
// ../../packages/cli/src/commands/update.ts
|
|
98227
98686
|
init_cjs_shims();
|
|
98228
|
-
var
|
|
98229
|
-
var
|
|
98230
|
-
var
|
|
98231
|
-
var
|
|
98232
|
-
var
|
|
98687
|
+
var import_node_fs19 = require("fs");
|
|
98688
|
+
var import_node_path21 = require("path");
|
|
98689
|
+
var import_node_os5 = require("os");
|
|
98690
|
+
var import_node_crypto7 = require("crypto");
|
|
98691
|
+
var import_picocolors41 = __toESM(require_picocolors(), 1);
|
|
98233
98692
|
var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
|
|
98234
98693
|
function parseVersion(v) {
|
|
98235
98694
|
const m = v.replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
@@ -98254,13 +98713,13 @@ async function fetchLatestRelease(_fetch = fetch) {
|
|
|
98254
98713
|
return res.json();
|
|
98255
98714
|
}
|
|
98256
98715
|
function sha256hex(buf) {
|
|
98257
|
-
return (0,
|
|
98716
|
+
return (0, import_node_crypto7.createHash)("sha256").update(buf).digest("hex");
|
|
98258
98717
|
}
|
|
98259
98718
|
function resolveBinaryPath() {
|
|
98260
98719
|
const raw = process.argv[1];
|
|
98261
98720
|
if (!raw) throw new Error("Cannot determine binary path from process.argv[1]");
|
|
98262
98721
|
try {
|
|
98263
|
-
return (0,
|
|
98722
|
+
return (0, import_node_fs19.realpathSync)(raw);
|
|
98264
98723
|
} catch {
|
|
98265
98724
|
return raw;
|
|
98266
98725
|
}
|
|
@@ -98270,33 +98729,33 @@ async function runUpdate(opts) {
|
|
|
98270
98729
|
const _fetch = opts._fetch ?? fetch;
|
|
98271
98730
|
const currentVersion = opts._currentVersion ?? HORUS_VERSION;
|
|
98272
98731
|
write(`
|
|
98273
|
-
${
|
|
98732
|
+
${import_picocolors41.default.bold("Horus update")}
|
|
98274
98733
|
`);
|
|
98275
|
-
write(` Current version: ${
|
|
98734
|
+
write(` Current version: ${import_picocolors41.default.cyan(currentVersion)}`);
|
|
98276
98735
|
let release;
|
|
98277
98736
|
try {
|
|
98278
98737
|
release = await fetchLatestRelease(_fetch);
|
|
98279
98738
|
} catch (e) {
|
|
98280
|
-
write(` ${
|
|
98739
|
+
write(` ${import_picocolors41.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
|
|
98281
98740
|
return 1;
|
|
98282
98741
|
}
|
|
98283
98742
|
const latest = release.tag_name.replace(/^v/, "");
|
|
98284
|
-
write(` Latest version: ${
|
|
98743
|
+
write(` Latest version: ${import_picocolors41.default.cyan(latest)}`);
|
|
98285
98744
|
const newer = isNewer(latest, currentVersion);
|
|
98286
98745
|
if (opts.check) {
|
|
98287
98746
|
if (newer) {
|
|
98288
98747
|
write(`
|
|
98289
|
-
${
|
|
98290
|
-
write(` Run ${
|
|
98748
|
+
${import_picocolors41.default.yellow("\u2192")} ${import_picocolors41.default.bold(latest)} is available.`);
|
|
98749
|
+
write(` Run ${import_picocolors41.default.bold("horus update")} to upgrade.`);
|
|
98291
98750
|
} else {
|
|
98292
98751
|
write(`
|
|
98293
|
-
${
|
|
98752
|
+
${import_picocolors41.default.green("\u2713")} Already on the latest version.`);
|
|
98294
98753
|
}
|
|
98295
98754
|
return 0;
|
|
98296
98755
|
}
|
|
98297
98756
|
if (!newer && !opts.force) {
|
|
98298
98757
|
write(`
|
|
98299
|
-
${
|
|
98758
|
+
${import_picocolors41.default.green("\u2713")} Already on the latest version.`);
|
|
98300
98759
|
return 0;
|
|
98301
98760
|
}
|
|
98302
98761
|
const tag = release.tag_name;
|
|
@@ -98305,8 +98764,8 @@ ${import_picocolors40.default.bold("Horus update")}
|
|
|
98305
98764
|
const binaryAsset = release.assets.find((a) => a.name === assetName);
|
|
98306
98765
|
const checksumAsset = release.assets.find((a) => a.name === checksumName);
|
|
98307
98766
|
if (!binaryAsset) {
|
|
98308
|
-
write(` ${
|
|
98309
|
-
write(` Fallback: ${
|
|
98767
|
+
write(` ${import_picocolors41.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
|
|
98768
|
+
write(` Fallback: ${import_picocolors41.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
|
|
98310
98769
|
return 1;
|
|
98311
98770
|
}
|
|
98312
98771
|
write(`
|
|
@@ -98316,7 +98775,7 @@ ${import_picocolors40.default.bold("Horus update")}
|
|
|
98316
98775
|
redirect: "follow"
|
|
98317
98776
|
});
|
|
98318
98777
|
if (!binRes.ok || !binRes.body) {
|
|
98319
|
-
write(` ${
|
|
98778
|
+
write(` ${import_picocolors41.default.red("\u2717")} Download failed: ${binRes.status}`);
|
|
98320
98779
|
return 1;
|
|
98321
98780
|
}
|
|
98322
98781
|
const binBuf = Buffer.from(await binRes.arrayBuffer());
|
|
@@ -98330,75 +98789,75 @@ ${import_picocolors40.default.bold("Horus update")}
|
|
|
98330
98789
|
const expectedHash = csText.trim().split(/\s+/)[0];
|
|
98331
98790
|
const actualHash = sha256hex(binBuf);
|
|
98332
98791
|
if (expectedHash !== actualHash) {
|
|
98333
|
-
write(` ${
|
|
98792
|
+
write(` ${import_picocolors41.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
|
|
98334
98793
|
write(` Expected: ${expectedHash}`);
|
|
98335
98794
|
write(` Got: ${actualHash}`);
|
|
98336
98795
|
return 1;
|
|
98337
98796
|
}
|
|
98338
|
-
write(` ${
|
|
98797
|
+
write(` ${import_picocolors41.default.green("\u2713")} Checksum verified.`);
|
|
98339
98798
|
}
|
|
98340
98799
|
}
|
|
98341
98800
|
let binaryPath;
|
|
98342
98801
|
try {
|
|
98343
98802
|
binaryPath = resolveBinaryPath();
|
|
98344
98803
|
} catch (e) {
|
|
98345
|
-
write(` ${
|
|
98804
|
+
write(` ${import_picocolors41.default.red("\u2717")} ${e.message}`);
|
|
98346
98805
|
return 1;
|
|
98347
98806
|
}
|
|
98348
|
-
if (!(0,
|
|
98349
|
-
write(` ${
|
|
98807
|
+
if (!(0, import_node_fs19.existsSync)(binaryPath)) {
|
|
98808
|
+
write(` ${import_picocolors41.default.red("\u2717")} Binary not found at ${binaryPath}`);
|
|
98350
98809
|
return 1;
|
|
98351
98810
|
}
|
|
98352
|
-
const tmpPath = (0,
|
|
98811
|
+
const tmpPath = (0, import_node_path21.join)((0, import_node_os5.tmpdir)(), `horus-update-${tag}-${process.pid}`);
|
|
98353
98812
|
const backupPath = `${binaryPath}.bak`;
|
|
98354
98813
|
try {
|
|
98355
|
-
(0,
|
|
98814
|
+
(0, import_node_fs19.writeFileSync)(tmpPath, binBuf, { mode: 493 });
|
|
98356
98815
|
} catch (e) {
|
|
98357
|
-
write(` ${
|
|
98816
|
+
write(` ${import_picocolors41.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
|
|
98358
98817
|
return 1;
|
|
98359
98818
|
}
|
|
98360
98819
|
try {
|
|
98361
|
-
(0,
|
|
98820
|
+
(0, import_node_fs19.renameSync)(binaryPath, backupPath);
|
|
98362
98821
|
try {
|
|
98363
|
-
(0,
|
|
98364
|
-
(0,
|
|
98822
|
+
(0, import_node_fs19.renameSync)(tmpPath, binaryPath);
|
|
98823
|
+
(0, import_node_fs19.chmodSync)(binaryPath, 493);
|
|
98365
98824
|
} catch (e) {
|
|
98366
98825
|
try {
|
|
98367
|
-
(0,
|
|
98826
|
+
(0, import_node_fs19.renameSync)(backupPath, binaryPath);
|
|
98368
98827
|
} catch {
|
|
98369
98828
|
}
|
|
98370
98829
|
throw e;
|
|
98371
98830
|
}
|
|
98372
98831
|
try {
|
|
98373
|
-
(0,
|
|
98832
|
+
(0, import_node_fs19.unlinkSync)(backupPath);
|
|
98374
98833
|
} catch {
|
|
98375
98834
|
}
|
|
98376
98835
|
} catch (e) {
|
|
98377
98836
|
try {
|
|
98378
|
-
(0,
|
|
98837
|
+
(0, import_node_fs19.unlinkSync)(tmpPath);
|
|
98379
98838
|
} catch {
|
|
98380
98839
|
}
|
|
98381
98840
|
const msg = e.message ?? String(e);
|
|
98382
98841
|
const needsSudo = e.code === "EACCES" || e.code === "EPERM";
|
|
98383
|
-
write(` ${
|
|
98842
|
+
write(` ${import_picocolors41.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
|
|
98384
98843
|
if (needsSudo) {
|
|
98385
98844
|
write(`
|
|
98386
98845
|
Try with sudo:`);
|
|
98387
98846
|
write(
|
|
98388
|
-
` ${
|
|
98847
|
+
` ${import_picocolors41.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
|
|
98389
98848
|
);
|
|
98390
98849
|
}
|
|
98391
98850
|
return 1;
|
|
98392
98851
|
}
|
|
98393
|
-
write(` ${
|
|
98394
|
-
write(` ${
|
|
98852
|
+
write(` ${import_picocolors41.default.green("\u2713")} Updated: ${import_picocolors41.default.bold(currentVersion)} \u2192 ${import_picocolors41.default.bold(latest)}`);
|
|
98853
|
+
write(` ${import_picocolors41.default.dim(binaryPath)}`);
|
|
98395
98854
|
return 0;
|
|
98396
98855
|
}
|
|
98397
98856
|
|
|
98398
98857
|
// ../../packages/cli/src/commands/login.ts
|
|
98399
98858
|
init_cjs_shims();
|
|
98400
98859
|
var import_node_child_process9 = require("child_process");
|
|
98401
|
-
var
|
|
98860
|
+
var import_picocolors42 = __toESM(require_picocolors(), 1);
|
|
98402
98861
|
var sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
98403
98862
|
function openBrowser(url) {
|
|
98404
98863
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
@@ -98423,19 +98882,19 @@ async function loginWithToken(apiBaseUrl, token) {
|
|
|
98423
98882
|
}
|
|
98424
98883
|
});
|
|
98425
98884
|
console.log(
|
|
98426
|
-
`${
|
|
98885
|
+
`${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.`
|
|
98427
98886
|
);
|
|
98428
98887
|
return 0;
|
|
98429
98888
|
} catch (err) {
|
|
98430
98889
|
if (err instanceof CloudOfflineError) {
|
|
98431
|
-
console.error(
|
|
98890
|
+
console.error(import_picocolors42.default.red(err.message));
|
|
98432
98891
|
return 1;
|
|
98433
98892
|
}
|
|
98434
98893
|
if (err instanceof CloudError && err.status === 401) {
|
|
98435
|
-
console.error(
|
|
98894
|
+
console.error(import_picocolors42.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
|
|
98436
98895
|
return 1;
|
|
98437
98896
|
}
|
|
98438
|
-
console.error(
|
|
98897
|
+
console.error(import_picocolors42.default.red(`Login failed: ${err.message}`));
|
|
98439
98898
|
return 1;
|
|
98440
98899
|
}
|
|
98441
98900
|
}
|
|
@@ -98446,19 +98905,19 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
98446
98905
|
session = await client.startCliSession();
|
|
98447
98906
|
} catch (err) {
|
|
98448
98907
|
if (err instanceof CloudOfflineError) {
|
|
98449
|
-
console.error(
|
|
98908
|
+
console.error(import_picocolors42.default.red(err.message));
|
|
98450
98909
|
return 1;
|
|
98451
98910
|
}
|
|
98452
|
-
console.error(
|
|
98911
|
+
console.error(import_picocolors42.default.red(`Could not start login: ${err.message}`));
|
|
98453
98912
|
return 1;
|
|
98454
98913
|
}
|
|
98455
98914
|
console.log("");
|
|
98456
98915
|
console.log(` To sign in, open this URL in your browser:`);
|
|
98457
|
-
console.log(` ${
|
|
98916
|
+
console.log(` ${import_picocolors42.default.cyan(import_picocolors42.default.bold(session.verificationUri))}`);
|
|
98458
98917
|
console.log("");
|
|
98459
|
-
console.log(` and enter the code: ${
|
|
98918
|
+
console.log(` and enter the code: ${import_picocolors42.default.bold(session.userCode)}`);
|
|
98460
98919
|
console.log("");
|
|
98461
|
-
console.log(
|
|
98920
|
+
console.log(import_picocolors42.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
|
|
98462
98921
|
openBrowser(session.verificationUriComplete);
|
|
98463
98922
|
const deadline = Date.now() + session.expiresIn * 1e3;
|
|
98464
98923
|
let intervalMs = Math.max(session.interval, 1) * 1e3;
|
|
@@ -98469,7 +98928,7 @@ async function loginWithBrowser(apiBaseUrl) {
|
|
|
98469
98928
|
poll = await client.pollCliSession(session.deviceCode);
|
|
98470
98929
|
} catch (err) {
|
|
98471
98930
|
if (err instanceof CloudOfflineError) {
|
|
98472
|
-
console.error(
|
|
98931
|
+
console.error(import_picocolors42.default.red(`
|
|
98473
98932
|
${err.message}`));
|
|
98474
98933
|
return 1;
|
|
98475
98934
|
}
|
|
@@ -98482,14 +98941,14 @@ ${err.message}`));
|
|
|
98482
98941
|
intervalMs += 2e3;
|
|
98483
98942
|
break;
|
|
98484
98943
|
case "denied":
|
|
98485
|
-
console.error(
|
|
98944
|
+
console.error(import_picocolors42.default.red("\nLogin was denied in the browser."));
|
|
98486
98945
|
return 1;
|
|
98487
98946
|
case "expired":
|
|
98488
|
-
console.error(
|
|
98947
|
+
console.error(import_picocolors42.default.red("\nThis login request expired. Run `horus login` again."));
|
|
98489
98948
|
return 1;
|
|
98490
98949
|
case "approved":
|
|
98491
98950
|
if (!poll.token || !poll.account) {
|
|
98492
|
-
console.error(
|
|
98951
|
+
console.error(import_picocolors42.default.red("\nLogin approved but no token was returned. Try again."));
|
|
98493
98952
|
return 1;
|
|
98494
98953
|
}
|
|
98495
98954
|
writeAuth({
|
|
@@ -98499,12 +98958,12 @@ ${err.message}`));
|
|
|
98499
98958
|
});
|
|
98500
98959
|
console.log(
|
|
98501
98960
|
`
|
|
98502
|
-
${
|
|
98961
|
+
${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.`
|
|
98503
98962
|
);
|
|
98504
98963
|
return 0;
|
|
98505
98964
|
}
|
|
98506
98965
|
}
|
|
98507
|
-
console.error(
|
|
98966
|
+
console.error(import_picocolors42.default.red("\nTimed out waiting for approval. Run `horus login` again."));
|
|
98508
98967
|
return 1;
|
|
98509
98968
|
}
|
|
98510
98969
|
async function runLogin(opts = {}) {
|
|
@@ -98518,7 +98977,7 @@ async function runLogin(opts = {}) {
|
|
|
98518
98977
|
async function runLogout() {
|
|
98519
98978
|
const auth = readAuth();
|
|
98520
98979
|
if (!auth) {
|
|
98521
|
-
console.log(
|
|
98980
|
+
console.log(import_picocolors42.default.dim("Not logged in."));
|
|
98522
98981
|
return 0;
|
|
98523
98982
|
}
|
|
98524
98983
|
const session = authedClient();
|
|
@@ -98532,14 +98991,14 @@ async function runLogout() {
|
|
|
98532
98991
|
}
|
|
98533
98992
|
}
|
|
98534
98993
|
clearAuth();
|
|
98535
|
-
console.log(`${
|
|
98994
|
+
console.log(`${import_picocolors42.default.green("\u2713")} Logged out. Local mode still works.`);
|
|
98536
98995
|
return 0;
|
|
98537
98996
|
}
|
|
98538
98997
|
|
|
98539
98998
|
// ../../packages/cli/src/commands/cloud.ts
|
|
98540
98999
|
init_cjs_shims();
|
|
98541
|
-
var
|
|
98542
|
-
var
|
|
99000
|
+
var import_picocolors43 = __toESM(require_picocolors(), 1);
|
|
99001
|
+
var import_promises3 = require("readline/promises");
|
|
98543
99002
|
|
|
98544
99003
|
// ../../packages/cli/src/lib/cloud/git.ts
|
|
98545
99004
|
init_cjs_shims();
|
|
@@ -98583,7 +99042,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98583
99042
|
const root = repoRootOrCwd(opts.cwd);
|
|
98584
99043
|
const session = authedClient();
|
|
98585
99044
|
if (!session) {
|
|
98586
|
-
console.error(
|
|
99045
|
+
console.error(import_picocolors43.default.red(`Not logged in. Run ${import_picocolors43.default.bold("horus login")} first.`));
|
|
98587
99046
|
return 1;
|
|
98588
99047
|
}
|
|
98589
99048
|
let ctx;
|
|
@@ -98593,7 +99052,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98593
99052
|
return reportCloudError(err);
|
|
98594
99053
|
}
|
|
98595
99054
|
const remote = detectGitRemote(root);
|
|
98596
|
-
if (remote) console.log(
|
|
99055
|
+
if (remote) console.log(import_picocolors43.default.dim(`Detected git remote: ${remote.remoteUrl}`));
|
|
98597
99056
|
let resolved = null;
|
|
98598
99057
|
let target = opts.project;
|
|
98599
99058
|
if (!target) {
|
|
@@ -98609,7 +99068,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98609
99068
|
const org = ctx.organizations.find((o) => o.id === match.organizationId);
|
|
98610
99069
|
if (ws && org) {
|
|
98611
99070
|
target = `${org.slug}/${ws.slug}/${match.slug}`;
|
|
98612
|
-
console.log(
|
|
99071
|
+
console.log(import_picocolors43.default.dim(`Matched existing project by remote: ${import_picocolors43.default.bold(target)}`));
|
|
98613
99072
|
}
|
|
98614
99073
|
}
|
|
98615
99074
|
}
|
|
@@ -98618,8 +99077,8 @@ async function runCloudLink(opts = {}) {
|
|
|
98618
99077
|
if (created === "aborted") return 1;
|
|
98619
99078
|
if (!created) {
|
|
98620
99079
|
console.error(
|
|
98621
|
-
|
|
98622
|
-
Run ${
|
|
99080
|
+
import_picocolors43.default.red("No project to link.") + `
|
|
99081
|
+
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")}.`
|
|
98623
99082
|
);
|
|
98624
99083
|
return 1;
|
|
98625
99084
|
}
|
|
@@ -98629,8 +99088,8 @@ async function runCloudLink(opts = {}) {
|
|
|
98629
99088
|
resolved = resolveTriple(ctx, target);
|
|
98630
99089
|
if (!resolved) {
|
|
98631
99090
|
console.error(
|
|
98632
|
-
|
|
98633
|
-
Run ${
|
|
99091
|
+
import_picocolors43.default.red(`You don't have access to ${import_picocolors43.default.bold(target)}.`) + `
|
|
99092
|
+
Run ${import_picocolors43.default.bold("horus context list")} to see available projects.`
|
|
98634
99093
|
);
|
|
98635
99094
|
return 1;
|
|
98636
99095
|
}
|
|
@@ -98643,10 +99102,10 @@ async function runCloudLink(opts = {}) {
|
|
|
98643
99102
|
project: resolved.project
|
|
98644
99103
|
});
|
|
98645
99104
|
console.log(
|
|
98646
|
-
`${
|
|
99105
|
+
`${import_picocolors43.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors43.default.bold(label)} ${import_picocolors43.default.dim("(cloud)")}.`
|
|
98647
99106
|
);
|
|
98648
99107
|
console.log(
|
|
98649
|
-
|
|
99108
|
+
import_picocolors43.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors43.default.bold("horus context use local")} to switch back.`)
|
|
98650
99109
|
);
|
|
98651
99110
|
return 0;
|
|
98652
99111
|
}
|
|
@@ -98654,11 +99113,11 @@ async function runCloudUnlink(opts = {}) {
|
|
|
98654
99113
|
const root = repoRootOrCwd(opts.cwd);
|
|
98655
99114
|
const cfg = readCloudConfig(root);
|
|
98656
99115
|
if (!cfg || cfg.context !== "cloud") {
|
|
98657
|
-
console.log(
|
|
99116
|
+
console.log(import_picocolors43.default.dim("This repo isn't linked to a cloud project."));
|
|
98658
99117
|
return 0;
|
|
98659
99118
|
}
|
|
98660
99119
|
clearCloudConfig(root);
|
|
98661
|
-
console.log(`${
|
|
99120
|
+
console.log(`${import_picocolors43.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors43.default.bold("local")} mode.`);
|
|
98662
99121
|
return 0;
|
|
98663
99122
|
}
|
|
98664
99123
|
async function runCloudStatus(opts = {}) {
|
|
@@ -98666,18 +99125,18 @@ async function runCloudStatus(opts = {}) {
|
|
|
98666
99125
|
const cfg = readCloudConfig(root);
|
|
98667
99126
|
const session = authedClient();
|
|
98668
99127
|
if (!isCloudActive(cfg) || !cfg?.project) {
|
|
98669
|
-
console.log(`${
|
|
98670
|
-
console.log(`${
|
|
99128
|
+
console.log(`${import_picocolors43.default.bold("Context:")} local`);
|
|
99129
|
+
console.log(`${import_picocolors43.default.bold("Storage:")} local Postgres (~/.horus)`);
|
|
98671
99130
|
console.log(
|
|
98672
|
-
|
|
99131
|
+
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.`)
|
|
98673
99132
|
);
|
|
98674
99133
|
return 0;
|
|
98675
99134
|
}
|
|
98676
99135
|
const triple = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
98677
|
-
console.log(`${
|
|
99136
|
+
console.log(`${import_picocolors43.default.bold("Context:")} ${triple} ${import_picocolors43.default.dim("(cloud)")}`);
|
|
98678
99137
|
let projectLine = `${cfg.project.slug}`;
|
|
98679
99138
|
let metaLines = [];
|
|
98680
|
-
let account =
|
|
99139
|
+
let account = import_picocolors43.default.dim("not logged in");
|
|
98681
99140
|
if (session) {
|
|
98682
99141
|
account = session.auth.account.email;
|
|
98683
99142
|
try {
|
|
@@ -98692,9 +99151,9 @@ async function runCloudStatus(opts = {}) {
|
|
|
98692
99151
|
} catch {
|
|
98693
99152
|
}
|
|
98694
99153
|
}
|
|
98695
|
-
console.log(`${
|
|
99154
|
+
console.log(`${import_picocolors43.default.bold("Project:")} ${projectLine}`);
|
|
98696
99155
|
for (const line2 of metaLines) console.log(line2);
|
|
98697
|
-
console.log(`${
|
|
99156
|
+
console.log(`${import_picocolors43.default.bold("Account:")} ${account}`);
|
|
98698
99157
|
return 0;
|
|
98699
99158
|
}
|
|
98700
99159
|
async function runCloudSync(opts = {}) {
|
|
@@ -98702,13 +99161,13 @@ async function runCloudSync(opts = {}) {
|
|
|
98702
99161
|
const cfg = readCloudConfig(root);
|
|
98703
99162
|
if (!isCloudActive(cfg) || !cfg.project) {
|
|
98704
99163
|
console.error(
|
|
98705
|
-
|
|
99164
|
+
import_picocolors43.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors43.default.bold("horus cloud link")} first.`
|
|
98706
99165
|
);
|
|
98707
99166
|
return 1;
|
|
98708
99167
|
}
|
|
98709
99168
|
const session = authedClient();
|
|
98710
99169
|
if (!session) {
|
|
98711
|
-
console.error(
|
|
99170
|
+
console.error(import_picocolors43.default.red(`Not logged in. Run ${import_picocolors43.default.bold("horus login")} first.`));
|
|
98712
99171
|
return 1;
|
|
98713
99172
|
}
|
|
98714
99173
|
const { db, sql: sql2 } = createDb(await resolveDbUrl(opts.config));
|
|
@@ -98729,34 +99188,34 @@ async function runCloudSync(opts = {}) {
|
|
|
98729
99188
|
const target = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
|
|
98730
99189
|
if (uploadable.length === 0) {
|
|
98731
99190
|
console.log(
|
|
98732
|
-
|
|
99191
|
+
import_picocolors43.default.dim(
|
|
98733
99192
|
`No local investigations with reports to upload.` + (skippedNoReport ? ` (${skippedNoReport} without a report skipped)` : "")
|
|
98734
99193
|
)
|
|
98735
99194
|
);
|
|
98736
99195
|
return 0;
|
|
98737
99196
|
}
|
|
98738
|
-
console.log(
|
|
99197
|
+
console.log(import_picocolors43.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors43.default.dim("(cloud)")}:`));
|
|
98739
99198
|
for (const c of uploadable.slice(0, 20)) {
|
|
98740
|
-
console.log(` ${
|
|
99199
|
+
console.log(` ${import_picocolors43.default.dim(c.id.slice(0, 8))} ${(c.title ?? "untitled").slice(0, 70)}`);
|
|
98741
99200
|
}
|
|
98742
|
-
if (uploadable.length > 20) console.log(
|
|
98743
|
-
if (skippedNoReport) console.log(
|
|
99201
|
+
if (uploadable.length > 20) console.log(import_picocolors43.default.dim(` \u2026and ${uploadable.length - 20} more`));
|
|
99202
|
+
if (skippedNoReport) console.log(import_picocolors43.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
|
|
98744
99203
|
if (opts.dryRun) {
|
|
98745
|
-
console.log(
|
|
99204
|
+
console.log(import_picocolors43.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
|
|
98746
99205
|
return 0;
|
|
98747
99206
|
}
|
|
98748
99207
|
if (!opts.yes) {
|
|
98749
99208
|
if (!process.stdin.isTTY) {
|
|
98750
99209
|
console.error(
|
|
98751
|
-
|
|
99210
|
+
import_picocolors43.default.yellow(`Re-run with ${import_picocolors43.default.bold("--yes")} to upload, or ${import_picocolors43.default.bold("--dry-run")} to preview only.`)
|
|
98752
99211
|
);
|
|
98753
99212
|
return 1;
|
|
98754
99213
|
}
|
|
98755
|
-
const rl = (0,
|
|
99214
|
+
const rl = (0, import_promises3.createInterface)({ input: process.stdin, output: process.stdout });
|
|
98756
99215
|
try {
|
|
98757
99216
|
const answer = (await rl.question(`Upload ${uploadable.length} investigation(s) to ${target}? (y/N) `)).trim().toLowerCase();
|
|
98758
99217
|
if (answer !== "y" && answer !== "yes") {
|
|
98759
|
-
console.log(
|
|
99218
|
+
console.log(import_picocolors43.default.dim("Aborted. Nothing uploaded."));
|
|
98760
99219
|
return 0;
|
|
98761
99220
|
}
|
|
98762
99221
|
} finally {
|
|
@@ -98769,18 +99228,18 @@ async function runCloudSync(opts = {}) {
|
|
|
98769
99228
|
try {
|
|
98770
99229
|
const refs = await uploadInvestigationToCloud(session.client, cfg, c.report);
|
|
98771
99230
|
created++;
|
|
98772
|
-
console.log(`${
|
|
99231
|
+
console.log(`${import_picocolors43.default.green("\u2713")} ${import_picocolors43.default.dim(c.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
|
|
98773
99232
|
} catch (err) {
|
|
98774
99233
|
failed++;
|
|
98775
|
-
console.error(`${
|
|
99234
|
+
console.error(`${import_picocolors43.default.red("\u2717")} ${import_picocolors43.default.dim(c.id.slice(0, 8))} ${err.message}`);
|
|
98776
99235
|
}
|
|
98777
99236
|
}
|
|
98778
99237
|
console.log("");
|
|
98779
99238
|
console.log(
|
|
98780
|
-
`${
|
|
99239
|
+
`${import_picocolors43.default.bold("Sync complete:")} ${import_picocolors43.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors43.default.red(`${failed} failed`) : "0 failed") + "."
|
|
98781
99240
|
);
|
|
98782
99241
|
console.log(
|
|
98783
|
-
|
|
99242
|
+
import_picocolors43.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
|
|
98784
99243
|
);
|
|
98785
99244
|
return failed > 0 ? 1 : 0;
|
|
98786
99245
|
}
|
|
@@ -98788,12 +99247,12 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98788
99247
|
if (!process.stdin.isTTY && !yes) return null;
|
|
98789
99248
|
if (ctx.organizations.length === 0) {
|
|
98790
99249
|
console.error(
|
|
98791
|
-
|
|
98792
|
-
Create one at ${
|
|
99250
|
+
import_picocolors43.default.red("You're not a member of any organization yet.") + `
|
|
99251
|
+
Create one at ${import_picocolors43.default.bold("https://cloud.horus.sh")} first.`
|
|
98793
99252
|
);
|
|
98794
99253
|
return null;
|
|
98795
99254
|
}
|
|
98796
|
-
const rl = (0,
|
|
99255
|
+
const rl = (0, import_promises3.createInterface)({ input: process.stdin, output: process.stdout });
|
|
98797
99256
|
const ask2 = async (q, def) => {
|
|
98798
99257
|
if (yes && def !== void 0) return def;
|
|
98799
99258
|
const a = (await rl.question(def ? `${q} (${def}): ` : `${q}: `)).trim();
|
|
@@ -98814,9 +99273,9 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98814
99273
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
98815
99274
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
98816
99275
|
workspace = { id: ws.id, slug: ws.slug };
|
|
98817
|
-
console.log(
|
|
99276
|
+
console.log(import_picocolors43.default.dim(`Created workspace ${import_picocolors43.default.bold(ws.slug)}.`));
|
|
98818
99277
|
} else {
|
|
98819
|
-
console.log(`Workspaces in ${
|
|
99278
|
+
console.log(`Workspaces in ${import_picocolors43.default.bold(org.slug)}:`);
|
|
98820
99279
|
orgWorkspaces.forEach((w, i) => console.log(` ${i + 1}) ${w.slug}`));
|
|
98821
99280
|
console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
|
|
98822
99281
|
const pick2 = Number(await ask2("Workspace number", "1"));
|
|
@@ -98824,7 +99283,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98824
99283
|
const wsName = await ask2("New workspace name", repoName || "default");
|
|
98825
99284
|
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
98826
99285
|
workspace = { id: ws.id, slug: ws.slug };
|
|
98827
|
-
console.log(
|
|
99286
|
+
console.log(import_picocolors43.default.dim(`Created workspace ${import_picocolors43.default.bold(ws.slug)}.`));
|
|
98828
99287
|
} else {
|
|
98829
99288
|
const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
|
|
98830
99289
|
workspace = { id: chosen.id, slug: chosen.slug };
|
|
@@ -98836,7 +99295,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98836
99295
|
remoteUrl: remote?.remoteUrl,
|
|
98837
99296
|
provider: remote ? inferProvider(remote.remoteUrl) : void 0
|
|
98838
99297
|
});
|
|
98839
|
-
console.log(
|
|
99298
|
+
console.log(import_picocolors43.default.dim(`Created project ${import_picocolors43.default.bold(project.slug)}.`));
|
|
98840
99299
|
return {
|
|
98841
99300
|
organization: { id: org.id, slug: org.slug },
|
|
98842
99301
|
workspace,
|
|
@@ -98850,6 +99309,297 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
|
98850
99309
|
}
|
|
98851
99310
|
}
|
|
98852
99311
|
|
|
99312
|
+
// ../../packages/cli/src/commands/telemetry.ts
|
|
99313
|
+
init_cjs_shims();
|
|
99314
|
+
var import_picocolors45 = __toESM(require_picocolors(), 1);
|
|
99315
|
+
var import_node_crypto8 = require("crypto");
|
|
99316
|
+
|
|
99317
|
+
// ../../packages/cli/src/lib/telemetry/transport.ts
|
|
99318
|
+
init_cjs_shims();
|
|
99319
|
+
var CHUNK = 100;
|
|
99320
|
+
var DEFAULT_TIMEOUT_MS = 1500;
|
|
99321
|
+
function telemetryBaseUrl() {
|
|
99322
|
+
return process.env.HORUS_TELEMETRY_URL ?? process.env.HORUS_API_BASE_URL ?? readAuth()?.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
99323
|
+
}
|
|
99324
|
+
async function postBatch(url, events, signal) {
|
|
99325
|
+
const res = await fetch(url, {
|
|
99326
|
+
method: "POST",
|
|
99327
|
+
headers: { "content-type": "application/json" },
|
|
99328
|
+
body: JSON.stringify({ events }),
|
|
99329
|
+
signal
|
|
99330
|
+
});
|
|
99331
|
+
return res.ok;
|
|
99332
|
+
}
|
|
99333
|
+
async function flushTelemetry(opts = {}) {
|
|
99334
|
+
try {
|
|
99335
|
+
if (!resolveConsent().tierA) return;
|
|
99336
|
+
const events = readSpooledEvents();
|
|
99337
|
+
if (events.length === 0) return;
|
|
99338
|
+
const url = `${telemetryBaseUrl().replace(/\/$/, "")}/v1/telemetry`;
|
|
99339
|
+
const controller = new AbortController();
|
|
99340
|
+
const timer2 = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
99341
|
+
try {
|
|
99342
|
+
for (let i = 0; i < events.length; i += CHUNK) {
|
|
99343
|
+
const ok = await postBatch(url, events.slice(i, i + CHUNK), controller.signal);
|
|
99344
|
+
if (!ok) return;
|
|
99345
|
+
}
|
|
99346
|
+
clearSpool();
|
|
99347
|
+
} finally {
|
|
99348
|
+
clearTimeout(timer2);
|
|
99349
|
+
}
|
|
99350
|
+
} catch {
|
|
99351
|
+
}
|
|
99352
|
+
}
|
|
99353
|
+
async function deleteRemoteTelemetry(installId, opts = {}) {
|
|
99354
|
+
try {
|
|
99355
|
+
if (!installId) return;
|
|
99356
|
+
const url = `${telemetryBaseUrl().replace(/\/$/, "")}/v1/telemetry/delete`;
|
|
99357
|
+
const controller = new AbortController();
|
|
99358
|
+
const timer2 = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
99359
|
+
try {
|
|
99360
|
+
await fetch(url, {
|
|
99361
|
+
method: "POST",
|
|
99362
|
+
headers: { "content-type": "application/json" },
|
|
99363
|
+
body: JSON.stringify({ installId }),
|
|
99364
|
+
signal: controller.signal
|
|
99365
|
+
});
|
|
99366
|
+
} finally {
|
|
99367
|
+
clearTimeout(timer2);
|
|
99368
|
+
}
|
|
99369
|
+
} catch {
|
|
99370
|
+
}
|
|
99371
|
+
}
|
|
99372
|
+
|
|
99373
|
+
// ../../packages/cli/src/lib/telemetry/notice.ts
|
|
99374
|
+
init_cjs_shims();
|
|
99375
|
+
var import_picocolors44 = __toESM(require_picocolors(), 1);
|
|
99376
|
+
var PRIVACY_URL = "https://horus.sh/privacy";
|
|
99377
|
+
var SUPPRESS_FIRST_TOKEN = /* @__PURE__ */ new Set(["telemetry", "help"]);
|
|
99378
|
+
var SUPPRESS_FLAGS = /* @__PURE__ */ new Set(["-V", "--version", "-h", "--help"]);
|
|
99379
|
+
function shouldSuppressFor(argv) {
|
|
99380
|
+
const args = argv.slice(2);
|
|
99381
|
+
if (args.length === 0) return true;
|
|
99382
|
+
if (args.some((a) => SUPPRESS_FLAGS.has(a))) return true;
|
|
99383
|
+
const firstToken = args.find((a) => !a.startsWith("-"));
|
|
99384
|
+
return firstToken != null && SUPPRESS_FIRST_TOKEN.has(firstToken);
|
|
99385
|
+
}
|
|
99386
|
+
function banner() {
|
|
99387
|
+
const b2 = import_picocolors44.default.bold;
|
|
99388
|
+
const dim = import_picocolors44.default.dim;
|
|
99389
|
+
return [
|
|
99390
|
+
"",
|
|
99391
|
+
import_picocolors44.default.cyan(b2("Horus collects anonymous usage data to improve the tool.")),
|
|
99392
|
+
dim(" \u2022 On by default: command names, timings, success/failure, and where the"),
|
|
99393
|
+
dim(" engine ran out of evidence. No file contents, queries, or secrets."),
|
|
99394
|
+
dim(" \u2022 Off by default: your investigation inputs/outputs (opt in to help train"),
|
|
99395
|
+
` ${dim("Horus)")} ${dim("\u2014 run")} ${b2("horus telemetry enable-content")}`,
|
|
99396
|
+
dim(" \u2022 If you use Horus Cloud, linked investigations sync to your workspace."),
|
|
99397
|
+
"",
|
|
99398
|
+
` ${dim("Opt out anytime:")} ${b2("horus telemetry disable")} ${dim("\xB7")} ${b2("HORUS_TELEMETRY=0")} ${dim("\xB7")} ${b2("DO_NOT_TRACK=1")}`,
|
|
99399
|
+
` ${dim("Details:")} ${import_picocolors44.default.underline(PRIVACY_URL)}`,
|
|
99400
|
+
""
|
|
99401
|
+
].join("\n");
|
|
99402
|
+
}
|
|
99403
|
+
function maybeShowFirstRunNotice(argv = process.argv) {
|
|
99404
|
+
try {
|
|
99405
|
+
const env = process.env;
|
|
99406
|
+
if (resolveConsent({ env }).forcedOff) return;
|
|
99407
|
+
if (isCI(env)) return;
|
|
99408
|
+
const state = loadOrInitTelemetryState();
|
|
99409
|
+
if (state.tierA.noticeShownAt) return;
|
|
99410
|
+
if (shouldSuppressFor(argv)) return;
|
|
99411
|
+
if (!process.stderr.isTTY) return;
|
|
99412
|
+
process.stderr.write(banner() + "\n");
|
|
99413
|
+
updateTelemetryState((s) => {
|
|
99414
|
+
s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99415
|
+
});
|
|
99416
|
+
} catch {
|
|
99417
|
+
}
|
|
99418
|
+
}
|
|
99419
|
+
|
|
99420
|
+
// ../../packages/cli/src/commands/telemetry.ts
|
|
99421
|
+
function onOff(enabled) {
|
|
99422
|
+
return enabled ? import_picocolors45.default.green("on") : import_picocolors45.default.dim("off");
|
|
99423
|
+
}
|
|
99424
|
+
function warnIfEnvOverrides() {
|
|
99425
|
+
const decision = resolveConsent();
|
|
99426
|
+
if (decision.forcedOff) {
|
|
99427
|
+
console.log(
|
|
99428
|
+
import_picocolors45.default.yellow(
|
|
99429
|
+
`
|
|
99430
|
+
Note: telemetry is currently forced off by your environment (${decision.reason}).`
|
|
99431
|
+
)
|
|
99432
|
+
);
|
|
99433
|
+
console.log(import_picocolors45.default.dim("Your saved preference will apply once that override is removed."));
|
|
99434
|
+
} else if (decision.source === "ci") {
|
|
99435
|
+
console.log(
|
|
99436
|
+
import_picocolors45.default.yellow("\nNote: CI environment detected \u2014 telemetry stays off here regardless.")
|
|
99437
|
+
);
|
|
99438
|
+
}
|
|
99439
|
+
}
|
|
99440
|
+
async function runTelemetryStatus() {
|
|
99441
|
+
const state = readTelemetryState();
|
|
99442
|
+
const decision = resolveConsent({ state });
|
|
99443
|
+
console.log(import_picocolors45.default.bold("Horus telemetry"));
|
|
99444
|
+
console.log(
|
|
99445
|
+
` Usage metadata (Tier A): ${onOff(decision.tierA)} ${import_picocolors45.default.dim(`[${decision.source}]`)}`
|
|
99446
|
+
);
|
|
99447
|
+
console.log(` Content sharing (Tier B): ${onOff(decision.tierB)}`);
|
|
99448
|
+
console.log(import_picocolors45.default.dim(` Reason: ${decision.reason}`));
|
|
99449
|
+
if (state) {
|
|
99450
|
+
console.log(import_picocolors45.default.dim(` Install ID: ${state.installId}`));
|
|
99451
|
+
console.log(
|
|
99452
|
+
import_picocolors45.default.dim(
|
|
99453
|
+
` Saved: Tier A ${state.tierA.enabled ? "on" : "off"}, Tier B ${state.tierB.enabled ? "on" : "off"}`
|
|
99454
|
+
)
|
|
99455
|
+
);
|
|
99456
|
+
} else {
|
|
99457
|
+
console.log(import_picocolors45.default.dim(" No saved preference yet (defaults apply)."));
|
|
99458
|
+
}
|
|
99459
|
+
console.log(import_picocolors45.default.dim(` Config: ${telemetryPath()}`));
|
|
99460
|
+
console.log(import_picocolors45.default.dim(` Privacy: ${PRIVACY_URL}`));
|
|
99461
|
+
if (!state || decision.source === "env" || decision.source === "ci") {
|
|
99462
|
+
warnIfEnvOverrides();
|
|
99463
|
+
}
|
|
99464
|
+
return 0;
|
|
99465
|
+
}
|
|
99466
|
+
async function runTelemetryEnable() {
|
|
99467
|
+
updateTelemetryState((s) => {
|
|
99468
|
+
s.tierA.enabled = true;
|
|
99469
|
+
if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99470
|
+
});
|
|
99471
|
+
console.log(import_picocolors45.default.green("Anonymous usage metadata (Tier A) is now ON. Thank you!"));
|
|
99472
|
+
warnIfEnvOverrides();
|
|
99473
|
+
return 0;
|
|
99474
|
+
}
|
|
99475
|
+
async function runTelemetryDisable() {
|
|
99476
|
+
updateTelemetryState((s) => {
|
|
99477
|
+
s.tierA.enabled = false;
|
|
99478
|
+
s.tierB.enabled = false;
|
|
99479
|
+
if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99480
|
+
});
|
|
99481
|
+
console.log(import_picocolors45.default.yellow("All telemetry is now OFF (Tier A and Tier B)."));
|
|
99482
|
+
return 0;
|
|
99483
|
+
}
|
|
99484
|
+
async function runTelemetryEnableContent() {
|
|
99485
|
+
updateTelemetryState((s) => {
|
|
99486
|
+
s.tierA.enabled = true;
|
|
99487
|
+
s.tierB.enabled = true;
|
|
99488
|
+
s.tierB.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99489
|
+
if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
99490
|
+
});
|
|
99491
|
+
console.log(
|
|
99492
|
+
import_picocolors45.default.green("Content sharing (Tier B) is now ON \u2014 redacted inputs/outputs help improve Horus.")
|
|
99493
|
+
);
|
|
99494
|
+
console.log(import_picocolors45.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
|
|
99495
|
+
console.log(import_picocolors45.default.dim(`What this means: ${PRIVACY_URL}`));
|
|
99496
|
+
warnIfEnvOverrides();
|
|
99497
|
+
return 0;
|
|
99498
|
+
}
|
|
99499
|
+
async function runTelemetryDisableContent() {
|
|
99500
|
+
updateTelemetryState((s) => {
|
|
99501
|
+
s.tierB.enabled = false;
|
|
99502
|
+
});
|
|
99503
|
+
console.log(import_picocolors45.default.yellow("Content sharing (Tier B) is now OFF. Usage metadata is unchanged."));
|
|
99504
|
+
return 0;
|
|
99505
|
+
}
|
|
99506
|
+
async function runTelemetryResetId() {
|
|
99507
|
+
const next = (0, import_node_crypto8.randomUUID)();
|
|
99508
|
+
updateTelemetryState((s) => {
|
|
99509
|
+
s.installId = next;
|
|
99510
|
+
});
|
|
99511
|
+
console.log(import_picocolors45.default.green("Install ID reset."));
|
|
99512
|
+
console.log(import_picocolors45.default.dim(` New install ID: ${next}`));
|
|
99513
|
+
return 0;
|
|
99514
|
+
}
|
|
99515
|
+
async function runTelemetryDelete() {
|
|
99516
|
+
const installId = readTelemetryState()?.installId;
|
|
99517
|
+
if (installId) await deleteRemoteTelemetry(installId);
|
|
99518
|
+
deleteTelemetryState();
|
|
99519
|
+
clearSpool();
|
|
99520
|
+
console.log(import_picocolors45.default.green("Telemetry deleted \u2014 local state cleared and a server purge requested."));
|
|
99521
|
+
console.log(
|
|
99522
|
+
import_picocolors45.default.dim(
|
|
99523
|
+
"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."
|
|
99524
|
+
)
|
|
99525
|
+
);
|
|
99526
|
+
return 0;
|
|
99527
|
+
}
|
|
99528
|
+
|
|
99529
|
+
// ../../packages/cli/src/lib/telemetry/command-hooks.ts
|
|
99530
|
+
init_cjs_shims();
|
|
99531
|
+
function commandPath(cmd) {
|
|
99532
|
+
const parts = [];
|
|
99533
|
+
let current = cmd;
|
|
99534
|
+
while (current && current.parent) {
|
|
99535
|
+
parts.unshift(current.name());
|
|
99536
|
+
current = current.parent;
|
|
99537
|
+
}
|
|
99538
|
+
return parts.join(" ") || cmd.name();
|
|
99539
|
+
}
|
|
99540
|
+
function installCommandTelemetry(program2) {
|
|
99541
|
+
let startedAt = 0;
|
|
99542
|
+
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
99543
|
+
startedAt = Date.now();
|
|
99544
|
+
track({
|
|
99545
|
+
type: "command.invoked",
|
|
99546
|
+
command: commandPath(actionCommand),
|
|
99547
|
+
flags: extractFlagNames(process.argv)
|
|
99548
|
+
});
|
|
99549
|
+
});
|
|
99550
|
+
program2.hook("postAction", (_thisCommand, actionCommand) => {
|
|
99551
|
+
const exitCode = typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
99552
|
+
track({
|
|
99553
|
+
type: "command.completed",
|
|
99554
|
+
command: commandPath(actionCommand),
|
|
99555
|
+
flags: extractFlagNames(process.argv),
|
|
99556
|
+
durationMs: startedAt ? Date.now() - startedAt : 0,
|
|
99557
|
+
exitCode,
|
|
99558
|
+
ok: exitCode === 0
|
|
99559
|
+
});
|
|
99560
|
+
});
|
|
99561
|
+
}
|
|
99562
|
+
|
|
99563
|
+
// ../../packages/cli/src/commands/feedback.ts
|
|
99564
|
+
init_cjs_shims();
|
|
99565
|
+
var import_picocolors46 = __toESM(require_picocolors(), 1);
|
|
99566
|
+
async function runFeedback(investigationId, opts = {}) {
|
|
99567
|
+
const id = investigationId?.trim();
|
|
99568
|
+
if (!id) {
|
|
99569
|
+
console.error(import_picocolors46.default.red("Usage: horus feedback <investigationId> [--resolved yes|partly|no]"));
|
|
99570
|
+
return 1;
|
|
99571
|
+
}
|
|
99572
|
+
if (opts.resolved !== void 0) {
|
|
99573
|
+
const resolved = parseResolved(opts.resolved);
|
|
99574
|
+
if (resolved === null) {
|
|
99575
|
+
console.error(import_picocolors46.default.red(`Invalid --resolved "${opts.resolved}". Use: yes | partly | no.`));
|
|
99576
|
+
return 1;
|
|
99577
|
+
}
|
|
99578
|
+
let manualEstimateMinutes = null;
|
|
99579
|
+
if (opts.manualEstimateMin !== void 0) {
|
|
99580
|
+
const n = Number.parseInt(opts.manualEstimateMin, 10);
|
|
99581
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
99582
|
+
console.error(import_picocolors46.default.red(`Invalid --manual-estimate-min "${opts.manualEstimateMin}" (expect a non-negative integer).`));
|
|
99583
|
+
return 1;
|
|
99584
|
+
}
|
|
99585
|
+
manualEstimateMinutes = n;
|
|
99586
|
+
}
|
|
99587
|
+
submitFeedback({ investigationId: id, resolved, manualEstimateMinutes, horusSeconds: null, source: "flag" });
|
|
99588
|
+
console.log(import_picocolors46.default.dim(`Feedback recorded (${resolved}) \u2014 thanks, this helps improve Horus.`));
|
|
99589
|
+
return 0;
|
|
99590
|
+
}
|
|
99591
|
+
if (!process.stdin.isTTY) {
|
|
99592
|
+
console.error(
|
|
99593
|
+
import_picocolors46.default.yellow(
|
|
99594
|
+
"horus feedback needs an interactive terminal \u2014 or pass --resolved yes|partly|no for non-interactive (agent) use."
|
|
99595
|
+
)
|
|
99596
|
+
);
|
|
99597
|
+
return 1;
|
|
99598
|
+
}
|
|
99599
|
+
await runFeedbackPrompt(id, null);
|
|
99600
|
+
return 0;
|
|
99601
|
+
}
|
|
99602
|
+
|
|
98853
99603
|
// ../../packages/cli/src/index.ts
|
|
98854
99604
|
function buildProgram() {
|
|
98855
99605
|
const program2 = new Command();
|
|
@@ -99271,11 +100021,58 @@ Examples:
|
|
|
99271
100021
|
horus cloud sync --dry-run # preview what would upload
|
|
99272
100022
|
horus cloud sync # confirm, then upload
|
|
99273
100023
|
horus cloud sync --yes # upload without prompting`);
|
|
100024
|
+
program2.command("feedback [investigationId]").description("Leave quick impact feedback on an investigation (helps improve Horus)").option(
|
|
100025
|
+
"--resolved <verdict>",
|
|
100026
|
+
"did Horus point at the cause? yes | partly | no \u2014 non-interactive, for agents/scripts"
|
|
100027
|
+
).option(
|
|
100028
|
+
"--manual-estimate-min <minutes>",
|
|
100029
|
+
"estimated minutes this would have taken manually (optional, pair with --resolved)"
|
|
100030
|
+
).action(
|
|
100031
|
+
async (investigationId, opts) => {
|
|
100032
|
+
process.exitCode = await runFeedback(investigationId, opts);
|
|
100033
|
+
}
|
|
100034
|
+
);
|
|
100035
|
+
const telemetry = program2.command("telemetry").description("Manage anonymous usage telemetry that helps improve Horus");
|
|
100036
|
+
telemetry.command("status").description("Show the effective telemetry decision, saved preferences, and install ID").action(async () => {
|
|
100037
|
+
process.exitCode = await runTelemetryStatus();
|
|
100038
|
+
});
|
|
100039
|
+
telemetry.command("enable").description("Turn on anonymous usage metadata (Tier A)").action(async () => {
|
|
100040
|
+
process.exitCode = await runTelemetryEnable();
|
|
100041
|
+
});
|
|
100042
|
+
telemetry.command("disable").description("Turn off all telemetry (Tier A and Tier B)").action(async () => {
|
|
100043
|
+
process.exitCode = await runTelemetryDisable();
|
|
100044
|
+
});
|
|
100045
|
+
telemetry.command("enable-content").description("Opt in to sharing redacted investigation inputs/outputs (Tier B)").action(async () => {
|
|
100046
|
+
process.exitCode = await runTelemetryEnableContent();
|
|
100047
|
+
});
|
|
100048
|
+
telemetry.command("disable-content").description("Stop sharing inputs/outputs; keep usage metadata (Tier B off)").action(async () => {
|
|
100049
|
+
process.exitCode = await runTelemetryDisableContent();
|
|
100050
|
+
});
|
|
100051
|
+
telemetry.command("reset-id").description("Generate a new random install ID").action(async () => {
|
|
100052
|
+
process.exitCode = await runTelemetryResetId();
|
|
100053
|
+
});
|
|
100054
|
+
telemetry.command("delete").description("Delete local telemetry state (install ID + saved preferences)").action(async () => {
|
|
100055
|
+
process.exitCode = await runTelemetryDelete();
|
|
100056
|
+
});
|
|
100057
|
+
telemetry.addHelpText(
|
|
100058
|
+
"after",
|
|
100059
|
+
`
|
|
100060
|
+
Defaults: usage metadata ON (with this notice), content sharing OFF.
|
|
100061
|
+
Hard opt-out via env: HORUS_TELEMETRY=0 or DO_NOT_TRACK=1.
|
|
100062
|
+
|
|
100063
|
+
Examples:
|
|
100064
|
+
horus telemetry status
|
|
100065
|
+
horus telemetry disable
|
|
100066
|
+
horus telemetry enable-content`
|
|
100067
|
+
);
|
|
99274
100068
|
return program2;
|
|
99275
100069
|
}
|
|
99276
100070
|
async function run(argv = process.argv) {
|
|
100071
|
+
maybeShowFirstRunNotice(argv);
|
|
99277
100072
|
const program2 = buildProgram();
|
|
100073
|
+
installCommandTelemetry(program2);
|
|
99278
100074
|
await program2.parseAsync(argv);
|
|
100075
|
+
await flushTelemetry();
|
|
99279
100076
|
}
|
|
99280
100077
|
|
|
99281
100078
|
// src/index.ts
|