opencode-1password-auth 1.0.0 → 1.0.1

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/index.ts +86 -0
  2. package/package.json +1 -1
  3. package/setup.ps1 +93 -71
package/index.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
2
  import * as sdk from "@1password/sdk";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import * as os from "os";
3
6
 
4
7
  interface MCPServerConfig {
5
8
  environment?: Record<string, string>;
@@ -9,6 +12,18 @@ interface OpenCodeConfig {
9
12
  mcp?: Record<string, MCPServerConfig>;
10
13
  }
11
14
 
15
+ interface ProviderAuth {
16
+ type: string;
17
+ key: string;
18
+ }
19
+
20
+ interface AuthJson {
21
+ [providerId: string]: ProviderAuth;
22
+ }
23
+
24
+ const AUTH_JSON_PATH = path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
25
+ const OPENCODE_JSON_PATH = path.join(os.homedir(), ".config", "opencode", "opencode.json");
26
+
12
27
  export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
13
28
  let opClient: Awaited<ReturnType<typeof sdk.createClient>> | null = null;
14
29
  let mcpsEnvId: string | undefined;
@@ -55,6 +70,74 @@ export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
55
70
  return secrets;
56
71
  };
57
72
 
73
+ const updateAuthJson = async (providerEnvId: string): Promise<void> => {
74
+ if (!fs.existsSync(AUTH_JSON_PATH)) {
75
+ console.log("1Password: auth.json not found, skipping");
76
+ return;
77
+ }
78
+
79
+ try {
80
+ const content = fs.readFileSync(AUTH_JSON_PATH, "utf-8");
81
+ const auth: AuthJson = JSON.parse(content);
82
+ let modified = false;
83
+
84
+ for (const [providerId, authConfig] of Object.entries(auth)) {
85
+ if (authConfig.key && !authConfig.key.startsWith("{env:")) {
86
+ // Replace hardcoded key with env var reference
87
+ authConfig.key = `{env:${providerId}}`;
88
+ modified = true;
89
+ console.log(`1Password: Updated auth.json - ${providerId} -> {env:${providerId}}`);
90
+ }
91
+ }
92
+
93
+ if (modified) {
94
+ fs.writeFileSync(AUTH_JSON_PATH, JSON.stringify(auth, null, 2));
95
+ console.log("1Password: auth.json updated to use environment variables");
96
+ }
97
+ } catch (err) {
98
+ console.error("1Password: Failed to update auth.json:", err);
99
+ }
100
+ };
101
+
102
+ const updateOpenCodeJsonMCP = async (mcpEnvId: string): Promise<void> => {
103
+ if (!fs.existsSync(OPENCODE_JSON_PATH)) {
104
+ console.log("1Password: opencode.json not found, skipping");
105
+ return;
106
+ }
107
+
108
+ try {
109
+ const content = fs.readFileSync(OPENCODE_JSON_PATH, "utf-8");
110
+ const config = JSON.parse(content);
111
+
112
+ if (!config.mcp) {
113
+ console.log("1Password: No MCP configuration found, skipping");
114
+ return;
115
+ }
116
+
117
+ let modified = false;
118
+
119
+ for (const [serverName, serverConfig] of Object.entries(config.mcp)) {
120
+ if (serverConfig?.environment) {
121
+ for (const [key, value] of Object.entries(serverConfig.environment)) {
122
+ if (typeof value === "string" && !value.startsWith("{env:") && !value.startsWith("$")) {
123
+ // Replace hardcoded value with env var reference
124
+ serverConfig.environment[key] = `{env:${key}}`;
125
+ modified = true;
126
+ console.log(`1Password: Updated opencode.json - ${serverName}.${key} -> {env:${key}}`);
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ if (modified) {
133
+ fs.writeFileSync(OPENCODE_JSON_PATH, JSON.stringify(config, null, 2));
134
+ console.log("1Password: opencode.json MCP config updated to use environment variables");
135
+ }
136
+ } catch (err) {
137
+ console.error("1Password: Failed to update opencode.json:", err);
138
+ }
139
+ };
140
+
58
141
  const authenticateProviders = async (providerEnvId: string): Promise<void> => {
59
142
  if (!opClient) return;
60
143
 
@@ -146,12 +229,15 @@ export const OnePasswordAuthPlugin: Plugin = async (ctx) => {
146
229
 
147
230
  const providersEnvId = configEnvIds.OPENCODE_PROVIDERS_ENV_ID;
148
231
  if (providersEnvId) {
232
+ // First update the config files to use env var references
233
+ await updateAuthJson(providersEnvId);
149
234
  await authenticateProviders(providersEnvId);
150
235
  }
151
236
 
152
237
  const mcpEnvIdFromConfig = configEnvIds.OPENCODE_MCPS_ENV_ID;
153
238
  if (mcpEnvIdFromConfig) {
154
239
  mcpsEnvId = mcpEnvIdFromConfig;
240
+ await updateOpenCodeJsonMCP(mcpEnvIdFromConfig);
155
241
  const toInject = await injectMCPSecrets(mcpEnvIdFromConfig);
156
242
  if (Object.keys(toInject).length > 0) {
157
243
  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.1",
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
@@ -65,31 +65,53 @@ function Remove-RegistryValue {
65
65
  }
66
66
  }
67
67
 
68
+ function Get-OpenCodeNodeModulesPath {
69
+ $paths = @(
70
+ "$env:USERPROFILE\.cache\opencode\node_modules",
71
+ "$env:USERPROFILE\.config\opencode\node_modules",
72
+ "$env:APPDATA\opencode\node_modules"
73
+ )
74
+
75
+ foreach ($path in $paths) {
76
+ if (Test-Path "$path\@1password\sdk") {
77
+ return $path
78
+ }
79
+ }
80
+ return $null
81
+ }
82
+
68
83
  function Test-1PasswordConnection {
69
84
  param([string]$Token)
70
85
 
71
86
  try {
72
- # Create a temporary Node.js script to test the SDK
73
- $testScript = @"
74
- const sdk = require('@1password/sdk');
75
-
76
- async function test() {
77
- const client = await sdk.createClient({
78
- auth: '$Token',
79
- integrationName: 'opencode-1password-setup-test',
80
- integrationVersion: '1.0.0'
81
- });
82
-
83
- console.log('SUCCESS');
84
- }
85
-
86
- test().catch(err => {
87
- console.error('FAILED:', err.message);
88
- process.exit(1);
89
- });
90
- "@
87
+ $nodeModulesPath = Get-OpenCodeNodeModulesPath
88
+
89
+ if (-not $nodeModulesPath) {
90
+ Write-Error "Could not find @1password/sdk in any node_modules directory"
91
+ return $false
92
+ }
93
+
94
+ $sdkPath = ($nodeModulesPath -replace '\\', '/') + "/@1password/sdk/dist/sdk.js"
95
+
96
+ # Create a temporary Node.js script file
97
+ $tempScript = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.js'
98
+
99
+ $testScript = 'const sdk = require("' + $sdkPath + '");' + "`n" +
100
+ 'async function test() {' + "`n" +
101
+ ' const client = await sdk.createClient({' + "`n" +
102
+ ' auth: "' + $Token + '",' + "`n" +
103
+ ' integrationName: "opencode-1password-setup-test",' + "`n" +
104
+ ' integrationVersion: "1.0.0"' + "`n" +
105
+ ' });' + "`n" +
106
+ ' console.log("SUCCESS");' + "`n" +
107
+ '}' + "`n" +
108
+ 'test().catch(err => { console.error("FAILED:", err.message); process.exit(1); });'
109
+
110
+ $testScript | Out-File -FilePath $tempScript -Encoding UTF8 -NoNewline
111
+
112
+ $result = & node $tempScript 2>&1
113
+ Remove-Item $tempScript -ErrorAction SilentlyContinue
91
114
 
92
- $result = node -e $testScript 2>&1
93
115
  return $result -match "SUCCESS"
94
116
  } catch {
95
117
  return $false
@@ -100,36 +122,37 @@ function Get-1PasswordAudit {
100
122
  param([string]$Token, [string]$ConfigEnvId)
101
123
 
102
124
  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;
125
+ $nodeModulesPath = Get-OpenCodeNodeModulesPath
126
+
127
+ if (-not $nodeModulesPath) {
128
+ Write-Error "Could not find @1password/sdk in any node_modules directory"
129
+ return $null
120
130
  }
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
131
 
132
- $jsonResult = node -e $script 2>&1 | Out-String
132
+ $sdkPath = ($nodeModulesPath -replace '\\', '/') + "/@1password/sdk/dist/sdk.js"
133
+
134
+ $script = 'const sdk = require("' + $sdkPath + '");' + "`n" +
135
+ 'async function audit() {' + "`n" +
136
+ ' const client = await sdk.createClient({' + "`n" +
137
+ ' auth: "' + $Token + '",' + "`n" +
138
+ ' integrationName: "opencode-1password-setup-test",' + "`n" +
139
+ ' integrationVersion: "1.0.0"' + "`n" +
140
+ ' });' + "`n" +
141
+ ' const { variables: configVars } = await client.environments.getVariables("' + $ConfigEnvId + '");' + "`n" +
142
+ ' const envIds = {};' + "`n" +
143
+ ' for (const v of configVars) {' + "`n" +
144
+ ' if (v.name.endsWith("_ENV_ID") && v.value) {' + "`n" +
145
+ ' envIds[v.name] = v.value;' + "`n" +
146
+ ' }' + "`n" +
147
+ ' }' + "`n" +
148
+ ' console.log(JSON.stringify(envIds));' + "`n" +
149
+ '}' + "`n" +
150
+ 'audit().catch(err => { console.error("FAILED:", err.message); process.exit(1); });'
151
+
152
+ $tempScript = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.js'
153
+ $script | Out-File -FilePath $tempScript -Encoding UTF8 -NoNewline
154
+ $jsonResult = & node $tempScript 2>&1 | Out-String
155
+ Remove-Item $tempScript -ErrorAction SilentlyContinue
133
156
  return $jsonResult | ConvertFrom-Json
134
157
  } catch {
135
158
  return $null
@@ -139,6 +162,8 @@ audit().catch(err => {
139
162
  function Show-AuditReport {
140
163
  param([string]$Token, [hashtable]$EnvIds)
141
164
 
165
+ $nodeModulesPath = Get-OpenCodeNodeModulesPath
166
+
142
167
  Write-Host ""
143
168
  Write-Host "========================================" -ForegroundColor Cyan
144
169
  Write-Host " Configuration Audit Report" -ForegroundColor Cyan
@@ -163,30 +188,27 @@ function Show-AuditReport {
163
188
  Write-Host "--------------------------------"
164
189
 
165
190
  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
- "@
191
+ # Use forward slashes to avoid JS escape sequence issues
192
+ $sdkPath = ($nodeModulesPath -replace '\\', '/') + "/@1password/sdk/dist/sdk.js"
193
+ $script = 'const sdk = require("' + $sdkPath + '");' + "`n" +
194
+ 'async function read() {' + "`n" +
195
+ ' const client = await sdk.createClient({' + "`n" +
196
+ ' auth: "' + $Token + '",' + "`n" +
197
+ ' integrationName: "opencode-1password-setup-test",' + "`n" +
198
+ ' integrationVersion: "1.0.0"' + "`n" +
199
+ ' });' + "`n" +
200
+ ' const { variables } = await client.environments.getVariables("' + $envId + '");' + "`n" +
201
+ ' for (const v of variables) {' + "`n" +
202
+ ' const masked = v.value ? v.value.substring(0, 8) + "••••••••" : "(empty)";' + "`n" +
203
+ ' console.log(v.name + "=" + masked);' + "`n" +
204
+ ' }' + "`n" +
205
+ '}' + "`n" +
206
+ 'read().catch(err => { console.error("Error:", err.message); });'
188
207
 
189
- $result = node -e $script 2>&1
208
+ $tempScript = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.js'
209
+ $script | Out-File -FilePath $tempScript -Encoding UTF8 -NoNewline
210
+ $result = & node $tempScript 2>&1
211
+ Remove-Item $tempScript -ErrorAction SilentlyContinue
190
212
  foreach ($line in $result) {
191
213
  if ($line -and $line -notmatch "^(Error|Error:)") {
192
214
  Write-Host " $line" -ForegroundColor Gray