@anna-ai/cli 0.1.41 → 0.1.42

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.
@@ -29,9 +29,16 @@ const CONTENT_TYPES = {
29
29
  md: "text/markdown"
30
30
  };
31
31
  const MAX_TOTAL_BYTES = 1024 * 1024 * 1024;
32
- const MAX_FILE_BYTES = 100 * 1024 * 1024;
32
+ const MAX_FILE_BYTES = 50 * 1024 * 1024;
33
33
  const MAX_FILES = 2e3;
34
34
  const UPLOAD_PARALLEL = 4;
35
+ const fmtMB = (n) => (n / 1024 / 1024).toFixed(2);
36
+ function totalSizeError(totalBytes) {
37
+ return new CliError(`bundle is ${fmtMB(totalBytes)} MB — exceeds the ${fmtMB(MAX_TOTAL_BYTES)} MB publish limit.\n Tip: preview/screenshot images usually shrink 5-10x when exported as WebP/AVIF\n thumbnails sized for their on-screen dimensions. Unchanged files are deduplicated\n server-side, so only new bytes upload on re-publish. Assets that must stay large\n belong in executa binary distribution, not the UI bundle.`, 4);
38
+ }
39
+ function fileSizeError(relativePath, byteSize) {
40
+ return new CliError(`bundle file exceeds the ${fmtMB(MAX_FILE_BYTES)} MB per-file limit: ${relativePath} (${fmtMB(byteSize)} MB).\n Compress/split the asset, or ship it via executa binary distribution.`, 4);
41
+ }
35
42
  function guessContentType(name) {
36
43
  const m = /\.([a-z0-9]+)$/i.exec(name);
37
44
  if (!m) return "application/octet-stream";
@@ -88,9 +95,9 @@ async function uploadAppBundle(params) {
88
95
  if (files.length === 0) throw new CliError(`bundle directory is empty: ${bundleDir}`, 4);
89
96
  if (files.length > MAX_FILES) throw new CliError(`too many bundle files (${files.length} > ${MAX_FILES})`, 4);
90
97
  const totalBytes = files.reduce((s, f) => s + f.entry.byte_size, 0);
91
- if (totalBytes > MAX_TOTAL_BYTES) throw new CliError(`bundle is ${(totalBytes / 1024 / 1024).toFixed(2)} MB — exceeds 50 MB limit`, 4);
98
+ if (totalBytes > MAX_TOTAL_BYTES) throw totalSizeError(totalBytes);
92
99
  const tooBig = files.find((f) => f.entry.byte_size > MAX_FILE_BYTES);
93
- if (tooBig) throw new CliError(`bundle file exceeds 10 MB: ${tooBig.relativePath} (${(tooBig.entry.byte_size / 1024 / 1024).toFixed(2)} MB)`, 4);
100
+ if (tooBig) throw fileSizeError(tooBig.relativePath, tooBig.entry.byte_size);
94
101
  if (!files.some((f) => f.relativePath === entryPath)) throw new CliError(`manifest entry "${entryPath}" not found in bundle dir ${bundleDir}`, 4);
95
102
  const fingerprint = manifestFingerprint(files);
96
103
  const existing = await getBundle(client, appId, versionId);
@@ -117,7 +124,10 @@ async function uploadAppBundle(params) {
117
124
  external_origins: externalOrigins
118
125
  });
119
126
  const proxyByPath = new Map(initResp.files.map((p) => [p.relative_path, p.proxy_upload_url]));
120
- const queue = [...files];
127
+ const skipSet = new Set(initResp.files.filter((p) => p.skip_upload).map((p) => p.relative_path));
128
+ const toUpload = files.filter((f) => !skipSet.has(f.relativePath));
129
+ if (skipSet.size > 0) log?.(` ♻ ${skipSet.size}/${files.length} files unchanged on server — skipped (dedup)`);
130
+ const queue = [...toUpload];
121
131
  const uploadOne = async () => {
122
132
  for (;;) {
123
133
  const f = queue.shift();
@@ -129,7 +139,7 @@ async function uploadAppBundle(params) {
129
139
  await uploadBundleFile(client, appId, versionId, f.relativePath, blob);
130
140
  }
131
141
  };
132
- await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, files.length) }, uploadOne));
142
+ await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, toUpload.length) }, uploadOne));
133
143
  const detail = await finalizeBundle(client, appId, versionId, false);
134
144
  log?.(` ✓ bundle finalized (${detail.file_count} files, status=${detail.status})`);
