@openclaw/kilocode-provider 0.0.0 → 2026.6.9

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/README.md CHANGED
@@ -1,3 +1,12 @@
1
- # @openclaw/kilocode-provider
1
+ # OpenClaw Kilo Gateway Provider
2
2
 
3
- Reserved placeholder for the official OpenClaw plugin package. Use a published release version instead.
3
+ Official OpenClaw provider plugin for Kilo Gateway.
4
+
5
+ Install from OpenClaw:
6
+
7
+ ```bash
8
+ openclaw plugins install @openclaw/kilocode-provider
9
+ openclaw gateway restart
10
+ ```
11
+
12
+ See <https://docs.openclaw.ai/providers/kilocode> for setup and configuration.
package/dist/api.js ADDED
@@ -0,0 +1,3 @@
1
+ import { KILOCODE_BASE_URL, KILOCODE_DEFAULT_CONTEXT_WINDOW, KILOCODE_DEFAULT_COST, KILOCODE_DEFAULT_MAX_TOKENS, KILOCODE_DEFAULT_MODEL_ID, KILOCODE_DEFAULT_MODEL_NAME, KILOCODE_DEFAULT_MODEL_REF, KILOCODE_MODELS_URL, KILOCODE_MODEL_CATALOG, buildKilocodeModelDefinition, discoverKilocodeModels } from "./provider-models.js";
2
+ import { buildKilocodeProvider, buildKilocodeProviderWithDiscovery } from "./provider-catalog.js";
3
+ export { KILOCODE_BASE_URL, KILOCODE_DEFAULT_CONTEXT_WINDOW, KILOCODE_DEFAULT_COST, KILOCODE_DEFAULT_MAX_TOKENS, KILOCODE_DEFAULT_MODEL_ID, KILOCODE_DEFAULT_MODEL_NAME, KILOCODE_DEFAULT_MODEL_REF, KILOCODE_MODELS_URL, KILOCODE_MODEL_CATALOG, buildKilocodeModelDefinition, buildKilocodeProvider, buildKilocodeProviderWithDiscovery, discoverKilocodeModels };
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ import { KILOCODE_DEFAULT_MODEL_REF } from "./provider-models.js";
2
+ import { buildKilocodeProvider, buildKilocodeProviderWithDiscovery } from "./provider-catalog.js";
3
+ import { applyKilocodeConfig } from "./onboard.js";
4
+ import { wrapKilocodeProviderStream } from "./stream.js";
5
+ import { readConfiguredProviderCatalogEntries } from "openclaw/plugin-sdk/provider-catalog-shared";
6
+ import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
7
+ import { PASSTHROUGH_GEMINI_REPLAY_HOOKS } from "openclaw/plugin-sdk/provider-model-shared";
8
+ //#region extensions/kilocode/index.ts
9
+ const PROVIDER_ID = "kilocode";
10
+ var kilocode_default = defineSingleProviderPluginEntry({
11
+ id: PROVIDER_ID,
12
+ name: "Kilo Gateway Provider",
13
+ description: "Bundled Kilo Gateway provider plugin",
14
+ provider: {
15
+ label: "Kilo Gateway",
16
+ docsPath: "/providers/kilocode",
17
+ auth: [{
18
+ methodId: "api-key",
19
+ label: "Kilo Gateway API key",
20
+ hint: "API key (OpenRouter-compatible)",
21
+ optionKey: "kilocodeApiKey",
22
+ flagName: "--kilocode-api-key",
23
+ envVar: "KILOCODE_API_KEY",
24
+ promptMessage: "Enter Kilo Gateway API key",
25
+ defaultModel: KILOCODE_DEFAULT_MODEL_REF,
26
+ applyConfig: (cfg) => applyKilocodeConfig(cfg)
27
+ }],
28
+ catalog: {
29
+ buildProvider: buildKilocodeProviderWithDiscovery,
30
+ buildStaticProvider: buildKilocodeProvider
31
+ },
32
+ augmentModelCatalog: ({ config }) => readConfiguredProviderCatalogEntries({
33
+ config,
34
+ providerId: PROVIDER_ID
35
+ }),
36
+ ...PASSTHROUGH_GEMINI_REPLAY_HOOKS,
37
+ wrapStreamFn: wrapKilocodeProviderStream,
38
+ isCacheTtlEligible: (ctx) => ctx.modelId.startsWith("anthropic/")
39
+ }
40
+ });
41
+ //#endregion
42
+ export { kilocode_default as default };
@@ -0,0 +1,22 @@
1
+ import { KILOCODE_BASE_URL, KILOCODE_DEFAULT_MODEL_REF } from "./provider-models.js";
2
+ import { buildKilocodeProvider } from "./provider-catalog.js";
3
+ import { createModelCatalogPresetAppliers } from "openclaw/plugin-sdk/provider-onboard";
4
+ //#region extensions/kilocode/onboard.ts
5
+ const kilocodePresetAppliers = createModelCatalogPresetAppliers({
6
+ primaryModelRef: KILOCODE_DEFAULT_MODEL_REF,
7
+ resolveParams: (_cfg) => ({
8
+ providerId: "kilocode",
9
+ api: "openai-completions",
10
+ baseUrl: KILOCODE_BASE_URL,
11
+ catalogModels: buildKilocodeProvider().models ?? [],
12
+ aliases: [{
13
+ modelRef: KILOCODE_DEFAULT_MODEL_REF,
14
+ alias: "Kilo Gateway"
15
+ }]
16
+ })
17
+ });
18
+ function applyKilocodeConfig(cfg) {
19
+ return kilocodePresetAppliers.applyConfig(cfg);
20
+ }
21
+ //#endregion
22
+ export { KILOCODE_BASE_URL, KILOCODE_DEFAULT_MODEL_REF, applyKilocodeConfig };
@@ -0,0 +1,26 @@
1
+ import { KILOCODE_BASE_URL, KILOCODE_DEFAULT_COST, KILOCODE_MODEL_CATALOG, discoverKilocodeModels } from "./provider-models.js";
2
+ //#region extensions/kilocode/provider-catalog.ts
3
+ function buildKilocodeProvider() {
4
+ return {
5
+ baseUrl: KILOCODE_BASE_URL,
6
+ api: "openai-completions",
7
+ models: KILOCODE_MODEL_CATALOG.map((model) => ({
8
+ id: model.id,
9
+ name: model.name,
10
+ reasoning: model.reasoning,
11
+ input: model.input,
12
+ cost: KILOCODE_DEFAULT_COST,
13
+ contextWindow: model.contextWindow ?? 1e6,
14
+ maxTokens: model.maxTokens ?? 128e3
15
+ }))
16
+ };
17
+ }
18
+ async function buildKilocodeProviderWithDiscovery() {
19
+ return {
20
+ baseUrl: KILOCODE_BASE_URL,
21
+ api: "openai-completions",
22
+ models: await discoverKilocodeModels()
23
+ };
24
+ }
25
+ //#endregion
26
+ export { buildKilocodeProvider, buildKilocodeProviderWithDiscovery };
@@ -0,0 +1,137 @@
1
+ import { readProviderJsonArrayFieldResponse } from "openclaw/plugin-sdk/provider-http";
2
+ import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
3
+ import { fetchWithSsrFGuard, ssrfPolicyFromHttpBaseUrlAllowedHostname } from "openclaw/plugin-sdk/ssrf-runtime";
4
+ import { asPositiveSafeInteger, normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ //#region extensions/kilocode/provider-models.ts
6
+ const log = createSubsystemLogger("kilocode-models");
7
+ const KILOCODE_BASE_URL = "https://api.kilo.ai/api/gateway/";
8
+ const KILOCODE_DEFAULT_MODEL_ID = "kilo/auto";
9
+ const KILOCODE_DEFAULT_MODEL_REF = `kilocode/${KILOCODE_DEFAULT_MODEL_ID}`;
10
+ const KILOCODE_DEFAULT_MODEL_NAME = "Kilo Auto";
11
+ const KILOCODE_MODEL_CATALOG = [{
12
+ id: KILOCODE_DEFAULT_MODEL_ID,
13
+ name: KILOCODE_DEFAULT_MODEL_NAME,
14
+ input: ["text", "image"],
15
+ reasoning: true
16
+ }];
17
+ const KILOCODE_DEFAULT_CONTEXT_WINDOW = 1e6;
18
+ const KILOCODE_DEFAULT_MAX_TOKENS = 128e3;
19
+ const KILOCODE_DEFAULT_COST = {
20
+ input: 0,
21
+ output: 0,
22
+ cacheRead: 0,
23
+ cacheWrite: 0
24
+ };
25
+ const KILOCODE_MODELS_URL = `${KILOCODE_BASE_URL}models`;
26
+ const DISCOVERY_TIMEOUT_MS = 5e3;
27
+ function toPricePerMillion(perToken) {
28
+ if (!perToken) return 0;
29
+ const num = Number(perToken);
30
+ if (!Number.isFinite(num) || num < 0) return 0;
31
+ return num * 1e6;
32
+ }
33
+ function parseModality(entry) {
34
+ const modalities = entry.architecture?.input_modalities;
35
+ if (!Array.isArray(modalities)) return ["text"];
36
+ return modalities.some((m) => typeof m === "string" && normalizeLowercaseStringOrEmpty(m) === "image") ? ["text", "image"] : ["text"];
37
+ }
38
+ function parseReasoning(entry) {
39
+ const params = entry.supported_parameters;
40
+ if (!Array.isArray(params)) return false;
41
+ return params.includes("reasoning") || params.includes("include_reasoning");
42
+ }
43
+ function toModelDefinition(entry) {
44
+ return {
45
+ id: entry.id,
46
+ name: entry.name || entry.id,
47
+ reasoning: parseReasoning(entry),
48
+ input: parseModality(entry),
49
+ cost: {
50
+ input: toPricePerMillion(entry.pricing.prompt),
51
+ output: toPricePerMillion(entry.pricing.completion),
52
+ cacheRead: toPricePerMillion(entry.pricing.input_cache_read),
53
+ cacheWrite: toPricePerMillion(entry.pricing.input_cache_write)
54
+ },
55
+ contextWindow: asPositiveSafeInteger(entry.context_length) ?? 1e6,
56
+ maxTokens: asPositiveSafeInteger(entry.top_provider?.max_completion_tokens) ?? 128e3
57
+ };
58
+ }
59
+ function buildStaticCatalog() {
60
+ return KILOCODE_MODEL_CATALOG.map((model) => ({
61
+ id: model.id,
62
+ name: model.name,
63
+ reasoning: model.reasoning,
64
+ input: model.input,
65
+ cost: KILOCODE_DEFAULT_COST,
66
+ contextWindow: model.contextWindow ?? 1e6,
67
+ maxTokens: model.maxTokens ?? 128e3
68
+ }));
69
+ }
70
+ function asGatewayModelEntry(value) {
71
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Kilocode model list: malformed JSON response");
72
+ const entry = value;
73
+ if (typeof entry.id !== "string" || typeof entry.pricing !== "object" || entry.pricing === null || Array.isArray(entry.pricing)) throw new Error("Kilocode model list: malformed JSON response");
74
+ return value;
75
+ }
76
+ function readGatewayModelId(value) {
77
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return "";
78
+ const id = value.id;
79
+ return typeof id === "string" ? id.trim() : "";
80
+ }
81
+ async function discoverKilocodeModels() {
82
+ if (process.env.VITEST) return buildStaticCatalog();
83
+ try {
84
+ const { response, release } = await fetchWithSsrFGuard({
85
+ url: KILOCODE_MODELS_URL,
86
+ init: { headers: { Accept: "application/json" } },
87
+ timeoutMs: DISCOVERY_TIMEOUT_MS,
88
+ policy: ssrfPolicyFromHttpBaseUrlAllowedHostname(KILOCODE_BASE_URL),
89
+ auditContext: "kilocode.model_discovery"
90
+ });
91
+ try {
92
+ if (!response.ok) {
93
+ log.warn(`Failed to discover models: HTTP ${response.status}, using static catalog`);
94
+ return buildStaticCatalog();
95
+ }
96
+ const data = await readProviderJsonArrayFieldResponse(response, "Kilocode model list", "data");
97
+ if (data.length === 0) {
98
+ log.warn("No models found from gateway API, using static catalog");
99
+ return buildStaticCatalog();
100
+ }
101
+ const models = [];
102
+ const discoveredIds = /* @__PURE__ */ new Set();
103
+ for (const rawEntry of data) {
104
+ const id = readGatewayModelId(rawEntry);
105
+ try {
106
+ const entry = asGatewayModelEntry(rawEntry);
107
+ if (!id || discoveredIds.has(id)) continue;
108
+ models.push(toModelDefinition(entry));
109
+ discoveredIds.add(id);
110
+ } catch (e) {
111
+ log.warn(`Skipping malformed model entry "${id}": ${String(e)}`);
112
+ }
113
+ }
114
+ const staticModels = buildStaticCatalog();
115
+ for (const staticModel of staticModels) if (!discoveredIds.has(staticModel.id)) models.unshift(staticModel);
116
+ return models.length > 0 ? models : buildStaticCatalog();
117
+ } finally {
118
+ await release();
119
+ }
120
+ } catch (error) {
121
+ log.warn(`Discovery failed: ${String(error)}, using static catalog`);
122
+ return buildStaticCatalog();
123
+ }
124
+ }
125
+ function buildKilocodeModelDefinition() {
126
+ return {
127
+ id: KILOCODE_DEFAULT_MODEL_ID,
128
+ name: KILOCODE_DEFAULT_MODEL_NAME,
129
+ reasoning: true,
130
+ input: ["text", "image"],
131
+ cost: KILOCODE_DEFAULT_COST,
132
+ contextWindow: KILOCODE_DEFAULT_CONTEXT_WINDOW,
133
+ maxTokens: KILOCODE_DEFAULT_MAX_TOKENS
134
+ };
135
+ }
136
+ //#endregion
137
+ export { KILOCODE_BASE_URL, KILOCODE_DEFAULT_CONTEXT_WINDOW, KILOCODE_DEFAULT_COST, KILOCODE_DEFAULT_MAX_TOKENS, KILOCODE_DEFAULT_MODEL_ID, KILOCODE_DEFAULT_MODEL_NAME, KILOCODE_DEFAULT_MODEL_REF, KILOCODE_MODELS_URL, KILOCODE_MODEL_CATALOG, buildKilocodeModelDefinition, discoverKilocodeModels };
package/dist/stream.js ADDED
@@ -0,0 +1,83 @@
1
+ import { resolveProviderRequestHeaders } from "openclaw/plugin-sdk/provider-http";
2
+ import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ //#region extensions/kilocode/stream.ts
4
+ const KILOCODE_FEATURE_HEADER = "X-KILOCODE-FEATURE";
5
+ const KILOCODE_FEATURE_DEFAULT = "openclaw";
6
+ const KILOCODE_FEATURE_ENV_VAR = "KILOCODE_FEATURE";
7
+ function resolveKilocodeAppHeaders() {
8
+ const feature = process.env[KILOCODE_FEATURE_ENV_VAR]?.trim() || KILOCODE_FEATURE_DEFAULT;
9
+ return { [KILOCODE_FEATURE_HEADER]: feature };
10
+ }
11
+ function mapThinkingLevelToReasoningEffort(thinkingLevel) {
12
+ if (thinkingLevel === "off") return "none";
13
+ if (thinkingLevel === "adaptive") return "medium";
14
+ if (thinkingLevel === "max") return "xhigh";
15
+ return thinkingLevel;
16
+ }
17
+ function normalizeKilocodeReasoningPayload(payloadObj, thinkingLevel) {
18
+ delete payloadObj.reasoning_effort;
19
+ if (!thinkingLevel || thinkingLevel === "off") return;
20
+ const existingReasoning = payloadObj.reasoning;
21
+ if (existingReasoning && typeof existingReasoning === "object" && !Array.isArray(existingReasoning)) {
22
+ const reasoningObj = existingReasoning;
23
+ if (!("max_tokens" in reasoningObj) && !("effort" in reasoningObj)) reasoningObj.effort = mapThinkingLevelToReasoningEffort(thinkingLevel);
24
+ } else if (!existingReasoning) payloadObj.reasoning = { effort: mapThinkingLevelToReasoningEffort(thinkingLevel) };
25
+ }
26
+ function normalizeKilocodeStopPayload(payloadObj) {
27
+ if (typeof payloadObj.stop === "string") payloadObj.stop = [payloadObj.stop];
28
+ }
29
+ function asRecord(value) {
30
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
31
+ }
32
+ function normalizeKilocodeStopAfterCaller(value, fallbackPayload) {
33
+ const replacementPayload = asRecord(value);
34
+ if (replacementPayload) {
35
+ normalizeKilocodeStopPayload(replacementPayload);
36
+ return value;
37
+ }
38
+ if (fallbackPayload) normalizeKilocodeStopPayload(fallbackPayload);
39
+ return value;
40
+ }
41
+ function isProxyReasoningUnsupported(modelId) {
42
+ const trimmed = normalizeOptionalLowercaseString(modelId);
43
+ const slashIndex = trimmed?.indexOf("/") ?? -1;
44
+ return slashIndex > 0 && trimmed?.slice(0, slashIndex) === "x-ai";
45
+ }
46
+ function resolveKilocodeThinkingLevel(ctx) {
47
+ if (ctx.modelId === "kilo/auto" || isProxyReasoningUnsupported(ctx.modelId)) return;
48
+ return ctx.thinkingLevel;
49
+ }
50
+ function createKilocodeStreamWrapper(baseStreamFn, thinkingLevel) {
51
+ if (!baseStreamFn) return;
52
+ const underlying = baseStreamFn;
53
+ return (model, context, options) => {
54
+ const originalOnPayload = options?.onPayload;
55
+ const headers = resolveProviderRequestHeaders({
56
+ provider: typeof model.provider === "string" ? model.provider : "kilocode",
57
+ api: model.api,
58
+ baseUrl: typeof model.baseUrl === "string" ? model.baseUrl : void 0,
59
+ capability: "llm",
60
+ transport: "stream",
61
+ callerHeaders: options?.headers,
62
+ defaultHeaders: resolveKilocodeAppHeaders(),
63
+ precedence: "defaults-win"
64
+ });
65
+ return underlying(model, context, {
66
+ ...options,
67
+ headers,
68
+ onPayload(payload, payloadModel) {
69
+ const payloadObj = asRecord(payload);
70
+ if (payloadObj) normalizeKilocodeReasoningPayload(payloadObj, thinkingLevel);
71
+ const result = originalOnPayload?.(payload, payloadModel);
72
+ if (result && typeof result.then === "function") return Promise.resolve(result).then((resolved) => normalizeKilocodeStopAfterCaller(resolved, payloadObj));
73
+ return normalizeKilocodeStopAfterCaller(result, payloadObj);
74
+ }
75
+ });
76
+ };
77
+ }
78
+ function wrapKilocodeProviderStream(ctx) {
79
+ if (normalizeOptionalLowercaseString(ctx.provider) !== "kilocode") return;
80
+ return createKilocodeStreamWrapper(ctx.streamFn, resolveKilocodeThinkingLevel(ctx));
81
+ }
82
+ //#endregion
83
+ export { createKilocodeStreamWrapper, wrapKilocodeProviderStream };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@openclaw/kilocode-provider",
3
+ "version": "2026.6.9",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@openclaw/kilocode-provider",
9
+ "version": "2026.6.9"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "id": "kilocode",
3
+ "activation": {
4
+ "onStartup": false
5
+ },
6
+ "enabledByDefault": true,
7
+ "providers": ["kilocode"],
8
+ "modelPricing": {
9
+ "providers": {
10
+ "kilocode": {
11
+ "openRouter": {
12
+ "passthroughProviderModel": true
13
+ },
14
+ "liteLLM": {
15
+ "passthroughProviderModel": true
16
+ }
17
+ }
18
+ }
19
+ },
20
+ "setup": {
21
+ "providers": [
22
+ {
23
+ "id": "kilocode",
24
+ "envVars": ["KILOCODE_API_KEY"]
25
+ }
26
+ ]
27
+ },
28
+ "providerAuthChoices": [
29
+ {
30
+ "provider": "kilocode",
31
+ "method": "api-key",
32
+ "choiceId": "kilocode-api-key",
33
+ "choiceLabel": "Kilo Gateway API key",
34
+ "choiceHint": "API key (OpenRouter-compatible)",
35
+ "groupId": "kilocode",
36
+ "groupLabel": "Kilo Gateway",
37
+ "groupHint": "API key (OpenRouter-compatible)",
38
+ "optionKey": "kilocodeApiKey",
39
+ "cliFlag": "--kilocode-api-key",
40
+ "cliOption": "--kilocode-api-key <key>",
41
+ "cliDescription": "Kilo Gateway API key"
42
+ }
43
+ ],
44
+ "configSchema": {
45
+ "type": "object",
46
+ "additionalProperties": false,
47
+ "properties": {}
48
+ },
49
+ "modelCatalog": {
50
+ "providers": {
51
+ "kilocode": {
52
+ "baseUrl": "https://api.kilo.ai/api/gateway/",
53
+ "api": "openai-completions",
54
+ "models": [
55
+ {
56
+ "id": "kilo/auto",
57
+ "name": "Kilo Auto",
58
+ "reasoning": true,
59
+ "input": ["text", "image"],
60
+ "cost": {
61
+ "input": 0,
62
+ "output": 0,
63
+ "cacheRead": 0,
64
+ "cacheWrite": 0
65
+ },
66
+ "contextWindow": 1000000,
67
+ "maxTokens": 128000
68
+ }
69
+ ]
70
+ }
71
+ },
72
+ "discovery": {
73
+ "kilocode": "refreshable"
74
+ }
75
+ }
76
+ }
package/package.json CHANGED
@@ -1,14 +1,50 @@
1
1
  {
2
2
  "name": "@openclaw/kilocode-provider",
3
- "version": "0.0.0",
4
- "description": "Reserved package name for an official OpenClaw plugin.",
5
- "license": "MIT",
3
+ "version": "2026.6.9",
4
+ "description": "OpenClaw Kilo Gateway provider plugin.",
6
5
  "repository": {
7
6
  "type": "git",
8
- "url": "git+https://github.com/openclaw/openclaw.git"
7
+ "url": "https://github.com/openclaw/openclaw"
9
8
  },
10
- "publishConfig": {
11
- "access": "public",
12
- "tag": "placeholder"
13
- }
9
+ "type": "module",
10
+ "openclaw": {
11
+ "extensions": [
12
+ "./index.ts"
13
+ ],
14
+ "install": {
15
+ "clawhubSpec": "clawhub:@openclaw/kilocode-provider",
16
+ "npmSpec": "@openclaw/kilocode-provider",
17
+ "defaultChoice": "npm",
18
+ "minHostVersion": ">=2026.6.8"
19
+ },
20
+ "compat": {
21
+ "pluginApi": ">=2026.6.9"
22
+ },
23
+ "build": {
24
+ "openclawVersion": "2026.6.9",
25
+ "bundledDist": false
26
+ },
27
+ "release": {
28
+ "publishToClawHub": true,
29
+ "publishToNpm": true
30
+ },
31
+ "runtimeExtensions": [
32
+ "./dist/index.js"
33
+ ]
34
+ },
35
+ "files": [
36
+ "dist/**",
37
+ "openclaw.plugin.json",
38
+ "npm-shrinkwrap.json",
39
+ "README.md"
40
+ ],
41
+ "peerDependencies": {
42
+ "openclaw": ">=2026.6.9"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "openclaw": {
46
+ "optional": true
47
+ }
48
+ },
49
+ "bundledDependencies": []
14
50
  }