@madewithremy/admin 0.1.5 → 0.1.6

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
@@ -228,6 +228,16 @@ function parseCommand(spec, argv) {
228
228
 
229
229
  // src/http.ts
230
230
  var REQUEST_TIMEOUT_MS = 3e4;
231
+ var TRANSIENT_GRACE_MS = 2 * 6e4;
232
+ function isTransientError(err) {
233
+ if (err instanceof AdminApiError) {
234
+ return err.status >= 500;
235
+ }
236
+ if (err instanceof AdminTimeoutError) {
237
+ return true;
238
+ }
239
+ return err instanceof TypeError;
240
+ }
231
241
  var REPORT_TIMEOUT_MS = 6e4;
232
242
  function seg(value) {
233
243
  return encodeURIComponent(String(value));
@@ -913,7 +923,9 @@ function summarizeRelease(release) {
913
923
  }
914
924
  return {
915
925
  ...summary2,
916
- error: buildFailureReason(release) ?? "Build failed (no error entry in the build log)"
926
+ error: release.failureReason ?? buildFailureReason(release) ?? "Build failed (no error entry in the build log)",
927
+ ...release.failureKind ? { failureKind: release.failureKind } : {},
928
+ ...release.failurePhase ? { failurePhase: release.failurePhase } : {}
917
929
  };
918
930
  }
919
931
  var POLL_MS = 3e3;
@@ -1164,6 +1176,12 @@ the exit code so you can branch on $? without parsing:
1164
1176
  Any other failure (bad arguments, auth, API error) exits 10, so exit 1 always
1165
1177
  means the build itself failed.
1166
1178
 
1179
+ Every release shape ('get', 'list', 'status', 'wait') carries why a failed
1180
+ build failed: 'failureReason' (message), 'failurePhase' (the stage that was
1181
+ running), and 'failureKind' \u2014 'build' (the code), 'unbuildable' (commit is
1182
+ not a readable app), or 'interrupted' (a platform restart killed the build;
1183
+ a retry is already running, so don't re-push).
1184
+
1167
1185
  Examples:
1168
1186
  git push origin HEAD && remy-admin releases wait
1169
1187
  remy-admin releases wait --commit 91ca67a --timeout 600
@@ -2919,12 +2937,26 @@ async function waitForIngest(ctx, params) {
2919
2937
  }
2920
2938
  const wanted = new Set(documentIds);
2921
2939
  const start = Date.now();
2940
+ let transientSince = null;
2922
2941
  for (; ; ) {
2923
- const { documents: docs } = await call(
2924
- ctx,
2925
- "GET",
2926
- `${base(ctx.appId)}/documents?slug=${encodeURIComponent(slug)}`
2927
- );
2942
+ let docs;
2943
+ try {
2944
+ docs = await allDocuments(ctx, { slug, ids: documentIds });
2945
+ transientSince = null;
2946
+ } catch (err) {
2947
+ if (!isTransientError(err)) {
2948
+ throw err;
2949
+ }
2950
+ transientSince ??= Date.now();
2951
+ if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
2952
+ throw err;
2953
+ }
2954
+ onProgress?.(
2955
+ `retrying after a gateway error\u2026 (${Math.round((Date.now() - start) / 1e3)}s)`
2956
+ );
2957
+ await sleep(POLL_MS2);
2958
+ continue;
2959
+ }
2928
2960
  const tracked = (docs ?? []).filter((d) => wanted.has(d.id));
2929
2961
  const pending = tracked.filter((d) => d.status === "processing");
2930
2962
  const failed = tracked.filter((d) => d.status === "error");
@@ -2949,12 +2981,41 @@ async function waitForIngest(ctx, params) {
2949
2981
  await sleep(POLL_MS2);
2950
2982
  }
2951
2983
  }
2984
+ var DOCUMENTS_IDS_MAX = 200;
2952
2985
  function documents(ctx, params) {
2953
2986
  return call(
2954
2987
  ctx,
2955
2988
  "GET",
2956
- `${base(ctx.appId)}/documents${qs({ slug: params.slug, candidate: params.candidate || void 0 })}`
2957
- );
2989
+ `${base(ctx.appId)}/documents${qs({
2990
+ slug: params.slug,
2991
+ candidate: params.candidate || void 0,
2992
+ ids: params.ids?.length ? params.ids.join(",") : void 0,
2993
+ limit: params.limit,
2994
+ cursor: params.cursor
2995
+ })}`
2996
+ );
2997
+ }
2998
+ async function allDocuments(ctx, params) {
2999
+ const groups = params.ids ? Array.from(
3000
+ { length: Math.ceil(params.ids.length / DOCUMENTS_IDS_MAX) },
3001
+ (_, i) => params.ids.slice(i * DOCUMENTS_IDS_MAX, (i + 1) * DOCUMENTS_IDS_MAX)
3002
+ ) : [void 0];
3003
+ const out2 = [];
3004
+ for (const ids of groups) {
3005
+ let cursor;
3006
+ do {
3007
+ const page = await documents(ctx, {
3008
+ slug: params.slug,
3009
+ candidate: params.candidate,
3010
+ ids,
3011
+ limit: 1e3,
3012
+ cursor
3013
+ });
3014
+ out2.push(...page.documents ?? []);
3015
+ cursor = page.nextCursor ?? void 0;
3016
+ } while (cursor);
3017
+ }
3018
+ return out2;
2958
3019
  }
2959
3020
  function list8(ctx) {
2960
3021
  return call(ctx, "GET", base(ctx.appId));
@@ -2996,7 +3057,7 @@ function configGet(ctx, slug) {
2996
3057
  );
2997
3058
  }
2998
3059
  function configSet(ctx, params) {
2999
- const { slug, ingest, retrieval } = params;
3060
+ const { slug, ingest, retrieval, placement } = params;
3000
3061
  return call(
3001
3062
  ctx,
3002
3063
  "POST",
@@ -3004,10 +3065,20 @@ function configSet(ctx, params) {
3004
3065
  {
3005
3066
  slug,
3006
3067
  ...ingest ? { ingest } : {},
3007
- ...retrieval ? { retrieval } : {}
3068
+ ...retrieval ? { retrieval } : {},
3069
+ ...placement !== void 0 ? { placement } : {}
3008
3070
  }
3009
3071
  );
3010
3072
  }
3073
+ function create2(ctx, params) {
3074
+ const { slug, name, ingest, placement } = params;
3075
+ return call(ctx, "POST", base(ctx.appId), {
3076
+ slug,
3077
+ ...name ? { name } : {},
3078
+ ...ingest ? { ingest } : {},
3079
+ ...placement !== void 0 ? { placement } : {}
3080
+ });
3081
+ }
3011
3082
  function revectorize(ctx, params) {
3012
3083
  const { slug, ingest } = params;
3013
3084
  return call(
@@ -3065,8 +3136,8 @@ function deleteSource(ctx, slug) {
3065
3136
  // src/commands/dataSources.ts
3066
3137
  var DEFAULT_SOURCE = "default";
3067
3138
  var POLL_MS3 = 3e3;
3068
- var CONFIG_FLAGS = {
3069
- // Pinned — changing any of these needs a re-vectorize.
3139
+ var INGEST_FLAGS = {
3140
+ chunking: { type: "string" },
3070
3141
  "max-chars": { type: "number", min: 200 },
3071
3142
  "min-chars": { type: "number", min: 0 },
3072
3143
  "drop-blocks": { type: "string" },
@@ -3074,9 +3145,12 @@ var CONFIG_FLAGS = {
3074
3145
  "contextual-model": { type: "string" },
3075
3146
  "describe-images": { type: "string" },
3076
3147
  "embedding-model": { type: "string" },
3148
+ "embedding-dimensions": { type: "number", min: 1 },
3077
3149
  "image-model": { type: "string" },
3078
- "extraction-model": { type: "string" },
3079
- // Live — take effect on the next search, no rebuild.
3150
+ "extraction-model": { type: "string" }
3151
+ };
3152
+ var CHUNKING_STRATEGIES = ["structural", "whole"];
3153
+ var RETRIEVAL_FLAGS = {
3080
3154
  rerank: { type: "string" },
3081
3155
  // Which cross-encoder reranks. Live, not pinned: a reranker runs at query
3082
3156
  // time over text already in the index, so switching costs nothing and is
@@ -3085,6 +3159,10 @@ var CONFIG_FLAGS = {
3085
3159
  hybrid: { type: "string" },
3086
3160
  "top-k": { type: "number", min: 1 }
3087
3161
  };
3162
+ var CONFIG_FLAGS = {
3163
+ ...INGEST_FLAGS,
3164
+ ...RETRIEVAL_FLAGS
3165
+ };
3088
3166
  var dataSourcesSpecs = {
3089
3167
  "datasources add": {
3090
3168
  usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>",
@@ -3124,9 +3202,24 @@ var dataSourcesSpecs = {
3124
3202
  highlight: { type: "boolean" }
3125
3203
  }
3126
3204
  },
3205
+ "datasources create": {
3206
+ usage: "Usage: remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [ingest settings...]",
3207
+ flags: {
3208
+ source: { type: "string" },
3209
+ name: { type: "string" },
3210
+ placement: { type: "string" },
3211
+ ...INGEST_FLAGS
3212
+ }
3213
+ },
3127
3214
  "datasources config": {
3128
- usage: "Usage: remy-admin datasources config [--source <slug>] [settings...]",
3129
- flags: { source: { type: "string" }, ...CONFIG_FLAGS }
3215
+ usage: "Usage: remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]",
3216
+ flags: {
3217
+ source: { type: "string" },
3218
+ // Where the corpus lives: a dedicated resource id from `infra list`, or
3219
+ // `shared`. Only changeable while the source has nothing built.
3220
+ placement: { type: "string" },
3221
+ ...CONFIG_FLAGS
3222
+ }
3130
3223
  },
3131
3224
  "datasources revectorize": {
3132
3225
  usage: "Usage: remy-admin datasources revectorize [--source <slug>] [settings...] [--wait]",
@@ -3215,10 +3308,10 @@ async function dataSourcesList(ctx) {
3215
3308
  }
3216
3309
  async function dataSourcesStatus(ctx, a) {
3217
3310
  const slug = sourceOf(a);
3218
- const { documents: documents2 } = await documents(ctx, { slug });
3311
+ const documents2 = await allDocuments(ctx, { slug });
3219
3312
  out({
3220
3313
  dataSource: slug,
3221
- documents: (documents2 ?? []).map(summarize)
3314
+ documents: documents2.map(summarize)
3222
3315
  });
3223
3316
  }
3224
3317
  async function dataSourcesRm(ctx, a) {
@@ -3327,6 +3420,15 @@ function triState(a, flag, key) {
3327
3420
  }
3328
3421
  function configFromFlags(a) {
3329
3422
  const chunking = {};
3423
+ const strategy = a.str("chunking");
3424
+ if (strategy !== void 0) {
3425
+ if (!CHUNKING_STRATEGIES.includes(strategy)) {
3426
+ fatal(
3427
+ `--chunking must be one of: ${CHUNKING_STRATEGIES.join(", ")} (got "${strategy}").`
3428
+ );
3429
+ }
3430
+ chunking.strategy = strategy;
3431
+ }
3330
3432
  if (a.num("max-chars") !== void 0) {
3331
3433
  chunking.maxChars = a.num("max-chars");
3332
3434
  }
@@ -3346,6 +3448,7 @@ function configFromFlags(a) {
3346
3448
  ...a.str("image-model") ? { modelId: a.str("image-model") } : {}
3347
3449
  };
3348
3450
  const embeddingModel = a.str("embedding-model");
3451
+ const embeddingDimensions = a.num("embedding-dimensions");
3349
3452
  const extractionModel = a.str("extraction-model");
3350
3453
  const ingest = {};
3351
3454
  if (Object.keys(chunking).length) {
@@ -3357,8 +3460,11 @@ function configFromFlags(a) {
3357
3460
  if (Object.keys(images).length) {
3358
3461
  ingest.images = images;
3359
3462
  }
3360
- if (embeddingModel) {
3361
- ingest.embedding = { modelId: embeddingModel };
3463
+ if (embeddingModel || embeddingDimensions !== void 0) {
3464
+ ingest.embedding = {
3465
+ ...embeddingModel ? { modelId: embeddingModel } : {},
3466
+ ...embeddingDimensions !== void 0 ? { dimensions: embeddingDimensions } : {}
3467
+ };
3362
3468
  }
3363
3469
  if (extractionModel) {
3364
3470
  ingest.extraction = { modelId: extractionModel };
@@ -3381,10 +3487,32 @@ function configFromFlags(a) {
3381
3487
  retrieval: Object.keys(retrieval).length ? retrieval : void 0
3382
3488
  };
3383
3489
  }
3490
+ var placementOf = (a) => {
3491
+ const flag = a.str("placement");
3492
+ return flag === void 0 ? void 0 : flag === "shared" ? "shared" : { resourceId: flag };
3493
+ };
3494
+ async function dataSourcesCreate(ctx, a) {
3495
+ const slug = a.str("source");
3496
+ if (!slug) {
3497
+ fatal("--source is required.");
3498
+ }
3499
+ const { ingest } = configFromFlags(a);
3500
+ const name = a.str("name");
3501
+ const placement = placementOf(a);
3502
+ out(
3503
+ await create2(ctx, {
3504
+ slug,
3505
+ ...name ? { name } : {},
3506
+ ...ingest ? { ingest } : {},
3507
+ ...placement !== void 0 ? { placement } : {}
3508
+ })
3509
+ );
3510
+ }
3384
3511
  async function dataSourcesConfig(ctx, a) {
3385
3512
  const slug = sourceOf(a);
3386
3513
  const { ingest, retrieval } = configFromFlags(a);
3387
- if (!ingest && !retrieval) {
3514
+ const placement = placementOf(a);
3515
+ if (!ingest && !retrieval && placement === void 0) {
3388
3516
  out(await configGet(ctx, slug));
3389
3517
  return;
3390
3518
  }
@@ -3392,7 +3520,8 @@ async function dataSourcesConfig(ctx, a) {
3392
3520
  await configSet(ctx, {
3393
3521
  slug,
3394
3522
  ...ingest ? { ingest } : {},
3395
- ...retrieval ? { retrieval } : {}
3523
+ ...retrieval ? { retrieval } : {},
3524
+ ...placement !== void 0 ? { placement } : {}
3396
3525
  })
3397
3526
  );
3398
3527
  }
@@ -3411,12 +3540,12 @@ async function dataSourcesRevectorize(ctx, a) {
3411
3540
  const timeoutMs = timeoutSec ? timeoutSec * 1e3 : DEFAULT_WAIT_TIMEOUT_MS;
3412
3541
  const start = Date.now();
3413
3542
  for (; ; ) {
3414
- const { documents: documents2 } = await documents(ctx, {
3543
+ const documents2 = await allDocuments(ctx, {
3415
3544
  slug,
3416
3545
  candidate: true
3417
3546
  });
3418
- const pending = (documents2 ?? []).filter((d) => d.status === "processing");
3419
- const failed = (documents2 ?? []).filter((d) => d.status === "error");
3547
+ const pending = documents2.filter((d) => d.status === "processing");
3548
+ const failed = documents2.filter((d) => d.status === "error");
3420
3549
  if (pending.length === 0) {
3421
3550
  out({
3422
3551
  dataSource: slug,
@@ -3469,6 +3598,7 @@ var dataSourcesHandlers = {
3469
3598
  "datasources status": dataSourcesStatus,
3470
3599
  "datasources rm": dataSourcesRm,
3471
3600
  "datasources search": dataSourcesSearch,
3601
+ "datasources create": dataSourcesCreate,
3472
3602
  "datasources config": dataSourcesConfig,
3473
3603
  "datasources revectorize": dataSourcesRevectorize,
3474
3604
  "datasources promote": dataSourcesPromote,
@@ -3486,6 +3616,7 @@ Subcommands:
3486
3616
  status Show per-document ingest state
3487
3617
  rm Remove a document and its vectors
3488
3618
  search Query a corpus \u2014 useful to sanity-check one you just built
3619
+ create Create an empty source, optionally on dedicated capacity
3489
3620
  config Show or change how a corpus is processed and searched
3490
3621
  revectorize Rebuild a corpus under new settings, alongside the live one
3491
3622
  promote Make a rebuilt version live
@@ -3498,7 +3629,8 @@ Usage:
3498
3629
  remy-admin datasources status [--source <slug>]
3499
3630
  remy-admin datasources rm [--source <slug>] --document <id>
3500
3631
  remy-admin datasources search [--source <slug>] [search options] <query>
3501
- remy-admin datasources config [--source <slug>] [settings...]
3632
+ remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
3633
+ remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]
3502
3634
  remy-admin datasources revectorize [--source <slug>] [settings...] [--wait]
3503
3635
  remy-admin datasources promote [--source <slug>] [--force]
3504
3636
  remy-admin datasources drop [--source <slug>] [--version <n>]
@@ -3540,6 +3672,13 @@ Tuning a corpus:
3540
3672
  reprocessed. Changing these on a corpus that already has documents is
3541
3673
  REJECTED; use \`revectorize\` instead, which builds a new version alongside
3542
3674
  the live one so search never degrades:
3675
+ --chunking <structural|whole>
3676
+ structural (default) splits on headings, blocks
3677
+ and paragraphs. whole embeds each document as one
3678
+ chunk: for corpora of short records (articles,
3679
+ tickets, product rows) where the document is the
3680
+ unit you want back. Documents over --max-chars
3681
+ (default 24000 for whole) fall back to structural.
3543
3682
  --max-chars <n> Target chunk size (default 2000)
3544
3683
  --min-chars <n> Merge chunks smaller than this (default 120)
3545
3684
  --drop-blocks <a,b> Block types to discard, e.g. footer,header
@@ -3555,10 +3694,18 @@ Tuning a corpus:
3555
3694
  invisible to search rather than merely ranked low.
3556
3695
  --image-model <id>
3557
3696
  --embedding-model <id>
3697
+ --embedding-dimensions <n> Vector width, for models that offer several
3698
+ (Matryoshka). Must be the model's native size or
3699
+ one it lists; the error names the choices.
3700
+ Smaller vectors cut index memory, and dedicated
3701
+ capacity needs, roughly in proportion.
3558
3702
  --extraction-model <id>
3559
3703
 
3560
3704
  Notes:
3561
- --source defaults to "${DEFAULT_SOURCE}" and is created on first use.
3705
+ --source defaults to "${DEFAULT_SOURCE}" and is created on first use, on the
3706
+ shared pool. A corpus meant for dedicated capacity (\`infra provision\`)
3707
+ starts with \`create --placement <resource-id>\`, since a source with built
3708
+ documents cannot move; \`config --placement\` moves one that is still empty.
3562
3709
  --wait blocks until processing finishes, so you can search immediately after.
3563
3710
  Exits ${EXIT.buildFailed} if a document failed, ${EXIT.timeout} on timeout.
3564
3711
  Re-adding an unchanged file is free: no upload, no re-embedding. Re-adding
@@ -5333,6 +5480,286 @@ Examples:
5333
5480
  remy-admin cron list --runs 50
5334
5481
  remy-admin cron run dailyDigest`;
5335
5482
 
5483
+ // src/ops/infra.ts
5484
+ var DEFAULT_WAIT_TIMEOUT_MS2 = 10 * 60 * 1e3;
5485
+ var POLL_MS4 = 3e3;
5486
+ function base2(appId) {
5487
+ return `/_internal/v2/apps/${appId}/infra`;
5488
+ }
5489
+ function list11(ctx, params) {
5490
+ return call(
5491
+ ctx,
5492
+ "GET",
5493
+ `${base2(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
5494
+ );
5495
+ }
5496
+ function get8(ctx, params) {
5497
+ return call(
5498
+ ctx,
5499
+ "GET",
5500
+ `${base2(ctx.appId)}/${encodeURIComponent(params.id)}`
5501
+ );
5502
+ }
5503
+ function provision(ctx, params) {
5504
+ return call(ctx, "POST", base2(ctx.appId), {
5505
+ offeringId: params.offeringId,
5506
+ ...params.name ? { name: params.name } : {}
5507
+ });
5508
+ }
5509
+ function hibernate(ctx, params) {
5510
+ return call(
5511
+ ctx,
5512
+ "POST",
5513
+ `${base2(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
5514
+ );
5515
+ }
5516
+ function resume(ctx, params) {
5517
+ return call(
5518
+ ctx,
5519
+ "POST",
5520
+ `${base2(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
5521
+ );
5522
+ }
5523
+ function destroy(ctx, params) {
5524
+ return call(
5525
+ ctx,
5526
+ "POST",
5527
+ `${base2(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
5528
+ );
5529
+ }
5530
+ function rename(ctx, params) {
5531
+ return call(
5532
+ ctx,
5533
+ "POST",
5534
+ `${base2(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
5535
+ { name: params.name }
5536
+ );
5537
+ }
5538
+ async function waitForPhase(ctx, params) {
5539
+ const timeoutMs = params.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS2;
5540
+ const wanted = new Set(params.phases);
5541
+ const start = Date.now();
5542
+ let transientSince = null;
5543
+ for (; ; ) {
5544
+ let resource;
5545
+ try {
5546
+ ({ resource } = await get8(ctx, { id: params.id }));
5547
+ transientSince = null;
5548
+ } catch (err) {
5549
+ if (!isTransientError(err)) {
5550
+ throw err;
5551
+ }
5552
+ transientSince ??= Date.now();
5553
+ if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
5554
+ throw err;
5555
+ }
5556
+ params.onProgress?.(
5557
+ `retrying after a gateway error\u2026 (${Math.round((Date.now() - start) / 1e3)}s)`
5558
+ );
5559
+ await sleep(POLL_MS4);
5560
+ continue;
5561
+ }
5562
+ if (wanted.has(resource.phase)) {
5563
+ return { status: "done", resource };
5564
+ }
5565
+ if (resource.phase === "failed") {
5566
+ return {
5567
+ status: "failed",
5568
+ resource,
5569
+ error: resource.lastError ?? "The platform could not complete the change."
5570
+ };
5571
+ }
5572
+ if (Date.now() - start > timeoutMs) {
5573
+ return {
5574
+ status: "timeout",
5575
+ resource,
5576
+ error: `Timed out after ${Math.round(timeoutMs / 1e3)}s; resource is still ${resource.phase}`
5577
+ };
5578
+ }
5579
+ params.onProgress?.(
5580
+ `${resource.phase}${resource.detail ? ` \xB7 ${resource.detail}` : ""}\u2026 (${Math.round((Date.now() - start) / 1e3)}s)`
5581
+ );
5582
+ await sleep(POLL_MS4);
5583
+ }
5584
+ }
5585
+
5586
+ // src/commands/infra.ts
5587
+ var infraSpecs = {
5588
+ "infra list": {
5589
+ usage: "Usage: remy-admin infra list [--include-destroyed]",
5590
+ flags: { "include-destroyed": { type: "boolean" } }
5591
+ },
5592
+ "infra get": {
5593
+ usage: "Usage: remy-admin infra get <id>",
5594
+ positionals: [{ name: "id", required: true }]
5595
+ },
5596
+ "infra provision": {
5597
+ usage: "Usage: remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]",
5598
+ flags: {
5599
+ offering: { type: "string" },
5600
+ name: { type: "string" },
5601
+ wait: { type: "boolean" },
5602
+ timeout: { type: "string" }
5603
+ },
5604
+ requireAnyOf: {
5605
+ flags: ["offering"],
5606
+ message: "--offering is required (see `infra list` for the catalog)."
5607
+ }
5608
+ },
5609
+ "infra hibernate": {
5610
+ usage: "Usage: remy-admin infra hibernate <id> [--wait] [--timeout <sec>]",
5611
+ positionals: [{ name: "id", required: true }],
5612
+ flags: { wait: { type: "boolean" }, timeout: { type: "string" } }
5613
+ },
5614
+ "infra resume": {
5615
+ usage: "Usage: remy-admin infra resume <id> [--wait] [--timeout <sec>]",
5616
+ positionals: [{ name: "id", required: true }],
5617
+ flags: { wait: { type: "boolean" }, timeout: { type: "string" } }
5618
+ },
5619
+ "infra destroy": {
5620
+ usage: "Usage: remy-admin infra destroy <id> [--wait] [--timeout <sec>]",
5621
+ positionals: [{ name: "id", required: true }],
5622
+ flags: { wait: { type: "boolean" }, timeout: { type: "string" } }
5623
+ },
5624
+ "infra rename": {
5625
+ usage: "Usage: remy-admin infra rename <id> --name <name>",
5626
+ positionals: [{ name: "id", required: true }],
5627
+ flags: { name: { type: "string" } },
5628
+ requireAnyOf: { flags: ["name"], message: "--name is required." }
5629
+ }
5630
+ };
5631
+ async function waitAndReport(ctx, a, id, phases, summary2) {
5632
+ const timeoutSec = a.str("timeout");
5633
+ const result = await waitForPhase(ctx, {
5634
+ id,
5635
+ phases,
5636
+ ...timeoutSec ? { timeoutMs: Number(timeoutSec) * 1e3 } : {},
5637
+ onProgress: progress
5638
+ });
5639
+ out({ ...summary2, ...result });
5640
+ if (result.status === "failed") {
5641
+ throw new CliError(result.error, EXIT.buildFailed);
5642
+ }
5643
+ if (result.status === "timeout") {
5644
+ throw new CliError(result.error, EXIT.timeout);
5645
+ }
5646
+ }
5647
+ async function infraList(ctx, a) {
5648
+ out(await list11(ctx, { includeDestroyed: a.bool("include-destroyed") }));
5649
+ }
5650
+ async function infraGet(ctx, a) {
5651
+ out(await get8(ctx, { id: a.req("id") }));
5652
+ }
5653
+ async function infraProvision(ctx, a) {
5654
+ const offeringId = a.str("offering");
5655
+ const name = a.str("name");
5656
+ const { resource } = await provision(ctx, {
5657
+ offeringId,
5658
+ ...name ? { name } : {}
5659
+ });
5660
+ if (!a.bool("wait")) {
5661
+ out({
5662
+ resource,
5663
+ note: "Provisioning; poll `infra get <id>` or re-run with --wait."
5664
+ });
5665
+ return;
5666
+ }
5667
+ await waitAndReport(ctx, a, resource.id, ["active"], { action: "provision" });
5668
+ }
5669
+ async function infraHibernate(ctx, a) {
5670
+ const id = a.req("id");
5671
+ const { resource } = await hibernate(ctx, { id });
5672
+ if (!a.bool("wait")) {
5673
+ out({ resource });
5674
+ return;
5675
+ }
5676
+ await waitAndReport(ctx, a, id, ["hibernated"], { action: "hibernate" });
5677
+ }
5678
+ async function infraResume(ctx, a) {
5679
+ const id = a.req("id");
5680
+ const { resource } = await resume(ctx, { id });
5681
+ if (!a.bool("wait")) {
5682
+ out({ resource });
5683
+ return;
5684
+ }
5685
+ await waitAndReport(ctx, a, id, ["active"], { action: "resume" });
5686
+ }
5687
+ async function infraDestroy(ctx, a) {
5688
+ const id = a.req("id");
5689
+ const { resource } = await destroy(ctx, { id });
5690
+ if (!a.bool("wait")) {
5691
+ out({ resource });
5692
+ return;
5693
+ }
5694
+ await waitAndReport(ctx, a, id, ["destroyed"], { action: "destroy" });
5695
+ }
5696
+ async function infraRename(ctx, a) {
5697
+ out(
5698
+ await rename(ctx, {
5699
+ id: a.req("id"),
5700
+ name: a.str("name")
5701
+ })
5702
+ );
5703
+ }
5704
+ var infraHandlers = {
5705
+ "infra list": infraList,
5706
+ "infra get": infraGet,
5707
+ "infra provision": infraProvision,
5708
+ "infra hibernate": infraHibernate,
5709
+ "infra resume": infraResume,
5710
+ "infra destroy": infraDestroy,
5711
+ "infra rename": infraRename
5712
+ };
5713
+ var infraHelp = `remy-admin infra \u2014 Dedicated infrastructure your app leases from the platform.
5714
+
5715
+ Today the one kind is dedicated retrieval: the app's own vector-store capacity
5716
+ for data sources, isolated from the shared pool. Sizes and prices come from the
5717
+ platform catalog (\`infra list\` prints them); nothing is priced client-side.
5718
+
5719
+ A resource bills by the hour from activation, at the retained-storage rate while
5720
+ hibernated, and not at all once destroyed. Provisioning needs a month's worth of
5721
+ credits available in the workspace; nothing is charged until activation. Every
5722
+ action below is audited.
5723
+
5724
+ Subcommands:
5725
+ list Resources on this app, plus the offerings you can provision
5726
+ get One resource with its state timeline and attached sources
5727
+ provision Lease a new resource (spends credits)
5728
+ hibernate Park it: data kept, searches paused, compute charge stops
5729
+ resume Bring a hibernated resource back
5730
+ destroy Delete it (refused while data sources are placed on it)
5731
+ rename Change the display name
5732
+
5733
+ Usage:
5734
+ remy-admin infra list [--include-destroyed]
5735
+ remy-admin infra get <id>
5736
+ remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]
5737
+ remy-admin infra hibernate <id> [--wait] [--timeout <sec>]
5738
+ remy-admin infra resume <id> [--wait] [--timeout <sec>]
5739
+ remy-admin infra destroy <id> [--wait] [--timeout <sec>]
5740
+ remy-admin infra rename <id> --name <name>
5741
+
5742
+ Placing a data source on a resource:
5743
+ remy-admin datasources create --source <slug> --placement <resource-id>
5744
+ remy-admin datasources config --source <slug> --placement <resource-id>
5745
+ remy-admin datasources config --source <slug> --placement shared
5746
+
5747
+ Start a new corpus on a resource with \`create\`; \`add\` creates a source on
5748
+ the shared pool. Placement can only change while the source has no built
5749
+ documents; moving a populated source is a re-vectorization onto the new
5750
+ capacity, which is not available yet. Searches on a source whose resource is
5751
+ not active fail with capacity_<phase>: capacity_hibernated once parked,
5752
+ capacity_hibernating / capacity_resuming while it moves, and
5753
+ capacity_restoring while a resumed instance reloads its data.
5754
+
5755
+ Phases:
5756
+ requested \u2192 provisioning \u2192 active \u21C4 hibernated \u2192 destroyed, with hibernating /
5757
+ resuming / decommissioning in between and failed when a change cannot complete
5758
+ (retried automatically; resume or hibernate to retry by hand).
5759
+
5760
+ --wait exit codes: 0 reached the target phase \xB7 1 the platform reported failure \xB7
5761
+ 2 still transitioning when --timeout elapsed (default 600s).`;
5762
+
5336
5763
  // src/commands/index.ts
5337
5764
  var SPECS = {
5338
5765
  ...requestsSpecs,
@@ -5356,7 +5783,8 @@ var SPECS = {
5356
5783
  ...jewelsSpecs,
5357
5784
  ...settingsSpecs,
5358
5785
  ...emailSpecs,
5359
- ...cronSpecs
5786
+ ...cronSpecs,
5787
+ ...infraSpecs
5360
5788
  };
5361
5789
  var HANDLERS = {
5362
5790
  ...requestsHandlers,
@@ -5380,7 +5808,8 @@ var HANDLERS = {
5380
5808
  ...jewelsHandlers,
5381
5809
  ...settingsHandlers,
5382
5810
  ...emailHandlers,
5383
- ...cronHandlers
5811
+ ...cronHandlers,
5812
+ ...infraHandlers
5384
5813
  };
5385
5814
  var GROUP_HELP = {
5386
5815
  requests: requestsHelp,
@@ -5404,7 +5833,8 @@ var GROUP_HELP = {
5404
5833
  jewels: jewelsHelp,
5405
5834
  settings: settingsHelp,
5406
5835
  email: emailHelp,
5407
- cron: cronHelp
5836
+ cron: cronHelp,
5837
+ infra: infraHelp
5408
5838
  };
5409
5839
 
5410
5840
  // src/ctx.ts
@@ -5517,7 +5947,8 @@ var GROUPS = [
5517
5947
  "jewels",
5518
5948
  "settings",
5519
5949
  "email",
5520
- "cron"
5950
+ "cron",
5951
+ "infra"
5521
5952
  ];
5522
5953
  var KEYS = new Set(Object.keys(SPECS));
5523
5954
  function resolveRoute(argv) {