@bytesbrains/opencode-cruise 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +189 -0
  3. package/dist/auth.d.ts +7 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +44 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/base-url.d.ts +8 -0
  8. package/dist/base-url.d.ts.map +1 -0
  9. package/dist/base-url.js +30 -0
  10. package/dist/base-url.js.map +1 -0
  11. package/dist/constants.d.ts +20 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/constants.js +20 -0
  14. package/dist/constants.js.map +1 -0
  15. package/dist/errors.d.ts +14 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +33 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/fetch-models.d.ts +23 -0
  20. package/dist/fetch-models.d.ts.map +1 -0
  21. package/dist/fetch-models.js +61 -0
  22. package/dist/fetch-models.js.map +1 -0
  23. package/dist/index.d.ts +22 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +103 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/mcp-client.d.ts +27 -0
  28. package/dist/mcp-client.d.ts.map +1 -0
  29. package/dist/mcp-client.js +95 -0
  30. package/dist/mcp-client.js.map +1 -0
  31. package/dist/mcp-url.d.ts +6 -0
  32. package/dist/mcp-url.d.ts.map +1 -0
  33. package/dist/mcp-url.js +22 -0
  34. package/dist/mcp-url.js.map +1 -0
  35. package/dist/models.d.ts +42 -0
  36. package/dist/models.d.ts.map +1 -0
  37. package/dist/models.js +132 -0
  38. package/dist/models.js.map +1 -0
  39. package/dist/provider.d.ts +22 -0
  40. package/dist/provider.d.ts.map +1 -0
  41. package/dist/provider.js +68 -0
  42. package/dist/provider.js.map +1 -0
  43. package/dist/resolve-api-key.d.ts +15 -0
  44. package/dist/resolve-api-key.d.ts.map +1 -0
  45. package/dist/resolve-api-key.js +43 -0
  46. package/dist/resolve-api-key.js.map +1 -0
  47. package/dist/tools.d.ts +124 -0
  48. package/dist/tools.d.ts.map +1 -0
  49. package/dist/tools.js +231 -0
  50. package/dist/tools.js.map +1 -0
  51. package/package.json +60 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Thin JSON-RPC client for Cruise's remote MCP (`POST …/mcp`).
