kifaru 1.0.169 → 1.0.171

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.
Files changed (2) hide show
  1. package/kifaru-pi.js +109 -22
  2. package/package.json +2 -2
package/kifaru-pi.js CHANGED
@@ -16476,7 +16476,7 @@ var init_installation = __esm(async () => {
16476
16476
  });
16477
16477
  }
16478
16478
  Installation.upgrade = upgrade;
16479
- Installation.VERSION = "1.0.169";
16479
+ Installation.VERSION = "1.0.171";
16480
16480
  Installation.CHANNEL = typeof KIFARU_CHANNEL === "string" ? KIFARU_CHANNEL : "local";
16481
16481
  Installation.USER_AGENT = `kifaru/${Installation.CHANNEL}/${Installation.VERSION}`;
16482
16482
  async function latest() {
@@ -127710,6 +127710,63 @@ ${await capturePythonPrompt(ctx.cwd)}`
127710
127710
  };
127711
127711
  }
127712
127712
 
127713
+ // src/tui-logger.ts
127714
+ import fs14 from "fs";
127715
+ import os10 from "os";
127716
+ import path21 from "path";
127717
+ function kifaruDataDir() {
127718
+ if (process.env.KIFARU_XDG_DATA_HOME)
127719
+ return process.env.KIFARU_XDG_DATA_HOME;
127720
+ if (os10.platform() === "win32") {
127721
+ return path21.join(process.env.LOCALAPPDATA || path21.join(os10.homedir(), "AppData", "Local"), "kifaru", "data");
127722
+ }
127723
+ return path21.join(process.env.XDG_DATA_HOME || path21.join(os10.homedir(), ".local", "share"), "kifaru");
127724
+ }
127725
+ function resolveLogFilePath() {
127726
+ if (process.env.KIFARU_LOG_FILE)
127727
+ return path21.resolve(process.env.KIFARU_LOG_FILE);
127728
+ return path21.join(kifaruDataDir(), "log", "dev.log");
127729
+ }
127730
+ var initialized = false;
127731
+ var activeLogPath = "";
127732
+ function ensureLogDir() {
127733
+ if (!initialized) {
127734
+ activeLogPath = resolveLogFilePath();
127735
+ try {
127736
+ fs14.mkdirSync(path21.dirname(activeLogPath), { recursive: true });
127737
+ } catch {}
127738
+ initialized = true;
127739
+ }
127740
+ return activeLogPath;
127741
+ }
127742
+ function writeLog(level, category, message2, extra) {
127743
+ try {
127744
+ const file3 = ensureLogDir();
127745
+ const ts = new Date().toISOString();
127746
+ const extraStr = extra !== undefined ? ` ${typeof extra === "object" ? JSON.stringify(extra) : String(extra)}` : "";
127747
+ const line = `${ts} [${level}] [${category}] ${message2}${extraStr}
127748
+ `;
127749
+ fs14.appendFileSync(file3, line, "utf8");
127750
+ } catch {}
127751
+ }
127752
+ var tuiLog = {
127753
+ get filePath() {
127754
+ return ensureLogDir();
127755
+ },
127756
+ debug(category, message2, extra) {
127757
+ writeLog("DEBUG", category, message2, extra);
127758
+ },
127759
+ info(category, message2, extra) {
127760
+ writeLog("INFO", category, message2, extra);
127761
+ },
127762
+ warn(category, message2, extra) {
127763
+ writeLog("WARN", category, message2, extra);
127764
+ },
127765
+ error(category, message2, extra) {
127766
+ writeLog("ERROR", category, message2, extra);
127767
+ }
127768
+ };
127769
+
127713
127770
  // src/pi-platform-client.ts
127714
127771
  function trimSlash(value) {
127715
127772
  return value.replace(/\/+$/, "");
@@ -127738,10 +127795,15 @@ function createPiPlatformClient(options2 = {}) {
127738
127795
  const env2 = options2.env ?? process.env;
127739
127796
  const fetchImpl = options2.fetchImpl ?? fetch;
127740
127797
  const config4 = resolveConfig(env2);
127741
- if (!config4)
127798
+ if (!config4) {
127799
+ tuiLog.info("platform", "Platform ingestion disabled: KIFARU_PLATFORM_INTERNAL_TOKEN is not set. Runs and findings will remain host-local.");
127742
127800
  return disabledClient;
127743
- async function request(path21, method, body) {
127744
- const response = await fetchImpl(`${config4.baseURL}/api/v1/${path21}`, {
127801
+ }
127802
+ tuiLog.info("platform", `Platform ingestion enabled -> ${config4.baseURL}`);
127803
+ async function request(path22, method, body) {
127804
+ const url3 = `${config4.baseURL}/api/v1/${path22}`;
127805
+ tuiLog.debug("platform:http", `-> ${method} ${url3}`);
127806
+ const response = await fetchImpl(url3, {
127745
127807
  method,
127746
127808
  headers: {
127747
127809
  Authorization: `Bearer ${config4.token}`,
@@ -127750,9 +127812,11 @@ function createPiPlatformClient(options2 = {}) {
127750
127812
  signal: AbortSignal.timeout(1e4),
127751
127813
  ...body === undefined ? {} : { body: JSON.stringify(body) }
127752
127814
  });
127815
+ tuiLog.debug("platform:http", `<- ${response.status} ${method} ${url3}`);
127753
127816
  if (!response.ok) {
127754
127817
  const detail = await response.text().catch(() => "");
127755
127818
  const code = response.status === 404 && /was not found/i.test(detail) ? "KIFARU_RUN_MISSING" : `KIFARU_HTTP_${response.status}`;
127819
+ tuiLog.error("platform:http", `Request failed (${response.status}): ${detail || response.statusText}`);
127756
127820
  throw Object.assign(new Error(`Kifaru platform request failed (${response.status}): ${detail || response.statusText}`), { code });
127757
127821
  }
127758
127822
  return response;
@@ -127765,6 +127829,7 @@ function createPiPlatformClient(options2 = {}) {
127765
127829
  const result = await response.json();
127766
127830
  if (!result.result?.recorded)
127767
127831
  throw Object.assign(new Error("Platform operation was not linked to a run"), { code: "KIFARU_RUN_UNLINKED" });
127832
+ tuiLog.debug("platform:operation", `Recorded operation ${input.toolCallID} (${input.tool}) -> ${input.status}`);
127768
127833
  },
127769
127834
  async createRun(input) {
127770
127835
  const response = await request("runs", "POST", {
@@ -127777,17 +127842,23 @@ function createPiPlatformClient(options2 = {}) {
127777
127842
  ...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}
127778
127843
  });
127779
127844
  const parsed = await response.json();
127780
- return parsed.run?.id ?? null;
127845
+ const runID = parsed.run?.id ?? null;
127846
+ tuiLog.info("platform:run", `Created platform run: ${runID}`, { title: input.title, target: input.targetID });
127847
+ return runID;
127781
127848
  },
127782
127849
  async ingestFindings(runID, findings) {
127783
127850
  if (findings.length === 0)
127784
127851
  return { created: 0, updated: 0 };
127785
127852
  const response = await request(`runs/${encodeURIComponent(runID)}/findings`, "POST", { findings });
127786
127853
  const parsed = await response.json();
127787
- return { created: parsed.result?.created ?? 0, updated: parsed.result?.updated ?? 0 };
127854
+ const created = parsed.result?.created ?? 0;
127855
+ const updated = parsed.result?.updated ?? 0;
127856
+ tuiLog.info("platform:finding", `Ingested ${findings.length} finding(s) into run ${runID} (created=${created}, updated=${updated})`);
127857
+ return { created, updated };
127788
127858
  },
127789
127859
  async transitionRun(runID, status, details = {}) {
127790
127860
  await request(`runs/${encodeURIComponent(runID)}/status`, "POST", { status, ...details });
127861
+ tuiLog.info("platform:run", `Transitioned run ${runID} to ${status}`, details);
127791
127862
  }
127792
127863
  };
127793
127864
  }
@@ -132179,6 +132250,7 @@ ${p2.statutoryObligations.map((o2) => `\u2022 ${o2}`).join(`
132179
132250
  remediation: p2.remediation?.slice(0, 20000)
132180
132251
  };
