@kindgi/tools 0.0.0-bootstrap.0 → 0.1.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 +201 -0
- package/README.md +86 -1
- package/dist/define.d.ts +138 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +318 -0
- package/dist/define.js.map +1 -0
- package/dist/errors.d.ts +113 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -0
- package/dist/http.d.ts +2 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +213 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/invoke.d.ts +27 -0
- package/dist/invoke.d.ts.map +1 -0
- package/dist/invoke.js +127 -0
- package/dist/invoke.js.map +1 -0
- package/dist/manifest.d.ts +23 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +37 -0
- package/dist/manifest.js.map +1 -0
- package/dist/precondition.d.ts +25 -0
- package/dist/precondition.d.ts.map +1 -0
- package/dist/precondition.js +34 -0
- package/dist/precondition.js.map +1 -0
- package/dist/registry.d.ts +87 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +171 -0
- package/dist/registry.js.map +1 -0
- package/dist/spec-registry.d.ts +48 -0
- package/dist/spec-registry.d.ts.map +1 -0
- package/dist/spec-registry.js +37 -0
- package/dist/spec-registry.js.map +1 -0
- package/dist/tool.schema.json +297 -0
- package/dist/types.d.ts +508 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -4
- package/src/define.ts +537 -0
- package/src/errors.ts +139 -0
- package/src/http.ts +268 -0
- package/src/index.ts +68 -0
- package/src/invoke.ts +165 -0
- package/src/manifest.ts +40 -0
- package/src/precondition.ts +40 -0
- package/src/registry.ts +262 -0
- package/src/spec-registry.ts +78 -0
- package/src/tool.schema.json +297 -0
- package/src/types.ts +538 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Result, TenantId, ToolId } from '@kindgi/types';
|
|
2
|
+
import type { ToolError } from './errors.js';
|
|
3
|
+
import type { AnyTool } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* In-process registry mapping (tool id, version) → tool. Multi-version:
|
|
6
|
+
* a single id may carry multiple compiled implementations at different
|
|
7
|
+
* exact semver strings; agent bindings reference tools by (id, range)
|
|
8
|
+
* and dispatch resolves at run start via `resolve(id, range)` (npm-
|
|
9
|
+
* compatible semver grammar, backed by `semver.maxSatisfying`).
|
|
10
|
+
*
|
|
11
|
+
* Not tenant-scoped. Multi-tenant deployments hold one registry per
|
|
12
|
+
* tenant or encode tenancy into tool ids. Cross-tenant tool sharing goes
|
|
13
|
+
* through the policy engine, not through registry design.
|
|
14
|
+
*/
|
|
15
|
+
export interface ToolRegistry {
|
|
16
|
+
/**
|
|
17
|
+
* Register a tool. Fails with `duplicate-tool-version` if the same
|
|
18
|
+
* `(id, version)` pair is already registered. Distinct versions of the
|
|
19
|
+
* same id may coexist — that's the whole point of the versioned model.
|
|
20
|
+
*/
|
|
21
|
+
register(tool: AnyTool): Result<void, ToolError>;
|
|
22
|
+
/**
|
|
23
|
+
* Return the latest active version for an id. "Latest" is defined by
|
|
24
|
+
* `semver.compare` — the highest exact version present. For callers
|
|
25
|
+
* that have no version range to resolve. Prefer `resolve(id, range)`
|
|
26
|
+
* when a range is available.
|
|
27
|
+
*/
|
|
28
|
+
get(id: ToolId): Result<AnyTool, ToolError>;
|
|
29
|
+
/** Return a specific `(id, version)` pair. */
|
|
30
|
+
getVersion(id: ToolId, version: string): Result<AnyTool, ToolError>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a semver range against the versions registered under `id`.
|
|
33
|
+
* Uses `semver.maxSatisfying` (npm grammar). Distinct failure modes:
|
|
34
|
+
* - `tool-not-found` → id has no versions at all
|
|
35
|
+
* - `invalid-version-range` → range string is grammatically broken
|
|
36
|
+
* - `tool-version-unresolvable` → id is present but no version satisfies
|
|
37
|
+
*/
|
|
38
|
+
resolve(id: ToolId, range: string): Result<ToolResolution, ToolError>;
|
|
39
|
+
has(id: ToolId): boolean;
|
|
40
|
+
hasVersion(id: ToolId, version: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Return every registered tool version, sorted by id then by version
|
|
43
|
+
* descending. Callers that want a per-id latest-only view use
|
|
44
|
+
* `.get(id)` per id.
|
|
45
|
+
*/
|
|
46
|
+
list(): readonly AnyTool[];
|
|
47
|
+
/** Return every id with at least one registered version. */
|
|
48
|
+
ids(): readonly ToolId[];
|
|
49
|
+
/** Return every version registered under `id`, sorted descending. */
|
|
50
|
+
versions(id: ToolId): readonly string[];
|
|
51
|
+
/**
|
|
52
|
+
* The registry for one tenant: lookups on the returned registry
|
|
53
|
+
* (`get`, `getVersion`, `resolve`, `list`, …) answer for `tenantId`
|
|
54
|
+
* only. Multi-tenant implementations load that tenant's tools (e.g.
|
|
55
|
+
* from a persistent store, cached) and return a view bound to it —
|
|
56
|
+
* never shared per-call state, so concurrent turns of different
|
|
57
|
+
* tenants cannot see each other's tools. A single-tenant registry
|
|
58
|
+
* (like `createToolRegistry`) returns itself. Callers resolve a
|
|
59
|
+
* turn's tools through the returned registry.
|
|
60
|
+
*/
|
|
61
|
+
forTenant(tenantId: TenantId): Promise<ToolRegistry>;
|
|
62
|
+
/**
|
|
63
|
+
* Optional cache invalidation. Storage-backed implementations call
|
|
64
|
+
* this from the write path (`POST /v1/tools`, unregister, reinstate)
|
|
65
|
+
* so the next `forTenant(tenantId)` re-reads from the persistent
|
|
66
|
+
* store. Without it, tools published after a tenant was loaded stay
|
|
67
|
+
* invisible to that tenant's cached view. In-memory implementations
|
|
68
|
+
* leave it undefined — there is no cache to invalidate.
|
|
69
|
+
*/
|
|
70
|
+
readonly invalidate?: (tenantId: TenantId) => void;
|
|
71
|
+
}
|
|
72
|
+
/** The outcome of a successful `resolve(id, range)` lookup. */
|
|
73
|
+
export interface ToolResolution {
|
|
74
|
+
readonly tool: AnyTool;
|
|
75
|
+
/**
|
|
76
|
+
* The exact version picked by `maxSatisfying`. Callers capture this in
|
|
77
|
+
* provenance + telemetry so a replay can pin against the same version.
|
|
78
|
+
*/
|
|
79
|
+
readonly resolvedVersion: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create an empty tool registry. Seeds are registered in order; the seed
|
|
83
|
+
* throws on duplicate `(id, version)` so accidental clobbers of a
|
|
84
|
+
* running tool fail loud at boot.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createToolRegistry(seed?: readonly AnyTool[]): ToolRegistry;
|
|
87
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE9D,OAAO,KAAK,EAGV,SAAS,EAIV,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACjD;;;;;OAKG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC5C,8CAA8C;IAC9C,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACtE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACjD;;;;OAIG;IACH,IAAI,IAAI,SAAS,OAAO,EAAE,CAAC;IAC3B,4DAA4D;IAC5D,GAAG,IAAI,SAAS,MAAM,EAAE,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;;;;;OASG;IACH,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACrD;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CACpD;AAED,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,SAAS,OAAO,EAAO,GAAG,YAAY,CAgK9E"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
import semver from 'semver';
|
|
4
|
+
/**
|
|
5
|
+
* Create an empty tool registry. Seeds are registered in order; the seed
|
|
6
|
+
* throws on duplicate `(id, version)` so accidental clobbers of a
|
|
7
|
+
* running tool fail loud at boot.
|
|
8
|
+
*/
|
|
9
|
+
export function createToolRegistry(seed = []) {
|
|
10
|
+
// id → version → tool. Nested map so we can iterate versions per id
|
|
11
|
+
// cheaply and cache a sorted-version list.
|
|
12
|
+
const tools = new Map();
|
|
13
|
+
function versionsDesc(id) {
|
|
14
|
+
const versions = tools.get(id);
|
|
15
|
+
if (versions === undefined)
|
|
16
|
+
return [];
|
|
17
|
+
return [...versions.keys()].sort((a, b) => semver.rcompare(a, b));
|
|
18
|
+
}
|
|
19
|
+
const registry = {
|
|
20
|
+
register(tool) {
|
|
21
|
+
let versions = tools.get(tool.id);
|
|
22
|
+
if (versions === undefined) {
|
|
23
|
+
versions = new Map();
|
|
24
|
+
tools.set(tool.id, versions);
|
|
25
|
+
}
|
|
26
|
+
if (versions.has(tool.version)) {
|
|
27
|
+
const dup = {
|
|
28
|
+
code: 'duplicate-tool-version',
|
|
29
|
+
message: `Tool "${tool.id}@${tool.version}" is already registered`,
|
|
30
|
+
id: tool.id,
|
|
31
|
+
version: tool.version,
|
|
32
|
+
};
|
|
33
|
+
return { kind: 'err', error: dup };
|
|
34
|
+
}
|
|
35
|
+
versions.set(tool.version, tool);
|
|
36
|
+
return { kind: 'ok', value: undefined };
|
|
37
|
+
},
|
|
38
|
+
get(id) {
|
|
39
|
+
const desc = versionsDesc(id);
|
|
40
|
+
if (desc.length === 0) {
|
|
41
|
+
const err = {
|
|
42
|
+
code: 'tool-not-found',
|
|
43
|
+
message: `No tool registered with id "${id}"`,
|
|
44
|
+
id,
|
|
45
|
+
};
|
|
46
|
+
return { kind: 'err', error: err };
|
|
47
|
+
}
|
|
48
|
+
// desc is sorted desc — desc[0] is highest.
|
|
49
|
+
const latest = tools.get(id)?.get(desc[0]);
|
|
50
|
+
if (latest === undefined) {
|
|
51
|
+
const err = {
|
|
52
|
+
code: 'tool-not-found',
|
|
53
|
+
message: `No tool registered with id "${id}"`,
|
|
54
|
+
id,
|
|
55
|
+
};
|
|
56
|
+
return { kind: 'err', error: err };
|
|
57
|
+
}
|
|
58
|
+
return { kind: 'ok', value: latest };
|
|
59
|
+
},
|
|
60
|
+
getVersion(id, version) {
|
|
61
|
+
const found = tools.get(id)?.get(version);
|
|
62
|
+
if (found === undefined) {
|
|
63
|
+
if (tools.get(id) === undefined) {
|
|
64
|
+
const err = {
|
|
65
|
+
code: 'tool-not-found',
|
|
66
|
+
message: `No tool registered with id "${id}"`,
|
|
67
|
+
id,
|
|
68
|
+
};
|
|
69
|
+
return { kind: 'err', error: err };
|
|
70
|
+
}
|
|
71
|
+
const err = {
|
|
72
|
+
code: 'tool-version-not-found',
|
|
73
|
+
message: `Tool "${id}" has no version "${version}" registered`,
|
|
74
|
+
id,
|
|
75
|
+
version,
|
|
76
|
+
};
|
|
77
|
+
return { kind: 'err', error: err };
|
|
78
|
+
}
|
|
79
|
+
return { kind: 'ok', value: found };
|
|
80
|
+
},
|
|
81
|
+
resolve(id, range) {
|
|
82
|
+
const versions = tools.get(id);
|
|
83
|
+
if (versions === undefined || versions.size === 0) {
|
|
84
|
+
const err = {
|
|
85
|
+
code: 'tool-not-found',
|
|
86
|
+
message: `No tool registered with id "${id}"`,
|
|
87
|
+
id,
|
|
88
|
+
};
|
|
89
|
+
return { kind: 'err', error: err };
|
|
90
|
+
}
|
|
91
|
+
if (semver.validRange(range) === null) {
|
|
92
|
+
const err = {
|
|
93
|
+
code: 'invalid-version-range',
|
|
94
|
+
message: `Version range "${range}" for tool "${id}" is not a valid semver range`,
|
|
95
|
+
id,
|
|
96
|
+
range,
|
|
97
|
+
};
|
|
98
|
+
return { kind: 'err', error: err };
|
|
99
|
+
}
|
|
100
|
+
const available = [...versions.keys()];
|
|
101
|
+
const picked = semver.maxSatisfying(available, range);
|
|
102
|
+
if (picked === null) {
|
|
103
|
+
const err = {
|
|
104
|
+
code: 'tool-version-unresolvable',
|
|
105
|
+
message: `Tool "${id}" has no version satisfying "${range}" (available: ${available
|
|
106
|
+
.sort((a, b) => semver.rcompare(a, b))
|
|
107
|
+
.join(', ')})`,
|
|
108
|
+
id,
|
|
109
|
+
range,
|
|
110
|
+
availableVersions: available.sort((a, b) => semver.rcompare(a, b)),
|
|
111
|
+
};
|
|
112
|
+
return { kind: 'err', error: err };
|
|
113
|
+
}
|
|
114
|
+
const tool = versions.get(picked);
|
|
115
|
+
// maxSatisfying returned a version we just enumerated — guaranteed
|
|
116
|
+
// to be in the map. The `if (tool === undefined)` branch is a
|
|
117
|
+
// defensive no-op for the type system.
|
|
118
|
+
if (tool === undefined) {
|
|
119
|
+
const err = {
|
|
120
|
+
code: 'tool-version-not-found',
|
|
121
|
+
message: `Tool "${id}" resolved to "${picked}" but the version is missing (internal)`,
|
|
122
|
+
id,
|
|
123
|
+
version: picked,
|
|
124
|
+
};
|
|
125
|
+
return { kind: 'err', error: err };
|
|
126
|
+
}
|
|
127
|
+
return { kind: 'ok', value: { tool, resolvedVersion: picked } };
|
|
128
|
+
},
|
|
129
|
+
has(id) {
|
|
130
|
+
const versions = tools.get(id);
|
|
131
|
+
return versions !== undefined && versions.size > 0;
|
|
132
|
+
},
|
|
133
|
+
hasVersion(id, version) {
|
|
134
|
+
return tools.get(id)?.has(version) ?? false;
|
|
135
|
+
},
|
|
136
|
+
list() {
|
|
137
|
+
const out = [];
|
|
138
|
+
const ids = [...tools.keys()].sort();
|
|
139
|
+
for (const id of ids) {
|
|
140
|
+
for (const v of versionsDesc(id)) {
|
|
141
|
+
const tool = tools.get(id)?.get(v);
|
|
142
|
+
if (tool !== undefined)
|
|
143
|
+
out.push(tool);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
},
|
|
148
|
+
ids() {
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const [id, versions] of tools) {
|
|
151
|
+
if (versions.size > 0)
|
|
152
|
+
out.push(id);
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
},
|
|
156
|
+
versions(id) {
|
|
157
|
+
return versionsDesc(id);
|
|
158
|
+
},
|
|
159
|
+
async forTenant() {
|
|
160
|
+
// Single-tenant: the same registry serves every tenant.
|
|
161
|
+
return registry;
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
for (const tool of seed) {
|
|
165
|
+
const r = registry.register(tool);
|
|
166
|
+
if (r.kind === 'err')
|
|
167
|
+
throw new Error(`seed failed: ${r.error.message}`);
|
|
168
|
+
}
|
|
169
|
+
return registry;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ToolContext, ToolSpec } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A synthesizer takes a declarative `ToolSpec` variant and returns
|
|
4
|
+
* the runtime handler that satisfies its semantics. Framework core
|
|
5
|
+
* ships the `'http'` synthesizer (see `./http.ts` for the impl).
|
|
6
|
+
* Third-party kinds (SQL, MCP, webhook, ...) register their own via
|
|
7
|
+
* `registerToolSpecSynthesizer(kind, fn)` at module load.
|
|
8
|
+
*
|
|
9
|
+
* The synthesizer receives the fully-validated spec (author-time
|
|
10
|
+
* validation has already run) and returns a handler with the
|
|
11
|
+
* standard `(input, ctx) => Promise<output>` signature. Any
|
|
12
|
+
* per-invoke correctness bits (URL substitution, abort chaining,
|
|
13
|
+
* secret resolution) live inside the handler the synthesizer
|
|
14
|
+
* returns.
|
|
15
|
+
*/
|
|
16
|
+
/** What the runtime gives a synthesized handler. */
|
|
17
|
+
export interface ToolSpecSynthesizerOptions {
|
|
18
|
+
/**
|
|
19
|
+
* The fetch for requests to the hosts a spec names. The runtime passes
|
|
20
|
+
* one that refuses the hosts its deployment forbids
|
|
21
|
+
* (`KINDGI_TENANT_HOST_ACCESS`: the cloud metadata endpoints and the
|
|
22
|
+
* server's own host). Absent: the global `fetch`.
|
|
23
|
+
*/
|
|
24
|
+
readonly fetch?: typeof fetch;
|
|
25
|
+
}
|
|
26
|
+
export type ToolSpecSynthesizer<TSpec extends ToolSpec = ToolSpec> = (spec: TSpec, toolId: string, options?: ToolSpecSynthesizerOptions) => (input: unknown, ctx: ToolContext) => Promise<unknown>;
|
|
27
|
+
/**
|
|
28
|
+
* Register a synthesizer for a given `spec.kind`. First-party kinds
|
|
29
|
+
* register at framework load; third-party kinds register when their
|
|
30
|
+
* package's module runs. Overwriting an existing kind throws — kind
|
|
31
|
+
* ownership must be explicit to avoid silent shadowing.
|
|
32
|
+
*/
|
|
33
|
+
export declare function registerToolSpecSynthesizer<TSpec extends ToolSpec>(kind: TSpec['kind'], synthesizer: ToolSpecSynthesizer<TSpec>): void;
|
|
34
|
+
/**
|
|
35
|
+
* Look up the synthesizer for a `ToolSpec`. Returns `undefined` when
|
|
36
|
+
* no synthesizer is registered for the spec's `kind` — callers
|
|
37
|
+
* (typically `defineTool`) surface this as a clear "no synthesizer
|
|
38
|
+
* for kind X" error at define time.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getToolSpecSynthesizer(kind: string): ToolSpecSynthesizer | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Test-only helper. Removes a registered synthesizer so tests can
|
|
43
|
+
* verify the "no synthesizer registered" error path without process
|
|
44
|
+
* teardown. Not exported from the package barrel — call it via
|
|
45
|
+
* direct import in tests only.
|
|
46
|
+
*/
|
|
47
|
+
export declare function _unregisterToolSpecSynthesizer(kind: string): void;
|
|
48
|
+
//# sourceMappingURL=spec-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-registry.d.ts","sourceRoot":"","sources":["../src/spec-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,oDAAoD;AACpD,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CAC/B;AAED,MAAM,MAAM,mBAAmB,CAAC,KAAK,SAAS,QAAQ,GAAG,QAAQ,IAAI,CACnE,IAAI,EAAE,KAAK,EACX,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,0BAA0B,KACjC,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAO5D;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,SAAS,QAAQ,EAChE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EACnB,WAAW,EAAE,mBAAmB,CAAC,KAAK,CAAC,GACtC,IAAI,CAON;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAEpF;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEjE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
// Registry keyed by the spec's `kind` discriminant. Populated by
|
|
4
|
+
// side-effect imports of first-party synthesizer packages (`./http.js`
|
|
5
|
+
// registers `'http'`) or third-party equivalents.
|
|
6
|
+
const registry = new Map();
|
|
7
|
+
/**
|
|
8
|
+
* Register a synthesizer for a given `spec.kind`. First-party kinds
|
|
9
|
+
* register at framework load; third-party kinds register when their
|
|
10
|
+
* package's module runs. Overwriting an existing kind throws — kind
|
|
11
|
+
* ownership must be explicit to avoid silent shadowing.
|
|
12
|
+
*/
|
|
13
|
+
export function registerToolSpecSynthesizer(kind, synthesizer) {
|
|
14
|
+
if (registry.has(kind)) {
|
|
15
|
+
throw new Error(`ToolSpec synthesizer for kind "${kind}" is already registered. Multiple registrations for the same kind indicate shadowing — unregister first if replacement is intended.`);
|
|
16
|
+
}
|
|
17
|
+
registry.set(kind, synthesizer);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Look up the synthesizer for a `ToolSpec`. Returns `undefined` when
|
|
21
|
+
* no synthesizer is registered for the spec's `kind` — callers
|
|
22
|
+
* (typically `defineTool`) surface this as a clear "no synthesizer
|
|
23
|
+
* for kind X" error at define time.
|
|
24
|
+
*/
|
|
25
|
+
export function getToolSpecSynthesizer(kind) {
|
|
26
|
+
return registry.get(kind);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Test-only helper. Removes a registered synthesizer so tests can
|
|
30
|
+
* verify the "no synthesizer registered" error path without process
|
|
31
|
+
* teardown. Not exported from the package barrel — call it via
|
|
32
|
+
* direct import in tests only.
|
|
33
|
+
*/
|
|
34
|
+
export function _unregisterToolSpecSynthesizer(kind) {
|
|
35
|
+
registry.delete(kind);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=spec-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-registry.js","sourceRoot":"","sources":["../src/spec-registry.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAmCjC,iEAAiE;AACjE,uEAAuE;AACvE,kDAAkD;AAClD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;AAExD;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,IAAmB,EACnB,WAAuC;IAEvC,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,kCAAkC,IAAI,qIAAqI,CAC5K,CAAC;IACJ,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,WAAkC,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,8BAA8B,CAAC,IAAY;IACzD,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
|