node-red-contrib-vectorprime 0.1.40 → 0.1.42

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 CHANGED
@@ -1,70 +1,57 @@
1
1
  # node-red-contrib-vectorprime
2
+ ### VectorPrime — Decision Ranking for Node-RED
2
3
 
3
- **VectorPrime Rank** automatically rank tasks, alerts, or decisions using the VectorPrime Decision Kernel (**Contract v1**).
4
+ **node-red-contrib-vectorprime** lets you rank tasks, alerts, or actions inside Node-RED when multiple options compete.
4
5
 
5
- This node helps you pick the **best next action** when you have multiple choices — **without faking certainty**.
6
-
7
- ✅ Prioritize tasks
8
- ✅ Rank alerts by urgency
9
- ✅ Choose the best option from multiple choices
10
- ✅ Turn “too many choices” into a safe automation decision state
11
- ✅ Works inside Node-RED flows with 1 click
12
-
13
- ---
14
-
15
- ## What it does (in 1 sentence)
16
-
17
- **Send a list of options → get them ranked best-to-worst with probabilities + metadata you can branch on.**
6
+ Instead of hard-coding rules, VectorPrime helps flows choose the **safest, most urgent, or most rational next step** automatically.
18
7
 
19
8
  ---
20
9
 
21
- ## Why developers install this
10
+ ## 🧠 What this node does (plain English)
22
11
 
23
- Most automation flows break down when you have **multiple possible actions** and don’t know which one to run.
12
+ You send VectorPrime a list of options (tasks, actions, alerts). Each option can include simple metadata like urgency, impact, deadline, or effort.
24
13
 
25
- VectorPrime solves that by turning this:
14
+ VectorPrime returns:
15
+ - A ranked list (best → worst)
16
+ - Probabilities / confidence signals (when provided)
17
+ - Metadata about the ranking
26
18
 
27
- - “Which alert should fire first?”
28
- - “Which ticket should I do next?”
29
- - “Which customer should get priority?”
30
- - “Which action gives the best outcome with least risk?”
31
- - “Which fix should I deploy first?”
32
-
33
- Into:
34
-
35
- ✅ A **ranked result** when the input supports a winner
36
- ✅ Or a **tie / no-dominant outcome** (so your flow can branch safely)
19
+ This turns Node-RED into a **decision-aware automation engine**, not just `if/else`.
37
20
 
38
21
  ---
39
22
 
40
- ## Nodes Included
23
+ ## 📦 Nodes included
41
24
 
42
25
  - **VectorPrime Config**
43
- Stores your VectorPrime Base URL + API Key.
26
+ Stores Base URL + API key securely using Node-RED credentials (not exported with flows).
44
27
 
45
- - **VectorPrime Rank**
46
- Sends options to VectorPrime (`/v1/kernel/rank`) and returns the ranked result.
28
+ - **Rank Decision (VectorPrime)**
29
+ Sends options to the VectorPrime Rank API and returns ranked results.
47
30
 
48
31
  ---
49
32
 
50
- ## Install
33
+ ## ⚡ Quick start (5 minutes)
51
34
 
52
- ### Option A: Install from the Node-RED palette (recommended)
53
- 1) Open Node-RED in your browser
54
- 2) Click the menu (top-right) ☰
55
- 3) Click **Manage palette**
56
- 4) Click the **Install** tab
57
- 5) Search: `node-red-contrib-vectorprime`
58
- 6) Click **Install**
35
+ 1. Open Node-RED
36
+ 2. Menu **Manage Palette** → **Install**
37
+ 3. Search for: **node-red-contrib-vectorprime**
38
+ 4. Install
39
+ 5. Drag **Rank Decision (VectorPrime)** into a flow
40
+ 6. Create/select a **VectorPrime Config** and click **Get Free Key**
41
+ 7. Inject one of the examples below into `msg.payload`
59
42
 
60
- ### Option B: Install from terminal (Node-RED user directory)
61
-
62
- Open your Node-RED user directory:
43
+ ---
63
44
 
64
- - **Windows:** `C:\Users\<you>\.node-red`
65
- - **macOS/Linux:** `~/.node-red`
45
+ ## Input format (simple) — `items[]` (recommended)
66
46
 
67
- Then run:
47
+ The node will automatically convert `items[]` into the backend’s required `options[]` format.
68
48
 
69
- ```bash
70
- npm install node-red-contrib-vectorprime
49
+ ```json
50
+ {
51
+ "items": [
52
+ { "id": "payroll", "label": "Fix payroll outage", "urgency": 10, "impact": 10, "effort": 3 },
53
+ { "id": "email", "label": "Reply to customer email", "urgency": 4, "impact": 5, "effort": 2 },
54
+ { "id": "deploy", "label": "Ship small hotfix", "urgency": 7, "impact": 7, "effort": 4 }
55
+ ],
56
+ "prompt": "Rank the best next action."
57
+ }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "node-red-contrib-vectorprime",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "Rank tasks, alerts, or actions inside Node-RED when multiple options compete. Choose the safest next step automatically instead of hard-coded rules.",
5
5
  "license": "MIT",
