@madewithremy/admin 0.1.13 → 0.1.15

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/prod.js CHANGED
@@ -342,6 +342,62 @@ function statsForMethod(ctx, methodId, params = {}) {
342
342
  );
343
343
  }
344
344
 
345
+ // src/sleep.ts
346
+ function sleep(ms) {
347
+ return new Promise((r) => setTimeout(r, ms));
348
+ }
349
+
350
+ // src/poll.ts
351
+ async function pollUntil(read, isSettled, options) {
352
+ const start2 = Date.now();
353
+ let movedAt = start2;
354
+ let lastKey;
355
+ let transientSince = null;
356
+ while (true) {
357
+ let value;
358
+ try {
359
+ value = await read();
360
+ transientSince = null;
361
+ } catch (err) {
362
+ if (!isTransientError(err)) {
363
+ throw err;
364
+ }
365
+ transientSince ??= Date.now();
366
+ if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
367
+ throw err;
368
+ }
369
+ options.onProgress?.(
370
+ `retrying after a gateway error\u2026 (${elapsedSeconds(start2)}s)`
371
+ );
372
+ await sleep(options.pollMs);
373
+ continue;
374
+ }
375
+ if (isSettled(value)) {
376
+ return { settled: true, value };
377
+ }
378
+ if (options.progressKey) {
379
+ const key = options.progressKey(value);
380
+ if (key !== lastKey) {
381
+ lastKey = key;
382
+ movedAt = Date.now();
383
+ }
384
+ }
385
+ if (Date.now() - movedAt > options.timeoutMs) {
386
+ return { settled: false, value };
387
+ }
388
+ if (options.describe) {
389
+ options.onProgress?.(options.describe(value, start2));
390
+ }
391
+ await sleep(options.pollMs);
392
+ }
393
+ }
394
+ function elapsedSeconds(start2) {
395
+ return Math.round((Date.now() - start2) / 1e3);
396
+ }
397
+ function timedOut(timeoutMs, still) {
398
+ return `Timed out after ${Math.round(timeoutMs / 1e3)}s ${still}`;
399
+ }
400
+
345
401
  // src/output.ts
346
402
  var PRETTY = process.stdout.isTTY === true;
