scene-capability-engine 3.6.2 → 3.6.3
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 +21 -0
- package/README.md +7 -2
- package/README.zh.md +7 -2
- package/bin/scene-capability-engine.js +2 -0
- package/docs/command-reference.md +39 -0
- package/lib/commands/auth.js +269 -0
- package/lib/commands/studio.js +57 -7
- package/lib/commands/task.js +25 -2
- package/lib/security/write-authorization.js +632 -0
- package/lib/state/sce-state-store.js +400 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const crypto = require('crypto');
|
|
2
3
|
const fs = require('fs-extra');
|
|
3
4
|
|
|
4
5
|
const DEFAULT_BACKEND = 'sqlite';
|
|
@@ -31,6 +32,27 @@ function parseJsonSafe(value, fallback) {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function normalizeStringArray(value, fallback = []) {
|
|
36
|
+
if (!Array.isArray(value)) {
|
|
37
|
+
return [...fallback];
|
|
38
|
+
}
|
|
39
|
+
return value
|
|
40
|
+
.map((item) => normalizeString(item))
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeIsoTimestamp(value, fallback = '') {
|
|
45
|
+
const normalized = normalizeString(value);
|
|
46
|
+
if (!normalized) {
|
|
47
|
+
return normalizeString(fallback);
|
|
48
|
+
}
|
|
49
|
+
const parsed = Date.parse(normalized);
|
|
50
|
+
if (!Number.isFinite(parsed)) {
|
|
51
|
+
return normalizeString(fallback);
|
|
52
|
+
}
|
|
53
|
+
return new Date(parsed).toISOString();
|
|
54
|
+
}
|
|
55
|
+
|
|
34
56
|
function formatSegment(value) {
|
|
35
57
|
const normalized = normalizeInteger(value, 0);
|
|
36
58
|
if (normalized <= 0) {
|
|
@@ -83,6 +105,8 @@ class SceStateStore {
|
|
|
83
105
|
specs: {},
|
|
84
106
|
tasks: {},
|
|
85
107
|
refs: {},
|
|
108
|
+
auth_leases: {},
|
|
109
|
+
auth_events: [],
|
|
86
110
|
sequences: {
|
|
87
111
|
scene_next: 1,
|
|
88
112
|
spec_next_by_scene: {},
|
|
@@ -181,6 +205,39 @@ class SceStateStore {
|
|
|
181
205
|
|
|
182
206
|
CREATE INDEX IF NOT EXISTS idx_studio_event_stream_job_ts
|
|
183
207
|
ON studio_event_stream(job_id, event_timestamp);
|
|
208
|
+
|
|
209
|
+
CREATE TABLE IF NOT EXISTS auth_lease_registry (
|
|
210
|
+
lease_id TEXT PRIMARY KEY,
|
|
211
|
+
subject TEXT NOT NULL,
|
|
212
|
+
role TEXT NOT NULL,
|
|
213
|
+
scope_json TEXT NOT NULL,
|
|
214
|
+
reason TEXT,
|
|
215
|
+
metadata_json TEXT,
|
|
216
|
+
issued_at TEXT NOT NULL,
|
|
217
|
+
expires_at TEXT NOT NULL,
|
|
218
|
+
revoked_at TEXT,
|
|
219
|
+
created_at TEXT NOT NULL,
|
|
220
|
+
updated_at TEXT NOT NULL
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
CREATE INDEX IF NOT EXISTS idx_auth_lease_registry_expires
|
|
224
|
+
ON auth_lease_registry(expires_at);
|
|
225
|
+
|
|
226
|
+
CREATE TABLE IF NOT EXISTS auth_event_stream (
|
|
227
|
+
event_id TEXT PRIMARY KEY,
|
|
228
|
+
event_timestamp TEXT NOT NULL,
|
|
229
|
+
event_type TEXT NOT NULL,
|
|
230
|
+
action TEXT,
|
|
231
|
+
actor TEXT,
|
|
232
|
+
lease_id TEXT,
|
|
233
|
+
result TEXT,
|
|
234
|
+
target TEXT,
|
|
235
|
+
detail_json TEXT,
|
|
236
|
+
created_at TEXT NOT NULL
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
CREATE INDEX IF NOT EXISTS idx_auth_event_stream_ts
|
|
240
|
+
ON auth_event_stream(event_timestamp);
|
|
184
241
|
`);
|
|
185
242
|
}
|
|
186
243
|
|
|
@@ -264,6 +321,43 @@ class SceStateStore {
|
|
|
264
321
|
};
|
|
265
322
|
}
|
|
266
323
|
|
|
324
|
+
_mapAuthLeaseRow(row) {
|
|
325
|
+
if (!row) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
lease_id: normalizeString(row.lease_id),
|
|
330
|
+
subject: normalizeString(row.subject),
|
|
331
|
+
role: normalizeString(row.role),
|
|
332
|
+
scope: normalizeStringArray(parseJsonSafe(row.scope_json, []), ['project:*']),
|
|
333
|
+
reason: normalizeString(row.reason) || null,
|
|
334
|
+
metadata: parseJsonSafe(row.metadata_json, {}) || {},
|
|
335
|
+
issued_at: normalizeIsoTimestamp(row.issued_at) || null,
|
|
336
|
+
expires_at: normalizeIsoTimestamp(row.expires_at) || null,
|
|
337
|
+
revoked_at: normalizeIsoTimestamp(row.revoked_at) || null,
|
|
338
|
+
created_at: normalizeIsoTimestamp(row.created_at) || null,
|
|
339
|
+
updated_at: normalizeIsoTimestamp(row.updated_at) || null
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
_mapAuthEventRow(row) {
|
|
344
|
+
if (!row) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
event_id: normalizeString(row.event_id),
|
|
349
|
+
event_timestamp: normalizeIsoTimestamp(row.event_timestamp) || null,
|
|
350
|
+
event_type: normalizeString(row.event_type),
|
|
351
|
+
action: normalizeString(row.action) || null,
|
|
352
|
+
actor: normalizeString(row.actor) || null,
|
|
353
|
+
lease_id: normalizeString(row.lease_id) || null,
|
|
354
|
+
result: normalizeString(row.result) || null,
|
|
355
|
+
target: normalizeString(row.target) || null,
|
|
356
|
+
detail: parseJsonSafe(row.detail_json, {}) || {},
|
|
357
|
+
created_at: normalizeIsoTimestamp(row.created_at) || null
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
267
361
|
async resolveOrCreateTaskRef(options = {}) {
|
|
268
362
|
const sceneId = normalizeString(options.sceneId);
|
|
269
363
|
const specId = normalizeString(options.specId);
|
|
@@ -510,6 +604,287 @@ class SceStateStore {
|
|
|
510
604
|
return events;
|
|
511
605
|
}
|
|
512
606
|
|
|
607
|
+
async issueAuthLease(options = {}) {
|
|
608
|
+
const subject = normalizeString(options.subject) || 'unknown';
|
|
609
|
+
const role = normalizeString(options.role) || 'maintainer';
|
|
610
|
+
const scope = normalizeStringArray(options.scope, ['project:*']);
|
|
611
|
+
const reason = normalizeString(options.reason) || null;
|
|
612
|
+
const metadata = options.metadata && typeof options.metadata === 'object'
|
|
613
|
+
? options.metadata
|
|
614
|
+
: {};
|
|
615
|
+
const issuedAt = normalizeIsoTimestamp(options.issued_at || options.issuedAt, this.now()) || this.now();
|
|
616
|
+
const ttlMinutes = normalizeInteger(options.ttl_minutes || options.ttlMinutes, 15);
|
|
617
|
+
const fallbackExpiresAt = new Date(
|
|
618
|
+
(Date.parse(issuedAt) || Date.now()) + (Math.max(ttlMinutes, 1) * 60 * 1000)
|
|
619
|
+
).toISOString();
|
|
620
|
+
const expiresAt = normalizeIsoTimestamp(options.expires_at || options.expiresAt, fallbackExpiresAt) || fallbackExpiresAt;
|
|
621
|
+
const leaseId = normalizeString(options.lease_id || options.leaseId)
|
|
622
|
+
|| `lease-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
|
|
623
|
+
const nowIso = this.now();
|
|
624
|
+
|
|
625
|
+
if (this._useMemoryBackend()) {
|
|
626
|
+
return this._issueAuthLeaseInMemory({
|
|
627
|
+
leaseId,
|
|
628
|
+
subject,
|
|
629
|
+
role,
|
|
630
|
+
scope,
|
|
631
|
+
reason,
|
|
632
|
+
metadata,
|
|
633
|
+
issuedAt,
|
|
634
|
+
expiresAt,
|
|
635
|
+
nowIso
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (!await this.ensureReady()) {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
this._db
|
|
644
|
+
.prepare(`
|
|
645
|
+
INSERT OR REPLACE INTO auth_lease_registry(
|
|
646
|
+
lease_id, subject, role, scope_json, reason, metadata_json, issued_at, expires_at, revoked_at, created_at, updated_at
|
|
647
|
+
)
|
|
648
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?)
|
|
649
|
+
`)
|
|
650
|
+
.run(
|
|
651
|
+
leaseId,
|
|
652
|
+
subject,
|
|
653
|
+
role,
|
|
654
|
+
JSON.stringify(scope),
|
|
655
|
+
reason,
|
|
656
|
+
JSON.stringify(metadata),
|
|
657
|
+
issuedAt,
|
|
658
|
+
expiresAt,
|
|
659
|
+
nowIso,
|
|
660
|
+
nowIso
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
return this.getAuthLease(leaseId);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
async getAuthLease(leaseId) {
|
|
667
|
+
const normalizedLeaseId = normalizeString(leaseId);
|
|
668
|
+
if (!normalizedLeaseId) {
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (this._useMemoryBackend()) {
|
|
673
|
+
const row = this._memory.auth_leases[normalizedLeaseId];
|
|
674
|
+
return row
|
|
675
|
+
? {
|
|
676
|
+
...row,
|
|
677
|
+
scope: normalizeStringArray(row.scope, ['project:*']),
|
|
678
|
+
metadata: { ...(row.metadata || {}) }
|
|
679
|
+
}
|
|
680
|
+
: null;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (!await this.ensureReady()) {
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const row = this._db
|
|
688
|
+
.prepare(`
|
|
689
|
+
SELECT lease_id, subject, role, scope_json, reason, metadata_json, issued_at, expires_at, revoked_at, created_at, updated_at
|
|
690
|
+
FROM auth_lease_registry
|
|
691
|
+
WHERE lease_id = ?
|
|
692
|
+
`)
|
|
693
|
+
.get(normalizedLeaseId);
|
|
694
|
+
return this._mapAuthLeaseRow(row);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
async listAuthLeases(options = {}) {
|
|
698
|
+
const activeOnly = options.activeOnly !== false;
|
|
699
|
+
const limit = normalizeInteger(options.limit, 20);
|
|
700
|
+
const nowIso = this.now();
|
|
701
|
+
|
|
702
|
+
if (this._useMemoryBackend()) {
|
|
703
|
+
let rows = Object.values(this._memory.auth_leases || {}).map((item) => ({
|
|
704
|
+
...item,
|
|
705
|
+
scope: normalizeStringArray(item.scope, ['project:*']),
|
|
706
|
+
metadata: { ...(item.metadata || {}) }
|
|
707
|
+
}));
|
|
708
|
+
if (activeOnly) {
|
|
709
|
+
const nowTime = Date.parse(nowIso) || Date.now();
|
|
710
|
+
rows = rows.filter((item) => {
|
|
711
|
+
const revokedAt = normalizeString(item.revoked_at);
|
|
712
|
+
if (revokedAt) {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
const expiresAt = Date.parse(item.expires_at || '') || 0;
|
|
716
|
+
return expiresAt > nowTime;
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
rows.sort((left, right) => (Date.parse(right.created_at || '') || 0) - (Date.parse(left.created_at || '') || 0));
|
|
720
|
+
return limit > 0 ? rows.slice(0, limit) : rows;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (!await this.ensureReady()) {
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const query = activeOnly
|
|
728
|
+
? `
|
|
729
|
+
SELECT lease_id, subject, role, scope_json, reason, metadata_json, issued_at, expires_at, revoked_at, created_at, updated_at
|
|
730
|
+
FROM auth_lease_registry
|
|
731
|
+
WHERE revoked_at IS NULL AND expires_at > ?
|
|
732
|
+
ORDER BY created_at DESC
|
|
733
|
+
LIMIT ?
|
|
734
|
+
`
|
|
735
|
+
: `
|
|
736
|
+
SELECT lease_id, subject, role, scope_json, reason, metadata_json, issued_at, expires_at, revoked_at, created_at, updated_at
|
|
737
|
+
FROM auth_lease_registry
|
|
738
|
+
ORDER BY created_at DESC
|
|
739
|
+
LIMIT ?
|
|
740
|
+
`;
|
|
741
|
+
|
|
742
|
+
const statement = this._db.prepare(query);
|
|
743
|
+
const rows = activeOnly
|
|
744
|
+
? statement.all(nowIso, limit)
|
|
745
|
+
: statement.all(limit);
|
|
746
|
+
return rows
|
|
747
|
+
.map((row) => this._mapAuthLeaseRow(row))
|
|
748
|
+
.filter(Boolean);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
async revokeAuthLease(leaseId, options = {}) {
|
|
752
|
+
const normalizedLeaseId = normalizeString(leaseId);
|
|
753
|
+
if (!normalizedLeaseId) {
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
const revokedAt = normalizeIsoTimestamp(options.revoked_at || options.revokedAt, this.now()) || this.now();
|
|
757
|
+
|
|
758
|
+
if (this._useMemoryBackend()) {
|
|
759
|
+
const existing = this._memory.auth_leases[normalizedLeaseId];
|
|
760
|
+
if (!existing) {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
existing.revoked_at = revokedAt;
|
|
764
|
+
existing.updated_at = revokedAt;
|
|
765
|
+
this._memory.auth_leases[normalizedLeaseId] = existing;
|
|
766
|
+
return {
|
|
767
|
+
...existing,
|
|
768
|
+
scope: normalizeStringArray(existing.scope, ['project:*']),
|
|
769
|
+
metadata: { ...(existing.metadata || {}) }
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (!await this.ensureReady()) {
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
this._db
|
|
778
|
+
.prepare('UPDATE auth_lease_registry SET revoked_at = ?, updated_at = ? WHERE lease_id = ?')
|
|
779
|
+
.run(revokedAt, revokedAt, normalizedLeaseId);
|
|
780
|
+
return this.getAuthLease(normalizedLeaseId);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
async appendAuthEvent(event = {}) {
|
|
784
|
+
const eventType = normalizeString(event.event_type || event.eventType);
|
|
785
|
+
if (!eventType) {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
const eventId = normalizeString(event.event_id || event.eventId)
|
|
789
|
+
|| `auth-evt-${Date.now()}-${crypto.randomBytes(3).toString('hex')}`;
|
|
790
|
+
const timestamp = normalizeIsoTimestamp(event.event_timestamp || event.timestamp, this.now()) || this.now();
|
|
791
|
+
const normalizedEvent = {
|
|
792
|
+
event_id: eventId,
|
|
793
|
+
event_timestamp: timestamp,
|
|
794
|
+
event_type: eventType,
|
|
795
|
+
action: normalizeString(event.action) || null,
|
|
796
|
+
actor: normalizeString(event.actor) || null,
|
|
797
|
+
lease_id: normalizeString(event.lease_id || event.leaseId) || null,
|
|
798
|
+
result: normalizeString(event.result) || null,
|
|
799
|
+
target: normalizeString(event.target) || null,
|
|
800
|
+
detail: event.detail && typeof event.detail === 'object'
|
|
801
|
+
? event.detail
|
|
802
|
+
: {}
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
if (this._useMemoryBackend()) {
|
|
806
|
+
const existingIndex = this._memory.auth_events
|
|
807
|
+
.findIndex((item) => normalizeString(item.event_id) === eventId);
|
|
808
|
+
const row = {
|
|
809
|
+
...normalizedEvent,
|
|
810
|
+
created_at: this.now()
|
|
811
|
+
};
|
|
812
|
+
if (existingIndex >= 0) {
|
|
813
|
+
this._memory.auth_events[existingIndex] = row;
|
|
814
|
+
} else {
|
|
815
|
+
this._memory.auth_events.push(row);
|
|
816
|
+
}
|
|
817
|
+
this._memory.auth_events.sort((left, right) => {
|
|
818
|
+
const l = Date.parse(left.event_timestamp || '') || 0;
|
|
819
|
+
const r = Date.parse(right.event_timestamp || '') || 0;
|
|
820
|
+
return l - r;
|
|
821
|
+
});
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (!await this.ensureReady()) {
|
|
826
|
+
return false;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
this._db
|
|
830
|
+
.prepare(`
|
|
831
|
+
INSERT OR REPLACE INTO auth_event_stream(
|
|
832
|
+
event_id, event_timestamp, event_type, action, actor, lease_id, result, target, detail_json, created_at
|
|
833
|
+
)
|
|
834
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
835
|
+
`)
|
|
836
|
+
.run(
|
|
837
|
+
normalizedEvent.event_id,
|
|
838
|
+
normalizedEvent.event_timestamp,
|
|
839
|
+
normalizedEvent.event_type,
|
|
840
|
+
normalizedEvent.action,
|
|
841
|
+
normalizedEvent.actor,
|
|
842
|
+
normalizedEvent.lease_id,
|
|
843
|
+
normalizedEvent.result,
|
|
844
|
+
normalizedEvent.target,
|
|
845
|
+
JSON.stringify(normalizedEvent.detail || {}),
|
|
846
|
+
this.now()
|
|
847
|
+
);
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
async listAuthEvents(options = {}) {
|
|
852
|
+
const limit = normalizeInteger(options.limit, 50);
|
|
853
|
+
|
|
854
|
+
if (this._useMemoryBackend()) {
|
|
855
|
+
const rows = [...this._memory.auth_events]
|
|
856
|
+
.sort((left, right) => (Date.parse(right.event_timestamp || '') || 0) - (Date.parse(left.event_timestamp || '') || 0))
|
|
857
|
+
.map((row) => ({
|
|
858
|
+
...row,
|
|
859
|
+
detail: row.detail && typeof row.detail === 'object' ? row.detail : {}
|
|
860
|
+
}));
|
|
861
|
+
return limit > 0 ? rows.slice(0, limit) : rows;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (!await this.ensureReady()) {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
const query = limit > 0
|
|
869
|
+
? `
|
|
870
|
+
SELECT event_id, event_timestamp, event_type, action, actor, lease_id, result, target, detail_json, created_at
|
|
871
|
+
FROM auth_event_stream
|
|
872
|
+
ORDER BY event_timestamp DESC
|
|
873
|
+
LIMIT ?
|
|
874
|
+
`
|
|
875
|
+
: `
|
|
876
|
+
SELECT event_id, event_timestamp, event_type, action, actor, lease_id, result, target, detail_json, created_at
|
|
877
|
+
FROM auth_event_stream
|
|
878
|
+
ORDER BY event_timestamp DESC
|
|
879
|
+
`;
|
|
880
|
+
|
|
881
|
+
const statement = this._db.prepare(query);
|
|
882
|
+
const rows = limit > 0 ? statement.all(limit) : statement.all();
|
|
883
|
+
return rows
|
|
884
|
+
.map((row) => this._mapAuthEventRow(row))
|
|
885
|
+
.filter(Boolean);
|
|
886
|
+
}
|
|
887
|
+
|
|
513
888
|
_resolveOrCreateTaskRefInMemory(options = {}) {
|
|
514
889
|
const sceneId = normalizeString(options.sceneId);
|
|
515
890
|
const specId = normalizeString(options.specId);
|
|
@@ -566,6 +941,30 @@ class SceStateStore {
|
|
|
566
941
|
this._memory.refs[taskRef] = row;
|
|
567
942
|
return { ...row, metadata: { ...(row.metadata || {}) } };
|
|
568
943
|
}
|
|
944
|
+
|
|
945
|
+
_issueAuthLeaseInMemory(options = {}) {
|
|
946
|
+
const leaseId = normalizeString(options.leaseId)
|
|
947
|
+
|| `lease-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
|
|
948
|
+
const row = {
|
|
949
|
+
lease_id: leaseId,
|
|
950
|
+
subject: normalizeString(options.subject) || 'unknown',
|
|
951
|
+
role: normalizeString(options.role) || 'maintainer',
|
|
952
|
+
scope: normalizeStringArray(options.scope, ['project:*']),
|
|
953
|
+
reason: normalizeString(options.reason) || null,
|
|
954
|
+
metadata: options.metadata && typeof options.metadata === 'object' ? { ...options.metadata } : {},
|
|
955
|
+
issued_at: normalizeIsoTimestamp(options.issuedAt, this.now()) || this.now(),
|
|
956
|
+
expires_at: normalizeIsoTimestamp(options.expiresAt, this.now()) || this.now(),
|
|
957
|
+
revoked_at: null,
|
|
958
|
+
created_at: normalizeIsoTimestamp(options.nowIso, this.now()) || this.now(),
|
|
959
|
+
updated_at: normalizeIsoTimestamp(options.nowIso, this.now()) || this.now()
|
|
960
|
+
};
|
|
961
|
+
this._memory.auth_leases[leaseId] = row;
|
|
962
|
+
return {
|
|
963
|
+
...row,
|
|
964
|
+
scope: normalizeStringArray(row.scope, ['project:*']),
|
|
965
|
+
metadata: { ...(row.metadata || {}) }
|
|
966
|
+
};
|
|
967
|
+
}
|
|
569
968
|
}
|
|
570
969
|
|
|
571
970
|
const STORE_CACHE = new Map();
|
package/package.json
CHANGED