@com55/node-red-ai-access 0.2.0-beta.4 → 0.2.0-beta.5

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/plugin.d.cts CHANGED
@@ -268,6 +268,11 @@ interface Proposal {
268
268
  * Never blocks approve — AI may scaffold first; humans configure later.
269
269
  */
270
270
  warnings?: ProposalWarning[];
271
+ /** Fields whose `[REDACTED]` placeholder was restored to the live secret at submit. */
272
+ restoredSecrets?: {
273
+ nodeId: string;
274
+ property: string;
275
+ }[];
271
276
  /**
272
277
  * Short human title (≤80 chars). Set at submit (AI-supplied or auto-generated).
273
278
  * Optional on legacy proposals; API responses always resolve a title.
@@ -291,6 +296,10 @@ interface CreateProposalInput {
291
296
  title: string;
292
297
  /** Soft validation notes to persist with the proposal. */
293
298
  warnings?: ProposalWarning[];
299
+ restoredSecrets?: {
300
+ nodeId: string;
301
+ property: string;
302
+ }[];
294
303
  }
295
304
  declare class ProposalStore {
296
305
  private readonly paths;
@@ -355,6 +364,8 @@ interface PersistedAiAccessSettings {
355
364
  rateLimitWindowMs?: number;
356
365
  rateLimitMaxPerIp?: number;
357
366
  rateLimitMaxPerToken?: number;
367
+ /** Secret-like env / config names redacted from AI responses (case-insensitive words). */
368
+ secretNamePatterns?: string[];
358
369
  oauth?: {
359
370
  enabled?: boolean;
360
371
  accessTokenTtlSec?: number;
@@ -374,6 +385,7 @@ interface EffectiveAiAccessSettings {
374
385
  rateLimitWindowMs: number;
375
386
  rateLimitMaxPerIp: number;
376
387
  rateLimitMaxPerToken: number;
388
+ secretNamePatterns: string[];
377
389
  oauth: {
378
390
  enabled: boolean;
379
391
  accessTokenTtlSec: number;
@@ -401,8 +413,11 @@ declare class SettingsStore {
401
413
  * disk override and fall back to settings.js / default).
402
414
  */
403
415
  update(partial: PersistedAiAccessSettings): EffectiveAiAccessSettingsWithMeta;
404
- /** Merge into existing disk overrides (only provided keys change). */
405
- patch(partial: PersistedAiAccessSettings): EffectiveAiAccessSettingsWithMeta;
416
+ /**
417
+ * Merge into existing disk overrides (only provided keys change). Keys in
418
+ * `reset` are removed from disk so they fall back to settings.js / default.
419
+ */
420
+ patch(partial: PersistedAiAccessSettings, reset?: readonly (keyof PersistedAiAccessSettings)[]): EffectiveAiAccessSettingsWithMeta;
406
421
  private persist;
407
422
  }
408
423
 
@@ -1505,6 +1505,7 @@ components:
1505
1505
  - `chunk_conflict` (409): Same draft seq re-sent with different data
1506
1506
  - `missing_chunks` (400): Draft submit with gaps in seq
1507
1507
  - `draft_hash_mismatch` (400): Draft sha256 does not match assembled bytes
1508
+ - `redacted_placeholder_mismatch` (400): A `[REDACTED]` placeholder was added, duplicated, moved, or its env/header name changed
1508
1509
  - `upstream_error` (502): Node-RED runtime/flows error
1509
1510
  - `internal_error` (500): Unexpected server error
1510
1511
  enum:
@@ -1557,6 +1558,7 @@ components:
1557
1558
  - chunk_conflict
1558
1559
  - missing_chunks
1559
1560
  - draft_hash_mismatch
1561
+ - redacted_placeholder_mismatch
1560
1562
  - upstream_error
1561
1563
  - internal_error
1562
1564
  # END GENERATED ERROR CODES
package/docs/errors.md CHANGED
@@ -62,6 +62,7 @@ GET-link `401` responses include a `WWW-Authenticate: Bearer …` header.
62
62
  | `chunk_conflict` | 409 | Same draft seq re-sent with different data | Re-send identical data or start a new draft | GET |
63
63
  | `missing_chunks` | 400 | Draft submit with gaps in seq | Upload every seq from 0 through max | GET |
64
64
  | `draft_hash_mismatch` | 400 | Draft sha256 does not match assembled bytes | Send the hex sha256 of the assembled UTF-8 | GET |
65
+ | `redacted_placeholder_mismatch` | 400 | A `[REDACTED]` placeholder was added, duplicated, moved, or its env/header name changed | Keep placeholders exactly where they were shown (same line, same name) or supply a real value | GET, REST, MCP |
65
66
  | `upstream_error` | 502 | Node-RED runtime/flows error | Retry; check Node-RED logs | GET, REST, MCP |
66
67
  | `internal_error` | 500 | Unexpected server error | Retry; check Node-RED logs | GET, REST, MCP |
67
68
 
@@ -74,6 +75,13 @@ Returned in `warnings[]` on a successful propose/submit; they never block.
74
75
  | `missing_config_node` | 201 | Warning: node has no (or an unresolved) config node yet | Configure before deploy if needed; see `candidates` | GET, REST, MCP |
75
76
  | `unwired_node` | 201 | Warning: node has outputs but no wires | Wire it, or ignore while scaffolding | GET, REST, MCP |
76
77
  | `incomplete_wires` | 201 | Warning: fewer wire ports than outputs | Add ports if outputs should connect | GET, REST, MCP |
78
+ | `secret_destination_changed` | 201 | Warning: a node holding a restored secret changed where it sends data (url/host/method/headers/…) | Reviewer must verify the new destination before approving | GET, REST, MCP |
79
+ | `secret_node_code_changed` | 201 | Warning: a node holding a restored secret had its code changed | Reviewer must check the code does not send the secret elsewhere | GET, REST, MCP |
80
+ | `secret_wires_changed` | 201 | Warning: a node holding a restored secret had its output wires changed | Reviewer must check where its messages now go | GET, REST, MCP |
81
+ | `secret_scope_changed` | 201 | Warning: tab/group env or a config node scoping a secret-holding node changed | Reviewer must check env-resolved hosts/URLs (e.g. `${API_HOST}`) | GET, REST, MCP |
82
+ | `outbound_node_changed` | 201 | Warning: proposal adds/changes an outbound-capable node (http request/response, mqtt, tcp, udp, email, websocket, link out/call, function, exec, file, or any node with url/host fields) | Reviewer must check it cannot send secret data elsewhere | GET, REST, MCP |
83
+ | `secret_tab_changed` | 201 | Warning: proposal with restored secrets also changes other nodes in the tab | Review every change before approving | GET, REST, MCP |
84
+ | `secret_reference_added` | 201 | Warning: an added/changed node now reads a secret-named env/global/flow value (env.get, $env, ${…}, typed input) | Reviewer must check the value is not sent anywhere unexpected | GET, REST, MCP |
77
85
 
78
86
  ## Auto-closed proposal statuses
79
87
 
package/docs/mcp-tools.md CHANGED
@@ -69,7 +69,7 @@ _No input parameters._
69
69
 
70
70
  ## `list_flows`
71
71
 
72
- List all flows as a flat Admin API v2 array plus the current rev string. Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches.
72
+ List all flows as a flat Admin API v2 array plus the current rev string. Likely secrets are shown as "[REDACTED]" (nodes list redacted paths in _aiRedactions). Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches.
73
73
 
74
74
  _No input parameters._
75
75
 
@@ -126,7 +126,7 @@ Search the public Node-RED catalogue for modules to recommend. Returns module id
126
126
 
127
127
  ## `submit_proposal`
128
128
 
129
- Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } — find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (≤80 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Payload size limit is 1500000 bytes (413 if exceeded). expectedRev is checked against the live rev at submit time (409 if stale). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches — call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, …) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl — not the raw id. Humans approve on the /ai-access dashboard (reviewUrl opens/scrolls to it).
129
+ Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } — find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (≤80 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Secret values appear as "[REDACTED]" in reads; leave the placeholder unchanged to keep the stored secret (it is restored on submit), or replace it with a new value to change it. Payload size limit is 1500000 bytes (413 if exceeded). expectedRev is checked against the live rev at submit time (409 if stale). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches — call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, …) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl — not the raw id. Humans approve on the /ai-access dashboard (reviewUrl opens/scrolls to it).
130
130
 
131
131
  | Parameter | Required | Type | Description |
132
132
  | --- | --- | --- | --- |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@com55/node-red-ai-access",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.5",
4
4
  "description": "Node-RED plugin that gives AI agents temporary, owner-approved access via REST and MCP",
5
5
  "main": "dist/plugin.cjs",
6
6
  "types": "dist/plugin.d.cts",