@apso/cli 0.32.0 → 0.33.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.
@@ -287,7 +287,14 @@ async function apiRequest(path, options = {}) {
287
287
  }
288
288
  // Handle server errors with retry
289
289
  if (response.status >= 500) {
290
- lastError = new ApiClientError(errorData.message || "Server error", response.status, errorData.details);
290
+ // The BFF returns its message in `.error`; the backend uses `.message`.
291
+ const errObj = errorData;
292
+ const serverMsg = errorData.message ||
293
+ (typeof errObj.error === "string"
294
+ ? errObj.error
295
+ : undefined) ||
296
+ "Server error";
297
+ lastError = new ApiClientError(serverMsg, response.status, errorData.details);
291
298
  await sleep(getBackoffDelay(attempt));
292
299
  continue;
293
300
  }
@@ -138,5 +138,16 @@ export declare const codeApi: {
138
138
  bucket?: string;
139
139
  key?: string;
140
140
  }>;
141
+ /**
142
+ * Record S3 metadata after the presigned PUT completes. Without this the
143
+ * service row's s3_code_key stays null and a server-side push fails with
144
+ * "No S3 code found". Mirrors the browser upload flow (upload-url -> PUT ->
145
+ * finalize).
146
+ */
147
+ finalize(serviceId: string): Promise<{
148
+ success: boolean;
149
+ version?: string;
150
+ size?: number;
151
+ }>;
141
152
  };
142
153
  export {};
@@ -377,4 +377,13 @@ exports.codeApi = {
377
377
  async getUploadUrl(serviceId) {
378
378
  return client_1.api.get(`/api/services/${serviceId}/code/upload-url`);
379
379
  },
380
+ /**
381
+ * Record S3 metadata after the presigned PUT completes. Without this the
382
+ * service row's s3_code_key stays null and a server-side push fails with
383
+ * "No S3 code found". Mirrors the browser upload flow (upload-url -> PUT ->
384
+ * finalize).
385
+ */
386
+ async finalize(serviceId) {
387
+ return client_1.api.post(`/api/services/${serviceId}/code/finalize`, {});
388
+ },
380
389
  };
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
- /** Zip the project source into a buffer, skipping build/vcs/dependency dirs. */
3
+ /** Zip the project source into a buffer, skipping build/vcs/dependency/secret files. */
4
4
  export declare function zipProject(cwd: string): Buffer;
5
5
  /**
6
6
  * Server-side push: upload the project zip to S3 and have the platform push it
@@ -15,12 +15,28 @@ const EXCLUDE_DIRS = new Set([
15
15
  ".apso",
16
16
  "coverage",
17
17
  ]);
18
- /** Zip the project source into a buffer, skipping build/vcs/dependency dirs. */
18
+ /**
19
+ * Never push local secrets to the repo. `.env` and `.env.*` (e.g. `.env.local`)
20
+ * routinely hold DB passwords and session secrets; `.env.example` is the one
21
+ * env file that is meant to be committed. The backend injects real production
22
+ * env at runtime, so these are never needed in the pushed code.
23
+ */
24
+ function isSecretEnvFile(basename) {
25
+ if (basename === ".env.example" || basename === ".env.sample")
26
+ return false;
27
+ return basename === ".env" || basename.startsWith(".env.");
28
+ }
29
+ /** Zip the project source into a buffer, skipping build/vcs/dependency/secret files. */
19
30
  function zipProject(cwd) {
20
31
  const zip = new adm_zip_1.default();
21
32
  zip.addLocalFolder(cwd, "", (filename) => {
22
33
  const parts = filename.split(path.sep);
23
- return !parts.some((p) => EXCLUDE_DIRS.has(p));
34
+ if (parts.some((p) => EXCLUDE_DIRS.has(p)))
35
+ return false;
36
+ const base = parts[parts.length - 1];
37
+ if (isSecretEnvFile(base))
38
+ return false;
39
+ return true;
24
40
  });
25
41
  return zip.toBuffer();
26
42
  }
@@ -41,6 +57,11 @@ async function serverSidePush(opts) {
41
57
  if (!res.ok) {
42
58
  throw new Error(`Code upload failed (${res.status}). ${await res.text().catch(() => "")}`);
43
59
  }
60
+ // Record the S3 metadata (bucket/key/version) on the service row. The
61
+ // presigned PUT lands the zip in S3 but does NOT persist where it went; the
62
+ // backend push reads s3_code_key from the DB, so without this it fails with
63
+ // "No S3 code found". Same three-step flow the browser upload uses.
64
+ await services_1.codeApi.finalize(opts.serviceId);
44
65
  await services_1.githubApi.push(opts.serviceId, opts.connectionId, {
45
66
  branch: opts.branch,
46
67
  message: opts.message,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@apso/cli",
9
- "version": "0.32.0",
9
+ "version": "0.33.1",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@biomejs/biome": "^1.9.4",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.32.0",
2
+ "version": "0.33.1",
3
3
  "commands": {
4
4
  "config": {
5
5
  "id": "config",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
4
4
  "mcpName": "io.github.apsoai/apso",
5
5
  "description": "Apso CLI",
6
6
  "author": "Apso by Mavric - @mavric",