@kamrankhan027/probe-ai 1.0.2 → 1.0.4
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/README.md +128 -0
- package/dist/index.js +121 -58
- package/package.json +7 -5
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# 🔍 ProbeAI — Autonomous API Reliability & Postman Suite Generator
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**An autonomous developer CLI agent that inspects OpenAPI specs, synthesizes multi-scenario test suites, executes real-time HTTP calls, and auto-exports ready-to-import Postman collections.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@kamrankhan027/probe-ai)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://langchain-ai.github.io/langgraphjs/)
|
|
10
|
+
[](https://ai.google.dev/)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## ⚡ Quickstart
|
|
18
|
+
|
|
19
|
+
### Option 1: Run Instantly via `npx` (Zero Installation Required)
|
|
20
|
+
```bash
|
|
21
|
+
npx @kamrankhan027/probe-ai
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### Option 2: Install Globally
|
|
27
|
+
|
|
28
|
+
**Using npm:**
|
|
29
|
+
```bash
|
|
30
|
+
npm install -g @kamrankhan027/probe-ai
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Using pnpm:**
|
|
34
|
+
```bash
|
|
35
|
+
pnpm add -g @kamrankhan027/probe-ai
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Using yarn:**
|
|
39
|
+
```bash
|
|
40
|
+
yarn global add @kamrankhan027/probe-ai
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then run ProbeAI from **any directory** on your machine:
|
|
44
|
+
```bash
|
|
45
|
+
probe-ai
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 🔑 API Key Setup
|
|
51
|
+
|
|
52
|
+
ProbeAI uses Google Gemini for fast, structured reasoning.
|
|
53
|
+
* **If you have a `.env` file**: Add `GEMINI_API_KEY=your_key_here`
|
|
54
|
+
* **If running via `npx` without a `.env`**: ProbeAI will interactively prompt you to paste your free Gemini API key on startup.
|
|
55
|
+
|
|
56
|
+
*(Get a free key in 10 seconds at [aistudio.google.com](https://aistudio.google.com/)).*
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 🌟 Key Superpowers
|
|
61
|
+
|
|
62
|
+
* **🔍 Auto-Discovery via OpenAPI**: Probes any API base URL (e.g. `http://localhost:8000`) and parses `/openapi.json` to map all routes, parameters, and security schemas.
|
|
63
|
+
* **📦 1-Click Postman Collection Export**: Eliminates 30+ minutes of manual route recreation by generating ready-to-import **Postman Collection v2.1** files (`reports/probe_collection.json`).
|
|
64
|
+
* **🔐 Smart Auth Detective**: Supports direct token injection (`Bearer ...` or `X-API-Key`) **AND** autonomous login flows (executing `POST /auth/login`, extracting the JWT token, and binding it to subsequent requests).
|
|
65
|
+
* **⚡ Real-Time HTTP Latency Profiling**: Fires live requests, catches `422/404/401` edge cases, and logs latency in milliseconds.
|
|
66
|
+
* **📊 Standalone HTML & Terminal Reports**: Generates interactive single-page HTML audit dashboards with health scores and payload replays.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 💡 Example Prompts to Try
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
You: Inspect https://bookmark-agent-backend.onrender.com and export a Postman collection
|
|
74
|
+
```
|
|
75
|
+
*Auto-maps all endpoints and creates `reports/postman_collection.json`.*
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
You: Test all endpoints on https://bookmark-agent-backend.onrender.com and save an HTML report
|
|
79
|
+
```
|
|
80
|
+
*Executes Happy Path, Query Filter, and Error test cases and saves `reports/audit_report.html`.*
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
You: Set auth Bearer my_jwt_token_123 and test protected endpoints
|
|
84
|
+
```
|
|
85
|
+
*Injects your authorization header into session memory for all subsequent calls.*
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
You: Authenticate via POST https://api.example.com/login with {"email":"dev@example.com","password":"secret"}, then test /api/dashboard
|
|
89
|
+
```
|
|
90
|
+
*Extracts the JWT from the login response and tests the protected dashboard route automatically.*
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 🏗️ Repository Architecture
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
probe-ai/
|
|
98
|
+
├── cli/ # 🚀 Native TypeScript NPM Package (@kamrankhan027/probe-ai)
|
|
99
|
+
│ ├── src/
|
|
100
|
+
│ │ ├── index.ts # Interactive CLI loop & Marked terminal styling
|
|
101
|
+
│ │ ├── agent.ts # @langchain/langgraph ReAct workflow
|
|
102
|
+
│ │ └── tools/ # OpenAPI, HTTP, Auth, Postman & Report tools
|
|
103
|
+
│ ├── dist/
|
|
104
|
+
│ │ └── index.js # ⚡ Single zero-dependency bundled executable (tsup)
|
|
105
|
+
│ └── package.json
|
|
106
|
+
├── python/ # Python implementation (LangGraph + Rich)
|
|
107
|
+
│ ├── agent.py
|
|
108
|
+
│ └── requirements.txt
|
|
109
|
+
└── reports/ # Shared output directory
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 🛠️ Local Development
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/kamran-027/probe-ai.git
|
|
118
|
+
cd probe-ai/cli
|
|
119
|
+
npm install
|
|
120
|
+
npm run build
|
|
121
|
+
npm start
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
<div align="center">
|
|
127
|
+
Built by <b>Kamran Khan</b> as part of <b>Cadence Labs</b>.
|
|
128
|
+
</div>
|
package/dist/index.js
CHANGED
|
@@ -7,8 +7,7 @@ var __export = (target, all) => {
|
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import dotenv from "dotenv";
|
|
10
|
-
import
|
|
11
|
-
import boxen from "boxen";
|
|
10
|
+
import chalk7 from "chalk";
|
|
12
11
|
import ora from "ora";
|
|
13
12
|
import readline from "readline";
|
|
14
13
|
import { marked } from "marked";
|
|
@@ -4067,15 +4066,81 @@ var NEVER = INVALID;
|
|
|
4067
4066
|
|
|
4068
4067
|
// src/tools/openapi.ts
|
|
4069
4068
|
import axios from "axios";
|
|
4069
|
+
import chalk2 from "chalk";
|
|
4070
|
+
|
|
4071
|
+
// src/ui.ts
|
|
4070
4072
|
import chalk from "chalk";
|
|
4073
|
+
import boxen from "boxen";
|
|
4074
|
+
function printModernBanner(version = "1.0.4") {
|
|
4075
|
+
const content = `${chalk.bold.hex("#6366F1")("\u2726 PROBE AI")} ${chalk.dim(`v${version}`)} ${chalk.hex("#10B981")("\u25CF READY")}
|
|
4076
|
+
${chalk.white("Autonomous API Schema & Reliability Inspector")}
|
|
4077
|
+
|
|
4078
|
+
${chalk.dim("Capabilities:")}
|
|
4079
|
+
${chalk.cyan("\u2022")} ${chalk.bold("OpenAPI Discovery")} ${chalk.dim("Auto-maps schemas & security")}
|
|
4080
|
+
${chalk.cyan("\u2022")} ${chalk.bold("Postman Exporter")} ${chalk.dim("1-click collection generation")}
|
|
4081
|
+
${chalk.cyan("\u2022")} ${chalk.bold("Smart Auth")} ${chalk.dim("Login token exchange & Bearer injection")}
|
|
4082
|
+
${chalk.cyan("\u2022")} ${chalk.bold("HTML Audit Reports")} ${chalk.dim("Latency profiling & payload replays")}
|
|
4083
|
+
|
|
4084
|
+
${chalk.dim("Quick Test Prompts:")}
|
|
4085
|
+
${chalk.gray("\u276F")} ${chalk.yellow("Inspect https://bookmark-agent-backend.onrender.com and export Postman collection")}
|
|
4086
|
+
${chalk.gray("\u276F")} ${chalk.yellow("Test all endpoints on http://localhost:8000 and save HTML report")}
|
|
4087
|
+
${chalk.dim("Type 'exit' to quit")}`;
|
|
4088
|
+
console.log(
|
|
4089
|
+
boxen(content, {
|
|
4090
|
+
padding: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
4091
|
+
margin: { top: 1, bottom: 1 },
|
|
4092
|
+
borderStyle: "round",
|
|
4093
|
+
borderColor: "#6366F1"
|
|
4094
|
+
})
|
|
4095
|
+
);
|
|
4096
|
+
}
|
|
4097
|
+
function formatMethodBadge(method) {
|
|
4098
|
+
const m = method.toUpperCase();
|
|
4099
|
+
switch (m) {
|
|
4100
|
+
case "GET":
|
|
4101
|
+
return chalk.bgBlue.bold.white(" GET ");
|
|
4102
|
+
case "POST":
|
|
4103
|
+
return chalk.bgGreen.bold.white(" POST ");
|
|
4104
|
+
case "PUT":
|
|
4105
|
+
return chalk.bgYellow.bold.black(" PUT ");
|
|
4106
|
+
case "DELETE":
|
|
4107
|
+
return chalk.bgRed.bold.white(" DEL ");
|
|
4108
|
+
case "PATCH":
|
|
4109
|
+
return chalk.bgMagenta.bold.white(" PATCH ");
|
|
4110
|
+
default:
|
|
4111
|
+
return chalk.bgGray.bold.white(` ${m} `);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
function formatStatusBadge(status) {
|
|
4115
|
+
if (status >= 200 && status < 300) {
|
|
4116
|
+
return chalk.hex("#10B981").bold(`${status} OK`);
|
|
4117
|
+
}
|
|
4118
|
+
if (status === 401 || status === 403) {
|
|
4119
|
+
return chalk.hex("#F59E0B").bold(`${status} Auth Required`);
|
|
4120
|
+
}
|
|
4121
|
+
if (status === 404) {
|
|
4122
|
+
return chalk.hex("#64748B").bold(`${status} Not Found`);
|
|
4123
|
+
}
|
|
4124
|
+
if (status === 422) {
|
|
4125
|
+
return chalk.hex("#EC4899").bold(`${status} Unprocessable Entity`);
|
|
4126
|
+
}
|
|
4127
|
+
return chalk.hex("#EF4444").bold(`${status} Error`);
|
|
4128
|
+
}
|
|
4129
|
+
function logStep(icon, title, detail = "") {
|
|
4130
|
+
const iconColored = chalk.hex("#6366F1")(icon);
|
|
4131
|
+
const titleColored = chalk.bold.white(title);
|
|
4132
|
+
const detailColored = detail ? chalk.dim(` ${detail}`) : "";
|
|
4133
|
+
console.log(` ${iconColored} ${titleColored}${detailColored}`);
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
// src/tools/openapi.ts
|
|
4071
4137
|
var fetchOpenApiSpecTool = tool(
|
|
4072
4138
|
async ({ baseUrl }) => {
|
|
4073
|
-
console.log(chalk.cyan(`
|
|
4074
|
-
\u{1F50D} [Tool: fetch_openapi_spec] Probing: ${baseUrl}`));
|
|
4075
4139
|
let targetUrl = baseUrl.replace(/\/+$/, "");
|
|
4076
4140
|
if (!targetUrl.endsWith("openapi.json")) {
|
|
4077
4141
|
targetUrl = `${targetUrl}/openapi.json`;
|
|
4078
4142
|
}
|
|
4143
|
+
logStep("\u{1F50D}", "Probing OpenAPI Spec", chalk2.cyan(targetUrl));
|
|
4079
4144
|
try {
|
|
4080
4145
|
const response = await axios.get(targetUrl, { timeout: 1e4 });
|
|
4081
4146
|
const spec = response.data;
|
|
@@ -4096,6 +4161,7 @@ var fetchOpenApiSpecTool = tool(
|
|
|
4096
4161
|
});
|
|
4097
4162
|
}
|
|
4098
4163
|
}
|
|
4164
|
+
logStep("\u2713", `Discovered ${endpointsSummary.length} endpoints`, chalk2.dim(`(API v${info.version || "1.0"})`));
|
|
4099
4165
|
return JSON.stringify(
|
|
4100
4166
|
{
|
|
4101
4167
|
title: info.title || "API Spec",
|
|
@@ -4108,6 +4174,7 @@ var fetchOpenApiSpecTool = tool(
|
|
|
4108
4174
|
2
|
|
4109
4175
|
);
|
|
4110
4176
|
} catch (err) {
|
|
4177
|
+
logStep("\u2715", "Could not fetch OpenAPI spec", chalk2.red(err.message));
|
|
4111
4178
|
return `Could not fetch OpenAPI spec from ${targetUrl}: ${err.message}`;
|
|
4112
4179
|
}
|
|
4113
4180
|
},
|
|
@@ -4123,7 +4190,7 @@ var fetchOpenApiSpecTool = tool(
|
|
|
4123
4190
|
// src/tools/auth.ts
|
|
4124
4191
|
import { tool as tool2 } from "@langchain/core/tools";
|
|
4125
4192
|
import axios2 from "axios";
|
|
4126
|
-
import
|
|
4193
|
+
import chalk3 from "chalk";
|
|
4127
4194
|
|
|
4128
4195
|
// src/tools/state.ts
|
|
4129
4196
|
var sessionHeaders = {
|
|
@@ -4139,8 +4206,7 @@ var authenticateViaLoginTool = tool2(
|
|
|
4139
4206
|
payloadJson = "{}",
|
|
4140
4207
|
tokenField = "access_token"
|
|
4141
4208
|
}) => {
|
|
4142
|
-
|
|
4143
|
-
\u{1F510} [Tool: authenticate_via_login] Authenticating at: ${loginUrl}`));
|
|
4209
|
+
logStep("\u{1F510}", "Authenticating via login endpoint...", chalk3.dim(loginUrl));
|
|
4144
4210
|
let payload = {};
|
|
4145
4211
|
try {
|
|
4146
4212
|
if (payloadJson && payloadJson.trim() !== "{}") {
|
|
@@ -4162,12 +4228,14 @@ var authenticateViaLoginTool = tool2(
|
|
|
4162
4228
|
token = data[tokenField] || data.token || data.access_token || data.jwt || data.key;
|
|
4163
4229
|
}
|
|
4164
4230
|
if (!token) {
|
|
4231
|
+
logStep("\u2715", "Login succeeded but token field not found", chalk3.red(tokenField));
|
|
4165
4232
|
return `Login succeeded (HTTP ${res.status}), but token field '${tokenField}' was not found in response: ${JSON.stringify(data)}`;
|
|
4166
4233
|
}
|
|
4167
4234
|
sessionHeaders["Authorization"] = `Bearer ${token}`;
|
|
4168
|
-
|
|
4235
|
+
logStep("\u2713", "Extracted Bearer token & saved to session", chalk3.hex("#10B981")("Authorization: Bearer \u2022\u2022\u2022\u2022\u2022\u2022"));
|
|
4169
4236
|
return `Authentication successful. Bearer token extracted from '${tokenField}' and saved to session headers.`;
|
|
4170
4237
|
} catch (err) {
|
|
4238
|
+
logStep("\u2715", "Login failed", chalk3.red(err.message));
|
|
4171
4239
|
return `Login request failed: ${err.message}`;
|
|
4172
4240
|
}
|
|
4173
4241
|
},
|
|
@@ -4189,8 +4257,7 @@ var setAuthHeaderTool = tool2(
|
|
|
4189
4257
|
cleanVal = `Bearer ${cleanVal}`;
|
|
4190
4258
|
}
|
|
4191
4259
|
sessionHeaders[headerName] = cleanVal;
|
|
4192
|
-
|
|
4193
|
-
\u{1F511} [Tool: set_auth_header] Set '${headerName}' header in session.`));
|
|
4260
|
+
logStep("\u{1F511}", `Set auth header: ${headerName}`, chalk3.hex("#10B981")("\u2022\u2022\u2022\u2022\u2022\u2022"));
|
|
4194
4261
|
return `Successfully set '${headerName}' header in session.`;
|
|
4195
4262
|
},
|
|
4196
4263
|
{
|
|
@@ -4206,7 +4273,7 @@ var setAuthHeaderTool = tool2(
|
|
|
4206
4273
|
// src/tools/http.ts
|
|
4207
4274
|
import { tool as tool3 } from "@langchain/core/tools";
|
|
4208
4275
|
import axios3 from "axios";
|
|
4209
|
-
import
|
|
4276
|
+
import chalk4 from "chalk";
|
|
4210
4277
|
var executeHttpRequestTool = tool3(
|
|
4211
4278
|
async ({
|
|
4212
4279
|
method,
|
|
@@ -4216,8 +4283,6 @@ var executeHttpRequestTool = tool3(
|
|
|
4216
4283
|
bodyJson = "{}"
|
|
4217
4284
|
}) => {
|
|
4218
4285
|
const cleanMethod = method.trim().toUpperCase();
|
|
4219
|
-
console.log(chalk3.yellow(`
|
|
4220
|
-
\u26A1 [Tool: execute_http_request] ${cleanMethod} ${url}`));
|
|
4221
4286
|
const mergedHeaders = { ...sessionHeaders };
|
|
4222
4287
|
try {
|
|
4223
4288
|
if (headersJson && headersJson.trim() !== "{}") {
|
|
@@ -4247,12 +4312,20 @@ var executeHttpRequestTool = tool3(
|
|
|
4247
4312
|
params,
|
|
4248
4313
|
data,
|
|
4249
4314
|
validateStatus: () => true,
|
|
4250
|
-
// Don't throw on 4xx/5xx so agent can inspect response
|
|
4251
4315
|
timeout: 15e3
|
|
4252
4316
|
});
|
|
4253
4317
|
const elapsedMs = Date.now() - startTime;
|
|
4254
|
-
const
|
|
4255
|
-
|
|
4318
|
+
const methodBadge = formatMethodBadge(cleanMethod);
|
|
4319
|
+
const statusBadge = formatStatusBadge(response.status);
|
|
4320
|
+
const latencyStr = chalk4.dim(`${elapsedMs}ms`);
|
|
4321
|
+
let pathPreview = url;
|
|
4322
|
+
try {
|
|
4323
|
+
const u = new URL(url);
|
|
4324
|
+
pathPreview = u.pathname + (u.search || "");
|
|
4325
|
+
} catch {
|
|
4326
|
+
pathPreview = url;
|
|
4327
|
+
}
|
|
4328
|
+
console.log(` \u26A1 ${methodBadge} ${chalk4.white(pathPreview)} ${statusBadge} ${latencyStr}`);
|
|
4256
4329
|
const now = /* @__PURE__ */ new Date();
|
|
4257
4330
|
testExecutionLogs.push({
|
|
4258
4331
|
method: cleanMethod,
|
|
@@ -4277,6 +4350,7 @@ var executeHttpRequestTool = tool3(
|
|
|
4277
4350
|
2
|
|
4278
4351
|
);
|
|
4279
4352
|
} catch (err) {
|
|
4353
|
+
console.log(` \u2715 ${chalk4.bgRed.white(` ${cleanMethod} `)} ${chalk4.red(url)} ${chalk4.red(err.message)}`);
|
|
4280
4354
|
return JSON.stringify({
|
|
4281
4355
|
error: `Request failed: ${err.message}`,
|
|
4282
4356
|
status_code: 0
|
|
@@ -4299,7 +4373,7 @@ var executeHttpRequestTool = tool3(
|
|
|
4299
4373
|
// src/tools/postman.ts
|
|
4300
4374
|
import { tool as tool4 } from "@langchain/core/tools";
|
|
4301
4375
|
import axios4 from "axios";
|
|
4302
|
-
import
|
|
4376
|
+
import chalk5 from "chalk";
|
|
4303
4377
|
import fs from "fs";
|
|
4304
4378
|
import path from "path";
|
|
4305
4379
|
var exportPostmanCollectionTool = tool4(
|
|
@@ -4307,12 +4381,11 @@ var exportPostmanCollectionTool = tool4(
|
|
|
4307
4381
|
baseUrl,
|
|
4308
4382
|
outputFilename = "probe_collection.json"
|
|
4309
4383
|
}) => {
|
|
4310
|
-
console.log(chalk4.magenta(`
|
|
4311
|
-
\u{1F4E6} [Tool: export_postman_collection] Exporting Postman collection...`));
|
|
4312
4384
|
let targetUrl = baseUrl.replace(/\/+$/, "");
|
|
4313
4385
|
if (!targetUrl.endsWith("openapi.json")) {
|
|
4314
4386
|
targetUrl = `${targetUrl}/openapi.json`;
|
|
4315
4387
|
}
|
|
4388
|
+
logStep("\u{1F4E6}", "Generating Postman v2.1 Collection...", chalk5.dim(baseUrl));
|
|
4316
4389
|
try {
|
|
4317
4390
|
const res = await axios4.get(targetUrl, { timeout: 1e4 });
|
|
4318
4391
|
const spec = res.data;
|
|
@@ -4359,9 +4432,10 @@ var exportPostmanCollectionTool = tool4(
|
|
|
4359
4432
|
}
|
|
4360
4433
|
const outPath = path.join(reportsDir, path.basename(outputFilename));
|
|
4361
4434
|
fs.writeFileSync(outPath, JSON.stringify(postmanCollection, null, 2));
|
|
4362
|
-
|
|
4435
|
+
logStep("\u2713", `Saved Postman Collection (${postmanItems.length} routes)`, chalk5.hex("#10B981")(outPath));
|
|
4363
4436
|
return `Postman collection with ${postmanItems.length} endpoints successfully generated at '${outPath}'. You can import this directly into Postman/Bruno/Insomnia!`;
|
|
4364
4437
|
} catch (err) {
|
|
4438
|
+
logStep("\u2715", "Postman Export Failed", chalk5.red(err.message));
|
|
4365
4439
|
return `Failed to export Postman collection: ${err.message}`;
|
|
4366
4440
|
}
|
|
4367
4441
|
},
|
|
@@ -4377,7 +4451,7 @@ var exportPostmanCollectionTool = tool4(
|
|
|
4377
4451
|
|
|
4378
4452
|
// src/tools/report.ts
|
|
4379
4453
|
import { tool as tool5 } from "@langchain/core/tools";
|
|
4380
|
-
import
|
|
4454
|
+
import chalk6 from "chalk";
|
|
4381
4455
|
import fs2 from "fs";
|
|
4382
4456
|
import path2 from "path";
|
|
4383
4457
|
var saveHtmlAuditReportTool = tool5(
|
|
@@ -4478,8 +4552,7 @@ var saveHtmlAuditReportTool = tool5(
|
|
|
4478
4552
|
</html>
|
|
4479
4553
|
`;
|
|
4480
4554
|
fs2.writeFileSync(outPath, htmlContent);
|
|
4481
|
-
|
|
4482
|
-
\u{1F4CA} [Tool: save_html_audit_report] Interactive HTML Report saved to: ${outPath}`));
|
|
4555
|
+
logStep("\u{1F4CA}", "Generated HTML Audit Dashboard", chalk6.hex("#10B981")(outPath));
|
|
4483
4556
|
return `HTML audit report successfully generated at '${outPath}'. Open it in your browser to view the interactive dashboard!`;
|
|
4484
4557
|
},
|
|
4485
4558
|
{
|
|
@@ -4512,7 +4585,9 @@ function createProbeAgent(apiKey) {
|
|
|
4512
4585
|
const model = new ChatGoogleGenerativeAI({
|
|
4513
4586
|
model: "gemini-3.6-flash",
|
|
4514
4587
|
temperature: 0.2,
|
|
4515
|
-
apiKey
|
|
4588
|
+
apiKey,
|
|
4589
|
+
maxRetries: 3
|
|
4590
|
+
// Auto-retry on transient Google 503 spikes
|
|
4516
4591
|
}).bindTools(tools);
|
|
4517
4592
|
const callModel = async (state) => {
|
|
4518
4593
|
const response = await model.invoke(state.messages);
|
|
@@ -4539,9 +4614,10 @@ marked.use(
|
|
|
4539
4614
|
width: 90,
|
|
4540
4615
|
reflowText: true,
|
|
4541
4616
|
tab: 2,
|
|
4542
|
-
heading:
|
|
4543
|
-
firstHeading:
|
|
4544
|
-
strong:
|
|
4617
|
+
heading: chalk7.bold.hex("#6366F1"),
|
|
4618
|
+
firstHeading: chalk7.bold.hex("#A855F7"),
|
|
4619
|
+
strong: chalk7.bold.white,
|
|
4620
|
+
listitem: chalk7.cyan,
|
|
4545
4621
|
tableOptions: {
|
|
4546
4622
|
style: {
|
|
4547
4623
|
head: ["cyan", "bold"],
|
|
@@ -4550,28 +4626,8 @@ marked.use(
|
|
|
4550
4626
|
}
|
|
4551
4627
|
})
|
|
4552
4628
|
);
|
|
4553
|
-
function printBanner() {
|
|
4554
|
-
console.log(
|
|
4555
|
-
boxen(
|
|
4556
|
-
`${chalk6.bold.cyan("\u{1F50D} ProbeAI \u2014 Autonomous API Reliability & Postman Suite Generator")}
|
|
4557
|
-
${chalk6.dim("TypeScript CLI Edition \u2022 Dynamic Auth \u2022 Postman v2.1 Exporter \u2022 HTML Reports")}
|
|
4558
|
-
|
|
4559
|
-
${chalk6.yellow("Try prompts like:")}
|
|
4560
|
-
\u2022 'Inspect https://bookmark-agent-backend.onrender.com and export a Postman collection'
|
|
4561
|
-
\u2022 'Test all endpoints on https://bookmark-agent-backend.onrender.com and save an HTML report'
|
|
4562
|
-
\u2022 'Set auth Bearer my_secret_token and test protected routes'
|
|
4563
|
-
\u2022 Type ${chalk6.bold.red("'exit'")} to quit`,
|
|
4564
|
-
{
|
|
4565
|
-
padding: 1,
|
|
4566
|
-
margin: 1,
|
|
4567
|
-
borderStyle: "round",
|
|
4568
|
-
borderColor: "cyan"
|
|
4569
|
-
}
|
|
4570
|
-
)
|
|
4571
|
-
);
|
|
4572
|
-
}
|
|
4573
4629
|
async function main() {
|
|
4574
|
-
|
|
4630
|
+
printModernBanner("1.0.4");
|
|
4575
4631
|
const rl = readline.createInterface({
|
|
4576
4632
|
input: process.stdin,
|
|
4577
4633
|
output: process.stdout
|
|
@@ -4585,30 +4641,33 @@ async function main() {
|
|
|
4585
4641
|
};
|
|
4586
4642
|
let apiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
|
|
4587
4643
|
if (!apiKey) {
|
|
4588
|
-
console.log(
|
|
4589
|
-
console.log(
|
|
4590
|
-
apiKey = await promptUser(
|
|
4644
|
+
console.log(chalk7.yellow("\u{1F511} No GEMINI_API_KEY found in your environment or .env file."));
|
|
4645
|
+
console.log(chalk7.dim("Get a free key from https://aistudio.google.com/\n"));
|
|
4646
|
+
apiKey = await promptUser(chalk7.bold.hex("#6366F1")("Enter your Gemini API Key: "));
|
|
4591
4647
|
if (!apiKey) {
|
|
4592
|
-
console.log(
|
|
4648
|
+
console.log(chalk7.red("\nError: An API key is required to run ProbeAI. Exiting...\n"));
|
|
4593
4649
|
rl.close();
|
|
4594
4650
|
process.exit(1);
|
|
4595
4651
|
}
|
|
4596
|
-
console.log(
|
|
4652
|
+
console.log(chalk7.hex("#10B981")("\u2713 API Key registered for this session.\n"));
|
|
4597
4653
|
}
|
|
4598
4654
|
const app = createProbeAgent(apiKey);
|
|
4599
4655
|
const messages = [PROBE_SYSTEM_MESSAGE];
|
|
4600
4656
|
while (true) {
|
|
4601
4657
|
try {
|
|
4602
|
-
const
|
|
4658
|
+
const promptSymbol = chalk7.bold.hex("#6366F1")("\u276F ");
|
|
4659
|
+
const input = await promptUser(`
|
|
4660
|
+
${promptSymbol}`);
|
|
4603
4661
|
if (!input) continue;
|
|
4604
4662
|
if (input.toLowerCase() === "exit" || input.toLowerCase() === "quit") {
|
|
4605
|
-
console.log(
|
|
4663
|
+
console.log(chalk7.hex("#6366F1")("\nGoodbye! \u{1F44B}\n"));
|
|
4606
4664
|
rl.close();
|
|
4607
4665
|
process.exit(0);
|
|
4608
4666
|
}
|
|
4667
|
+
console.log();
|
|
4609
4668
|
messages.push(new HumanMessage(input));
|
|
4610
4669
|
const spinner = ora({
|
|
4611
|
-
text:
|
|
4670
|
+
text: chalk7.dim("Synthesizing test scenarios & auditing schemas..."),
|
|
4612
4671
|
color: "cyan"
|
|
4613
4672
|
}).start();
|
|
4614
4673
|
let finalResponse = "";
|
|
@@ -4623,12 +4682,16 @@ async function main() {
|
|
|
4623
4682
|
}
|
|
4624
4683
|
spinner.stop();
|
|
4625
4684
|
if (finalResponse) {
|
|
4626
|
-
console.log(
|
|
4685
|
+
console.log(
|
|
4686
|
+
"\n" + chalk7.hex("#6366F1")(
|
|
4687
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 ProbeAI Assessment \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"
|
|
4688
|
+
)
|
|
4689
|
+
);
|
|
4627
4690
|
const rendered = marked(finalResponse);
|
|
4628
4691
|
console.log(rendered);
|
|
4629
4692
|
}
|
|
4630
4693
|
} catch (err) {
|
|
4631
|
-
console.log(
|
|
4694
|
+
console.log(chalk7.red(`
|
|
4632
4695
|
An error occurred: ${err.message}`));
|
|
4633
4696
|
}
|
|
4634
4697
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kamrankhan027/probe-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Autonomous API Schema & Reliability Inspector CLI. Probes OpenAPI specs, generates test suites, executes real-time HTTP calls, and exports Postman collections.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"bin":
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"probe-ai": "./dist/index.js"
|
|
9
|
+
},
|
|
8
10
|
"files": [
|
|
9
11
|
"dist"
|
|
10
12
|
],
|
|
11
13
|
"scripts": {
|
|
12
14
|
"build": "tsup",
|
|
13
15
|
"dev": "tsup --watch",
|
|
14
|
-
"start": "node dist/index.js"
|
|
16
|
+
"start": "node ./dist/index.js"
|
|
15
17
|
},
|
|
16
18
|
"keywords": [
|
|
17
19
|
"ai",
|