@ggui-ai/registry-core 0.6.3 → 0.8.0
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/impls/memory-bundle-storage.d.ts.map +1 -1
- package/dist/impls/memory-bundle-storage.js +25 -22
- package/dist/impls/memory-registry-storage.d.ts.map +1 -1
- package/dist/impls/memory-registry-storage.js +47 -4
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/install-command.d.ts +25 -0
- package/dist/install-command.d.ts.map +1 -0
- package/dist/install-command.js +15 -0
- package/dist/interfaces/bundle-storage.d.ts +55 -31
- package/dist/interfaces/bundle-storage.d.ts.map +1 -1
- package/dist/interfaces/registry-storage.d.ts +106 -1
- package/dist/interfaces/registry-storage.d.ts.map +1 -1
- package/dist/mcp-tool-filters.d.ts +23 -0
- package/dist/mcp-tool-filters.d.ts.map +1 -0
- package/dist/mcp-tool-filters.js +14 -0
- package/dist/ops/delete-author-key.d.ts +45 -0
- package/dist/ops/delete-author-key.d.ts.map +1 -0
- package/dist/ops/delete-author-key.js +31 -0
- package/dist/ops/list-author-keys.d.ts +34 -0
- package/dist/ops/list-author-keys.d.ts.map +1 -0
- package/dist/ops/list-author-keys.js +45 -0
- package/dist/ops/list-versions.d.ts +19 -10
- package/dist/ops/list-versions.d.ts.map +1 -1
- package/dist/ops/list-versions.js +43 -8
- package/dist/ops/private-read-authz.d.ts +88 -0
- package/dist/ops/private-read-authz.d.ts.map +1 -0
- package/dist/ops/private-read-authz.js +58 -0
- package/dist/ops/publish.d.ts +68 -1
- package/dist/ops/publish.d.ts.map +1 -1
- package/dist/ops/publish.js +292 -16
- package/dist/ops/read.d.ts +28 -6
- package/dist/ops/read.d.ts.map +1 -1
- package/dist/ops/read.js +47 -7
- package/dist/ops/register-author-key.d.ts +13 -0
- package/dist/ops/register-author-key.d.ts.map +1 -1
- package/dist/ops/register-author-key.js +42 -0
- package/dist/ops/search.d.ts +13 -1
- package/dist/ops/search.d.ts.map +1 -1
- package/dist/ops/search.js +54 -2
- package/dist/testing/bundle-storage-contract.d.ts.map +1 -1
- package/dist/testing/bundle-storage-contract.js +61 -27
- package/dist/testing/registry-storage-contract.d.ts +1 -1
- package/dist/testing/registry-storage-contract.d.ts.map +1 -1
- package/dist/testing/registry-storage-contract.js +351 -1
- package/dist/types.d.ts +257 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +79 -14
- package/package.json +20 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-bundle-storage.d.ts","sourceRoot":"","sources":["../../src/impls/memory-bundle-storage.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-bundle-storage.d.ts","sourceRoot":"","sources":["../../src/impls/memory-bundle-storage.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGrE,MAAM,WAAW,4BAA4B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC,GACzC,aAAa,CAkDf"}
|
|
@@ -3,38 +3,41 @@ export function inMemoryBundleStorage(options = {}) {
|
|
|
3
3
|
const bundles = new Map();
|
|
4
4
|
const signatures = new Map();
|
|
5
5
|
const manifests = new Map();
|
|
6
|
-
|
|
6
|
+
// Visibility segments the key (H1 prefix split) — a blob written
|
|
7
|
+
// `public` must be invisible to a `private` read of the same triple,
|
|
8
|
+
// exactly like the prefix-split object keys of the durable impls.
|
|
9
|
+
const key = (scope, name, version, visibility) => `${visibility}/${scope}/${name}/${version}`;
|
|
7
10
|
return {
|
|
8
|
-
async putBundle(scope, name, version, bytes) {
|
|
9
|
-
bundles.set(key(scope, name, version), new Uint8Array(bytes));
|
|
10
|
-
return this.bundleUrl(scope, name, version);
|
|
11
|
+
async putBundle(scope, name, version, visibility, bytes) {
|
|
12
|
+
bundles.set(key(scope, name, version, visibility), new Uint8Array(bytes));
|
|
13
|
+
return this.bundleUrl(scope, name, version, visibility);
|
|
11
14
|
},
|
|
12
|
-
async getBundle(scope, name, version) {
|
|
13
|
-
const bytes = bundles.get(key(scope, name, version));
|
|
15
|
+
async getBundle(scope, name, version, visibility) {
|
|
16
|
+
const bytes = bundles.get(key(scope, name, version, visibility));
|
|
14
17
|
return bytes === undefined ? null : new Uint8Array(bytes);
|
|
15
18
|
},
|
|
16
|
-
async putSignature(scope, name, version, signature) {
|
|
17
|
-
signatures.set(key(scope, name, version), signature);
|
|
18
|
-
return this.signatureUrl(scope, name, version);
|
|
19
|
+
async putSignature(scope, name, version, visibility, signature) {
|
|
20
|
+
signatures.set(key(scope, name, version, visibility), signature);
|
|
21
|
+
return this.signatureUrl(scope, name, version, visibility);
|
|
19
22
|
},
|
|
20
|
-
async getSignature(scope, name, version) {
|
|
21
|
-
return signatures.get(key(scope, name, version)) ?? null;
|
|
23
|
+
async getSignature(scope, name, version, visibility) {
|
|
24
|
+
return signatures.get(key(scope, name, version, visibility)) ?? null;
|
|
22
25
|
},
|
|
23
|
-
async putManifest(scope, name, version, manifest) {
|
|
24
|
-
manifests.set(key(scope, name, version), manifest);
|
|
25
|
-
return this.manifestUrl(scope, name, version);
|
|
26
|
+
async putManifest(scope, name, version, visibility, manifest) {
|
|
27
|
+
manifests.set(key(scope, name, version, visibility), manifest);
|
|
28
|
+
return this.manifestUrl(scope, name, version, visibility);
|
|
26
29
|
},
|
|
27
|
-
async getManifest(scope, name, version) {
|
|
28
|
-
return manifests.get(key(scope, name, version)) ?? null;
|
|
30
|
+
async getManifest(scope, name, version, visibility) {
|
|
31
|
+
return manifests.get(key(scope, name, version, visibility)) ?? null;
|
|
29
32
|
},
|
|
30
|
-
bundleUrl(scope, name, version) {
|
|
31
|
-
return `${bundleHost}/bundles/${scope}/${name}/${version}/bundle.js`;
|
|
33
|
+
bundleUrl(scope, name, version, visibility) {
|
|
34
|
+
return `${bundleHost}/bundles/${visibility}/${scope}/${name}/${version}/bundle.js`;
|
|
32
35
|
},
|
|
33
|
-
signatureUrl(scope, name, version) {
|
|
34
|
-
return `${bundleHost}/bundles/${scope}/${name}/${version}/bundle.js.sig`;
|
|
36
|
+
signatureUrl(scope, name, version, visibility) {
|
|
37
|
+
return `${bundleHost}/bundles/${visibility}/${scope}/${name}/${version}/bundle.js.sig`;
|
|
35
38
|
},
|
|
36
|
-
manifestUrl(scope, name, version) {
|
|
37
|
-
return `${bundleHost}/bundles/${scope}/${name}/${version}/manifest.json`;
|
|
39
|
+
manifestUrl(scope, name, version, visibility) {
|
|
40
|
+
return `${bundleHost}/bundles/${visibility}/${scope}/${name}/${version}/manifest.json`;
|
|
38
41
|
},
|
|
39
42
|
};
|
|
40
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-registry-storage.d.ts","sourceRoot":"","sources":["../../src/impls/memory-registry-storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory-registry-storage.d.ts","sourceRoot":"","sources":["../../src/impls/memory-registry-storage.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,mCAAmC,CAAC;AAM3C,wBAAgB,uBAAuB,IAAI,eAAe,CA2JzD"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthorKeyAlreadyExistsError, } from '../interfaces/registry-storage.js';
|
|
2
|
+
import { matchesMcpToolFilters } from '../mcp-tool-filters.js';
|
|
2
3
|
const DEFAULT_LIMIT = 50;
|
|
3
4
|
const MAX_LIMIT = 200;
|
|
4
5
|
export function inMemoryRegistryStorage() {
|
|
@@ -6,8 +7,12 @@ export function inMemoryRegistryStorage() {
|
|
|
6
7
|
const versions = new Map();
|
|
7
8
|
const authorKeys = new Map();
|
|
8
9
|
const compiledBlobs = new Map();
|
|
10
|
+
const scopeOwners = new Map();
|
|
9
11
|
const versionKey = (artifactId, version) => `${artifactId}@${version}`;
|
|
10
|
-
|
|
12
|
+
// JSON tuple — unambiguous for ANY subject/keyId contents. Subjects
|
|
13
|
+
// are operator-defined free text, so a `${subject}/${keyId}` composite
|
|
14
|
+
// would make ('team', …) and ('team/alice', …) collide or leak.
|
|
15
|
+
const authorKey = (subject, keyId) => JSON.stringify([subject, keyId]);
|
|
11
16
|
return {
|
|
12
17
|
async getArtifactMetadata(artifactId) {
|
|
13
18
|
return metadata.get(artifactId) ?? null;
|
|
@@ -92,6 +97,36 @@ export function inMemoryRegistryStorage() {
|
|
|
92
97
|
});
|
|
93
98
|
return { ok: true, mode: 'dedup' };
|
|
94
99
|
},
|
|
100
|
+
async getScopeOwner(scope) {
|
|
101
|
+
return scopeOwners.get(scope) ?? null;
|
|
102
|
+
},
|
|
103
|
+
async claimScope(row) {
|
|
104
|
+
// First-writer-wins. The check-and-set pair runs with no `await`
|
|
105
|
+
// between them, so the single-threaded event loop makes it
|
|
106
|
+
// atomic — two racing claims resolve strictly one-after-another.
|
|
107
|
+
if (scopeOwners.has(row.scope)) {
|
|
108
|
+
return { conflict: true };
|
|
109
|
+
}
|
|
110
|
+
scopeOwners.set(row.scope, row);
|
|
111
|
+
return { ok: true };
|
|
112
|
+
},
|
|
113
|
+
async updateScopeOwner(row, expect) {
|
|
114
|
+
// Compare-and-set against the caller's read snapshot. The
|
|
115
|
+
// check-and-set pair runs with no interleaving `await`, so the
|
|
116
|
+
// event loop makes it atomic (same discipline as claimScope).
|
|
117
|
+
const current = scopeOwners.get(row.scope);
|
|
118
|
+
if ('absent' in expect) {
|
|
119
|
+
if (current !== undefined)
|
|
120
|
+
return { conflict: true };
|
|
121
|
+
}
|
|
122
|
+
else if (current === undefined ||
|
|
123
|
+
current.ownerSubject !== expect.ownerSubject ||
|
|
124
|
+
current.verification !== expect.verification) {
|
|
125
|
+
return { conflict: true };
|
|
126
|
+
}
|
|
127
|
+
scopeOwners.set(row.scope, row);
|
|
128
|
+
return { ok: true };
|
|
129
|
+
},
|
|
95
130
|
async getAuthorKey(subject, keyId) {
|
|
96
131
|
return authorKeys.get(authorKey(subject, keyId)) ?? null;
|
|
97
132
|
},
|
|
@@ -103,14 +138,20 @@ export function inMemoryRegistryStorage() {
|
|
|
103
138
|
authorKeys.set(key, row);
|
|
104
139
|
},
|
|
105
140
|
async listAuthorKeys(subject) {
|
|
106
|
-
|
|
141
|
+
// Row-field filter, not key-prefix matching — the row's own
|
|
142
|
+
// `subject` is the only unambiguous ownership signal.
|
|
107
143
|
const matches = [];
|
|
108
|
-
for (const
|
|
109
|
-
if (
|
|
144
|
+
for (const row of authorKeys.values()) {
|
|
145
|
+
if (row.subject === subject)
|
|
110
146
|
matches.push(row);
|
|
111
147
|
}
|
|
112
148
|
return matches;
|
|
113
149
|
},
|
|
150
|
+
async deleteAuthorKey(subject, keyId) {
|
|
151
|
+
// Map#delete returns whether an entry existed — exactly the
|
|
152
|
+
// contract's `deleted` signal.
|
|
153
|
+
return authorKeys.delete(authorKey(subject, keyId));
|
|
154
|
+
},
|
|
114
155
|
};
|
|
115
156
|
}
|
|
116
157
|
function clampLimit(limit) {
|
|
@@ -137,6 +178,8 @@ function rowMatchesFilter(row, q) {
|
|
|
137
178
|
return false;
|
|
138
179
|
if (q.hook !== undefined && row.hook !== q.hook)
|
|
139
180
|
return false;
|
|
181
|
+
if (!matchesMcpToolFilters(row.mcpTools, q))
|
|
182
|
+
return false;
|
|
140
183
|
if (q.tag !== undefined) {
|
|
141
184
|
if (row.tags === undefined || !row.tags.includes(q.tag))
|
|
142
185
|
return false;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,17 +9,22 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export type { AuthnContext } from './interfaces/authn.js';
|
|
11
11
|
export type { BundleStorage } from './interfaces/bundle-storage.js';
|
|
12
|
-
export { AuthorKeyAlreadyExistsError, type PutAuthorKeyOptions, type RegistryStorage, } from './interfaces/registry-storage.js';
|
|
13
|
-
export { ARTIFACTS_METADATA_SK, type ArtifactKind, type ArtifactScanFilter, type ArtifactVersionRow, type ArtifactsMetadataRow, type AuthorKeyRow, type CompiledBlobRow, type ErrorBody, LIST_VERSIONS_ERROR_CODES, type ListVersionsErrorBody, type ListVersionsErrorCode, type ListVersionsResponse, PUBLISH_ERROR_CODES, type PublishErrorBody, type PublishErrorCode, type PublishRequestBody, type PublishResponseBody, READ_ERROR_CODES, type ReadErrorBody, type ReadErrorCode, type ReadPkgResponse, REGISTER_AUTHOR_KEY_ERROR_CODES, type RegisterAuthorKeyErrorBody, type RegisterAuthorKeyErrorCode, type RegisterAuthorKeyRequestBody, type RegisterAuthorKeyResponseBody, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, type SearchErrorBody, type SearchErrorCode, type SearchResponse, type SearchResultEntry, type SearchSort, type VersionListEntry, type Visibility, } from './types.js';
|
|
12
|
+
export { AuthorKeyAlreadyExistsError, type PutAuthorKeyOptions, type RegistryStorage, type ScopeOwnerExpectation, } from './interfaces/registry-storage.js';
|
|
13
|
+
export { ARTIFACTS_METADATA_SK, type ArtifactKind, type ArtifactScanFilter, type ArtifactVersionRow, type ArtifactsMetadataRow, type AuthorKeyListEntry, type AuthorKeyRow, type CompiledBlobRow, DELETE_AUTHOR_KEY_ERROR_CODES, type DeleteAuthorKeyErrorBody, type DeleteAuthorKeyErrorCode, type DeleteAuthorKeyResponseBody, type ErrorBody, LIST_AUTHOR_KEYS_ERROR_CODES, type ListAuthorKeysErrorBody, type ListAuthorKeysErrorCode, type ListAuthorKeysResponseBody, LIST_VERSIONS_ERROR_CODES, type ListVersionsErrorBody, type ListVersionsErrorCode, type ListVersionsResponse, PUBLISH_ERROR_CODES, type PublishErrorBody, type PublishErrorCode, type PublishRequestBody, type PublishResponseBody, READ_ERROR_CODES, type ReadErrorBody, type ReadErrorCode, type ReadPkgResponse, REGISTER_AUTHOR_KEY_ERROR_CODES, type RegisterAuthorKeyErrorBody, type RegisterAuthorKeyErrorCode, type RegisterAuthorKeyRequestBody, type RegisterAuthorKeyResponseBody, SAN_ALLOWLIST_INVALID, type SanAllowlistInvalid, SCOPE_VERIFICATIONS, type ScopeOwnerRow, type ScopeVerification, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, unauthorizedErrorBody, type SearchErrorBody, type SearchErrorCode, type SearchResponse, type SearchResultEntry, type SearchSort, type VersionListEntry, type Visibility, } from './types.js';
|
|
14
14
|
export { compileBlueprint, type CompileBlueprintErr, type CompileBlueprintOk, type CompileBlueprintResult, } from './ops/compile.js';
|
|
15
15
|
export { checkConformance, MAX_BLUEPRINT_SOURCE_BYTES, type BlueprintProbeRunner, type ConformanceError, type ConformanceErrorCode, type ConformanceFailureCode, type ConformanceRequestPayload, type ConformanceResponseBody, } from './ops/conformance.js';
|
|
16
|
-
export { publishArtifact, MAX_BUNDLE_BYTES, type PublishArtifactDeps, type PublishArtifactInput, type PublishArtifactResult, } from './ops/publish.js';
|
|
16
|
+
export { publishArtifact, MAX_BUNDLE_BYTES, RESERVED_SCOPES, type PublishArtifactDeps, type PublishArtifactInput, type PublishArtifactResult, type VerifiedEmailResolver, } from './ops/publish.js';
|
|
17
17
|
export { readArtifact, type ReadArtifactDeps, type ReadArtifactInput, type ReadArtifactResult, } from './ops/read.js';
|
|
18
|
+
export { artifactScope, canReadPrivateArtifact, createScopeOwnerResolver, } from './ops/private-read-authz.js';
|
|
18
19
|
export { listArtifactVersions, type ListArtifactVersionsDeps, type ListArtifactVersionsInput, type ListArtifactVersionsResult, } from './ops/list-versions.js';
|
|
19
20
|
export { searchArtifacts, type SearchArtifactsDeps, type SearchArtifactsInput, type SearchArtifactsResult, } from './ops/search.js';
|
|
20
21
|
export { registerAuthorKey, type RegisterAuthorKeyDeps, type RegisterAuthorKeyInput, type RegisterAuthorKeyResult, } from './ops/register-author-key.js';
|
|
22
|
+
export { listAuthorKeys, type ListAuthorKeysDeps, type ListAuthorKeysResult, } from './ops/list-author-keys.js';
|
|
23
|
+
export { deleteAuthorKey, type DeleteAuthorKeyDeps, type DeleteAuthorKeyInput, type DeleteAuthorKeyResult, } from './ops/delete-author-key.js';
|
|
21
24
|
export { inMemoryRegistryStorage } from './impls/memory-registry-storage.js';
|
|
22
25
|
export { inMemoryBundleStorage, type InMemoryBundleStorageOptions, } from './impls/memory-bundle-storage.js';
|
|
26
|
+
export { matchesMcpToolFilters } from './mcp-tool-filters.js';
|
|
27
|
+
export { installCommand, type InstallCommandOptions, } from './install-command.js';
|
|
23
28
|
export { compareSemver } from './utils/semver.js';
|
|
24
29
|
export { base64Encode, safeBase64Decode, sha384Base64 } from './utils/base64.js';
|
|
25
30
|
export type { Ed25519Signature, GadgetSignature } from '@ggui-ai/gadget-signing';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EACL,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -9,18 +9,24 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { AuthorKeyAlreadyExistsError, } from './interfaces/registry-storage.js';
|
|
11
11
|
// Types — rows + wire shapes
|
|
12
|
-
export { ARTIFACTS_METADATA_SK, LIST_VERSIONS_ERROR_CODES, PUBLISH_ERROR_CODES, READ_ERROR_CODES, REGISTER_AUTHOR_KEY_ERROR_CODES, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, } from './types.js';
|
|
12
|
+
export { ARTIFACTS_METADATA_SK, DELETE_AUTHOR_KEY_ERROR_CODES, LIST_AUTHOR_KEYS_ERROR_CODES, LIST_VERSIONS_ERROR_CODES, PUBLISH_ERROR_CODES, READ_ERROR_CODES, REGISTER_AUTHOR_KEY_ERROR_CODES, SAN_ALLOWLIST_INVALID, SCOPE_VERIFICATIONS, SEARCH_ERROR_CODES, SEARCH_SORT_OPTIONS, unauthorizedErrorBody, } from './types.js';
|
|
13
13
|
// Operations
|
|
14
14
|
export { compileBlueprint, } from './ops/compile.js';
|
|
15
15
|
export { checkConformance, MAX_BLUEPRINT_SOURCE_BYTES, } from './ops/conformance.js';
|
|
16
|
-
export { publishArtifact, MAX_BUNDLE_BYTES, } from './ops/publish.js';
|
|
16
|
+
export { publishArtifact, MAX_BUNDLE_BYTES, RESERVED_SCOPES, } from './ops/publish.js';
|
|
17
17
|
export { readArtifact, } from './ops/read.js';
|
|
18
|
+
export { artifactScope, canReadPrivateArtifact, createScopeOwnerResolver, } from './ops/private-read-authz.js';
|
|
18
19
|
export { listArtifactVersions, } from './ops/list-versions.js';
|
|
19
20
|
export { searchArtifacts, } from './ops/search.js';
|
|
20
21
|
export { registerAuthorKey, } from './ops/register-author-key.js';
|
|
22
|
+
export { listAuthorKeys, } from './ops/list-author-keys.js';
|
|
23
|
+
export { deleteAuthorKey, } from './ops/delete-author-key.js';
|
|
21
24
|
// In-memory impls
|
|
22
25
|
export { inMemoryRegistryStorage } from './impls/memory-registry-storage.js';
|
|
23
26
|
export { inMemoryBundleStorage, } from './impls/memory-bundle-storage.js';
|
|
27
|
+
// Shared scan-filter semantics
|
|
28
|
+
export { matchesMcpToolFilters } from './mcp-tool-filters.js';
|
|
24
29
|
// Utils
|
|
30
|
+
export { installCommand, } from './install-command.js';
|
|
25
31
|
export { compareSemver } from './utils/semver.js';
|
|
26
32
|
export { base64Encode, safeBase64Decode, sha384Base64 } from './utils/base64.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ArtifactKind } from './types.js';
|
|
2
|
+
export interface InstallCommandOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Registry origin to pin via an explicit `--registry=<url>` flag —
|
|
5
|
+
* pass the URL the artifact was discovered on (the same origin the
|
|
6
|
+
* search/read endpoints were queried against). When provided, the
|
|
7
|
+
* emitted command installs from exactly that registry instead of
|
|
8
|
+
* whatever the CLI's ambient default resolves to; when omitted, the
|
|
9
|
+
* flag is left off and the CLI default applies.
|
|
10
|
+
*/
|
|
11
|
+
readonly registryHost?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Compose the canonical CLI install command for a published artifact:
|
|
15
|
+
*
|
|
16
|
+
* ggui <kind> install <scope>/<name>@<version> [--registry=<url>]
|
|
17
|
+
*
|
|
18
|
+
* Same shape the publish flow reports back after a successful publish,
|
|
19
|
+
* so every surface that displays an install command agrees on one
|
|
20
|
+
* wording. Pure string composition — callers are responsible for
|
|
21
|
+
* validating `artifactId` (`@scope/name`) and `version` (semver)
|
|
22
|
+
* before display; this function does not re-validate.
|
|
23
|
+
*/
|
|
24
|
+
export declare function installCommand(kind: ArtifactKind, artifactId: string, version: string, opts?: InstallCommandOptions): string;
|
|
25
|
+
//# sourceMappingURL=install-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-command.d.ts","sourceRoot":"","sources":["../src/install-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,qBAA0B,GAC/B,MAAM,CAIR"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compose the canonical CLI install command for a published artifact:
|
|
3
|
+
*
|
|
4
|
+
* ggui <kind> install <scope>/<name>@<version> [--registry=<url>]
|
|
5
|
+
*
|
|
6
|
+
* Same shape the publish flow reports back after a successful publish,
|
|
7
|
+
* so every surface that displays an install command agrees on one
|
|
8
|
+
* wording. Pure string composition — callers are responsible for
|
|
9
|
+
* validating `artifactId` (`@scope/name`) and `version` (semver)
|
|
10
|
+
* before display; this function does not re-validate.
|
|
11
|
+
*/
|
|
12
|
+
export function installCommand(kind, artifactId, version, opts = {}) {
|
|
13
|
+
const registry = opts.registryHost !== undefined ? ` --registry=${opts.registryHost}` : '';
|
|
14
|
+
return `ggui ${kind} install ${artifactId}@${version}${registry}`;
|
|
15
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `BundleStorage` — bundle + signature + manifest blob storage. A
|
|
3
3
|
* hosted implementation may serve blobs from object storage behind a
|
|
4
4
|
* CDN; the open-source server backs it with the filesystem under
|
|
5
|
-
* `<root>/bundles/<scope>/<name>/<version>/`. A memory
|
|
6
|
-
* provided for tests.
|
|
5
|
+
* `<root>/bundles/<visibility>/<scope>/<name>/<version>/`. A memory
|
|
6
|
+
* impl is provided for tests.
|
|
7
7
|
*
|
|
8
8
|
* Each `put*` method returns the fully-qualified URL the consumer
|
|
9
9
|
* (iframe runtime, install CLI) can fetch. The URL prefix is
|
|
@@ -11,17 +11,33 @@
|
|
|
11
11
|
* CDN-fronted implementation, or `http://localhost:9001` (or
|
|
12
12
|
* whatever the server is bound to) for the open-source server.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* ## Visibility-split key layout (load-bearing)
|
|
15
|
+
*
|
|
16
|
+
* Every blob lives under a visibility-segmented key:
|
|
17
|
+
*
|
|
18
|
+
* bundles/public/<scope>/<name>/<version>/{bundle.js,bundle.js.sig,manifest.json}
|
|
19
|
+
* bundles/private/<scope>/<name>/<version>/{...}
|
|
20
|
+
*
|
|
21
|
+
* Placement IS the access-control boundary: the `bundles/public/`
|
|
22
|
+
* prefix may be served anonymously (CDN, static route); the
|
|
23
|
+
* `bundles/private/` prefix MUST only be reachable through an
|
|
24
|
+
* authenticated route that authorizes the caller against the
|
|
25
|
+
* artifact's publisher or scope owner. An impl that flattens the two
|
|
26
|
+
* prefixes back together silently republishes every private artifact.
|
|
27
|
+
*
|
|
28
|
+
* Bundles are immutable post-publish. Public-prefix responses MUST
|
|
29
|
+
* emit `Cache-Control: public, max-age=31536000, immutable` — SRI
|
|
30
|
+
* integrity depends on it. Private-prefix responses stay immutable but
|
|
31
|
+
* MUST NOT be shared-cacheable (`Cache-Control: private, …`). The OSS
|
|
32
|
+
* server's bundle routes set these headers explicitly; a CDN-fronted
|
|
33
|
+
* implementation sets the public policy on its cache behavior.
|
|
19
34
|
*
|
|
20
35
|
* ## Protocol & Contract Bar
|
|
21
36
|
*
|
|
22
37
|
* **Parties:**
|
|
23
38
|
* - Producer: {@link publishArtifact} writes bundle (gadgets),
|
|
24
|
-
* signature (gadgets), and manifest (always) on every publish
|
|
39
|
+
* signature (gadgets), and manifest (always) on every publish, at
|
|
40
|
+
* the key the manifest's `visibility` selects.
|
|
25
41
|
* - Consumer: iframe-runtime (bundleUrl), install CLI (signatureUrl,
|
|
26
42
|
* manifestUrl), audit tooling (manifestUrl on yanked versions).
|
|
27
43
|
*
|
|
@@ -31,49 +47,57 @@
|
|
|
31
47
|
* per-version row immutability invariant on {@link RegistryStorage}
|
|
32
48
|
* prevents true re-publishes; the bundle-storage layer needs no
|
|
33
49
|
* conflict semantics).
|
|
50
|
+
* - Key composition MUST segment on `visibility` — a blob written
|
|
51
|
+
* `public` is invisible to a `private` read of the same triple, and
|
|
52
|
+
* vice versa.
|
|
34
53
|
* - URL composition methods (`bundleUrl`, `signatureUrl`,
|
|
35
|
-
* `manifestUrl`) MUST be pure — no I/O, no async
|
|
36
|
-
*
|
|
54
|
+
* `manifestUrl`) MUST be pure — no I/O, no async — and MUST embed
|
|
55
|
+
* the same visibility segment the corresponding `put*` used, so the
|
|
56
|
+
* persisted row URLs point at the blob's real location. Consumers
|
|
57
|
+
* cache URLs liberally.
|
|
37
58
|
*
|
|
38
59
|
* **Failure mode:**
|
|
39
60
|
* - Transport-level failures throw; the publish op wraps and returns
|
|
40
61
|
* 500.
|
|
41
|
-
* - Missing-blob reads return `null`; never throw.
|
|
62
|
+
* - Missing-blob reads return `null`; never throw. A visibility
|
|
63
|
+
* mismatch reads as a miss.
|
|
42
64
|
*
|
|
43
65
|
* **Observable violation:**
|
|
44
66
|
* - Contract test {@link bundleStorageContract} covers: bundle
|
|
45
67
|
* round-trip preserves bytes; signature round-trip preserves the
|
|
46
68
|
* structured object; manifest round-trip preserves the full
|
|
47
|
-
* discriminated-union shape; URL methods compose without side effects
|
|
48
|
-
* missing reads return null
|
|
69
|
+
* discriminated-union shape; URL methods compose without side effects
|
|
70
|
+
* and embed the visibility segment; missing reads return null;
|
|
71
|
+
* cross-visibility reads return null.
|
|
49
72
|
*/
|
|
50
73
|
import type { ArtifactManifest } from '@ggui-ai/artifact-manifest';
|
|
51
74
|
import type { GadgetSignature } from '@ggui-ai/gadget-signing';
|
|
75
|
+
import type { Visibility } from '../types.js';
|
|
52
76
|
export interface BundleStorage {
|
|
53
|
-
/** Write gadget bundle bytes. Returns the
|
|
54
|
-
putBundle(scope: string, name: string, version: string, bytes: Uint8Array): Promise<string>;
|
|
55
|
-
/** Read gadget bundle bytes. `null` on miss. */
|
|
56
|
-
getBundle(scope: string, name: string, version: string): Promise<Uint8Array | null>;
|
|
77
|
+
/** Write gadget bundle bytes. Returns the fetch URL. */
|
|
78
|
+
putBundle(scope: string, name: string, version: string, visibility: Visibility, bytes: Uint8Array): Promise<string>;
|
|
79
|
+
/** Read gadget bundle bytes. `null` on miss (including visibility mismatch). */
|
|
80
|
+
getBundle(scope: string, name: string, version: string, visibility: Visibility): Promise<Uint8Array | null>;
|
|
57
81
|
/**
|
|
58
|
-
* Write the signature envelope. Returns the
|
|
82
|
+
* Write the signature envelope. Returns the fetch URL.
|
|
59
83
|
*
|
|
60
84
|
* The envelope is a {@link GadgetSignature} — a discriminated union
|
|
61
85
|
* over `algorithm` (`ed25519` for private gadgets, `sigstore-cosign`
|
|
62
86
|
* for public). Impls serialize via `JSON.stringify(signature)` and
|
|
63
87
|
* store the resulting bytes verbatim.
|
|
64
88
|
*/
|
|
65
|
-
putSignature(scope: string, name: string, version: string, signature: GadgetSignature): Promise<string>;
|
|
66
|
-
/** Read the signature envelope. `null` on miss. */
|
|
67
|
-
getSignature(scope: string, name: string, version: string): Promise<GadgetSignature | null>;
|
|
68
|
-
/** Write the manifest verbatim. Returns the
|
|
69
|
-
putManifest(scope: string, name: string, version: string, manifest: ArtifactManifest): Promise<string>;
|
|
70
|
-
/** Read the manifest. `null` on miss. */
|
|
71
|
-
getManifest(scope: string, name: string, version: string): Promise<ArtifactManifest | null>;
|
|
72
|
-
/** Compose the
|
|
73
|
-
bundleUrl(scope: string, name: string, version: string): string;
|
|
74
|
-
/** Compose the
|
|
75
|
-
signatureUrl(scope: string, name: string, version: string): string;
|
|
76
|
-
/** Compose the
|
|
77
|
-
manifestUrl(scope: string, name: string, version: string): string;
|
|
89
|
+
putSignature(scope: string, name: string, version: string, visibility: Visibility, signature: GadgetSignature): Promise<string>;
|
|
90
|
+
/** Read the signature envelope. `null` on miss (including visibility mismatch). */
|
|
91
|
+
getSignature(scope: string, name: string, version: string, visibility: Visibility): Promise<GadgetSignature | null>;
|
|
92
|
+
/** Write the manifest verbatim. Returns the fetch URL. */
|
|
93
|
+
putManifest(scope: string, name: string, version: string, visibility: Visibility, manifest: ArtifactManifest): Promise<string>;
|
|
94
|
+
/** Read the manifest. `null` on miss (including visibility mismatch). */
|
|
95
|
+
getManifest(scope: string, name: string, version: string, visibility: Visibility): Promise<ArtifactManifest | null>;
|
|
96
|
+
/** Compose the bundle URL (embeds the visibility segment). No I/O. */
|
|
97
|
+
bundleUrl(scope: string, name: string, version: string, visibility: Visibility): string;
|
|
98
|
+
/** Compose the signature URL (embeds the visibility segment). No I/O. */
|
|
99
|
+
signatureUrl(scope: string, name: string, version: string, visibility: Visibility): string;
|
|
100
|
+
/** Compose the manifest URL (embeds the visibility segment). No I/O. */
|
|
101
|
+
manifestUrl(scope: string, name: string, version: string, visibility: Visibility): string;
|
|
78
102
|
}
|
|
79
103
|
//# sourceMappingURL=bundle-storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/bundle-storage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bundle-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/bundle-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,SAAS,CACP,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,gFAAgF;IAChF,SAAS,CACP,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAE9B;;;;;;;OAOG;IACH,YAAY,CACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,eAAe,GACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,mFAAmF;IACnF,YAAY,CACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAEnC,0DAA0D;IAC1D,WAAW,CACT,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,yEAAyE;IACzE,WAAW,CACT,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAEpC,sEAAsE;IACtE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;IACxF,yEAAyE;IACzE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;IAC3F,wEAAwE;IACxE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;CAC3F"}
|
|
@@ -27,6 +27,23 @@
|
|
|
27
27
|
* at the storage layer. Implementations
|
|
28
28
|
* MUST NOT overwrite on conflict — per-version immutability is a
|
|
29
29
|
* load-bearing registry invariant.
|
|
30
|
+
* - {@link claimScope} MUST be an atomic first-writer-wins conditional
|
|
31
|
+
* create: when two claims race on the same scope, EXACTLY one wins
|
|
32
|
+
* and the loser observes `{ conflict: true }` with the winner's row
|
|
33
|
+
* left untouched. Scope ownership is the publish-authorization
|
|
34
|
+
* invariant — a lost race that overwrote the winner would silently
|
|
35
|
+
* reassign every future publish under that scope.
|
|
36
|
+
* - {@link getScopeOwner} MUST observe a completed {@link claimScope}
|
|
37
|
+
* immediately (read-your-writes strong). The publish gate's
|
|
38
|
+
* claim-conflict path re-reads to learn the winner; a read that can
|
|
39
|
+
* lag a durable claim would fail the losing racer's publish with a
|
|
40
|
+
* spurious storage-inconsistency error.
|
|
41
|
+
* - {@link updateScopeOwner} MUST be conditional on the caller's read
|
|
42
|
+
* snapshot (`expect`): the write lands only when the stored row still
|
|
43
|
+
* matches (or is still absent, for `{ absent: true }`). On mismatch
|
|
44
|
+
* it returns `{ conflict: true }` with the stored row untouched —
|
|
45
|
+
* an operator rewrite MUST NOT clobber a concurrent transfer or a
|
|
46
|
+
* racing first-publish claim.
|
|
30
47
|
* - {@link getArtifactMetadata} / {@link getArtifactVersion} MUST return
|
|
31
48
|
* exactly what was last written. `null` on miss; throw on transport
|
|
32
49
|
* failure (caller decides retry).
|
|
@@ -36,6 +53,13 @@
|
|
|
36
53
|
* rows MUST come back ordered by `publishedAt` descending (newest
|
|
37
54
|
* first), globally correct across the full cursor chain. Without an
|
|
38
55
|
* `order`, consumers MUST treat ordering as non-deterministic.
|
|
56
|
+
* - {@link scanArtifacts} MUST apply the `tool` / `server` filter
|
|
57
|
+
* dimensions with the shared semantics of `matchesMcpToolFilters`:
|
|
58
|
+
* `tool` matches any binding entry with that tool name; `server`
|
|
59
|
+
* matches entries declaring that server (bare entries never match);
|
|
60
|
+
* together they require one entry with exactly that (server, tool)
|
|
61
|
+
* pair. Case-sensitive exact, AND-composed with every other
|
|
62
|
+
* dimension.
|
|
39
63
|
*
|
|
40
64
|
* **Failure mode:**
|
|
41
65
|
* - Transport-level failures (store throttling, disk full) throw.
|
|
@@ -48,10 +72,11 @@
|
|
|
48
72
|
* round-trip preservation, idempotent {@link putArtifactMetadata},
|
|
49
73
|
* `putArtifactVersionIfAbsent` rejects on collision, missing returns
|
|
50
74
|
* null, `listAuthorKeys` returns only keys for the queried subject,
|
|
75
|
+
* `deleteAuthorKey` true/false idempotency + per-subject isolation,
|
|
51
76
|
* and `order: 'recent'` newest-first ordering (single page + across
|
|
52
77
|
* the cursor chain).
|
|
53
78
|
*/
|
|
54
|
-
import type { ArtifactScanFilter, ArtifactVersionRow, ArtifactsMetadataRow, AuthorKeyRow, CompiledBlobRow } from '../types.js';
|
|
79
|
+
import type { ArtifactScanFilter, ArtifactVersionRow, ArtifactsMetadataRow, AuthorKeyRow, CompiledBlobRow, ScopeOwnerRow } from '../types.js';
|
|
55
80
|
/**
|
|
56
81
|
* Optional flags for {@link RegistryStorage.putAuthorKey}.
|
|
57
82
|
*
|
|
@@ -79,6 +104,18 @@ export declare class AuthorKeyAlreadyExistsError extends Error {
|
|
|
79
104
|
readonly keyId: string;
|
|
80
105
|
constructor(subject: string, keyId: string);
|
|
81
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The caller's read snapshot for {@link RegistryStorage.updateScopeOwner}
|
|
109
|
+
* — either the `(ownerSubject, verification)` pair the caller read, or
|
|
110
|
+
* an assertion that no row exists yet (`{ absent: true }`, the operator
|
|
111
|
+
* seed path).
|
|
112
|
+
*/
|
|
113
|
+
export type ScopeOwnerExpectation = {
|
|
114
|
+
readonly ownerSubject: string;
|
|
115
|
+
readonly verification: ScopeOwnerRow['verification'];
|
|
116
|
+
} | {
|
|
117
|
+
readonly absent: true;
|
|
118
|
+
};
|
|
82
119
|
export interface RegistryStorage {
|
|
83
120
|
getArtifactMetadata(artifactId: string): Promise<ArtifactsMetadataRow | null>;
|
|
84
121
|
putArtifactMetadata(row: ArtifactsMetadataRow): Promise<void>;
|
|
@@ -92,6 +129,10 @@ export interface RegistryStorage {
|
|
|
92
129
|
* When `filter.order` is `'recent'`, rows MUST be returned newest
|
|
93
130
|
* first by `publishedAt`, globally correct across the full cursor
|
|
94
131
|
* chain — see the Obligations section above.
|
|
132
|
+
*
|
|
133
|
+
* `filter.tool` / `filter.server` follow the shared MCP-binding
|
|
134
|
+
* filter semantics (`matchesMcpToolFilters`) — see the Obligations
|
|
135
|
+
* section above.
|
|
95
136
|
*/
|
|
96
137
|
scanArtifacts(filter: ArtifactScanFilter): Promise<{
|
|
97
138
|
readonly rows: readonly ArtifactsMetadataRow[];
|
|
@@ -201,6 +242,56 @@ export interface RegistryStorage {
|
|
|
201
242
|
ok: false;
|
|
202
243
|
reason: 'version_exists';
|
|
203
244
|
}>;
|
|
245
|
+
/**
|
|
246
|
+
* Fetch the ownership row for `scope` (leading `@` included, e.g.
|
|
247
|
+
* `@acme`). Returns `null` when the scope is unclaimed.
|
|
248
|
+
*
|
|
249
|
+
* MUST be read-your-writes strong with respect to {@link claimScope}
|
|
250
|
+
* and {@link updateScopeOwner} — see the Obligations section above.
|
|
251
|
+
*/
|
|
252
|
+
getScopeOwner(scope: string): Promise<ScopeOwnerRow | null>;
|
|
253
|
+
/**
|
|
254
|
+
* Atomic first-writer-wins claim — creates the ownership row only
|
|
255
|
+
* when no row exists for `row.scope`. On conflict (a row already
|
|
256
|
+
* exists, including one that landed in a concurrent race) returns
|
|
257
|
+
* `{ conflict: true }` WITHOUT touching the existing row.
|
|
258
|
+
*
|
|
259
|
+
* The publish op claims on the caller's first publish into an
|
|
260
|
+
* unclaimed scope. Consumers MUST NOT pre-check with
|
|
261
|
+
* {@link getScopeOwner} + claim as a substitute for handling
|
|
262
|
+
* `conflict` — the conditional create is the only race-safe
|
|
263
|
+
* primitive (re-read on conflict instead).
|
|
264
|
+
*/
|
|
265
|
+
claimScope(row: ScopeOwnerRow): Promise<{
|
|
266
|
+
ok: true;
|
|
267
|
+
} | {
|
|
268
|
+
conflict: true;
|
|
269
|
+
}>;
|
|
270
|
+
/**
|
|
271
|
+
* Conditional full-row put of the ownership row — a compare-and-set
|
|
272
|
+
* against the caller's read snapshot.
|
|
273
|
+
*
|
|
274
|
+
* `expect` states what the caller believes is stored:
|
|
275
|
+
* - `{ ownerSubject, verification }` — the row MUST still carry
|
|
276
|
+
* exactly this pair (the snapshot identity of an ownership row).
|
|
277
|
+
* - `{ absent: true }` — no row may exist yet (operator seeding).
|
|
278
|
+
*
|
|
279
|
+
* On mismatch the write is refused with `{ conflict: true }` and the
|
|
280
|
+
* stored row is untouched — the caller re-reads and retries. This is
|
|
281
|
+
* the structural defense against read-modify-write races between two
|
|
282
|
+
* operators, or between an operator write and a concurrent
|
|
283
|
+
* first-publish claim.
|
|
284
|
+
*
|
|
285
|
+
* Operator-only caller: verification flips and ownership transfers
|
|
286
|
+
* (including seeding rows for reserved scopes, which are never
|
|
287
|
+
* first-publish-claimable). The publish path MUST NOT call this —
|
|
288
|
+
* {@link claimScope} is its only write.
|
|
289
|
+
*/
|
|
290
|
+
updateScopeOwner(row: ScopeOwnerRow, expect: ScopeOwnerExpectation): Promise<{
|
|
291
|
+
ok: true;
|
|
292
|
+
} | {
|
|
293
|
+
conflict: true;
|
|
294
|
+
}>;
|
|
204
295
|
getAuthorKey(subject: string, keyId: string): Promise<AuthorKeyRow | null>;
|
|
205
296
|
/**
|
|
206
297
|
* Write an AuthorKey row. With `options.ifNotExists === true`, the
|
|
@@ -211,5 +302,19 @@ export interface RegistryStorage {
|
|
|
211
302
|
*/
|
|
212
303
|
putAuthorKey(row: AuthorKeyRow, options?: PutAuthorKeyOptions): Promise<void>;
|
|
213
304
|
listAuthorKeys(subject: string): Promise<readonly AuthorKeyRow[]>;
|
|
305
|
+
/**
|
|
306
|
+
* Hard-delete the row for `(subject, keyId)`. Returns `true` when a
|
|
307
|
+
* row existed and was removed, `false` when no row existed — the
|
|
308
|
+
* caller-visible idempotency signal for the delete op's
|
|
309
|
+
* `deleted: false` response. Implementations MUST NOT throw on the
|
|
310
|
+
* absent case; transport failures still throw.
|
|
311
|
+
*
|
|
312
|
+
* Hard delete is the registry's established author-key removal
|
|
313
|
+
* semantic: the durable audit trail is the public key pinned per
|
|
314
|
+
* published version (`ArtifactVersionRow.authorPublicKey` +
|
|
315
|
+
* `publishedBy`), which this delete never touches — removal only
|
|
316
|
+
* blocks FUTURE publishes signed with the key.
|
|
317
|
+
*/
|
|
318
|
+
deleteAuthorKey(subject: string, keyId: string): Promise<boolean>;
|
|
214
319
|
}
|
|
215
320
|
//# sourceMappingURL=registry-storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/registry-storage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"registry-storage.d.ts","sourceRoot":"","sources":["../../src/interfaces/registry-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AACH,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,eAAe,EACf,aAAa,EACd,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAQ3C;AAED;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAC7B;IACE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CACtD,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;CAAE,CAAC;AAE9B,MAAM,WAAW,eAAe;IAE9B,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC9E,mBAAmB,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;QACjD,QAAQ,CAAC,IAAI,EAAE,SAAS,oBAAoB,EAAE,CAAC;QAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC,CAAC;IAGH,kBAAkB,CAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IACtC;;;;;;;;;;;;;;;;;OAiBG;IACH,oBAAoB,CAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IAC1C;;;;;OAKG;IACH,0BAA0B,CACxB,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IACnE,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGxE;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,oBAAoB,CAClB,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,eAAe,GACvB,OAAO,CACN;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAA;KAAE,GACxC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAC1C,CAAC;IAGF;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC5D;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAC3E;;;;;;;;;;;;;;;;;;;OAmBG;IACH,gBAAgB,CACd,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAG9C,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC3E;;;;;;OAMG;IACH,YAAY,CACV,GAAG,EAAE,YAAY,EACjB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC,CAAC;IAClE;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP-tool search-filter predicate — the reference semantics
|
|
3
|
+
* every {@link RegistryStorage.scanArtifacts} implementation must
|
|
4
|
+
* reproduce for the `tool` / `server` filter dimensions:
|
|
5
|
+
*
|
|
6
|
+
* - `tool` only → any binding entry with that tool name (with or
|
|
7
|
+
* without a `server`).
|
|
8
|
+
* - `server` only → any binding entry declaring that server; bare
|
|
9
|
+
* (server-less) entries never match.
|
|
10
|
+
* - both → a single entry with exactly that (server, tool)
|
|
11
|
+
* pair.
|
|
12
|
+
* - neither → always `true` (the filter is inactive).
|
|
13
|
+
*
|
|
14
|
+
* Matching is case-sensitive exact on both dimensions — MCP tool names
|
|
15
|
+
* are case-sensitive. AND-composes with every other scan-filter
|
|
16
|
+
* dimension; artifacts without bindings never match an active filter.
|
|
17
|
+
*/
|
|
18
|
+
import type { McpToolBinding } from '@ggui-ai/artifact-manifest';
|
|
19
|
+
export declare function matchesMcpToolFilters(bindings: ReadonlyArray<McpToolBinding> | undefined, filters: {
|
|
20
|
+
tool?: string;
|
|
21
|
+
server?: string;
|
|
22
|
+
}): boolean;
|
|
23
|
+
//# sourceMappingURL=mcp-tool-filters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-tool-filters.d.ts","sourceRoot":"","sources":["../src/mcp-tool-filters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,aAAa,CAAC,cAAc,CAAC,GAAG,SAAS,EACnD,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1C,OAAO,CAST"}
|