@tickernelz/paperclip-pro-adapter-opencode-local 2026.925.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 (78) hide show
  1. package/LICENSE +22 -0
  2. package/dist/cli/format-event.d.ts +2 -0
  3. package/dist/cli/format-event.d.ts.map +1 -0
  4. package/dist/cli/format-event.js +115 -0
  5. package/dist/cli/format-event.js.map +1 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +2 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/index.d.ts +11 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +113 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/server/execute.d.ts +13 -0
  15. package/dist/server/execute.d.ts.map +1 -0
  16. package/dist/server/execute.js +595 -0
  17. package/dist/server/execute.js.map +1 -0
  18. package/dist/server/execute.remote.test.d.ts +2 -0
  19. package/dist/server/execute.remote.test.d.ts.map +1 -0
  20. package/dist/server/execute.remote.test.js +351 -0
  21. package/dist/server/execute.remote.test.js.map +1 -0
  22. package/dist/server/execute.test.d.ts +2 -0
  23. package/dist/server/execute.test.d.ts.map +1 -0
  24. package/dist/server/execute.test.js +290 -0
  25. package/dist/server/execute.test.js.map +1 -0
  26. package/dist/server/index.d.ts +8 -0
  27. package/dist/server/index.d.ts.map +1 -0
  28. package/dist/server/index.js +63 -0
  29. package/dist/server/index.js.map +1 -0
  30. package/dist/server/models.d.ts +24 -0
  31. package/dist/server/models.d.ts.map +1 -0
  32. package/dist/server/models.js +281 -0
  33. package/dist/server/models.js.map +1 -0
  34. package/dist/server/models.test.d.ts +2 -0
  35. package/dist/server/models.test.d.ts.map +1 -0
  36. package/dist/server/models.test.js +258 -0
  37. package/dist/server/models.test.js.map +1 -0
  38. package/dist/server/parse.d.ts +14 -0
  39. package/dist/server/parse.d.ts.map +1 -0
  40. package/dist/server/parse.js +99 -0
  41. package/dist/server/parse.js.map +1 -0
  42. package/dist/server/parse.test.d.ts +2 -0
  43. package/dist/server/parse.test.d.ts.map +1 -0
  44. package/dist/server/parse.test.js +73 -0
  45. package/dist/server/parse.test.js.map +1 -0
  46. package/dist/server/runtime-config.d.ts +20 -0
  47. package/dist/server/runtime-config.d.ts.map +1 -0
  48. package/dist/server/runtime-config.js +216 -0
  49. package/dist/server/runtime-config.js.map +1 -0
  50. package/dist/server/runtime-config.test.d.ts +2 -0
  51. package/dist/server/runtime-config.test.d.ts.map +1 -0
  52. package/dist/server/runtime-config.test.js +255 -0
  53. package/dist/server/runtime-config.test.js.map +1 -0
  54. package/dist/server/skills.d.ts +8 -0
  55. package/dist/server/skills.d.ts.map +1 -0
  56. package/dist/server/skills.js +73 -0
  57. package/dist/server/skills.js.map +1 -0
  58. package/dist/server/test.d.ts +3 -0
  59. package/dist/server/test.d.ts.map +1 -0
  60. package/dist/server/test.js +416 -0
  61. package/dist/server/test.js.map +1 -0
  62. package/dist/server/test.remote.test.d.ts +2 -0
  63. package/dist/server/test.remote.test.d.ts.map +1 -0
  64. package/dist/server/test.remote.test.js +132 -0
  65. package/dist/server/test.remote.test.js.map +1 -0
  66. package/dist/ui/build-config.d.ts +3 -0
  67. package/dist/ui/build-config.d.ts.map +1 -0
  68. package/dist/ui/build-config.js +32 -0
  69. package/dist/ui/build-config.js.map +1 -0
  70. package/dist/ui/index.d.ts +3 -0
  71. package/dist/ui/index.d.ts.map +1 -0
  72. package/dist/ui/index.js +3 -0
  73. package/dist/ui/index.js.map +1 -0
  74. package/dist/ui/parse-stdout.d.ts +3 -0
  75. package/dist/ui/parse-stdout.d.ts.map +1 -0
  76. package/dist/ui/parse-stdout.js +141 -0
  77. package/dist/ui/parse-stdout.js.map +1 -0
  78. package/package.json +58 -0