132181
132252
  if (!client.enabled) {
132253
+ tuiLog.warn("report_finding", `Finding recorded locally ONLY (Platform ingestion disabled): "${finding.title}"`);
132182
132254
  return {
132183
132255
  content: [
132184
132256
  {
@@ -132195,6 +132267,7 @@ ${p2.statutoryObligations.map((o2) => `\u2022 ${o2}`).join(`
132195
132267
  }
132196
132268
  try {
132197
132269
  const result = await withPiPlatformRun(client, ctx, () => client.ingestFindings(ctx.runID, [finding]));
132270
+ tuiLog.info("report_finding", `Finding recorded to platform: [${finding.severity.toUpperCase()}] "${finding.title}" -> run ${ctx.runID}`, { file: finding.file, category, cvssScore: finding.cvssScore });
132198
132271
  ctx.findingCount += 1;
132199
132272
  if (!ctx.worstSeverity || SEVERITY_RANK[finding.severity] > SEVERITY_RANK[ctx.worstSeverity]) {
132200
132273
  ctx.worstSeverity = finding.severity;
@@ -132247,7 +132320,7 @@ function createReportFindingExtension(deps2) {
132247
132320
 
132248
132321
  // src/pi-dossier-sync.ts
132249
132322
  import { readFile } from "fs/promises";
132250
- import path21 from "path";
132323
+ import path22 from "path";
132251
132324
  var severityWords = [
132252
132325
  [/critical|account takeover|authentication bypass|sql injection|arbitrary query/i, "critical"],
132253
132326
  [/high|privilege escalation|ssrf|credential|idor|authorization|sensitive/i, "high"],
@@ -132307,7 +132380,7 @@ async function syncPiDossier(client, context3, cwd, read = readFile) {
132307
132380
  return { ...NOTHING, skipped: "unparseable-target" };
132308
132381
  let raw;
132309
132382
  try {
132310
- raw = await read(path21.join(cwd, ".kifaru", "dossiers", `${name21}.json`), "utf8");
132383
+ raw = await read(path22.join(cwd, ".kifaru", "dossiers", `${name21}.json`), "utf8");
132311
132384
  } catch (error92) {
132312
132385
  if (error92.code === "ENOENT")
132313
132386
  return { ...NOTHING, skipped: "no-dossier-file" };
@@ -132366,11 +132439,23 @@ function createKifaruExtension(state2, cwd, deps2) {
132366
132439
  factory(pi) {
132367
132440
  pi.on("session_start", (_event, ctx) => updateStatus(ctx));
132368
132441
  const toolStarts = new Map;
132442
+ let lastIngestionWarning = 0;
132443
+ const warnIngestionError = (ctx, error92, action2) => {
132444
+ const now2 = Date.now();
132445
+ tuiLog.warn("platform", `Dashboard ${action2} failed: ${String(error92)}`);
132446
+ if (now2 - lastIngestionWarning > 30000) {
132447
+ lastIngestionWarning = now2;
132448
+ const isConnectionError = /unable to connect|fetch failed|econnrefused/i.test(String(error92));
132449
+ const message2 = isConnectionError ? `Dashboard gateway unreachable at ${deps2.client.baseURL ?? "http://127.0.0.1:38413"}. Start it with "bun run platform:demo" (or tunnel to remote VM) to record activity.` : `Dashboard activity ingestion failed: ${String(error92)}`;
132450
+ ctx.ui.notify(message2, "warning");
132451
+ }
132452
+ };
132369
132453
  pi.on("agent_start", async (_event, ctx) => {
132454
+ tuiLog.info("agent", `Agent turn starting: workflow=${state2.agentKey}, target=${state2.target}`);
132370
132455
  try {
132371
132456
  await ensurePiPlatformRun(deps2.client, deps2.runContext);
132372
132457
  } catch (error92) {
132373
- ctx.ui.notify(`Dashboard ingestion failed: ${String(error92)}`, "warning");
132458
+ warnIngestionError(ctx, error92, "run creation");
132374
132459
  }
132375
132460
  });
132376
132461
  pi.on("tool_execution_start", (event) => {
@@ -132381,6 +132466,7 @@ function createKifaruExtension(state2, cwd, deps2) {
132381
132466
  const startedAt = start?.startedAt ?? Date.now();
132382
132467
  const args = start?.args;
132383
132468
  toolStarts.delete(event.toolCallId);
132469
+ tuiLog.info("agent:tool", `Tool call finished: ${event.toolName} (status: ${event.isError ? "error" : "ok"}, duration: ${Date.now() - startedAt}ms)`);
132384
132470
  if (!deps2.client.enabled || !deps2.client.recordOperation)
132385
132471
  return;
132386
132472
  let outputPreview;
@@ -132409,10 +132495,11 @@ function createKifaruExtension(state2, cwd, deps2) {
132409
132495
  completedAt: Date.now()
132410
132496
  }));
132411
132497
  } catch (error92) {
132412
- ctx.ui.notify(`Dashboard activity ingestion failed: ${String(error92)}`, "warning");
132498
+ warnIngestionError(ctx, error92, "operation recording");
132413
132499
  }
132414
132500
  });
132415
132501
  pi.on("agent_settled", async () => {
132502
+ tuiLog.info("agent", `Agent turn settled: findings=${deps2.runContext.findingCount}, worstSeverity=${deps2.runContext.worstSeverity ?? "none"}`);
132416
132503
  if (!deps2.client.enabled)
132417
132504
  return;
132418
132505
  await completePiPlatformRun(deps2.client, deps2.runContext, {
@@ -132581,14 +132668,14 @@ async function runKifaruTui(options2) {
132581
132668
  }
132582
132669
 
132583
132670
  // src/session-client.ts
132584
- import fs14 from "fs/promises";
132585
- import os10 from "os";
132586
- import path22 from "path";
132671
+ import fs15 from "fs/promises";
132672
+ import os11 from "os";
132673
+ import path23 from "path";
132587
132674
  function sessionFile() {
132588
132675
  if (process.env.KIFARU_SESSION_FILE)
132589
132676
  return process.env.KIFARU_SESSION_FILE;
132590
- const xdg = process.env.XDG_DATA_HOME || path22.join(os10.homedir(), ".local", "share");
132591
- return path22.join(xdg, "kifaru", "data", "auth", "session.json");
132677
+ const xdg = process.env.XDG_DATA_HOME || path23.join(os11.homedir(), ".local", "share");
132678
+ return path23.join(xdg, "kifaru", "data", "auth", "session.json");
132592
132679
  }
132593
132680
  async function resolveGatewayOrigin(explicitOrigin) {
132594
132681
  if (explicitOrigin && explicitOrigin.trim()) {
@@ -132610,7 +132697,7 @@ async function resolveGatewayOrigin(explicitOrigin) {
132610
132697
  return "https://kifaru.bastco.org";
132611
132698
  }
132612
132699
  async function readSession() {
132613
- const raw = await fs14.readFile(sessionFile(), "utf8").catch(() => null);
132700
+ const raw = await fs15.readFile(sessionFile(), "utf8").catch(() => null);
132614
132701
  if (!raw)
132615
132702
  return null;
132616
132703
  try {
@@ -132631,10 +132718,10 @@ async function readSession() {
132631
132718
  }
132632
132719
  async function writeSession(session) {
132633
132720
  const file3 = sessionFile();
132634
- await fs14.mkdir(path22.dirname(file3), { recursive: true });
132721
+ await fs15.mkdir(path23.dirname(file3), { recursive: true });
132635
132722
  const tmp = `${file3}.tmp`;
132636
- await fs14.writeFile(tmp, JSON.stringify(session, null, 2), "utf8");
132637
- await fs14.rename(tmp, file3);
132723
+ await fs15.writeFile(tmp, JSON.stringify(session, null, 2), "utf8");
132724
+ await fs15.rename(tmp, file3);
132638
132725
  }
132639
132726
  function mask(token) {
132640
132727
  return token.length > 12 ? `${token.slice(0, 6)}\u2026${token.slice(-4)}` : "\u2026";
@@ -132752,8 +132839,8 @@ async function executeSessionLogout() {
132752
132839
  signal: AbortSignal.timeout(3000)
132753
132840
  }).catch(() => null);
132754
132841
  } catch {}
132755
- await fs14.rm(sessionFile(), { force: true }).catch(() => null);
132756
- await fs14.rm(`${sessionFile()}.tmp`, { force: true }).catch(() => null);
132842
+ await fs15.rm(sessionFile(), { force: true }).catch(() => null);
132843
+ await fs15.rm(`${sessionFile()}.tmp`, { force: true }).catch(() => null);
132757
132844
  }
132758
132845
  return { ok: true, text: "\u2713 Successfully logged out. Session removed." };
132759
132846
  }
@@ -132781,7 +132868,7 @@ Interactive TUI Commands:
132781
132868
  async function handleCli() {
132782
132869
  const args = process.argv.slice(2);
132783
132870
  if (args.includes("--version") || args.includes("-v")) {
132784
- console.log("1.0.169");
132871
+ console.log("1.0.171");
132785
132872
  process.exit(0);
132786
132873
  }
132787
132874
  if (args.includes("--help") || args.includes("-h")) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kifaru",
3
- "version": "1.0.169",
4
- "gitHead": "f8e5b19906a55d628aa38cbab2cc1febae10cc3a",
3
+ "version": "1.0.171",
4
+ "gitHead": "cdd22c7a2e77ae0a717564abee14b69a0e607f06",
5
5
  "description": "Kifaru security agent on the Pi runtime",
6
6
  "type": "module",
7
7
  "license": "MIT",