Download lib/runtime.mjs from teddyoas/Asnip: direct link, hf CLI and curl.
- Browser
- Download file 9.59 kB
-
https://huggingface.co/teddyoas/Asnip/resolve/main/lib/runtime.mjs
- Command line
-
hf download hf://teddyoas/Asnip/lib/runtime.mjs
-
curl -L -o runtime.mjs https://huggingface.co/teddyoas/Asnip/resolve/main/lib/runtime.mjs
9.59 kB
| // Phantom — shared runtime state | |
| // Mutable exports set by phantom.mjs, read by lib modules. | |
| // ESM live bindings ensure imports see the latest values. | |
| import { execFileSync } from "child_process"; | |
| import { resolve } from "path"; | |
| // Lazy import for evolve — avoid circular dep at module load time | |
| let _evolve = null; | |
| async function getEvolve() { | |
| if (!_evolve) _evolve = await import("./evolve.mjs"); | |
| return _evolve; | |
| } | |
| export const __r = { | |
| llmInstance: null, | |
| _config: {}, | |
| setProvider: null, | |
| PROVIDERS: null, | |
| PHANTOM_LLM_PROVIDER: "openai", | |
| ENV: null, | |
| }; | |
| // ── Async shell exec helper ────────────────────────────── | |
| // Replaces execSync throughout tools.mjs so the event loop stays | |
| // responsive during long-running commands (stdin keeps working). | |
| export function execAsync(cmd, opts = {}) { | |
| const timeout = opts.timeout ?? 30000; | |
| const maxBuffer = opts.maxBuffer ?? 1024 * 1024; | |
| return new Promise((resolve, reject) => { | |
| // Dynamic import to avoid top-level require | |
| import("child_process").then(({ exec }) => { | |
| exec(cmd, { encoding: "utf-8", timeout, maxBuffer }, (err, stdout, stderr) => { | |
| if (err) { | |
| err.stderr = (stderr || "").substring(0, 500); | |
| return reject(err); | |
| } | |
| resolve(stdout); | |
| }); | |
| }).catch(reject); | |
| }); | |
| } | |
| // ── External tool helper ───────────────────────────────── | |
| // DRY wrapper for all external-binary tools. | |
| // Handles dynamic import caching, which-check, error formatting. | |
| // Returns trimmed stdout lines array. Throws on error. | |
| const _cp = /* lazily cached */ null; | |
| function _ensureChildProcess() { | |
| // execFileSync is already statically imported; this is a no-op | |
| // but kept for symmetry with tools that imported dynamically | |
| return { execFileSync }; | |
| } | |
| /** | |
| * Run an external binary and return its trimmed stdout lines. | |
| * | |
| * @param {string} tool Binary name (e.g. "nmap", "subfinder") | |
| * @param {string[]} args CLI arguments | |
| * @param {object} [opts] | |
| * @param {number} [opts.timeout=60000] | |
| * @param {number} [opts.maxBuffer=1024*1024] | |
| * @param {string} [opts.input] stdin content | |
| * @param {string} [opts.installGuide] shown if binary missing | |
| * @returns {string[]} non-empty stdout lines | |
| */ | |
| export function runExternal(tool, args, opts = {}) { | |
| const { execFileSync } = _ensureChildProcess(); | |
| const timeout = opts.timeout ?? 60000; | |
| const maxBuffer = opts.maxBuffer ?? 1024 * 1024; | |
| const input = opts.input; | |
| const guide = opts.installGuide || `go install ...${tool}@latest`; | |
| // which check — prefer cached env.tools, fallback to which | |
| const cached = __r.ENV?.tools?.[tool]; | |
| if (cached === false) { | |
| throw new Error(`[${tool}] NOT INSTALLED — ${guide}`); | |
| } | |
| if (cached !== true) { | |
| try { execFileSync("which", [tool], { encoding: "utf-8", timeout: 5000 }); } | |
| catch { | |
| throw new Error(`[${tool}] NOT INSTALLED — ${guide}`); | |
| } | |
| } | |
| const execOpts = { encoding: "utf-8", timeout, maxBuffer }; | |
| if (input !== undefined) execOpts.input = input; | |
| const out = execFileSync(tool, args, execOpts); | |
| return out.trim().split("\n").filter(Boolean); | |
| } | |
| /** | |
| * Format external tool output for display. | |
| * @param {string} toolName | |
| * @param {string} target | |
| * @param {string[]} lines | |
| * @param {number} [maxLines=50] | |
| * @returns {string} formatted output | |
| */ | |
| export function formatExternal(toolName, target, lines, maxLines = 50) { | |
| if (!lines.length) return `[${toolName}] No results for ${target}`; | |
| const result = [`🔎 ${toolName}: ${target}`, `Results: ${lines.length}`]; | |
| result.push(...lines.slice(0, maxLines).map(l => ` ${l}`)); | |
| if (lines.length > maxLines) result.push(` ... and ${lines.length - maxLines} more`); | |
| return result.join("\n"); | |
| } | |
| // ── Tool execution wrapper ─────────────────────────────── | |
| // Handles JSON output, tool chaining/piping, error wrapping. | |
| /** | |
| * Run a tool from the hackerTools registry with optional flags. | |
| * | |
| * @param {object} tools - hackerTools object | |
| * @param {string} toolName | |
| * @param {string} input | |
| * @param {object} [opts] | |
| * @param {boolean} [opts.json=false] Return JSON string instead of formatted text | |
| * @returns {Promise<string>} Formatted text or JSON string | |
| */ | |
| export async function runTool(tools, toolName, input, opts = {}) { | |
| const fn = tools[toolName]; | |
| if (!fn) { | |
| const msg = `[${toolName}] Unknown tool. Use --list to see available tools.`; | |
| return opts.json ? JSON.stringify({ ok: false, error: msg, tool: toolName }) : msg; | |
| } | |
| // Built-in --help for any tool | |
| const trimmed = (input || "").trim().toLowerCase(); | |
| if (trimmed === "--help" || trimmed === "-h") { | |
| const help = `[${toolName}] Usage: @${toolName}|<args> | |
| Run "${toolName}" with empty input for built-in usage. | |
| Available tools: ${Object.keys(tools).length}`; | |
| return opts.json ? JSON.stringify({ ok: true, tool: toolName, help: true, data: help }) : help; | |
| } | |
| // Show spinner for slow tools (non-CLI mode — REPL handles its own feedback) | |
| const timer = setTimeout(() => process.stdout.write(`[*] ${toolName} running...\r`), 2000); | |
| try { | |
| const result = await fn(input); | |
| clearTimeout(timer); | |
| if (opts.json) { | |
| return JSON.stringify({ ok: true, tool: toolName, input, data: result, length: result.length }); | |
| } | |
| return result; | |
| } catch (e) { | |
| clearTimeout(timer); | |
| // Auto-heal: try to fix the error and retry once | |
| try { | |
| const evolve = await getEvolve(); | |
| const fix = await evolve.analyzeError(toolName, e.message); | |
| if (fix && fix.fixed) { | |
| // Retry the tool after successful fix | |
| const retryResult = await fn(input); | |
| if (opts.json) { | |
| return JSON.stringify({ ok: true, tool: toolName, input, data: retryResult, length: retryResult.length, auto_healed: true, fix: fix.message }); | |
| } | |
| return `[auto-heal] ${fix.message}\n${retryResult}`; | |
| } | |
| } catch {} | |
| const msg = `[${toolName} Error] ${e.message}`; | |
| return opts.json ? JSON.stringify({ ok: false, error: e.message, tool: toolName, input }) : msg; | |
| } | |
| } | |
| /** | |
| * Parse and execute a piped tool chain. | |
| * Format: tool1|args | tool2 | tool3|more_args | |
| * | |
| * @param {object} tools - hackerTools object | |
| * @param {string} chain - e.g. "subfinder|example.com | httpx | nuclei|https://x.com" | |
| * @param {object} [opts] | |
| * @returns {Promise<string>} | |
| */ | |
| export async function runPipe(tools, chain, opts = {}) { | |
| // Chain separator = " | " (space-pipe-space) | |
| // Tool/args separator = "|" (no spaces needed) | |
| // This avoids conflict between "shell|echo hi" and "subfinder|x.com | httpx" | |
| const segments = chain.split(/\s+\|\s+/).filter(Boolean); | |
| if (segments.length === 0) return opts.json ? JSON.stringify({ ok: false, error: "Empty pipe chain" }) : "[pipe] Empty chain"; | |
| if (segments.length === 1) { | |
| // Single tool, no piping | |
| const [toolName, ...args] = segments[0].split("|"); | |
| const input = args.join("|").trim(); | |
| return runTool(tools, toolName, input, opts); | |
| } | |
| // Multi-segment pipe: output of each tool becomes input of the next | |
| let pipeInput = ""; | |
| let lastResult = ""; | |
| for (let i = 0; i < segments.length; i++) { | |
| const seg = segments[i].trim(); | |
| const [toolName, ...args] = seg.split("|"); | |
| let input = args.join("|").trim(); | |
| // If there's piped input from previous tool, prepend it | |
| // Format: previous output lines become part of the argument | |
| if (pipeInput && input) { | |
| // Both piped input and explicit args — append piped data | |
| input = input + " " + pipeInput; | |
| } else if (pipeInput && !input) { | |
| input = pipeInput; | |
| } | |
| // Last segment gets the opts as-is; intermediate segments are not JSON | |
| const segOpts = (i === segments.length - 1) ? opts : {}; | |
| const result = await runTool(tools, toolName, input, segOpts); | |
| if (i === segments.length - 1) { | |
| lastResult = result; | |
| } else { | |
| // Intermediate output: take the last line or the whole thing | |
| const lines = result.split("\n").filter(Boolean); | |
| // Try to find the most relevant data lines (skip header/count) | |
| const dataLines = lines.filter(l => !l.startsWith("🔎") && !l.startsWith("Results:") && !l.startsWith("[") && l.trim()); | |
| pipeInput = dataLines.length > 0 ? dataLines.join("\n") : lines.slice(-10).join("\n"); | |
| } | |
| } | |
| return lastResult; | |
| } | |
| /** | |
| * Run a scheduled scan: executes a tool on each scope target. | |
| * @param {object} tools - hackerTools object | |
| * @param {string} toolName - tool to run | |
| * @param {string} [extra] - extra options | |
| * @param {number} [concurrency=1] | |
| * @returns {Promise<string>} | |
| */ | |
| export async function runScheduledScan(tools, toolName, extra = "", concurrency = 1) { | |
| // Load scope | |
| const scopeResult = await tools.scope("export"); | |
| const targets = scopeResult.split("\n").filter(l => l.trim() && !l.includes("(empty") && !l.includes("Commands")); | |
| if (targets.length === 0) return "[schedule] No scope targets defined. Use scope add <target> first."; | |
| const fn = tools[toolName]; | |
| if (!fn) return `[schedule] Unknown tool: ${toolName}`; | |
| const results = []; | |
| for (const target of targets) { | |
| try { | |
| const r = await fn(`${target} ${extra}`.trim()); | |
| results.push(`=== ${target} ===\n${r}`); | |
| } catch (e) { | |
| results.push(`=== ${target} ===\n[${toolName} Error] ${e.message}`); | |
| } | |
| } | |
| return results.join("\n\n"); | |
| } | |