135
145
  return {
@@ -158,9 +168,9 @@ async function uploadWorkingBundle(params) {
158
168
  if (files.length === 0) throw new CliError(`bundle directory is empty: ${bundleDir}`, 4);
159
169
  if (files.length > MAX_FILES) throw new CliError(`too many bundle files (${files.length} > ${MAX_FILES})`, 4);
160
170
  const totalBytes = files.reduce((s, f) => s + f.entry.byte_size, 0);
161
- if (totalBytes > MAX_TOTAL_BYTES) throw new CliError(`bundle is ${(totalBytes / 1024 / 1024).toFixed(2)} MB — exceeds 50 MB limit`, 4);
171
+ if (totalBytes > MAX_TOTAL_BYTES) throw totalSizeError(totalBytes);
162
172
  const tooBig = files.find((f) => f.entry.byte_size > MAX_FILE_BYTES);
163
- if (tooBig) throw new CliError(`bundle file exceeds 10 MB: ${tooBig.relativePath} (${(tooBig.entry.byte_size / 1024 / 1024).toFixed(2)} MB)`, 4);
173
+ if (tooBig) throw fileSizeError(tooBig.relativePath, tooBig.entry.byte_size);
164
174
  if (!files.some((f) => f.relativePath === entryPath)) throw new CliError(`manifest entry "${entryPath}" not found in bundle dir ${bundleDir}`, 4);
165
175
  const fingerprint = manifestFingerprint(files);
166
176
  if (knownFingerprint && knownFingerprint === fingerprint) {
@@ -185,7 +195,10 @@ async function uploadWorkingBundle(params) {
185
195
  external_origins: externalOrigins
186
196
  });
187
197
  const proxyByPath = new Map(initResp.files.map((p) => [p.relative_path, p.proxy_upload_url]));
188
- const queue = [...files];
198
+ const skipSet = new Set(initResp.files.filter((p) => p.skip_upload).map((p) => p.relative_path));
199
+ const toUpload = files.filter((f) => !skipSet.has(f.relativePath));
200
+ if (skipSet.size > 0) log?.(` ♻ ${skipSet.size}/${files.length} files unchanged on server — skipped (dedup)`);
201
+ const queue = [...toUpload];
189
202
  const uploadOne = async () => {
190
203
  for (;;) {
191
204
  const f = queue.shift();
@@ -197,7 +210,7 @@ async function uploadWorkingBundle(params) {
197
210
  await uploadWorkingBundleFile(client, appId, f.relativePath, blob);
198
211
  }
199
212
  };
200
- await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, files.length) }, uploadOne));
213
+ await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, toUpload.length) }, uploadOne));
201
214
  const detail = await finalizeWorkingBundle(client, appId, false);
202
215
  log?.(` ✓ working bundle staged (${detail.file_count} files, status=${detail.bundle_status})`);
