opencode-1password-auth 1.0.6 → 1.0.7

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 (3) hide show
  1. package/README.md +179 -16
  2. package/package.json +1 -1
  3. package/index-v2.ts +0 -387
package/README.md CHANGED
@@ -2,11 +2,49 @@
2
2
 
3
3
  Authenticate LLM providers and inject MCP server secrets from 1Password environments.
4
4
 
5
+ ## ⚠️ Important Note
6
+
7
+ **OpenCode does NOT resolve `{env:VAR}` syntax in `auth.json`**. This plugin works around this limitation by:
8
+ 1. Setting API keys via `client.auth.set()` at runtime
9
+ 2. Setting environment variables in `process.env` for MCP servers
10
+ 3. Using `{env:VAR}` references in config files for MCP servers only
11
+
12
+ **Version 1.0.6+ includes critical timing fixes** - the plugin now initializes immediately when loaded, not waiting for the `server.connected` event.
13
+
14
+ ## 🚀 Quick Start
15
+
16
+ 1. **Run setup script**:
17
+ ```powershell
18
+ # Windows
19
+ .\setup.ps1
20
+
21
+ # macOS/Linux
22
+ ./setup.sh
23
+ ```
24
+
25
+ 2. **Update config files**:
26
+ ```powershell
27
+ .\setup.ps1 -UpdateConfig
28
+ ```
29
+
30
+ 3. **Add to OpenCode config** (`~/.config/opencode/opencode.json`):
31
+ ```json
32
+ {
33
+ "plugin": ["opencode-1password-auth"]
34
+ }
35
+ ```
36
+
37
+ 4. **Restart OpenCode** completely
38
+
39
+ 5. **Check debug logs** at `~/.opencode-1password-debug/debug.log`
40
+
5
41
  ## Features
6
42
 
7
- - **Provider Authentication**: Automatically authenticate OpenCode providers (MiniMax, DeepSeek, OpenCode, etc.) from 1Password
43
+ - **Provider Authentication**: Automatically authenticate OpenCode providers (MiniMax, DeepSeek, OpenCode, etc.) from 1Password at runtime
8
44
  - **MCP Secret Injection**: Inject secrets into MCP server environments from 1Password
9
45
  - **.env Access Warning**: Warns when .env files are accessed
46
+ - **Config File Management**: Automatically update config files to use `{env:VAR}` references (for MCP servers)
47
+ - **Debug Logging**: Detailed logs at `~/.opencode-1password-debug/debug.log`
10
48
 
11
49
  ## Prerequisites
12
50
 
@@ -39,6 +77,24 @@ Create these environments in 1Password:
39
77
  **MCPS Environment** (holds MCP secrets):
40
78
  - Secret names matching your MCP server config (e.g., `MINIMAX_API_KEY`, `MINIMAX_API_HOST`)
41
79
 
80
+ ### Variable Naming Guide
81
+
82
+ The plugin normalizes variable names to match auth.json provider IDs:
83
+
84
+ | 1Password Variable | Normalized Provider ID | auth.json Key |
85
+ |-------------------|------------------------|---------------|
86
+ | `MINIMAX_CODING_PLAN` | `minimax-coding-plan` | `minimax-coding-plan` |
87
+ | `OPENCODE` | `opencode` | `opencode` |
88
+ | `DEEPSEEK` | `deepseek` | `deepseek` |
89
+ | `minimax_coding_plan` | `minimax-coding-plan` | `minimax-coding-plan` |
90
+ | `open_code` | `open-code` | `open-code` |
91
+
92
+ **Rules:**
93
+ 1. Variable names are **case-insensitive**
94
+ 2. Underscores (`_`) are converted to hyphens (`-`)
95
+ 3. The plugin sets environment variables in multiple formats for compatibility
96
+ 4. MCP variable names should match exactly what your MCP server expects
97
+
42
98
  ## Setup
43
99
 
44
100
  ### 1. Run Setup Script
@@ -63,9 +119,13 @@ The script will:
63
119
  - Show a full audit of your configuration
64
120
 
65
121
  **Script Options:**
66
- - `.\setup.ps1 -Audit` - View current configuration
122
+ - `.\setup.ps1` - Interactive setup (default)
123
+ - `.\setup.ps1 -Audit` - View current configuration without making changes
124
+ - `.\setup.ps1 -UpdateConfig` - **Critical**: Update `auth.json` and `opencode.json` to use `{env:VAR}` references
67
125
  - `.\setup.ps1 -Uninstall` - Remove environment variables
68
126
 
127
+ > **⚠️ IMPORTANT**: You MUST run `.\setup.ps1 -UpdateConfig` after setting up environment variables to update your config files.
128
+
69
129
  ### 2. Install Plugin
70
130
 