6
6
  "author": "Faisal Khan",
7
7
  "homepage": "https://gitlab.com/faisalkhan3000/vectorprime-node-red-node",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://gitlab.com/faisalkhan3000/vectorprime-node-red-node.git"
10
+ "url": "git+https://gitlab.com/faisalkhan3000/vectorprime-node-red-node.git"
11
11
  },
12
12
  "bugs": {
13
13
  "url": "https://gitlab.com/faisalkhan3000/vectorprime-node-red-node/-/issues"
package/vectorprime.html CHANGED
@@ -1,5 +1,6 @@
1
1
  <!-- =========================
2
2
  VectorPrime Config Template
3
+ WEBSITE-ONLY KEYS (no minting inside Node-RED)
3
4
  ========================= -->
4
5
  <script type="text/x-red" data-template-name="vectorprime-config">
5
6
  <div class="form-row">
@@ -9,26 +10,19 @@
9
10
  <input type="text" id="node-config-input-baseUrl" placeholder="https://vectorprime-kernel-backend.onrender.com">
10
11
  </div>
11
12
 
12
- <!-- ✅ REQUIRED: email for /v1/billing/signup -->
13
- <div class="form-row">
14
- <label for="node-config-input-email">
15
- <i class="fa fa-envelope"></i> Email
16
- </label>
17
- <input type="text" id="node-config-input-email" placeholder="you@example.com">
18
- </div>
19
-
20
13
  <div class="form-row">
21
14
  <label for="node-config-input-apiKey">
22
15
  <i class="fa fa-key"></i> API Key
23
16
  </label>
24
- <input type="password" id="node-config-input-apiKey" placeholder="vp_free_...">
17
+ <input type="password" id="node-config-input-apiKey" placeholder="vp_live_...">
25
18
  </div>
26
19
 
27
20
  <div class="form-row">
28
- <button id="vp-get-free-key" class="red-ui-button">
29
- Get Free Key
30
- </button>
31
- <span id="vp-key-status" style="margin-left:10px; font-size:12px;"></span>
21
+ <div style="font-size:12px; color:#666; line-height:1.4;">
22
+ Get your API key from
23
+ <a href="https://vectorprime.tech" target="_blank" rel="noopener noreferrer">vectorprime.tech</a>,
24
+ then paste it above.
25
+ </div>
32
26
  </div>
33
27
 
34
28
  <div class="form-row">
@@ -50,8 +44,7 @@
50
44
  RED.nodes.registerType("vectorprime-config", {
51
45
  category: "config",
52
46
  defaults: {
53
- baseUrl: { value: "https://vectorprime-kernel-backend.onrender.com", required: true },
54
- email: { value: "", required: true }
47
+ baseUrl: { value: "https://vectorprime-kernel-backend.onrender.com", required: true }
55
48
  },
56
49
  credentials: {
57
50
  apiKey: { type: "password" }
@@ -60,65 +53,16 @@
60
53
  return "VectorPrime Config";
61
54
  },
62
55
  oneditprepare: function () {
63
- const statusEl = document.getElementById("vp-key-status");
64
- const btn = document.getElementById("vp-get-free-key");
65
-
66
- btn.onclick = async function (e) {
67
- e.preventDefault();
68
- statusEl.textContent = "Requesting free key...";
69
- statusEl.style.color = "#444";
70
-
71
- try {
72
- const baseUrlInput = document.getElementById("node-config-input-baseUrl");
73
- const emailInput = document.getElementById("node-config-input-email");
74
- const apiKeyInput = document.getElementById("node-config-input-apiKey");
75
-
76
- const baseUrl = normalizeBaseUrl(baseUrlInput.value);
77
- const email = ((emailInput && emailInput.value) ? emailInput.value : "").trim();
78
-
79
- if (!baseUrl.startsWith("http")) {
80
- statusEl.textContent = "❌ Base URL missing/invalid";
81
- statusEl.style.color = "red";
82
- return;
83
- }
84
-
85
- if (!email || !email.includes("@")) {
86
- statusEl.textContent = "❌ Enter a valid email";
87
- statusEl.style.color = "red";
88
- return;
89
- }
90
-
91
- // ✅ Call REAL backend endpoint that exists
92
- const resp = await fetch(`${baseUrl}/v1/billing/signup`, {
93
- method: "POST",
94
- headers: { "Content-Type": "application/json" },
95
- body: JSON.stringify({ email: email })
96
- });
97
-
98
- const text = await resp.text();
99
- let data;
100
- try { data = JSON.parse(text); } catch { data = { raw: text }; }
101
-
102
- if (!resp.ok) {
103
- statusEl.textContent = `❌ Failed (${resp.status}): ${data.message || data.error || data.detail || "unknown"}`;
104
- statusEl.style.color = "red";
105
- return;
106
- }
107
-
108
- if (!data.api_key) {
109
- statusEl.textContent = "❌ Backend did not return api_key";
110
- statusEl.style.color = "red";
111
- return;
112
- }
113
-
114
- apiKeyInput.value = data.api_key;
115
- statusEl.textContent = "✅ Free key generated + saved";
116
- statusEl.style.color = "green";
117
- } catch (err) {
118
- statusEl.textContent = `❌ Error: ${err.message}`;
119
- statusEl.style.color = "red";
120
- }
121
- };
56
+ const baseUrlInput = document.getElementById("node-config-input-baseUrl");
57
+ if (baseUrlInput && baseUrlInput.value) {
58
+ baseUrlInput.value = normalizeBaseUrl(baseUrlInput.value);
59
+ }
60
+ },
61
+ oneditsave: function () {
62
+ const baseUrlInput = document.getElementById("node-config-input-baseUrl");
63
+ if (baseUrlInput && baseUrlInput.value) {
64
+ baseUrlInput.value = normalizeBaseUrl(baseUrlInput.value);
65
+ }
122
66
  }
123
67
  });
124
68
  })();
