@agentproto/registry 0.1.0-alpha.1
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/LICENSE +21 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.mjs +113 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jeremy André and agentproto contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AIP-43 REGISTRY — public types.
|
|
3
|
+
*
|
|
4
|
+
* The registry is type-parametric over a doctype handle `H`. The
|
|
5
|
+
* package itself doesn't know what STORAGE / SANDBOX / OPERATOR
|
|
6
|
+
* handles look like — it just relies on a `keyBy(handle) => string`
|
|
7
|
+
* selector and treats `handle.capabilities` (if present) as opaque
|
|
8
|
+
* metadata for `lookup()` queries.
|
|
9
|
+
*/
|
|
10
|
+
interface RegistryOptions<H> {
|
|
11
|
+
/**
|
|
12
|
+
* Informational label used in error messages and (when discovery
|
|
13
|
+
* hooks are enabled) MCP tool names. Common values: `"storage"`,
|
|
14
|
+
* `"sandbox"`, `"operator"`. Doesn't affect lookup semantics.
|
|
15
|
+
*/
|
|
16
|
+
family: string;
|
|
17
|
+
/**
|
|
18
|
+
* How to derive the registry key from a handle. Required when the
|
|
19
|
+
* handle type doesn't expose a recognisable id field; the default
|
|
20
|
+
* resolution (per AIP-43 § Identity) inspects `handle.id`,
|
|
21
|
+
* `handle.provider`, `handle.slug` in priority order.
|
|
22
|
+
*/
|
|
23
|
+
keyBy?: (handle: H) => string;
|
|
24
|
+
}
|
|
25
|
+
interface Registry<H> {
|
|
26
|
+
/**
|
|
27
|
+
* Add a handle. Throws `RegistryDuplicateError` if the resolved key
|
|
28
|
+
* is already present — silent overwrite is unsafe (a second
|
|
29
|
+
* `defineStorage({ provider: "s3" })` shadowing the first is the
|
|
30
|
+
* exact bug class the registry exists to surface).
|
|
31
|
+
*/
|
|
32
|
+
register(handle: H): void;
|
|
33
|
+
/** Returns true if the resolved key is registered. */
|
|
34
|
+
has(id: string): boolean;
|
|
35
|
+
/** Number of handles currently registered. */
|
|
36
|
+
count(): number;
|
|
37
|
+
/** Get the handle by id. Returns undefined if absent. */
|
|
38
|
+
get(id: string): H | undefined;
|
|
39
|
+
/** Every handle currently registered, insertion-ordered. */
|
|
40
|
+
list(): readonly H[];
|
|
41
|
+
/** Every `[id, handle]` pair, insertion-ordered. */
|
|
42
|
+
entries(): ReadonlyArray<readonly [string, H]>;
|
|
43
|
+
/**
|
|
44
|
+
* Every handle matching `predicate`. Predicate runs in registration
|
|
45
|
+
* order; ties are broken by insertion order.
|
|
46
|
+
*/
|
|
47
|
+
lookup(predicate: (handle: H) => boolean): readonly H[];
|
|
48
|
+
/**
|
|
49
|
+
* Remove `id` from the registry. Returns true if a handle was
|
|
50
|
+
* removed. Use sparingly — registries are intended to be
|
|
51
|
+
* boot-time-stable.
|
|
52
|
+
*/
|
|
53
|
+
unregister(id: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Replace an existing handle. Throws `RegistryNotFoundError` if no
|
|
56
|
+
* handle exists at `keyBy(handle)`. Use when a hot-reload picks up
|
|
57
|
+
* a re-defined backend.
|
|
58
|
+
*/
|
|
59
|
+
replace(handle: H): void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Optional metadata that handles MAY expose on a `capabilities` field
|
|
63
|
+
* (per AIP-43 § Capability metadata namespace). The registry does NOT
|
|
64
|
+
* validate the shape — capabilities are opaque to the catalog. This
|
|
65
|
+
* type is a documentation hint for hosts that want to converge on a
|
|
66
|
+
* common namespace.
|
|
67
|
+
*/
|
|
68
|
+
interface SuggestedCapabilities {
|
|
69
|
+
/** Can the bytes be mounted into a sandbox? (storage) */
|
|
70
|
+
bridgeable?: boolean;
|
|
71
|
+
/** How a sandbox sees the bytes when bridgeable. */
|
|
72
|
+
transport?: "symlink" | "fuse" | "mcp" | "tunnel" | (string & {});
|
|
73
|
+
/** Backend ids of the other family that compose cleanly. */
|
|
74
|
+
pairsWith?: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Can a remote host reach this backend? `false` for `local-ide` /
|
|
77
|
+
* loopback `local-daemon` setups where the server can't reach the
|
|
78
|
+
* user's machine without a tunnel.
|
|
79
|
+
*/
|
|
80
|
+
serverReachable?: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** Thrown by `register(...)` when the key is already taken. */
|
|
83
|
+
declare class RegistryDuplicateError extends Error {
|
|
84
|
+
readonly code: "registry_duplicate";
|
|
85
|
+
constructor(family: string, id: string);
|
|
86
|
+
}
|
|
87
|
+
/** Thrown by `replace(...)` when no handle exists at the resolved key. */
|
|
88
|
+
declare class RegistryNotFoundError extends Error {
|
|
89
|
+
readonly code: "registry_not_found";
|
|
90
|
+
constructor(family: string, id: string);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Thrown when `keyBy` fails to derive a key (returns empty string,
|
|
94
|
+
* undefined, or throws). The registry can't store unkeyed handles.
|
|
95
|
+
*/
|
|
96
|
+
declare class RegistryKeyError extends Error {
|
|
97
|
+
readonly code: "registry_key_error";
|
|
98
|
+
constructor(family: string, reason: string);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* AIP-43 REGISTRY — `createRegistry<H>()` reference implementation.
|
|
103
|
+
*
|
|
104
|
+
* In-memory, insertion-ordered, duplicate-refusing catalog of doctype
|
|
105
|
+
* handles. Type-parametric so the same impl works for STORAGE
|
|
106
|
+
* handles, SANDBOX handles, OPERATOR handles, EXTENSION handles —
|
|
107
|
+
* whatever the host registers.
|
|
108
|
+
*
|
|
109
|
+
* Storage: a `Map<string, H>`. Insertion order is the iteration
|
|
110
|
+
* order JS guarantees on Maps; `list()` and `entries()` rely on it.
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
declare function createRegistry<H>(options: RegistryOptions<H>): Registry<H>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @agentproto/registry — AIP-43 reference implementation.
|
|
117
|
+
*
|
|
118
|
+
* A type-parametric in-memory catalog that collects N defineX'd
|
|
119
|
+
* doctype handles (storage, sandbox, operator, extension, …) and
|
|
120
|
+
* exposes a uniform lookup surface. Replaces hand-rolled per-host
|
|
121
|
+
* registries with one shape that works for every doctype family.
|
|
122
|
+
*
|
|
123
|
+
* @see https://agentproto.sh/docs/aip-43
|
|
124
|
+
*/
|
|
125
|
+
declare const SPEC_NAME: "agentregistry/v1";
|
|
126
|
+
declare const SPEC_VERSION: "1.0.0-alpha";
|
|
127
|
+
|
|
128
|
+
export { type Registry, RegistryDuplicateError, RegistryKeyError, RegistryNotFoundError, type RegistryOptions, SPEC_NAME, SPEC_VERSION, type SuggestedCapabilities, createRegistry };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentproto/registry v0.1.0-alpha
|
|
3
|
+
* AIP-43 REGISTRY reference implementation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// src/types.ts
|
|
7
|
+
var RegistryDuplicateError = class extends Error {
|
|
8
|
+
code = "registry_duplicate";
|
|
9
|
+
constructor(family, id) {
|
|
10
|
+
super(
|
|
11
|
+
`[registry/${family}] handle '${id}' is already registered. Call unregister('${id}') first if replacement is intentional, or replace(handle) to swap atomically.`
|
|
12
|
+
);
|
|
13
|
+
this.name = "RegistryDuplicateError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var RegistryNotFoundError = class extends Error {
|
|
17
|
+
code = "registry_not_found";
|
|
18
|
+
constructor(family, id) {
|
|
19
|
+
super(
|
|
20
|
+
`[registry/${family}] handle '${id}' is not registered; replace() requires an existing entry. Use register() for new handles.`
|
|
21
|
+
);
|
|
22
|
+
this.name = "RegistryNotFoundError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var RegistryKeyError = class extends Error {
|
|
26
|
+
code = "registry_key_error";
|
|
27
|
+
constructor(family, reason) {
|
|
28
|
+
super(`[registry/${family}] ${reason}`);
|
|
29
|
+
this.name = "RegistryKeyError";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/create-registry.ts
|
|
34
|
+
function defaultKeyBy(handle) {
|
|
35
|
+
if (!handle || typeof handle !== "object") return null;
|
|
36
|
+
const h = handle;
|
|
37
|
+
if (typeof h.id === "string" && h.id.length > 0) return h.id;
|
|
38
|
+
if (typeof h.provider === "string" && h.provider.length > 0) return h.provider;
|
|
39
|
+
if (typeof h.slug === "string" && h.slug.length > 0) return h.slug;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function createRegistry(options) {
|
|
43
|
+
const { family } = options;
|
|
44
|
+
const keyBy = options.keyBy ?? ((handle) => defaultKeyBy(handle) ?? "");
|
|
45
|
+
const entries = /* @__PURE__ */ new Map();
|
|
46
|
+
function resolveKey(handle) {
|
|
47
|
+
let key;
|
|
48
|
+
try {
|
|
49
|
+
key = keyBy(handle);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
throw new RegistryKeyError(
|
|
52
|
+
family,
|
|
53
|
+
`keyBy threw while computing the key for a handle: ${err instanceof Error ? err.message : String(err)}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (typeof key !== "string" || key.length === 0) {
|
|
57
|
+
throw new RegistryKeyError(
|
|
58
|
+
family,
|
|
59
|
+
`keyBy returned ${key === "" ? "an empty string" : key === void 0 ? "undefined" : key === null ? "null" : `a non-string (${typeof key})`} for a handle. Provide an explicit \`keyBy\` in the registry options or set \`id\`/\`provider\`/\`slug\` on the handle.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return key;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
register(handle) {
|
|
66
|
+
const key = resolveKey(handle);
|
|
67
|
+
if (entries.has(key)) {
|
|
68
|
+
throw new RegistryDuplicateError(family, key);
|
|
69
|
+
}
|
|
70
|
+
entries.set(key, handle);
|
|
71
|
+
},
|
|
72
|
+
has(id) {
|
|
73
|
+
return entries.has(id);
|
|
74
|
+
},
|
|
75
|
+
count() {
|
|
76
|
+
return entries.size;
|
|
77
|
+
},
|
|
78
|
+
get(id) {
|
|
79
|
+
return entries.get(id);
|
|
80
|
+
},
|
|
81
|
+
list() {
|
|
82
|
+
return Array.from(entries.values());
|
|
83
|
+
},
|
|
84
|
+
entries() {
|
|
85
|
+
return Array.from(entries.entries());
|
|
86
|
+
},
|
|
87
|
+
lookup(predicate) {
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const handle of entries.values()) {
|
|
90
|
+
if (predicate(handle)) out.push(handle);
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
},
|
|
94
|
+
unregister(id) {
|
|
95
|
+
return entries.delete(id);
|
|
96
|
+
},
|
|
97
|
+
replace(handle) {
|
|
98
|
+
const key = resolveKey(handle);
|
|
99
|
+
if (!entries.has(key)) {
|
|
100
|
+
throw new RegistryNotFoundError(family, key);
|
|
101
|
+
}
|
|
102
|
+
entries.set(key, handle);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/index.ts
|
|
108
|
+
var SPEC_NAME = "agentregistry/v1";
|
|
109
|
+
var SPEC_VERSION = "1.0.0-alpha";
|
|
110
|
+
|
|
111
|
+
export { RegistryDuplicateError, RegistryKeyError, RegistryNotFoundError, SPEC_NAME, SPEC_VERSION, createRegistry };
|
|
112
|
+
//# sourceMappingURL=index.mjs.map
|
|
113
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/create-registry.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * AIP-43 REGISTRY — public types.\n *\n * The registry is type-parametric over a doctype handle `H`. The\n * package itself doesn't know what STORAGE / SANDBOX / OPERATOR\n * handles look like — it just relies on a `keyBy(handle) => string`\n * selector and treats `handle.capabilities` (if present) as opaque\n * metadata for `lookup()` queries.\n */\n\nexport interface RegistryOptions<H> {\n /**\n * Informational label used in error messages and (when discovery\n * hooks are enabled) MCP tool names. Common values: `\"storage\"`,\n * `\"sandbox\"`, `\"operator\"`. Doesn't affect lookup semantics.\n */\n family: string\n /**\n * How to derive the registry key from a handle. Required when the\n * handle type doesn't expose a recognisable id field; the default\n * resolution (per AIP-43 § Identity) inspects `handle.id`,\n * `handle.provider`, `handle.slug` in priority order.\n */\n keyBy?: (handle: H) => string\n}\n\nexport interface Registry<H> {\n /**\n * Add a handle. Throws `RegistryDuplicateError` if the resolved key\n * is already present — silent overwrite is unsafe (a second\n * `defineStorage({ provider: \"s3\" })` shadowing the first is the\n * exact bug class the registry exists to surface).\n */\n register(handle: H): void\n\n /** Returns true if the resolved key is registered. */\n has(id: string): boolean\n\n /** Number of handles currently registered. */\n count(): number\n\n /** Get the handle by id. Returns undefined if absent. */\n get(id: string): H | undefined\n\n /** Every handle currently registered, insertion-ordered. */\n list(): readonly H[]\n\n /** Every `[id, handle]` pair, insertion-ordered. */\n entries(): ReadonlyArray<readonly [string, H]>\n\n /**\n * Every handle matching `predicate`. Predicate runs in registration\n * order; ties are broken by insertion order.\n */\n lookup(predicate: (handle: H) => boolean): readonly H[]\n\n /**\n * Remove `id` from the registry. Returns true if a handle was\n * removed. Use sparingly — registries are intended to be\n * boot-time-stable.\n */\n unregister(id: string): boolean\n\n /**\n * Replace an existing handle. Throws `RegistryNotFoundError` if no\n * handle exists at `keyBy(handle)`. Use when a hot-reload picks up\n * a re-defined backend.\n */\n replace(handle: H): void\n}\n\n/**\n * Optional metadata that handles MAY expose on a `capabilities` field\n * (per AIP-43 § Capability metadata namespace). The registry does NOT\n * validate the shape — capabilities are opaque to the catalog. This\n * type is a documentation hint for hosts that want to converge on a\n * common namespace.\n */\nexport interface SuggestedCapabilities {\n /** Can the bytes be mounted into a sandbox? (storage) */\n bridgeable?: boolean\n /** How a sandbox sees the bytes when bridgeable. */\n transport?: \"symlink\" | \"fuse\" | \"mcp\" | \"tunnel\" | (string & {})\n /** Backend ids of the other family that compose cleanly. */\n pairsWith?: string[]\n /**\n * Can a remote host reach this backend? `false` for `local-ide` /\n * loopback `local-daemon` setups where the server can't reach the\n * user's machine without a tunnel.\n */\n serverReachable?: boolean\n}\n\n/** Thrown by `register(...)` when the key is already taken. */\nexport class RegistryDuplicateError extends Error {\n readonly code = \"registry_duplicate\" as const\n constructor(family: string, id: string) {\n super(\n `[registry/${family}] handle '${id}' is already registered. ` +\n `Call unregister('${id}') first if replacement is intentional, ` +\n `or replace(handle) to swap atomically.`,\n )\n this.name = \"RegistryDuplicateError\"\n }\n}\n\n/** Thrown by `replace(...)` when no handle exists at the resolved key. */\nexport class RegistryNotFoundError extends Error {\n readonly code = \"registry_not_found\" as const\n constructor(family: string, id: string) {\n super(\n `[registry/${family}] handle '${id}' is not registered; ` +\n `replace() requires an existing entry. Use register() for new handles.`,\n )\n this.name = \"RegistryNotFoundError\"\n }\n}\n\n/**\n * Thrown when `keyBy` fails to derive a key (returns empty string,\n * undefined, or throws). The registry can't store unkeyed handles.\n */\nexport class RegistryKeyError extends Error {\n readonly code = \"registry_key_error\" as const\n constructor(family: string, reason: string) {\n super(`[registry/${family}] ${reason}`)\n this.name = \"RegistryKeyError\"\n }\n}\n","/**\n * AIP-43 REGISTRY — `createRegistry<H>()` reference implementation.\n *\n * In-memory, insertion-ordered, duplicate-refusing catalog of doctype\n * handles. Type-parametric so the same impl works for STORAGE\n * handles, SANDBOX handles, OPERATOR handles, EXTENSION handles —\n * whatever the host registers.\n *\n * Storage: a `Map<string, H>`. Insertion order is the iteration\n * order JS guarantees on Maps; `list()` and `entries()` rely on it.\n */\n\nimport {\n RegistryDuplicateError,\n RegistryKeyError,\n RegistryNotFoundError,\n type Registry,\n type RegistryOptions,\n} from \"./types.js\"\n\n/**\n * Default key resolution per AIP-43 § Identity:\n * 1. `handle.id` (standalone doctype manifests)\n * 2. `handle.provider` (STORAGE / SANDBOX handles)\n * 3. `handle.slug` (EXTENSION handles)\n *\n * Returns null when none of the above are non-empty strings — the\n * caller raises `RegistryKeyError` in that case.\n */\nfunction defaultKeyBy<H>(handle: H): string | null {\n if (!handle || typeof handle !== \"object\") return null\n const h = handle as { id?: unknown; provider?: unknown; slug?: unknown }\n if (typeof h.id === \"string\" && h.id.length > 0) return h.id\n if (typeof h.provider === \"string\" && h.provider.length > 0) return h.provider\n if (typeof h.slug === \"string\" && h.slug.length > 0) return h.slug\n return null\n}\n\nexport function createRegistry<H>(options: RegistryOptions<H>): Registry<H> {\n const { family } = options\n const keyBy = options.keyBy ?? ((handle: H) => defaultKeyBy(handle) ?? \"\")\n const entries = new Map<string, H>()\n\n function resolveKey(handle: H): string {\n let key: string\n try {\n key = keyBy(handle)\n } catch (err) {\n throw new RegistryKeyError(\n family,\n `keyBy threw while computing the key for a handle: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n if (typeof key !== \"string\" || key.length === 0) {\n throw new RegistryKeyError(\n family,\n `keyBy returned ${\n key === \"\"\n ? \"an empty string\"\n : key === undefined\n ? \"undefined\"\n : key === null\n ? \"null\"\n : `a non-string (${typeof key})`\n } for a handle. Provide an explicit \\`keyBy\\` in the registry options or set \\`id\\`/\\`provider\\`/\\`slug\\` on the handle.`,\n )\n }\n return key\n }\n\n return {\n register(handle) {\n const key = resolveKey(handle)\n if (entries.has(key)) {\n throw new RegistryDuplicateError(family, key)\n }\n entries.set(key, handle)\n },\n\n has(id) {\n return entries.has(id)\n },\n\n count() {\n return entries.size\n },\n\n get(id) {\n return entries.get(id)\n },\n\n list() {\n return Array.from(entries.values())\n },\n\n entries() {\n return Array.from(entries.entries())\n },\n\n lookup(predicate) {\n const out: H[] = []\n for (const handle of entries.values()) {\n if (predicate(handle)) out.push(handle)\n }\n return out\n },\n\n unregister(id) {\n return entries.delete(id)\n },\n\n replace(handle) {\n const key = resolveKey(handle)\n if (!entries.has(key)) {\n throw new RegistryNotFoundError(family, key)\n }\n entries.set(key, handle)\n },\n }\n}\n","/**\n * @agentproto/registry — AIP-43 reference implementation.\n *\n * A type-parametric in-memory catalog that collects N defineX'd\n * doctype handles (storage, sandbox, operator, extension, …) and\n * exposes a uniform lookup surface. Replaces hand-rolled per-host\n * registries with one shape that works for every doctype family.\n *\n * @see https://agentproto.sh/docs/aip-43\n */\n\nexport const SPEC_NAME = \"agentregistry/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport { createRegistry } from \"./create-registry.js\"\nexport {\n RegistryDuplicateError,\n RegistryKeyError,\n RegistryNotFoundError,\n type Registry,\n type RegistryOptions,\n type SuggestedCapabilities,\n} from \"./types.js\"\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentproto/registry",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "@agentproto/registry — AIP-43 reference implementation. A type-parametric in-memory catalog that collects N defineX'd doctype handles (storage, sandbox, operator, extension, ...) and exposes a uniform lookup surface. One impl, every doctype family. Replaces hand-rolled per-host registries.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agentproto",
|
|
7
|
+
"aip-43",
|
|
8
|
+
"registry",
|
|
9
|
+
"open-standard",
|
|
10
|
+
"agentic"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://agentproto.sh/docs/aip-43",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/agentproto/ts",
|
|
16
|
+
"directory": "packages/registry"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/agentproto/ts/issues"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "dist/index.mjs",
|
|
24
|
+
"module": "dist/index.mjs",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.mjs",
|
|
30
|
+
"default": "./dist/index.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^25.6.2",
|
|
44
|
+
"tsup": "^8.5.1",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vitest": "^3.2.4",
|
|
47
|
+
"@agentproto/tooling": "0.1.0-alpha.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"dev": "tsup --watch",
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"clean": "rm -rf dist",
|
|
53
|
+
"check-types": "tsc --noEmit",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest"
|
|
56
|
+
}
|
|
57
|
+
}
|