opencode-1password-auth 1.0.3 → 1.0.5

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
@@ -31,7 +31,9 @@ Create these environments in 1Password:
31
31
  - `OPENCODE_MCPS_ENV_ID` - Environment ID containing MCP server secrets
32
32
 
33
33
  **Providers Environment** (holds API keys):
34
- - Provider IDs as variable names (e.g., `MINIMAX_CODING_PLAN`, `DEEPSEEK`, `OPENCODE`)
34
+ - Variable names should match provider IDs (case-insensitive, underscores converted to hyphens)
35
+ - Examples: `opencode`, `OPENCODE`, `deepseek`, `DEEPSEEK`, `minimax-coding-plan`, `MINIMAX_CODING_PLAN`
36
+ - The plugin normalizes: lowercase + underscores → hyphens
35
37
  - API keys as values
36
38
 
37
39
  **MCPS Environment** (holds MCP secrets):
@@ -97,9 +99,9 @@ To change configurations, simply update `OP_CONFIG_ENV_ID` to point to a differe
97
99
  ### Providers Environment
98
100
  ```
99
101
  opencode-providers
100
- ├── MINIMAX_CODING_PLAN = your-minimax-api-key
101
- ├── DEEPSEEK = your-deepseek-api-key
102
- └── OPENCODE = your-opencode-api-key
102
+ ├── opencode = your-opencode-api-key # or OPENCODE
103
+ ├── deepseek = your-deepseek-api-key # or DEEPSEEK
104
+ └── minimax-coding-plan = your-minimax-api-key # or MINIMAX_CODING_PLAN
103
105
  ```
104
106
 
105
107
  ### MCPS Environment