@@ -0,0 +1,216 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { asBoolean } from "@tickernelz/paperclip-pro-adapter-utils/server-utils";
5
+ function resolveXdgConfigHome(env) {
6
+ return ((typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()) ||
7
+ (typeof process.env.XDG_CONFIG_HOME === "string" && process.env.XDG_CONFIG_HOME.trim()) ||
8
+ path.join(os.homedir(), ".config"));
9
+ }
10
+ function isPlainObject(value) {
11
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12
+ }
13
+ // Recursively replace {env:VAR} placeholders with the resolved value. Used to bake
14
+ // gateway provider secrets (e.g. the LLM-gateway virtual key) into opencode.json
15
+ // SERVER-SIDE, where the value is reliably present. OpenCode's own {env:...}
16
+ // resolution happens inside the (possibly sandboxed) run process, whose env
17
+ // plumbing is not guaranteed to carry the key to OpenCode's spawned server -- so
18
+ // we resolve it here. Unresolvable placeholders are left intact for OpenCode to try.
19
+ function expandEnvPlaceholders(value, resolve) {
20
+ if (typeof value === "string") {
21
+ return value.replace(/\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g, (match, name) => {
22
+ const resolved = resolve(name);
23
+ return resolved !== undefined && resolved.length > 0 ? resolved : match;
24
+ });
25
+ }
26
+ if (Array.isArray(value)) {
27
+ return value.map((entry) => expandEnvPlaceholders(entry, resolve));
28
+ }
29
+ if (isPlainObject(value)) {
30
+ const out = {};
31
+ for (const [key, entry] of Object.entries(value)) {
32
+ out[key] = expandEnvPlaceholders(entry, resolve);
33
+ }
34
+ return out;
35
+ }
36
+ return value;
37
+ }
38
+ function parseProviderConfig(raw, resolveEnv, notes) {
39
+ if (typeof raw !== "string" || raw.trim().length === 0)
40
+ return null;
41
+ let parsed;
42
+ try {
43
+ parsed = JSON.parse(raw);
44
+ }
45
+ catch {
46
+ // Surface the misconfiguration instead of silently dropping the provider
47
+ // block; an unparseable value would otherwise be undiagnosable.
48
+ notes.push("PAPERCLIP_OPENCODE_PROVIDERS contains invalid JSON; custom providers ignored.");
49
+ return null;
50
+ }
51
+ if (!isPlainObject(parsed)) {
52
+ notes.push("PAPERCLIP_OPENCODE_PROVIDERS is set but is not a JSON object; custom providers ignored.");
53
+ return null;
54
+ }
55
+ // Only keep provider entries that are themselves objects; surface the ones
56
+ // we drop so a malformed entry is just as diagnosable as malformed JSON.
57
+ const providers = {};
58
+ const skipped = [];
59
+ for (const [key, value] of Object.entries(parsed)) {
60
+ if (isPlainObject(value))
61
+ providers[key] = expandEnvPlaceholders(value, resolveEnv);
62
+ else
63
+ skipped.push(key);
64
+ }
65
+ if (skipped.length > 0) {
66
+ notes.push(`PAPERCLIP_OPENCODE_PROVIDERS: skipped provider(s) with non-object values: ${skipped.join(", ")}.`);
67
+ }
68
+ return Object.keys(providers).length > 0 ? providers : null;
69
+ }
70
+ function parseConfiguredModelRef(raw) {
71
+ if (typeof raw !== "string")
72
+ return null;
73
+ const trimmed = raw.trim();
74
+ const slash = trimmed.indexOf("/");
75
+ if (slash <= 0 || slash === trimmed.length - 1)
76
+ return null;
77
+ return { provider: trimmed.slice(0, slash), model: trimmed.slice(slash + 1) };
78
+ }
79
+ async function readJsonObject(filepath) {
80
+ try {
81
+ const raw = await fs.readFile(filepath, "utf8");
82
+ const parsed = JSON.parse(raw);
83
+ return isPlainObject(parsed) ? parsed : {};
84
+ }
85
+ catch {
86
+ return {};
87
+ }
88
+ }
89
+ export async function prepareOpenCodeRuntimeConfig(input) {
90
+ const skipPermissions = asBoolean(input.config.dangerouslySkipPermissions, true);
91
+ if (!skipPermissions) {
92
+ return {
93
+ env: input.env,
94
+ notes: [],
95
+ cleanup: async () => { },
96
+ };
97
+ }
98
+ // For remote execution targets the host XDG_CONFIG_HOME path is meaningless
99
+ // (and actively harmful — it leaks a macOS-only path into the remote Linux
100
+ // env). Callers that need to ship a runtime opencode config to the remote
101
+ // box do that via prepareAdapterExecutionTargetRuntime in execute.ts; this
102
+ // host-fs helper is local-only.
103
+ if (input.targetIsRemote) {
104
+ return {
105
+ env: input.env,
106
+ notes: [],
107
+ cleanup: async () => { },
108
+ };
109
+ }
110
+ const sourceConfigDir = path.join(resolveXdgConfigHome(input.env), "opencode");
111
+ const runtimeConfigHome = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-opencode-config-"));
112
+ const runtimeConfigDir = path.join(runtimeConfigHome, "opencode");
113
+ const runtimeConfigPath = path.join(runtimeConfigDir, "opencode.json");
114
+ await fs.mkdir(runtimeConfigDir, { recursive: true });
115
+ try {
116
+ await fs.cp(sourceConfigDir, runtimeConfigDir, {
117
+ recursive: true,
118
+ force: true,
119
+ errorOnExist: false,
120
+ dereference: false,
121
+ });
122
+ }
123
+ catch (err) {
124
+ if (err?.code !== "ENOENT") {
125
+ throw err;
126
+ }
127
+ }
128
+ const existingConfig = await readJsonObject(runtimeConfigPath);
129
+ const notes = [
130
+ "Injected runtime OpenCode config with permission=allow for all tools and connections.",
131
+ ];
132
+ // Merge gateway/custom provider definitions supplied via PAPERCLIP_OPENCODE_PROVIDERS
133
+ // (a JSON object in OpenCode's `provider` shape). OpenCode resolves a `--model
134
+ // provider/model` only when that model exists in a provider's `models` map, and
135
+ // OPENCODE_ALLOW_ALL_MODELS does NOT bypass its internal getModel(). So routing a
136
+ // gateway model (e.g. an EU LLM gateway exposing OpenAI-compatible /v1) requires a
137
+ // custom provider with an explicit models map. We accept it as config (not
138
+ // hard-coded) so the gateway URL, key env, and model list stay declarative.
139
+ const resolveEnv = (name) => input.env[name] ?? process.env[name];
140
+ const gatewayProviders = parseProviderConfig(input.env.PAPERCLIP_OPENCODE_PROVIDERS ?? process.env.PAPERCLIP_OPENCODE_PROVIDERS, resolveEnv, notes);
141
+ const existingProvider = isPlainObject(existingConfig.provider) ? existingConfig.provider : {};
142
+ let nextProvider = gatewayProviders
143
+ ? { ...existingProvider, ...gatewayProviders }
144
+ : existingProvider;
145
+ if (gatewayProviders) {
146
+ notes.push(`Injected ${Object.keys(gatewayProviders).length} custom OpenCode provider(s) from PAPERCLIP_OPENCODE_PROVIDERS: ${Object.keys(gatewayProviders).join(", ")}.`);
147
+ }
148
+ // Register the configured model on its provider's models map. OpenCode resolves
149
+ // `--model provider/model` only when the model id exists in that map, so ids the
150
+ // models.dev catalog does not carry — OpenRouter routing variants such as
151
+ // `openai/gpt-oss-120b:nitro`, or models newer than the bundled catalog — are
152
+ // otherwise rejected with "Model not found" even though the provider serves them.
153
+ // An empty entry deep-merges with catalog metadata, so this is a no-op for models
154
+ // the catalog already knows, and we never clobber an explicit definition from the
155
+ // user config or PAPERCLIP_OPENCODE_PROVIDERS.
156
+ const configuredModel = parseConfiguredModelRef(input.config.model);
157
+ if (configuredModel) {
158
+ const providerEntry = isPlainObject(nextProvider[configuredModel.provider])
159
+ ? { ...nextProvider[configuredModel.provider] }
160
+ : {};
161
+ const providerModels = isPlainObject(providerEntry.models)
162
+ ? { ...providerEntry.models }
163
+ : {};
164
+ if (!isPlainObject(providerModels[configuredModel.model])) {
165
+ providerModels[configuredModel.model] = {};
166
+ providerEntry.models = providerModels;
167
+ nextProvider = { ...nextProvider, [configuredModel.provider]: providerEntry };
168
+ notes.push(`Registered configured model ${configuredModel.provider}/${configuredModel.model} in the runtime OpenCode config.`);
169
+ }
170
+ }
171
+ const nextConfig = {
172
+ ...existingConfig,
173
+ permission: "allow",
174
+ };
175
+ if (Object.keys(nextProvider).length > 0) {
176
+ nextConfig.provider = nextProvider;
177
+ }
178
+ // Pin OpenCode's auxiliary "small" model (used for session-title generation and
179
+ // other helper tasks) via PAPERCLIP_OPENCODE_SMALL_MODEL. OpenCode otherwise
180
+ // defaults the small model to a built-in provider default (e.g. a claude-* model
181
+ // for the anthropic provider); when that provider is repointed at a gateway that
182
+ // does not serve that exact model, the title-gen call fails and aborts the run.
183
+ // Setting small_model to a gateway-served model keeps every call on supported models.
184
+ const smallModel = (input.env.PAPERCLIP_OPENCODE_SMALL_MODEL ?? process.env.PAPERCLIP_OPENCODE_SMALL_MODEL)?.trim();
185
+ if (smallModel) {
186
+ nextConfig.small_model = smallModel;
187
+ notes.push(`Pinned OpenCode small_model to ${smallModel}.`);
188
+ }
189
+ await fs.writeFile(runtimeConfigPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
190
+ return {
191
+ env: {
192
+ ...input.env,
193
+ XDG_CONFIG_HOME: runtimeConfigHome,
194
+ },
195
+ notes,
196
+ cleanup: async () => {
197
+ await fs.rm(runtimeConfigHome, { recursive: true, force: true });
198
+ },
199
+ };
200
+ }
201
+ /** Managed credentials must never leave host-only homes in a remote process. */
202
+ export function prepareManagedOpenCodeRemoteHomes(input) {
203
+ if (!input.config.managedAiConnection)
204
+ return;
205
+ if (!input.runtimeRootDir)
206
+ throw new Error("Managed OpenCode authentication requires an isolated remote runtime directory.");
207
+ const home = path.posix.join(input.runtimeRootDir, "managed-auth", input.runId);
208
+ Object.assign(input.env, {
209
+ HOME: home,
210
+ XDG_CONFIG_HOME: input.configDir ?? path.posix.join(home, "config"),
211
+ XDG_DATA_HOME: path.posix.join(home, "data"),
212
+ XDG_CACHE_HOME: path.posix.join(home, "cache"),
213
+ XDG_STATE_HOME: path.posix.join(home, "state"),
214
+ });
215
+ }
216
+ //# sourceMappingURL=runtime-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.js","sourceRoot":"","sources":["../../src/server/runtime-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime-config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.test.d.ts","sourceRoot":"","sources":["../../src/server/runtime-config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,255 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { prepareOpenCodeRuntimeConfig } from "./runtime-config.js";
6
+ const cleanupPaths = new Set();
7
+ afterEach(async () => {
8
+ await Promise.all([...cleanupPaths].map(async (filepath) => {
9
+ await fs.rm(filepath, { recursive: true, force: true });
10
+ cleanupPaths.delete(filepath);
11
+ }));
12
+ });
13
+ async function makeConfigHome(initialConfig) {
14
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-opencode-test-"));
15
+ cleanupPaths.add(root);
16
+ const configDir = path.join(root, "opencode");
17
+ await fs.mkdir(configDir, { recursive: true });
18
+ if (initialConfig) {
19
+ await fs.writeFile(path.join(configDir, "opencode.json"), `${JSON.stringify(initialConfig, null, 2)}\n`, "utf8");
20
+ }
21
+ return root;
22
+ }
23
+ describe("prepareOpenCodeRuntimeConfig", () => {
24
+ it("allows all tools and connected tools by default", async () => {
25
+ const configHome = await makeConfigHome({
26
+ permission: {
27
+ read: "allow",
28
+ bash: "ask",
29
+ "mcp__example__write": "deny",
30
+ },
31
+ theme: "system",
32
+ });
33
+ const prepared = await prepareOpenCodeRuntimeConfig({
34
+ env: { XDG_CONFIG_HOME: configHome },
35
+ config: {},
36
+ });
37
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
38
+ expect(prepared.env.XDG_CONFIG_HOME).not.toBe(configHome);
39
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
40
+ expect(runtimeConfig).toMatchObject({
41
+ theme: "system",
42
+ permission: "allow",
43
+ });
44
+ await prepared.cleanup();
45
+ cleanupPaths.delete(prepared.env.XDG_CONFIG_HOME);
46
+ await expect(fs.access(prepared.env.XDG_CONFIG_HOME)).rejects.toThrow();
47
+ });
48
+ it("merges custom providers from PAPERCLIP_OPENCODE_PROVIDERS into the config", async () => {
49
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
50
+ const providers = {
51
+ bifrost: {
52
+ npm: "@ai-sdk/openai-compatible",
53
+ name: "Bifrost EU",
54
+ options: {
55
+ baseURL: "http://gateway.example.svc.cluster.local:8080/v1",
56
+ apiKey: "{env:ANTHROPIC_API_KEY}",
57
+ },
58
+ models: { "example/model-a": { name: "Model A" } },
59
+ },
60
+ };
61
+ const prepared = await prepareOpenCodeRuntimeConfig({
62
+ env: {
63
+ XDG_CONFIG_HOME: configHome,
64
+ PAPERCLIP_OPENCODE_PROVIDERS: JSON.stringify(providers),
65
+ },
66
+ config: {},
67
+ });
68
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
69
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
70
+ expect(runtimeConfig).toMatchObject({
71
+ permission: "allow",
72
+ provider: providers,
73
+ });
74
+ expect(prepared.notes.some((n) => n.includes("bifrost"))).toBe(true);
75
+ await prepared.cleanup();
76
+ });
77
+ it("reads PAPERCLIP_OPENCODE_PROVIDERS from process.env when absent from the run env", async () => {
78
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
79
+ const providers = { bifrost: { npm: "@ai-sdk/openai-compatible", models: { "example/model-a": {} } } };
80
+ process.env.PAPERCLIP_OPENCODE_PROVIDERS = JSON.stringify(providers);
81
+ try {
82
+ const prepared = await prepareOpenCodeRuntimeConfig({
83
+ env: { XDG_CONFIG_HOME: configHome },
84
+ config: {},
85
+ });
86
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
87
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
88
+ expect(runtimeConfig).toMatchObject({ provider: providers });
89
+ await prepared.cleanup();
90
+ }
91
+ finally {
92
+ delete process.env.PAPERCLIP_OPENCODE_PROVIDERS;
93
+ }
94
+ });
95
+ it("expands {env:VAR} placeholders in custom providers using the run/process env (bakes the literal vk)", async () => {
96
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
97
+ const providers = {
98
+ bifrost: {
99
+ npm: "@ai-sdk/openai-compatible",
100
+ options: { baseURL: "http://bifrost/v1", apiKey: "{env:ANTHROPIC_API_KEY}" },
101
+ models: { "example/model-a": {} },
102
+ },
103
+ };
104
+ const prepared = await prepareOpenCodeRuntimeConfig({
105
+ env: { XDG_CONFIG_HOME: configHome, PAPERCLIP_OPENCODE_PROVIDERS: JSON.stringify(providers), ANTHROPIC_API_KEY: "sk-bf-REALVK" },
106
+ config: {},
107
+ });
108
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
109
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
110
+ // The {env:...} placeholder must be replaced with the literal value, so OpenCode
111
+ // does not depend on its sandboxed process env carrying the key.
112
+ expect(runtimeConfig.provider.bifrost.options.apiKey).toBe("sk-bf-REALVK");
113
+ await prepared.cleanup();
114
+ });
115
+ it("leaves an unresolvable {env:VAR} placeholder intact", async () => {
116
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
117
+ const providers = { bifrost: { options: { apiKey: "{env:DEFINITELY_UNSET_VAR_XYZ}" }, models: { "x/y": {} } } };
118
+ const prepared = await prepareOpenCodeRuntimeConfig({
119
+ env: { XDG_CONFIG_HOME: configHome, PAPERCLIP_OPENCODE_PROVIDERS: JSON.stringify(providers) },
120
+ config: {},
121
+ });
122
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
123
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
124
+ expect(runtimeConfig.provider.bifrost.options.apiKey).toBe("{env:DEFINITELY_UNSET_VAR_XYZ}");
125
+ await prepared.cleanup();
126
+ });
127
+ it("pins small_model from PAPERCLIP_OPENCODE_SMALL_MODEL", async () => {
128
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
129
+ const prepared = await prepareOpenCodeRuntimeConfig({
130
+ env: { XDG_CONFIG_HOME: configHome, PAPERCLIP_OPENCODE_SMALL_MODEL: "example/model-a" },
131
+ config: {},
132
+ });
133
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
134
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
135
+ expect(runtimeConfig.small_model).toBe("example/model-a");
136
+ await prepared.cleanup();
137
+ });
138
+ it("ignores malformed PAPERCLIP_OPENCODE_PROVIDERS without writing a provider block and surfaces a note", async () => {
139
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
140
+ const prepared = await prepareOpenCodeRuntimeConfig({
141
+ env: { XDG_CONFIG_HOME: configHome, PAPERCLIP_OPENCODE_PROVIDERS: "not json" },
142
+ config: {},
143
+ });
144
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
145
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
146
+ expect(runtimeConfig.provider).toBeUndefined();
147
+ expect(prepared.notes).toContain("PAPERCLIP_OPENCODE_PROVIDERS contains invalid JSON; custom providers ignored.");
148
+ await prepared.cleanup();
149
+ });
150
+ it("surfaces a note when PAPERCLIP_OPENCODE_PROVIDERS is valid JSON but not an object", async () => {
151
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
152
+ const prepared = await prepareOpenCodeRuntimeConfig({
153
+ env: { XDG_CONFIG_HOME: configHome, PAPERCLIP_OPENCODE_PROVIDERS: "[1,2,3]" },
154
+ config: {},
155
+ });
156
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
157
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
158
+ expect(runtimeConfig.provider).toBeUndefined();
159
+ expect(prepared.notes).toContain("PAPERCLIP_OPENCODE_PROVIDERS is set but is not a JSON object; custom providers ignored.");
160
+ await prepared.cleanup();
161
+ });
162
+ it("surfaces skipped provider entries with non-object values and keeps the usable ones", async () => {
163
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
164
+ const prepared = await prepareOpenCodeRuntimeConfig({
165
+ env: {
166
+ XDG_CONFIG_HOME: configHome,
167
+ PAPERCLIP_OPENCODE_PROVIDERS: JSON.stringify({
168
+ bifrost: "http://gateway.example/v1",
169
+ usable: { options: { baseURL: "http://gateway.example/v1" } },
170
+ }),
171
+ },
172
+ config: {},
173
+ });
174
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
175
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
176
+ expect(runtimeConfig.provider?.usable).toBeDefined();
177
+ expect(runtimeConfig.provider?.bifrost).toBeUndefined();
178
+ expect(prepared.notes).toContain("PAPERCLIP_OPENCODE_PROVIDERS: skipped provider(s) with non-object values: bifrost.");
179
+ await prepared.cleanup();
180
+ });
181
+ it("surfaces skipped provider entries when no usable entries remain", async () => {
182
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
183
+ const prepared = await prepareOpenCodeRuntimeConfig({
184
+ env: {
185
+ XDG_CONFIG_HOME: configHome,
186
+ PAPERCLIP_OPENCODE_PROVIDERS: JSON.stringify({ bifrost: "http://gateway.example/v1" }),
187
+ },
188
+ config: {},
189
+ });
190
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
191
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
192
+ expect(runtimeConfig.provider).toBeUndefined();
193
+ expect(prepared.notes).toContain("PAPERCLIP_OPENCODE_PROVIDERS: skipped provider(s) with non-object values: bifrost.");
194
+ await prepared.cleanup();
195
+ });
196
+ it("registers a configured model missing from the catalog on its provider", async () => {
197
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
198
+ const prepared = await prepareOpenCodeRuntimeConfig({
199
+ env: { XDG_CONFIG_HOME: configHome },
200
+ config: { model: "openrouter/openai/gpt-oss-120b:nitro" },
201
+ });
202
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
203
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
204
+ expect(runtimeConfig.provider?.openrouter?.models).toEqual({
205
+ "openai/gpt-oss-120b:nitro": {},
206
+ });
207
+ expect(prepared.notes).toContain("Registered configured model openrouter/openai/gpt-oss-120b:nitro in the runtime OpenCode config.");
208
+ await prepared.cleanup();
209
+ });
210
+ it("does not clobber an explicit model definition when registering the configured model", async () => {
211
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
212
+ const providers = {
213
+ openrouter: {
214
+ models: {
215
+ "openai/gpt-oss-120b:nitro": { name: "GPT-OSS 120B (nitro)" },
216
+ "example/other": {},
217
+ },
218
+ },
219
+ };
220
+ const prepared = await prepareOpenCodeRuntimeConfig({
221
+ env: {
222
+ XDG_CONFIG_HOME: configHome,
223
+ PAPERCLIP_OPENCODE_PROVIDERS: JSON.stringify(providers),
224
+ },
225
+ config: { model: "openrouter/openai/gpt-oss-120b:nitro" },
226
+ });
227
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
228
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
229
+ expect(runtimeConfig.provider?.openrouter?.models).toEqual(providers.openrouter.models);
230
+ expect(prepared.notes.some((note) => note.startsWith("Registered configured model"))).toBe(false);
231
+ await prepared.cleanup();
232
+ });
233
+ it("skips model registration when the configured model is not provider/model shaped", async () => {
234
+ const configHome = await makeConfigHome({ permission: { read: "allow" } });
235
+ const prepared = await prepareOpenCodeRuntimeConfig({
236
+ env: { XDG_CONFIG_HOME: configHome },
237
+ config: { model: "not-a-provider-model" },
238
+ });
239
+ cleanupPaths.add(prepared.env.XDG_CONFIG_HOME);
240
+ const runtimeConfig = JSON.parse(await fs.readFile(path.join(prepared.env.XDG_CONFIG_HOME, "opencode", "opencode.json"), "utf8"));
241
+ expect(runtimeConfig.provider).toBeUndefined();
242
+ await prepared.cleanup();
243
+ });
244
+ it("respects explicit opt-out", async () => {
245
+ const configHome = await makeConfigHome();
246
+ const prepared = await prepareOpenCodeRuntimeConfig({
247
+ env: { XDG_CONFIG_HOME: configHome },
248
+ config: { dangerouslySkipPermissions: false },
249
+ });
250
+ expect(prepared.env).toEqual({ XDG_CONFIG_HOME: configHome });
251
+ expect(prepared.notes).toEqual([]);
252
+ await prepared.cleanup();
253
+ });
254
+ });
255
+ //# sourceMappingURL=runtime-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.test.js","sourceRoot":"","sources":["../../src/server/runtime-config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { AdapterSkillContext, AdapterSkillSnapshot } from "@tickernelz/paperclip-pro-adapter-utils";
2
+ export declare function resolveOpenCodeSkillsHome(config: Record<string, unknown>): string;
3
+ export declare function listOpenCodeSkills(ctx: AdapterSkillContext): Promise<AdapterSkillSnapshot>;
4
+ export declare function syncOpenCodeSkills(ctx: AdapterSkillContext, desiredSkills: string[]): Promise<AdapterSkillSnapshot>;
5
+ export declare function resolveOpenCodeDesiredSkillNames(config: Record<string, unknown>, availableEntries: Array<{
6
+ key: string;
7
+ }>): string[];
8
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/server/skills.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,yCAAyC,CAAC;AAejD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,UAQxE;AAwBD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAEhG;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,mBAAmB,EACxB,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,oBAAoB,CAAC,CA0B/B;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,gBAAgB,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,YAGzC"}
@@ -0,0 +1,73 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { buildPersistentSkillSnapshot, ensurePaperclipSkillSymlink, readPaperclipRuntimeSkillEntries, readInstalledSkillTargets, resolveLegacyPaperclipDesiredSkillNames, } from "@tickernelz/paperclip-pro-adapter-utils/server-utils";
6
+ const __moduleDir = path.dirname(fileURLToPath(import.meta.url));
7
+ function asString(value) {
8
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
9
+ }
10
+ export function resolveOpenCodeSkillsHome(config) {
11
+ const env = typeof config.env === "object" && config.env !== null && !Array.isArray(config.env)
12
+ ? config.env
13
+ : {};
14
+ const configuredHome = asString(env.HOME);
15
+ const home = configuredHome ? path.resolve(configuredHome) : os.homedir();
16
+ return path.join(home, ".claude", "skills");
17
+ }
18
+ async function buildOpenCodeSkillSnapshot(config) {
19
+ const availableEntries = await readPaperclipRuntimeSkillEntries(config, __moduleDir);
20
+ const desiredSkills = resolveLegacyPaperclipDesiredSkillNames(config, availableEntries);
21
+ const skillsHome = resolveOpenCodeSkillsHome(config);
22
+ const installed = await readInstalledSkillTargets(skillsHome);
23
+ return buildPersistentSkillSnapshot({
24
+ adapterType: "opencode_local",
25
+ availableEntries,
26
+ desiredSkills,
27
+ installed,
28
+ skillsHome,
29
+ locationLabel: "~/.claude/skills",
30
+ installedDetail: "Installed in the shared Claude/OpenCode skills home.",
31
+ missingDetail: "Configured but not currently linked into the shared Claude/OpenCode skills home.",
32
+ externalConflictDetail: "Skill name is occupied by an external installation in the shared skills home.",
33
+ externalDetail: "Installed outside Paperclip management in the shared skills home.",
34
+ warnings: [
35
+ "OpenCode currently uses the shared Claude skills home (~/.claude/skills).",
36
+ ],
37
+ });
38
+ }
39
+ export async function listOpenCodeSkills(ctx) {
40
+ return buildOpenCodeSkillSnapshot(ctx.config);
41
+ }
42
+ export async function syncOpenCodeSkills(ctx, desiredSkills) {
43
+ const availableEntries = await readPaperclipRuntimeSkillEntries(ctx.config, __moduleDir);
44
+ const desiredSet = new Set([
45
+ ...resolveLegacyPaperclipDesiredSkillNames({}, availableEntries),
46
+ ...desiredSkills,
47
+ ]);
48
+ const skillsHome = resolveOpenCodeSkillsHome(ctx.config);
49
+ await fs.mkdir(skillsHome, { recursive: true });
50
+ const installed = await readInstalledSkillTargets(skillsHome);
51
+ const availableByRuntimeName = new Map(availableEntries.map((entry) => [entry.runtimeName, entry]));
52
+ for (const available of availableEntries) {
53
+ if (!desiredSet.has(available.key))
54
+ continue;
55
+ const target = path.join(skillsHome, available.runtimeName);
56
+ await ensurePaperclipSkillSymlink(available.source, target);
57
+ }
58
+ for (const [name, installedEntry] of installed.entries()) {
59
+ const available = availableByRuntimeName.get(name);
60
+ if (!available)
61
+ continue;
62
+ if (desiredSet.has(available.key))
63
+ continue;
64
+ if (installedEntry.targetPath !== available.source)
65
+ continue;
66
+ await fs.unlink(path.join(skillsHome, name)).catch(() => { });
67
+ }
68
+ return buildOpenCodeSkillSnapshot(ctx.config);
69
+ }
70
+ export function resolveOpenCodeDesiredSkillNames(config, availableEntries) {
71
+ return resolveLegacyPaperclipDesiredSkillNames(config, availableEntries);
72
+ }
73
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/server/skills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AdapterEnvironmentTestContext, AdapterEnvironmentTestResult } from "@tickernelz/paperclip-pro-adapter-utils";
2
+ export declare function testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult>;
3
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/server/test.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,6BAA6B,EAC7B,4BAA4B,EAC7B,MAAM,yCAAyC,CAAC;AA4DjD,wBAAsB,eAAe,CACnC,GAAG,EAAE,6BAA6B,GACjC,OAAO,CAAC,4BAA4B,CAAC,CA0XvC"}