71
131
  Add to your `opencode.json`:
@@ -113,7 +173,7 @@ opencode-mcps
113
173
 
114
174
  ## MCP Configuration
115
175
 
116
- In your `opencode.json`, use `{env:VARIABLE_NAME}` syntax to reference injected secrets:
176
+ In your `opencode.json`, use `{env:VARIABLE_NAME}` syntax to reference injected secrets. **This works for MCP servers** because the plugin injects environment variables via the `shell.env` hook:
117
177
 
118
178
  ```json
119
179
  {
@@ -130,12 +190,41 @@ In your `opencode.json`, use `{env:VARIABLE_NAME}` syntax to reference injected
130
190
  }
131
191
  ```
132
192
 
133
- ## How It Works
193
+ > **Note**: While `{env:VAR}` syntax works for MCP servers in `opencode.json`, it **does NOT work** for provider authentication in `auth.json`. For providers, the plugin uses `client.auth.set()` instead.
194
+
195
+ ## How It Works (v1.0.6+)
196
+
197
+ 1. **Immediate Initialization**: When OpenCode loads the plugin, it immediately:
198
+ - Creates 1Password SDK client using `OP_SERVICE_ACCOUNT_TOKEN`
199
+ - Reads bootstrap environment ID from `OP_CONFIG_ENV_ID`
200
+ - Retrieves provider and MCP environment IDs
201
+ 2. **Provider Authentication**:
202
+ - Reads API keys from 1Password providers environment
203
+ - Sets `process.env` variables with multiple naming formats (e.g., `opencode`, `OPENCODE`, `opencode`, `OPENCODE`)
204
+ - Calls `client.auth.set()` to authenticate each provider at runtime
205
+ 3. **MCP Secret Injection**:
206
+ - Reads secrets from 1Password MCP environment
207
+ - Injects them via the `shell.env` hook when MCP servers start
208
+ 4. **Config File Management**:
209
+ - Updates `auth.json` to use `{env:providerId}` references (though OpenCode doesn't resolve these)
210
+ - Updates `opencode.json` MCP config to use `{env:VAR}` references (which DO work for MCP)
211
+
212
+ ## Authentication Flow
213
+
214
+ ```mermaid
215
+ graph TD
216
+ A[OpenCode starts] --> B[Load 1Password Plugin]
217
+ B --> C[Immediate initialization]
218
+ C --> D{Read 1Password config}
219
+ D --> E[Set process.env variables]
220
+ D --> F[Call client.auth.set()]
221
+ F --> G[Provider authenticated at runtime]
222
+ E --> H[MCP servers get env vars]
223
+ G --> I[Chat requests work]
224
+ H --> I
225
+ ```
134
226
 
135
- 1. On `server.connected` event, the plugin initializes the 1Password SDK
136
- 2. Reads environment IDs from your config environment
137
- 3. Authenticates providers using `client.auth.set()`
138
- 4. Injects MCP secrets via the `shell.env` hook
227
+ > **Note**: `client.auth.set()` happens AFTER OpenCode reads `auth.json`, so the plugin must initialize early enough to intercept authentication attempts.
139
228
 
140
229
  ## Requirements
141
230
 
@@ -145,31 +234,105 @@ In your `opencode.json`, use `{env:VARIABLE_NAME}` syntax to reference injected
145
234
 
146
235
  ## Troubleshooting
147
236
 
148
- **Plugin not loading?**
237
+ ### Plugin not loading?
149
238
  - Ensure environment variables are set system-wide (not just in terminal)
150
239
  - Restart OpenCode completely after setting environment variables
240
+ - Check OpenCode console for plugin loading errors
151
241
 
152
- **Can't read 1Password environments?**
153
- - Verify service account has access to the vaults containing your environments
242
+ ### Can't read 1Password environments?
243
+ - Verify service account has access to the vaults containing your environments
154
244
  - Check that environment IDs in config are correct
245
+ - Run `.\setup.ps1 -Audit` to verify connection and configuration
246
+
247
+ ### Authentication failing with "Your api key: ****eek} is invalid"?
248
+ This means OpenCode is reading the literal `{env:deepseek}` string from `auth.json`. The plugin needs to authenticate providers earlier.
249
+
250
+ **Solutions:**
251
+ 1. **Update to v1.0.6+** - Includes critical timing fixes
252
+ 2. **Check debug logs** - Look at `~/.opencode-1password-debug/debug.log`
253
+ 3. **Verify plugin is in opencode.json** - Ensure `"opencode-1password-auth"` is in the plugin array
254
+ 4. **Restart OpenCode** - Completely quit and restart to reload the plugin
255
+
256
+ ### Debug Logging
257
+ The plugin writes detailed logs to `~/.opencode-1password-debug/debug.log`. Check this file for:
258
+ - Plugin initialization status
259
+ - 1Password connection attempts
260
+ - `client.auth.set()` calls and results
261
+ - Environment variable injections
262
+
263
+ ### Common Issues
264
+ - **Timing**: If the plugin initializes too late, OpenCode already attempted authentication
265
+ - **Environment Variables**: Must be set system-wide, not just in current terminal
266
+ - **1Password Permissions**: Service account needs read access to environments
267
+ - **Variable Names**: Provider names in 1Password should match auth.json provider IDs (case-insensitive, underscores converted to hyphens)
268
+
269
+ ## Current Status (v1.0.6)
270
+
271
+ ### ✅ Implemented
272
+ - **Provider Authentication**: `client.auth.set()` at runtime
273
+ - **MCP Secret Injection**: Via `shell.env` hook
274
+ - **Config File Management**: Auto-update `auth.json` and `opencode.json` with `-UpdateConfig` flag
275
+ - **Debug Logging**: File-based logging at `~/.opencode-1password-debug/debug.log`
276
+ - **Variable Normalization**: Multiple naming formats supported
277
+ - **Immediate Initialization**: Plugin loads early in OpenCode lifecycle
278
+
279
+ ### 🔄 Working Around OpenCode Limitations
280
+ The plugin works around OpenCode's limitation of not resolving `{env:VAR}` in `auth.json` by:
281
+ 1. Calling `client.auth.set()` to authenticate providers at runtime
282
+ 2. Setting `process.env` variables for MCP servers
283
+ 3. Using timing fixes in v1.0.6+ to initialize before authentication attempts
155
284
 
156
285
  ## Roadmap
157
286
 
158
- The following features are planned for future releases:
159
-
160
287
  ### Phase 2 - Enhanced Scripting
161
288
  - [ ] **Automated service account creation** - Guide users through creating service accounts with correct permissions via 1Password API
162
289
  - [ ] **Bootstrap environment management via scripts** - Add, list, remove environment references from the bootstrap environment through CLI
290
+ - [ ] **Cross-platform setup improvements** - Better support for macOS/Linux setup scripts
163
291
 
164
- ### Phase 3 - Advanced Plugin Features
292
+ ### Phase 3 - Advanced Plugin Features
165
293
  - [ ] **Dynamic environment detection** - Plugin automatically detects new environments added to bootstrap and integrates them
166
- - [ ] **Config file injection** - Plugin replaces hardcoded API keys in OpenCode config files with `{env:VAR}` references
294
+ - [x] **Config file injection** - Implemented (uses `-UpdateConfig` flag)
167
295
  - [ ] **Custom environment support** - Support for additional environments beyond the standard opencode-providers, opencode-plugins, opencode-mcps pattern
168
296
  - [ ] **Write support** - Ability to store secrets back to 1Password environments
297
+ - [ ] **Auth.json workaround** - Investigate alternative approaches if `client.auth.set()` timing issues persist
169
298
 
170
- ### Phase 4 - polish
299
+ ### Phase 4 - Polish & Reliability
171
300
  - [ ] **Uninstall script** - Clean removal of environment variables and plugin configuration
172
301
  - [ ] **Configuration validation** - Validate OpenCode config files reference correct environment variables
302
+ - [ ] **Health checks** - Periodic verification that 1Password connection and authentication are working
303
+ - [ ] **Better error messages** - User-friendly error reporting for common issues
304
+
305
+ ## Deployment
306
+
307
+ ### Publishing New Versions
308
+
309
+ 1. **Update version** in `package.json`
310
+ 2. **Commit changes** with descriptive message:
311
+ ```bash
312
+ git add .
313
+ git commit -m "Brief description of changes"
314
+ ```
315
+ 3. **Tag the release** (optional but recommended):
316
+ ```bash
317
+ git tag v1.0.6
318
+ git push origin v1.0.6
319
+ ```
320
+ 4. **Publish to npm**:
321
+ ```bash
322
+ npm publish
323
+ ```
324
+ 5. **Update GitHub**:
325
+ ```bash
326
+ git push origin master
327
+ ```
328
+
329
+ ### Version History
330
+ - **v1.0.6**: Timing fixes - immediate initialization, config hook, enhanced logging
331
+ - **v1.0.5**: Debug logging and error handling improvements
332
+ - **v1.0.4**: Variable normalization and multiple env var formats
333
+ - **v1.0.3**: Setup scripts with config file modification
334
+ - **v1.0.2**: MCP secret injection and .env warnings
335
+ - **v1.0.1**: Initial release with basic provider authentication
173
336
 
174
337
  ## License
175
338
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-1password-auth",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
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/index-v2.ts DELETED
@@ -1,387 +0,0 @@
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;