@@ -131,8 +75,8 @@
131
75
  <h3>Quick Setup</h3>
132
76
  <ol>
133
77
  <li>Set <b>Base URL</b> (default is already correct)</li>
134
- <li>Enter your <b>Email</b></li>
135
- <li>Click <b>Get Free Key</b></li>
78
+ <li>Get your API key from <b>vectorprime.tech</b></li>
79
+ <li>Paste the key into <b>API Key</b></li>
136
80
  <li>Click <b>Done</b></li>
137
81
  </ol>
138
82
 
@@ -142,11 +86,6 @@
142
86
 
143
87
  <h3>Recommended Base URL</h3>
144
88
  <pre>https://vectorprime-kernel-backend.onrender.com</pre>
145
-
146
- <h3>Tip</h3>
147
- <p>
148
- You can also paste your own paid key here if you want higher limits.
149
- </p>
150
89
  </script>
151
90
 
152
91
  <hr />
package/vectorprime.js CHANGED
@@ -25,10 +25,6 @@ module.exports = function (RED) {
25
25
  return Number.isFinite(n) ? n : null;
26
26
  }
27
27
 
28
- function almostEqual(a, b, eps = 1e-9) {
29
- return Math.abs(a - b) <= eps;
30
- }
31
-
32
28
  function isFlatProbabilities(probObj) {
33
29
  if (!probObj || typeof probObj !== "object") return false;
34
30
  const keys = Object.keys(probObj);
@@ -87,6 +83,7 @@ module.exports = function (RED) {
87
83
 
88
84
  // -----------------------------
89
85
  // Config Node (stores Base URL + API Key)
86
+ // WEBSITE-ONLY KEYS: no email, no "Get Free Key" minting
90
87
  // -----------------------------
91
88
  function VectorPrimeConfigNode(n) {
92
89
  RED.nodes.createNode(this, n);
@@ -95,9 +92,8 @@ module.exports = function (RED) {
95
92
  (n.baseUrl && String(n.baseUrl).trim()) ||
96
93
  "https://vectorprime-kernel-backend.onrender.com";
97
94
 
98
- // NOTE: email is stored as a normal config property (not credentials).
99
- // Runtime node doesn't need it; editor uses it to request keys.
100
- this.email = (n.email && String(n.email).trim()) || "";
95
+ // NOTE: apiKey is stored in credentials only.
96
+ // No email stored here anymore (website-only keys).
101
97
  }
102
98
 
103
99
  RED.nodes.registerType("vectorprime-config", VectorPrimeConfigNode, {
@@ -145,7 +141,7 @@ module.exports = function (RED) {
145
141
  if (!storedKey) {
146
142
  node.status({ fill: "red", shape: "ring", text: "missing api key" });
147
143
  throw new Error(
148
- "No API key. Open VectorPrime Config and click 'Get Free Key' or paste your paid key."
144
+ "No API key. Get your key from vectorprime.tech and paste it into the VectorPrime Config."
149
145
  );
150
146
  }
151
147
 
@@ -214,15 +210,13 @@ module.exports = function (RED) {
214
210
  }
215
211
 
216
212
  // -----------------------------
217
- // ✅ FIX: If backend returns a tie/flat result, do deterministic local ranking
218
- // This prevents users from seeing "random" / "input-order" ranking.
213
+ // ✅ If backend returns a tie/flat result, do deterministic local ranking
219
214
  // -----------------------------
220
215
  const tieBreak =
221
216
  data && data.meta && typeof data.meta === "object"
222
217
  ? data.meta.tie_break
223
218
  : null;
224
219
 
225
- const hasRanking = Array.isArray(data && data.ranking);
226
220
  const hasProb = data && data.probabilities;
227
221
 
228
222
  const looksTied =
@@ -269,4 +263,4 @@ module.exports = function (RED) {
269
263
  }
270
264
 
271
265
  RED.nodes.registerType("vectorprime-rank", VectorPrimeRankNode);
272
- };
266
+ };