farai 0.3.2 → 0.3.3
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/cli/index.js
CHANGED
|
@@ -5398,7 +5398,7 @@ function parseManifest(value) {
|
|
|
5398
5398
|
if (!value || typeof value !== "object")
|
|
5399
5399
|
throw new Error("invalid farai tool manifest");
|
|
5400
5400
|
const candidate = value;
|
|
5401
|
-
if (
|
|
5401
|
+
if (!Array.isArray(candidate.aptPackages) || !candidate.pinnedTools || typeof candidate.pinnedTools !== "object" || !candidate.pinnedAssets || typeof candidate.pinnedAssets !== "object" || !candidate.workflows || typeof candidate.workflows !== "object") {
|
|
5402
5402
|
throw new Error("invalid farai tool manifest");
|
|
5403
5403
|
}
|
|
5404
5404
|
const aptPackages = candidate.aptPackages.filter((item) => typeof item === "string" && Boolean(item));
|
|
@@ -5445,7 +5445,6 @@ function parseManifest(value) {
|
|
|
5445
5445
|
if (!Object.keys(pinnedAssets).length)
|
|
5446
5446
|
throw new Error("empty pinned asset manifest");
|
|
5447
5447
|
return {
|
|
5448
|
-
contract: candidate.contract,
|
|
5449
5448
|
aptPackages,
|
|
5450
5449
|
pinnedTools,
|
|
5451
5450
|
pinnedAssets,
|
|
@@ -5455,11 +5454,15 @@ function parseManifest(value) {
|
|
|
5455
5454
|
function isSha256(value) {
|
|
5456
5455
|
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
5457
5456
|
}
|
|
5458
|
-
var KALI_TOOL_MANIFEST_PATH, KALI_TOOL_MANIFEST;
|
|
5457
|
+
var KALI_TOOL_MANIFEST_PATH, KALI_IMAGE_CONTRACT_PATH, KALI_TOOL_MANIFEST, KALI_IMAGE_CONTRACT;
|
|
5459
5458
|
var init_kali_tool_manifest = __esm(() => {
|
|
5460
5459
|
init_file_read();
|
|
5461
5460
|
KALI_TOOL_MANIFEST_PATH = join4(import.meta.dir, "..", "..", "docker", "kali", "farai-tool-manifest.json");
|
|
5461
|
+
KALI_IMAGE_CONTRACT_PATH = join4(import.meta.dir, "..", "..", "docker", "kali", "farai-image-contract");
|
|
5462
5462
|
KALI_TOOL_MANIFEST = parseManifest(JSON.parse(readBoundedFileTextSync(KALI_TOOL_MANIFEST_PATH, 1024 * 1024, "kali tool manifest")));
|
|
5463
|
+
KALI_IMAGE_CONTRACT = readBoundedFileTextSync(KALI_IMAGE_CONTRACT_PATH, 1024, "kali image contract").trim();
|
|
5464
|
+
if (!KALI_IMAGE_CONTRACT)
|
|
5465
|
+
throw new Error("empty kali image contract");
|
|
5463
5466
|
});
|
|
5464
5467
|
|
|
5465
5468
|
// src/agent-tools/mcp-builtins.ts
|
|
@@ -7284,20 +7287,59 @@ class KaliContainerBackend {
|
|
|
7284
7287
|
pullImageCommand() {
|
|
7285
7288
|
return ["docker", "pull", this.image];
|
|
7286
7289
|
}
|
|
7287
|
-
async
|
|
7288
|
-
const
|
|
7289
|
-
if (
|
|
7290
|
+
async remoteImageDigest() {
|
|
7291
|
+
const inspect = await this.processRunner("docker", ["buildx", "imagetools", "inspect", this.image, "--format", "{{.Manifest.Digest}}"]);
|
|
7292
|
+
if (inspect.exitCode !== 0)
|
|
7293
|
+
return;
|
|
7294
|
+
return digestOf(inspect.stdout.trim());
|
|
7295
|
+
}
|
|
7296
|
+
async checkForImageUpdate() {
|
|
7297
|
+
const local = await this.resolveImage();
|
|
7298
|
+
if (!local.exists)
|
|
7290
7299
|
return {
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7300
|
+
exists: false,
|
|
7301
|
+
upToDate: false,
|
|
7302
|
+
...local.error ? {
|
|
7303
|
+
error: local.error
|
|
7304
|
+
} : {}
|
|
7296
7305
|
};
|
|
7306
|
+
const remote = await this.remoteImageDigest();
|
|
7307
|
+
if (!remote || !local.repoDigest)
|
|
7308
|
+
return {
|
|
7309
|
+
exists: true,
|
|
7310
|
+
upToDate: true
|
|
7311
|
+
};
|
|
7312
|
+
return {
|
|
7313
|
+
exists: true,
|
|
7314
|
+
upToDate: local.repoDigest === remote
|
|
7315
|
+
};
|
|
7316
|
+
}
|
|
7317
|
+
async ensureImage() {
|
|
7318
|
+
const local = await this.resolveImage();
|
|
7319
|
+
if (local.exists) {
|
|
7320
|
+
const remote = await this.remoteImageDigest();
|
|
7321
|
+
if (!remote || local.repoDigest !== undefined && local.repoDigest === remote) {
|
|
7322
|
+
return {
|
|
7323
|
+
exitCode: 0,
|
|
7324
|
+
stdout: "image ready",
|
|
7325
|
+
stderr: "",
|
|
7326
|
+
durationMs: 0,
|
|
7327
|
+
timedOut: false
|
|
7328
|
+
};
|
|
7329
|
+
}
|
|
7297
7330
|
}
|
|
7298
7331
|
const started = Date.now();
|
|
7299
7332
|
const pulled = await this.pullRunner("docker", ["pull", this.image]);
|
|
7300
7333
|
if (pulled.exitCode !== 0) {
|
|
7334
|
+
if (local.exists) {
|
|
7335
|
+
return {
|
|
7336
|
+
exitCode: 0,
|
|
7337
|
+
stdout: pulled.stdout || "using local kali image",
|
|
7338
|
+
stderr: "",
|
|
7339
|
+
durationMs: Date.now() - started,
|
|
7340
|
+
timedOut: false
|
|
7341
|
+
};
|
|
7342
|
+
}
|
|
7301
7343
|
return {
|
|
7302
7344
|
...pulled,
|
|
7303
7345
|
stderr: dockerFailure(pulled, `could not pull kali image ${this.image}; check network access to ${KALI_IMAGE_REPO}`),
|
|
@@ -7315,15 +7357,6 @@ class KaliContainerBackend {
|
|
|
7315
7357
|
timedOut: false
|
|
7316
7358
|
};
|
|
7317
7359
|
}
|
|
7318
|
-
if (resolved.contract !== KALI_IMAGE_CONTRACT) {
|
|
7319
|
-
return {
|
|
7320
|
-
exitCode: 1,
|
|
7321
|
-
stdout: "",
|
|
7322
|
-
stderr: `pulled kali image ${this.image} does not satisfy the farai capability contract (${resolved.contract ?? "missing"})`,
|
|
7323
|
-
durationMs: Date.now() - started,
|
|
7324
|
-
timedOut: false
|
|
7325
|
-
};
|
|
7326
|
-
}
|
|
7327
7360
|
return {
|
|
7328
7361
|
exitCode: 0,
|
|
7329
7362
|
stdout: pulled.stdout || "image pulled",
|
|
@@ -7922,6 +7955,7 @@ function parseImageInspect(raw) {
|
|
|
7922
7955
|
exists: true
|
|
7923
7956
|
};
|
|
7924
7957
|
const contract = image.Config?.Labels?.[KALI_IMAGE_CONTRACT_LABEL];
|
|
7958
|
+
const repoDigest = digestOf((image.RepoDigests ?? []).find((entry) => entry.includes("@sha256:")));
|
|
7925
7959
|
return {
|
|
7926
7960
|
exists: true,
|
|
7927
7961
|
...image.Id ? {
|
|
@@ -7929,6 +7963,9 @@ function parseImageInspect(raw) {
|
|
|
7929
7963
|
} : {},
|
|
7930
7964
|
...contract ? {
|
|
7931
7965
|
contract
|
|
7966
|
+
} : {},
|
|
7967
|
+
...repoDigest ? {
|
|
7968
|
+
repoDigest
|
|
7932
7969
|
} : {}
|
|
7933
7970
|
};
|
|
7934
7971
|
} catch {
|
|
@@ -7937,6 +7974,12 @@ function parseImageInspect(raw) {
|
|
|
7937
7974
|
};
|
|
7938
7975
|
}
|
|
7939
7976
|
}
|
|
7977
|
+
function digestOf(reference) {
|
|
7978
|
+
if (!reference)
|
|
7979
|
+
return;
|
|
7980
|
+
const match = reference.match(/sha256:[a-f0-9]{64}/i);
|
|
7981
|
+
return match ? match[0].toLowerCase() : undefined;
|
|
7982
|
+
}
|
|
7940
7983
|
function dockerFailure(result, fallback) {
|
|
7941
7984
|
const detail = `${result.stderr}
|
|
7942
7985
|
${result.stdout}`.trim().replace(/\s+/g, " ");
|
|
@@ -7960,7 +8003,7 @@ function containerDoesNotExist2(result) {
|
|
|
7960
8003
|
return /no such (container|object)/i.test(`${result.stdout}
|
|
7961
8004
|
${result.stderr}`);
|
|
7962
8005
|
}
|
|
7963
|
-
var CONTAINER_WORKSPACE_MOUNT = "/workspace", CONTAINER_WORKTREES_MOUNT = "/worktrees", kaliSessions, kaliPtySessions, containerStartLocks, globalContainerStartChain, CONTAINER_EXEC_MARKER_DIR = "/tmp/farai-exec", CONTAINER_EXEC_WRAPPER, CONTAINER_EXEC_KILLER, CONTAINER_PREFIX,
|
|
8006
|
+
var CONTAINER_WORKSPACE_MOUNT = "/workspace", CONTAINER_WORKTREES_MOUNT = "/worktrees", kaliSessions, kaliPtySessions, containerStartLocks, globalContainerStartChain, CONTAINER_EXEC_MARKER_DIR = "/tmp/farai-exec", CONTAINER_EXEC_WRAPPER, CONTAINER_EXEC_KILLER, CONTAINER_PREFIX, KALI_IMAGE_REPO = "ghcr.io/pajarori/farai-kali", KALI_IMAGE_TAG = "latest", DEFAULT_KALI_IMAGE, KALI_IMAGE_CONTRACT_LABEL = "org.farai.kali.contract", KALI_IMAGE_PULL_TIMEOUT_MS;
|
|
7964
8007
|
var init_kali = __esm(() => {
|
|
7965
8008
|
init_spawn_session();
|
|
7966
8009
|
init_pty_session();
|
|
@@ -7978,8 +8021,7 @@ var init_kali = __esm(() => {
|
|
|
7978
8021
|
CONTAINER_EXEC_KILLER = ["import os, signal, sys, time", "path = sys.argv[1]", "root = None", "marker_deadline = time.monotonic() + 0.25", "while root is None and time.monotonic() < marker_deadline:", " try:", " with open(path, encoding='ascii') as handle:", " root = int(handle.read().strip())", " except (FileNotFoundError, OSError, ValueError):", " time.sleep(0.01)", "if root is None:", " raise SystemExit(0)", "def direct_children(pid):", " try:", " with open(f'/proc/{pid}/task/{pid}/children', encoding='ascii') as handle:", " return [int(value) for value in handle.read().split()]", " except (FileNotFoundError, OSError, ValueError):", " return []", "def descendants(pid):", " found = []", " pending = direct_children(pid)", " seen = set()", " while pending:", " child = pending.pop()", " if child in seen:", " continue", " seen.add(child)", " found.append(child)", " pending.extend(direct_children(child))", " return found", "def alive(pid):", " try:", " os.kill(pid, 0)", " return True", " except ProcessLookupError:", " return False", " except PermissionError:", " return True", "targets = descendants(root)", "for pid in [*reversed(targets), root]:", " try:", " os.kill(pid, signal.SIGTERM)", " except (ProcessLookupError, PermissionError):", " pass", "deadline = time.monotonic() + 1.0", "while time.monotonic() < deadline and any(alive(pid) for pid in [root, *targets]):", " time.sleep(0.025)", "targets = list(dict.fromkeys([*targets, *descendants(root)]))", "for pid in [*reversed(targets), root]:", " try:", " os.kill(pid, signal.SIGKILL)", " except (ProcessLookupError, PermissionError):", " pass", "try:", " os.unlink(path)", "except FileNotFoundError:", " pass"].join(`
|
|
7979
8022
|
`);
|
|
7980
8023
|
CONTAINER_PREFIX = FARAI_CONTAINER_NAME_PREFIX;
|
|
7981
|
-
|
|
7982
|
-
DEFAULT_KALI_IMAGE = `${KALI_IMAGE_REPO}:${KALI_IMAGE_CONTRACT}`;
|
|
8024
|
+
DEFAULT_KALI_IMAGE = `${KALI_IMAGE_REPO}:${KALI_IMAGE_TAG}`;
|
|
7983
8025
|
KALI_IMAGE_PULL_TIMEOUT_MS = 30 * 60 * 1000;
|
|
7984
8026
|
});
|
|
7985
8027
|
|
|
@@ -8732,8 +8774,8 @@ var init_process_output = __esm(() => {
|
|
|
8732
8774
|
|
|
8733
8775
|
// src/version.ts
|
|
8734
8776
|
function resolveFaraiVersion() {
|
|
8735
|
-
if ("0.3.
|
|
8736
|
-
return "0.3.
|
|
8777
|
+
if ("0.3.3")
|
|
8778
|
+
return "0.3.3";
|
|
8737
8779
|
try {
|
|
8738
8780
|
const parsed = JSON.parse(readBoundedFileTextSync(new URL("../package.json", import.meta.url), 1024 * 1024, "package metadata"));
|
|
8739
8781
|
if (typeof parsed.version === "string" && parsed.version)
|
|
@@ -43757,17 +43799,17 @@ async function runStartupContainerPreflight(workspace) {
|
|
|
43757
43799
|
const backend2 = new KaliContainerBackend({
|
|
43758
43800
|
workspace
|
|
43759
43801
|
});
|
|
43760
|
-
const
|
|
43802
|
+
const update = await backend2.checkForImageUpdate().catch(() => {
|
|
43761
43803
|
return;
|
|
43762
43804
|
});
|
|
43763
|
-
if (!
|
|
43805
|
+
if (!update || update.error)
|
|
43764
43806
|
return "continue";
|
|
43765
|
-
if (
|
|
43807
|
+
if (update.exists && update.upToDate)
|
|
43766
43808
|
return "continue";
|
|
43767
43809
|
const config = loadConfig(workspace);
|
|
43768
43810
|
if (config.updates?.prompt === false || !process.stdin.isTTY || !process.stdout.isTTY)
|
|
43769
43811
|
return "continue";
|
|
43770
|
-
const answer = await promptForImagePull(
|
|
43812
|
+
const answer = await promptForImagePull(update.exists);
|
|
43771
43813
|
if (answer === "cancelled")
|
|
43772
43814
|
return "cancelled";
|
|
43773
43815
|
if (answer === "later")
|
|
@@ -43779,11 +43821,11 @@ async function runStartupContainerPreflight(workspace) {
|
|
|
43779
43821
|
}
|
|
43780
43822
|
return "continue";
|
|
43781
43823
|
}
|
|
43782
|
-
async function promptForImagePull(
|
|
43824
|
+
async function promptForImagePull(exists) {
|
|
43783
43825
|
console.log("");
|
|
43784
43826
|
console.log(FARAI_BANNER);
|
|
43785
43827
|
console.log("");
|
|
43786
|
-
console.log(exists ?
|
|
43828
|
+
console.log(exists ? "a newer kali container image is available" : "kali container image is not installed");
|
|
43787
43829
|
const interfaceHandle = createInterface2({
|
|
43788
43830
|
input: process.stdin,
|
|
43789
43831
|
output: process.stdout
|
|
@@ -73872,11 +73914,12 @@ async function doctor() {
|
|
|
73872
73914
|
const backend2 = new KaliContainerBackend({
|
|
73873
73915
|
workspace: process.cwd()
|
|
73874
73916
|
});
|
|
73875
|
-
const
|
|
73876
|
-
|
|
73877
|
-
|
|
73878
|
-
|
|
73879
|
-
console.log(`kali
|
|
73917
|
+
const update = await backend2.checkForImageUpdate().catch(() => {
|
|
73918
|
+
return;
|
|
73919
|
+
});
|
|
73920
|
+
const status = !update ? "unknown (docker unavailable)" : !update.exists ? "not installed (pulled on first run)" : update.upToDate ? "up to date" : "update available (pulled on first run)";
|
|
73921
|
+
console.log(`kali image: ${backend2.image}`);
|
|
73922
|
+
console.log(`kali image status: ${status}`);
|
|
73880
73923
|
const {
|
|
73881
73924
|
contentStatus: contentStatus2
|
|
73882
73925
|
} = await Promise.resolve().then(() => (init_updater(), exports_updater));
|
|
@@ -74260,5 +74303,5 @@ Examples:
|
|
|
74260
74303
|
`);
|
|
74261
74304
|
}
|
|
74262
74305
|
|
|
74263
|
-
//# debugId=
|
|
74306
|
+
//# debugId=11DF5F38873B6FC764756E2164756E21
|
|
74264
74307
|
//# sourceMappingURL=index.js.map
|