package/index-v2.ts ADDED
@@ -0,0 +1,387 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ import * as sdk from "@1password/sdk";
3
+
4
+ // Debug logging to file
5
+ const debugLog = (message: string) => {
6
+ const timestamp = new Date().toISOString();
7
+ const logMessage = `[${timestamp}] 1Password: ${message}\n`;
8
+ console.log(logMessage.trim());
9
+
10
+ try {
11
+ // Try to write to a debug log file
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const home = process.env.HOME || process.env.USERPROFILE || '';
15
+ const logDir = path.join(home, '.opencode-1password-debug');
16
+ if (!fs.existsSync(logDir)) {
17
+ fs.mkdirSync(logDir, { recursive: true });
18
+ }
19
+ const logFile = path.join(logDir, 'debug.log');
20
+ fs.appendFileSync(logFile, logMessage);
21
+ } catch (err) {
22
+ // Ignore file logging errors
23
+ }
24
+ };
25
+
26
+ interface MCPServerConfig {
27
+ environment?: Record<string, string>;
28
+ }
29
+
30
+ interface OpenCodeConfig {
31
+ mcp?: Record<string, MCPServerConfig>;
32
+ }
33
+
34
+ interface ProviderAuth {
35
+ type: string;
36
+ key: string;
37
+ }
38
+
39
+ interface AuthJson {
40
+ [providerId: string]: ProviderAuth;
41
+ }
42
+
43
+ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
44
+ let opClient: Awaited<ReturnType<typeof sdk.createClient>> | null = null;
45
+ let mcpsEnvId: string | undefined;
46
+
47
+ debugLog("Plugin loaded");
48
+
49
+ const normalizeProviderId = (variableName: string): string => {
50
+ // Convert common patterns to match auth.json provider IDs
51
+ // e.g., "MINIMAX_CODING_PLAN" -> "minimax-coding-plan"
52
+ // e.g., "OPENCODE" -> "opencode"
53
+ // e.g., "DEEPSEEK" -> "deepseek"
54
+
55
+ let normalized = variableName.toLowerCase();
56
+
57
+ // Convert underscores to hyphens
58
+ normalized = normalized.replace(/_/g, '-');
59
+
60
+ debugLog(`Normalized ${variableName} -> ${normalized}`);
61
+ return normalized;
62
+ };
63
+
64
+ const getAlternativeEnvVarNames = (providerId: string): string[] => {
65
+ // Generate all possible env var names that might be used
66
+ const names = new Set<string>();
67
+
68
+ // Original providerId (e.g., "minimax-coding-plan")
69
+ names.add(providerId);
70
+
71
+ // Uppercase version (e.g., "MINIMAX-CODING-PLAN")
72
+ names.add(providerId.toUpperCase());
73
+
74
+ // With underscores instead of hyphens (e.g., "minimax_coding_plan")
75
+ names.add(providerId.replace(/-/g, '_'));
76
+
77
+ // Uppercase with underscores (e.g., "MINIMAX_CODING_PLAN")
78
+ names.add(providerId.replace(/-/g, '_').toUpperCase());
79
+
80
+ // Just the first part (e.g., "minimax")
81
+ const firstPart = providerId.split('-')[0];
82
+ if (firstPart && firstPart !== providerId) {
83
+ names.add(firstPart);
84
+ names.add(firstPart.toUpperCase());
85
+ }
86
+
87
+ return Array.from(names);
88
+ };
89
+
90
+ const getHomeDir = (): string => {
91
+ return process.env.HOME || process.env.USERPROFILE || "";
92
+ };
93
+
94
+ const getAuthJsonPath = (): string => {
95
+ const home = getHomeDir();
96
+ return `${home}/.local/share/opencode/auth.json`;
97
+ };
98
+
99
+ const getOpenCodeJsonPath = (): string => {
100
+ const home = getHomeDir();
101
+ return `${home}/.config/opencode/opencode.json`;
102
+ };
103
+
104
+ const initClient = async () => {
105
+ const token = process.env.OP_SERVICE_ACCOUNT_TOKEN;
106
+ if (!token) {
107
+ debugLog("Missing OP_SERVICE_ACCOUNT_TOKEN");
108
+ return null;
109
+ }
110
+
111
+ try {
112
+ return await sdk.createClient({
113
+ auth: token,
114
+ integrationName: "opencode-1password-env",
115
+ integrationVersion: "1.0.0",
116
+ });
117
+ } catch (err) {
118
+ debugLog(`Failed to create client: ${err}`);
119
+ return null;
120
+ }
121
+ };
122
+
123
+ const readEnvironmentVariables = async (envId: string) => {
124
+ if (!opClient) return [];
125
+
126
+ try {
127
+ const { variables } = await opClient.environments.getVariables(envId);
128
+ debugLog(`Read ${variables?.length || 0} variables from environment ${envId}`);
129
+ return variables || [];
130
+ } catch (err) {
131
+ debugLog(`Failed to read environment ${envId}: ${err}`);
132
+ return [];
133
+ }
134
+ };
135
+
136
+ const getSecretsFromEnvironment = async (envId: string): Promise<Record<string, string>> => {
137
+ const vars = await readEnvironmentVariables(envId);
138
+ const secrets: Record<string, string> = {};
139
+ for (const v of vars) {
140
+ if (v.value) {
141
+ secrets[v.name] = v.value;
142
+ debugLog(`Found secret: ${v.name} (${v.value.length} chars)`);
143
+ }
144
+ }
145
+ return secrets;
146
+ };
147
+
148
+ const updateAuthJson = async (providerEnvId: string): Promise<void> => {
149
+ const authJsonPath = getAuthJsonPath();
150
+
151
+ try {
152
+ // Read current auth.json using cat
153
+ const catResult = await $`cat "${authJsonPath}"`;
154
+ const content = catResult.stdout;
155
+ let auth: AuthJson;
156
+
157
+ try {
158
+ auth = JSON.parse(content);
159
+ } catch {
160
+ debugLog("auth.json is not valid JSON, skipping");
161
+ return;
162
+ }
163
+
164
+ let modified = false;
165
+
166
+ for (const [providerId, authConfig] of Object.entries(auth)) {
167
+ if (authConfig.key && !authConfig.key.startsWith("{env:")) {
168
+ // Replace hardcoded key with env var reference
169
+ authConfig.key = `{env:${providerId}}`;
170
+ modified = true;
171
+ debugLog(`Updated auth.json - ${providerId} -> {env:${providerId}}`);
172
+ }
173
+ }
174
+
175
+ if (modified) {
176
+ // Write updated content using a temp file and mv
177
+ const newContent = JSON.stringify(auth, null, 2);
178
+ // Use node to write the file since $ heredocs can be tricky
179
+ await $`node -e "const fs=require('fs'); fs.writeFileSync('${authJsonPath}', JSON.stringify(${JSON.stringify(auth)}, null, 2));"`;
180
+ debugLog("auth.json updated to use environment variables");
181
+ }
182
+ } catch (err) {
183
+ debugLog(`Failed to update auth.json: ${err}`);
184
+ }
185
+ };
186
+
187
+ const updateOpenCodeJsonMCP = async (mcpEnvId: string): Promise<void> => {
188
+ const openCodeJsonPath = getOpenCodeJsonPath();
189
+
190
+ try {
191
+ // Read current opencode.json using cat
192
+ const catResult = await $`cat "${openCodeJsonPath}"`;
193
+ const content = catResult.stdout;
194
+ let config: OpenCodeConfig;
195
+
196
+ try {
197
+ config = JSON.parse(content);
198
+ } catch {
199
+ debugLog("opencode.json is not valid JSON, skipping");
200
+ return;
201
+ }
202
+
203
+ if (!config.mcp) {
204
+ debugLog("No MCP configuration found, skipping");
205
+ return;
206
+ }
207
+
208
+ let modified = false;
209
+
210
+ for (const [serverName, serverConfig] of Object.entries(config.mcp)) {
211
+ if (serverConfig?.environment) {
212
+ for (const [key, value] of Object.entries(serverConfig.environment)) {
213
+ if (typeof value === "string" && !value.startsWith("{env:") && !value.startsWith("$")) {
214
+ // Replace hardcoded value with env var reference
215
+ serverConfig.environment[key] = `{env:${key}}`;
216
+ modified = true;
217
+ debugLog(`Updated opencode.json - ${serverName}.${key} -> {env:${key}}`);
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ if (modified) {
224
+ // Write updated content
225
+ await $`node -e "const fs=require('fs'); fs.writeFileSync('${openCodeJsonPath}', JSON.stringify(${JSON.stringify(config)}, null, 2));"`;
226
+ debugLog("opencode.json MCP config updated to use environment variables");
227
+ }
228
+ } catch (err) {
229
+ debugLog(`Failed to update opencode.json: ${err}`);
230
+ }
231
+ };
232
+
233
+ const authenticateProviders = async (providerEnvId: string): Promise<void> => {
234
+ if (!opClient) return;
235
+
236
+ debugLog(`Reading provider secrets from environment ${providerEnvId}`);
237
+ const secrets = await getSecretsFromEnvironment(providerEnvId);
238
+ debugLog(`Found ${Object.keys(secrets).length} provider secrets`);
239
+
240
+ for (const [variableName, apiKey] of Object.entries(secrets)) {
241
+ if (!apiKey) continue;
242
+
243
+ const providerId = normalizeProviderId(variableName);
244
+ debugLog(`Processing ${variableName} -> ${providerId} (key length: ${apiKey.length})`);
245
+
246
+ // Set environment variables in multiple formats
247
+ const envVarNames = getAlternativeEnvVarNames(providerId);
248
+ for (const envVarName of envVarNames) {
249
+ process.env[envVarName] = apiKey;
250
+ debugLog(`Set process.env.${envVarName} = ${apiKey.substring(0, 8)}...`);
251
+ }
252
+
253
+ try {
254
+ // Authenticate with OpenCode runtime
255
+ debugLog(`Calling client.auth.set for ${providerId}`);
256
+ await client.auth.set({
257
+ path: { id: providerId },
258
+ body: { type: "api", key: apiKey },
259
+ });
260
+ debugLog(`✓ ${providerId} authenticated (from ${variableName})`);
261
+ } catch (err) {
262
+ debugLog(`Failed to authenticate ${providerId}: ${err}`);
263
+ if (err instanceof Error) {
264
+ debugLog(`Error message: ${err.message}`);
265
+ }
266
+ }
267
+ }
268
+ };
269
+
270
+ const getConfiguredMCPVars = (): Set<string> => {
271
+ const vars = new Set<string>();
272
+
273
+ try {
274
+ const config = client.config as OpenCodeConfig;
275
+ if (config?.mcp) {
276
+ for (const [, serverConfig] of Object.entries(config.mcp)) {
277
+ if (serverConfig?.environment) {
278
+ for (const [key] of Object.entries(serverConfig.environment)) {
279
+ if (key.startsWith("{env:")) {
280
+ const envVar = key.slice(5, -1);
281
+ vars.add(envVar);
282
+ } else {
283
+ vars.add(key);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
289
+ } catch (err) {
290
+ debugLog(`Failed to read MCP config: ${err}`);
291
+ }
292
+
293
+ return vars;
294
+ };
295
+
296
+ const injectMCPSecrets = async (mcpEnvId: string): Promise<Record<string, string>> => {
297
+ const neededVars = getConfiguredMCPVars();
298
+ if (neededVars.size === 0) {
299
+ return {};
300
+ }
301
+
302
+ const secrets = await getSecretsFromEnvironment(mcpEnvId);
303
+ const toInject: Record<string, string> = {};
304
+
305
+ for (const varName of neededVars) {
306
+ if (secrets[varName]) {
307
+ toInject[varName] = secrets[varName];
308
+ }
309
+ }
310
+
311
+ return toInject;
312
+ };
313
+
314
+ return {
315
+ async event({ event }: { event: { type: string } }) {
316
+ debugLog(`Received event: ${event.type}`);
317
+ if (event.type === "server.connected") {
318
+ debugLog("Initializing...");
319
+
320
+ opClient = await initClient();
321
+ if (!opClient) {
322
+ debugLog("No client available");
323
+ return;
324
+ }
325
+
326
+ const configEnvId = process.env.OP_CONFIG_ENV_ID;
327
+ if (!configEnvId) {
328
+ debugLog("Missing OP_CONFIG_ENV_ID");
329
+ return;
330
+ }
331
+
332
+ debugLog(`Reading config from environment ${configEnvId}`);
333
+ const vars = await readEnvironmentVariables(configEnvId);
334
+ const configEnvIds: Record<string, string> = {};
335
+ for (const v of vars) {
336
+ if (v.name.endsWith("_ENV_ID") && v.value) {
337
+ configEnvIds[v.name] = v.value;
338
+ debugLog(`Found ${v.name} = ${v.value.substring(0, 10)}...`);
339
+ }
340
+ }
341
+
342
+ const providersEnvId = configEnvIds.OPENCODE_PROVIDERS_ENV_ID;
343
+ if (providersEnvId) {
344
+ // First update the config files to use env var references
345
+ await updateAuthJson(providersEnvId);
346
+ await authenticateProviders(providersEnvId);
347
+ }
348
+
349
+ const mcpEnvIdFromConfig = configEnvIds.OPENCODE_MCPS_ENV_ID;
350
+ if (mcpEnvIdFromConfig) {
351
+ mcpsEnvId = mcpEnvIdFromConfig;
352
+ await updateOpenCodeJsonMCP(mcpEnvIdFromConfig);
353
+ const toInject = await injectMCPSecrets(mcpEnvIdFromConfig);
354
+ if (Object.keys(toInject).length > 0) {
355
+ debugLog(`Injected ${Object.keys(toInject).join(", ")} for MCP`);
356
+ }
357
+ }
358
+
359
+ debugLog("Initialization complete");
360
+ }
361
+ },
362
+
363
+ async "shell.env"(input, output) {
364
+ if (!opClient || !mcpsEnvId) return;
365
+
366
+ const toInject = await injectMCPSecrets(mcpsEnvId);
367
+
368
+ for (const [varName, value] of Object.entries(toInject)) {
369
+ output.env[varName] = value;
370
+ debugLog(`Injected ${varName} into shell environment`);
371
+ }
372
+ },
373
+
374
+ async "tool.execute.before"(input) {
375
+ if (input.tool !== "read") return;
376
+
377
+ const fileArgs = input.args as { filePath?: string } | undefined;
378
+ if (fileArgs?.filePath && fileArgs.filePath.includes(".env")) {
379
+ debugLog(
380
+ "Warning - .env file access detected. Consider using 1Password for secrets management."
381
+ );
382
+ }
383
+ },
384
+ };
385
+ };
386
+
387
+ export default OnePasswordAuthPlugin;
package/index.ts CHANGED
@@ -1,6 +1,28 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
2
  import * as sdk from "@1password/sdk";
3
3
 
4
+ // Debug logging to file
5
+ const debugLog = (message: string) => {
6
+ const timestamp = new Date().toISOString();
7
+ const logMessage = `[${timestamp}] 1Password: ${message}\n`;
8
+ console.log(logMessage.trim());
9
+
10
+ try {
11
+ // Try to write to a debug log file
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const home = process.env.HOME || process.env.USERPROFILE || '';
15
+ const logDir = path.join(home, '.opencode-1password-debug');
16
+ if (!fs.existsSync(logDir)) {
17
+ fs.mkdirSync(logDir, { recursive: true });
18
+ }
19
+ const logFile = path.join(logDir, 'debug.log');
20
+ fs.appendFileSync(logFile, logMessage);
21
+ } catch (err) {
22
+ // Ignore file logging errors
23
+ }
24
+ };
25
+
4
26
  interface MCPServerConfig {
5
27
  environment?: Record<string, string>;
6
28
  }
@@ -22,6 +44,49 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
22
44
  let opClient: Awaited<ReturnType<typeof sdk.createClient>> | null = null;
23
45
  let mcpsEnvId: string | undefined;
24
46
 
47
+ debugLog("Plugin loaded");
48
+
49
+ const normalizeProviderId = (variableName: string): string => {
50
+ // Convert common patterns to match auth.json provider IDs
51
+ // e.g., "MINIMAX_CODING_PLAN" -> "minimax-coding-plan"
52
+ // e.g., "OPENCODE" -> "opencode"
53
+ // e.g., "DEEPSEEK" -> "deepseek"
54
+
55
+ let normalized = variableName.toLowerCase();
56
+
57
+ // Convert underscores to hyphens
58
+ normalized = normalized.replace(/_/g, '-');
59
+
60
+ debugLog(`Normalized ${variableName} -> ${normalized}`);
61
+ return normalized;
62
+ };
63
+
64
+ const getAlternativeEnvVarNames = (providerId: string): string[] => {
65
+ // Generate all possible env var names that might be used
66
+ const names = new Set<string>();
67
+
68
+ // Original providerId (e.g., "minimax-coding-plan")
69
+ names.add(providerId);
70
+
71
+ // Uppercase version (e.g., "MINIMAX-CODING-PLAN")
72
+ names.add(providerId.toUpperCase());
73
+
74
+ // With underscores instead of hyphens (e.g., "minimax_coding_plan")
75
+ names.add(providerId.replace(/-/g, '_'));
76
+
77
+ // Uppercase with underscores (e.g., "MINIMAX_CODING_PLAN")
78
+ names.add(providerId.replace(/-/g, '_').toUpperCase());
79
+
80
+ // Just the first part (e.g., "minimax")
81
+ const firstPart = providerId.split('-')[0];
82
+ if (firstPart && firstPart !== providerId) {
83
+ names.add(firstPart);
84
+ names.add(firstPart.toUpperCase());
85
+ }
86
+
87
+ return Array.from(names);
88
+ };
89
+
25
90
  const getHomeDir = (): string => {
26
91
  return process.env.HOME || process.env.USERPROFILE || "";
27
92
  };
@@ -39,7 +104,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
39
104
  const initClient = async () => {
40
105
  const token = process.env.OP_SERVICE_ACCOUNT_TOKEN;
41
106
  if (!token) {
42
- console.error("1Password: Missing OP_SERVICE_ACCOUNT_TOKEN");
107
+ debugLog("Missing OP_SERVICE_ACCOUNT_TOKEN");
43
108
  return null;
44
109
  }
45
110
 
@@ -50,7 +115,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
50
115
  integrationVersion: "1.0.0",
51
116
  });
52
117
  } catch (err) {
53
- console.error("1Password: Failed to create client:", err);
118
+ debugLog(`Failed to create client: ${err}`);
54
119
  return null;
55
120
  }
56
121
  };
@@ -60,9 +125,10 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
60
125
 
61
126
  try {
62
127
  const { variables } = await opClient.environments.getVariables(envId);
128
+ debugLog(`Read ${variables?.length || 0} variables from environment ${envId}`);
63
129
  return variables || [];
64
130
  } catch (err) {
65
- console.error(`1Password: Failed to read environment ${envId}:`, err);
131
+ debugLog(`Failed to read environment ${envId}: ${err}`);
66
132
  return [];
67
133
  }
68
134
  };
@@ -73,6 +139,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
73
139
  for (const v of vars) {
74
140
  if (v.value) {
75
141
  secrets[v.name] = v.value;
142
+ debugLog(`Found secret: ${v.name} (${v.value.length} chars)`);
76
143
  }
77
144
  }
78
145
  return secrets;
@@ -90,7 +157,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
90
157
  try {
91
158
  auth = JSON.parse(content);
92
159
  } catch {
93
- console.log("1Password: auth.json is not valid JSON, skipping");
160
+ debugLog("auth.json is not valid JSON, skipping");
94
161
  return;
95
162
  }
96
163
 
@@ -101,7 +168,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
101
168
  // Replace hardcoded key with env var reference
102
169
  authConfig.key = `{env:${providerId}}`;
103
170
  modified = true;
104
- console.log(`1Password: Updated auth.json - ${providerId} -> {env:${providerId}}`);
171
+ debugLog(`Updated auth.json - ${providerId} -> {env:${providerId}}`);
105
172
  }
106
173
  }
107
174
 
@@ -110,10 +177,10 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
110
177
  const newContent = JSON.stringify(auth, null, 2);
111
178
  // Use node to write the file since $ heredocs can be tricky
112
179
  await $`node -e "const fs=require('fs'); fs.writeFileSync('${authJsonPath}', JSON.stringify(${JSON.stringify(auth)}, null, 2));"`;
113
- console.log("1Password: auth.json updated to use environment variables");
180
+ debugLog("auth.json updated to use environment variables");
114
181
  }
115
182
  } catch (err) {
116
- console.error("1Password: Failed to update auth.json:", err);
183
+ debugLog(`Failed to update auth.json: ${err}`);
117
184
  }
