@runuai/host 0.9.105 → 0.9.106

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.
@@ -279,10 +279,19 @@ async function hostEgressIp(): Promise<string> {
279
279
  );
280
280
  }
281
281
 
282
+ /** How long a resolved payload AMI is trusted before the newest published
283
+ * image is looked up again. Long enough that a burst of launches shares one
284
+ * describe-images; short enough that a freshly published image reaches every
285
+ * RUNNING host — shared and BYO — on its next task without a host restart
286
+ * (before 0.9.106 the answer was kept for the process lifetime). */
287
+ export const PAYLOAD_AMI_TTL_MS = 5 * 60_000;
288
+
282
289
  export function createAwsMachineProvider(
283
290
  config: AwsMachineConfig,
284
291
  runner: Runner = defaultRunner(config.region),
292
+ deps: { now?: () => number } = {},
285
293
  ): MachineProvider {
294
+ const now = deps.now ?? (() => Date.now());
286
295
  const taskIdOf = (machineId: string): string => {
287
296
  if (!machineId.startsWith("uai-machine-")) {
288
297
  throw new Error(`not a uai machine id: ${machineId}`);
@@ -332,10 +341,12 @@ export function createAwsMachineProvider(
332
341
  // orchestrator can never reach.
333
342
  // -------------------------------------------------------------------------
334
343
 
335
- let resolvedImage: string | null = null;
344
+ let resolvedImage: { id: string; at: number } | null = null;
336
345
  async function resolveImage(specImage: string): Promise<string> {
337
346
  if (specImage.startsWith("ami-")) return specImage;
338
- if (resolvedImage) return resolvedImage;
347
+ if (resolvedImage && now() - resolvedImage.at < PAYLOAD_AMI_TTL_MS) {
348
+ return resolvedImage.id;
349
+ }
339
350
  const res = await runner([
340
351
  "ec2",
341
352
  "describe-images",
@@ -356,12 +367,24 @@ export function createAwsMachineProvider(
356
367
  }
357
368
  }
358
369
  if (typeof image !== "string" || !image.startsWith("ami-")) {
370
+ // A stale-but-known image beats a failed launch when the lookup itself
371
+ // hiccups; only a host that never resolved one has nothing to offer.
372
+ if (resolvedImage) return resolvedImage.id;
359
373
  throw new Error(
360
374
  `no uai machine payload AMI is published in ${config.region} — set ` +
361
375
  "UAI_MACHINE_IMAGE to an AMI id, or ask Uai to publish this region",
362
376
  );
363
377
  }
364
- resolvedImage = image;
378
+ if (resolvedImage?.id !== image) {
379
+ // The journal line that tells an operator which payload a task got —
380
+ // an exit 127 for a missing CLI is otherwise the only symptom of a
381
+ // region that never received the latest publish.
382
+ console.log(
383
+ `[machine-aws] payload image for ${config.region}: ${image}` +
384
+ (resolvedImage ? ` (was ${resolvedImage.id})` : ""),
385
+ );
386
+ }
387
+ resolvedImage = { id: image, at: now() };
365
388
  return image;
366
389
  }
367
390
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runuai/host",
3
- "version": "0.9.105",
3
+ "version": "0.9.106",
4
4
  "description": "Uai host — runs ephemeral AI tasks in containers on a machine you control.",
5
5
  "license": "MIT",
6
6
  "author": "Uai Tech <team@runuai.com>",