summaryrefslogtreecommitdiffhomepage
path: root/packages/credential-store/src/registry.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-05 21:20:12 +0900
committerAdam Malczewski <[email protected]>2026-06-05 21:20:12 +0900
commit7fb3269c698ae583ea7997ce206c4ae252fd3218 (patch)
tree247d03408ecccd633290ea56b1b08811ebe460ec /packages/credential-store/src/registry.ts
parent4283d1f8a0bc3953e65962a2364c903d0015f047 (diff)
downloaddispatch-7fb3269c698ae583ea7997ce206c4ae252fd3218.tar.gz
dispatch-7fb3269c698ae583ea7997ce206c4ae252fd3218.zip
feat(backend): credential-store + model selection/catalog (GET /models) + per-turn cwd through orchestrator/transport/host-bin
Diffstat (limited to 'packages/credential-store/src/registry.ts')
-rw-r--r--packages/credential-store/src/registry.ts80
1 files changed, 80 insertions, 0 deletions
diff --git a/packages/credential-store/src/registry.ts b/packages/credential-store/src/registry.ts
new file mode 100644
index 0000000..c1ba771
--- /dev/null
+++ b/packages/credential-store/src/registry.ts
@@ -0,0 +1,80 @@
+import type { ProviderContract } from "@dispatch/kernel";
+
+export interface Credential {
+ readonly name: string;
+ readonly providerId: string;
+}
+
+export interface ResolvedModel {
+ readonly providerId: string;
+ readonly model: string;
+}
+
+export interface CredentialStore {
+ /**
+ * Split a model name on the FIRST "/": name=before, model=after (model may contain "/").
+ * Look up the credential by name; return its providerId + the model id, or undefined if
+ * the name is unknown or there is no model segment.
+ */
+ resolve(modelName: string): ResolvedModel | undefined;
+
+ /**
+ * The model catalog: for each credential, look up its provider and call listModels(),
+ * emitting `${credential.name}/${modelInfo.id}`. Skip credentials whose provider is
+ * missing or has no listModels.
+ */
+ listCatalog(): Promise<readonly string[]>;
+}
+
+export interface CredentialStoreDeps {
+ readonly credentials: readonly Credential[];
+ readonly getProvider: (id: string) => ProviderContract | undefined;
+}
+
+export function createCredentialStore(deps: CredentialStoreDeps): CredentialStore {
+ const credentialMap = new Map<string, string>();
+ for (const credential of deps.credentials) {
+ credentialMap.set(credential.name, credential.providerId);
+ }
+
+ return {
+ resolve(modelName: string): ResolvedModel | undefined {
+ const slashIndex = modelName.indexOf("/");
+ if (slashIndex === -1) {
+ return undefined;
+ }
+
+ const credentialName = modelName.slice(0, slashIndex);
+ const model = modelName.slice(slashIndex + 1);
+
+ if (!model) {
+ return undefined;
+ }
+
+ const providerId = credentialMap.get(credentialName);
+ if (!providerId) {
+ return undefined;
+ }
+
+ return { providerId, model };
+ },
+
+ async listCatalog(): Promise<readonly string[]> {
+ const results: string[] = [];
+
+ for (const credential of deps.credentials) {
+ const provider = deps.getProvider(credential.providerId);
+ if (!provider?.listModels) {
+ continue;
+ }
+
+ const models = await provider.listModels();
+ for (const model of models) {
+ results.push(`${credential.name}/${model.id}`);
+ }
+ }
+
+ return results;
+ },
+ };
+}