servyx-cli 0.1.12 → 0.1.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/lib/api.js +67 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "servyx-cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Servyx CLI — Upload local projects and deploy via the Servyx platform",
5
5
  "type": "module",
6
6
  "bin": {
package/src/lib/api.js CHANGED
@@ -64,18 +64,18 @@ function logApiResponse(response) {
64
64
  }
65
65
 
66
66
  export function logApiError({ method = 'REQUEST', url = 'unknown', err = null, response = null } = {}) {
67
- console.error(pc.yellow('--- Servyx API error ---'));
68
- console.error(pc.dim(`${method} ${url}`));
69
- console.error(pc.dim(`API base: ${getApiBaseUrl()}`));
67
+ // console.error(pc.yellow('--- Servyx API error ---'));
68
+ // console.error(pc.dim(`${method} ${url}`));
69
+ // console.error(pc.dim(`API base: ${getApiBaseUrl()}`));
70
70
 
71
71
  if (response) {
72
- console.error(pc.dim(`Status: ${response.status} ${response.statusText || ''}`));
72
+ // console.error(pc.dim(`Status: ${response.status} ${response.statusText || ''}`));
73
73
  const headers = response.headers || {};
74
74
  const contentType = headers['content-type'] || headers['Content-Type'];
75
75
  if (contentType) {
76
- console.error(pc.dim(`Content-Type: ${contentType}`));
76
+ // console.error(pc.dim(`Content-Type: ${contentType}`));
77
77
  }
78
- console.error(pc.dim(`Response body: ${summarizeBody(response.data)}`));
78
+ // console.error(pc.dim(`Response body: ${summarizeBody(response.data)}`));
79
79
  }
80
80
 
81
81
  if (err) {
@@ -86,9 +86,9 @@ export function logApiError({ method = 'REQUEST', url = 'unknown', err = null, r
86
86
  if (err.message) console.error(pc.dim(`Message: ${err.message}`));
87
87
  if (err.config && !response) {
88
88
  const reqUrl = err.config._servyxFullUrl || buildFullUrl(err.config.baseURL, err.config.url);
89
- console.error(pc.dim(`Request: ${err.config.method?.toUpperCase() || 'REQUEST'} ${reqUrl}`));
89
+ // console.error(pc.dim(`Request: ${err.config.method?.toUpperCase() || 'REQUEST'} ${reqUrl}`));
90
90
  if (err.config.data != null) {
91
- console.error(pc.dim(`Request body: ${summarizeBody(err.config.data)}`));
91
+ // console.error(pc.dim(`Request body: ${summarizeBody(err.config.data)}`));
92
92
  }
93
93
  }
94
94
  }
@@ -126,11 +126,37 @@ function attachLogging(client) {
126
126
  );
127
127
  }
128
128
 
129
- function createClient(token = getToken()) {
129
+ const START_DEPLOY_TIMEOUT_MS = 180000;
130
+ const NETWORK_RETRY_CODES = new Set(['ECONNRESET', 'ETIMEDOUT', 'ECONNABORTED']);
131
+
132
+ function sleep(ms) {
133
+ return new Promise((resolve) => setTimeout(resolve, ms));
134
+ }
135
+
136
+ async function findRecentActiveDeployment(appId, maxAgeMs = 120000) {
137
+ try {
138
+ const client = createClient();
139
+ const response = await client.get(`/apps/${appId}/deployments`, { params: { limit: 5 } });
140
+ if (response.status < 200 || response.status >= 300) return null;
141
+
142
+ const deployments = response.data?.data?.deployments || [];
143
+ const cutoff = Date.now() - maxAgeMs;
144
+
145
+ return deployments.find((deployment) => {
146
+ if (!['pending', 'in_progress'].includes(deployment.status)) return false;
147
+ const createdAt = deployment.created_at ? new Date(deployment.created_at).getTime() : 0;
148
+ return createdAt >= cutoff;
149
+ }) || null;
150
+ } catch {
151
+ return null;
152
+ }
153
+ }
154
+
155
+ function createClient(token = getToken(), { timeout = 120000 } = {}) {
130
156
  const baseURL = `${getApiBaseUrl()}/api/v1/cli`;
131
157
  const client = axios.create({
132
158
  baseURL,
133
- timeout: 120000,
159
+ timeout,
134
160
  headers: {
135
161
  'Content-Type': 'application/json',
136
162
  ...(token ? { Authorization: `Bearer ${token}` } : {}),
@@ -152,7 +178,9 @@ function mapNetworkError(err, meta = {}) {
152
178
  });
153
179
  }
154
180
  const hint = new Error(
155
- `Cannot reach Servyx API at ${getApiBaseUrl()} (${err.code}). Is cli-service running?`,
181
+ err.code === 'ECONNRESET'
182
+ ? `Connection to ${getApiBaseUrl()} was reset (${err.code}). The deploy may still have started — retry status polling or check the dashboard. Common when redeploying cli-service itself.`
183
+ : `Cannot reach Servyx API at ${getApiBaseUrl()} (${err.code}). Is cli-service running?`,
156
184
  );
157
185
  hint.code = err.code;
158
186
  throw hint;
@@ -205,14 +233,39 @@ export async function startDeployment(appId, message, { withEnv = false } = {})
205
233
  message,
206
234
  with_env: withEnv || undefined,
207
235
  };
236
+ const url = buildFullUrl(`${getApiBaseUrl()}/api/v1/cli`, path);
237
+ const client = createClient(undefined, { timeout: START_DEPLOY_TIMEOUT_MS });
238
+
239
+ const postDeploy = () => client.post(path, body);
240
+
208
241
  try {
209
- const client = createClient();
210
- return unwrap(await client.post(path, body));
242
+ return unwrap(await postDeploy());
211
243
  } catch (err) {
244
+ if (NETWORK_RETRY_CODES.has(err?.code)) {
245
+ await sleep(1500);
246
+ try {
247
+ return unwrap(await postDeploy());
248
+ } catch (retryErr) {
249
+ err = retryErr;
250
+ }
251
+ }
252
+
253
+ if (NETWORK_RETRY_CODES.has(err?.code)) {
254
+ const deployment = await findRecentActiveDeployment(appId);
255
+ if (deployment) {
256
+ console.error(pc.yellow('Connection dropped after deploy was queued; continuing with the active deployment.'));
257
+ return {
258
+ success: true,
259
+ data: { deployment, queue_position: 1 },
260
+ message: 'CLI deployment queued',
261
+ };
262
+ }
263
+ }
264
+
212
265
  mapNetworkError(err, {
213
266
  method: 'POST',
214
267
  path,
215
- url: buildFullUrl(`${getApiBaseUrl()}/api/v1/cli`, path),
268
+ url,
216
269
  });
217
270
  }
218
271
  }