3
+ * Same wire the Claude/Cursor plugins use; read-only tools only.
4
+ */
5
+ export declare const MCP_FETCH_TIMEOUT_MS = 10000;
6
+ export type CallCruiseMcpToolOptions = {
7
+ mcpUrl: string;
8
+ apiKey: string;
9
+ name: string;
10
+ arguments?: Record<string, unknown>;
11
+ fetchImpl?: typeof fetch;
12
+ timeoutMs?: number;
13
+ };
14
+ export type CallCruiseMcpToolResult = {
15
+ ok: true;
16
+ text: string;
17
+ structured?: unknown;
18
+ } | {
19
+ ok: false;
20
+ reason: string;
21
+ };
22
+ /**
23
+ * Call one Cruise MCP tool via JSON-RPC `tools/call`.
24
+ * Never logs the API key.
25
+ */
26
+ export declare function callCruiseMcpTool(options: CallCruiseMcpToolOptions): Promise<CallCruiseMcpToolResult>;
27
+ //# sourceMappingURL=mcp-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.d.ts","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAChD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAyBlC;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CAgElC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Thin JSON-RPC client for Cruise's remote MCP (`POST …/mcp`).
3
+ * Same wire the Claude/Cursor plugins use; read-only tools only.
4
+ */
5
+ export const MCP_FETCH_TIMEOUT_MS = 10_000;
6
+ function asRecord(value) {
7
+ return value && typeof value === "object" && !Array.isArray(value)
8
+ ? value
9
+ : undefined;
10
+ }
11
+ function textFromMcpResult(result) {
12
+ const content = result.content;
13
+ if (Array.isArray(content)) {
14
+ const parts = content
15
+ .map((part) => {
16
+ const row = asRecord(part);
17
+ return row && row.type === "text" && typeof row.text === "string" ? row.text : "";
18
+ })
19
+ .filter(Boolean);
20
+ if (parts.length > 0)
21
+ return parts.join("\n");
22
+ }
23
+ if (result.structuredContent !== undefined) {
24
+ return JSON.stringify(result.structuredContent, null, 2);
25
+ }
26
+ return JSON.stringify(result, null, 2);
27
+ }
28
+ /**
29
+ * Call one Cruise MCP tool via JSON-RPC `tools/call`.
30
+ * Never logs the API key.
31
+ */
32
+ export async function callCruiseMcpTool(options) {
33
+ const apiKey = options.apiKey.trim();
34
+ if (!apiKey) {
35
+ return { ok: false, reason: "Cruise API key is missing" };
36
+ }
37
+ const timeoutMs = options.timeoutMs ?? MCP_FETCH_TIMEOUT_MS;
38
+ const fetchImpl = options.fetchImpl ?? fetch;
39
+ const controller = new AbortController();
40
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
41
+ try {
42
+ const response = await fetchImpl(options.mcpUrl, {
43
+ method: "POST",
44
+ headers: {
45
+ Authorization: `Bearer ${apiKey}`,
46
+ "Content-Type": "application/json",
47
+ Accept: "application/json, text/event-stream",
48
+ },
49
+ body: JSON.stringify({
50
+ jsonrpc: "2.0",
51
+ id: 1,
52
+ method: "tools/call",
53
+ params: {
54
+ name: options.name,
55
+ arguments: options.arguments ?? {},
56
+ },
57
+ }),
58
+ signal: controller.signal,
59
+ });
60
+ const body = await response.json().catch(() => undefined);
61
+ if (!response.ok) {
62
+ return {
63
+ ok: false,
64
+ reason: `Cruise MCP HTTP ${response.status}${body ? ` — ${JSON.stringify(body)}` : ""}`,
65
+ };
66
+ }
67
+ const root = asRecord(body);
68
+ if (!root) {
69
+ return { ok: false, reason: "Cruise MCP returned a non-object body" };
70
+ }
71
+ if (root.error) {
72
+ return { ok: false, reason: `Cruise MCP error — ${JSON.stringify(root.error)}` };
73
+ }
74
+ const result = asRecord(root.result);
75
+ if (!result) {
76
+ return { ok: false, reason: "Cruise MCP response missing result" };
77
+ }
78
+ if (result.isError === true) {
79
+ return { ok: false, reason: textFromMcpResult(result) };
80
+ }
81
+ return {
82
+ ok: true,
83
+ text: textFromMcpResult(result),
84
+ structured: result.structuredContent,
85
+ };
86
+ }
87
+ catch (error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ return { ok: false, reason: `Cruise MCP request failed: ${message}` };
90
+ }
91
+ finally {
92
+ clearTimeout(timer);
93
+ }
94
+ }
95
+ //# sourceMappingURL=mcp-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Map an allowed Cruise `/v1` base URL to the MCP endpoint (`…/mcp`).
3
+ * Always goes through the same host allowlist as inference traffic.
4
+ */
5
+ export declare function resolveCruiseMcpUrl(baseUrl?: string): string;
6
+ //# sourceMappingURL=mcp-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-url.d.ts","sourceRoot":"","sources":["../src/mcp-url.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAa5D"}
@@ -0,0 +1,22 @@
1
+ import { resolveAllowedCruiseBaseUrl } from "./base-url.js";
2
+ import { CRUISE_BASE_URL } from "./constants.js";
3
+ /**
4
+ * Map an allowed Cruise `/v1` base URL to the MCP endpoint (`…/mcp`).
5
+ * Always goes through the same host allowlist as inference traffic.
6
+ */
7
+ export function resolveCruiseMcpUrl(baseUrl) {
8
+ const allowed = resolveAllowedCruiseBaseUrl(baseUrl ?? CRUISE_BASE_URL);
9
+ try {
10
+ const url = new URL(allowed);
11
+ // Inference bases end in `/v1`; MCP is `/mcp` on the same host.
12
+ const path = url.pathname.replace(/\/+$/, "").replace(/\/v1$/, "");
13
+ url.pathname = `${path}/mcp`.replace(/\/{2,}/g, "/");
14
+ url.search = "";
15
+ url.hash = "";
16
+ return url.toString().replace(/\/+$/, "");
17
+ }
18
+ catch {
19
+ return "https://cruise.bytesbrains.net/mcp";
20
+ }
21
+ }
22
+ //# sourceMappingURL=mcp-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-url.js","sourceRoot":"","sources":["../src/mcp-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,MAAM,OAAO,GAAG,2BAA2B,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC;IACxE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,gEAAgE;QAChE,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACnE,GAAG,CAAC,QAAQ,GAAG,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACrD,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;QAChB,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;QACd,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,oCAAoC,CAAC;IAC9C,CAAC;AACH,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Project Cruise `GET /v1/models` rows into OpenCode provider model entries.
3
+ *
4
+ * No frozen catalogue — only chat rows with `x-cruise` metadata are kept.
5
+ * Costs/windows come from live `x-cruise` fields (micros → $/MTok).
6
+ */
7
+ export type CruiseLiveModelRow = {
8
+ id?: unknown;
9
+ object?: unknown;
10
+ "x-cruise"?: unknown;
11
+ };
12
+ /** OpenCode `provider.<id>.models.<modelId>` entry. */
13
+ export type OpenCodeModelConfig = {
14
+ name: string;
15
+ reasoning?: boolean;
16
+ tool_call?: boolean;
17
+ cost?: {
18
+ input: number;
19
+ output: number;
20
+ cache_read?: number;
21
+ cache_write?: number;
22
+ };
23
+ limit?: {
24
+ context: number;
25
+ output: number;
26
+ };
27
+ modalities?: {
28
+ input: Array<"text" | "image" | "audio" | "video" | "pdf">;
29
+ output: Array<"text" | "image" | "audio" | "video" | "pdf">;
30
+ };
31
+ };
32
+ /**
33
+ * Cruise publishes rates as integer micros per million tokens.
34
+ * OpenCode costs are dollars per million tokens → divide by 1e6.
35
+ */
36
+ export declare function microsToDollarsPerMtok(micros: unknown): number | undefined;
37
+ /**
38
+ * Projects Cruise's authenticated `/models` response into an OpenCode models map.
39
+ * Last-wins on duplicate ids.
40
+ */
41
+ export declare function projectCruiseLiveModels(rows: readonly unknown[]): Record<string, OpenCodeModelConfig>;
42
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAcF,uDAAuD;AACvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC,CAAC;QAC3D,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC,CAAC;KAC7D,CAAC;CACH,CAAC;AAOF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAS1E;AA6FD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,OAAO,EAAE,GACvB,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAarC"}
package/dist/models.js ADDED
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Project Cruise `GET /v1/models` rows into OpenCode provider model entries.
3
+ *
4
+ * No frozen catalogue — only chat rows with `x-cruise` metadata are kept.
5
+ * Costs/windows come from live `x-cruise` fields (micros → $/MTok).
6
+ */
7
+ const DEFAULT_CONTEXT_WINDOW = 128_000;
8
+ const DEFAULT_MAX_TOKENS = 8_192;
9
+ const MICROS_PER_DOLLAR = 1_000_000;
10
+ /** Jobs that typically need extended thinking / tool-heavy agent loops. */
11
+ const REASONING_LANE_JOBS = new Set([
12
+ "agentic-coding",
13
+ "deep-reasoning",
14
+ "code-review",
15
+ "hunter",
16
+ "builder",
17
+ ]);
18
+ function readPositiveInteger(value) {
19
+ const number = typeof value === "number" ? value : Number(value);
20
+ return Number.isSafeInteger(number) && number > 0 ? number : undefined;
21
+ }
22
+ /**
23
+ * Cruise publishes rates as integer micros per million tokens.
24
+ * OpenCode costs are dollars per million tokens → divide by 1e6.
25
+ */
26
+ export function microsToDollarsPerMtok(micros) {
27
+ if (typeof micros !== "number" && (typeof micros !== "string" || !micros.trim())) {
28
+ return undefined;
29
+ }
30
+ const number = typeof micros === "number" ? micros : Number(micros);
31
+ if (!Number.isFinite(number) || number < 0) {
32
+ return undefined;
33
+ }
34
+ return number / MICROS_PER_DOLLAR;
35
+ }
36
+ function readCruisePricing(value) {
37
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
38
+ return {};
39
+ }
40
+ const pricing = value;
41
+ return {
42
+ input: microsToDollarsPerMtok(pricing.input_micros_per_mtok),
43
+ output: microsToDollarsPerMtok(pricing.output_micros_per_mtok),
44
+ cacheRead: microsToDollarsPerMtok(pricing.cached_input_micros_per_mtok),
45
+ };
46
+ }
47
+ function readCruiseExtension(row) {
48
+ const raw = row["x-cruise"];
49
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
50
+ return undefined;
51
+ }
52
+ return raw;
53
+ }
54
+ function laneDisplayName(ext, id) {
55
+ if (typeof ext.description === "string" && ext.description.trim()) {
56
+ return ext.description.trim();
57
+ }
58
+ if (typeof ext.job === "string" && ext.job.trim()) {
59
+ return `Cruise ${ext.job.trim()} (lane)`;
60
+ }
61
+ return id;
62
+ }
63
+ function resolveReasoning(ext) {
64
+ const isLane = ext.lane === true;
65
+ const job = typeof ext.job === "string" ? ext.job.trim() : "";
66
+ if (isLane && job) {
67
+ return REASONING_LANE_JOBS.has(job);
68
+ }
69
+ return false;
70
+ }
71
+ function projectLiveModel(row) {
72
+ if (row.object !== undefined && row.object !== "model") {
73
+ return undefined;
74
+ }
75
+ const id = typeof row.id === "string" ? row.id.trim() : "";
76
+ if (!id) {
77
+ return undefined;
78
+ }
79
+ const ext = readCruiseExtension(row);
80
+ // No frozen seeds — require x-cruise chat modality.
81
+ if (!ext) {
82
+ return undefined;
83
+ }
84
+ const modality = typeof ext.modality === "string" ? ext.modality.trim().toLowerCase() : "";
85
+ if (modality !== "chat") {
86
+ return undefined;
87
+ }
88
+ const isLane = ext.lane === true;
89
+ const pricing = readCruisePricing(ext.pricing);
90
+ const inputModalities = ext.vision === true ? ["text", "image"] : ["text"];
91
+ return {
92
+ id,
93
+ model: {
94
+ name: isLane ? laneDisplayName(ext, id) : id,
95
+ reasoning: resolveReasoning(ext),
96
+ tool_call: ext.tools !== false,
97
+ cost: {
98
+ input: pricing.input ?? 0,
99
+ output: pricing.output ?? 0,
100
+ cache_read: pricing.cacheRead ?? 0,
101
+ cache_write: 0,
102
+ },
103
+ limit: {
104
+ context: readPositiveInteger(ext.max_context) ?? DEFAULT_CONTEXT_WINDOW,
105
+ output: readPositiveInteger(ext.max_output) ?? DEFAULT_MAX_TOKENS,
106
+ },
107
+ modalities: {
108
+ input: inputModalities,
109
+ output: ["text"],
110
+ },
111
+ },
112
+ };
113
+ }
114
+ /**
115
+ * Projects Cruise's authenticated `/models` response into an OpenCode models map.
116
+ * Last-wins on duplicate ids.
117
+ */
118
+ export function projectCruiseLiveModels(rows) {
119
+ const byId = new Map();
120
+ for (const row of rows) {
121
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
122
+ continue;
123
+ }
124
+ const projected = projectLiveModel(row);
125
+ if (!projected) {
126
+ continue;
127
+ }
128
+ byId.set(projected.id, projected.model);
129
+ }
130
+ return Object.fromEntries(byId);
131
+ }
132
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Config } from "@opencode-ai/plugin";
2
+ export type ApplyCruiseProviderOptions = {
3
+ /** Env-like map; defaults to `process.env`. */
4
+ env?: Record<string, string | undefined>;
5
+ /** Resolved Cruise key (env or `/connect` auth store). */
6
+ apiKey?: string;
7
+ fetchImpl?: typeof fetch;
8
+ /** When false, skip live discovery (register provider shell only). */
9
+ fetchModels?: boolean;
10
+ };
11
+ /**
12
+ * Registers Cruise as an OpenAI-compatible OpenCode provider and, when a key
13
+ * is present, fills `models` from live `GET /v1/models`.
14
+ *
15
+ * Mutates `config` in place (OpenCode plugin `config` hook contract).
16
+ */
17
+ export declare function applyCruiseProvider(config: Config, options?: ApplyCruiseProviderOptions): Promise<{
18
+ fetched: boolean;
19
+ modelCount: number;
20
+ fetchError?: string;
21
+ }>;
22
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAclD,MAAM,MAAM,0BAA0B,GAAG;IACvC,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,sEAAsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAQF;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA+DxE"}
@@ -0,0 +1,68 @@
1
+ import { resolveAllowedCruiseBaseUrl } from "./base-url.js";
2
+ import { CRUISE_API_KEY_ENV, CRUISE_BASE_URL, CRUISE_BASE_URL_ENV, OPENAI_COMPATIBLE_NPM, PROVIDER_ID, } from "./constants.js";
3
+ import { fetchCruiseModels } from "./fetch-models.js";
4
+ function asRecord(value) {
5
+ return value && typeof value === "object" && !Array.isArray(value)
6
+ ? value
7
+ : {};
8
+ }
9
+ /**
10
+ * Registers Cruise as an OpenAI-compatible OpenCode provider and, when a key
11
+ * is present, fills `models` from live `GET /v1/models`.
12
+ *
13
+ * Mutates `config` in place (OpenCode plugin `config` hook contract).
14
+ */
15
+ export async function applyCruiseProvider(config, options = {}) {
16
+ const env = options.env ?? process.env;
17
+ const existing = asRecord(config.provider?.[PROVIDER_ID]);
18
+ const existingOptions = asRecord(existing.options);
19
+ const existingModels = asRecord(existing.models);
20
+ const configuredBase = typeof existingOptions.baseURL === "string"
21
+ ? existingOptions.baseURL
22
+ : typeof env[CRUISE_BASE_URL_ENV] === "string"
23
+ ? env[CRUISE_BASE_URL_ENV]
24
+ : CRUISE_BASE_URL;
25
+ const baseURL = resolveAllowedCruiseBaseUrl(configuredBase);
26
+ const apiKey = (options.apiKey ?? env[CRUISE_API_KEY_ENV] ?? "").trim();
27
+ config.provider ??= {};
28
+ config.provider[PROVIDER_ID] = {
29
+ ...existing,
30
+ npm: typeof existing.npm === "string" ? existing.npm : OPENAI_COMPATIBLE_NPM,
31
+ name: typeof existing.name === "string" ? existing.name : "BytesBrains Cruise",
32
+ env: Array.isArray(existing.env) ? existing.env : [CRUISE_API_KEY_ENV],
33
+ options: {
34
+ ...existingOptions,
35
+ baseURL,
36
+ // Prefer env /connect; keep an explicit {env:} so recipes work without /connect.
37
+ apiKey: typeof existingOptions.apiKey === "string"
38
+ ? existingOptions.apiKey
39
+ : `{env:${CRUISE_API_KEY_ENV}}`,
40
+ },
41
+ models: { ...existingModels },
42
+ };
43
+ if (options.fetchModels === false || !apiKey) {
44
+ return { fetched: false, modelCount: Object.keys(existingModels).length };
45
+ }
46
+ const result = await fetchCruiseModels({
47
+ baseUrl: baseURL,
48
+ apiKey,
49
+ fetchImpl: options.fetchImpl,
50
+ });
51
+ if (!result.ok) {
52
+ return {
53
+ fetched: false,
54
+ modelCount: Object.keys(config.provider[PROVIDER_ID].models ?? {}).length,
55
+ fetchError: result.reason,
56
+ };
57
+ }
58
+ // Live catalogue first; user-authored model overrides win on id conflict.
59
+ config.provider[PROVIDER_ID].models = {
60
+ ...result.models,
61
+ ...existingModels,
62
+ };
63
+ return {
64
+ fetched: true,
65
+ modelCount: Object.keys(config.provider[PROVIDER_ID].models ?? {}).length,
66
+ };
67
+ }
68
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export type ResolveCruiseApiKeyOptions = {
2
+ env?: Record<string, string | undefined>;
3
+ /** Explicit key (tests / callers that already resolved auth). */
4
+ apiKey?: string;
5
+ /** OpenCode state directory (contains `auth.json`). */
6
+ stateDir?: string;
7
+ /** Injectable read for tests. */
8
+ readFileImpl?: (path: string, encoding: "utf8") => Promise<string>;
9
+ };
10
+ /**
11
+ * Resolves a Cruise API key for live `GET /v1/models`.
12
+ * Prefer env (`CRUISE_API_KEY`); fall back to OpenCode `/connect` auth store.
13
+ */
14
+ export declare function resolveCruiseApiKey(options?: ResolveCruiseApiKeyOptions): Promise<string | undefined>;
15
+ //# sourceMappingURL=resolve-api-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-api-key.d.ts","sourceRoot":"","sources":["../src/resolve-api-key.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACpE,CAAC;AAEF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoC7B"}
@@ -0,0 +1,43 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import { readFile } from "node:fs/promises";
4
+ import { CRUISE_API_KEY_ENV, PROVIDER_ID } from "./constants.js";
5
+ /**
6
+ * Resolves a Cruise API key for live `GET /v1/models`.
7
+ * Prefer env (`CRUISE_API_KEY`); fall back to OpenCode `/connect` auth store.
8
+ */
9
+ export async function resolveCruiseApiKey(options = {}) {
10
+ const explicit = options.apiKey?.trim();
11
+ if (explicit) {
12
+ return explicit;
13
+ }
14
+ const env = options.env ?? process.env;
15
+ const fromEnv = env[CRUISE_API_KEY_ENV]?.trim();
16
+ if (fromEnv) {
17
+ return fromEnv;
18
+ }
19
+ const stateDir = options.stateDir?.trim() ||
20
+ join(homedir(), ".local", "share", "opencode");
21
+ const authPath = join(stateDir, "auth.json");
22
+ const read = options.readFileImpl ?? ((path, encoding) => readFile(path, encoding));
23
+ try {
24
+ const raw = await read(authPath, "utf8");
25
+ const parsed = JSON.parse(raw);
26
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
27
+ return undefined;
28
+ }
29
+ const entry = parsed[PROVIDER_ID];
30
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
31
+ return undefined;
32
+ }
33
+ const auth = entry;
34
+ if (auth.type === "api" && typeof auth.key === "string" && auth.key.trim()) {
35
+ return auth.key.trim();
36
+ }
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ return undefined;
42
+ }
43
+ //# sourceMappingURL=resolve-api-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-api-key.js","sourceRoot":"","sources":["../src/resolve-api-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAYjE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAsC,EAAE;IAExC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,OAAO,GAAG,GAAG,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,CAAC;IAChD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GACZ,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE;QACxB,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEpF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,KAAK,GAAI,MAAkC,CAAC,WAAW,CAAC,CAAC;QAC/D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,IAAI,GAAG,KAA0C,CAAC;QACxD,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3E,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}