@vellumai/assistant 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +11 -12
- package/docker-entrypoint.sh +13 -1
- package/docker-init-apt-root.sh +79 -6
- package/openapi.yaml +336 -21
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/context-token-estimator.test.ts +30 -65
- package/src/__tests__/conversation-agent-loop.test.ts +57 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-runtime-assembly.test.ts +26 -4
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +5 -0
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +218 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +6 -73
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/loop.ts +167 -18
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/notifications.ts +65 -35
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +21 -29
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +1 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +72 -12
- package/src/context/token-estimator.ts +32 -34
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +29 -2
- package/src/daemon/conversation-runtime-assembly.ts +9 -0
- package/src/daemon/conversation.ts +0 -7
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +49 -61
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/process-message.ts +3 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -0
- package/src/heartbeat/heartbeat-service.ts +34 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +14 -2
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +190 -3
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +49 -20
- package/src/memory/v2/page-index.ts +38 -13
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +1 -0
- package/src/notifications/home-feed-side-effect.ts +85 -6
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/plugins/defaults/injectors.ts +38 -19
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +6 -51
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/system-prompt.ts +0 -8
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/system-sections.ts +0 -9
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +9 -20
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +2 -0
- package/src/providers/model-catalog.ts +199 -244
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +159 -6
- package/src/providers/openrouter/client.ts +42 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/runtime/agent-wake.ts +61 -1
- package/src/runtime/auth/route-policy.ts +13 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +0 -47
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +66 -4
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-query-routes.ts +70 -11
- package/src/runtime/routes/conversation-routes.ts +7 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +134 -1
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/subagent/manager.ts +2 -0
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/registry.ts +2 -2
- package/src/tools/types.ts +37 -2
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { OpenAIChatCompletionsProvider } from "../../openai/chat-completions-provider.js";
|
|
4
|
+
import {
|
|
5
|
+
buildProviderAdapter,
|
|
6
|
+
createAdapterFromConnection,
|
|
7
|
+
} from "../adapter-factory.js";
|
|
8
|
+
import type { ProviderConnection, ResolvedAuth } from "../auth.js";
|
|
9
|
+
|
|
10
|
+
describe("openai-compatible adapter factory", () => {
|
|
11
|
+
test("buildProviderAdapter returns an OpenAIChatCompletionsProvider", () => {
|
|
12
|
+
const adapter = buildProviderAdapter("openai-compatible", {
|
|
13
|
+
apiKey: "test-key",
|
|
14
|
+
model: "my-local-model",
|
|
15
|
+
streamTimeoutMs: 60_000,
|
|
16
|
+
baseURL: "http://localhost:8080/v1",
|
|
17
|
+
useNativeWebSearch: false,
|
|
18
|
+
});
|
|
19
|
+
expect(adapter).toBeInstanceOf(OpenAIChatCompletionsProvider);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("createAdapterFromConnection wires baseURL from ResolvedAuth", () => {
|
|
23
|
+
const connection: ProviderConnection = {
|
|
24
|
+
name: "my-vllm",
|
|
25
|
+
provider: "openai-compatible",
|
|
26
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
27
|
+
status: "active",
|
|
28
|
+
label: "vLLM",
|
|
29
|
+
baseUrl: "http://localhost:8080/v1",
|
|
30
|
+
models: [{ id: "my-model" }],
|
|
31
|
+
createdAt: Date.now(),
|
|
32
|
+
updatedAt: Date.now(),
|
|
33
|
+
isManaged: false,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const resolvedAuth: ResolvedAuth = {
|
|
37
|
+
kind: "header",
|
|
38
|
+
headers: { Authorization: "Bearer sk-test" },
|
|
39
|
+
baseUrl: "http://localhost:8080/v1",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const adapter = createAdapterFromConnection(connection, resolvedAuth, {
|
|
43
|
+
model: "my-model",
|
|
44
|
+
streamTimeoutMs: 60_000,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(adapter).not.toBeNull();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("createAdapterFromConnection rejects 'none' auth for openai-compatible", () => {
|
|
51
|
+
const connection: ProviderConnection = {
|
|
52
|
+
name: "my-vllm",
|
|
53
|
+
provider: "openai-compatible",
|
|
54
|
+
auth: { type: "none" },
|
|
55
|
+
status: "active",
|
|
56
|
+
label: null,
|
|
57
|
+
baseUrl: "http://localhost:8080/v1",
|
|
58
|
+
models: [{ id: "my-model" }],
|
|
59
|
+
createdAt: Date.now(),
|
|
60
|
+
updatedAt: Date.now(),
|
|
61
|
+
isManaged: false,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const resolvedAuth: ResolvedAuth = { kind: "none" };
|
|
65
|
+
|
|
66
|
+
const adapter = createAdapterFromConnection(connection, resolvedAuth, {
|
|
67
|
+
model: "my-model",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// openai-compatible is setupMode: "api-key", not keyless, so none auth
|
|
71
|
+
// should be rejected.
|
|
72
|
+
expect(adapter).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import { migrateCreateProviderConnections } from "../../../memory/migrations/243-provider-connections.js";
|
|
7
|
+
import { migrateProviderConnectionStatusLabel } from "../../../memory/migrations/244-provider-connection-status-label.js";
|
|
8
|
+
import { migrateProviderConnectionBaseUrlAndModels } from "../../../memory/migrations/250-provider-connection-base-url-and-models.js";
|
|
9
|
+
import * as schema from "../../../memory/schema.js";
|
|
10
|
+
import {
|
|
11
|
+
createConnection,
|
|
12
|
+
getConnection,
|
|
13
|
+
listConnections,
|
|
14
|
+
updateConnection,
|
|
15
|
+
} from "../connections.js";
|
|
16
|
+
|
|
17
|
+
function createTestDb() {
|
|
18
|
+
const sqlite = new Database(":memory:");
|
|
19
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
20
|
+
return drizzle(sqlite, { schema });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function bootDb() {
|
|
24
|
+
const db = createTestDb();
|
|
25
|
+
migrateCreateProviderConnections(db);
|
|
26
|
+
migrateProviderConnectionStatusLabel(db);
|
|
27
|
+
migrateProviderConnectionBaseUrlAndModels(db);
|
|
28
|
+
return db;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe("openai-compatible connection CRUD", () => {
|
|
32
|
+
test("create requires base_url", () => {
|
|
33
|
+
const db = bootDb();
|
|
34
|
+
const result = createConnection(db, {
|
|
35
|
+
name: "my-vllm",
|
|
36
|
+
provider: "openai-compatible",
|
|
37
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
38
|
+
models: [{ id: "my-model" }],
|
|
39
|
+
});
|
|
40
|
+
expect(result.ok).toBe(false);
|
|
41
|
+
if (!result.ok) {
|
|
42
|
+
expect(result.error.code).toBe("base_url_required");
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("create requires at least one model", () => {
|
|
47
|
+
const db = bootDb();
|
|
48
|
+
const result = createConnection(db, {
|
|
49
|
+
name: "my-vllm",
|
|
50
|
+
provider: "openai-compatible",
|
|
51
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
52
|
+
baseUrl: "http://localhost:8080/v1",
|
|
53
|
+
});
|
|
54
|
+
expect(result.ok).toBe(false);
|
|
55
|
+
if (!result.ok) {
|
|
56
|
+
expect(result.error.code).toBe("models_required");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("create rejects empty models array", () => {
|
|
61
|
+
const db = bootDb();
|
|
62
|
+
const result = createConnection(db, {
|
|
63
|
+
name: "my-vllm",
|
|
64
|
+
provider: "openai-compatible",
|
|
65
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
66
|
+
baseUrl: "http://localhost:8080/v1",
|
|
67
|
+
models: [],
|
|
68
|
+
});
|
|
69
|
+
expect(result.ok).toBe(false);
|
|
70
|
+
if (!result.ok) {
|
|
71
|
+
expect(result.error.code).toBe("models_required");
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("create persists baseUrl and models; round-trips via getConnection", () => {
|
|
76
|
+
const db = bootDb();
|
|
77
|
+
const models = [
|
|
78
|
+
{ id: "llama-3-70b" },
|
|
79
|
+
{ id: "mistral-7b", displayName: "Mistral 7B" },
|
|
80
|
+
];
|
|
81
|
+
const result = createConnection(db, {
|
|
82
|
+
name: "my-vllm",
|
|
83
|
+
provider: "openai-compatible",
|
|
84
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
85
|
+
baseUrl: "http://localhost:8080/v1",
|
|
86
|
+
models,
|
|
87
|
+
});
|
|
88
|
+
expect(result.ok).toBe(true);
|
|
89
|
+
if (result.ok) {
|
|
90
|
+
expect(result.connection.baseUrl).toBe("http://localhost:8080/v1");
|
|
91
|
+
expect(result.connection.models).toEqual(models);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const fetched = getConnection(db, "my-vllm");
|
|
95
|
+
expect(fetched).not.toBeNull();
|
|
96
|
+
expect(fetched!.baseUrl).toBe("http://localhost:8080/v1");
|
|
97
|
+
expect(fetched!.models).toEqual(models);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("non-openai-compatible providers leave baseUrl/models null", () => {
|
|
101
|
+
const db = bootDb();
|
|
102
|
+
const result = createConnection(db, {
|
|
103
|
+
name: "my-anthropic",
|
|
104
|
+
provider: "anthropic",
|
|
105
|
+
auth: { type: "api_key", credential: "cred-anthropic" },
|
|
106
|
+
});
|
|
107
|
+
expect(result.ok).toBe(true);
|
|
108
|
+
if (result.ok) {
|
|
109
|
+
expect(result.connection.baseUrl).toBeNull();
|
|
110
|
+
expect(result.connection.models).toBeNull();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("updateConnection can change models without re-supplying baseUrl", () => {
|
|
115
|
+
const db = bootDb();
|
|
116
|
+
createConnection(db, {
|
|
117
|
+
name: "my-vllm",
|
|
118
|
+
provider: "openai-compatible",
|
|
119
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
120
|
+
baseUrl: "http://localhost:8080/v1",
|
|
121
|
+
models: [{ id: "llama-3-70b" }],
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const result = updateConnection(db, "my-vllm", {
|
|
125
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
126
|
+
models: [{ id: "llama-3-70b" }, { id: "mistral-7b" }],
|
|
127
|
+
});
|
|
128
|
+
expect(result.ok).toBe(true);
|
|
129
|
+
if (result.ok) {
|
|
130
|
+
expect(result.connection.baseUrl).toBe("http://localhost:8080/v1");
|
|
131
|
+
expect(result.connection.models).toEqual([
|
|
132
|
+
{ id: "llama-3-70b" },
|
|
133
|
+
{ id: "mistral-7b" },
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("updateConnection rejects clearing models on openai-compatible", () => {
|
|
139
|
+
const db = bootDb();
|
|
140
|
+
createConnection(db, {
|
|
141
|
+
name: "my-vllm",
|
|
142
|
+
provider: "openai-compatible",
|
|
143
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
144
|
+
baseUrl: "http://localhost:8080/v1",
|
|
145
|
+
models: [{ id: "llama-3-70b" }],
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const result = updateConnection(db, "my-vllm", {
|
|
149
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
150
|
+
models: [],
|
|
151
|
+
});
|
|
152
|
+
expect(result.ok).toBe(false);
|
|
153
|
+
if (!result.ok) {
|
|
154
|
+
expect(result.error.code).toBe("models_required");
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("listConnections includes baseUrl and models", () => {
|
|
159
|
+
const db = bootDb();
|
|
160
|
+
createConnection(db, {
|
|
161
|
+
name: "my-vllm",
|
|
162
|
+
provider: "openai-compatible",
|
|
163
|
+
auth: { type: "api_key", credential: "cred-vllm" },
|
|
164
|
+
baseUrl: "http://localhost:8080/v1",
|
|
165
|
+
models: [{ id: "llama-3-70b" }],
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const connections = listConnections(db, {
|
|
169
|
+
provider: "openai-compatible",
|
|
170
|
+
});
|
|
171
|
+
expect(connections.length).toBe(1);
|
|
172
|
+
expect(connections[0]!.baseUrl).toBe("http://localhost:8080/v1");
|
|
173
|
+
expect(connections[0]!.models).toEqual([{ id: "llama-3-70b" }]);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -5,6 +5,7 @@ import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
|
5
5
|
|
|
6
6
|
import { migrateCreateProviderConnections } from "../../../memory/migrations/243-provider-connections.js";
|
|
7
7
|
import { migrateProviderConnectionStatusLabel } from "../../../memory/migrations/244-provider-connection-status-label.js";
|
|
8
|
+
import { migrateProviderConnectionBaseUrlAndModels } from "../../../memory/migrations/250-provider-connection-base-url-and-models.js";
|
|
8
9
|
import * as schema from "../../../memory/schema.js";
|
|
9
10
|
import {
|
|
10
11
|
createConnection,
|
|
@@ -25,6 +26,7 @@ function bootDb() {
|
|
|
25
26
|
const db = createTestDb();
|
|
26
27
|
migrateCreateProviderConnections(db);
|
|
27
28
|
migrateProviderConnectionStatusLabel(db);
|
|
29
|
+
migrateProviderConnectionBaseUrlAndModels(db);
|
|
28
30
|
return db;
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -199,6 +201,19 @@ describe("disableManagedConnectionsForByokHatch", () => {
|
|
|
199
201
|
expect(getConnection(db, "gemini-managed")?.status).toBe("disabled");
|
|
200
202
|
});
|
|
201
203
|
|
|
204
|
+
test("leaves an excluded managed connection active", () => {
|
|
205
|
+
const db = bootDb();
|
|
206
|
+
seedCanonicalConnections(db);
|
|
207
|
+
|
|
208
|
+
disableManagedConnectionsForByokHatch(db, {
|
|
209
|
+
excludeConnection: "anthropic-managed",
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
expect(getConnection(db, "anthropic-managed")?.status).toBe("active");
|
|
213
|
+
expect(getConnection(db, "openai-managed")?.status).toBe("disabled");
|
|
214
|
+
expect(getConnection(db, "gemini-managed")?.status).toBe("disabled");
|
|
215
|
+
});
|
|
216
|
+
|
|
202
217
|
test("subsequent seedCanonicalConnections call does NOT re-flip a user-re-enabled connection", () => {
|
|
203
218
|
// Models the post-hatch lifecycle: at hatch we disable; the user
|
|
204
219
|
// later flips one back to active (e.g. after Vellum login). Every
|
|
@@ -84,33 +84,22 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
|
|
|
84
84
|
apiKey: apiKey || undefined,
|
|
85
85
|
streamTimeoutMs,
|
|
86
86
|
}),
|
|
87
|
-
fireworks: ({ apiKey, model, streamTimeoutMs }) =>
|
|
88
|
-
new FireworksProvider(apiKey, model, {
|
|
87
|
+
fireworks: ({ apiKey, model, streamTimeoutMs, baseURL }) =>
|
|
88
|
+
new FireworksProvider(apiKey, model, {
|
|
89
|
+
streamTimeoutMs,
|
|
90
|
+
...(baseURL ? { baseURL } : {}),
|
|
91
|
+
}),
|
|
89
92
|
openrouter: ({ apiKey, model, streamTimeoutMs, useNativeWebSearch }) =>
|
|
90
93
|
new OpenRouterProvider(apiKey, model, {
|
|
91
94
|
useNativeWebSearch,
|
|
92
95
|
streamTimeoutMs,
|
|
93
96
|
}),
|
|
94
|
-
|
|
95
|
-
new OpenAIChatCompletionsProvider(apiKey, model, {
|
|
96
|
-
providerName: "zai",
|
|
97
|
-
providerLabel: "z.ai",
|
|
98
|
-
baseURL: "https://api.z.ai/api/paas/v4/",
|
|
99
|
-
streamTimeoutMs,
|
|
100
|
-
}),
|
|
101
|
-
deepseek: ({ apiKey, model, streamTimeoutMs }) =>
|
|
97
|
+
"openai-compatible": ({ apiKey, model, streamTimeoutMs, baseURL }) =>
|
|
102
98
|
new OpenAIChatCompletionsProvider(apiKey, model, {
|
|
103
|
-
providerName: "
|
|
104
|
-
providerLabel: "
|
|
105
|
-
baseURL: "https://api.deepseek.com",
|
|
106
|
-
streamTimeoutMs,
|
|
107
|
-
}),
|
|
108
|
-
minimax: ({ apiKey, model, streamTimeoutMs }) =>
|
|
109
|
-
new OpenAIChatCompletionsProvider(apiKey, model, {
|
|
110
|
-
providerName: "minimax",
|
|
111
|
-
providerLabel: "MiniMax",
|
|
112
|
-
baseURL: "https://api.minimax.io/v1",
|
|
99
|
+
providerName: "openai-compatible",
|
|
100
|
+
providerLabel: "OpenAI-compatible",
|
|
113
101
|
streamTimeoutMs,
|
|
102
|
+
...(baseURL ? { baseURL } : {}),
|
|
114
103
|
}),
|
|
115
104
|
};
|
|
116
105
|
|
|
@@ -86,6 +86,16 @@ export const ConnectionProviderSchema = z.enum(
|
|
|
86
86
|
export const ConnectionStatusSchema = z.enum(["active", "disabled"]);
|
|
87
87
|
export type ConnectionStatus = z.infer<typeof ConnectionStatusSchema>;
|
|
88
88
|
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Per-connection model entries (openai-compatible)
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
export const ConnectionModelSchema = z.object({
|
|
94
|
+
id: z.string().min(1),
|
|
95
|
+
displayName: z.string().min(1).optional(),
|
|
96
|
+
});
|
|
97
|
+
export type ConnectionModel = z.infer<typeof ConnectionModelSchema>;
|
|
98
|
+
|
|
89
99
|
// ---------------------------------------------------------------------------
|
|
90
100
|
// Full connection shape used by CRUD layer
|
|
91
101
|
// ---------------------------------------------------------------------------
|
|
@@ -96,6 +106,8 @@ export const ProviderConnectionSchema = z.object({
|
|
|
96
106
|
auth: AuthSchema,
|
|
97
107
|
status: ConnectionStatusSchema,
|
|
98
108
|
label: z.string().min(1).nullable(),
|
|
109
|
+
baseUrl: z.string().url().nullable(),
|
|
110
|
+
models: z.array(ConnectionModelSchema).nullable(),
|
|
99
111
|
createdAt: z.number().int(),
|
|
100
112
|
updatedAt: z.number().int(),
|
|
101
113
|
/**
|
|
@@ -21,7 +21,12 @@ import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
|
21
21
|
import type { DrizzleDb } from "../../memory/db-connection.js";
|
|
22
22
|
import { credentialKey } from "../../security/credential-key.js";
|
|
23
23
|
import { getLogger } from "../../util/logger.js";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
createConnection,
|
|
26
|
+
getConnection,
|
|
27
|
+
PROVIDERS_REQUIRING_BASE_URL_AND_MODELS,
|
|
28
|
+
seedCanonicalConnections,
|
|
29
|
+
} from "./connections.js";
|
|
25
30
|
|
|
26
31
|
const log = getLogger("provider-connections-backfill");
|
|
27
32
|
|
|
@@ -149,6 +154,14 @@ function ensureProviderConnection(
|
|
|
149
154
|
const provider = entry.provider as string | undefined;
|
|
150
155
|
if (!provider) return false;
|
|
151
156
|
|
|
157
|
+
if (PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
|
|
158
|
+
log.warn(
|
|
159
|
+
{ entry: entryLabel, provider },
|
|
160
|
+
"Skipping backfill for provider that requires per-connection base_url/models",
|
|
161
|
+
);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
let connectionName: string;
|
|
153
166
|
|
|
154
167
|
if (globalMode === "managed" && MANAGED_PROVIDERS.has(provider)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { and, eq, isNull } from "drizzle-orm";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
|
|
3
4
|
import type { DrizzleDb } from "../../memory/db-connection.js";
|
|
4
5
|
import { providerConnections } from "../../memory/schema/inference.js";
|
|
@@ -6,6 +7,8 @@ import { clearConnectionProviderCache } from "../registry.js";
|
|
|
6
7
|
import {
|
|
7
8
|
type Auth,
|
|
8
9
|
AuthSchema,
|
|
10
|
+
type ConnectionModel,
|
|
11
|
+
ConnectionModelSchema,
|
|
9
12
|
type ConnectionProvider,
|
|
10
13
|
ConnectionProviderSchema,
|
|
11
14
|
type ConnectionStatus,
|
|
@@ -14,6 +17,23 @@ import {
|
|
|
14
17
|
VALID_CONNECTION_PROVIDERS,
|
|
15
18
|
} from "./auth.js";
|
|
16
19
|
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Helpers
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
function parseModelsColumn(raw: string | null): ConnectionModel[] | null {
|
|
25
|
+
if (raw === null || raw === "") return null;
|
|
26
|
+
try {
|
|
27
|
+
const parsed = z.array(ConnectionModelSchema).safeParse(JSON.parse(raw));
|
|
28
|
+
return parsed.success ? parsed.data : null;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const PROVIDERS_REQUIRING_BASE_URL_AND_MODELS: ReadonlySet<string> =
|
|
35
|
+
new Set(["openai-compatible"]);
|
|
36
|
+
|
|
17
37
|
// ---------------------------------------------------------------------------
|
|
18
38
|
// Read
|
|
19
39
|
// ---------------------------------------------------------------------------
|
|
@@ -46,6 +66,8 @@ export function listConnections(
|
|
|
46
66
|
provider: provider.data,
|
|
47
67
|
status,
|
|
48
68
|
label: row.label ?? null,
|
|
69
|
+
baseUrl: row.baseUrl ?? null,
|
|
70
|
+
models: parseModelsColumn(row.models),
|
|
49
71
|
isManaged: MANAGED_CONNECTION_NAMES.has(row.name),
|
|
50
72
|
},
|
|
51
73
|
];
|
|
@@ -77,6 +99,8 @@ export function getConnection(
|
|
|
77
99
|
provider: provider.data,
|
|
78
100
|
status,
|
|
79
101
|
label: row.label ?? null,
|
|
102
|
+
baseUrl: row.baseUrl ?? null,
|
|
103
|
+
models: parseModelsColumn(row.models),
|
|
80
104
|
isManaged: MANAGED_CONNECTION_NAMES.has(row.name),
|
|
81
105
|
};
|
|
82
106
|
}
|
|
@@ -91,22 +115,30 @@ export type CreateConnectionInput = {
|
|
|
91
115
|
auth: Auth;
|
|
92
116
|
status?: ConnectionStatus;
|
|
93
117
|
label?: string | null;
|
|
118
|
+
baseUrl?: string | null;
|
|
119
|
+
models?: ConnectionModel[] | null;
|
|
94
120
|
};
|
|
95
121
|
|
|
96
122
|
export type UpdateConnectionInput = {
|
|
97
123
|
auth: Auth;
|
|
98
124
|
status?: ConnectionStatus;
|
|
99
125
|
label?: string | null;
|
|
126
|
+
baseUrl?: string | null;
|
|
127
|
+
models?: ConnectionModel[] | null;
|
|
100
128
|
};
|
|
101
129
|
|
|
102
130
|
export type ConnectionCreateError =
|
|
103
131
|
| { code: "already_exists" }
|
|
104
132
|
| { code: "invalid_provider"; provider: string }
|
|
105
|
-
| { code: "invalid_auth" }
|
|
133
|
+
| { code: "invalid_auth" }
|
|
134
|
+
| { code: "base_url_required" }
|
|
135
|
+
| { code: "models_required" };
|
|
106
136
|
|
|
107
137
|
export type ConnectionUpdateError =
|
|
108
138
|
| { code: "not_found" }
|
|
109
|
-
| { code: "invalid_auth" }
|
|
139
|
+
| { code: "invalid_auth" }
|
|
140
|
+
| { code: "base_url_required" }
|
|
141
|
+
| { code: "models_required" };
|
|
110
142
|
|
|
111
143
|
export type ConnectionDeleteError =
|
|
112
144
|
| { code: "not_found" }
|
|
@@ -143,6 +175,15 @@ export function createConnection(
|
|
|
143
175
|
|
|
144
176
|
const status = input.status ?? "active";
|
|
145
177
|
const label = input.label ?? null;
|
|
178
|
+
const baseUrl = input.baseUrl ?? null;
|
|
179
|
+
const models = input.models ?? null;
|
|
180
|
+
|
|
181
|
+
if (PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
|
|
182
|
+
if (!baseUrl) return { ok: false, error: { code: "base_url_required" } };
|
|
183
|
+
if (!models || models.length === 0) {
|
|
184
|
+
return { ok: false, error: { code: "models_required" } };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
146
187
|
|
|
147
188
|
const now = Date.now();
|
|
148
189
|
db.insert(providerConnections)
|
|
@@ -152,6 +193,8 @@ export function createConnection(
|
|
|
152
193
|
auth: JSON.stringify(authResult.data),
|
|
153
194
|
status,
|
|
154
195
|
label,
|
|
196
|
+
baseUrl,
|
|
197
|
+
models: models === null ? null : JSON.stringify(models),
|
|
155
198
|
createdAt: now,
|
|
156
199
|
updatedAt: now,
|
|
157
200
|
})
|
|
@@ -169,6 +212,8 @@ export function createConnection(
|
|
|
169
212
|
auth: authResult.data,
|
|
170
213
|
status,
|
|
171
214
|
label,
|
|
215
|
+
baseUrl,
|
|
216
|
+
models,
|
|
172
217
|
createdAt: now,
|
|
173
218
|
updatedAt: now,
|
|
174
219
|
isManaged: MANAGED_CONNECTION_NAMES.has(input.name),
|
|
@@ -193,15 +238,34 @@ export function updateConnection(
|
|
|
193
238
|
return { ok: false, error: { code: "invalid_auth" } };
|
|
194
239
|
}
|
|
195
240
|
|
|
241
|
+
const nextBaseUrl =
|
|
242
|
+
input.baseUrl !== undefined ? input.baseUrl : existing.baseUrl;
|
|
243
|
+
const nextModels =
|
|
244
|
+
input.models !== undefined ? input.models : existing.models;
|
|
245
|
+
|
|
246
|
+
if (PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(existing.provider)) {
|
|
247
|
+
if (!nextBaseUrl)
|
|
248
|
+
return { ok: false, error: { code: "base_url_required" } };
|
|
249
|
+
if (!nextModels || nextModels.length === 0) {
|
|
250
|
+
return { ok: false, error: { code: "models_required" } };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
196
254
|
const now = Date.now();
|
|
197
255
|
const setClause: {
|
|
198
256
|
auth: string;
|
|
199
257
|
updatedAt: number;
|
|
200
258
|
status?: string;
|
|
201
259
|
label?: string | null;
|
|
260
|
+
baseUrl?: string | null;
|
|
261
|
+
models?: string | null;
|
|
202
262
|
} = { auth: JSON.stringify(authResult.data), updatedAt: now };
|
|
203
263
|
if (input.status !== undefined) setClause.status = input.status;
|
|
204
264
|
if (input.label !== undefined) setClause.label = input.label;
|
|
265
|
+
if (input.baseUrl !== undefined) setClause.baseUrl = input.baseUrl;
|
|
266
|
+
if (input.models !== undefined)
|
|
267
|
+
setClause.models =
|
|
268
|
+
input.models === null ? null : JSON.stringify(input.models);
|
|
205
269
|
|
|
206
270
|
db.update(providerConnections)
|
|
207
271
|
.set(setClause)
|
|
@@ -218,6 +282,8 @@ export function updateConnection(
|
|
|
218
282
|
auth: authResult.data,
|
|
219
283
|
status: input.status !== undefined ? input.status : existing.status,
|
|
220
284
|
label: input.label !== undefined ? input.label : existing.label,
|
|
285
|
+
baseUrl: nextBaseUrl,
|
|
286
|
+
models: nextModels,
|
|
221
287
|
updatedAt: now,
|
|
222
288
|
},
|
|
223
289
|
};
|
|
@@ -294,6 +360,12 @@ const CANONICAL_CONNECTIONS: Array<{
|
|
|
294
360
|
auth: { type: "platform" },
|
|
295
361
|
label: "Google Gemini",
|
|
296
362
|
},
|
|
363
|
+
{
|
|
364
|
+
name: "fireworks-managed",
|
|
365
|
+
provider: "fireworks",
|
|
366
|
+
auth: { type: "platform" },
|
|
367
|
+
label: "Fireworks",
|
|
368
|
+
},
|
|
297
369
|
];
|
|
298
370
|
|
|
299
371
|
/**
|
|
@@ -330,7 +402,7 @@ export const MANAGED_CONNECTION_NAMES: ReadonlySet<string> = new Set(
|
|
|
330
402
|
*
|
|
331
403
|
* Status handling: the upsert never touches `status` so user customization
|
|
332
404
|
* is preserved across reboots. New rows default to `status: "active"` via the
|
|
333
|
-
* column default. Off-platform installs flip
|
|
405
|
+
* column default. Off-platform installs flip canonical managed rows to
|
|
334
406
|
* `status: "disabled"` ONCE at hatch time via
|
|
335
407
|
* `disableManagedConnectionsForByokHatch` (called from `seedInferenceProfiles`
|
|
336
408
|
* when `isHatch && !isPlatform`); subsequent boots leave whatever the user
|
|
@@ -374,7 +446,7 @@ export function seedCanonicalConnections(db: DrizzleDb): void {
|
|
|
374
446
|
}
|
|
375
447
|
|
|
376
448
|
/**
|
|
377
|
-
* Flip
|
|
449
|
+
* Flip canonical managed connections to `status: "disabled"` at
|
|
378
450
|
* hatch time on BYOK (off-platform) installs.
|
|
379
451
|
*
|
|
380
452
|
* Why hatch-time only: managed connections need platform auth that a fresh
|
|
@@ -389,10 +461,18 @@ export function seedCanonicalConnections(db: DrizzleDb): void {
|
|
|
389
461
|
*
|
|
390
462
|
* Idempotent: a second hatch (workspace reset) re-disables the rows, which
|
|
391
463
|
* is the right call — re-hatch means re-onboard.
|
|
464
|
+
*
|
|
465
|
+
* When onboarding explicitly selected a managed profile, callers may exclude
|
|
466
|
+
* that selected connection so the managed route remains usable for the first
|
|
467
|
+
* post-onboarding message.
|
|
392
468
|
*/
|
|
393
|
-
export function disableManagedConnectionsForByokHatch(
|
|
469
|
+
export function disableManagedConnectionsForByokHatch(
|
|
470
|
+
db: DrizzleDb,
|
|
471
|
+
options: { excludeConnection?: string } = {},
|
|
472
|
+
): void {
|
|
394
473
|
const now = Date.now();
|
|
395
474
|
for (const name of MANAGED_CONNECTION_NAMES) {
|
|
475
|
+
if (name === options.excludeConnection) continue;
|
|
396
476
|
db.update(providerConnections)
|
|
397
477
|
.set({ status: "disabled", updatedAt: now })
|
|
398
478
|
.where(eq(providerConnections.name, name))
|
|
@@ -23,6 +23,7 @@ export type ResolveAuthError =
|
|
|
23
23
|
export async function resolveAuth(
|
|
24
24
|
auth: Auth,
|
|
25
25
|
provider: string,
|
|
26
|
+
opts: { baseUrl?: string | null } = {},
|
|
26
27
|
): Promise<
|
|
27
28
|
{ ok: true; resolved: ResolvedAuth } | { ok: false; error: ResolveAuthError }
|
|
28
29
|
> {
|
|
@@ -40,6 +41,7 @@ export async function resolveAuth(
|
|
|
40
41
|
resolved: {
|
|
41
42
|
kind: "header",
|
|
42
43
|
headers: { Authorization: `Bearer ${value}` },
|
|
44
|
+
...(opts.baseUrl ? { baseUrl: opts.baseUrl } : {}),
|
|
43
45
|
},
|
|
44
46
|
};
|
|
45
47
|
}
|