@com55/node-red-ai-access 0.2.0-beta.6 → 0.2.0-beta.8

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
@@ -217,7 +217,34 @@ declare class AuditLog {
217
217
  * Proposal JSON store under `.ai-access/proposals/`.
218
218
  */
219
219
 
220
- type ProposalStatus = 'pending' | 'approved' | 'rejected' | 'failed' | 'withdrawn' | 'expired' | 'superseded';
220
+ type ProposalStatus = 'pending' | 'conflict' | 'approved' | 'rejected' | 'failed' | 'withdrawn' | 'expired' | 'superseded';
221
+ /** One stored rebase conflict (field paths only, including env names / credential keys; never values). */
222
+ interface StoredRebaseConflict {
223
+ kind: string;
224
+ nodeId?: string;
225
+ nodeType?: string;
226
+ nodeName?: string;
227
+ fields?: string[];
228
+ message: string;
229
+ choices: ('live' | 'proposed')[];
230
+ }
231
+ /** Paths where an AI conflict choice replaced a live editor change. */
232
+ interface AiConflictOverride {
233
+ nodeId: string;
234
+ fields: string[];
235
+ }
236
+ /** Result of the last auto-rebase check against the live flows. */
237
+ interface ProposalRebaseInfo {
238
+ /** Live rev the check ran against. */
239
+ rev: string;
240
+ state: 'clean' | 'merged' | 'conflict';
241
+ /** Hash of the tab approve would deploy (absent on conflict). */
242
+ mergedHash?: string;
243
+ conflicts: StoredRebaseConflict[];
244
+ /** Nodes (or the tab) changed live since the base snapshot. */
245
+ liveChangedCount: number;
246
+ checkedAt: string;
247
+ }
221
248
  type ProposalType = 'replace_flow';
222
249
  interface AccessLinkProposalCreator {
223
250
  kind: 'access-link';
@@ -273,6 +300,28 @@ interface Proposal {
273
300
  nodeId: string;
274
301
  property: string;
275
302
  }[];
303
+ /** Persisted while a timed-out runtime deployment has an unknown outcome. */
304
+ deployPending?: {
305
+ mergedHash: string;
306
+ reason: string;
307
+ queueReleased?: boolean;
308
+ };
309
+ /** Last auto-rebase result (pending / conflict proposals). */
310
+ rebase?: ProposalRebaseInfo;
311
+ /** AI resolutions that replace editor changes and still require reviewer acknowledgement. */
312
+ aiConflictOverrides?: AiConflictOverride[];
313
+ /** Latest manual resolution, retained for admin undo until the next body edit. */
314
+ conflictChoice?: {
315
+ nodeId: string;
316
+ choice: 'live' | 'proposed';
317
+ chooser?: 'ai' | 'human';
318
+ undo?: {
319
+ baseFlow: Partial<FlowTab>;
320
+ flow: Partial<FlowTab>;
321
+ warnings: ProposalWarning[];
322
+ aiConflictOverrides?: AiConflictOverride[];
323
+ };
324
+ };
276
325
  /**
277
326
  * Short human title (≤80 chars). Set at submit (AI-supplied or auto-generated).
278
327
  * Optional on legacy proposals; API responses always resolve a title.
@@ -329,15 +378,40 @@ declare class ProposalStore {
329
378
  */
330
379
  beginApprove(id: string): Proposal;
331
380
  releaseLock(id: string): void;
381
+ recordDeployPending(id: string, pending: Proposal['deployPending']): Proposal;
332
382
  markApproved(id: string, decidedBy: ProposalDecisionBy, resultRev: string): Proposal;
333
383
  markRejected(id: string, decidedBy: ProposalDecisionBy | undefined, reason: string): Proposal;
334
384
  markFailed(id: string, decidedBy: ProposalDecisionBy | undefined, reason: string): Proposal;
335
385
  markWithdrawn(id: string, decidedBy: ProposalDecisionBy, reason: string): Proposal;
336
386
  /**
337
387
  * Auto-close a pending proposal that can no longer be approved
338
- * (`expired` = live rev moved; `superseded` = another proposal was approved).
388
+ * (`expired` = pending longer than the proposal TTL).
339
389
  */
340
390
  markClosed(id: string, status: 'expired' | 'superseded', reason: string, decidedBy?: ProposalDecisionBy): Proposal;
391
+ /**
392
+ * Store the latest rebase check and flip `pending` ↔ `conflict`.
393
+ * No-op (no write) when nothing changed. Skips decided or in-flight records.
394
+ */
395
+ recordRebase(id: string, info: ProposalRebaseInfo): Proposal;
396
+ /**
397
+ * Replace an open proposal's base snapshot and/or body (conflict resolution).
398
+ * Status returns to `pending`; the caller re-runs the rebase.
399
+ */
400
+ updateBody(id: string, update: {
401
+ baseFlow: Partial<FlowTab>;
402
+ flow: Partial<FlowTab>;
403
+ expectedRev?: string;
404
+ title?: string;
405
+ restoredSecrets?: {
406
+ nodeId: string;
407
+ property: string;
408
+ }[];
409
+ warnings?: ProposalWarning[];
410
+ conflictChoice?: Proposal['conflictChoice'];
411
+ aiConflictOverrides?: AiConflictOverride[];
412
+ }): Proposal;
413
+ /** True while an approve holds this proposal's lock. */
414
+ isApproving(id: string): boolean;
341
415
  }
342
416
 
343
417
  /**
@@ -357,6 +431,7 @@ interface PersistedAiAccessSettings {
357
431
  enabled?: boolean;
358
432
  allowWithoutAdminAuth?: boolean;
359
433
  defaultLinkTtlSeconds?: number;
434
+ proposalTtlDays?: number;
360
435
  defaultDeploymentType?: DeploymentType;
361
436
  maxActiveLinks?: number;
362
437
  publicBaseUrl?: string;
@@ -378,6 +453,7 @@ interface EffectiveAiAccessSettings {
378
453
  enabled: boolean;
379
454
  allowWithoutAdminAuth: boolean;
380
455
  defaultLinkTtlSeconds: number;
456
+ proposalTtlDays: number;
381
457
  defaultDeploymentType: DeploymentType;
382
458
  maxActiveLinks: number;
383
459
  publicBaseUrl: string;
@@ -463,15 +539,32 @@ type CatalogueFetcher = (url: string, etag?: string) => Promise<{
463
539
  * Approve / reject proposals through the FlowAdapter with audit logging.
464
540
  */
465
541
 
542
+ interface ProposalServiceOptions {
543
+ /** Maximum time to await updateFlow (default 60 seconds). Does not cancel the runtime operation. */
544
+ deployTimeoutMs?: number;
545
+ /** Maximum time waiting to start approval (default 60 seconds). */
546
+ approveQueueTimeoutMs?: number;
547
+ /** Release the queue after this much unsettled deployment time (default 5 minutes). */
548
+ deployHardCapMs?: number;
549
+ }
550
+ interface ApproveOptions {
551
+ deploymentType?: DeploymentType;
552
+ /**
553
+ * `mergedHash` the reviewer last saw. Required when the rebase merged live
554
+ * changes into the proposal; when supplied it must match the current merge.
555
+ */
556
+ mergedHash?: string;
557
+ }
466
558
  declare class ProposalService {
467
559
  private readonly proposals;
468
560
  private readonly audit;
469
561
  private readonly adapter;
470
562
  private readonly getDeploymentType;
471
- constructor(proposals: ProposalStore, audit: AuditLog, adapter: FlowAdapter, getDeploymentType: () => DeploymentType);
472
- approve(id: string, decidedBy: ProposalDecisionBy, options?: {
473
- deploymentType?: DeploymentType;
474
- }): Promise<Proposal>;
563
+ private readonly serviceOptions;
564
+ constructor(proposals: ProposalStore, audit: AuditLog, adapter: FlowAdapter, getDeploymentType: () => DeploymentType, serviceOptions?: ProposalServiceOptions);
565
+ approve(id: string, decidedBy: ProposalDecisionBy, options?: ApproveOptions): Promise<Proposal>;
566
+ private approveLocked;
567
+ private withDeployDeadline;
475
568
  reject(id: string, decidedBy: ProposalDecisionBy, reason: string): Proposal;
476
569
  }
477
570
 
@@ -495,10 +588,17 @@ interface AiAccessSettings {
495
588
  allowWithoutAdminAuth?: boolean;
496
589
  /** Default access-link TTL in seconds (default: 14400 = 4h). */
497
590
  defaultLinkTtlSeconds?: number;
591
+ proposalTtlDays?: number;
498
592
  /** Maximum concurrent active access links (default: 10). */
499
593
  maxActiveLinks?: number;
500
594
  /** Deployment type used when approving proposals (default: nodes). */
501
595
  defaultDeploymentType?: 'nodes' | 'flows' | 'full';
596
+ /** Approval queue waiting deadline in milliseconds (default 60,000). */
597
+ approveQueueTimeoutMs?: number;
598
+ /** Deployment caller deadline in milliseconds (default 60,000). */
599
+ deployTimeoutMs?: number;
600
+ /** Release an unsettled deployment's approval queue after this duration (default 300,000 ms). */
601
+ deployHardCapMs?: number;
502
602
  /**
503
603
  * Rate-limit window in milliseconds for `/mcp/api` and `/mcp/a`
504
604
  * (default: 60000).
@@ -643,6 +743,7 @@ interface NodeRedPluginAPI {
643
743
  };
644
744
  events?: {
645
745
  on(event: string, listener: () => void): void;
746
+ removeListener?(event: string, listener: () => void): void;
646
747
  };
647
748
  auth: NodeRedAuthAPI;
648
749
  plugins: {