118
185
  };
119
186
 
@@ -129,12 +196,12 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
129
196
  try {
130
197
  config = JSON.parse(content);
131
198
  } catch {
132
- console.log("1Password: opencode.json is not valid JSON, skipping");
199
+ debugLog("opencode.json is not valid JSON, skipping");
133
200
  return;
134
201
  }
135
202
 
136
203
  if (!config.mcp) {
137
- console.log("1Password: No MCP configuration found, skipping");
204
+ debugLog("No MCP configuration found, skipping");
138
205
  return;
139
206
  }
140
207
 
@@ -147,7 +214,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
147
214
  // Replace hardcoded value with env var reference
148
215
  serverConfig.environment[key] = `{env:${key}}`;
149
216
  modified = true;
150
- console.log(`1Password: Updated opencode.json - ${serverName}.${key} -> {env:${key}}`);
217
+ debugLog(`Updated opencode.json - ${serverName}.${key} -> {env:${key}}`);
151
218
  }
152
219
  }
153
220
  }
@@ -156,29 +223,46 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
156
223
  if (modified) {
157
224
  // Write updated content
158
225
  await $`node -e "const fs=require('fs'); fs.writeFileSync('${openCodeJsonPath}', JSON.stringify(${JSON.stringify(config)}, null, 2));"`;
159
- console.log("1Password: opencode.json MCP config updated to use environment variables");
226
+ debugLog("opencode.json MCP config updated to use environment variables");
160
227
  }
