@camstack/agent 1.1.42 → 1.1.44

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/dist/index.mjs CHANGED
@@ -5,8 +5,8 @@ import {
5
5
  loadAgentConfig,
6
6
  startAgent,
7
7
  startAgentHttpServer
8
- } from "./chunk-MMKRM4RT.mjs";
9
- import "./chunk-L34OOQI3.mjs";
8
+ } from "./chunk-Z7VZQCFX.mjs";
9
+ import "./chunk-Y6BUFOWG.mjs";
10
10
  export {
11
11
  createAgentHttpServer,
12
12
  createAgentService,
package/dist/starter.js CHANGED
@@ -59,6 +59,9 @@ var require_dist = __commonJS({
59
59
  var src_exports = {};
60
60
  __export(src_exports, {
61
61
  AGENT_ROOT_SPEC: () => AGENT_ROOT_SPEC2,
62
+ DEV_UPLOADS_DIRNAME: () => DEV_UPLOADS_DIRNAME,
63
+ DEV_UPLOADS_KEEP_COUNT: () => DEV_UPLOADS_KEEP_COUNT,
64
+ DEV_UPLOAD_MANIFEST_FILE: () => DEV_UPLOAD_MANIFEST_FILE,
62
65
  HOST_EXTERNAL_SPECIFIERS: () => HOST_EXTERNAL_SPECIFIERS,
63
66
  HUB_ROOT_SPEC: () => HUB_ROOT_SPEC,
64
67
  RESTART_INTENT_FILE: () => RESTART_INTENT_FILE,
@@ -68,11 +71,17 @@ var require_dist = __commonJS({
68
71
  clearRestartIntentMarker: () => clearRestartIntentMarker,
69
72
  compareSemver: () => compareSemver,
70
73
  detectWorkspaceRoot: () => detectWorkspaceRoot,
74
+ devChannelEpoch: () => devChannelEpoch,
75
+ devUploadManifestPath: () => devUploadManifestPath,
76
+ devUploadVersionDir: () => devUploadVersionDir,
77
+ devUploadsDir: () => devUploadsDir,
71
78
  emptyServerRootState: () => emptyServerRootState,
79
+ isDevChannelVersion: () => isDevChannelVersion,
72
80
  isHostExternal: () => isHostExternal,
73
81
  isServerRootState: () => isServerRootState,
74
82
  minNodeMajorOf: () => minNodeMajorOf,
75
83
  planBoot: () => planBoot,
84
+ readDevUploadManifest: () => readDevUploadManifest,
76
85
  readRestartIntentMarker: () => readRestartIntentMarker,
77
86
  readServerRootState: () => readServerRootState,
78
87
  registerActiveRootResolver: () => registerActiveRootResolver,
@@ -85,10 +94,58 @@ var require_dist = __commonJS({
85
94
  validateVersionDir: () => validateVersionDir,
86
95
  versionDir: () => versionDir,
87
96
  versionsDir: () => versionsDir,
97
+ writeDevUploadManifest: () => writeDevUploadManifest,
88
98
  writeRestartIntentMarker: () => writeRestartIntentMarker,
89
99
  writeServerRootState: () => writeServerRootState
90
100
  });
91
101
  module2.exports = __toCommonJS(src_exports);
102
+ var fs = __toESM2(require("fs"));
103
+ var path2 = __toESM2(require("path"));
104
+ var DEV_UPLOADS_DIRNAME = "dev-uploads";
105
+ var DEV_UPLOAD_MANIFEST_FILE = "manifest.json";
106
+ var DEV_UPLOADS_KEEP_COUNT = 3;
107
+ var DEV_CHANNEL_VERSION_RE = /-dev\.\d+$/;
108
+ function isDevChannelVersion(version) {
109
+ return DEV_CHANNEL_VERSION_RE.test(version);
110
+ }
111
+ function devChannelEpoch(version) {
112
+ const match = /-dev\.(\d+)$/.exec(version);
113
+ if (match === null) return null;
114
+ const epoch = Number.parseInt(match[1] ?? "", 10);
115
+ return Number.isNaN(epoch) ? null : epoch;
116
+ }
117
+ function devUploadsDir(rootDir) {
118
+ return path2.join(rootDir, DEV_UPLOADS_DIRNAME);
119
+ }
120
+ function devUploadVersionDir(rootDir, version) {
121
+ return path2.join(devUploadsDir(rootDir), version);
122
+ }
123
+ function devUploadManifestPath(versionDirPath) {
124
+ return path2.join(versionDirPath, DEV_UPLOAD_MANIFEST_FILE);
125
+ }
126
+ function isDevUploadManifest(v) {
127
+ if (typeof v !== "object" || v === null) return false;
128
+ const m = v;
129
+ if (typeof m["version"] !== "string") return false;
130
+ const packages = m["packages"];
131
+ if (typeof packages !== "object" || packages === null || Array.isArray(packages)) return false;
132
+ return Object.values(packages).every((filename) => typeof filename === "string");
133
+ }
134
+ function readDevUploadManifest(versionDirPath) {
135
+ try {
136
+ const raw = JSON.parse(fs.readFileSync(devUploadManifestPath(versionDirPath), "utf-8"));
137
+ return isDevUploadManifest(raw) ? raw : null;
138
+ } catch {
139
+ return null;
140
+ }
141
+ }
142
+ function writeDevUploadManifest(versionDirPath, manifest) {
143
+ fs.mkdirSync(versionDirPath, { recursive: true });
144
+ const target = devUploadManifestPath(versionDirPath);
145
+ const tmp = `${target}.tmp`;
146
+ fs.writeFileSync(tmp, JSON.stringify(manifest, null, 2), "utf-8");
147
+ fs.renameSync(tmp, target);
148
+ }
92
149
  var HUB_ROOT_SPEC = {
93
150
  packageName: "@camstack/server",
94
151
  entryRelPath: ["dist", "launcher.js"]
@@ -97,8 +154,8 @@ var require_dist = __commonJS({
97
154
  packageName: "@camstack/agent",
98
155
  entryRelPath: ["dist", "cli.js"]
99
156
  };
100
- var fs = __toESM2(require("fs"));
101
- var path2 = __toESM2(require("path"));
157
+ var fs2 = __toESM2(require("fs"));
158
+ var path22 = __toESM2(require("path"));
102
159
  var SERVER_ROOT_DIRNAME = "server-root";
103
160
  var SERVER_ROOT_STATE_FILE = "state.json";
104
161
  var RESTART_INTENT_FILE = ".restart-intent";
@@ -112,22 +169,22 @@ var require_dist = __commonJS({
112
169
  };
113
170
  }
114
171
  function serverRootDir(dataDir) {
115
- return path2.join(dataDir, SERVER_ROOT_DIRNAME);
172
+ return path22.join(dataDir, SERVER_ROOT_DIRNAME);
116
173
  }
117
174
  function versionsDir(rootDir) {
118
- return path2.join(rootDir, "versions");
175
+ return path22.join(rootDir, "versions");
119
176
  }
120
177
  function versionDir(rootDir, version) {
121
- return path2.join(versionsDir(rootDir), version);
178
+ return path22.join(versionsDir(rootDir), version);
122
179
  }
123
180
  function rootPackageDir(versionDirPath, spec) {
124
- return path2.join(versionDirPath, "node_modules", ...spec.packageName.split("/"));
181
+ return path22.join(versionDirPath, "node_modules", ...spec.packageName.split("/"));
125
182
  }
126
183
  function rootEntryPath(versionDirPath, spec) {
127
- return path2.join(rootPackageDir(versionDirPath, spec), ...spec.entryRelPath);
184
+ return path22.join(rootPackageDir(versionDirPath, spec), ...spec.entryRelPath);
128
185
  }
129
186
  function stateFilePath(rootDir) {
130
- return path2.join(rootDir, SERVER_ROOT_STATE_FILE);
187
+ return path22.join(rootDir, SERVER_ROOT_STATE_FILE);
131
188
  }
132
189
  function isNullableString(v) {
133
190
  return v === null || typeof v === "string";
@@ -154,21 +211,21 @@ var require_dist = __commonJS({
154
211
  }
155
212
  function readServerRootState(rootDir) {
156
213
  try {
157
- const raw = JSON.parse(fs.readFileSync(stateFilePath(rootDir), "utf-8"));
214
+ const raw = JSON.parse(fs2.readFileSync(stateFilePath(rootDir), "utf-8"));
158
215
  return isServerRootState(raw) ? raw : null;
159
216
  } catch {
160
217
  return null;
161
218
  }
162
219
  }
163
220
  function writeServerRootState(rootDir, state) {
164
- fs.mkdirSync(rootDir, { recursive: true });
221
+ fs2.mkdirSync(rootDir, { recursive: true });
165
222
  const target = stateFilePath(rootDir);
166
223
  const tmp = `${target}.tmp`;
167
- fs.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
168
- fs.renameSync(tmp, target);
224
+ fs2.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
225
+ fs2.renameSync(tmp, target);
169
226
  }
170
227
  function restartIntentMarkerPath(rootDir) {
171
- return path2.join(rootDir, RESTART_INTENT_FILE);
228
+ return path22.join(rootDir, RESTART_INTENT_FILE);
172
229
  }
173
230
  function isRestartIntentMarker(v) {
174
231
  if (typeof v !== "object" || v === null) return false;
@@ -176,15 +233,15 @@ var require_dist = __commonJS({
176
233
  return typeof m["requestedAtMs"] === "number" && typeof m["reason"] === "string";
177
234
  }
178
235
  function writeRestartIntentMarker(rootDir, marker) {
179
- fs.mkdirSync(rootDir, { recursive: true });
236
+ fs2.mkdirSync(rootDir, { recursive: true });
180
237
  const target = restartIntentMarkerPath(rootDir);
181
238
  const tmp = `${target}.tmp`;
182
- fs.writeFileSync(tmp, JSON.stringify(marker, null, 2), "utf-8");
183
- fs.renameSync(tmp, target);
239
+ fs2.writeFileSync(tmp, JSON.stringify(marker, null, 2), "utf-8");
240
+ fs2.renameSync(tmp, target);
184
241
  }
185
242
  function readRestartIntentMarker(rootDir) {
186
243
  try {
187
- const raw = JSON.parse(fs.readFileSync(restartIntentMarkerPath(rootDir), "utf-8"));
244
+ const raw = JSON.parse(fs2.readFileSync(restartIntentMarkerPath(rootDir), "utf-8"));
188
245
  return isRestartIntentMarker(raw) ? raw : null;
189
246
  } catch {
190
247
  return null;
@@ -192,7 +249,7 @@ var require_dist = __commonJS({
192
249
  }
193
250
  function clearRestartIntentMarker(rootDir) {
194
251
  try {
195
- fs.rmSync(restartIntentMarkerPath(rootDir), { force: true });
252
+ fs2.rmSync(restartIntentMarkerPath(rootDir), { force: true });
196
253
  } catch {
197
254
  }
198
255
  }
@@ -205,13 +262,13 @@ var require_dist = __commonJS({
205
262
  function validateVersionDir(rootDir, version, nodeMajor, spec) {
206
263
  const vDir = versionDir(rootDir, version);
207
264
  const entry = rootEntryPath(vDir, spec);
208
- if (!fs.existsSync(entry)) {
265
+ if (!fs2.existsSync(entry)) {
209
266
  return `root entry missing: ${entry}`;
210
267
  }
211
- const pkgJsonPath = path2.join(rootPackageDir(vDir, spec), "package.json");
268
+ const pkgJsonPath = path22.join(rootPackageDir(vDir, spec), "package.json");
212
269
  let pkg;
213
270
  try {
214
- const raw = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
271
+ const raw = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf-8"));
215
272
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
216
273
  return `package.json malformed: ${pkgJsonPath}`;
217
274
  }
@@ -358,25 +415,25 @@ var require_dist = __commonJS({
358
415
  if (pa.prerelease > pb.prerelease) return 1;
359
416
  return 0;
360
417
  }
361
- var fs2 = __toESM2(require("fs"));
362
- var path22 = __toESM2(require("path"));
418
+ var fs3 = __toESM2(require("fs"));
419
+ var path3 = __toESM2(require("path"));
363
420
  function detectWorkspaceRoot(fromDir) {
364
- let dir = path22.resolve(fromDir);
421
+ let dir = path3.resolve(fromDir);
365
422
  for (; ; ) {
366
- const pkgPath = path22.join(dir, "package.json");
423
+ const pkgPath = path3.join(dir, "package.json");
367
424
  try {
368
- const raw = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
425
+ const raw = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
369
426
  if (typeof raw === "object" && raw !== null && raw["workspaces"] !== void 0) {
370
427
  return dir;
371
428
  }
372
429
  } catch {
373
430
  }
374
- const parent = path22.dirname(dir);
431
+ const parent = path3.dirname(dir);
375
432
  if (parent === dir) return null;
376
433
  dir = parent;
377
434
  }
378
435
  }
379
- var path3 = __toESM2(require("path"));
436
+ var path4 = __toESM2(require("path"));
380
437
  var import_node_url = require("url");
381
438
  var HOST_EXTERNAL_SPECIFIERS = [
382
439
  "@camstack/system",
@@ -404,7 +461,7 @@ var require_dist = __commonJS({
404
461
  return;
405
462
  }
406
463
  const anchorURL = (0, import_node_url.pathToFileURL)(
407
- path3.join(activeRootDir, "node_modules", "__camstack_starter_anchor__.js")
464
+ path4.join(activeRootDir, "node_modules", "__camstack_starter_anchor__.js")
408
465
  ).href;
409
466
  const hooks = {
410
467
  resolve: (specifier, context, nextResolve) => {
@@ -482,8 +539,8 @@ var require_dist = __commonJS({
482
539
  options.loadEntry(options.seedEntry);
483
540
  }
484
541
  var import_node_child_process = require("child_process");
485
- var fs3 = __toESM2(require("fs"));
486
- var path4 = __toESM2(require("path"));
542
+ var fs4 = __toESM2(require("fs"));
543
+ var path5 = __toESM2(require("path"));
487
544
  var import_node_util = require("util");
488
545
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
489
546
  function buildNpmRegistryArgs(registry) {
@@ -495,7 +552,7 @@ var require_dist = __commonJS({
495
552
  var RESTART_REASON_PREFIX = "server-update";
496
553
  function readPackageVersion(pkgJsonPath) {
497
554
  try {
498
- const raw = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
555
+ const raw = JSON.parse(fs4.readFileSync(pkgJsonPath, "utf-8"));
499
556
  if (typeof raw === "object" && raw !== null) {
500
557
  const version = raw["version"];
501
558
  if (typeof version === "string") return version;
@@ -522,7 +579,7 @@ var require_dist = __commonJS({
522
579
  this.envNames = options.envNames;
523
580
  this.logger = options.logger;
524
581
  this.restartServerFn = options.restartServer;
525
- this.dataDir = path4.resolve(options.dataDir);
582
+ this.dataDir = path5.resolve(options.dataDir);
526
583
  this.execNpm = options.execNpm ?? (async (args, opts) => {
527
584
  const { stdout } = await execFileAsync("npm", [...args], {
528
585
  cwd: opts.cwd,
@@ -551,7 +608,7 @@ var require_dist = __commonJS({
551
608
  seedVersion() {
552
609
  const seedDir = this.env[this.envNames.seedDir];
553
610
  if (seedDir === void 0 || seedDir.length === 0) return null;
554
- return readPackageVersion(path4.join(seedDir, "package.json"));
611
+ return readPackageVersion(path5.join(seedDir, "package.json"));
555
612
  }
556
613
  rootDir() {
557
614
  return serverRootDir(this.dataDir);
@@ -569,7 +626,7 @@ var require_dist = __commonJS({
569
626
  const rootDir = this.rootDir();
570
627
  const raw = readServerRootState(rootDir);
571
628
  if (raw !== null) return { state: raw, corrupt: false };
572
- return { state: emptyServerRootState(), corrupt: fs3.existsSync(stateFilePath(rootDir)) };
629
+ return { state: emptyServerRootState(), corrupt: fs4.existsSync(stateFilePath(rootDir)) };
573
630
  }
574
631
  updateState(state) {
575
632
  if (this.inFlight === "checking") return "checking";
@@ -709,6 +766,17 @@ var require_dist = __commonJS({
709
766
  message: `Refused: ${this.spec.packageName}@${target} is already running.`
710
767
  };
711
768
  }
769
+ if (isDevChannelVersion(target)) {
770
+ const uploadsDir = devUploadVersionDir(this.rootDir(), target);
771
+ if (!fs4.existsSync(uploadsDir)) {
772
+ return {
773
+ accepted: false,
774
+ targetVersion: target,
775
+ restarting: false,
776
+ message: `Refused: dev-channel version ${target} has no uploaded tarballs on this node (${uploadsDir} is missing). Dev server deploys are hub-only in this phase \u2014 upload via \`camstack deploy-server\` against this node first.`
777
+ };
778
+ }
779
+ }
712
780
  this.inFlight = "staging";
713
781
  try {
714
782
  await this.stageAndActivate(target);
@@ -755,34 +823,40 @@ var require_dist = __commonJS({
755
823
  async stageAndActivate(target) {
756
824
  const rootDir = this.rootDir();
757
825
  const vDir = versionsDir(rootDir);
758
- fs3.mkdirSync(vDir, { recursive: true });
759
- const stagingDir = path4.join(vDir, `.staging-${target}-${process.pid}-${this.now()}`);
760
- fs3.mkdirSync(stagingDir, { recursive: true });
826
+ fs4.mkdirSync(vDir, { recursive: true });
827
+ const stagingDir = path5.join(vDir, `.staging-${target}-${process.pid}-${this.now()}`);
828
+ fs4.mkdirSync(stagingDir, { recursive: true });
761
829
  try {
762
- fs3.writeFileSync(
763
- path4.join(stagingDir, "package.json"),
764
- JSON.stringify({ name: "camstack-node-root", private: true }, null, 2),
765
- "utf-8"
766
- );
830
+ const devDir = devUploadVersionDir(rootDir, target);
767
831
  const registry = this.env["CAMSTACK_NPM_REGISTRY"];
768
- await this.execNpm(
769
- [
770
- "install",
771
- "--omit=dev",
772
- "--no-audit",
773
- "--no-fund",
774
- "--loglevel=error",
775
- `${this.spec.packageName}@${target}`,
776
- ...buildNpmRegistryArgs(registry)
777
- ],
778
- { cwd: stagingDir, timeoutMs: NPM_INSTALL_TIMEOUT_MS }
779
- );
832
+ const installArgs = ["install", "--omit=dev", "--no-audit", "--no-fund", "--loglevel=error"];
833
+ if (fs4.existsSync(devDir)) {
834
+ fs4.writeFileSync(
835
+ path5.join(stagingDir, "package.json"),
836
+ JSON.stringify(this.buildDevUploadsPackageJson(devDir, target), null, 2),
837
+ "utf-8"
838
+ );
839
+ await this.execNpm([...installArgs, ...buildNpmRegistryArgs(registry)], {
840
+ cwd: stagingDir,
841
+ timeoutMs: NPM_INSTALL_TIMEOUT_MS
842
+ });
843
+ } else {
844
+ fs4.writeFileSync(
845
+ path5.join(stagingDir, "package.json"),
846
+ JSON.stringify({ name: "camstack-node-root", private: true }, null, 2),
847
+ "utf-8"
848
+ );
849
+ await this.execNpm(
850
+ [...installArgs, `${this.spec.packageName}@${target}`, ...buildNpmRegistryArgs(registry)],
851
+ { cwd: stagingDir, timeoutMs: NPM_INSTALL_TIMEOUT_MS }
852
+ );
853
+ }
780
854
  const entry = rootEntryPath(stagingDir, this.spec);
781
- if (!fs3.existsSync(entry)) {
855
+ if (!fs4.existsSync(entry)) {
782
856
  throw new Error(`staged closure is missing the root entry (${entry})`);
783
857
  }
784
858
  const stagedVersion = readPackageVersion(
785
- path4.join(rootPackageDir(stagingDir, this.spec), "package.json")
859
+ path5.join(rootPackageDir(stagingDir, this.spec), "package.json")
786
860
  );
787
861
  if (stagedVersion !== target) {
788
862
  throw new Error(
@@ -790,17 +864,61 @@ var require_dist = __commonJS({
790
864
  );
791
865
  }
792
866
  const dest = versionDir(rootDir, target);
793
- if (fs3.existsSync(dest)) {
867
+ if (fs4.existsSync(dest)) {
794
868
  const aside = `${dest}.evicted-${this.now()}`;
795
- await fs3.promises.rename(dest, aside);
796
- await fs3.promises.rm(aside, { recursive: true, force: true }).catch(() => void 0);
869
+ await fs4.promises.rename(dest, aside);
870
+ await fs4.promises.rm(aside, { recursive: true, force: true }).catch(() => void 0);
797
871
  }
798
- await fs3.promises.rename(stagingDir, dest);
872
+ await fs4.promises.rename(stagingDir, dest);
799
873
  } catch (err) {
800
- await fs3.promises.rm(stagingDir, { recursive: true, force: true }).catch(() => void 0);
874
+ await fs4.promises.rm(stagingDir, { recursive: true, force: true }).catch(() => void 0);
801
875
  throw err;
802
876
  }
803
877
  }
878
+ /**
879
+ * Synthetic staging package.json for the dev server-deploy channel:
880
+ * `dependencies` carries the root package as a `file:` ref to its uploaded
881
+ * tgz; `overrides` maps EVERY OTHER uploaded `@camstack/<name>` to its tgz
882
+ * so any transitive occurrence resolves to the uploaded copy, never npm.
883
+ * Fail-closed: a missing/invalid manifest, a manifest for a different
884
+ * version, a missing root package, or a listed-but-absent tgz all throw
885
+ * (surfaced by `applyServerUpdate` as a staging failure).
886
+ */
887
+ buildDevUploadsPackageJson(devDir, target) {
888
+ const manifest = readDevUploadManifest(devDir);
889
+ if (manifest === null) {
890
+ throw new Error(`dev-uploads manifest missing or invalid in ${devDir}`);
891
+ }
892
+ if (manifest.version !== target) {
893
+ throw new Error(
894
+ `dev-uploads manifest version mismatch: dir is for ${target}, manifest says ${manifest.version}`
895
+ );
896
+ }
897
+ const rootTgz = manifest.packages[this.spec.packageName];
898
+ if (rootTgz === void 0) {
899
+ throw new Error(
900
+ `dev-uploads for ${target} do not include the root package ${this.spec.packageName}`
901
+ );
902
+ }
903
+ const fileRef = (filename) => {
904
+ const abs = path5.join(devDir, filename);
905
+ if (!fs4.existsSync(abs)) {
906
+ throw new Error(`dev-uploads tarball listed in the manifest is missing: ${abs}`);
907
+ }
908
+ return `file:${abs}`;
909
+ };
910
+ const overrides = {};
911
+ for (const [pkgName, filename] of Object.entries(manifest.packages)) {
912
+ if (pkgName === this.spec.packageName) continue;
913
+ overrides[pkgName] = fileRef(filename);
914
+ }
915
+ return {
916
+ name: "camstack-node-root",
917
+ private: true,
918
+ dependencies: { [this.spec.packageName]: fileRef(rootTgz) },
919
+ ...Object.keys(overrides).length > 0 ? { overrides } : {}
920
+ };
921
+ }
804
922
  // ── Rollback ──────────────────────────────────────────────────────────
805
923
  async rollbackServerUpdate() {
806
924
  if (this.inFlight !== "idle") {
@@ -930,6 +1048,7 @@ var require_dist = __commonJS({
930
1048
  (v) => typeof v === "string"
931
1049
  )
932
1050
  );
1051
+ this.sweepDevUploads(DEV_UPLOADS_KEEP_COUNT);
933
1052
  this.logger.info("root package update confirmed healthy", {
934
1053
  meta: {
935
1054
  version: promoted.currentVersion,
@@ -953,23 +1072,23 @@ var require_dist = __commonJS({
953
1072
  const vDir = versionsDir(this.rootDir());
954
1073
  let entries;
955
1074
  try {
956
- entries = fs3.readdirSync(vDir);
1075
+ entries = fs4.readdirSync(vDir);
957
1076
  } catch {
958
1077
  return;
959
1078
  }
960
1079
  for (const entry of entries) {
961
1080
  if (keep.includes(entry)) continue;
962
- const full = path4.join(vDir, entry);
1081
+ const full = path5.join(vDir, entry);
963
1082
  if (entry.startsWith(".")) {
964
1083
  try {
965
- const ageMs = this.now() - fs3.statSync(full).mtimeMs;
1084
+ const ageMs = this.now() - fs4.statSync(full).mtimeMs;
966
1085
  if (ageMs < _RootUpdateService.STALE_TRANSIENT_MS) continue;
967
1086
  } catch {
968
1087
  continue;
969
1088
  }
970
1089
  }
971
1090
  try {
972
- fs3.rmSync(full, { recursive: true, force: true });
1091
+ fs4.rmSync(full, { recursive: true, force: true });
973
1092
  this.logger.debug("pruned server-root version dir", { meta: { entry } });
974
1093
  } catch (err) {
975
1094
  this.logger.warn("failed to prune server-root version dir", {
@@ -978,6 +1097,56 @@ var require_dist = __commonJS({
978
1097
  }
979
1098
  }
980
1099
  }
1100
+ /**
1101
+ * Promote-time sweep of `server-root/dev-uploads/`: keep only the
1102
+ * `keepCount` most recent entries (ordered by the self-describing
1103
+ * `-dev.<epochSeconds>` suffix; non-dev-shaped residue counts as oldest).
1104
+ * Uses the same rename-aside graveyard pattern as the addon installer —
1105
+ * a rename is a metadata op that succeeds even when a tarball is held
1106
+ * open, and residue in the graveyard is re-swept on the next promote.
1107
+ */
1108
+ sweepDevUploads(keepCount) {
1109
+ const dir = devUploadsDir(this.rootDir());
1110
+ let entries;
1111
+ try {
1112
+ entries = fs4.readdirSync(dir);
1113
+ } catch {
1114
+ return;
1115
+ }
1116
+ const graveyard = path5.join(dir, ".sweeping");
1117
+ try {
1118
+ for (const residue of fs4.readdirSync(graveyard)) {
1119
+ try {
1120
+ fs4.rmSync(path5.join(graveyard, residue), { recursive: true, force: true });
1121
+ } catch {
1122
+ }
1123
+ }
1124
+ } catch {
1125
+ }
1126
+ const versions = entries.filter((name) => !name.startsWith("."));
1127
+ const byNewestFirst = [...versions].sort(
1128
+ (a, b) => (devChannelEpoch(b) ?? -1) - (devChannelEpoch(a) ?? -1)
1129
+ );
1130
+ const doomed = byNewestFirst.slice(keepCount);
1131
+ if (doomed.length === 0) return;
1132
+ fs4.mkdirSync(graveyard, { recursive: true });
1133
+ for (const entry of doomed) {
1134
+ const aside = path5.join(graveyard, `${entry}-${this.now()}`);
1135
+ try {
1136
+ fs4.renameSync(path5.join(dir, entry), aside);
1137
+ } catch (err) {
1138
+ this.logger.warn("failed to move dev-uploads entry aside for sweep", {
1139
+ meta: { entry, error: err instanceof Error ? err.message : String(err) }
1140
+ });
1141
+ continue;
1142
+ }
1143
+ try {
1144
+ fs4.rmSync(aside, { recursive: true, force: true });
1145
+ this.logger.debug("swept dev-uploads entry", { meta: { entry } });
1146
+ } catch {
1147
+ }
1148
+ }
1149
+ }
981
1150
  /** Transient (.staging- / .evicted-) dirs younger than this are never pruned. */
982
1151
  static STALE_TRANSIENT_MS = 24 * 60 * 60 * 1e3;
983
1152
  };