@pentoshi/clai 0.8.2 → 0.9.2
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/agent/loop-guard.d.ts +43 -0
- package/dist/agent/loop-guard.js +75 -0
- package/dist/agent/loop-guard.js.map +1 -0
- package/dist/agent/progress.d.ts +14 -0
- package/dist/agent/progress.js +112 -0
- package/dist/agent/progress.js.map +1 -0
- package/dist/agent/runner.js +45 -11
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/task-analyzer.d.ts +18 -0
- package/dist/agent/task-analyzer.js +21 -0
- package/dist/agent/task-analyzer.js.map +1 -0
- package/dist/agent/task-plan.d.ts +37 -0
- package/dist/agent/task-plan.js +98 -0
- package/dist/agent/task-plan.js.map +1 -0
- package/dist/commands/update.js +1 -1
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.js +74 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/repl.js +25 -1
- package/dist/repl.js.map +1 -1
- package/dist/safety/classifier.js +64 -0
- package/dist/safety/classifier.js.map +1 -1
- package/dist/safety/path-permissions.d.ts +14 -0
- package/dist/safety/path-permissions.js +43 -0
- package/dist/safety/path-permissions.js.map +1 -0
- package/dist/tools/capabilities.d.ts +11 -0
- package/dist/tools/capabilities.js +136 -0
- package/dist/tools/capabilities.js.map +1 -0
- package/dist/tools/command-intent.d.ts +11 -0
- package/dist/tools/command-intent.js +61 -0
- package/dist/tools/command-intent.js.map +1 -0
- package/dist/tools/fs.d.ts +10 -0
- package/dist/tools/fs.js +73 -2
- package/dist/tools/fs.js.map +1 -1
- package/dist/tools/jobs.d.ts +28 -0
- package/dist/tools/jobs.js +161 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/net-ping-sweep.d.ts +24 -0
- package/dist/tools/net-ping-sweep.js +205 -0
- package/dist/tools/net-ping-sweep.js.map +1 -0
- package/dist/tools/network-context.d.ts +26 -0
- package/dist/tools/network-context.js +138 -0
- package/dist/tools/network-context.js.map +1 -0
- package/dist/tools/registry.js +48 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/shell.js +3 -3
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/validate.js +7 -4
- package/dist/tools/validate.js.map +1 -1
- package/dist/ui/task-pane.d.ts +17 -0
- package/dist/ui/task-pane.js +80 -0
- package/dist/ui/task-pane.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createWriteStream } from "node:fs";
|
|
3
|
+
import { readFile, mkdir } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
export class JobManager {
|
|
8
|
+
jobs = new Map();
|
|
9
|
+
processes = new Map();
|
|
10
|
+
streams = new Map();
|
|
11
|
+
async startJob(command, options) {
|
|
12
|
+
const id = randomUUID().slice(0, 8);
|
|
13
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
14
|
+
const dir = join(homedir(), ".clai", "jobs");
|
|
15
|
+
await mkdir(dir, { recursive: true });
|
|
16
|
+
const artifactPath = join(dir, `${new Date().toISOString().replace(/[:.]/g, "-")}-${id}.txt`);
|
|
17
|
+
const stream = createWriteStream(artifactPath, { flags: "w" });
|
|
18
|
+
stream.write(`$ ${command}\n\n`);
|
|
19
|
+
try {
|
|
20
|
+
const detached = process.platform !== "win32";
|
|
21
|
+
const child = spawn(command, {
|
|
22
|
+
cwd,
|
|
23
|
+
detached,
|
|
24
|
+
shell: true,
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
const job = {
|
|
28
|
+
id,
|
|
29
|
+
command,
|
|
30
|
+
cwd,
|
|
31
|
+
pid: child.pid,
|
|
32
|
+
status: "running",
|
|
33
|
+
startedAt: new Date().toISOString(),
|
|
34
|
+
artifactPath,
|
|
35
|
+
};
|
|
36
|
+
this.jobs.set(id, job);
|
|
37
|
+
this.processes.set(id, child);
|
|
38
|
+
this.streams.set(id, stream);
|
|
39
|
+
child.stdout?.on("data", (chunk) => {
|
|
40
|
+
stream.write(chunk);
|
|
41
|
+
});
|
|
42
|
+
child.stderr?.on("data", (chunk) => {
|
|
43
|
+
stream.write(chunk);
|
|
44
|
+
});
|
|
45
|
+
child.on("close", (code) => {
|
|
46
|
+
job.status = "exited";
|
|
47
|
+
job.exitCode = code ?? undefined;
|
|
48
|
+
job.endedAt = new Date().toISOString();
|
|
49
|
+
stream.end();
|
|
50
|
+
this.processes.delete(id);
|
|
51
|
+
this.streams.delete(id);
|
|
52
|
+
});
|
|
53
|
+
child.on("error", () => {
|
|
54
|
+
job.status = "failed";
|
|
55
|
+
job.endedAt = new Date().toISOString();
|
|
56
|
+
stream.end();
|
|
57
|
+
this.processes.delete(id);
|
|
58
|
+
this.streams.delete(id);
|
|
59
|
+
});
|
|
60
|
+
// Detach so the child outlives the parent if needed
|
|
61
|
+
child.unref();
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
output: `Background job started: id=${id} pid=${child.pid ?? "?"}\nCommand: ${command}\nOutput: ${artifactPath}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
stream.end();
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
output: `Failed to start job: ${error instanceof Error ? error.message : String(error)}`,
|
|
72
|
+
exitCode: 1,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
listJobs() {
|
|
77
|
+
if (this.jobs.size === 0) {
|
|
78
|
+
return { ok: true, output: "No background jobs." };
|
|
79
|
+
}
|
|
80
|
+
const lines = [];
|
|
81
|
+
for (const job of this.jobs.values()) {
|
|
82
|
+
const status = job.status === "running" ? "▶ running" : `${job.status} (exit=${job.exitCode ?? "?"})`;
|
|
83
|
+
const elapsed = job.endedAt
|
|
84
|
+
? `${Math.round((new Date(job.endedAt).getTime() - new Date(job.startedAt).getTime()) / 1000)}s`
|
|
85
|
+
: `${Math.round((Date.now() - new Date(job.startedAt).getTime()) / 1000)}s ago`;
|
|
86
|
+
lines.push(`[${job.id}] ${status} ${elapsed} ${job.command.slice(0, 60)}`);
|
|
87
|
+
}
|
|
88
|
+
return { ok: true, output: lines.join("\n") };
|
|
89
|
+
}
|
|
90
|
+
getJob(id) {
|
|
91
|
+
return this.jobs.get(id);
|
|
92
|
+
}
|
|
93
|
+
async tailJob(id, bytes) {
|
|
94
|
+
const job = this.jobs.get(id);
|
|
95
|
+
if (!job) {
|
|
96
|
+
return { ok: false, output: `Job "${id}" not found.`, exitCode: 1 };
|
|
97
|
+
}
|
|
98
|
+
const tailBytes = bytes ?? 8_000;
|
|
99
|
+
try {
|
|
100
|
+
const content = await readFile(job.artifactPath, "utf8");
|
|
101
|
+
const tail = content.length > tailBytes
|
|
102
|
+
? `... (showing last ${tailBytes.toLocaleString()} bytes)\n${content.slice(-tailBytes)}`
|
|
103
|
+
: content;
|
|
104
|
+
return {
|
|
105
|
+
ok: true,
|
|
106
|
+
output: `[${job.id}] ${job.status}:\n${tail}`,
|
|
107
|
+
outputPath: job.artifactPath,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
output: `Failed to read job output: ${error instanceof Error ? error.message : String(error)}`,
|
|
114
|
+
exitCode: 1,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
stopJob(id, signal) {
|
|
119
|
+
const job = this.jobs.get(id);
|
|
120
|
+
if (!job) {
|
|
121
|
+
return { ok: false, output: `Job "${id}" not found.`, exitCode: 1 };
|
|
122
|
+
}
|
|
123
|
+
if (job.status !== "running") {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
output: `Job "${id}" is already ${job.status}.`,
|
|
127
|
+
exitCode: 1,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const child = this.processes.get(id);
|
|
131
|
+
if (!child?.pid) {
|
|
132
|
+
job.status = "killed";
|
|
133
|
+
job.endedAt = new Date().toISOString();
|
|
134
|
+
return { ok: true, output: `Job "${id}" marked as killed (no pid).` };
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const sig = signal ?? "SIGTERM";
|
|
138
|
+
if (process.platform !== "win32") {
|
|
139
|
+
process.kill(-child.pid, sig);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
child.kill(sig);
|
|
143
|
+
}
|
|
144
|
+
job.status = "killed";
|
|
145
|
+
job.endedAt = new Date().toISOString();
|
|
146
|
+
return { ok: true, output: `Job "${id}" sent ${sig}.` };
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return {
|
|
150
|
+
ok: false,
|
|
151
|
+
output: `Failed to stop job "${id}".`,
|
|
152
|
+
exitCode: 1,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
getRunningJobs() {
|
|
157
|
+
return [...this.jobs.values()].filter((j) => j.status === "running");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export const jobManager = new JobManager();
|
|
161
|
+
//# sourceMappingURL=jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/tools/jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ToolResult } from "../types.js";
|
|
2
|
+
export interface PingSweepArgs {
|
|
3
|
+
target: string;
|
|
4
|
+
method?: "auto" | "nmap" | "arp" | "native" | undefined;
|
|
5
|
+
timeoutMs?: number | undefined;
|
|
6
|
+
}
|
|
7
|
+
export interface ActiveDevice {
|
|
8
|
+
ip: string;
|
|
9
|
+
hostname?: string | undefined;
|
|
10
|
+
mac?: string | undefined;
|
|
11
|
+
vendor?: string | undefined;
|
|
12
|
+
source: "nmap" | "arp-scan" | "arp" | "ip-neigh";
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse nmap -sn output to extract active devices.
|
|
16
|
+
* Exported for testing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseNmapPingSweep(output: string): ActiveDevice[];
|
|
19
|
+
/**
|
|
20
|
+
* Parse arp -a output to extract devices from the ARP table.
|
|
21
|
+
* Exported for testing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseArpTable(output: string): ActiveDevice[];
|
|
24
|
+
export declare function pingSweep(args: PingSweepArgs): Promise<ToolResult>;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { platform } from "node:os";
|
|
4
|
+
const PRIVATE_CIDR_RE = /^(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3})(?:\/\d{1,2})?$/;
|
|
5
|
+
function isPrivateCidr(target) {
|
|
6
|
+
return PRIVATE_CIDR_RE.test(target);
|
|
7
|
+
}
|
|
8
|
+
function commandAvailable(command) {
|
|
9
|
+
try {
|
|
10
|
+
const cmd = platform() === "win32" ? `where.exe ${command}` : `command -v ${command}`;
|
|
11
|
+
execSync(cmd, { timeout: 3_000, stdio: "ignore" });
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function runCommand(command, argv, timeoutMs) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const child = spawn(command, argv, {
|
|
21
|
+
shell: false,
|
|
22
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
23
|
+
});
|
|
24
|
+
let stdout = "";
|
|
25
|
+
let stderr = "";
|
|
26
|
+
let killed = false;
|
|
27
|
+
const timeout = setTimeout(() => {
|
|
28
|
+
killed = true;
|
|
29
|
+
child.kill("SIGTERM");
|
|
30
|
+
}, timeoutMs);
|
|
31
|
+
child.stdout?.on("data", (chunk) => {
|
|
32
|
+
stdout += chunk.toString();
|
|
33
|
+
});
|
|
34
|
+
child.stderr?.on("data", (chunk) => {
|
|
35
|
+
stderr += chunk.toString();
|
|
36
|
+
});
|
|
37
|
+
child.on("error", () => {
|
|
38
|
+
clearTimeout(timeout);
|
|
39
|
+
resolve({ ok: false, stdout, stderr, exitCode: 1 });
|
|
40
|
+
});
|
|
41
|
+
child.on("close", (code) => {
|
|
42
|
+
clearTimeout(timeout);
|
|
43
|
+
resolve({
|
|
44
|
+
ok: !killed && code === 0,
|
|
45
|
+
stdout,
|
|
46
|
+
stderr,
|
|
47
|
+
exitCode: killed ? 124 : code,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse nmap -sn output to extract active devices.
|
|
54
|
+
* Exported for testing.
|
|
55
|
+
*/
|
|
56
|
+
export function parseNmapPingSweep(output) {
|
|
57
|
+
const devices = [];
|
|
58
|
+
const blocks = output.split(/(?=Nmap scan report for )/);
|
|
59
|
+
for (const block of blocks) {
|
|
60
|
+
const hostMatch = /Nmap scan report for\s+(?:(\S+)\s+\()?(\d+\.\d+\.\d+\.\d+)\)?/i.exec(block);
|
|
61
|
+
if (!hostMatch)
|
|
62
|
+
continue;
|
|
63
|
+
const ip = hostMatch[2];
|
|
64
|
+
const hostname = hostMatch[1] || undefined;
|
|
65
|
+
if (!/Host is up/i.test(block))
|
|
66
|
+
continue;
|
|
67
|
+
const macMatch = /MAC Address:\s+([0-9A-Fa-f:]+)(?:\s+\(([^)]+)\))?/i.exec(block);
|
|
68
|
+
devices.push({
|
|
69
|
+
ip,
|
|
70
|
+
hostname,
|
|
71
|
+
mac: macMatch?.[1],
|
|
72
|
+
vendor: macMatch?.[2],
|
|
73
|
+
source: "nmap",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return devices;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse arp -a output to extract devices from the ARP table.
|
|
80
|
+
* Exported for testing.
|
|
81
|
+
*/
|
|
82
|
+
export function parseArpTable(output) {
|
|
83
|
+
const devices = [];
|
|
84
|
+
const lines = output.split("\n");
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
// macOS: ? (192.168.1.1) at aa:bb:cc:dd:ee:ff on en0 [ethernet]
|
|
87
|
+
// Linux: ? (192.168.1.1) at aa:bb:cc:dd:ee:ff [ether] on eth0
|
|
88
|
+
const match = /\((\d+\.\d+\.\d+\.\d+)\)\s+at\s+([0-9a-fA-F:]+)/i.exec(line);
|
|
89
|
+
if (match && match[2] !== "(incomplete)") {
|
|
90
|
+
devices.push({
|
|
91
|
+
ip: match[1],
|
|
92
|
+
mac: match[2],
|
|
93
|
+
source: "arp",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return devices;
|
|
98
|
+
}
|
|
99
|
+
function parseArpScanOutput(output) {
|
|
100
|
+
const devices = [];
|
|
101
|
+
const lines = output.split("\n");
|
|
102
|
+
for (const line of lines) {
|
|
103
|
+
// 192.168.1.1 aa:bb:cc:dd:ee:ff Vendor Corp
|
|
104
|
+
const match = /^(\d+\.\d+\.\d+\.\d+)\s+([0-9a-fA-F:]+)\s*(.*)/i.exec(line);
|
|
105
|
+
if (match) {
|
|
106
|
+
devices.push({
|
|
107
|
+
ip: match[1],
|
|
108
|
+
mac: match[2],
|
|
109
|
+
vendor: match[3]?.trim() || undefined,
|
|
110
|
+
source: "arp-scan",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return devices;
|
|
115
|
+
}
|
|
116
|
+
function parseIpNeigh(output) {
|
|
117
|
+
const devices = [];
|
|
118
|
+
const lines = output.split("\n");
|
|
119
|
+
for (const line of lines) {
|
|
120
|
+
// 192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE
|
|
121
|
+
const match = /^(\d+\.\d+\.\d+\.\d+)\s+.*?lladdr\s+([0-9a-fA-F:]+)/i.exec(line);
|
|
122
|
+
if (match) {
|
|
123
|
+
devices.push({
|
|
124
|
+
ip: match[1],
|
|
125
|
+
mac: match[2],
|
|
126
|
+
source: "ip-neigh",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return devices;
|
|
131
|
+
}
|
|
132
|
+
function formatDevices(devices) {
|
|
133
|
+
if (devices.length === 0)
|
|
134
|
+
return "No active devices found.";
|
|
135
|
+
const lines = [`Found ${devices.length} active device(s):\n`];
|
|
136
|
+
for (const dev of devices) {
|
|
137
|
+
const parts = [dev.ip];
|
|
138
|
+
if (dev.hostname)
|
|
139
|
+
parts.push(`(${dev.hostname})`);
|
|
140
|
+
if (dev.mac)
|
|
141
|
+
parts.push(`MAC: ${dev.mac}`);
|
|
142
|
+
if (dev.vendor)
|
|
143
|
+
parts.push(`[${dev.vendor}]`);
|
|
144
|
+
lines.push(` ${parts.join(" ")}`);
|
|
145
|
+
}
|
|
146
|
+
return lines.join("\n");
|
|
147
|
+
}
|
|
148
|
+
export async function pingSweep(args) {
|
|
149
|
+
const target = args.target.trim();
|
|
150
|
+
const timeoutMs = args.timeoutMs ?? 120_000;
|
|
151
|
+
const method = args.method ?? "auto";
|
|
152
|
+
if (!isPrivateCidr(target)) {
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
output: `net.pingSweep is restricted to local/private networks. Target "${target}" does not appear to be a private CIDR. Use net.scan for individual host scanning.`,
|
|
156
|
+
exitCode: 1,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// Method selection
|
|
160
|
+
if (method === "nmap" || (method === "auto" && commandAvailable("nmap"))) {
|
|
161
|
+
const result = await runCommand("nmap", ["-sn", target], timeoutMs);
|
|
162
|
+
const devices = parseNmapPingSweep(result.stdout);
|
|
163
|
+
return {
|
|
164
|
+
ok: result.ok,
|
|
165
|
+
output: formatDevices(devices) + (result.ok ? "" : `\n\nStderr: ${result.stderr}`),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (method === "arp" || method === "auto") {
|
|
169
|
+
// Try arp-scan first (gives richer data)
|
|
170
|
+
if (commandAvailable("arp-scan")) {
|
|
171
|
+
const result = await runCommand("arp-scan", ["--localnet"], timeoutMs);
|
|
172
|
+
const devices = parseArpScanOutput(result.stdout);
|
|
173
|
+
if (devices.length > 0) {
|
|
174
|
+
return {
|
|
175
|
+
ok: true,
|
|
176
|
+
output: formatDevices(devices),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Linux: try ip neigh
|
|
181
|
+
if (platform() === "linux" && commandAvailable("ip")) {
|
|
182
|
+
const result = await runCommand("ip", ["neigh", "show"], timeoutMs);
|
|
183
|
+
const devices = parseIpNeigh(result.stdout);
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
output: formatDevices(devices) +
|
|
187
|
+
"\n\nNote: ARP cache only shows recently-seen devices. For comprehensive discovery, install nmap: `pkg.install nmap`",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Fallback: arp -a (available everywhere)
|
|
191
|
+
const result = await runCommand("arp", ["-a"], timeoutMs);
|
|
192
|
+
const devices = parseArpTable(result.stdout);
|
|
193
|
+
return {
|
|
194
|
+
ok: true,
|
|
195
|
+
output: formatDevices(devices) +
|
|
196
|
+
"\n\nNote: ARP cache only shows recently-seen devices. For comprehensive discovery, install nmap: `pkg.install nmap`",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
output: `No suitable method for ping sweep. Install nmap for best results: pkg.install nmap`,
|
|
202
|
+
exitCode: 1,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=net-ping-sweep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"net-ping-sweep.js","sourceRoot":"","sources":["../../src/tools/net-ping-sweep.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ToolResult } from "../types.js";
|
|
2
|
+
export interface NetworkInterfaceInfo {
|
|
3
|
+
name: string;
|
|
4
|
+
address: string;
|
|
5
|
+
family: "IPv4" | "IPv6";
|
|
6
|
+
netmask?: string | undefined;
|
|
7
|
+
cidr?: string | undefined;
|
|
8
|
+
mac?: string | undefined;
|
|
9
|
+
internal: boolean;
|
|
10
|
+
defaultRoute?: boolean | undefined;
|
|
11
|
+
gateway?: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
export interface NetworkContextResult {
|
|
14
|
+
hostname: string;
|
|
15
|
+
platform: NodeJS.Platform;
|
|
16
|
+
interfaces: NetworkInterfaceInfo[];
|
|
17
|
+
candidates: NetworkInterfaceInfo[];
|
|
18
|
+
selected?: NetworkInterfaceInfo | undefined;
|
|
19
|
+
warnings?: string[] | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Convert a dotted-quad IPv4 netmask (e.g. "255.255.255.0") to CIDR prefix
|
|
23
|
+
* length (e.g. 24). Returns undefined if the mask is not valid.
|
|
24
|
+
*/
|
|
25
|
+
export declare function netmaskToCidr(netmask: string): number | undefined;
|
|
26
|
+
export declare function getNetworkContext(): Promise<ToolResult>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { networkInterfaces, hostname, platform } from "node:os";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
/**
|
|
4
|
+
* Convert a dotted-quad IPv4 netmask (e.g. "255.255.255.0") to CIDR prefix
|
|
5
|
+
* length (e.g. 24). Returns undefined if the mask is not valid.
|
|
6
|
+
*/
|
|
7
|
+
export function netmaskToCidr(netmask) {
|
|
8
|
+
const parts = netmask.split(".");
|
|
9
|
+
if (parts.length !== 4)
|
|
10
|
+
return undefined;
|
|
11
|
+
let bits = 0;
|
|
12
|
+
for (const part of parts) {
|
|
13
|
+
const n = Number(part);
|
|
14
|
+
if (Number.isNaN(n) || n < 0 || n > 255)
|
|
15
|
+
return undefined;
|
|
16
|
+
bits += n.toString(2).split("1").length - 1;
|
|
17
|
+
}
|
|
18
|
+
return bits;
|
|
19
|
+
}
|
|
20
|
+
function isPrivateV4(address) {
|
|
21
|
+
const parts = address.split(".").map(Number);
|
|
22
|
+
const [a, b] = parts;
|
|
23
|
+
if (a === 10)
|
|
24
|
+
return true;
|
|
25
|
+
if (a === 172 && b !== undefined && b >= 16 && b <= 31)
|
|
26
|
+
return true;
|
|
27
|
+
if (a === 192 && b === 168)
|
|
28
|
+
return true;
|
|
29
|
+
if (a === 169 && b === 254)
|
|
30
|
+
return true;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function detectGateway() {
|
|
34
|
+
const os = platform();
|
|
35
|
+
try {
|
|
36
|
+
if (os === "darwin") {
|
|
37
|
+
const out = execSync("route -n get default 2>/dev/null", {
|
|
38
|
+
timeout: 5_000,
|
|
39
|
+
encoding: "utf8",
|
|
40
|
+
});
|
|
41
|
+
const match = /gateway:\s+(\S+)/i.exec(out);
|
|
42
|
+
return match?.[1];
|
|
43
|
+
}
|
|
44
|
+
if (os === "linux") {
|
|
45
|
+
const out = execSync("ip route show default 2>/dev/null", {
|
|
46
|
+
timeout: 5_000,
|
|
47
|
+
encoding: "utf8",
|
|
48
|
+
});
|
|
49
|
+
const match = /via\s+(\S+)/i.exec(out);
|
|
50
|
+
return match?.[1];
|
|
51
|
+
}
|
|
52
|
+
if (os === "win32") {
|
|
53
|
+
const out = execSync('powershell -Command "(Get-NetRoute -DestinationPrefix 0.0.0.0/0 | Select-Object -First 1).NextHop"', { timeout: 5_000, encoding: "utf8" });
|
|
54
|
+
const trimmed = out.trim();
|
|
55
|
+
return trimmed || undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// gateway detection failed — non-fatal
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
function sameSubnet(addr, gateway, prefix) {
|
|
64
|
+
const toBits = (ip) => {
|
|
65
|
+
const parts = ip.split(".").map(Number);
|
|
66
|
+
return ((parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]) >>> 0;
|
|
67
|
+
};
|
|
68
|
+
const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;
|
|
69
|
+
return (toBits(addr) & mask) === (toBits(gateway) & mask);
|
|
70
|
+
}
|
|
71
|
+
export async function getNetworkContext() {
|
|
72
|
+
const ifaces = networkInterfaces();
|
|
73
|
+
const all = [];
|
|
74
|
+
const warnings = [];
|
|
75
|
+
const gateway = detectGateway();
|
|
76
|
+
for (const [name, addrs] of Object.entries(ifaces)) {
|
|
77
|
+
if (!addrs)
|
|
78
|
+
continue;
|
|
79
|
+
for (const addr of addrs) {
|
|
80
|
+
all.push({
|
|
81
|
+
name,
|
|
82
|
+
address: addr.address,
|
|
83
|
+
family: addr.family,
|
|
84
|
+
netmask: addr.netmask,
|
|
85
|
+
cidr: addr.cidr ?? undefined,
|
|
86
|
+
mac: addr.mac,
|
|
87
|
+
internal: addr.internal,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Filter to active private IPv4 interfaces
|
|
92
|
+
const candidates = all.filter((iface) => iface.family === "IPv4" &&
|
|
93
|
+
!iface.internal &&
|
|
94
|
+
isPrivateV4(iface.address));
|
|
95
|
+
let selected;
|
|
96
|
+
if (gateway && candidates.length > 0) {
|
|
97
|
+
// Find the interface whose subnet contains the gateway
|
|
98
|
+
for (const candidate of candidates) {
|
|
99
|
+
const prefix = candidate.netmask
|
|
100
|
+
? netmaskToCidr(candidate.netmask)
|
|
101
|
+
: undefined;
|
|
102
|
+
if (prefix !== undefined && sameSubnet(candidate.address, gateway, prefix)) {
|
|
103
|
+
selected = {
|
|
104
|
+
...candidate,
|
|
105
|
+
defaultRoute: true,
|
|
106
|
+
gateway,
|
|
107
|
+
};
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// If no subnet match, just take the first candidate
|
|
112
|
+
if (!selected && candidates.length > 0) {
|
|
113
|
+
selected = { ...candidates[0], gateway };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if (candidates.length === 1) {
|
|
117
|
+
selected = candidates[0];
|
|
118
|
+
}
|
|
119
|
+
else if (candidates.length > 1) {
|
|
120
|
+
warnings.push(`Multiple network interfaces found (${candidates.map((c) => `${c.name}:${c.address}`).join(", ")}). Could not determine default route.`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
warnings.push("No active private IPv4 interfaces detected.");
|
|
124
|
+
}
|
|
125
|
+
const result = {
|
|
126
|
+
hostname: hostname(),
|
|
127
|
+
platform: platform(),
|
|
128
|
+
interfaces: all,
|
|
129
|
+
candidates,
|
|
130
|
+
selected,
|
|
131
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
ok: candidates.length > 0,
|
|
135
|
+
output: JSON.stringify(result, null, 2),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=network-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-context.js","sourceRoot":"","sources":["../../src/tools/network-context.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/registry.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { detectSystem } from "../os/detect.js";
|
|
2
2
|
import { detectPackageManager, assertSafePackageName } from "../os/pkgmgr.js";
|
|
3
|
-
import { fsList, fsRead, fsSearch, fsWrite } from "./fs.js";
|
|
3
|
+
import { fsEdit, fsDelete, fsList, fsRead, fsSearch, fsWrite } from "./fs.js";
|
|
4
4
|
import { httpFetch } from "./http.js";
|
|
5
5
|
import { shellExec, spawnArgv } from "./shell.js";
|
|
6
6
|
import { classifyToolCall } from "../safety/classifier.js";
|
|
7
7
|
import { loadScope } from "../store/scope.js";
|
|
8
8
|
import { parseHost, parsePortSpec, parseLegacyFlags, profileToNmapArgs, } from "./validate.js";
|
|
9
|
+
import { getNetworkContext } from "./network-context.js";
|
|
10
|
+
import { pingSweep } from "./net-ping-sweep.js";
|
|
11
|
+
import { toolCheckHandler } from "./capabilities.js";
|
|
12
|
+
import { jobManager } from "./jobs.js";
|
|
9
13
|
function requireString(args, key) {
|
|
10
14
|
const value = args[key];
|
|
11
15
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -74,7 +78,11 @@ export const toolRegistry = {
|
|
|
74
78
|
? args.profile
|
|
75
79
|
: undefined;
|
|
76
80
|
const legacyFlags = optionalString(args, "flags");
|
|
77
|
-
|
|
81
|
+
// ports and topPorts conflict on the nmap CLI — ports takes priority
|
|
82
|
+
const cleanedProfile = ports && profile?.topPorts
|
|
83
|
+
? { ...profile, topPorts: undefined }
|
|
84
|
+
: profile;
|
|
85
|
+
const profileArgs = profileToNmapArgs(cleanedProfile);
|
|
78
86
|
const legacyArgs = legacyFlags ? parseLegacyFlags(legacyFlags) : [];
|
|
79
87
|
const argv = [];
|
|
80
88
|
if (ports)
|
|
@@ -232,6 +240,42 @@ export const toolRegistry = {
|
|
|
232
240
|
async "tool.batch"(args, options) {
|
|
233
241
|
return runToolBatch(args, options);
|
|
234
242
|
},
|
|
243
|
+
async "net.context"() {
|
|
244
|
+
return getNetworkContext();
|
|
245
|
+
},
|
|
246
|
+
async "net.pingSweep"(args) {
|
|
247
|
+
const target = requireString(args, "target");
|
|
248
|
+
return pingSweep({
|
|
249
|
+
target,
|
|
250
|
+
method: optionalString(args, "method"),
|
|
251
|
+
timeoutMs: optionalNumber(args, "timeoutMs"),
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
async "tool.check"(args) {
|
|
255
|
+
return toolCheckHandler(args);
|
|
256
|
+
},
|
|
257
|
+
async "shell.start"(args) {
|
|
258
|
+
const command = requireString(args, "command");
|
|
259
|
+
return jobManager.startJob(command, {
|
|
260
|
+
cwd: optionalString(args, "cwd"),
|
|
261
|
+
name: optionalString(args, "name"),
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
async "shell.jobs"() {
|
|
265
|
+
return jobManager.listJobs();
|
|
266
|
+
},
|
|
267
|
+
async "shell.tail"(args) {
|
|
268
|
+
return jobManager.tailJob(requireString(args, "id"), optionalNumber(args, "bytes"));
|
|
269
|
+
},
|
|
270
|
+
async "shell.stop"(args) {
|
|
271
|
+
return jobManager.stopJob(requireString(args, "id"));
|
|
272
|
+
},
|
|
273
|
+
async "fs.edit"(args) {
|
|
274
|
+
return fsEdit(requireString(args, "path"), requireString(args, "oldText"), requireString(args, "newText"), optionalNumber(args, "expectedReplacements"));
|
|
275
|
+
},
|
|
276
|
+
async "fs.delete"(args) {
|
|
277
|
+
return fsDelete(requireString(args, "path"), typeof args.recursive === "boolean" ? args.recursive : undefined);
|
|
278
|
+
},
|
|
235
279
|
};
|
|
236
280
|
export function availableToolNames() {
|
|
237
281
|
return Object.keys(toolRegistry);
|
|
@@ -258,6 +302,8 @@ const BATCH_SAFE_TOOLS = new Set([
|
|
|
258
302
|
"sysinfo",
|
|
259
303
|
"dns.lookup",
|
|
260
304
|
"whois.lookup",
|
|
305
|
+
"net.context",
|
|
306
|
+
"tool.check",
|
|
261
307
|
]);
|
|
262
308
|
const BATCH_MAX_CALLS = 8;
|
|
263
309
|
const BATCH_DEFAULT_CONCURRENCY = 3;
|