161
228
  } catch (err) {
162
- console.error("1Password: Failed to update opencode.json:", err);
229
+ debugLog(`Failed to update opencode.json: ${err}`);
163
230
  }
164
231
  };
165
232
 
166
233
  const authenticateProviders = async (providerEnvId: string): Promise<void> => {
167
234
  if (!opClient) return;
168
235
 
236
+ debugLog(`Reading provider secrets from environment ${providerEnvId}`);
169
237
  const secrets = await getSecretsFromEnvironment(providerEnvId);
238
+ debugLog(`Found ${Object.keys(secrets).length} provider secrets`);
170
239
 
171
- for (const [providerId, apiKey] of Object.entries(secrets)) {
240
+ for (const [variableName, apiKey] of Object.entries(secrets)) {
172
241
  if (!apiKey) continue;
173
242
 
243
+ const providerId = normalizeProviderId(variableName);
244
+ debugLog(`Processing ${variableName} -> ${providerId} (key length: ${apiKey.length})`);
245
+
246
+ // Set environment variables in multiple formats
247
+ const envVarNames = getAlternativeEnvVarNames(providerId);
248
+ for (const envVarName of envVarNames) {
249
+ process.env[envVarName] = apiKey;
250
+ debugLog(`Set process.env.${envVarName} = ${apiKey.substring(0, 8)}...`);
251
+ }
252
+
174
253
  try {
254
+ // Authenticate with OpenCode runtime
255
+ debugLog(`Calling client.auth.set for ${providerId}`);
175
256
  await client.auth.set({
176
257
  path: { id: providerId },
177
258
  body: { type: "api", key: apiKey },
178
259
  });
179
- console.log(`1Password: ${providerId} authenticated`);
260
+ debugLog(`✓ ${providerId} authenticated (from ${variableName})`);
180
261
  } catch (err) {
181
- console.error(`1Password: Failed to authenticate ${providerId}:`, err);
262
+ debugLog(`Failed to authenticate ${providerId}: ${err}`);
263
+ if (err instanceof Error) {
264
+ debugLog(`Error message: ${err.message}`);
265
+ }
182
266
  }
183
267
  }
184
268
  };
@@ -203,7 +287,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
203
287
  }
