@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.
Files changed (53) hide show
  1. package/dist/agent/loop-guard.d.ts +43 -0
  2. package/dist/agent/loop-guard.js +75 -0
  3. package/dist/agent/loop-guard.js.map +1 -0
  4. package/dist/agent/progress.d.ts +14 -0
  5. package/dist/agent/progress.js +112 -0
  6. package/dist/agent/progress.js.map +1 -0
  7. package/dist/agent/runner.js +45 -11
  8. package/dist/agent/runner.js.map +1 -1
  9. package/dist/agent/task-analyzer.d.ts +18 -0
  10. package/dist/agent/task-analyzer.js +21 -0
  11. package/dist/agent/task-analyzer.js.map +1 -0
  12. package/dist/agent/task-plan.d.ts +37 -0
  13. package/dist/agent/task-plan.js +98 -0
  14. package/dist/agent/task-plan.js.map +1 -0
  15. package/dist/commands/update.js +1 -1
  16. package/dist/prompts/index.d.ts +1 -1
  17. package/dist/prompts/index.js +74 -1
  18. package/dist/prompts/index.js.map +1 -1
  19. package/dist/repl.js +25 -1
  20. package/dist/repl.js.map +1 -1
  21. package/dist/safety/classifier.js +64 -0
  22. package/dist/safety/classifier.js.map +1 -1
  23. package/dist/safety/path-permissions.d.ts +14 -0
  24. package/dist/safety/path-permissions.js +43 -0
  25. package/dist/safety/path-permissions.js.map +1 -0
  26. package/dist/tools/capabilities.d.ts +11 -0
  27. package/dist/tools/capabilities.js +136 -0
  28. package/dist/tools/capabilities.js.map +1 -0
  29. package/dist/tools/command-intent.d.ts +11 -0
  30. package/dist/tools/command-intent.js +61 -0
  31. package/dist/tools/command-intent.js.map +1 -0
  32. package/dist/tools/fs.d.ts +10 -0
  33. package/dist/tools/fs.js +73 -2
  34. package/dist/tools/fs.js.map +1 -1
  35. package/dist/tools/jobs.d.ts +28 -0
  36. package/dist/tools/jobs.js +161 -0
  37. package/dist/tools/jobs.js.map +1 -0
  38. package/dist/tools/net-ping-sweep.d.ts +24 -0
  39. package/dist/tools/net-ping-sweep.js +205 -0
  40. package/dist/tools/net-ping-sweep.js.map +1 -0
  41. package/dist/tools/network-context.d.ts +26 -0
  42. package/dist/tools/network-context.js +138 -0
  43. package/dist/tools/network-context.js.map +1 -0
  44. package/dist/tools/registry.js +48 -2
  45. package/dist/tools/registry.js.map +1 -1
  46. package/dist/tools/shell.js +3 -3
  47. package/dist/tools/shell.js.map +1 -1
  48. package/dist/tools/validate.js +7 -4
  49. package/dist/tools/validate.js.map +1 -1
  50. package/dist/ui/task-pane.d.ts +17 -0
  51. package/dist/ui/task-pane.js +80 -0
  52. package/dist/ui/task-pane.js.map +1 -0
  53. 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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"}
@@ -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
- const profileArgs = profileToNmapArgs(profile);
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;