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.
- package/kifaru-pi.js +109 -22
- 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.
|
|
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
|
-
|
|
127744
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
132585
|
-
import
|
|
132586
|
-
import
|
|
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 ||
|
|
132591
|
-
return
|
|
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
|
|
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
|
|
132721
|
+
await fs15.mkdir(path23.dirname(file3), { recursive: true });
|
|
132635
132722
|
const tmp = `${file3}.tmp`;
|
|
132636
|
-
await
|
|
132637
|
-
await
|
|
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
|
|
132756
|
-
await
|
|
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.
|
|
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.
|
|
4
|
-
"gitHead": "
|
|
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",
|