204
288
  }
205
289
  } catch (err) {
206
- console.error("1Password: Failed to read MCP config:", err);
290
+ debugLog(`Failed to read MCP config: ${err}`);
207
291
  }
208
292
 
209
293
  return vars;
@@ -229,26 +313,29 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
229
313
 
230
314
  return {
231
315
  async event({ event }: { event: { type: string } }) {
316
+ debugLog(`Received event: ${event.type}`);
232
317
  if (event.type === "server.connected") {
233
- console.log("1Password: Initializing...");
318
+ debugLog("Initializing...");
234
319
 
235
320
  opClient = await initClient();
236
321
  if (!opClient) {
237
- console.error("1Password: No client available");
322
+ debugLog("No client available");
238
323
  return;
239
324
  }
240
325
 
241
326
  const configEnvId = process.env.OP_CONFIG_ENV_ID;
242
327
  if (!configEnvId) {
243
- console.error("1Password: Missing OP_CONFIG_ENV_ID");
328
+ debugLog("Missing OP_CONFIG_ENV_ID");
244
329
  return;
245
330
  }
246
331
 
332
+ debugLog(`Reading config from environment ${configEnvId}`);
247
333
  const vars = await readEnvironmentVariables(configEnvId);
248
334
  const configEnvIds: Record<string, string> = {};
249
335
  for (const v of vars) {
250
336
  if (v.name.endsWith("_ENV_ID") && v.value) {
251
337
  configEnvIds[v.name] = v.value;
338
+ debugLog(`Found ${v.name} = ${v.value.substring(0, 10)}...`);
252
339
  }
253
340
  }
254
341
 
@@ -265,11 +352,11 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
265
352
  await updateOpenCodeJsonMCP(mcpEnvIdFromConfig);
266
353
  const toInject = await injectMCPSecrets(mcpEnvIdFromConfig);
267
354
  if (Object.keys(toInject).length > 0) {
268
- console.log(`1Password: Injected ${Object.keys(toInject).join(", ")} for MCP`);
355
+ debugLog(`Injected ${Object.keys(toInject).join(", ")} for MCP`);
269
356
  }
270
357
  }
271
358
 
272
- console.log("1Password: Initialization complete");
359
+ debugLog("Initialization complete");
273
360
  }
274
361
  },
