apex-dev 3.10.19 → 3.10.21

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/cli.js CHANGED
@@ -145,6 +145,8 @@ function getDownloadUrl() {
145
145
  return `https://github.com/Marcus-Mok-GH/apex-dev/releases/download/v${VERSION}/apex-dev-${platform}-${arch}`;
146
146
  }
147
147
 
148
+ const DOWNLOAD_TIMEOUT = 10 * 1000;
149
+
148
150
  function downloadBinary(destPath) {
149
151
  const url = getDownloadUrl();
150
152
  console.error(`Downloading apex-dev v${VERSION} for ${detectPlatform().platform}-${detectPlatform().arch}...`);
@@ -152,45 +154,80 @@ function downloadBinary(destPath) {
152
154
 
153
155
  return new Promise((resolve, reject) => {
154
156
  const file = fs.createWriteStream(destPath, { mode: 0o755 });
155
- const DOWNLOAD_TIMEOUT = 10000;
156
-
157
- const req = https
158
- .get(url, { timeout: DOWNLOAD_TIMEOUT }, (response) => {
159
- if (response.statusCode === 302 || response.statusCode === 301) {
160
- const redirectReq = https.get(response.headers.location, { timeout: DOWNLOAD_TIMEOUT }, (redirectRes) => {
161
- if (redirectRes.statusCode !== 200) {
162
- reject(new Error(`Download failed with status ${redirectRes.statusCode}`));
163
- return;
164
- }
165
- redirectRes.pipe(file);
166
- file.on("finish", () => {
167
- file.close();
168
- resolve();
169
- });
170
- });
171
- redirectReq.on("error", reject);
172
- redirectReq.on("timeout", () => {
173
- redirectReq.destroy();
174
- reject(new Error("Download timed out after 10s"));
175
- });
176
- return;
157
+ let settled = false;
158
+
159
+ function cleanupAndReject(err) {
160
+ if (settled) return;
161
+ settled = true;
162
+ file.destroy();
163
+ try { fs.unlinkSync(destPath); } catch {}
164
+ reject(err);
165
+ }
166
+
167
+ function onFileError(err) {
168
+ cleanupAndReject(new Error(`Write stream error: ${err.message}`));
169
+ }
170
+
171
+ file.on("error", onFileError);
172
+
173
+ const req = https.get(url, (response) => {
174
+ if (response.statusCode === 302 || response.statusCode === 301) {
175
+ const redirectReq = https.get(response.headers.location, (redirectRes) => {
176
+ if (redirectRes.statusCode !== 200) {
177
+ cleanupAndReject(new Error(`Download failed with status ${redirectRes.statusCode}`));
178
+ return;
179
+ }
180
+
181
+ function onFinish() {
182
+ file.removeListener("error", onFileError);
183
+ redirectReq.removeListener("error", onRedirectError);
184
+ redirectReq.removeListener("timeout", onRedirectTimeout);
185
+ file.close();
186
+ resolve();
187
+ }
188
+
189
+ file.on("finish", onFinish);
190
+ redirectRes.pipe(file);
191
+ });
192
+
193
+ function onRedirectError(err) {
194
+ cleanupAndReject(new Error(`Redirect request error: ${err.message}`));
177
195
  }
178
- if (response.statusCode !== 200) {
179
- reject(new Error(`Download failed with status ${response.statusCode}`));
180
- return;
196
+ function onRedirectTimeout() {
197
+ cleanupAndReject(new Error("Download redirect timed out"));
181
198
  }
182
- response.pipe(file);
183
- file.on("finish", () => {
184
- file.close();
185
- resolve();
186
- });
187
- });
188
199
 
189
- req.on("error", reject);
190
- req.on("timeout", () => {
191
- req.destroy();
192
- reject(new Error("Download timed out after 10s"));
200
+ redirectReq.on("error", onRedirectError);
201
+ redirectReq.setTimeout(DOWNLOAD_TIMEOUT, onRedirectTimeout);
202
+ return;
203
+ }
204
+
205
+ if (response.statusCode !== 200) {
206
+ cleanupAndReject(new Error(`Download failed with status ${response.statusCode}`));
207
+ return;
208
+ }
209
+
210
+ function onFinish() {
211
+ file.removeListener("error", onFileError);
212
+ req.removeListener("error", onReqError);
213
+ req.removeListener("timeout", onReqTimeout);
214
+ file.close();
215
+ resolve();
216
+ }
217
+
218
+ file.on("finish", onFinish);
219
+ response.pipe(file);
193
220
  });
221
+
222
+ function onReqError(err) {
223
+ cleanupAndReject(new Error(`Download request error: ${err.message}`));
224
+ }
225
+ function onReqTimeout() {
226
+ cleanupAndReject(new Error("Download timed out"));
227
+ }
228
+
229
+ req.on("error", onReqError);
230
+ req.setTimeout(DOWNLOAD_TIMEOUT, onReqTimeout);
194
231
  });
195
232
  }
196
233
 
package/dist/apex CHANGED
Binary file
package/dist/index.js CHANGED
@@ -54,6 +54,7 @@ var require_store = __commonJS((exports, module2) => {
54
54
  var _detectedProvider = config.detectInitialProvider();
55
55
  var _providerEnvKey = config.PROVIDERS[_detectedProvider].envKey;
56
56
  var _apiKey = process.env[_providerEnvKey] || "";
57
+ var _needsConfig = process.env.APEX_DEV_NEEDS_CONFIG === "true" || !Boolean(_apiKey);
57
58
  var state = {
58
59
  messages: [],
59
60
  streamingContent: "",
@@ -63,7 +64,7 @@ var require_store = __commonJS((exports, module2) => {
63
64
  showSummary: false,
64
65
  apiKey: _apiKey,
65
66
  provider: _detectedProvider,
66
- needsConfig: !Boolean(_apiKey)
67
+ needsConfig: _needsConfig
67
68
  };
68
69
  var nextId = 1;
69
70
  var listeners = new Set;
@@ -867,16 +868,26 @@ The user asks you to implement a new feature. You respond in multiple steps:
867
868
  deepseek: { model: "deepseek/deepseek-chat-v3", temperature: 0.1, maxTokens: 8192 },
868
869
  minimax: { model: "minimax/minimax-01", temperature: 0.1, maxTokens: 8192 }
869
870
  };
870
- const _initialProvider = PROVIDERS[currentProvider];
871
+ const os = __require("os");
872
+ let savedProvider = null;
871
873
  try {
872
- const configPath = path.join(__require("os").homedir(), ".apex-dev", "config.json");
874
+ const configPath = path.join(os.homedir(), ".apex-dev", "config.json");
873
875
  if (fs.existsSync(configPath)) {
874
876
  const savedConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
875
- if (savedConfig[currentProvider] && !process.env[_initialProvider.envKey]) {
876
- process.env[_initialProvider.envKey] = savedConfig[currentProvider];
877
+ const hasEnvKey = Object.values(PROVIDERS).some((p) => process.env[p.envKey]);
878
+ if (!hasEnvKey) {
879
+ for (const [providerKey, provider] of Object.entries(PROVIDERS)) {
880
+ if (savedConfig[providerKey]) {
881
+ currentProvider = providerKey;
882
+ process.env[provider.envKey] = savedConfig[providerKey];
883
+ savedProvider = providerKey;
884
+ break;
885
+ }
886
+ }
877
887
  }
878
888
  }
879
889
  } catch (e) {}
890
+ const _initialProvider = PROVIDERS[currentProvider];
880
891
  const _initialKey = process.env[_initialProvider.envKey] || "no-key";
881
892
  let _internalClient = new OpenAI({
882
893
  apiKey: _initialKey,
@@ -4686,6 +4697,7 @@ function App() {
4686
4697
  });
4687
4698
  }
4688
4699
  }, []);
4700
+ const showConfig = state.needsConfig || process.env.APEX_DEV_NEEDS_CONFIG === "true";
4689
4701
  return /* @__PURE__ */ jsx_runtime15.jsxs("box", {
4690
4702
  style: { flexDirection: "column", flexGrow: 1 },
4691
4703
  children: [
@@ -4709,6 +4721,7 @@ function App() {
4709
4721
  onClose: () => import_store5.setState({ showHelp: false }),
4710
4722
  onCommand: handleHelpCommand
4711
4723
  }) : null,
4724
+ showConfig ? /* @__PURE__ */ jsx_runtime15.jsx(globalThis._ApiKeyModal, {}) : null,
4712
4725
  state.needsConfig ? /* @__PURE__ */ jsx_runtime15.jsx(globalThis._ProviderSelector, {}) : null
4713
4726
  ]
4714
4727
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-dev",
3
- "version": "3.10.19",
3
+ "version": "3.10.21",
4
4
  "description": "Apex AI - a friendly agentic coding assistant for the terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {