@madewithremy/admin 0.1.12 → 0.1.14

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
@@ -3017,33 +3017,48 @@ async function waitForIngest(ctx, params) {
3017
3017
  documents: tracked.map(summarize)
3018
3018
  };
3019
3019
  }
3020
+ var FAILURES_PAGE = 50;
3020
3021
  async function waitForCandidate(ctx, params) {
3021
3022
  const timeoutMs = params.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
3022
- const pendingOf = (docs2) => docs2.filter((d) => d.status === "processing");
3023
3023
  const outcome = await pollUntil(
3024
- () => allDocuments(ctx, { slug: params.slug, candidate: true }),
3025
- (docs2) => pendingOf(docs2).length === 0,
3024
+ async () => {
3025
+ const { dataSources } = await list8(ctx);
3026
+ const source = dataSources.find((s) => s.slug === params.slug);
3027
+ if (!source) {
3028
+ throw new Error(`Data source "${params.slug}" not found.`);
3029
+ }
3030
+ return source.candidate?.progress ?? null;
3031
+ },
3032
+ (progress2) => progress2 === null || progress2.processing === 0,
3026
3033
  {
3027
3034
  timeoutMs,
3028
3035
  pollMs: POLL_MS2,
3029
- describe: (docs2, start2) => `rebuilding\u2026 ${docs2.length - pendingOf(docs2).length}/${docs2.length} done (${elapsedSeconds(start2)}s)`,
3036
+ describe: (progress2, start2) => progress2 ? `rebuilding\u2026 ${progress2.done}/${progress2.total} done (${elapsedSeconds(start2)}s)` : `rebuilding\u2026 (${elapsedSeconds(start2)}s)`,
3030
3037
  onProgress: params.onProgress
3031
3038
  }
3032
3039
  );
3033
- const docs = outcome.value;
3040
+ const counts = outcome.value;
3041
+ const failed = counts && counts.error > 0 ? (await documents(ctx, {
3042
+ slug: params.slug,
3043
+ candidate: true,
3044
+ status: "error",
3045
+ limit: FAILURES_PAGE
3046
+ })).documents.map(summarize) : [];
3034
3047
  if (!outcome.settled) {
3035
3048
  return {
3036
3049
  status: "timeout",
3037
- documents: docs.map(summarize),
3050
+ counts,
3051
+ documents: failed,
3038
3052
  error: timedOut(
3039
3053
  timeoutMs,
3040
- `with ${pendingOf(docs).length} document(s) still building`
3054
+ `with ${counts?.processing ?? 0} document(s) still building`
3041
3055
  )
3042
3056
  };
3043
3057
  }
3044
3058
  return {
3045
- status: docs.some((d) => d.status === "error") ? "error" : "ready",
3046
- documents: docs.map(summarize)
3059
+ status: counts && counts.error > 0 ? "error" : "ready",
3060
+ counts,
3061
+ documents: failed
3047
3062
  };
3048
3063
  }
3049
3064
  var DOCUMENTS_IDS_MAX = 200;
@@ -3056,7 +3071,10 @@ function documents(ctx, params) {
3056
3071
  candidate: params.candidate || void 0,
3057
3072
  ids: params.ids?.length ? params.ids.join(",") : void 0,
3058
3073
  limit: params.limit,
3059
- cursor: params.cursor
3074
+ cursor: params.cursor,
3075
+ order: params.order,
3076
+ status: params.status,
3077
+ filename: params.filename
3060
3078
  })}`
3061
3079
  );
3062
3080
  }
@@ -3074,7 +3092,10 @@ async function allDocuments(ctx, params) {
3074
3092
  candidate: params.candidate,
3075
3093
  ids,
3076
3094
  limit: 1e3,
3077
- cursor
3095
+ cursor,
3096
+ order: params.order,
3097
+ status: params.status,
3098
+ filename: params.filename
3078
3099
  });
3079
3100
  out2.push(...page.documents ?? []);
3080
3101
  cursor = page.nextCursor ?? void 0;
@@ -3268,13 +3289,14 @@ function base2(appId) {
3268
3289
  return `/_internal/v2/apps/${appId}/datasources/jobs`;
3269
3290
  }
3270
3291
  function start(ctx, params) {
3271
- const { slug, source, approve: approve2, budgetDollars, concurrency, limit } = params;
3292
+ const { slug, source, approve: approve2, budgetDollars, concurrency, priority, limit } = params;
3272
3293
  return call(ctx, "POST", base2(ctx.appId), {
3273
3294
  slug,
3274
3295
  source,
3275
3296
  ...approve2 ? { approve: true } : {},
3276
3297
  ...budgetDollars !== void 0 ? { budgetDollars } : {},
3277
3298
  ...concurrency !== void 0 ? { concurrency } : {},
3299
+ ...priority ? { priority: true } : {},
3278
3300
  ...limit !== void 0 ? { limit } : {}
3279
3301
  });
3280
3302
  }
@@ -3307,9 +3329,16 @@ function approve(ctx, params) {
3307
3329
  ctx,
3308
3330
  "POST",
3309
3331
  `${base2(ctx.appId)}/${encodeURIComponent(params.id)}/approve`,
3310
- params.concurrency !== void 0 ? { concurrency: params.concurrency } : void 0
3332
+ controlBody(params)
3311
3333
  );
3312
3334
  }
3335
+ function controlBody(params) {
3336
+ const body = {
3337
+ ...params.concurrency !== void 0 ? { concurrency: params.concurrency } : {},
3338
+ ...params.priority !== void 0 ? { priority: params.priority } : {}
3339
+ };
3340
+ return Object.keys(body).length > 0 ? body : void 0;
3341
+ }
3313
3342
  function pause(ctx, params) {
3314
3343
  return call(
3315
3344
  ctx,
@@ -3322,7 +3351,7 @@ function resume(ctx, params) {
3322
3351
  ctx,
3323
3352
  "POST",
3324
3353
  `${base2(ctx.appId)}/${encodeURIComponent(params.id)}/resume`,
3325
- params.concurrency !== void 0 ? { concurrency: params.concurrency } : void 0
3354
+ controlBody(params)
3326
3355
  );
3327
3356
  }
3328
3357
  function cancel(ctx, params) {
@@ -3477,7 +3506,8 @@ function sync(ctx, params) {
3477
3506
  slug: params.slug,
3478
3507
  ...params.budgetDollars !== void 0 ? { budgetDollars: params.budgetDollars } : {},
3479
3508
  ...params.limit !== void 0 ? { limit: params.limit } : {},
3480
- ...params.concurrency !== void 0 ? { concurrency: params.concurrency } : {}
3509
+ ...params.concurrency !== void 0 ? { concurrency: params.concurrency } : {},
3510
+ ...params.priority ? { priority: true } : {}
3481
3511
  });
3482
3512
  }
3483
3513
 
@@ -3506,12 +3536,13 @@ var dataSourceConnectorsSpecs = {
3506
3536
  flags: { source: { type: "string" } }
3507
3537
  },
3508
3538
  "datasources sync": {
3509
- usage: "Usage: remy-admin datasources sync [--source <slug>] [--budget <dollars>] [--limit <n>] [--concurrency <n>] [--wait] [--timeout <sec>]",
3539
+ usage: "Usage: remy-admin datasources sync [--source <slug>] [--budget <dollars>] [--limit <n>] [--concurrency <n>] [--priority] [--wait] [--timeout <sec>]",
3510
3540
  flags: {
3511
3541
  source: { type: "string" },
3512
3542
  budget: { type: "number", min: 0.01 },
3513
3543
  limit: { type: "number", min: 1 },
3514
3544
  concurrency: { type: "number", min: 1 },
3545
+ priority: { type: "boolean" },
3515
3546
  wait: { type: "boolean" },
3516
3547
  timeout: { type: "string" }
3517
3548
  }
@@ -3587,7 +3618,8 @@ async function sync2(ctx, a) {
3587
3618
  slug,
3588
3619
  ...budget !== void 0 ? { budgetDollars: budget } : {},
3589
3620
  ...limit !== void 0 ? { limit } : {},
3590
- ...concurrency !== void 0 ? { concurrency } : {}
3621
+ ...concurrency !== void 0 ? { concurrency } : {},
3622
+ ...a.bool("priority") ? { priority: true } : {}
3591
3623
  });
3592
3624
  if (!a.bool("wait")) {
3593
3625
  out({
@@ -3612,7 +3644,7 @@ var dataSourceConnectorsHelp = `
3612
3644
  Following an S3 bucket (connectors):
3613
3645
  remy-admin datasources connect --source <slug> --bucket <name> --region <aws-region> [--prefix <p>] --access-key-secret <NAME> --secret-key-secret <NAME> [--deletions mirror|keep] [--budget-per-sync <dollars>]
3614
3646
  remy-admin datasources connector [--source <slug>]
3615
- remy-admin datasources sync [--source <slug>] [--budget <dollars>] [--limit <n>] [--concurrency <n>] [--wait] [--timeout <sec>]
3647
+ remy-admin datasources sync [--source <slug>] [--budget <dollars>] [--limit <n>] [--concurrency <n>] [--priority] [--wait] [--timeout <sec>]
3616
3648
  remy-admin datasources disconnect --source <slug>
3617
3649
 
3618
3650
  A connector makes a bucket the customer owns the origin of a source. The
@@ -3658,7 +3690,7 @@ function hydrate(ctx, params) {
3658
3690
  ctx,
3659
3691
  "POST",
3660
3692
  `${base4(ctx.appId)}/hydrate`,
3661
- { slug: params.slug }
3693
+ { slug: params.slug, ...params.since ? { since: params.since } : {} }
3662
3694
  );
3663
3695
  }
3664
3696
  function reindex(ctx, params) {
@@ -3872,9 +3904,10 @@ async function waitForSet(ctx, params) {
3872
3904
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3873
3905
  var dataSourceEvalsSpecs = {
3874
3906
  "datasources hydrate": {
3875
- usage: "Usage: remy-admin datasources hydrate [--source <slug>] [--wait] [--timeout <sec>]",
3907
+ usage: "Usage: remy-admin datasources hydrate [--source <slug>] [--since <iso>] [--wait] [--timeout <sec>]",
3876
3908
  flags: {
3877
3909
  source: { type: "string" },
3910
+ since: { type: "string" },
3878
3911
  wait: { type: "boolean" },
3879
3912
  timeout: { type: "string" }
3880
3913
  }
@@ -4045,7 +4078,14 @@ function selectorFromFlag(raw) {
4045
4078
  }
4046
4079
  async function hydrate2(ctx, a) {
4047
4080
  const slug = a.str("source") || DEFAULT_SOURCE;
4048
- const started = await hydrate(ctx, { slug });
4081
+ const since = a.str("since");
4082
+ if (since && Number.isNaN(Date.parse(since))) {
4083
+ fatal("--since must be an ISO 8601 timestamp, e.g. 2026-09-08T15:17:00Z.");
4084
+ }
4085
+ const started = await hydrate(ctx, {
4086
+ slug,
4087
+ ...since ? { since: new Date(since).toISOString() } : {}
4088
+ });
4049
4089
  if (started.resident || !a.bool("wait")) {
4050
4090
  out({
4051
4091
  dataSource: slug,
@@ -4340,7 +4380,7 @@ var dataSourceEvalsHandlers = {
4340
4380
  };
4341
4381
  var dataSourceEvalsHelp = `
4342
4382
  Warming and sampling:
4343
- remy-admin datasources hydrate [--source <slug>] [--wait]
4383
+ remy-admin datasources hydrate [--source <slug>] [--since <iso>] [--wait]
4344
4384
  remy-admin datasources sample --source <slug> --size <n> [--as <slug>] [--filter <k=v,...|json>] [--stratify <key>] [--placement <id|shared>] [--wait]
4345
4385
 
4346
4386
  A large source whose index was evicted (the shared pool holds a working set,
@@ -4350,6 +4390,14 @@ Warming and sampling:
4350
4390
  \`--wait\` blocks until the index is back. \`datasources list\` shows the
4351
4391
  reload's progress under \`hydration\`.
4352
4392
 
4393
+ \`hydrate --since <iso>\` is a refill rather than a warm-up: it copies every
4394
+ document built at or after that time into the index whether or not the index
4395
+ is resident. Dedicated capacity that restored from a snapshot is missing
4396
+ everything written after the snapshot was taken; the platform starts this
4397
+ refill itself when it sees the restore, and \`infra logs\` shows it. Use the
4398
+ flag for a restore that predates that, with the snapshot's time from the
4399
+ resource log.
4400
+
4353
4401
  \`sample\` draws documents from a source into a new one that shares its
4354
4402
  exact pinned config, so what you measure on the sample says something about
4355
4403
  the parent. Uniform by default; --stratify <metadata key> takes each value
@@ -4411,7 +4459,7 @@ import fs4 from "fs";
4411
4459
  import path4 from "path";
4412
4460
  var dataSourceJobsSpecs = {
4413
4461
  "datasources jobs start": {
4414
- usage: "Usage: remy-admin datasources jobs start [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --manifest <file.jsonl>) [--limit <n>] [--budget <dollars>] [--concurrency <n>] [--approve] [--wait] [--timeout <sec>]",
4462
+ usage: "Usage: remy-admin datasources jobs start [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --manifest <file.jsonl>) [--limit <n>] [--budget <dollars>] [--concurrency <n>] [--priority] [--approve] [--wait] [--timeout <sec>]",
4415
4463
  flags: {
4416
4464
  source: { type: "string" },
4417
4465
  store: { type: "string" },
@@ -4421,6 +4469,7 @@ var dataSourceJobsSpecs = {
4421
4469
  limit: { type: "number", min: 1 },
4422
4470
  budget: { type: "number", min: 0.01 },
4423
4471
  concurrency: { type: "number", min: 1 },
4472
+ priority: { type: "boolean" },
4424
4473
  approve: { type: "boolean" },
4425
4474
  wait: { type: "boolean" },
4426
4475
  timeout: { type: "string" }
@@ -4440,10 +4489,11 @@ var dataSourceJobsSpecs = {
4440
4489
  flags: { wait: { type: "boolean" }, timeout: { type: "string" } }
4441
4490
  },
4442
4491
  "datasources jobs approve": {
4443
- usage: "Usage: remy-admin datasources jobs approve <id> [--concurrency <n>] [--wait] [--timeout <sec>]",
4492
+ usage: "Usage: remy-admin datasources jobs approve <id> [--concurrency <n>] [--priority] [--wait] [--timeout <sec>]",
4444
4493
  positionals: [{ name: "id", required: true }],
4445
4494
  flags: {
4446
4495
  concurrency: { type: "number", min: 1 },
4496
+ priority: { type: "boolean" },
4447
4497
  wait: { type: "boolean" },
4448
4498
  timeout: { type: "string" }
4449
4499
  }
@@ -4453,10 +4503,11 @@ var dataSourceJobsSpecs = {
4453
4503
  positionals: [{ name: "id", required: true }]
4454
4504
  },
4455
4505
  "datasources jobs resume": {
4456
- usage: "Usage: remy-admin datasources jobs resume <id> [--concurrency <n>] [--wait] [--timeout <sec>]",
4506
+ usage: "Usage: remy-admin datasources jobs resume <id> [--concurrency <n>] [--priority] [--wait] [--timeout <sec>]",
4457
4507
  positionals: [{ name: "id", required: true }],
4458
4508
  flags: {
4459
4509
  concurrency: { type: "number", min: 1 },
4510
+ priority: { type: "boolean" },
4460
4511
  wait: { type: "boolean" },
4461
4512
  timeout: { type: "string" }
4462
4513
  }
@@ -4506,6 +4557,7 @@ async function jobsStart(ctx, a) {
4506
4557
  ...a.bool("approve") ? { approve: true } : {},
4507
4558
  ...budget !== void 0 ? { budgetDollars: budget } : {},
4508
4559
  ...concurrency !== void 0 ? { concurrency } : {},
4560
+ ...a.bool("priority") ? { priority: true } : {},
4509
4561
  ...limit !== void 0 ? { limit } : {}
4510
4562
  });
4511
4563
  if (!a.bool("wait")) {
@@ -4539,7 +4591,8 @@ async function jobsApprove(ctx, a) {
4539
4591
  const concurrency = a.num("concurrency");
4540
4592
  const { job } = await approve(ctx, {
4541
4593
  id,
4542
- ...concurrency !== void 0 ? { concurrency } : {}
4594
+ ...concurrency !== void 0 ? { concurrency } : {},
4595
+ ...a.bool("priority") ? { priority: true } : {}
4543
4596
  });
4544
4597
  if (!a.bool("wait")) {
4545
4598
  out({
@@ -4558,7 +4611,8 @@ async function jobsResume(ctx, a) {
4558
4611
  const concurrency = a.num("concurrency");
4559
4612
  const { job } = await resume(ctx, {
4560
4613
  id,
4561
- ...concurrency !== void 0 ? { concurrency } : {}
4614
+ ...concurrency !== void 0 ? { concurrency } : {},
4615
+ ...a.bool("priority") ? { priority: true } : {}
4562
4616
  });
4563
4617
  if (!a.bool("wait")) {
4564
4618
  out({ job });
@@ -4606,10 +4660,14 @@ Bulk ingestion (jobs):
4606
4660
  --approve Run as soon as the plan passes the gates, no separate approve
4607
4661
  --budget <dollars> Pause when estimated spend reaches this (default 1.5x the plan)
4608
4662
  --concurrency <n> Batches in flight at once (default 64, max 128)
4663
+ --priority Embed at the provider's priority tier: calls skip the
4664
+ provider's queue at 1.5x the embedding price. For the
4665
+ load where the clock matters more than the price.
4609
4666
  --limit <n> Only the first N objects: a cheap sample of the corpus
4610
4667
  --wait Block until the plan is ready (or, with --approve, until done)
4611
4668
  On approve and resume:
4612
4669
  --concurrency <n> Batches in flight from here on (max 128); a plan made at 8 can run at 64
4670
+ --priority Priority-tier embedding from here on (1.5x); pause and resume to change it
4613
4671
 
4614
4672
  While running, documents are read in batches of fifty with one embedding
4615
4673
  call per batch. Unchanged documents are skipped by content hash, so re-running
@@ -5053,8 +5111,12 @@ var dataSourcesSpecs = {
5053
5111
  usage: "Usage: remy-admin datasources list"
5054
5112
  },
5055
5113
  "datasources status": {
5056
- usage: "Usage: remy-admin datasources status [--source <slug>]",
5057
- flags: { source: { type: "string" } }
5114
+ usage: "Usage: remy-admin datasources status [--source <slug>] [--status error] [--all]",
5115
+ flags: {
5116
+ source: { type: "string" },
5117
+ status: { type: "string" },
5118
+ all: { type: "boolean" }
5119
+ }
5058
5120
  },
5059
5121
  "datasources rm": {
5060
5122
  usage: "Usage: remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)",
@@ -5195,10 +5257,42 @@ async function dataSourcesList(ctx) {
5195
5257
  }
5196
5258
  async function dataSourcesStatus(ctx, a) {
5197
5259
  const slug = sourceOf(a);
5198
- const documents2 = await allDocuments(ctx, { slug });
5260
+ const status2 = a.str("status");
5261
+ if (status2 !== void 0 && status2 !== "error") {
5262
+ fatal("--status can only be 'error'.");
5263
+ }
5264
+ if (a.bool("all")) {
5265
+ const documents2 = await allDocuments(ctx, {
5266
+ slug,
5267
+ ...status2 ? { status: "error" } : {}
5268
+ });
5269
+ out({
5270
+ dataSource: slug,
5271
+ documents: documents2.map(summarize)
5272
+ });
5273
+ return;
5274
+ }
5275
+ const { dataSources: sources2 } = await list8(ctx);
5276
+ const source = sources2.find((s) => s.slug === slug);
5277
+ if (!source) {
5278
+ out({ dataSource: slug, exists: false });
5279
+ return;
5280
+ }
5281
+ const failures = status2 === "error" || source.counts.error > 0 ? await documents(ctx, {
5282
+ slug,
5283
+ status: "error",
5284
+ limit: FAILURES_PAGE
5285
+ }) : null;
5199
5286
  out({
5200
5287
  dataSource: slug,
5201
- documents: documents2.map(summarize)
5288
+ counts: source.counts,
5289
+ chunkCount: source.chunkCount,
5290
+ lastIngestedAt: source.lastIngestedAt,
5291
+ activeVersion: source.activeVersion,
5292
+ candidate: source.candidate,
5293
+ job: source.job,
5294
+ failures: (failures?.documents ?? []).map(summarize),
5295
+ moreFailures: !!failures?.nextCursor
5202
5296
  });
5203
5297
  }
5204
5298
  async function dataSourcesRm(ctx, a) {
@@ -5494,7 +5588,8 @@ async function dataSourcesRevectorize(ctx, a) {
5494
5588
  dataSource: slug,
5495
5589
  candidateVersion: started.candidateVersion,
5496
5590
  status: result.status,
5497
- documents: result.documents,
5591
+ counts: result.counts,
5592
+ failures: result.documents,
5498
5593
  note: result.status === "error" ? "Some documents failed. Promote with --force to accept, or fix and re-run." : "Run `promote` to make this version live."
5499
5594
  });
5500
5595
  if (result.status === "error") {
@@ -5540,7 +5635,7 @@ matching passages with a citation pointing back at the source document.
5540
5635
  Subcommands:
5541
5636
  add Add one or more documents (skips unchanged files)
5542
5637
  list List data sources with document counts
5543
- status Show per-document ingest state
5638
+ status Show a source's counts and its failed documents (--all walks every document)
5544
5639
  rm Remove a document, or every document matching a filter
5545
5640
  search Query a corpus \u2014 useful to sanity-check one you just built
5546
5641
  create Create an empty source, optionally on dedicated capacity
@@ -5567,7 +5662,7 @@ Subcommands:
5567
5662
  Usage:
5568
5663
  remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>
5569
5664
  remy-admin datasources list
5570
- remy-admin datasources status [--source <slug>]
5665
+ remy-admin datasources status [--source <slug>] [--status error] [--all]
5571
5666
  remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)
5572
5667
  remy-admin datasources search [--source <slug>] [search options] <query>
5573
5668
  remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
@@ -7480,6 +7575,21 @@ function get10(ctx, params) {
7480
7575
  `${base6(ctx.appId)}/${encodeURIComponent(params.id)}`
7481
7576
  );
7482
7577
  }
7578
+ function logs(ctx, params) {
7579
+ const query3 = new URLSearchParams();
7580
+ if (params.after) {
7581
+ query3.set("after", params.after);
7582
+ }
7583
+ if (params.limit) {
7584
+ query3.set("limit", String(params.limit));
7585
+ }
7586
+ const search2 = query3.toString();
7587
+ return call(
7588
+ ctx,
7589
+ "GET",
7590
+ `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
7591
+ );
7592
+ }
7483
7593
  function provision(ctx, params) {
7484
7594
  return call(ctx, "POST", base6(ctx.appId), {
7485
7595
  offeringId: params.offeringId,
@@ -7565,6 +7675,11 @@ var infraSpecs = {
7565
7675
  usage: "Usage: remy-admin infra get <id>",
7566
7676
  positionals: [{ name: "id", required: true }]
7567
7677
  },
7678
+ "infra logs": {
7679
+ usage: "Usage: remy-admin infra logs <id> [--follow] [--limit <n>]",
7680
+ positionals: [{ name: "id", required: true }],
7681
+ flags: { follow: { type: "boolean" }, limit: { type: "string" } }
7682
+ },
7568
7683
  "infra provision": {
7569
7684
  usage: "Usage: remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]",
7570
7685
  flags: {
@@ -7636,6 +7751,30 @@ async function infraList(ctx, a) {
7636
7751
  async function infraGet(ctx, a) {
7637
7752
  out(await get10(ctx, { id: a.req("id") }));
7638
7753
  }
7754
+ var FOLLOW_POLL_MS = 4e3;
7755
+ async function infraLogs(ctx, a) {
7756
+ const id = a.req("id");
7757
+ const limitRaw = a.str("limit");
7758
+ const limit = limitRaw ? Number(limitRaw) : void 0;
7759
+ let { logs: logs2 } = await logs(ctx, { id, ...limit ? { limit } : {} });
7760
+ out({ logs: logs2 });
7761
+ if (!a.bool("follow")) {
7762
+ return;
7763
+ }
7764
+ let after = logs2.length > 0 ? logs2[logs2.length - 1].id : void 0;
7765
+ while (true) {
7766
+ await new Promise((resolve2) => setTimeout(resolve2, FOLLOW_POLL_MS));
7767
+ ({ logs: logs2 } = await logs(ctx, {
7768
+ id,
7769
+ ...after ? { after } : {},
7770
+ limit: 500
7771
+ }));
7772
+ if (logs2.length > 0) {
7773
+ out({ logs: logs2 });
7774
+ after = logs2[logs2.length - 1].id;
7775
+ }
7776
+ }
7777
+ }
7639
7778
  function pricingOf(resource) {
7640
7779
  const offering = resource.offering;
7641
7780
  if (!offering) {
@@ -7734,6 +7873,7 @@ async function infraResize(ctx, a) {
7734
7873
  var infraHandlers = {
7735
7874
  "infra list": infraList,
7736
7875
  "infra get": infraGet,
7876
+ "infra logs": infraLogs,
7737
7877
  "infra provision": infraProvision,
7738
7878
  "infra hibernate": infraHibernate,
7739
7879
  "infra resume": infraResume,
@@ -7755,7 +7895,8 @@ the price it committed to. Every action below is audited.
7755
7895
 
7756
7896
  Subcommands:
7757
7897
  list Resources on this app, plus the offerings you can provision
7758
- get One resource with its state timeline and attached sources
7898
+ get One resource: billing to date, state timeline, recent log, attached sources
7899
+ logs The platform's log for a resource (--follow to keep reading)
7759
7900
  provision Lease a new resource (needs a month's credits; prints the price)
7760
7901
  hibernate Park it: data kept, searches paused, compute charge stops
7761
7902
  resume Bring a hibernated resource back
@@ -7766,6 +7907,7 @@ Subcommands:
7766
7907
  Usage:
7767
7908
  remy-admin infra list [--include-destroyed]
7768
7909
  remy-admin infra get <id>
7910
+ remy-admin infra logs <id> [--follow] [--limit <n>]
7769
7911
  remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]
7770
7912
  remy-admin infra hibernate <id> [--wait] [--timeout <sec>]
7771
7913
  remy-admin infra resume <id> [--wait] [--timeout <sec>]
@@ -7808,6 +7950,19 @@ Phases and timing:
7808
7950
  takes about two minutes, more with more data: snapshots and restores scale
7809
7951
  with the collection. --wait defaults to 600s; pass --timeout for a large one.
7810
7952
 
7953
+ Billing and the log:
7954
+ \`infra get\` reports resource.billing: what the resource has actually been
7955
+ charged to date (from the ledger, exact across resizes), the hours at each
7956
+ rate, and when it was last billed. Rates are on resource.offering.
7957
+
7958
+ The log is the platform's narration of the resource, not the instance's
7959
+ stdout: each step a transition went through ("Waiting for capacity",
7960
+ "Restoring collections 2/3"), each failed attempt, and each failure with the
7961
+ pod's recent Kubernetes events and the warnings-and-errors tail of the
7962
+ instance log at that moment (line.data). \`infra get\` includes the newest
7963
+ 200 lines; \`infra logs\` pages the rest and, with --follow, prints new lines
7964
+ every few seconds as their own JSON document until interrupted.
7965
+
7811
7966
  --wait exit codes: 0 reached the target phase \xB7 1 the platform reported failure \xB7
7812
7967
  2 still transitioning when --timeout elapsed (default 600s).`;
7813
7968
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madewithremy/admin",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Admin SDK + CLI for managing production Remy apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",