347
403
  function out(data) {
@@ -352,6 +408,31 @@ function progress(message) {
352
408
  process.stderr.write(`${message}
353
409
  `);
354
410
  }
411
+ function describeEta(eta, now = Date.now()) {
412
+ if (!eta) {
413
+ return null;
414
+ }
415
+ const minutes = Math.max(1, Math.round((Date.parse(eta) - now) / 6e4));
416
+ if (minutes < 90) {
417
+ return `about ${minutes} min left`;
418
+ }
419
+ const hours = Math.round(minutes / 60 * 10) / 10;
420
+ return `about ${hours} h left`;
421
+ }
422
+ function renderProgress(p, start2, extra) {
423
+ const count = p.total !== null ? `${p.done.toLocaleString()}/${p.total.toLocaleString()} ${p.unit}` : p.done > 0 ? `${p.done.toLocaleString()} ${p.unit}` : null;
424
+ const rate = p.ratePerMinute !== null && p.ratePerMinute > 0 ? `${Math.round(p.ratePerMinute).toLocaleString()}/min` : null;
425
+ const parts = [
426
+ `${p.phase}\u2026`,
427
+ count,
428
+ rate,
429
+ describeEta(p.etaAt),
430
+ extra ?? null,
431
+ p.stalled ? "no progress for 30 min" : null
432
+ ].filter((part) => Boolean(part));
433
+ const elapsed = start2 !== void 0 ? ` (${elapsedSeconds(start2)}s)` : "";
434
+ return `${parts.join(" \xB7 ")}${elapsed}`;
435
+ }
355
436
 
356
437
  // src/commands/requests.ts
357
438
  var requestsSpecs = {
@@ -866,11 +947,6 @@ import { execFileSync } from "child_process";
866
947
  // src/config.ts
867
948
  var WORKSPACE_DIR = process.env["WORKSPACE_DIR"] || "/home/vercel-sandbox/workspace";
868
949
 
869
- // src/sleep.ts
870
- function sleep(ms) {
871
- return new Promise((r) => setTimeout(r, ms));
872
- }
873
-
874
950
  // src/ops/releases.ts
875
951
  function list3(ctx, params = {}) {
876
952
  return call(
@@ -2874,50 +2950,6 @@ import path5 from "path";
2874
2950
 
2875
2951
  // src/ops/dataSources.ts
2876
2952
  import { createHash as createHash2 } from "crypto";
2877
-
2878
- // src/poll.ts
2879
- async function pollUntil(read, isSettled, options) {
2880
- const start2 = Date.now();
2881
- let transientSince = null;
2882
- while (true) {
2883
- let value;
2884
- try {
2885
- value = await read();
2886
- transientSince = null;
2887
- } catch (err) {
2888
- if (!isTransientError(err)) {
2889
- throw err;
2890
- }
2891
- transientSince ??= Date.now();
2892
- if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
2893
- throw err;
2894
- }
2895
- options.onProgress?.(
2896
- `retrying after a gateway error\u2026 (${elapsedSeconds(start2)}s)`
2897
- );
2898
- await sleep(options.pollMs);
2899
- continue;
2900
- }
2901
- if (isSettled(value)) {
2902
- return { settled: true, value };
2903
- }
2904
- if (Date.now() - start2 > options.timeoutMs) {
2905
- return { settled: false, value };
2906
- }
2907
- if (options.describe) {
2908
- options.onProgress?.(options.describe(value, start2));
2909
- }
2910
- await sleep(options.pollMs);
2911
- }
2912
- }
2913
- function elapsedSeconds(start2) {
2914
- return Math.round((Date.now() - start2) / 1e3);
2915
- }
2916
- function timedOut(timeoutMs, still) {
2917
- return `Timed out after ${Math.round(timeoutMs / 1e3)}s ${still}`;
2918
- }
2919
-
2920
- // src/ops/dataSources.ts
2921
2953
  var DEFAULT_WAIT_TIMEOUT_MS = 15 * 60 * 1e3;
2922
2954
  var POLL_MS2 = 3e3;
2923
2955
  function base(appId) {
@@ -2998,6 +3030,7 @@ async function waitForIngest(ctx, params) {
2998
3030
  timeoutMs,
2999
3031
  pollMs: POLL_MS2,
3000
3032
  describe: (tracked2, start2) => `ingesting\u2026 ${tracked2.length - pendingOf(tracked2).length}/${tracked2.length} done (${elapsedSeconds(start2)}s)`,
3033
+ progressKey: (tracked2) => pendingOf(tracked2).length,
3001
3034
  onProgress
3002
3035
  }
3003
3036
  );
@@ -3017,33 +3050,49 @@ async function waitForIngest(ctx, params) {
3017
3050
  documents: tracked.map(summarize)
3018
3051
  };
3019
3052
  }
3053
+ var FAILURES_PAGE = 50;
3020
3054
  async function waitForCandidate(ctx, params) {
3021
3055
  const timeoutMs = params.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
3022
- const pendingOf = (docs2) => docs2.filter((d) => d.status === "processing");
3023
3056
  const outcome = await pollUntil(
3024
- () => allDocuments(ctx, { slug: params.slug, candidate: true }),
3025
- (docs2) => pendingOf(docs2).length === 0,
3057
+ async () => {
3058
+ const { dataSources } = await list8(ctx);
3059
+ const source = dataSources.find((s) => s.slug === params.slug);
3060
+ if (!source) {
3061
+ throw new Error(`Data source "${params.slug}" not found.`);
3062
+ }
3063
+ return source.candidate?.progress ?? null;
3064
+ },
3065
+ (progress2) => progress2 === null || progress2.processing === 0,
3026
3066
  {
3027
3067
  timeoutMs,
3028
3068
  pollMs: POLL_MS2,
3029
- describe: (docs2, start2) => `rebuilding\u2026 ${docs2.length - pendingOf(docs2).length}/${docs2.length} done (${elapsedSeconds(start2)}s)`,
3069
+ describe: (progress2, start2) => progress2 ? `rebuilding\u2026 ${progress2.done}/${progress2.total} done (${elapsedSeconds(start2)}s)` : `rebuilding\u2026 (${elapsedSeconds(start2)}s)`,
3070
+ progressKey: (progress2) => progress2?.processing ?? null,
3030
3071
  onProgress: params.onProgress
3031
3072
  }
3032
3073
  );
3033
- const docs = outcome.value;
3074
+ const counts = outcome.value;
3075
+ const failed = counts && counts.error > 0 ? (await documents(ctx, {
3076
+ slug: params.slug,
3077
+ candidate: true,
3078
+ status: "error",
3079
+ limit: FAILURES_PAGE
3080
+ })).documents.map(summarize) : [];
3034
3081
  if (!outcome.settled) {
3035
3082
  return {
3036
3083
  status: "timeout",
3037
- documents: docs.map(summarize),
3084
+ counts,
3085
+ documents: failed,
3038
3086
  error: timedOut(
3039
3087
  timeoutMs,
3040
- `with ${pendingOf(docs).length} document(s) still building`
3088
+ `with ${counts?.processing ?? 0} document(s) still building`
3041
3089
  )
3042
3090
  };
3043
3091
  }
3044
3092
  return {
3045
- status: docs.some((d) => d.status === "error") ? "error" : "ready",
3046
- documents: docs.map(summarize)
3093
+ status: counts && counts.error > 0 ? "error" : "ready",
3094
+ counts,
3095
+ documents: failed
3047
3096
  };
3048
3097
  }
3049
3098
  var DOCUMENTS_IDS_MAX = 200;
@@ -3056,7 +3105,10 @@ function documents(ctx, params) {
3056
3105
  candidate: params.candidate || void 0,
3057
3106
  ids: params.ids?.length ? params.ids.join(",") : void 0,
3058
3107
  limit: params.limit,
3059
- cursor: params.cursor
3108
+ cursor: params.cursor,
3109
+ order: params.order,
3110
+ status: params.status,
3111
+ filename: params.filename
3060
3112
  })}`
3061
3113
  );
3062
3114
  }
@@ -3074,7 +3126,10 @@ async function allDocuments(ctx, params) {
3074
3126
  candidate: params.candidate,
3075
3127
  ids,
3076
3128
  limit: 1e3,
3077
- cursor
3129
+ cursor,
3130
+ order: params.order,
3131
+ status: params.status,
3132
+ filename: params.filename
3078
3133
  });
3079
3134
  out2.push(...page.documents ?? []);
3080
3135
  cursor = page.nextCursor ?? void 0;
@@ -3165,7 +3220,10 @@ async function waitForMove(ctx, params) {
3165
3220
  {
3166
3221
  timeoutMs,
3167
3222
  pollMs: POLL_MS2,
3168
- describe: (source2, start2) => `moving\u2026 ${source2.migration.copied}/${source2.migration.total} documents (${elapsedSeconds(start2)}s)`,
3223
+ describe: (source2, start2) => renderProgress(source2.migration.progress, start2),
3224
+ // A corpus of any size moves as long as it keeps moving; the timeout
3225
+ // bounds silence.
3226
+ progressKey: (source2) => source2.migration?.copied ?? null,
3169
3227
  onProgress: params.onProgress
3170
3228
  }
3171
3229
  );
@@ -3359,6 +3417,9 @@ async function settle(ctx, params, isSettled) {
3359
3417
  timeoutMs,
3360
3418
  pollMs: POLL_MS3,
3361
3419
  describe: describeProgress,
3420
+ // A load of any length is waited out while it moves; the timeout bounds
3421
+ // silence. Planning moves by objects listed, a run by documents.
3422
+ progressKey: (job2) => `${job2.state}:${job2.progress.done}`,
3362
3423
  onProgress: params.onProgress
3363
3424
  }
3364
3425
  );
@@ -3394,14 +3455,15 @@ async function settle(ctx, params, isSettled) {
3394
3455
  return { status: "done", job };
3395
3456
  }
3396
3457
  function describeProgress(job, start2) {
3397
- const elapsed = `(${elapsedSeconds(start2)}s)`;
3398
- if (job.state === "planning") {
3399
- return job.enumerationDone ? `planning\u2026 reading a sample of ${job.counts.objectsSeen.toLocaleString()} objects ${elapsed}` : `planning\u2026 ${job.counts.objectsSeen.toLocaleString()} objects counted ${elapsed}`;
3458
+ if (job.state === "planning" && job.enumerationDone) {
3459
+ return renderProgress(
3460
+ { ...job.progress, phase: "planning" },
3461
+ start2,
3462
+ `reading a sample of ${job.counts.objectsSeen.toLocaleString()} objects`
3463
+ );
3400
3464
  }
3401
- const total = job.plan?.projected.documents ?? job.counts.objectsSeen;
3402
- const processed = job.counts.documentsDone + job.counts.documentsSkipped + job.counts.documentsFailed;
3403
- const spend = `$${job.estimatedDollars.toFixed(2)}${job.budgetDollars !== null ? ` of $${job.budgetDollars.toFixed(2)}` : ""}`;
3404
- return `${job.state}\u2026 ${processed.toLocaleString()}/${total.toLocaleString()} documents \xB7 ${spend} ${elapsed}`;
3465
+ const spend = job.state === "planning" ? null : `$${job.estimatedDollars.toFixed(2)}${job.budgetDollars !== null ? ` of $${job.budgetDollars.toFixed(2)}` : ""}`;
3466
+ return renderProgress(job.progress, start2, spend);
3405
3467
  }
3406
3468
 
3407
3469
  // src/commands/_shared/dataSources.ts
@@ -3695,7 +3757,10 @@ async function waitForHydration(ctx, params) {
3695
3757
  {
3696
3758
  timeoutMs,
3697
3759
  pollMs: POLL_MS4,
3698
- describe: (source2, start2) => `loading\u2026 ${source2.hydration.copied}/${source2.hydration.total} documents (${elapsedSeconds(start2)}s)`,
3760
+ describe: (source2, start2) => renderProgress(source2.hydration.progress, start2),
3761
+ // A reload of any size is waited out while it moves; the timeout
3762
+ // bounds silence.
3763
+ progressKey: (source2) => source2.hydration?.copied ?? null,
3699
3764
  onProgress: params.onProgress
3700
3765
  }
3701
3766
  );
@@ -4359,7 +4424,9 @@ Warming and sampling:
4359
4424
  background; small sources reload inside the search and never show it.
4360
4425
  \`hydrate\` starts that reload ahead of time \u2014 before a demo, say \u2014 and
4361
4426
  \`--wait\` blocks until the index is back. \`datasources list\` shows the
4362
- reload's progress under \`hydration\`.
4427
+ reload's progress under \`hydration\`. The same reload is what dedicated
4428
+ capacity runs on its own after a lost node or a resume; \`infra logs\` shows
4429
+ it start, and the source's \`hydration\` shows it land.
4363
4430
 
4364
4431
  \`sample\` draws documents from a source into a new one that shares its
4365
4432
  exact pinned config, so what you measure on the sample says something about
@@ -5074,8 +5141,12 @@ var dataSourcesSpecs = {
5074
5141
  usage: "Usage: remy-admin datasources list"
5075
5142
  },
5076
5143
  "datasources status": {
5077
- usage: "Usage: remy-admin datasources status [--source <slug>]",
5078
- flags: { source: { type: "string" } }
5144
+ usage: "Usage: remy-admin datasources status [--source <slug>] [--status error] [--all]",
5145
+ flags: {
5146
+ source: { type: "string" },
5147
+ status: { type: "string" },
5148
+ all: { type: "boolean" }
5149
+ }
5079
5150
  },
5080
5151
  "datasources rm": {
5081
5152
  usage: "Usage: remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)",
@@ -5216,10 +5287,42 @@ async function dataSourcesList(ctx) {
5216
5287
  }
5217
5288
  async function dataSourcesStatus(ctx, a) {
5218
5289
  const slug = sourceOf(a);
5219
- const documents2 = await allDocuments(ctx, { slug });
5290
+ const status2 = a.str("status");
5291
+ if (status2 !== void 0 && status2 !== "error") {
5292
+ fatal("--status can only be 'error'.");
5293
+ }
5294
+ if (a.bool("all")) {
5295
+ const documents2 = await allDocuments(ctx, {
5296
+ slug,
5297
+ ...status2 ? { status: "error" } : {}
5298
+ });
5299
+ out({
5300
+ dataSource: slug,
5301
+ documents: documents2.map(summarize)
5302
+ });
5303
+ return;
5304
+ }
5305
+ const { dataSources: sources2 } = await list8(ctx);
5306
+ const source = sources2.find((s) => s.slug === slug);
5307
+ if (!source) {
5308
+ out({ dataSource: slug, exists: false });
5309
+ return;
5310
+ }
5311
+ const failures = status2 === "error" || source.counts.error > 0 ? await documents(ctx, {
5312
+ slug,
5313
+ status: "error",
5314
+ limit: FAILURES_PAGE
5315
+ }) : null;
5220
5316
  out({
5221
5317
  dataSource: slug,
5222
- documents: documents2.map(summarize)
5318
+ counts: source.counts,
5319
+ chunkCount: source.chunkCount,
5320
+ lastIngestedAt: source.lastIngestedAt,
5321
+ activeVersion: source.activeVersion,
5322
+ candidate: source.candidate,
5323
+ job: source.job,
5324
+ failures: (failures?.documents ?? []).map(summarize),
5325
+ moreFailures: !!failures?.nextCursor
5223
5326
  });
5224
5327
  }
5225
5328
  async function dataSourcesRm(ctx, a) {
@@ -5515,7 +5618,8 @@ async function dataSourcesRevectorize(ctx, a) {
5515
5618
  dataSource: slug,
5516
5619
  candidateVersion: started.candidateVersion,
5517
5620
  status: result.status,
5518
- documents: result.documents,
5621
+ counts: result.counts,
5622
+ failures: result.documents,
5519
5623
  note: result.status === "error" ? "Some documents failed. Promote with --force to accept, or fix and re-run." : "Run `promote` to make this version live."
5520
5624
  });
5521
5625
  if (result.status === "error") {
@@ -5561,7 +5665,7 @@ matching passages with a citation pointing back at the source document.
5561
5665
  Subcommands:
5562
5666
  add Add one or more documents (skips unchanged files)
5563
5667
  list List data sources with document counts
5564
- status Show per-document ingest state
5668
+ status Show a source's counts and its failed documents (--all walks every document)
5565
5669
  rm Remove a document, or every document matching a filter
5566
5670
  search Query a corpus \u2014 useful to sanity-check one you just built
5567
5671
  create Create an empty source, optionally on dedicated capacity
@@ -5588,7 +5692,7 @@ Subcommands:
5588
5692
  Usage:
5589
5693
  remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>
5590
5694
  remy-admin datasources list
5591
- remy-admin datasources status [--source <slug>]
5695
+ remy-admin datasources status [--source <slug>] [--status error] [--all]
5592
5696
  remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)
5593
5697
  remy-admin datasources search [--source <slug>] [search options] <query>
5594
5698
  remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
@@ -7501,6 +7605,21 @@ function get10(ctx, params) {
7501
7605
  `${base6(ctx.appId)}/${encodeURIComponent(params.id)}`
7502
7606
  );
7503
7607
  }
7608
+ function logs(ctx, params) {
7609
+ const query3 = new URLSearchParams();
7610
+ if (params.after) {
7611
+ query3.set("after", params.after);
7612
+ }
7613
+ if (params.limit) {
7614
+ query3.set("limit", String(params.limit));
7615
+ }
7616
+ const search2 = query3.toString();
7617
+ return call(
7618
+ ctx,
7619
+ "GET",
7620
+ `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
7621
+ );
7622
+ }
7504
7623
  function provision(ctx, params) {
7505
7624
  return call(ctx, "POST", base6(ctx.appId), {
7506
7625
  offeringId: params.offeringId,
@@ -7554,7 +7673,14 @@ async function waitForPhase(ctx, params) {
7554
7673
  {
7555
7674
  timeoutMs,
7556
7675
  pollMs: POLL_MS5,
7557
- describe: (resource2, start2) => `${resource2.phase}${resource2.detail ? ` \xB7 ${resource2.detail}` : ""}${resource2.resizingTo ? ` \xB7 resizing to ${resource2.resizingTo.label}` : ""}\u2026 (${elapsedSeconds(start2)}s)`,
7676
+ describe: (resource2, start2) => resource2.progress ? renderProgress(
7677
+ resource2.progress,
7678
+ start2,
7679
+ resource2.resizingTo ? `resizing to ${resource2.resizingTo.label}` : null
7680
+ ) : `${resource2.phase}${resource2.detail ? ` \xB7 ${resource2.detail}` : ""}${resource2.resizingTo ? ` \xB7 resizing to ${resource2.resizingTo.label}` : ""}\u2026 (${elapsedSeconds(start2)}s)`,
7681
+ // A rebuild after a resume runs as long as it must; the timeout bounds
7682
+ // silence, and a phase or step change counts as movement.
7683
+ progressKey: (resource2) => `${resource2.phase}:${resource2.progress?.done ?? ""}:${resource2.detail ?? ""}`,
7558
7684
  onProgress: params.onProgress
7559
7685
  }
7560
7686
  );
@@ -7586,6 +7712,11 @@ var infraSpecs = {
7586
7712
  usage: "Usage: remy-admin infra get <id>",
7587
7713
  positionals: [{ name: "id", required: true }]
7588
7714
  },
7715
+ "infra logs": {
7716
+ usage: "Usage: remy-admin infra logs <id> [--follow] [--limit <n>]",
7717
+ positionals: [{ name: "id", required: true }],
7718
+ flags: { follow: { type: "boolean" }, limit: { type: "string" } }
7719
+ },
7589
7720
  "infra provision": {
7590
7721
  usage: "Usage: remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]",
7591
7722
  flags: {
@@ -7657,6 +7788,30 @@ async function infraList(ctx, a) {
7657
7788
  async function infraGet(ctx, a) {
7658
7789
  out(await get10(ctx, { id: a.req("id") }));
7659
7790
  }
7791
+ var FOLLOW_POLL_MS = 4e3;
7792
+ async function infraLogs(ctx, a) {
7793
+ const id = a.req("id");
7794
+ const limitRaw = a.str("limit");
7795
+ const limit = limitRaw ? Number(limitRaw) : void 0;
7796
+ let { logs: logs2 } = await logs(ctx, { id, ...limit ? { limit } : {} });
7797
+ out({ logs: logs2 });
7798
+ if (!a.bool("follow")) {
7799
+ return;
7800
+ }
7801
+ let after = logs2.length > 0 ? logs2[logs2.length - 1].id : void 0;
7802
+ while (true) {
7803
+ await new Promise((resolve2) => setTimeout(resolve2, FOLLOW_POLL_MS));
7804
+ ({ logs: logs2 } = await logs(ctx, {
7805
+ id,
7806
+ ...after ? { after } : {},
7807
+ limit: 500
7808
+ }));
7809
+ if (logs2.length > 0) {
7810
+ out({ logs: logs2 });
7811
+ after = logs2[logs2.length - 1].id;
7812
+ }
7813
+ }
7814
+ }
7660
7815
  function pricingOf(resource) {
7661
7816
  const offering = resource.offering;
7662
7817
  if (!offering) {
@@ -7755,6 +7910,7 @@ async function infraResize(ctx, a) {
7755
7910
  var infraHandlers = {
7756
7911
  "infra list": infraList,
7757
7912
  "infra get": infraGet,
7913
+ "infra logs": infraLogs,
7758
7914
  "infra provision": infraProvision,
7759
7915
  "infra hibernate": infraHibernate,
7760
7916
  "infra resume": infraResume,
@@ -7776,7 +7932,8 @@ the price it committed to. Every action below is audited.
7776
7932
 
7777
7933
  Subcommands:
7778
7934
  list Resources on this app, plus the offerings you can provision
7779
- get One resource with its state timeline and attached sources
7935
+ get One resource: billing to date, state timeline, recent log, attached sources
7936
+ logs The platform's log for a resource (--follow to keep reading)
7780
7937
  provision Lease a new resource (needs a month's credits; prints the price)
7781
7938
  hibernate Park it: data kept, searches paused, compute charge stops
7782
7939
  resume Bring a hibernated resource back
@@ -7787,6 +7944,7 @@ Subcommands:
7787
7944
  Usage:
7788
7945
  remy-admin infra list [--include-destroyed]
7789
7946
  remy-admin infra get <id>
7947
+ remy-admin infra logs <id> [--follow] [--limit <n>]
7790
7948
  remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]
7791
7949
  remy-admin infra hibernate <id> [--wait] [--timeout <sec>]
7792
7950
  remy-admin infra resume <id> [--wait] [--timeout <sec>]
@@ -7808,26 +7966,43 @@ Placing a data source on a resource:
7808
7966
 
7809
7967
  Searches on a source whose resource is not active fail with
7810
7968
  capacity_<phase>: capacity_hibernated once parked, capacity_hibernating /
7811
- capacity_resuming while it moves. \`active\` means searchable: a resumed
7812
- resource reports active only once its data is restored. capacity_restoring
7813
- is the rare case of an active instance that was replaced (node loss) and is
7814
- refilling from its snapshot. The messages are written for the app's end
7815
- user; the stable code is what to act on.
7969
+ capacity_resuming while it moves. Once active, a source whose index is being
7970
+ rebuilt from durable storage (after a resume, a resize or a lost node)
7971
+ answers index_warming with the rebuild's progress and ETA until it lands;
7972
+ \`datasources list\` shows it under hydration and the resource under
7973
+ progress. The messages are written for the app's end user; the stable code
7974
+ is what to act on.
7816
7975
 
7817
7976
  Resizing:
7818
- A resize keeps the data. The resource is parked (a fresh snapshot), its spec
7819
- is swapped, and it comes back up restored from that snapshot, so searches on
7820
- its sources pause for the few minutes it takes. A hibernated resource swaps
7821
- in place and stays parked. Growing needs a month's credits at the new rate;
7822
- shrinking is refused below what the resource holds (resize_too_small).
7823
- \`infra get\` shows resizingTo until the swap has happened.
7977
+ A resize keeps the documents: the resource is parked, its spec is swapped,
7978
+ and it comes back up with its sources' indexes rebuilt from durable storage
7979
+ in the background (nothing is re-embedded). Searches on those sources answer
7980
+ index_warming until the rebuild lands. A hibernated resource swaps in place
7981
+ and stays parked. Growing needs a month's credits at the new rate; shrinking
7982
+ is refused below what the resource holds (resize_too_small). \`infra get\`
7983
+ shows resizingTo until the swap has happened.
7824
7984
 
7825
7985
  Phases and timing:
7826
7986
  requested \u2192 provisioning \u2192 active \u21C4 hibernated \u2192 destroyed, with hibernating /
7827
7987
  resuming / decommissioning in between and failed when a change cannot complete
7828
- (retried automatically; resume or hibernate to retry by hand). Each transition
7829
- takes about two minutes, more with more data: snapshots and restores scale
7830
- with the collection. --wait defaults to 600s; pass --timeout for a large one.
7988
+ (retried automatically; resume or hibernate to retry by hand). A transition
7989
+ takes a minute or two; the rebuild that follows a resume runs under its own
7990
+ progress with a measured ETA. --wait gives up only after --timeout (default
7991
+ 600s) WITHOUT movement, so a long rebuild is waited out while it moves.
7992
+
7993
+ Billing and the log:
7994
+ \`infra get\` reports resource.billing: what the resource has actually been
7995
+ charged to date (from the ledger, exact across resizes), the hours at each
7996
+ rate, and when it was last billed. Rates are on resource.offering.
7997
+
7998
+ The log is the platform's narration of the resource, not the instance's
7999
+ stdout: each step a transition went through ("Waiting for capacity",
8000
+ "Rebuilding archive v1 from its artifacts: 14,333,673 document(s)"), each
8001
+ failed attempt, and each failure with the
8002
+ pod's recent Kubernetes events and the warnings-and-errors tail of the
8003
+ instance log at that moment (line.data). \`infra get\` includes the newest
8004
+ 200 lines; \`infra logs\` pages the rest and, with --follow, prints new lines
8005
+ every few seconds as their own JSON document until interrupted.
7831
8006
 
7832
8007
  --wait exit codes: 0 reached the target phase \xB7 1 the platform reported failure \xB7
7833
8008
  2 still transitioning when --timeout elapsed (default 600s).`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madewithremy/admin",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Admin SDK + CLI for managing production Remy apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",