orcastrator 0.2.12 → 0.2.13

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
@@ -188,7 +188,7 @@ Global:
188
188
 
189
189
  - `--anthropic-key <key>`
190
190
  - `--openai-key <key>`
191
- - `--check` (also checks `~/.openclaw/openclaw.json` `env.vars` fallback)
191
+ - `--check` (API key lookup order: CLI flag → process env → `~/.openclaw/openclaw.json` `env.vars` → `~/.claude/.env` → `~/.config/claude/.env` → `./.env`)
192
192
  - `--global`
193
193
  - `--project`
194
194
 
@@ -48,7 +48,7 @@ function commandExists(command) {
48
48
  const result = spawnSync("which", [command], { stdio: "ignore" });
49
49
  return result.status === 0;
50
50
  }
51
- export function resolveApiKey(flagValue, envVarName, openclawConfigPath) {
51
+ export function resolveApiKey(flagValue, envVarName, openclawConfigPathOrOptions, maybeOptions) {
52
52
  if (flagValue && flagValue.trim().length > 0) {
53
53
  return flagValue.trim();
54
54
  }
@@ -56,14 +56,25 @@ export function resolveApiKey(flagValue, envVarName, openclawConfigPath) {
56
56
  if (envValue && envValue.trim().length > 0) {
57
57
  return envValue.trim();
58
58
  }
59
- const openclawValue = readOpenclawEnvVar(envVarName, openclawConfigPath);
59
+ const options = typeof openclawConfigPathOrOptions === "string"
60
+ ? { ...(maybeOptions ?? {}), openclawConfigPath: openclawConfigPathOrOptions }
61
+ : (openclawConfigPathOrOptions ?? {});
62
+ const homedir = options.homedir ?? os.homedir();
63
+ const openclawValue = readOpenclawEnvVar(envVarName, options.openclawConfigPath, homedir);
60
64
  if (openclawValue) {
61
65
  return openclawValue;
62
66
  }
67
+ const dotenvValue = readDotEnvFallback(envVarName, {
68
+ cwd: options.cwd ?? process.cwd(),
69
+ homedir
70
+ });
71
+ if (dotenvValue) {
72
+ return dotenvValue;
73
+ }
63
74
  return undefined;
64
75
  }
65
- function readOpenclawEnvVar(envVarName, openclawConfigPath) {
66
- const configPath = openclawConfigPath ?? path.join(os.homedir(), ".openclaw", "openclaw.json");
76
+ function readOpenclawEnvVar(envVarName, openclawConfigPath, homedir = os.homedir()) {
77
+ const configPath = openclawConfigPath ?? path.join(homedir, ".openclaw", "openclaw.json");
67
78
  try {
68
79
  const fileText = readFileSync(configPath, "utf8");
69
80
  const parsed = JSON.parse(fileText);
@@ -90,6 +101,93 @@ function readOpenclawEnvVar(envVarName, openclawConfigPath) {
90
101
  }
91
102
  return undefined;
92
103
  }
104
+ function readDotEnvFallback(envVarName, options) {
105
+ const candidatePaths = [
106
+ path.join(options.homedir, ".claude", ".env"),
107
+ path.join(options.homedir, ".config", "claude", ".env"),
108
+ path.join(options.cwd, ".env")
109
+ ];
110
+ for (const candidatePath of candidatePaths) {
111
+ const value = readEnvVarFromDotEnvFile(candidatePath, envVarName);
112
+ if (value !== undefined) {
113
+ return value;
114
+ }
115
+ }
116
+ return undefined;
117
+ }
118
+ function readEnvVarFromDotEnvFile(filePath, envVarName) {
119
+ try {
120
+ const parsed = parseDotEnv(readFileSync(filePath, "utf8"));
121
+ const value = parsed[envVarName];
122
+ if (value !== undefined && value.trim().length > 0) {
123
+ return value.trim();
124
+ }
125
+ }
126
+ catch {
127
+ return undefined;
128
+ }
129
+ return undefined;
130
+ }
131
+ function parseDotEnv(text) {
132
+ const values = {};
133
+ for (const rawLine of text.split(/\r?\n/)) {
134
+ const line = rawLine.trim();
135
+ if (!line || line.startsWith("#")) {
136
+ continue;
137
+ }
138
+ const normalized = line.startsWith("export ") ? line.slice(7).trimStart() : line;
139
+ const separatorIndex = normalized.indexOf("=");
140
+ if (separatorIndex <= 0) {
141
+ continue;
142
+ }
143
+ const key = normalized.slice(0, separatorIndex).trim();
144
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
145
+ continue;
146
+ }
147
+ let rawValue = normalized.slice(separatorIndex + 1).trim();
148
+ if (!rawValue) {
149
+ values[key] = "";
150
+ continue;
151
+ }
152
+ if (rawValue.startsWith('"') || rawValue.startsWith("'")) {
153
+ const quote = rawValue.charAt(0);
154
+ const closingQuoteIndex = findClosingQuote(rawValue, quote);
155
+ if (closingQuoteIndex > 0) {
156
+ rawValue = rawValue.slice(1, closingQuoteIndex);
157
+ }
158
+ else {
159
+ rawValue = rawValue.slice(1);
160
+ }
161
+ values[key] = quote === '"' ? unescapeDoubleQuoted(rawValue) : unescapeSingleQuoted(rawValue);
162
+ continue;
163
+ }
164
+ const commentStart = rawValue.search(/\s#/);
165
+ if (commentStart >= 0) {
166
+ rawValue = rawValue.slice(0, commentStart).trimEnd();
167
+ }
168
+ values[key] = rawValue;
169
+ }
170
+ return values;
171
+ }
172
+ function findClosingQuote(value, quote) {
173
+ for (let i = 1; i < value.length; i += 1) {
174
+ if (value[i] === quote && value[i - 1] !== "\\") {
175
+ return i;
176
+ }
177
+ }
178
+ return -1;
179
+ }
180
+ function unescapeDoubleQuoted(value) {
181
+ return value
182
+ .replace(/\\n/g, "\n")
183
+ .replace(/\\r/g, "\r")
184
+ .replace(/\\t/g, "\t")
185
+ .replace(/\\"/g, '"')
186
+ .replace(/\\\\/g, "\\");
187
+ }
188
+ function unescapeSingleQuoted(value) {
189
+ return value.replace(/\\'/g, "'").replace(/\\\\/g, "\\");
190
+ }
93
191
  export function detectPackageManager(exists = commandExists) {
94
192
  if (exists("brew")) {
95
193
  return "brew";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orcastrator",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "orca": "dist/cli/index.js"