opencode-1password-auth 1.0.0 → 1.0.3

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 (4) hide show
  1. package/index.ts +114 -3
  2. package/package.json +1 -1
  3. package/setup.ps1 +284 -69
  4. package/setup.sh +205 -3
package/index.ts CHANGED
@@ -9,10 +9,33 @@ interface OpenCodeConfig {
9
9
  mcp?: Record<string, MCPServerConfig>;
10
10
  }
11
11
 
12
- export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
12
+ interface ProviderAuth {
13
+ type: string;
14
+ key: string;
15
+ }
16
+
17
+ interface AuthJson {
18
+ [providerId: string]: ProviderAuth;
19
+ }
20
+
21
+ export const OnePasswordAuthPlugin: Plugin = async ({ client, $ }) => {
13
22
  let opClient: Awaited<ReturnType<typeof sdk.createClient>> | null = null;
14
23
  let mcpsEnvId: string | undefined;
15
24
 
25
+ const getHomeDir = (): string => {
26
+ return process.env.HOME || process.env.USERPROFILE || "";
27
+ };
28
+
29
+ const getAuthJsonPath = (): string => {
30
+ const home = getHomeDir();
31
+ return `${home}/.local/share/opencode/auth.json`;
32
+ };
33
+
34
+ const getOpenCodeJsonPath = (): string => {
35
+ const home = getHomeDir();
36
+ return `${home}/.config/opencode/opencode.json`;
37
+ };
38
+
16
39
  const initClient = async () => {
17
40
  const token = process.env.OP_SERVICE_ACCOUNT_TOKEN;
18
41
  if (!token) {
@@ -55,6 +78,91 @@ export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
55
78
  return secrets;
56
79
  };
57
80
 
81
+ const updateAuthJson = async (providerEnvId: string): Promise<void> => {
82
+ const authJsonPath = getAuthJsonPath();
83
+
84
+ try {
85
+ // Read current auth.json using cat
86
+ const catResult = await $`cat "${authJsonPath}"`;
87
+ const content = catResult.stdout;
88
+ let auth: AuthJson;
89
+
90
+ try {
91
+ auth = JSON.parse(content);
92
+ } catch {
93
+ console.log("1Password: auth.json is not valid JSON, skipping");
94
+ return;
95
+ }
96
+
97
+ let modified = false;
98
+
99
+ for (const [providerId, authConfig] of Object.entries(auth)) {
100
+ if (authConfig.key && !authConfig.key.startsWith("{env:")) {
101
+ // Replace hardcoded key with env var reference
102
+ authConfig.key = `{env:${providerId}}`;
103
+ modified = true;
104
+ console.log(`1Password: Updated auth.json - ${providerId} -> {env:${providerId}}`);
105
+ }
106
+ }
107
+
108
+ if (modified) {
109
+ // Write updated content using a temp file and mv
110
+ const newContent = JSON.stringify(auth, null, 2);
111
+ // Use node to write the file since $ heredocs can be tricky
112
+ 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");
114
+ }
115
+ } catch (err) {
116
+ console.error("1Password: Failed to update auth.json:", err);
117
+ }
118
+ };
119
+
120
+ const updateOpenCodeJsonMCP = async (mcpEnvId: string): Promise<void> => {
121
+ const openCodeJsonPath = getOpenCodeJsonPath();
122
+
123
+ try {
124
+ // Read current opencode.json using cat
125
+ const catResult = await $`cat "${openCodeJsonPath}"`;
126
+ const content = catResult.stdout;
127
+ let config: OpenCodeConfig;
128
+
129
+ try {
130
+ config = JSON.parse(content);
131
+ } catch {
132
+ console.log("1Password: opencode.json is not valid JSON, skipping");
133
+ return;
134
+ }
135
+
136
+ if (!config.mcp) {
137
+ console.log("1Password: No MCP configuration found, skipping");
138
+ return;
139
+ }
140
+
141
+ let modified = false;
142
+
143
+ for (const [serverName, serverConfig] of Object.entries(config.mcp)) {
144
+ if (serverConfig?.environment) {
145
+ for (const [key, value] of Object.entries(serverConfig.environment)) {
146
+ if (typeof value === "string" && !value.startsWith("{env:") && !value.startsWith("$")) {
147
+ // Replace hardcoded value with env var reference
148
+ serverConfig.environment[key] = `{env:${key}}`;
149
+ modified = true;
150
+ console.log(`1Password: Updated opencode.json - ${serverName}.${key} -> {env:${key}}`);
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ if (modified) {
157
+ // Write updated content
158
+ 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");
160
+ }
161
+ } catch (err) {
162
+ console.error("1Password: Failed to update opencode.json:", err);
163
+ }
164
+ };
165
+
58
166
  const authenticateProviders = async (providerEnvId: string): Promise<void> => {
59
167
  if (!opClient) return;
60
168
 
@@ -64,7 +172,7 @@ export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
64
172
  if (!apiKey) continue;
65
173
 
66
174
  try {
67
- await ctx.client.auth.set({
175
+ await client.auth.set({
68
176
  path: { id: providerId },
69
177
  body: { type: "api", key: apiKey },
70
178
  });
@@ -79,7 +187,7 @@ export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
79
187
  const vars = new Set<string>();
80
188
 
81
189
  try {
82
- const config = ctx.config as OpenCodeConfig;
190
+ const config = client.config as OpenCodeConfig;
83
191
  if (config?.mcp) {
84
192
  for (const [, serverConfig] of Object.entries(config.mcp)) {
85
193
  if (serverConfig?.environment) {
@@ -146,12 +254,15 @@ export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
146
254
 
147
255
  const providersEnvId = configEnvIds.OPENCODE_PROVIDERS_ENV_ID;
148
256
  if (providersEnvId) {
257
+ // First update the config files to use env var references
258
+ await updateAuthJson(providersEnvId);
149
259
  await authenticateProviders(providersEnvId);
150
260
  }
151
261
 
152
262
  const mcpEnvIdFromConfig = configEnvIds.OPENCODE_MCPS_ENV_ID;
153
263
  if (mcpEnvIdFromConfig) {
154
264
  mcpsEnvId = mcpEnvIdFromConfig;
265
+ await updateOpenCodeJsonMCP(mcpEnvIdFromConfig);
155
266
  const toInject = await injectMCPSecrets(mcpEnvIdFromConfig);
156
267
  if (Object.keys(toInject).length > 0) {
157
268
  console.log(`1Password: Injected ${Object.keys(toInject).join(", ")} for MCP`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-1password-auth",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
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
@@ -3,7 +3,8 @@
3
3
 
4
4
  param(
5
5
  [switch]$Uninstall,
6
- [switch]$Audit
6
+ [switch]$Audit,
7
+ [switch]$UpdateConfig
7
8
  )
8
9
 
9
10
  # Colors for output
@@ -65,31 +66,184 @@ function Remove-RegistryValue {
65
66
  }
66
67
  }
67
68
 
68
- function Test-1PasswordConnection {
69
- param([string]$Token)
69
+ function Get-OpenCodeAuthJsonPath {
70
+ $home = if ($env:USERPROFILE) { $env:USERPROFILE } else { $env:USERPROFILE }
71
+ return "$home/.local/share/opencode/auth.json"
72
+ }
73
+
74
+ function Get-OpenCodeConfigJsonPath {
75
+ $home = if ($env:USERPROFILE) { $env:USERPROFILE } else { $env:USERPROFILE }
76
+ return "$home/.config/opencode/opencode.json"
77
+ }
78
+
79
+ function Update-ConfigFiles {
80
+ param([string]$Token, [string]$ProvidersEnvId, [string]$McpsEnvId)
70
81
 
71
- try {
72
- # Create a temporary Node.js script to test the SDK
73
- $testScript = @"
74
- const sdk = require('@1password/sdk');
82
+ $nodeModulesPath = Get-OpenCodeNodeModulesPath
83
+ if (-not $nodeModulesPath) {
84
+ Write-Error "Could not find @1password/sdk in any node_modules directory"
85
+ return
86
+ }
87
+
88
+ $sdkPath = ($nodeModulesPath -replace '\\', '/') + "/@1password/sdk/dist/sdk.js"
89
+
90
+ # Get provider secrets from 1Password
91
+ Write-Info "Reading provider secrets from 1Password..."
92
+
93
+ $script = @"
94
+ const sdk = require('${sdkPath}');
75
95
 
76
- async function test() {
96
+ async function getSecrets() {
77
97
  const client = await sdk.createClient({
78
- auth: '$Token',
79
- integrationName: 'opencode-1password-setup-test',
98
+ auth: '${Token}',
99
+ integrationName: 'opencode-1password-setup',
80
100
  integrationVersion: '1.0.0'
81
101
  });
82
102
 
83
- console.log('SUCCESS');
103
+ const providers = {};
104
+ const { variables: providerVars } = await client.environments.getVariables('${ProvidersEnvId}');
105
+ for (const v of providerVars) {
106
+ if (v.value) providers[v.name] = v.value;
107
+ }
108
+
109
+ const mcps = {};
110
+ if ('${McpsEnvId}') {
111
+ const { variables: mcpVars } = await client.environments.getVariables('${McpsEnvId}');
112
+ for (const v of mcpVars) {
113
+ if (v.value) mcps[v.name] = v.value;
114
+ }
115
+ }
116
+
117
+ console.log(JSON.stringify({ providers, mcps }));
84
118
  }
85
119
 
86
- test().catch(err => {
87
- console.error('FAILED:', err.message);
88
- process.exit(1);
89
- });
120
+ getSecrets().catch(err => { console.error('FAILED:', err.message); process.exit(1); });
90
121
  "@
122
+
123
+ $tempScript = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.js'
124
+ $script | Out-File -FilePath $tempScript -Encoding UTF8 -NoNewline
125
+
126
+ $result = & node $tempScript 2>&1 | Out-String
127
+ Remove-Item $tempScript -ErrorAction SilentlyContinue
128
+
129
+ if ($result -match "FAILED:") {
130
+ Write-Error "Failed to read secrets from 1Password: $($result -replace 'FAILED:', '')"
131
+ return
132
+ }
133
+
134
+ $secrets = $result | ConvertFrom-Json
135
+
136
+ # Update auth.json
137
+ $authJsonPath = Get-OpenCodeAuthJsonPath
138
+ Write-Info "Updating auth.json..."
139
+
140
+ if (Test-Path $authJsonPath) {
141
+ $authContent = Get-Content $authJsonPath -Raw -Encoding UTF8
142
+ $auth = $authContent | ConvertFrom-Json
143
+
144
+ $modified = $false
145
+ foreach ($providerId in $auth.PSObject.Properties.Name) {
146
+ $authConfig = $auth.$providerId
147
+ if ($authConfig.key -and -not $authConfig.key.StartsWith("{env:")) {
148
+ $authConfig.key = "{env:$providerId}"
149
+ $modified = $true
150
+ Write-Success "Updated $providerId -> {env:$providerId}"
151
+ }
152
+ }
153
+
154
+ if ($modified) {
155
+ $auth | ConvertTo-Json -Depth 10 | Set-Content $authJsonPath -Encoding UTF8 -NoNewline
156
+ Write-Success "auth.json updated"
157
+ } else {
158
+ Write-Info "auth.json already uses environment variable references"
159
+ }
160
+ } else {
161
+ Write-Info "auth.json not found at $authJsonPath"
162
+ }
163
+
164
+ # Update opencode.json MCP config
165
+ $configJsonPath = Get-OpenCodeConfigJsonPath
166
+ Write-Info "Updating opencode.json MCP config..."
167
+
168
+ if (Test-Path $configJsonPath) {
169
+ $configContent = Get-Content $configJsonPath -Raw -Encoding UTF8
170
+ $config = $configContent | ConvertFrom-Json
171
+
172
+ $modified = $false
173
+ if ($config.mcp) {
174
+ foreach ($serverName in $config.mcp.PSObject.Properties.Name) {
175
+ $serverConfig = $config.mcp.$serverName
176
+ if ($serverConfig.environment) {
177
+ foreach ($key in $serverConfig.environment.PSObject.Properties.Name) {
178
+ $value = $serverConfig.environment.$key
179
+ if ($value -and -not $value.StartsWith("{env:") -and -not $value.StartsWith("$")) {
180
+ $serverConfig.environment.$key = "{env:$key}"
181
+ $modified = $true
182
+ Write-Success "Updated $serverName.$key -> {env:$key}"
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ if ($modified) {
190
+ $config | ConvertTo-Json -Depth 10 | Set-Content $configJsonPath -Encoding UTF8 -NoNewline
191
+ Write-Success "opencode.json updated"
192
+ } else {
193
+ Write-Info "opencode.json already uses environment variable references"
194
+ }
195
+ } else {
196
+ Write-Info "opencode.json not found at $configJsonPath"
197
+ }
198
+ }
199
+
200
+ function Get-OpenCodeNodeModulesPath {
201
+ $paths = @(
202
+ "$env:USERPROFILE\.cache\opencode\node_modules",
203
+ "$env:USERPROFILE\.config\opencode\node_modules",
204
+ "$env:APPDATA\opencode\node_modules"
205
+ )
206
+
207
+ foreach ($path in $paths) {
208
+ if (Test-Path "$path\@1password\sdk") {
209
+ return $path
210
+ }
211
+ }
212
+ return $null
213
+ }
214
+
215
+ function Test-1PasswordConnection {
216
+ param([string]$Token)
217
+
218
+ try {
219
+ $nodeModulesPath = Get-OpenCodeNodeModulesPath
220
+
221
+ if (-not $nodeModulesPath) {
222
+ Write-Error "Could not find @1password/sdk in any node_modules directory"
223
+ return $false
224
+ }
225
+
226
+ $sdkPath = ($nodeModulesPath -replace '\\', '/') + "/@1password/sdk/dist/sdk.js"
227
+
228
+ # Create a temporary Node.js script file
229
+ $tempScript = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.js'
230
+
231
+ $testScript = 'const sdk = require("' + $sdkPath + '");' + "`n" +
232
+ 'async function test() {' + "`n" +
233
+ ' const client = await sdk.createClient({' + "`n" +
234
+ ' auth: "' + $Token + '",' + "`n" +
235
+ ' integrationName: "opencode-1password-setup-test",' + "`n" +
236
+ ' integrationVersion: "1.0.0"' + "`n" +
237
+ ' });' + "`n" +
238
+ ' console.log("SUCCESS");' + "`n" +
239
+ '}' + "`n" +
240
+ 'test().catch(err => { console.error("FAILED:", err.message); process.exit(1); });'
241
+
242
+ $testScript | Out-File -FilePath $tempScript -Encoding UTF8 -NoNewline
243
+
244
+ $result = & node $tempScript 2>&1
245
+ Remove-Item $tempScript -ErrorAction SilentlyContinue
91
246
 
92
- $result = node -e $testScript 2>&1
93
247
  return $result -match "SUCCESS"
94
248
  } catch {
95
249
  return $false
@@ -100,36 +254,37 @@ function Get-1PasswordAudit {
100
254
  param([string]$Token, [string]$ConfigEnvId)
101
255
 
102
256
  try {
103
- $script = @"
104
- const sdk = require('@1password/sdk');
105
-
106
- async function audit() {
107
- const client = await sdk.createClient({
108
- auth: '$Token',
109
- integrationName: 'opencode-1password-setup-test',
110
- integrationVersion: '1.0.0'
111
- });
112
-
113
- // Read bootstrap environment
114
- const { variables: configVars } = await client.environments.getVariables('$ConfigEnvId');
115
-
116
- const envIds = {};
117
- for (const v of configVars) {
118
- if (v.name.endsWith('_ENV_ID') && v.value) {
119
- envIds[v.name] = v.value;
257
+ $nodeModulesPath = Get-OpenCodeNodeModulesPath
258
+
259
+ if (-not $nodeModulesPath) {
260
+ Write-Error "Could not find @1password/sdk in any node_modules directory"
261
+ return $null
120
262
  }
121
- }
122
-
123
- console.log(JSON.stringify(envIds));
124
- }
125
-
126
- audit().catch(err => {
127
- console.error('FAILED:', err.message);
128
- process.exit(1);
129
- });
130
- "@
131
263
 
132
- $jsonResult = node -e $script 2>&1 | Out-String
264
+ $sdkPath = ($nodeModulesPath -replace '\\', '/') + "/@1password/sdk/dist/sdk.js"
265
+
266
+ $script = 'const sdk = require("' + $sdkPath + '");' + "`n" +
267
+ 'async function audit() {' + "`n" +
268
+ ' const client = await sdk.createClient({' + "`n" +
269
+ ' auth: "' + $Token + '",' + "`n" +
270
+ ' integrationName: "opencode-1password-setup-test",' + "`n" +
271
+ ' integrationVersion: "1.0.0"' + "`n" +
272
+ ' });' + "`n" +
273
+ ' const { variables: configVars } = await client.environments.getVariables("' + $ConfigEnvId + '");' + "`n" +
274
+ ' const envIds = {};' + "`n" +
275
+ ' for (const v of configVars) {' + "`n" +
276
+ ' if (v.name.endsWith("_ENV_ID") && v.value) {' + "`n" +
277
+ ' envIds[v.name] = v.value;' + "`n" +
278
+ ' }' + "`n" +
279
+ ' }' + "`n" +
280
+ ' console.log(JSON.stringify(envIds));' + "`n" +
281
+ '}' + "`n" +
282
+ 'audit().catch(err => { console.error("FAILED:", err.message); process.exit(1); });'
283
+
284
+ $tempScript = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.js'
285
+ $script | Out-File -FilePath $tempScript -Encoding UTF8 -NoNewline
286
+ $jsonResult = & node $tempScript 2>&1 | Out-String
287
+ Remove-Item $tempScript -ErrorAction SilentlyContinue
133
288
  return $jsonResult | ConvertFrom-Json
134
289
  } catch {
135
290
  return $null
@@ -139,6 +294,8 @@ audit().catch(err => {
139
294
  function Show-AuditReport {
140
295
  param([string]$Token, [hashtable]$EnvIds)
141
296
 
297
+ $nodeModulesPath = Get-OpenCodeNodeModulesPath
298
+
142
299
  Write-Host ""
143
300
  Write-Host "========================================" -ForegroundColor Cyan
144
301
  Write-Host " Configuration Audit Report" -ForegroundColor Cyan
@@ -163,30 +320,27 @@ function Show-AuditReport {
163
320
  Write-Host "--------------------------------"
164
321
 
165
322
  try {
166
- $script = @"
167
- const sdk = require('@1password/sdk');
168
-
169
- async function read() {
170
- const client = await sdk.createClient({
171
- auth: '$Token',
172
- integrationName: 'opencode-1password-setup-test',
173
- integrationVersion: '1.0.0'
174
- });
175
-
176
- const { variables } = await client.environments.getVariables('$envId');
177
-
178
- for (const v of variables) {
179
- const masked = v.value ? v.value.substring(0, 8) + '••••••••' : '(empty)';
180
- console.log(v.name + '=' + masked);
181
- }
182
- }
183
-
184
- read().catch(err => {
185
- console.error('Error:', err.message);
186
- });
187
- "@
323
+ # Use forward slashes to avoid JS escape sequence issues
324
+ $sdkPath = ($nodeModulesPath -replace '\\', '/') + "/@1password/sdk/dist/sdk.js"
325
+ $script = 'const sdk = require("' + $sdkPath + '");' + "`n" +
326
+ 'async function read() {' + "`n" +
327
+ ' const client = await sdk.createClient({' + "`n" +
328
+ ' auth: "' + $Token + '",' + "`n" +
329
+ ' integrationName: "opencode-1password-setup-test",' + "`n" +
330
+ ' integrationVersion: "1.0.0"' + "`n" +
331
+ ' });' + "`n" +
332
+ ' const { variables } = await client.environments.getVariables("' + $envId + '");' + "`n" +
333
+ ' for (const v of variables) {' + "`n" +
334
+ ' const masked = v.value ? v.value.substring(0, 8) + "••••••••" : "(empty)";' + "`n" +
335
+ ' console.log(v.name + "=" + masked);' + "`n" +
336
+ ' }' + "`n" +
337
+ '}' + "`n" +
338
+ 'read().catch(err => { console.error("Error:", err.message); });'
188
339
 
189
- $result = node -e $script 2>&1
340
+ $tempScript = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.js'
341
+ $script | Out-File -FilePath $tempScript -Encoding UTF8 -NoNewline
342
+ $result = & node $tempScript 2>&1
343
+ Remove-Item $tempScript -ErrorAction SilentlyContinue
190
344
  foreach ($line in $result) {
191
345
  if ($line -and $line -notmatch "^(Error|Error:)") {
192
346
  Write-Host " $line" -ForegroundColor Gray
@@ -298,6 +452,66 @@ if ($Audit) {
298
452
  exit 0
299
453
  }
300
454
 
455
+ if ($UpdateConfig) {
456
+ # Update Config mode
457
+ Write-Host "Update Config Mode" -ForegroundColor Yellow
458
+ Write-Host "------------------" -ForegroundColor Yellow
459
+ Write-Host ""
460
+
461
+ $token = Get-RegistryValue -Name "OP_SERVICE_ACCOUNT_TOKEN" -Scope "Machine"
462
+ $configId = Get-RegistryValue -Name "OP_CONFIG_ENV_ID" -Scope "Machine"
463
+
464
+ if (-not $token) {
465
+ $token = Get-RegistryValue -Name "OP_SERVICE_ACCOUNT_TOKEN" -Scope "User"
466
+ $configId = Get-RegistryValue -Name "OP_CONFIG_ENV_ID" -Scope "User"
467
+ }
468
+
469
+ if (-not $token -or -not $configId) {
470
+ Write-Error "Environment variables not set. Run setup first."
471
+ exit 1
472
+ }
473
+
474
+ Write-Info "Testing 1Password connection..."
475
+
476
+ if (Test-1PasswordConnection -Token $token) {
477
+ Write-Success "1Password connection successful!"
478
+ } else {
479
+ Write-Error "Failed to connect to 1Password. Check your service account token."
480
+ exit 1
481
+ }
482
+
483
+ Write-Info "Reading configuration from 1Password..."
484
+
485
+ $envIds = Get-1PasswordAudit -Token $token -ConfigEnvId $configId
486
+
487
+ if ($envIds) {
488
+ $providersEnvId = $null
489
+ $mcpsEnvId = $null
490
+
491
+ foreach ($prop in $envIds.PSObject.Properties) {
492
+ if ($prop.Name -eq "OPENCODE_PROVIDERS_ENV_ID") {
493
+ $providersEnvId = $prop.Value
494
+ } elseif ($prop.Name -eq "OPENCODE_MCPS_ENV_ID") {
495
+ $mcpsEnvId = $prop.Value
496
+ }
497
+ }
498
+
499
+ if ($providersEnvId) {
500
+ Write-Info "Updating config files to use environment variables..."
501
+ Update-ConfigFiles -Token $token -ProvidersEnvId $providersEnvId -McpsEnvId $mcpsEnvId
502
+ Write-Success "Config update complete!"
503
+ } else {
504
+ Write-Error "Could not find OPENCODE_PROVIDERS_ENV_ID in bootstrap environment"
505
+ exit 1
506
+ }
507
+ } else {
508
+ Write-Error "Failed to read bootstrap environment."
509
+ exit 1
510
+ }
511
+
512
+ exit 0
513
+ }
514
+
301
515
  # Setup mode (default)
302
516
  Write-Host "Setup Mode" -ForegroundColor Yellow
303
517
  Write-Host "----------" -ForegroundColor Yellow
@@ -416,6 +630,7 @@ Write-Success "Setup complete!"
416
630
  Write-Info "Restart OpenCode to activate the plugin."
417
631
  Write-Host ""
418
632
  Write-Host "Usage:" -ForegroundColor White
419
- Write-Host " ./setup.ps1 -Audit Show current configuration"
420
- Write-Host " ./setup.ps1 -Uninstall Remove environment variables"
633
+ Write-Host " ./setup.ps1 -Audit Show current configuration"
634
+ Write-Host " ./setup.ps1 -UpdateConfig Update config files to use {env:VAR} references"
635
+ Write-Host " ./setup.ps1 -Uninstall Remove environment variables"
421
636
  Write-Host ""
package/setup.sh CHANGED
@@ -221,9 +221,161 @@ EOFINNER
221
221
  done
222
222
  }
223
223
 
224
+ update_config_files() {
225
+ local token="$1"
226
+ local providers_env_id="$2"
227
+ local mcps_env_id="$3"
228
+
229
+ log_info "Reading secrets from 1Password..."
230
+
231
+ # Get provider and MCP secrets
232
+ cat > /tmp/update_config_$$.js << 'EOF'
233
+ const sdk = require('@1password/sdk');
234
+
235
+ async function getSecrets() {
236
+ const token = process.argv[1];
237
+ const providersEnvId = process.argv[2];
238
+ const mcpsEnvId = process.argv[3];
239
+
240
+ const client = await sdk.createClient({
241
+ auth: token,
242
+ integrationName: 'opencode-1password-setup',
243
+ integrationVersion: '1.0.0'
244
+ });
245
+
246
+ const providers = {};
247
+ const { variables: providerVars } = await client.environments.getVariables(providersEnvId);
248
+ for (const v of providerVars) {
249
+ if (v.value) providers[v.name] = v.value;
250
+ }
251
+
252
+ const mcps = {};
253
+ if (mcpsEnvId) {
254
+ const { variables: mcpVars } = await client.environments.getVariables(mcpsEnvId);
255
+ for (const v of mcpVars) {
256
+ if (v.value) mcps[v.name] = v.value;
257
+ }
258
+ }
259
+
260
+ console.log(JSON.stringify({ providers, mcps }));
261
+ }
262
+
263
+ getSecrets().catch(err => {
264
+ console.error('FAILED:', err.message);
265
+ process.exit(1);
266
+ });
267
+ EOF
268
+
269
+ result=$(node /tmp/update_config_$$.js "$token" "$providers_env_id" "$mcps_env_id" 2>&1)
270
+ rm -f /tmp/update_config_$$.js
271
+
272
+ if [[ "$result" == "FAILED:"* ]]; then
273
+ log_error "Failed to read secrets from 1Password: $result"
274
+ return 1
275
+ fi
276
+
277
+ # Get home directory
278
+ home="${HOME:-}"
279
+ if [[ -z "$home" ]]; then
280
+ home=$(eval echo ~)
281
+ fi
282
+
283
+ # Update auth.json
284
+ auth_json_path="$home/.local/share/opencode/auth.json"
285
+ log_info "Updating auth.json..."
286
+
287
+ if [[ -f "$auth_json_path" ]]; then
288
+ # Read auth.json and update
289
+ auth_content=$(cat "$auth_json_path")
290
+ # Use node to parse and update JSON
291
+ cat > /tmp/update_auth_$$.js << 'EOFJ'
292
+ const fs = require('fs');
293
+ const authPath = process.argv[1];
294
+ const auth = JSON.parse(fs.readFileSync(authPath, 'utf8'));
295
+
296
+ let modified = false;
297
+ for (const [providerId, authConfig] of Object.entries(auth)) {
298
+ if (authConfig.key && !authConfig.key.startsWith('{env:')) {
299
+ authConfig.key = '{env:' + providerId + '}';
300
+ modified = true;
301
+ console.log('Updated ' + providerId + ' -> {env:' + providerId + '}');
302
+ }
303
+ }
304
+
305
+ if (modified) {
306
+ fs.writeFileSync(authPath, JSON.stringify(auth, null, 2));
307
+ console.log('auth.json updated');
308
+ } else {
309
+ console.log('auth.json already uses environment variable references');
310
+ }
311
+ EOFJ
312
+
313
+ update_result=$(node /tmp/update_auth_$$.js "$auth_json_path" 2>&1)
314
+ rm -f /tmp/update_auth_$$.js
315
+ echo "$update_result" | while read line; do
316
+ if [[ "$line" == Updated* ]] || [[ "$line" == *updated* ]]; then
317
+ log_success "$(echo "$line" | sed 's/Updated/Updated/')"
318
+ else
319
+ log_info "$line"
320
+ fi
321
+ done
322
+ else
323
+ log_info "auth.json not found at $auth_json_path"
324
+ fi
325
+
326
+ # Update opencode.json MCP config
327
+ config_json_path="$home/.config/opencode/opencode.json"
328
+ log_info "Updating opencode.json MCP config..."
329
+
330
+ if [[ -f "$config_json_path" ]]; then
331
+ cat > /tmp/update_mcp_$$.js << 'EOFMCP'
332
+ const fs = require('fs');
333
+ const configPath = process.argv[1];
334
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
335
+
336
+ let modified = false;
337
+ if (config.mcp) {
338
+ for (const [serverName, serverConfig] of Object.entries(config.mcp)) {
339
+ if (serverConfig && serverConfig.environment) {
340
+ for (const [key, value] of Object.entries(serverConfig.environment)) {
341
+ if (value && !value.startsWith('{env:') && !value.startsWith('$')) {
342
+ serverConfig.environment[key] = '{env:' + key + '}';
343
+ modified = true;
344
+ console.log('Updated ' + serverName + '.' + key + ' -> {env:' + key + '}');
345
+ }
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ if (modified) {
352
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
353
+ console.log('opencode.json updated');
354
+ } else {
355
+ console.log('opencode.json already uses environment variable references');
356
+ }
357
+ EOFMCP
358
+
359
+ update_result=$(node /tmp/update_mcp_$$.js "$config_json_path" 2>&1)
360
+ rm -f /tmp/update_mcp_$$.js
361
+ echo "$update_result" | while read line; do
362
+ if [[ "$line" == Updated* ]] || [[ "$line" == *updated* ]]; then
363
+ log_success "$(echo "$line" | sed 's/Updated/Updated/')"
364
+ else
365
+ log_info "$line"
366
+ fi
367
+ done
368
+ else
369
+ log_info "opencode.json not found at $config_json_path"
370
+ fi
371
+
372
+ log_success "Config update complete!"
373
+ }
374
+
224
375
  # Parse arguments
225
376
  UNINSTALL=false
226
377
  AUDIT=false
378
+ UPDATE_CONFIG=false
227
379
 
228
380
  while [[ $# -gt 0 ]]; do
229
381
  case $1 in
@@ -235,8 +387,12 @@ while [[ $# -gt 0 ]]; do
235
387
  AUDIT=true
236
388
  shift
237
389
  ;;
390
+ -c|--update-config)
391
+ UPDATE_CONFIG=true
392
+ shift
393
+ ;;
238
394
  *)
239
- echo "Usage: $0 [--uninstall|--audit]"
395
+ echo "Usage: $0 [--uninstall|--audit|--update-config]"
240
396
  exit 1
241
397
  ;;
242
398
  esac
@@ -325,6 +481,51 @@ if [[ "$AUDIT" == true ]]; then
325
481
  exit 0
326
482
  fi
327
483
 
484
+ if [[ "$UPDATE_CONFIG" == true ]]; then
485
+ echo -e "${YELLOW}Update Config Mode${NC}"
486
+ echo -e "${YELLOW}------------------${NC}"
487
+ echo ""
488
+
489
+ get_existing_values
490
+
491
+ if [[ -z "$OP_SERVICE_ACCOUNT_TOKEN" || -z "$OP_CONFIG_ENV_ID" ]]; then
492
+ log_error "Environment variables not set. Run setup first."
493
+ exit 1
494
+ fi
495
+
496
+ log_info "Testing 1Password connection..."
497
+
498
+ if test_connection "$OP_SERVICE_ACCOUNT_TOKEN"; then
499
+ log_success "1Password connection successful!"
500
+ else
501
+ log_error "Failed to connect to 1Password. Check your service account token."
502
+ exit 1
503
+ fi
504
+
505
+ log_info "Reading configuration from 1Password..."
506
+
507
+ env_ids_json=$(get_audit_data "$OP_SERVICE_ACCOUNT_TOKEN" "$OP_CONFIG_ENV_ID")
508
+
509
+ if [[ -n "$env_ids_json" && "$env_ids_json" != "FAILED:"* ]]; then
510
+ # Parse JSON to extract env IDs
511
+ providers_env_id=$(echo "$env_ids_json" | sed -n 's/.*"OPENCODE_PROVIDERS_ENV_ID":"\([^"]*\)".*/\1/p')
512
+ mcps_env_id=$(echo "$env_ids_json" | sed -n 's/.*"OPENCODE_MCPS_ENV_ID":"\([^"]*\)".*/\1/p')
513
+
514
+ if [[ -n "$providers_env_id" ]]; then
515
+ log_info "Updating config files to use environment variables..."
516
+ update_config_files "$OP_SERVICE_ACCOUNT_TOKEN" "$providers_env_id" "$mcps_env_id"
517
+ else
518
+ log_error "Could not find OPENCODE_PROVIDERS_ENV_ID in bootstrap environment"
519
+ exit 1
520
+ fi
521
+ else
522
+ log_error "Failed to read bootstrap environment."
523
+ exit 1
524
+ fi
525
+
526
+ exit 0
527
+ fi
528
+
328
529
  # Setup mode (default)
329
530
  echo -e "${YELLOW}Setup Mode${NC}"
330
531
  echo -e "${YELLOW}----------${NC}"
@@ -439,6 +640,7 @@ log_success "Setup complete!"
439
640
  log_info "Restart OpenCode to activate the plugin."
440
641
  echo ""
441
642
  echo "Usage:"
442
- echo " ./setup.sh --audit Show current configuration"
443
- echo " ./setup.sh --uninstall Remove environment variables"
643
+ echo " ./setup.sh --audit Show current configuration"
644
+ echo " ./setup.sh --update-config Update config files to use {env:VAR} references"
645
+ echo " ./setup.sh --uninstall Remove environment variables"
444
646
  echo ""