opencode-dotenv 0.4.1 → 0.5.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.
- package/README.md +45 -21
- package/dist/index.js +221 -66
- package/package.json +28 -6
package/README.md
CHANGED
|
@@ -2,16 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
OpenCode plugin to load `.env` files at startup.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Important Limitation**
|
|
6
|
+
>
|
|
7
|
+
> This plugin **cannot** set environment variables for use in OpenCode's config file (`opencode.jsonc`). The plugin loads *after* OpenCode parses its configuration, so `{env:VARNAME}` references in config are resolved before this plugin runs.
|
|
8
|
+
>
|
|
9
|
+
> **Use this plugin for:** Managing environment variables available during chat sessions, tool executions, and bash commands.
|
|
10
|
+
>
|
|
11
|
+
> **Do not use this plugin for:** Setting API keys or other config values referenced via `{env:VAR}` syntax in `opencode.jsonc`. For those, set variables in your shell profile (`~/.zshrc`, `~/.bashrc`) before starting OpenCode.
|
|
12
|
+
>
|
|
13
|
+
> See [Architecture](docs/ARCHITECTURE.md) for details on the OpenCode startup sequence.
|
|
6
14
|
|
|
15
|
+
## Features
|
|
16
|
+
|
|
7
17
|
- Load multiple `.env` files in order via config file
|
|
8
18
|
- Load `.env` from current working directory (optional)
|
|
9
19
|
- Override existing environment variables (later files override earlier ones)
|
|
10
|
-
- Configurable logging to
|
|
20
|
+
- Configurable logging to `~/.local/share/opencode/dotenv.log` (disabled by default)
|
|
11
21
|
- Prevents double loading with load guard
|
|
12
22
|
- JSONC config file format (supports comments and trailing commas)
|
|
13
23
|
- **Requires Bun runtime**
|
|
14
24
|
|
|
25
|
+
## Limitations
|
|
26
|
+
|
|
27
|
+
1. **Cannot modify existing OpenCode config** - Variables loaded by this plugin cannot be referenced in `opencode.jsonc` using `{env:VAR}` syntax. OpenCode reads its config before plugins initialize.
|
|
28
|
+
|
|
29
|
+
2. **Only affects subsequent operations** - Loaded environment variables are only available to new chat sessions, tool calls, and operations that occur AFTER plugin initialization.
|
|
30
|
+
|
|
31
|
+
3. **No config reload capability** - Changing `.env` files while OpenCode is running will NOT trigger config re-parsing. To apply changes, restart OpenCode.
|
|
32
|
+
|
|
33
|
+
**Recommended approach:** Set environment variables in your shell profile (`.zshrc`, `.bashrc`) before starting OpenCode.
|
|
34
|
+
|
|
15
35
|
## Installation
|
|
16
36
|
|
|
17
37
|
Add to your `opencode.jsonc`:
|
|
@@ -33,12 +53,12 @@ After publishing to npm, you can use:
|
|
|
33
53
|
|
|
34
54
|
## Configuration
|
|
35
55
|
|
|
36
|
-
Create `
|
|
56
|
+
Create `dotenv.jsonc` in one of these locations (searched in order, first found wins):
|
|
37
57
|
|
|
38
|
-
1.
|
|
39
|
-
2.
|
|
58
|
+
1. `./dotenv.jsonc` in current working directory (project-specific)
|
|
59
|
+
2. `~/.config/opencode/dotenv.jsonc` (global config)
|
|
40
60
|
|
|
41
|
-
**Note:**
|
|
61
|
+
**Note:** Only the first found config file is used; configs are not merged.
|
|
42
62
|
|
|
43
63
|
### Config Schema
|
|
44
64
|
|
|
@@ -56,7 +76,7 @@ Config file uses **JSONC format** (JSON with Comments), which supports:
|
|
|
56
76
|
],
|
|
57
77
|
"load_cwd_env": true,
|
|
58
78
|
"logging": {
|
|
59
|
-
"enabled":
|
|
79
|
+
"enabled": false
|
|
60
80
|
}
|
|
61
81
|
}
|
|
62
82
|
```
|
|
@@ -64,13 +84,13 @@ Config file uses **JSONC format** (JSON with Comments), which supports:
|
|
|
64
84
|
**Fields:**
|
|
65
85
|
- `files` (array, optional): List of `.env` file paths to load in order. Later files override earlier ones.
|
|
66
86
|
- `load_cwd_env` (boolean, optional): Whether to load `.env` from the directory where OpenCode is opened. Defaults to `true`.
|
|
67
|
-
- `logging.enabled` (boolean, optional): Enable/disable logging to
|
|
87
|
+
- `logging.enabled` (boolean, optional): Enable/disable logging to `~/.local/share/opencode/dotenv.log`. Defaults to `false`.
|
|
68
88
|
|
|
69
89
|
**Notes:**
|
|
70
90
|
- Use `~` for home directory (automatically expanded)
|
|
71
91
|
- Paths are expanded before loading
|
|
72
92
|
- If no config file exists, only loads `./.env` from cwd (if present)
|
|
73
|
-
- Logging writes to
|
|
93
|
+
- Logging writes to `~/.local/share/opencode/dotenv.log` for debugging
|
|
74
94
|
|
|
75
95
|
### Load Order
|
|
76
96
|
|
|
@@ -83,7 +103,7 @@ This ensures project-specific env vars have the highest precedence.
|
|
|
83
103
|
|
|
84
104
|
### Load global and project-specific .env files
|
|
85
105
|
|
|
86
|
-
Config (`~/.config/opencode/
|
|
106
|
+
Config (`~/.config/opencode/dotenv.jsonc`):
|
|
87
107
|
|
|
88
108
|
```jsonc
|
|
89
109
|
{
|
|
@@ -100,11 +120,11 @@ Config (`~/.config/opencode/opencode-dotenv.jsonc`):
|
|
|
100
120
|
Result:
|
|
101
121
|
1. Loads `~/.config/opencode/.env`
|
|
102
122
|
2. Loads `./.env` from cwd (overrides any conflicts)
|
|
103
|
-
3. Logs all activity to
|
|
123
|
+
3. Logs all activity to `~/.local/share/opencode/dotenv.log`
|
|
104
124
|
|
|
105
125
|
### Load multiple global files without cwd .env
|
|
106
126
|
|
|
107
|
-
Config (`~/.config/opencode/
|
|
127
|
+
Config (`~/.config/opencode/dotenv.jsonc`):
|
|
108
128
|
|
|
109
129
|
```jsonc
|
|
110
130
|
{
|
|
@@ -126,41 +146,42 @@ Result:
|
|
|
126
146
|
4. No logging output
|
|
127
147
|
|
|
128
148
|
### Example .env files
|
|
129
|
-
|
|
149
|
+
|
|
130
150
|
`~/.config/opencode/.env`:
|
|
131
|
-
|
|
151
|
+
|
|
132
152
|
```bash
|
|
133
153
|
# OpenCode Dotenv Configuration
|
|
134
|
-
OPENCODE_API_KEY=your_api_key_here
|
|
135
154
|
OPENCODE_DEBUG=true
|
|
136
155
|
OPENCODE_MAX_TOKENS=100000
|
|
156
|
+
MY_PROJECT_KEY=secret123
|
|
137
157
|
```
|
|
138
158
|
|
|
139
|
-
|
|
159
|
+
**Note:** This plugin cannot inject variables into OpenCode's configuration loading process. To set `ANTHROPIC_API_KEY` or other provider API keys, set them in your shell profile (`~/.zshrc`, `~/.bashrc`) before starting OpenCode.
|
|
140
160
|
|
|
161
|
+
`./.env` (project-specific):
|
|
141
162
|
```bash
|
|
142
163
|
# Project-specific overrides
|
|
143
164
|
OPENCODE_DEBUG=false
|
|
144
165
|
PROJECT_API_KEY=project_specific_key
|
|
145
166
|
```
|
|
146
167
|
|
|
147
|
-
Result: `OPENCODE_DEBUG` will be `false` (from cwd), `
|
|
168
|
+
Result: `OPENCODE_DEBUG` will be `false` (from cwd), `MY_PROJECT_KEY` from global, `PROJECT_API_KEY` from cwd.
|
|
148
169
|
|
|
149
170
|
### Logging
|
|
150
171
|
|
|
151
172
|
View plugin activity logs:
|
|
152
173
|
|
|
153
174
|
```bash
|
|
154
|
-
tail -f /
|
|
175
|
+
tail -f ~/.local/share/opencode/dotenv.log
|
|
155
176
|
```
|
|
156
177
|
|
|
157
|
-
|
|
178
|
+
Enable logging in config:
|
|
158
179
|
|
|
159
180
|
```jsonc
|
|
160
181
|
{
|
|
161
182
|
"files": ["~/.config/opencode/.env"],
|
|
162
183
|
"logging": {
|
|
163
|
-
"enabled":
|
|
184
|
+
"enabled": true
|
|
164
185
|
}
|
|
165
186
|
}
|
|
166
187
|
```
|
|
@@ -173,7 +194,10 @@ Disable logging in config:
|
|
|
173
194
|
opencode-dotenv/
|
|
174
195
|
├── package.json
|
|
175
196
|
├── src/
|
|
176
|
-
│
|
|
197
|
+
│ ├── index.ts
|
|
198
|
+
│ └── plugin.ts
|
|
199
|
+
├── docs/
|
|
200
|
+
│ └── ARCHITECTURE.md
|
|
177
201
|
└── dist/
|
|
178
202
|
└── index.js (built)
|
|
179
203
|
```
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// src/
|
|
2
|
+
// src/plugin.ts
|
|
3
3
|
import { homedir } from "os";
|
|
4
|
+
import { appendFile, mkdir } from "fs/promises";
|
|
4
5
|
|
|
5
6
|
// node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
6
7
|
function createScanner(text, ignoreTrivia = false) {
|
|
@@ -806,9 +807,165 @@ var ParseErrorCode;
|
|
|
806
807
|
ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
|
|
807
808
|
})(ParseErrorCode || (ParseErrorCode = {}));
|
|
808
809
|
|
|
809
|
-
// src/
|
|
810
|
-
|
|
810
|
+
// src/profiler/profiler.ts
|
|
811
|
+
function percentile(sorted, p) {
|
|
812
|
+
if (sorted.length === 0)
|
|
813
|
+
return 0;
|
|
814
|
+
const index = Math.ceil(p / 100 * sorted.length) - 1;
|
|
815
|
+
return sorted[Math.max(0, index)];
|
|
816
|
+
}
|
|
817
|
+
function calculateStats(measurements) {
|
|
818
|
+
if (measurements.length === 0)
|
|
819
|
+
return null;
|
|
820
|
+
const sorted = [...measurements].sort((a, b) => a - b);
|
|
821
|
+
const total = sorted.reduce((sum, v) => sum + v, 0);
|
|
822
|
+
return {
|
|
823
|
+
count: sorted.length,
|
|
824
|
+
min: sorted[0],
|
|
825
|
+
max: sorted[sorted.length - 1],
|
|
826
|
+
avg: total / sorted.length,
|
|
827
|
+
p50: percentile(sorted, 50),
|
|
828
|
+
p95: percentile(sorted, 95),
|
|
829
|
+
p99: percentile(sorted, 99),
|
|
830
|
+
total
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
class Profiler {
|
|
835
|
+
marks = new Map;
|
|
836
|
+
measures = new Map;
|
|
837
|
+
fileMetrics = [];
|
|
838
|
+
initStartTime = null;
|
|
839
|
+
initEndTime = null;
|
|
840
|
+
initState = "idle";
|
|
841
|
+
configLoadTime = null;
|
|
842
|
+
configPath = null;
|
|
843
|
+
totalVars = 0;
|
|
844
|
+
startTime = performance.now();
|
|
845
|
+
mark(name) {
|
|
846
|
+
this.marks.set(name, performance.now());
|
|
847
|
+
}
|
|
848
|
+
measure(name, startMark) {
|
|
849
|
+
const start = this.marks.get(startMark);
|
|
850
|
+
if (start === undefined) {
|
|
851
|
+
return -1;
|
|
852
|
+
}
|
|
853
|
+
const duration = performance.now() - start;
|
|
854
|
+
const existing = this.measures.get(name) || [];
|
|
855
|
+
existing.push(duration);
|
|
856
|
+
this.measures.set(name, existing);
|
|
857
|
+
return duration;
|
|
858
|
+
}
|
|
859
|
+
record(name, duration) {
|
|
860
|
+
const existing = this.measures.get(name) || [];
|
|
861
|
+
existing.push(duration);
|
|
862
|
+
this.measures.set(name, existing);
|
|
863
|
+
}
|
|
864
|
+
getStats(name) {
|
|
865
|
+
const measurements = this.measures.get(name);
|
|
866
|
+
if (!measurements)
|
|
867
|
+
return null;
|
|
868
|
+
return calculateStats(measurements);
|
|
869
|
+
}
|
|
870
|
+
initStart() {
|
|
871
|
+
this.initStartTime = performance.now();
|
|
872
|
+
this.initState = "loading";
|
|
873
|
+
}
|
|
874
|
+
initComplete(state) {
|
|
875
|
+
this.initEndTime = performance.now();
|
|
876
|
+
this.initState = state;
|
|
877
|
+
}
|
|
878
|
+
recordConfigLoad(duration, path) {
|
|
879
|
+
this.configLoadTime = duration;
|
|
880
|
+
this.configPath = path;
|
|
881
|
+
}
|
|
882
|
+
recordFileLoad(path, duration, varCount, success, error) {
|
|
883
|
+
this.fileMetrics.push({
|
|
884
|
+
path,
|
|
885
|
+
duration,
|
|
886
|
+
varCount,
|
|
887
|
+
success,
|
|
888
|
+
error
|
|
889
|
+
});
|
|
890
|
+
if (success) {
|
|
891
|
+
this.totalVars += varCount;
|
|
892
|
+
this.record("file.load", duration);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
getInitState() {
|
|
896
|
+
return this.initState;
|
|
897
|
+
}
|
|
898
|
+
getInitDuration() {
|
|
899
|
+
if (this.initStartTime === null)
|
|
900
|
+
return null;
|
|
901
|
+
if (this.initEndTime !== null) {
|
|
902
|
+
return this.initEndTime - this.initStartTime;
|
|
903
|
+
}
|
|
904
|
+
return performance.now() - this.initStartTime;
|
|
905
|
+
}
|
|
906
|
+
getTotalVars() {
|
|
907
|
+
return this.totalVars;
|
|
908
|
+
}
|
|
909
|
+
getFileMetrics() {
|
|
910
|
+
return [...this.fileMetrics];
|
|
911
|
+
}
|
|
912
|
+
export() {
|
|
913
|
+
return {
|
|
914
|
+
timestamp: new Date().toISOString(),
|
|
915
|
+
uptime: performance.now() - this.startTime,
|
|
916
|
+
initialization: {
|
|
917
|
+
startTime: this.initStartTime || 0,
|
|
918
|
+
endTime: this.initEndTime,
|
|
919
|
+
duration: this.getInitDuration(),
|
|
920
|
+
state: this.initState
|
|
921
|
+
},
|
|
922
|
+
config: {
|
|
923
|
+
loadTime: this.configLoadTime,
|
|
924
|
+
path: this.configPath
|
|
925
|
+
},
|
|
926
|
+
files: {
|
|
927
|
+
loadTimes: this.getStats("file.load"),
|
|
928
|
+
details: this.fileMetrics,
|
|
929
|
+
totalVars: this.totalVars
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
reset() {
|
|
934
|
+
this.marks.clear();
|
|
935
|
+
this.measures.clear();
|
|
936
|
+
this.fileMetrics = [];
|
|
937
|
+
this.initStartTime = null;
|
|
938
|
+
this.initEndTime = null;
|
|
939
|
+
this.initState = "idle";
|
|
940
|
+
this.configLoadTime = null;
|
|
941
|
+
this.configPath = null;
|
|
942
|
+
this.totalVars = 0;
|
|
943
|
+
}
|
|
944
|
+
startTimer(name) {
|
|
945
|
+
const start = performance.now();
|
|
946
|
+
return () => {
|
|
947
|
+
const duration = performance.now() - start;
|
|
948
|
+
this.record(name, duration);
|
|
949
|
+
return duration;
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
var globalProfiler = new Profiler;
|
|
954
|
+
// src/plugin.ts
|
|
955
|
+
var CONFIG_NAME = "dotenv.jsonc";
|
|
956
|
+
var LOG_DIR = `${homedir()}/.local/share/opencode`;
|
|
957
|
+
var LOG_FILE = `${LOG_DIR}/dotenv.log`;
|
|
811
958
|
var LOAD_GUARD = "__opencodeDotenvLoaded";
|
|
959
|
+
var isTestEnv = !!process.env.BUN_TEST;
|
|
960
|
+
var loggingEnabled = false;
|
|
961
|
+
function log(message) {
|
|
962
|
+
if (!loggingEnabled || isTestEnv)
|
|
963
|
+
return;
|
|
964
|
+
const timestamp = new Date().toISOString();
|
|
965
|
+
const line = `[${timestamp}] ${message}
|
|
966
|
+
`;
|
|
967
|
+
mkdir(LOG_DIR, { recursive: true }).then(() => appendFile(LOG_FILE, line)).catch(() => {});
|
|
968
|
+
}
|
|
812
969
|
function parseDotenv(content) {
|
|
813
970
|
const result = {};
|
|
814
971
|
if (typeof content !== "string")
|
|
@@ -820,114 +977,112 @@ function parseDotenv(content) {
|
|
|
820
977
|
continue;
|
|
821
978
|
const match = trimmed.match(/^export\s+([^=]+)=(.*)$/);
|
|
822
979
|
const key = match ? match[1] : trimmed.split("=")[0];
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
parsedValue = parsedValue.slice(1, -1);
|
|
980
|
+
if (!key)
|
|
981
|
+
continue;
|
|
982
|
+
const value = match ? match[2] : trimmed.substring(key.length + 1);
|
|
983
|
+
if (value === undefined)
|
|
984
|
+
continue;
|
|
985
|
+
let parsedValue = value.trim();
|
|
986
|
+
if (!parsedValue.startsWith('"') && !parsedValue.startsWith("'")) {
|
|
987
|
+
const inlineCommentIndex = parsedValue.indexOf(" #");
|
|
988
|
+
if (inlineCommentIndex !== -1) {
|
|
989
|
+
parsedValue = parsedValue.substring(0, inlineCommentIndex).trim();
|
|
834
990
|
}
|
|
835
|
-
result[key.trim()] = parsedValue;
|
|
836
991
|
}
|
|
992
|
+
if (parsedValue.startsWith('"') && parsedValue.endsWith('"') || parsedValue.startsWith("'") && parsedValue.endsWith("'")) {
|
|
993
|
+
parsedValue = parsedValue.slice(1, -1);
|
|
994
|
+
}
|
|
995
|
+
result[key.trim()] = parsedValue;
|
|
837
996
|
}
|
|
838
997
|
return result;
|
|
839
998
|
}
|
|
840
999
|
function expandPath(path) {
|
|
841
1000
|
return path.replace(/^~/, homedir());
|
|
842
1001
|
}
|
|
843
|
-
var loggingEnabled = true;
|
|
844
|
-
function logToFile(message) {
|
|
845
|
-
if (!loggingEnabled)
|
|
846
|
-
return;
|
|
847
|
-
try {
|
|
848
|
-
const timestamp = new Date().toISOString();
|
|
849
|
-
Bun.appendFileSync(LOG_FILE, `[${timestamp}] ${message}
|
|
850
|
-
`);
|
|
851
|
-
} catch (e) {}
|
|
852
|
-
}
|
|
853
1002
|
async function loadConfig() {
|
|
1003
|
+
const timer = globalProfiler.startTimer("config.load");
|
|
854
1004
|
const configPaths = [
|
|
855
|
-
`${
|
|
856
|
-
`${
|
|
1005
|
+
`${process.cwd()}/${CONFIG_NAME}`,
|
|
1006
|
+
`${homedir()}/.config/opencode/${CONFIG_NAME}`
|
|
857
1007
|
];
|
|
858
1008
|
for (const configPath of configPaths) {
|
|
859
1009
|
try {
|
|
860
1010
|
const file = Bun.file(configPath);
|
|
861
|
-
if (!await file.exists())
|
|
862
|
-
continue;
|
|
863
1011
|
const content = await file.text();
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1012
|
+
if (!content || typeof content !== "string") {
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
const config = parse2(content, [], {
|
|
1016
|
+
allowTrailingComma: true
|
|
1017
|
+
});
|
|
1018
|
+
if (config && typeof config === "object" && Array.isArray(config.files)) {
|
|
1019
|
+
loggingEnabled = config.logging?.enabled === true;
|
|
1020
|
+
const duration = timer();
|
|
1021
|
+
globalProfiler.recordConfigLoad(duration, configPath);
|
|
1022
|
+
return { config, path: configPath };
|
|
1023
|
+
}
|
|
1024
|
+
} catch {}
|
|
870
1025
|
}
|
|
871
|
-
|
|
1026
|
+
timer();
|
|
1027
|
+
return { config: { files: [], load_cwd_env: true }, path: null };
|
|
872
1028
|
}
|
|
873
1029
|
async function loadDotenvFile(filePath) {
|
|
1030
|
+
const start = performance.now();
|
|
874
1031
|
try {
|
|
875
1032
|
const file = Bun.file(filePath);
|
|
876
|
-
if (!await file.exists()) {
|
|
877
|
-
logToFile(`File not found: ${filePath}`);
|
|
878
|
-
return { count: 0, success: false };
|
|
879
|
-
}
|
|
880
1033
|
const content = await file.text();
|
|
881
|
-
if (typeof content !== "string") {
|
|
882
|
-
|
|
1034
|
+
if (typeof content !== "string" || !content) {
|
|
1035
|
+
const duration2 = performance.now() - start;
|
|
1036
|
+
globalProfiler.recordFileLoad(filePath, duration2, 0, false, "Empty or invalid content");
|
|
1037
|
+
log(`Invalid or empty content from ${filePath}`);
|
|
883
1038
|
return { count: 0, success: false };
|
|
884
1039
|
}
|
|
885
1040
|
const envVars = parseDotenv(content);
|
|
1041
|
+
const count = Object.keys(envVars).length;
|
|
886
1042
|
for (const [key, value] of Object.entries(envVars)) {
|
|
887
1043
|
process.env[key] = value;
|
|
888
1044
|
}
|
|
889
|
-
|
|
1045
|
+
const duration = performance.now() - start;
|
|
1046
|
+
globalProfiler.recordFileLoad(filePath, duration, count, true);
|
|
1047
|
+
return { count, success: true };
|
|
890
1048
|
} catch (error) {
|
|
891
|
-
|
|
1049
|
+
const duration = performance.now() - start;
|
|
1050
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1051
|
+
globalProfiler.recordFileLoad(filePath, duration, 0, false, errorMsg);
|
|
1052
|
+
log(`Failed to load ${filePath}: ${errorMsg}`);
|
|
892
1053
|
return { count: 0, success: false };
|
|
893
1054
|
}
|
|
894
1055
|
}
|
|
895
|
-
var DotEnvPlugin = async (
|
|
1056
|
+
var DotEnvPlugin = async (_ctx) => {
|
|
896
1057
|
if (globalThis[LOAD_GUARD]) {
|
|
897
1058
|
return {};
|
|
898
1059
|
}
|
|
899
1060
|
globalThis[LOAD_GUARD] = true;
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1061
|
+
globalProfiler.initStart();
|
|
1062
|
+
log("Plugin started");
|
|
1063
|
+
const { config, path: configPath } = await loadConfig();
|
|
1064
|
+
log(`Config loaded from ${configPath || "default"}: ${config.files.length} files, load_cwd_env=${config.load_cwd_env}, logging=${loggingEnabled}`);
|
|
1065
|
+
const filesToLoad = [...config.files.map(expandPath)];
|
|
1066
|
+
if (config.load_cwd_env !== false) {
|
|
1067
|
+
filesToLoad.push(`${process.cwd()}/.env`);
|
|
1068
|
+
}
|
|
903
1069
|
let totalFiles = 0;
|
|
904
1070
|
let totalVars = 0;
|
|
905
|
-
for (const
|
|
906
|
-
|
|
907
|
-
logToFile(`Loading: ${filePath}`);
|
|
1071
|
+
for (const filePath of filesToLoad) {
|
|
1072
|
+
log(`Loading: ${filePath}`);
|
|
908
1073
|
const result = await loadDotenvFile(filePath);
|
|
909
1074
|
if (result.success) {
|
|
910
1075
|
totalFiles++;
|
|
911
1076
|
totalVars += result.count;
|
|
912
|
-
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
if (config.load_cwd_env !== false) {
|
|
916
|
-
const cwdEnvPath = `${process.cwd()}/.env`;
|
|
917
|
-
logToFile(`Loading cwd: ${cwdEnvPath}`);
|
|
918
|
-
const result = await loadDotenvFile(cwdEnvPath);
|
|
919
|
-
if (result.success) {
|
|
920
|
-
totalFiles++;
|
|
921
|
-
totalVars += result.count;
|
|
922
|
-
logToFile(`Loaded ${result.count} vars from cwd`);
|
|
1077
|
+
log(`Loaded ${result.count} vars from ${filePath}`);
|
|
923
1078
|
}
|
|
924
1079
|
}
|
|
925
|
-
|
|
1080
|
+
globalProfiler.initComplete("ready");
|
|
1081
|
+
const initDuration = globalProfiler.getInitDuration();
|
|
1082
|
+
log(`Plugin finished: ${totalFiles} files, ${totalVars} vars in ${initDuration?.toFixed(2)}ms`);
|
|
926
1083
|
return {};
|
|
927
1084
|
};
|
|
928
|
-
var src_default = DotEnvPlugin;
|
|
929
1085
|
export {
|
|
930
|
-
|
|
931
|
-
src_default as default,
|
|
1086
|
+
DotEnvPlugin as default,
|
|
932
1087
|
DotEnvPlugin
|
|
933
1088
|
};
|
package/package.json
CHANGED
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-dotenv",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "OpenCode plugin to load .env files at startup",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
6
7
|
"type": "module",
|
|
7
|
-
"
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
8
14
|
"scripts": {
|
|
9
15
|
"build": "bun build src/index.ts --outdir dist --target bun",
|
|
16
|
+
"dev": "bun --hot src/index.ts",
|
|
17
|
+
"test": "bun test",
|
|
18
|
+
"test:coverage": "bun test --coverage",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"bench": "bun run bench/init.bench.ts",
|
|
10
21
|
"prepublishOnly": "bun run build"
|
|
11
22
|
},
|
|
12
23
|
"files": [
|
|
13
|
-
"dist
|
|
24
|
+
"dist",
|
|
14
25
|
"README.md",
|
|
15
26
|
"LICENSE"
|
|
16
27
|
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"opencode",
|
|
30
|
+
"plugin",
|
|
31
|
+
"dotenv",
|
|
32
|
+
"env",
|
|
33
|
+
"environment"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/bun": "latest",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
},
|
|
17
40
|
"peerDependencies": {
|
|
18
41
|
"@opencode-ai/plugin": "*"
|
|
19
42
|
},
|
|
20
43
|
"dependencies": {
|
|
21
44
|
"jsonc-parser": "^3.3.1"
|
|
22
|
-
}
|
|
23
|
-
"devDependencies": {}
|
|
45
|
+
}
|
|
24
46
|
}
|