203
216
  return {
@@ -7,7 +7,7 @@ import { loadAppManifest } from "./manifest-CAF3_r1T.js";
7
7
  import { bumpVersion, bundleHash, manifestHash, rewriteVersion, runExecutaPublish } from "./executa-publish-DueD_0hh.js";
8
8
  import { appCacheMatches, readAppIdentity, writeAppIdentity } from "./app-cache-Cr3tKbux.js";
9
9
  import { syncAppListingMeta } from "./listing-meta-CtVtyaWu.js";
10
- import { isExistingDir, uploadAppBundle } from "./app-bundle-upload-B6TJeyOq.js";
10
+ import { isExistingDir, uploadAppBundle } from "./app-bundle-upload-DgoZIhQG.js";
11
11
  import { resolve } from "node:path";
12
12
  import { bold, cyan, dim, green, yellow } from "kleur/colors";
13
13
 
@@ -11,7 +11,7 @@ import "./binary-upload-CKMU5Rfw.js";
11
11
  import "./executa-upload-binaries-DURJTXRI.js";
12
12
  import "./app-cache-Cr3tKbux.js";
13
13
  import "./listing-meta-CtVtyaWu.js";
14
- import "./app-bundle-upload-B6TJeyOq.js";
15
- import { runAppsPublish } from "./apps-publish-bcqO88S0.js";
14
+ import "./app-bundle-upload-DgoZIhQG.js";
15
+ import { runAppsPublish } from "./apps-publish-l4itT8Dk.js";
16
16
 
17
17
  export { runAppsPublish };
@@ -15,7 +15,7 @@ import "./executa-install-jSVVx6oh.js";
15
15
  import "./app-cache-Cr3tKbux.js";
16
16
  import { installLocalBundledShims, resolveAppIdentity, resolveBundledExecutas } from "./working-orchestration-oFZwsWqQ.js";
17
17
  import { syncAppListingMeta } from "./listing-meta-CtVtyaWu.js";
18
- import { isExistingDir, uploadWorkingBundle } from "./app-bundle-upload-B6TJeyOq.js";
18
+ import { isExistingDir, uploadWorkingBundle } from "./app-bundle-upload-DgoZIhQG.js";
19
19
  import { resolve } from "node:path";
20
20
  import { bold, cyan, dim, green, yellow } from "kleur/colors";
21
21
 
@@ -59,7 +59,7 @@ async function runAppsRelease(opts) {
59
59
  if (!target) {
60
60
  if (opts.allowCreate) {
61
61
  if (!opts.json) console.log(yellow(`version ${opts.version} not found remotely; running 'apps publish' first (--allow-create)…`));
62
- const { runAppsPublish } = await import("./apps-publish-DnU-B7YF.js");
62
+ const { runAppsPublish } = await import("./apps-publish-z0YSOUL0.js");
63
63
  const code = await runAppsPublish({
64
64
  cwd: opts.cwd,
65
65
  account: opts.account,
package/dist/cli.js CHANGED
@@ -671,7 +671,7 @@ apps.command("publish").description("Publish or update an Anna App (manifest.jso
671
671
  console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
672
672
  process.exit(2);
673
673
  }
674
- const { runAppsPublish } = await import("./apps-publish-DnU-B7YF.js");
674
+ const { runAppsPublish } = await import("./apps-publish-z0YSOUL0.js");
675
675
  process.exit(await runAppsPublish({
676
676
  cwd: opts.cwd,
677
677
  manifest: opts.manifest,
@@ -705,7 +705,7 @@ apps.command("push").description("Upsert the mutable working draft (manifest + b
705
705
  process.exit(2);
706
706
  }
707
707
  }
708
- const { runAppsPush } = await import("./apps-push-CnZdmaYm.js");
708
+ const { runAppsPush } = await import("./apps-push-CDLYRDt8.js");
709
709
  process.exit(await runAppsPush({
710
710
  cwd: opts.cwd,
711
711
  manifest: opts.manifest,
@@ -747,7 +747,7 @@ apps.command("discard").description("Drop the mutable working draft (leaves cut
747
747
  }));
748
748
  });
749
749
  apps.command("release <version>").description("Freeze & publish an existing remote version (go live)").option("--slug <slug>", "App slug (default: resolve from .anna/app.json cache)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--allow-create", "If the version isn't on the server, run 'apps publish' first, then release", false).option("--dry-run", "Resolve target + pre-flight but don't publish", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (version, opts) => {
750
- const { runAppsRelease } = await import("./apps-release-CX7FtoOH.js");
750
+ const { runAppsRelease } = await import("./apps-release-DlXBYdbb.js");
751
751
  process.exit(await runAppsRelease({
752
752
  version,
753
753
  slug: opts.slug,
@@ -1001,7 +1001,7 @@ program.command("publish").description("Auto-detect cwd and publish (apps publis
1001
1001
  console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
1002
1002
  process.exit(2);
1003
1003
  }
1004
- const { runTopLevelPublish } = await import("./publish-BQL5NDnd.js");
1004
+ const { runTopLevelPublish } = await import("./publish-BC5dFF_P.js");
1005
1005
  process.exit(await runTopLevelPublish({
1006
1006
  cwd: opts.cwd,
1007
1007
  bump: opts.bump,
@@ -11,8 +11,8 @@ import "./binary-upload-CKMU5Rfw.js";
11
11
  import "./executa-upload-binaries-DURJTXRI.js";
12
12
  import "./app-cache-Cr3tKbux.js";
13
13
  import "./listing-meta-CtVtyaWu.js";
14
- import "./app-bundle-upload-B6TJeyOq.js";
15
- import { runAppsPublish } from "./apps-publish-bcqO88S0.js";
14
+ import "./app-bundle-upload-DgoZIhQG.js";
15
+ import { runAppsPublish } from "./apps-publish-l4itT8Dk.js";
16
16
  import { resolve } from "node:path";
17
17
  import { existsSync } from "node:fs";
18
18
  import { red, yellow } from "kleur/colors";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anna-ai/cli",
3
- "version": "0.1.41",
3
+ "version": "0.1.42",
4
4
  "description": "Anna App developer CLI: scaffold, validate, harness (Phase 2 MVP: init + validate).",
5
5
  "license": "MIT",
6
6
  "type": "module",