@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/CHANGELOG.md +50 -0
- package/README.md +90 -10
- package/ai-access.html +1 -1
- package/dist/plugin.cjs +678 -407
- package/dist/plugin.d.cts +107 -6
- package/docs/api/openapi.yaml +325 -42
- package/docs/errors.md +17 -6
- package/docs/mcp-tools.md +20 -7
- package/package.json +1 -1
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` =
|
|
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
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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: {
|