opencode-ext-connector 0.3.2 → 0.4.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/CHANGELOG.md +36 -0
- package/README.md +160 -40
- package/dist/core/options.d.ts +4 -0
- package/dist/core/options.js +35 -6
- package/dist/opencode/host-options.d.ts +8 -1
- package/dist/opencode/host-options.js +24 -7
- package/dist/opencode/language-factory.d.ts +1 -1
- package/dist/opencode/language-factory.js +3 -0
- package/dist/opencode/ollama-probe.d.ts +2 -1
- package/dist/opencode/ollama-probe.js +2 -2
- package/dist/opencode/ollama-production.d.ts +7 -0
- package/dist/opencode/ollama-production.js +19 -7
- package/dist/opencode/provider-entry.d.ts +1 -0
- package/dist/opencode/providers.d.ts +2 -0
- package/dist/opencode/providers.js +12 -5
- package/dist/opencode/v1-catalog.js +6 -1
- package/dist/opencode/v1-language.js +5 -2
- package/dist/opencode/v1-session-auth.d.ts +2 -1
- package/dist/opencode/v1-session-auth.js +7 -6
- package/dist/providers/command-code/language-model.js +46 -39
- package/dist/providers/command-code/open-generation.d.ts +16 -0
- package/dist/providers/command-code/open-generation.js +45 -0
- package/dist/providers/cursor/credential-retry.d.ts +14 -0
- package/dist/providers/cursor/credential-retry.js +26 -0
- package/dist/providers/cursor/direct-run-failure.d.ts +8 -0
- package/dist/providers/cursor/direct-run-failure.js +26 -0
- package/dist/providers/cursor/direct-run-types.d.ts +1 -0
- package/dist/providers/cursor/direct-run.js +40 -27
- package/dist/providers/cursor/direct-runtime.js +26 -39
- package/dist/providers/cursor/run-session.js +1 -1
- package/dist/providers/ollama/adapter.d.ts +2 -0
- package/dist/providers/ollama/adapter.js +3 -1
- package/dist/providers/ollama/endpoints.d.ts +8 -0
- package/dist/providers/ollama/endpoints.js +38 -0
- package/dist/providers/ollama/index.d.ts +1 -0
- package/dist/providers/ollama/index.js +1 -0
- package/dist/providers/ollama/language-model.d.ts +2 -0
- package/dist/providers/ollama/language-model.js +2 -0
- package/dist/providers/ollama/local-catalog.d.ts +2 -1
- package/dist/providers/ollama/local-catalog.js +3 -3
- package/dist/providers/ollama/runtime.d.ts +2 -0
- package/dist/providers/ollama/runtime.js +5 -5
- package/dist/sdk/ollama.d.ts +4 -1
- package/dist/server.js +33 -7
- package/docs/README.ko.md +160 -40
- package/package.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ProviderAdapter } from "../../core/adapter.js";
|
|
2
2
|
import type { OllamaCatalogState } from "./catalog-state.js";
|
|
3
|
+
import { type OllamaEndpoints } from "./endpoints.js";
|
|
3
4
|
import type { OllamaFetch } from "./http.js";
|
|
4
5
|
export type OllamaAdapterOptions = {
|
|
5
6
|
readonly fetch: OllamaFetch;
|
|
6
7
|
readonly catalog: OllamaCatalogState;
|
|
8
|
+
readonly endpoints?: OllamaEndpoints;
|
|
7
9
|
};
|
|
8
10
|
export declare function createOllamaAdapter(options: OllamaAdapterOptions): ProviderAdapter;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OperationCancelledError } from "../../core/errors.js";
|
|
2
2
|
import { parseProviderId } from "../../core/ids.js";
|
|
3
3
|
import { createAsyncDisposable } from "../../core/lifecycle.js";
|
|
4
|
+
import { parseOllamaEndpoints } from "./endpoints.js";
|
|
4
5
|
import { OllamaCatalogError } from "./errors.js";
|
|
5
6
|
import { listLocalOllamaModels } from "./local-catalog.js";
|
|
6
7
|
function mergeModels(local, cloud) {
|
|
@@ -23,6 +24,7 @@ function catalogFailure(error) {
|
|
|
23
24
|
}
|
|
24
25
|
export function createOllamaAdapter(options) {
|
|
25
26
|
const providerId = parseProviderId("ollama");
|
|
27
|
+
const endpoints = options.endpoints ?? parseOllamaEndpoints(undefined);
|
|
26
28
|
const lease = options.catalog.acquire();
|
|
27
29
|
const disposal = createAsyncDisposable(() => lease.dispose());
|
|
28
30
|
let lastMergedModels = null;
|
|
@@ -33,7 +35,7 @@ export function createOllamaAdapter(options) {
|
|
|
33
35
|
throw new OperationCancelledError("ollama-snapshot");
|
|
34
36
|
let local;
|
|
35
37
|
try {
|
|
36
|
-
local = await listLocalOllamaModels(options.fetch, signal);
|
|
38
|
+
local = await listLocalOllamaModels(options.fetch, signal, endpoints);
|
|
37
39
|
}
|
|
38
40
|
catch (error) {
|
|
39
41
|
const failure = catalogFailure(error);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
2
|
+
export type OllamaEndpoints = {
|
|
3
|
+
readonly baseURL: string;
|
|
4
|
+
readonly tagsURL: string;
|
|
5
|
+
readonly pullURL: string;
|
|
6
|
+
readonly chatURL: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function parseOllamaEndpoints(input: unknown): OllamaEndpoints;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
3
|
+
const DirectApiPaths = ["/api/tags", "/api/pull", "/api/chat"];
|
|
4
|
+
const OllamaBaseURLSchema = z
|
|
5
|
+
.string()
|
|
6
|
+
.refine((value) => value.length > 0 &&
|
|
7
|
+
value === value.trim() &&
|
|
8
|
+
/^https?:\/\//iu.test(value) &&
|
|
9
|
+
!/[\t\r\n\\]/u.test(value) &&
|
|
10
|
+
!/^https?:\/\/[^/?#]*@/iu.test(value) &&
|
|
11
|
+
!/[?#]$/u.test(value) &&
|
|
12
|
+
URL.canParse(value))
|
|
13
|
+
.transform((value) => new URL(value))
|
|
14
|
+
.superRefine((url, context) => {
|
|
15
|
+
const cloudHostname = url.hostname.toLowerCase().replace(/\.+$/u, "");
|
|
16
|
+
const path = url.pathname.replace(/\/+$/u, "") || "/";
|
|
17
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:") ||
|
|
18
|
+
url.hostname.length === 0 ||
|
|
19
|
+
url.username.length > 0 ||
|
|
20
|
+
url.password.length > 0 ||
|
|
21
|
+
url.search.length > 0 ||
|
|
22
|
+
url.hash.length > 0 ||
|
|
23
|
+
cloudHostname === "ollama.com" ||
|
|
24
|
+
cloudHostname.endsWith(".ollama.com") ||
|
|
25
|
+
DirectApiPaths.some((apiPath) => path.endsWith(apiPath))) {
|
|
26
|
+
context.addIssue({ code: "custom", message: "invalid Ollama daemon base URL" });
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.transform((url) => `${url.origin}${url.pathname.replace(/\/+$/u, "")}`);
|
|
30
|
+
export function parseOllamaEndpoints(input) {
|
|
31
|
+
const baseURL = OllamaBaseURLSchema.parse(input === undefined ? DEFAULT_OLLAMA_BASE_URL : input);
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
baseURL,
|
|
34
|
+
tagsURL: `${baseURL}/api/tags`,
|
|
35
|
+
pullURL: `${baseURL}/api/pull`,
|
|
36
|
+
chatURL: `${baseURL}/api/chat`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createOllamaAdapter, type OllamaAdapterOptions } from "./adapter.js";
|
|
2
2
|
export { createOllamaCatalogState, type OllamaCatalogLease, type OllamaCatalogState, type OllamaCatalogStateOptions, } from "./catalog-state.js";
|
|
3
3
|
export { discoverOllamaCloudModels } from "./cloud-catalog.js";
|
|
4
|
+
export { DEFAULT_OLLAMA_BASE_URL, type OllamaEndpoints, parseOllamaEndpoints, } from "./endpoints.js";
|
|
4
5
|
export { OllamaCatalogError, type OllamaCatalogErrorKind, OllamaGenerationError, type OllamaGenerationOperation, } from "./errors.js";
|
|
5
6
|
export { type OllamaFetch, productionOllamaFetch } from "./http.js";
|
|
6
7
|
export { createOllamaLanguageModel, type OllamaLanguageModelOptions, } from "./language-model.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createOllamaAdapter } from "./adapter.js";
|
|
2
2
|
export { createOllamaCatalogState, } from "./catalog-state.js";
|
|
3
3
|
export { discoverOllamaCloudModels } from "./cloud-catalog.js";
|
|
4
|
+
export { DEFAULT_OLLAMA_BASE_URL, parseOllamaEndpoints, } from "./endpoints.js";
|
|
4
5
|
export { OllamaCatalogError, OllamaGenerationError, } from "./errors.js";
|
|
5
6
|
export { productionOllamaFetch } from "./http.js";
|
|
6
7
|
export { createOllamaLanguageModel, } from "./language-model.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
2
2
|
import type { OllamaCatalogState } from "./catalog-state.js";
|
|
3
|
+
import { type OllamaEndpoints } from "./endpoints.js";
|
|
3
4
|
import type { OllamaFetch } from "./http.js";
|
|
4
5
|
import { type OllamaRuntime } from "./runtime.js";
|
|
5
6
|
export type OllamaLanguageModelOptions = {
|
|
@@ -7,5 +8,6 @@ export type OllamaLanguageModelOptions = {
|
|
|
7
8
|
readonly runtime?: OllamaRuntime;
|
|
8
9
|
readonly catalog?: OllamaCatalogState;
|
|
9
10
|
readonly fetch?: OllamaFetch;
|
|
11
|
+
readonly endpoints?: OllamaEndpoints;
|
|
10
12
|
};
|
|
11
13
|
export declare function createOllamaLanguageModel(options: OllamaLanguageModelOptions): LanguageModelV3;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OperationCancelledError } from "../../core/errors.js";
|
|
2
|
+
import { parseOllamaEndpoints } from "./endpoints.js";
|
|
2
3
|
import { generateFromOllamaStream } from "./generate.js";
|
|
3
4
|
import { buildOllamaCall } from "./prompt.js";
|
|
4
5
|
import { createOllamaRuntime } from "./runtime.js";
|
|
@@ -10,6 +11,7 @@ function runtimeFromOptions(options) {
|
|
|
10
11
|
throw new TypeError("Ollama catalog state is required");
|
|
11
12
|
return createOllamaRuntime({
|
|
12
13
|
catalog: options.catalog,
|
|
14
|
+
endpoints: options.endpoints ?? parseOllamaEndpoints(undefined),
|
|
13
15
|
...(options.fetch === undefined ? {} : { fetch: options.fetch }),
|
|
14
16
|
});
|
|
15
17
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { AdapterModel } from "../../core/models.js";
|
|
2
|
+
import { type OllamaEndpoints } from "./endpoints.js";
|
|
2
3
|
import { type OllamaFetch } from "./http.js";
|
|
3
|
-
export declare function listLocalOllamaModels(fetch: OllamaFetch, signal: AbortSignal): Promise<readonly AdapterModel[]>;
|
|
4
|
+
export declare function listLocalOllamaModels(fetch: OllamaFetch, signal: AbortSignal, endpoints?: OllamaEndpoints): Promise<readonly AdapterModel[]>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { parseAdapterModel } from "../../core/models.js";
|
|
3
|
+
import { parseOllamaEndpoints } from "./endpoints.js";
|
|
3
4
|
import { OllamaCatalogError } from "./errors.js";
|
|
4
5
|
import { requestOllamaCatalog } from "./http.js";
|
|
5
|
-
const LOCAL_TAGS_URL = "http://localhost:11434/api/tags";
|
|
6
6
|
const LocalModelSchema = z
|
|
7
7
|
.object({ model: z.string().optional(), name: z.string().optional() })
|
|
8
8
|
.superRefine((value, context) => {
|
|
@@ -17,9 +17,9 @@ const LocalModelSchema = z
|
|
|
17
17
|
})
|
|
18
18
|
.transform(({ model, name }) => (model?.length === 0 ? undefined : model) ?? name ?? "");
|
|
19
19
|
const LocalTagsSchema = z.object({ models: z.array(LocalModelSchema) }).readonly();
|
|
20
|
-
export async function listLocalOllamaModels(fetch, signal) {
|
|
20
|
+
export async function listLocalOllamaModels(fetch, signal, endpoints = parseOllamaEndpoints(undefined)) {
|
|
21
21
|
const text = await requestOllamaCatalog({
|
|
22
|
-
url:
|
|
22
|
+
url: endpoints.tagsURL,
|
|
23
23
|
accept: "application/json",
|
|
24
24
|
operation: "local-tags",
|
|
25
25
|
fetch,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { OllamaCatalogState } from "./catalog-state.js";
|
|
2
|
+
import { type OllamaEndpoints } from "./endpoints.js";
|
|
2
3
|
import { type OllamaFetch } from "./http.js";
|
|
3
4
|
import { type OllamaChatRequest } from "./protocol.js";
|
|
4
5
|
export type OllamaRuntimeOptions = {
|
|
5
6
|
readonly catalog: OllamaCatalogState;
|
|
7
|
+
readonly endpoints?: OllamaEndpoints;
|
|
6
8
|
readonly fetch?: OllamaFetch;
|
|
7
9
|
};
|
|
8
10
|
export interface OllamaRuntime {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { OperationCancelledError } from "../../core/errors.js";
|
|
2
|
+
import { parseOllamaEndpoints } from "./endpoints.js";
|
|
2
3
|
import { OllamaGenerationError } from "./errors.js";
|
|
3
4
|
import { productionOllamaFetch } from "./http.js";
|
|
4
5
|
import { listLocalOllamaModels } from "./local-catalog.js";
|
|
5
6
|
import { parseOllamaNdjson } from "./ndjson.js";
|
|
6
7
|
import { OllamaPullChunkSchema } from "./protocol.js";
|
|
7
|
-
const PULL_URL = "http://localhost:11434/api/pull";
|
|
8
|
-
const CHAT_URL = "http://localhost:11434/api/chat";
|
|
9
8
|
const JSON_HEADERS = {
|
|
10
9
|
accept: "application/x-ndjson",
|
|
11
10
|
"content-type": "application/json",
|
|
@@ -43,6 +42,7 @@ async function post(fetch, url, body, signal, operation) {
|
|
|
43
42
|
}
|
|
44
43
|
export function createOllamaRuntime(options) {
|
|
45
44
|
const fetch = options.fetch ?? productionOllamaFetch;
|
|
45
|
+
const endpoints = options.endpoints ?? parseOllamaEndpoints(undefined);
|
|
46
46
|
const pulls = new Map();
|
|
47
47
|
const pull = (modelId) => {
|
|
48
48
|
const existing = pulls.get(modelId);
|
|
@@ -50,7 +50,7 @@ export function createOllamaRuntime(options) {
|
|
|
50
50
|
return existing;
|
|
51
51
|
const controller = new AbortController();
|
|
52
52
|
const promise = (async () => {
|
|
53
|
-
const response = await post(fetch,
|
|
53
|
+
const response = await post(fetch, endpoints.pullURL, { model: modelId, stream: true }, controller.signal, "pull");
|
|
54
54
|
let succeeded = false;
|
|
55
55
|
for await (const chunk of parseOllamaNdjson(response, OllamaPullChunkSchema, "pull-response")) {
|
|
56
56
|
if (chunk.error !== undefined)
|
|
@@ -109,7 +109,7 @@ export function createOllamaRuntime(options) {
|
|
|
109
109
|
openChat: async (request, signal) => {
|
|
110
110
|
let local;
|
|
111
111
|
try {
|
|
112
|
-
local = await listLocalOllamaModels(fetch, signal);
|
|
112
|
+
local = await listLocalOllamaModels(fetch, signal, endpoints);
|
|
113
113
|
}
|
|
114
114
|
catch (error) {
|
|
115
115
|
if (error instanceof OperationCancelledError)
|
|
@@ -122,7 +122,7 @@ export function createOllamaRuntime(options) {
|
|
|
122
122
|
}
|
|
123
123
|
await waitForPull(request.model, pull(request.model), signal);
|
|
124
124
|
}
|
|
125
|
-
return post(fetch,
|
|
125
|
+
return post(fetch, endpoints.chatURL, request, signal, "chat");
|
|
126
126
|
},
|
|
127
127
|
};
|
|
128
128
|
}
|
package/dist/sdk/ollama.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
|
2
2
|
export type OllamaProvider = {
|
|
3
3
|
readonly languageModel: (modelId: string) => LanguageModelV3;
|
|
4
4
|
};
|
|
5
|
-
export
|
|
5
|
+
export type OllamaProviderOptions = {
|
|
6
|
+
readonly ollamaBaseURL?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function createOllama(options?: OllamaProviderOptions): OllamaProvider;
|
package/dist/server.js
CHANGED
|
@@ -5,11 +5,13 @@ import { parseConnectorOptions } from "./core/options.js";
|
|
|
5
5
|
import { createFetchHttpTransport } from "./http/fetch-transport.js";
|
|
6
6
|
import { createConsoleLogger } from "./logging/logger.js";
|
|
7
7
|
import { createOpenCodeAuthStore } from "./opencode/auth-store.js";
|
|
8
|
-
import { pickConnectorOptionsInput } from "./opencode/host-options.js";
|
|
8
|
+
import { pickConnectorOptionsInput, pickOllamaBaseURL } from "./opencode/host-options.js";
|
|
9
|
+
import { getProductionOllamaBundle } from "./opencode/ollama-production.js";
|
|
9
10
|
import { createProviderRegistry, selectConfiguredProviders } from "./opencode/providers.js";
|
|
10
11
|
import { disposeV1LanguageRuntime } from "./opencode/v1-language.js";
|
|
11
|
-
import { createV1AuthServer, createV1Server } from "./opencode/v1-module.js";
|
|
12
|
+
import { buildV1AuthHooks, createV1AuthServer, createV1Server } from "./opencode/v1-module.js";
|
|
12
13
|
import { writeClaudeCredentials } from "./providers/claude/writeback.js";
|
|
14
|
+
import { productionOllamaFetch } from "./providers/ollama/http.js";
|
|
13
15
|
const env = process.env;
|
|
14
16
|
const transport = createFetchHttpTransport();
|
|
15
17
|
const authStore = createOpenCodeAuthStore({ env });
|
|
@@ -44,7 +46,21 @@ const providerDeps = {
|
|
|
44
46
|
};
|
|
45
47
|
export const connectorServer = async (input, options) => {
|
|
46
48
|
const connectorOptions = parseConnectorOptions(pickConnectorOptionsInput(options));
|
|
47
|
-
const
|
|
49
|
+
const ollama = connectorOptions.providers.includes("ollama")
|
|
50
|
+
? getProductionOllamaBundle(pickOllamaBaseURL(options))
|
|
51
|
+
: undefined;
|
|
52
|
+
const providers = selectConfiguredProviders(createProviderRegistry({
|
|
53
|
+
writeClaudeCredentials,
|
|
54
|
+
...(ollama === undefined
|
|
55
|
+
? {}
|
|
56
|
+
: {
|
|
57
|
+
ollama: {
|
|
58
|
+
fetch: productionOllamaFetch,
|
|
59
|
+
catalog: ollama.catalog,
|
|
60
|
+
endpoints: ollama.endpoints,
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
}), connectorOptions.providers);
|
|
48
64
|
const hooks = await createV1Server({
|
|
49
65
|
clock,
|
|
50
66
|
transport,
|
|
@@ -74,7 +90,6 @@ export const connectorServer = async (input, options) => {
|
|
|
74
90
|
const claudeEntry = registry.find((entry) => entry.id === "claude");
|
|
75
91
|
const cursorEntry = registry.find((entry) => entry.id === "cursor");
|
|
76
92
|
const commandCodeEntry = registry.find((entry) => entry.id === "command-code");
|
|
77
|
-
const ollamaEntry = registry.find((entry) => entry.id === "ollama");
|
|
78
93
|
export const claudeAuthServer = claudeEntry === undefined
|
|
79
94
|
? async () => ({})
|
|
80
95
|
: createV1AuthServer(claudeEntry, providerDeps);
|
|
@@ -84,9 +99,20 @@ export const cursorAuthServer = cursorEntry === undefined
|
|
|
84
99
|
export const commandCodeAuthServer = commandCodeEntry === undefined
|
|
85
100
|
? async () => ({})
|
|
86
101
|
: createV1AuthServer(commandCodeEntry, providerDeps);
|
|
87
|
-
export const ollamaAuthServer =
|
|
88
|
-
|
|
89
|
-
|
|
102
|
+
export const ollamaAuthServer = async (_input, options) => {
|
|
103
|
+
const connectorOptions = parseConnectorOptions(pickConnectorOptionsInput(options));
|
|
104
|
+
if (!connectorOptions.providers.includes("ollama"))
|
|
105
|
+
return {};
|
|
106
|
+
const ollama = getProductionOllamaBundle(pickOllamaBaseURL(options));
|
|
107
|
+
const entry = createProviderRegistry({
|
|
108
|
+
ollama: {
|
|
109
|
+
fetch: productionOllamaFetch,
|
|
110
|
+
catalog: ollama.catalog,
|
|
111
|
+
endpoints: ollama.endpoints,
|
|
112
|
+
},
|
|
113
|
+
}).find((candidate) => candidate.id === "ollama");
|
|
114
|
+
return entry === undefined ? {} : buildV1AuthHooks(entry, providerDeps, options);
|
|
115
|
+
};
|
|
90
116
|
export const plugin = {
|
|
91
117
|
id: "opencode-ext-connector",
|
|
92
118
|
server: connectorServer,
|