275
362
 
@@ -280,6 +367,7 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
280
367
 
281
368
  for (const [varName, value] of Object.entries(toInject)) {
282
369
  output.env[varName] = value;
370
+ debugLog(`Injected ${varName} into shell environment`);
283
371
  }
284
372
  },
285
373
 
@@ -288,8 +376,8 @@ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
288
376
 
289
377
  const fileArgs = input.args as { filePath?: string } | undefined;
290
378
  if (fileArgs?.filePath && fileArgs.filePath.includes(".env")) {
291
- console.warn(
292
- "1Password: Warning - .env file access detected. Consider using 1Password for secrets management."
379
+ debugLog(
380
+ "Warning - .env file access detected. Consider using 1Password for secrets management."
293
381
  );
294
382
  }
295
383
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-1password-auth",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "1Password integration for OpenCode - authenticate providers and inject MCP secrets from 1Password environments",
5
5
  "type": "module",
6
6
  "main": "index.ts",
package/setup.ps1 CHANGED
@@ -67,13 +67,13 @@ function Remove-RegistryValue {
67
67
  }
68
68
 
69
69
  function Get-OpenCodeAuthJsonPath {
70
- $home = if ($env:USERPROFILE) { $env:USERPROFILE } else { $env:USERPROFILE }
71
- return "$home/.local/share/opencode/auth.json"
70
+ $homeDir = if ($env:USERPROFILE) { $env:USERPROFILE } else { $env:USERPROFILE }
71
+ return "$homeDir/.local/share/opencode/auth.json"
72
72
  }
73
73
 
74
74
  function Get-OpenCodeConfigJsonPath {
75
- $home = if ($env:USERPROFILE) { $env:USERPROFILE } else { $env:USERPROFILE }
76
- return "$home/.config/opencode/opencode.json"
75
+ $homeDir = if ($env:USERPROFILE) { $env:USERPROFILE } else { $env:USERPROFILE }
76
+ return "$homeDir/.config/opencode/opencode.json"
77
77
  }
78
78
 
79
79
  function Update-ConfigFiles {