@madewithremy/admin 0.1.31 → 0.1.33

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
@@ -1019,13 +1019,6 @@ async function dashboardLive(ctx) {
1019
1019
  );
1020
1020
  return dashboard.liveRelease;
1021
1021
  }
1022
- function buildFailureReason(release) {
1023
- const log = Array.isArray(release.buildLog) ? release.buildLog : [];
1024
- const errors = log.filter(
1025
- (entry) => entry?.phase === "error" && typeof entry.message === "string"
1026
- );
1027
- return errors.length ? errors[errors.length - 1].message : null;
1028
- }
1029
1022
  function summarizeRelease(release) {
1030
1023
  const summary2 = {
1031
1024
  releaseId: release.id,
@@ -1048,7 +1041,7 @@ function summarizeRelease(release) {
1048
1041
  }
1049
1042
  return {
1050
1043
  ...summary2,
1051
- error: release.failureReason ?? buildFailureReason(release) ?? "Build failed (no error entry in the build log)",
1044
+ error: release.failureReason ?? "Build failed (no error entry in the build log)",
1052
1045
  ...release.failureKind ? { failureKind: release.failureKind } : {},
1053
1046
  ...release.failurePhase ? { failurePhase: release.failurePhase } : {}
1054
1047
  };
@@ -2946,7 +2939,7 @@ var filesSpecs = {
2946
2939
  usage: "Usage: remy-admin files list"
2947
2940
  },
2948
2941
  "files rm": {
2949
- usage: "Usage: remy-admin files rm --store <name> --key <key> [--private]",
2942
+ usage: "Usage: remy-admin files rm --store <name> --key <key> [--public|--private]",
2950
2943
  flags: {
2951
2944
  ...ACCESS_FLAGS,
2952
2945
  store: { type: "string" },
@@ -2957,6 +2950,45 @@ var filesSpecs = {
2957
2950
  function resolveAccess(a) {
2958
2951
  return a.bool("private") ? "private" : "public";
2959
2952
  }
2953
+ async function readAccess(ctx, a, key) {
2954
+ if (a.bool("private")) {
2955
+ return "private";
2956
+ }
2957
+ if (a.bool("public")) {
2958
+ return "public";
2959
+ }
2960
+ const store = storeOf(a);
2961
+ const { stores } = await summary(ctx);
2962
+ const held = stores.filter((s) => s.store === store && s.objectCount > 0);
2963
+ if (held.length === 1) {
2964
+ return held[0].access;
2965
+ }
2966
+ if (held.length === 0) {
2967
+ if (key === void 0) {
2968
+ return "public";
2969
+ }
2970
+ fatal(`Store "${store}" holds no objects, public or private.`);
2971
+ }
2972
+ if (key === void 0) {
2973
+ const counts = held.map((s) => `${s.objectCount.toLocaleString()} ${s.access}`).join(", ");
2974
+ fatal(
2975
+ `Store "${store}" holds objects in both halves (${counts}): pass --public or --private.`
2976
+ );
2977
+ }
2978
+ for (const access of ["private", "public"]) {
2979
+ try {
2980
+ await stat(ctx, { store, access, key });
2981
+ return access;
2982
+ } catch (err) {
2983
+ if (!(err instanceof AdminApiError) || err.status !== 404) {
2984
+ throw err;
2985
+ }
2986
+ }
2987
+ }
2988
+ fatal(
2989
+ `No object "${key}" in store "${store}" (looked in its private and public halves).`
2990
+ );
2991
+ }
2960
2992
  function storeOf(a) {
2961
2993
  return a.str("store") || DEFAULT_STORE;
2962
2994
  }
@@ -2967,8 +2999,8 @@ function requireKeyFlag(a) {
2967
2999
  }
2968
3000
  return key;
2969
3001
  }
2970
- function refOf(a, key) {
2971
- return { store: storeOf(a), access: resolveAccess(a), key };
3002
+ async function refOf(ctx, a, key) {
3003
+ return { store: storeOf(a), access: await readAccess(ctx, a, key), key };
2972
3004
  }
2973
3005
  async function filesPut(ctx, a) {
2974
3006
  const file = a.req("file");
@@ -2994,7 +3026,7 @@ async function filesPut(ctx, a) {
2994
3026
  }
2995
3027
  async function filesGet(ctx, a) {
2996
3028
  const key = a.req("key");
2997
- const ref = refOf(a, key);
3029
+ const ref = await refOf(ctx, a, key);
2998
3030
  const { url } = await sign(
2999
3031
  ctx,
3000
3032
  ref,
@@ -3004,7 +3036,9 @@ async function filesGet(ctx, a) {
3004
3036
  const abs = path2.isAbsolute(outPath) ? outPath : path2.join(WORKSPACE_DIR, outPath);
3005
3037
  const res = await fetch(url);
3006
3038
  if (!res.ok || !res.body) {
3007
- fatal(`Download failed: ${res.status} ${res.statusText}`);
3039
+ fatal(
3040
+ res.status === 403 || res.status === 404 ? `No object "${key}" in the ${ref.access} half of store "${ref.store}" (download answered ${res.status}).` : `Download failed: ${res.status} ${res.statusText}`
3041
+ );
3008
3042
  }
3009
3043
  await pipeline(
3010
3044
  Readable.fromWeb(res.body),
@@ -3013,16 +3047,18 @@ async function filesGet(ctx, a) {
3013
3047
  out({ path: abs, size: fs2.statSync(abs).size });
3014
3048
  }
3015
3049
  async function filesSign(ctx, a) {
3016
- out(await sign(ctx, refOf(a, requireKeyFlag(a)), a.num("ttl")));
3050
+ out(
3051
+ await sign(ctx, await refOf(ctx, a, requireKeyFlag(a)), a.num("ttl"))
3052
+ );
3017
3053
  }
3018
3054
  async function filesStat(ctx, a) {
3019
- out(await stat(ctx, refOf(a, requireKeyFlag(a))));
3055
+ out(await stat(ctx, await refOf(ctx, a, requireKeyFlag(a))));
3020
3056
  }
3021
3057
  async function filesLs(ctx, a) {
3022
3058
  out(
3023
3059
  await ls(ctx, {
3024
3060
  store: storeOf(a),
3025
- access: resolveAccess(a),
3061
+ access: await readAccess(ctx, a),
3026
3062
  prefix: a.str("prefix"),
3027
3063
  q: a.str("q"),
3028
3064
  cursor: a.str("cursor"),
@@ -3042,7 +3078,13 @@ async function filesRm(ctx, a) {
3042
3078
  if (!key) {
3043
3079
  fatal("--key is required.");
3044
3080
  }
3045
- out(await remove(ctx, { store, access: resolveAccess(a), key }));
3081
+ out(
3082
+ await remove(ctx, {
3083
+ store,
3084
+ access: await readAccess(ctx, a, key),
3085
+ key
3086
+ })
3087
+ );
3046
3088
  }
3047
3089
  var filesHandlers = {
3048
3090
  "files put": filesPut,
@@ -3066,12 +3108,12 @@ Subcommands:
3066
3108
 
3067
3109
  Usage:
3068
3110
  remy-admin files put [--public|--private] [--store <name>] [--key <key>] [--cache-control <value>] <file>
3069
- remy-admin files get [--private] [--store <name>] [--out <path>] <key>
3070
- remy-admin files sign [--private] [--store <name>] [--ttl <seconds>] --key <key>
3071
- remy-admin files stat [--private] [--store <name>] --key <key>
3072
- remy-admin files ls [--private] [--store <name>] [--prefix <prefix>] [--q <substring>] [--limit <n>]
3111
+ remy-admin files get [--public|--private] [--store <name>] [--out <path>] <key>
3112
+ remy-admin files sign [--public|--private] [--store <name>] [--ttl <seconds>] --key <key>
3113
+ remy-admin files stat [--public|--private] [--store <name>] --key <key>
3114
+ remy-admin files ls [--public|--private] [--store <name>] [--prefix <prefix>] [--q <substring>] [--limit <n>]
3073
3115
  remy-admin files list
3074
- remy-admin files rm --store <name> --key <key> [--private]
3116
+ remy-admin files rm --store <name> --key <key> [--public|--private]
3075
3117
 
3076
3118
  Examples:
3077
3119
  remy-admin files put --public ./hero.jpg
@@ -3081,7 +3123,10 @@ Examples:
3081
3123
  remy-admin files rm --store handoff --key <key> --private
3082
3124
 
3083
3125
  Notes:
3084
- - Defaults to a public store named 'assets'; pass --private / --store to change.
3126
+ - Defaults to the store named 'assets'. put writes public unless --private.
3127
+ Reads (get, sign, stat, ls, rm) without --public or --private use the half
3128
+ of the store that holds objects; in a store holding both, a key is found
3129
+ wherever it is, and ls asks which.
3085
3130
  - Handing someone a large or sensitive file? Use a PRIVATE store + \`sign\`:
3086
3131
  the link is unguessable, expires (--ttl seconds, max 7 days), and the object
3087
3132
  stays deletable with \`rm\`. Never use the account media CDN
@@ -3119,6 +3164,7 @@ var summarize = (d) => ({
3119
3164
  });
3120
3165
  async function addDocument(ctx, params) {
3121
3166
  const { slug, filename, content, metadata, entities: entities2, onProgress } = params;
3167
+ const { budgetDollars } = params;
3122
3168
  const contentHash = createHash2("sha256").update(content).digest("hex");
3123
3169
  const token = await call(
3124
3170
  ctx,
@@ -3134,11 +3180,13 @@ async function addDocument(ctx, params) {
3134
3180
  );
3135
3181
  if (token.alreadyCurrent) {
3136
3182
  onProgress?.(`${filename}: unchanged, skipped`);
3183
+ const documents3 = token.documents ?? [token.document];
3137
3184
  return {
3138
3185
  filename,
3139
3186
  skipped: true,
3140
3187
  document: token.document,
3141
- documents: token.documents ?? [token.document]
3188
+ documents: documents3,
3189
+ documentCount: token.documentCount ?? documents3.length
3142
3190
  };
3143
3191
  }
3144
3192
  onProgress?.(
@@ -3156,16 +3204,21 @@ async function addDocument(ctx, params) {
3156
3204
  contentType: token.contentType,
3157
3205
  size: content.length,
3158
3206
  ...metadata ? { metadata } : {},
3159
- ...entities2 ? { entities: entities2 } : {}
3207
+ ...entities2 ? { entities: entities2 } : {},
3208
+ ...budgetDollars !== void 0 ? { budgetDollars } : {}
3160
3209
  }
3161
3210
  );
3211
+ const documents2 = confirmed.documents ?? (confirmed.document ? [confirmed.document] : []);
3162
3212
  return {
3163
3213
  filename,
3164
3214
  skipped: false,
3165
3215
  queued: confirmed.queued,
3166
3216
  document: confirmed.document,
3167
- documents: confirmed.documents ?? (confirmed.document ? [confirmed.document] : []),
3168
- outcome: confirmed.outcome
3217
+ documents: documents2,
3218
+ documentCount: confirmed.documentCount ?? documents2.length,
3219
+ outcome: confirmed.outcome,
3220
+ invalid: confirmed.invalid ?? null,
3221
+ job: confirmed.job ?? null
3169
3222
  };
3170
3223
  }
3171
3224
  async function waitForIngest(ctx, params) {
@@ -3347,6 +3400,21 @@ function entities(ctx, params) {
3347
3400
  }
3348
3401
  );
3349
3402
  }
3403
+ function passages(ctx, params) {
3404
+ const { slug, filter, cursor, limit, candidate } = params;
3405
+ return call(
3406
+ ctx,
3407
+ "POST",
3408
+ `${base(ctx.appId)}/passages`,
3409
+ {
3410
+ slug,
3411
+ ...filter ? { filter } : {},
3412
+ ...cursor ? { cursor } : {},
3413
+ ...limit !== void 0 ? { limit } : {},
3414
+ ...candidate ? { candidate: true } : {}
3415
+ }
3416
+ );
3417
+ }
3350
3418
  function configGet(ctx, slug) {
3351
3419
  return call(
3352
3420
  ctx,
@@ -4870,12 +4938,14 @@ Dictionary:
4870
4938
  background (a relink; \`dictionary status\` shows it), with nothing
4871
4939
  re-embedded. An unchanged dictionary commits nothing.
4872
4940
 
4873
- Matching ignores case and a tag's separators (P-101A, P101A, p 101a), takes
4941
+ Matching ignores case and a tag's separators (P-101A, P101A, p 101a) and
4942
+ an inch or foot mark after a size (8"-P-1057-A1A is 8-P-1057-A1A), takes
4874
4943
  whole words only (P-101AB is not P-101A), and prefers the longest name; a
4875
4944
  name two ids claim tags both. A name needs three letters or digits (two in
4876
4945
  Chinese, Japanese or Korean). The push reports ambiguous names, the most
4877
- claimed, and names that can't match their own text as written
4878
- (\`unmatchable\`). Up to 250,000 entries, 16 names each, a million names.
4946
+ claimed (each with one name as pushed), and names that can't match their
4947
+ own text as written (\`unmatchable\`). Up to 250,000 entries, 16 names
4948
+ each, a million names.
4879
4949
 
4880
4950
  \`link\` shows what the current dictionary finds in any text: check one
4881
4951
  there before relying on it. --linker-version moves a dictionary to a newer
@@ -5179,7 +5249,8 @@ function mapTest(ctx, params) {
5179
5249
  ...selectionBody(params.selection),
5180
5250
  ...params.keys ? { keys: params.keys } : {},
5181
5251
  ...params.limit !== void 0 ? { limit: params.limit } : {},
5182
- ...params.dev ? { dev: true } : {}
5252
+ ...params.dev ? { dev: true } : {},
5253
+ ...params.releaseId ? { releaseId: params.releaseId } : {}
5183
5254
  }
5184
5255
  );
5185
5256
  }
@@ -5245,12 +5316,13 @@ var dataSourceMappersSpecs = {
5245
5316
  }
5246
5317
  },
5247
5318
  "datasources map test": {
5248
- usage: "Usage: remy-admin datasources map test [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --connector) [--keys k1,k2,...] [--limit <n>] [--dev] [--full]",
5319
+ usage: "Usage: remy-admin datasources map test [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --connector) [--keys k1,k2,...] [--limit <n>] [--dev [--release <dev-release-id>]] [--full]",
5249
5320
  flags: {
5250
5321
  source: { type: "string" },
5251
5322
  ...SELECTION_FLAGS,
5252
5323
  limit: { type: "number", min: 1 },
5253
5324
  dev: { type: "boolean" },
5325
+ release: { type: "string" },
5254
5326
  full: { type: "boolean" }
5255
5327
  },
5256
5328
  requireAnyOf: {
@@ -5362,7 +5434,8 @@ async function mapTest2(ctx, a) {
5362
5434
  selection: selectionOf(a),
5363
5435
  ...keysOf(a) ? { keys: keysOf(a) } : {},
5364
5436
  ...limit !== void 0 ? { limit } : {},
5365
- ...a.bool("dev") ? { dev: true } : {}
5437
+ ...a.bool("dev") ? { dev: true } : {},
5438
+ ...a.str("release") ? { releaseId: a.str("release") } : {}
5366
5439
  });
5367
5440
  const results = a.bool("full") ? result.results : result.results.map(
5368
5441
  (r) => r.outcome.kind === "documents" ? {
@@ -5496,7 +5569,7 @@ var dataSourceMappersHandlers = {
5496
5569
  var dataSourceMappersHelp = `
5497
5570
  Mapping raw objects into documents (mappers):
5498
5571
  remy-admin datasources inspect [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>]
5499
- remy-admin datasources map test [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>] [--dev] [--full]
5572
+ remy-admin datasources map test [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>] [--dev [--release <id>]] [--full]
5500
5573
  remy-admin datasources map deploy [--source <slug>] [--release <id>]
5501
5574
  remy-admin datasources remap [--source <slug>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
5502
5575
  remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]
@@ -5511,14 +5584,21 @@ Mapping raw objects into documents (mappers):
5511
5584
  map(object) reads the raw object (object.key, .bytes(), .text(), .json())
5512
5585
  and answers documents([{ externalId, title, markdown, metadata?, replaces? }]),
5513
5586
  passthrough() (ingest the raw object as-is), skip(reason) or
5514
- deletes([externalId, ...]); throwing is the object's error. It may call
5515
- models or fetch \u2014 every call is spend per object. Everything entering a
5516
- mapped source is mapped: jobs, syncs and Source.add() alike.
5587
+ deletes([externalId, ...]); throwing is the object's error. documents()
5588
+ takes an array or any iterable, with no cap: the platform stores what it
5589
+ produces as it goes, so a generator yielding a document per row streams an
5590
+ export of any size. It may call models or fetch \u2014 every call is spend per
5591
+ object. Everything entering
5592
+ a mapped source is mapped: jobs, syncs and Source.add() alike; an add() of
5593
+ an object mapping to more than a thousand documents registers the first
5594
+ thousand and hands the rest to a job (\`datasources add --budget\` caps it).
5517
5595
 
5518
5596
  The loop: \`inspect\` profiles the objects (key shapes, extensions, sizes,
5519
5597
  JSON key signatures of ten sampled heads) before a line is written;
5520
5598
  \`map test --dev\` runs the LOCAL mapper through the running dev session
5521
- over real objects and prints every outcome without ingesting anything;
5599
+ over real objects and prints every outcome without ingesting anything
5600
+ (each object's count, its first documents, and every document that fails
5601
+ validation, by its place in the mapper's output);
5522
5602
  push a branch and wait for its build (\`releases wait\`) \u2014 a mapper runs on
5523
5603
  the platform, so the platform has to compile it, and a private branch
5524
5604
  build is all it needs; \`map deploy\` makes that build's mapper the
@@ -5537,7 +5617,11 @@ Mapping raw objects into documents (mappers):
5537
5617
  ids this document supersedes (a writethru's original); deletes([...])
5538
5618
  removes by id. A plan on a mapped source records the mapper's outcome mix
5539
5619
  on its sample; a run whose skip share climbs past twice that (floor 5%)
5540
- pauses with pauseReason 'skips' for a look at the quarantine.
5620
+ pauses with pauseReason 'skips' for a look at the quarantine. Documents
5621
+ that fail validation are left out and their object gets one quarantine row
5622
+ naming them; the rest register. A mapper built with an older
5623
+ @mindstudio-ai/agent, which returns documents inline, errors every
5624
+ documents outcome until it is upgraded, pushed and map deployed.
5541
5625
  `;
5542
5626
 
5543
5627
  // src/ops/dataSourceReaders.ts
@@ -5552,7 +5636,8 @@ function readerTest(ctx, params) {
5552
5636
  {
5553
5637
  slug: params.slug,
5554
5638
  ...params.documents ? { documents: params.documents } : {},
5555
- ...params.limit !== void 0 ? { limit: params.limit } : {}
5639
+ ...params.limit !== void 0 ? { limit: params.limit } : {},
5640
+ ...params.releaseId ? { releaseId: params.releaseId } : {}
5556
5641
  }
5557
5642
  );
5558
5643
  }
@@ -5593,12 +5678,13 @@ function reread(ctx, params) {
5593
5678
  // src/commands/dataSourceReaders.ts
5594
5679
  var dataSourceReadersSpecs = {
5595
5680
  "datasources reader test": {
5596
- usage: "Usage: remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2,...] [--limit <n>]",
5681
+ usage: "Usage: remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2,...] [--limit <n>] [--release <dev-release-id>]",
5597
5682
  flags: {
5598
5683
  source: { type: "string" },
5599
5684
  dev: { type: "boolean" },
5600
5685
  documents: { type: "string" },
5601
- limit: { type: "number", min: 1 }
5686
+ limit: { type: "number", min: 1 },
5687
+ release: { type: "string" }
5602
5688
  }
5603
5689
  },
5604
5690
  "datasources reader status": {
@@ -5643,7 +5729,8 @@ async function readerTest2(ctx, a) {
5643
5729
  const result = await readerTest(ctx, {
5644
5730
  slug,
5645
5731
  ...documents2 ? { documents: documents2 } : {},
5646
- ...limit !== void 0 ? { limit } : {}
5732
+ ...limit !== void 0 ? { limit } : {},
5733
+ ...a.str("release") ? { releaseId: a.str("release") } : {}
5647
5734
  });
5648
5735
  out({
5649
5736
  dataSource: slug,
@@ -5714,7 +5801,7 @@ var dataSourceReadersHandlers = {
5714
5801
  };
5715
5802
  var dataSourceReadersHelp = `
5716
5803
  Reading documents into the app (readers):
5717
- remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2] [--limit <n>]
5804
+ remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2] [--limit <n>] [--release <id>]
5718
5805
  remy-admin datasources reader status [--source <slug>]
5719
5806
  remy-admin datasources reader retry [--source <slug>] [--job <id>]
5720
5807
  remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
@@ -5725,12 +5812,15 @@ Reading documents into the app (readers):
5725
5812
  platform's batching, retries, budgets and pauses. Declare it in
5726
5813
  mindstudio.json:
5727
5814
  "dataSources": [{ "slug": "archive", "reader": { "path": "dist/datasources/archive.reader.ts" } }]
5728
- The file exports defineReader(Archive, { read, removed, cleared? }) from
5729
- @mindstudio-ai/agent. read(document) gets the document (id, sourceId,
5730
- filename, metadata, entities) and document.chunks(), its passages as
5731
- indexed with each one's entities; removed(documentId) is called when a
5732
- document leaves the source; cleared(sourceId) once when the source is
5733
- deleted. Throwing fails that document, which is retried. A document can be
5815
+ The file exports defineReader(Archive, { read, removed, cleared?,
5816
+ relinked? }) from @mindstudio-ai/agent. read(document) gets the document
5817
+ (id, sourceId, filename, metadata, entities, url), document.chunks(), its
5818
+ passages as indexed with each one's entities, and document.bytes(), the
5819
+ file it came from; removed(documentId) is called when a document leaves
5820
+ the source; cleared(sourceId) once when the source is deleted; relinked(
5821
+ document) when a new dictionary re-tagged a document already read, for
5822
+ rows derived from its tags. Throwing fails that document, which is
5823
+ retried. A document can be
5734
5824
  read more than once, so read() replaces the document's rows as a set,
5735
5825
  keyed by its id, and removed() deletes them. Options on the declaration:
5736
5826
  timeoutMs (per document, default 60000, max 300000) and concurrency
@@ -5792,12 +5882,13 @@ var CONFIG_FLAGS = {
5792
5882
  };
5793
5883
  var dataSourcesSpecs = {
5794
5884
  "datasources add": {
5795
- usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>",
5885
+ usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--budget <dollars>] [--wait] [--timeout <sec>] <file...>",
5796
5886
  positionals: [{ name: "file", required: true, variadic: true }],
5797
5887
  flags: {
5798
5888
  source: { type: "string" },
5799
5889
  metadata: { type: "string" },
5800
5890
  entities: { type: "string" },
5891
+ budget: { type: "number", min: 0.01 },
5801
5892
  wait: { type: "boolean" },
5802
5893
  timeout: { type: "string" }
5803
5894
  }
@@ -5885,6 +5976,19 @@ var dataSourcesSpecs = {
5885
5976
  candidate: { type: "boolean" }
5886
5977
  }
5887
5978
  },
5979
+ "datasources passages": {
5980
+ usage: "Usage: remy-admin datasources passages [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--cursor <cursor>] [--limit <n>] [--candidate]",
5981
+ flags: {
5982
+ source: { type: "string" },
5983
+ filter: { type: "string" },
5984
+ entities: { type: "string" },
5985
+ contains: { type: "string" },
5986
+ phrase: { type: "string" },
5987
+ cursor: { type: "string" },
5988
+ limit: { type: "number", min: 1 },
5989
+ candidate: { type: "boolean" }
5990
+ }
5991
+ },
5888
5992
  "datasources move": {
5889
5993
  usage: "Usage: remy-admin datasources move [--source <slug>] --to <resource-id|shared> [--chains <n>] [--restart] [--wait] [--timeout <sec>] | --stop",
5890
5994
  flags: {
@@ -5928,6 +6032,7 @@ async function dataSourcesAdd(ctx, a) {
5928
6032
  const slug = sourceOf(a);
5929
6033
  const metadata = parseKeyValuePairs(a.str("metadata"), "metadata");
5930
6034
  const entities2 = parseIdList(a.str("entities"));
6035
+ const budgetDollars = a.num("budget");
5931
6036
  const results = [];
5932
6037
  for (const file of files) {
5933
6038
  const abs = path6.isAbsolute(file) ? file : path6.join(WORKSPACE_DIR, file);
@@ -5945,10 +6050,22 @@ async function dataSourcesAdd(ctx, a) {
5945
6050
  content: bytes,
5946
6051
  ...metadata ? { metadata } : {},
5947
6052
  ...entities2 ? { entities: entities2 } : {},
6053
+ ...budgetDollars !== void 0 ? { budgetDollars } : {},
5948
6054
  onProgress: progress
5949
6055
  })
5950
6056
  );
5951
6057
  }
6058
+ const jobs = results.flatMap(
6059
+ (r) => r.job ? [
6060
+ {
6061
+ file: r.filename,
6062
+ documents: r.documentCount,
6063
+ job: r.job.id,
6064
+ state: r.job.state,
6065
+ follow: `remy-admin datasources jobs status ${r.job.id}`
6066
+ }
6067
+ ] : []
6068
+ );
5952
6069
  if (a.bool("wait")) {
5953
6070
  const timeoutSec = a.num("timeout");
5954
6071
  const timeoutMs = timeoutSec ? timeoutSec * 1e3 : DEFAULT_WAIT_TIMEOUT_MS;
@@ -5964,7 +6081,8 @@ async function dataSourcesAdd(ctx, a) {
5964
6081
  dataSource: slug,
5965
6082
  status: result.status,
5966
6083
  ...result.error ? { error: result.error } : {},
5967
- documents: result.documents
6084
+ documents: result.documents,
6085
+ ...jobs.length > 0 ? { jobs } : {}
5968
6086
  });
5969
6087
  if (result.status === "timeout") {
5970
6088
  throw new CliError(result.error ?? "Timed out.", EXIT.timeout);
@@ -5974,7 +6092,11 @@ async function dataSourcesAdd(ctx, a) {
5974
6092
  }
5975
6093
  return;
5976
6094
  }
5977
- out({ dataSource: slug, documents: results });
6095
+ out({
6096
+ dataSource: slug,
6097
+ documents: results.map(({ job, ...r }) => r),
6098
+ ...jobs.length > 0 ? { jobs } : {}
6099
+ });
5978
6100
  }
5979
6101
  async function dataSourcesList(ctx) {
5980
6102
  out(await list8(ctx));
@@ -6098,6 +6220,22 @@ async function dataSourcesEntities(ctx, a) {
6098
6220
  })
6099
6221
  );
6100
6222
  }
6223
+ async function dataSourcesPassages(ctx, a) {
6224
+ const filter = filterFromFlags(a);
6225
+ const limit = a.num("limit");
6226
+ const cursor = a.str("cursor");
6227
+ const result = await passages(ctx, {
6228
+ slug: sourceOf(a),
6229
+ ...filter ? { filter } : {},
6230
+ ...cursor ? { cursor } : {},
6231
+ ...limit !== void 0 ? { limit } : {},
6232
+ ...a.bool("candidate") ? { candidate: true } : {}
6233
+ });
6234
+ out({
6235
+ ...result,
6236
+ ...result.nextCursor ? { note: `More: pass --cursor ${result.nextCursor} for the next page.` } : {}
6237
+ });
6238
+ }
6101
6239
  function parseIdList(raw) {
6102
6240
  if (raw === void 0) {
6103
6241
  return void 0;
@@ -6362,6 +6500,7 @@ var dataSourcesHandlers = {
6362
6500
  "datasources config": dataSourcesConfig,
6363
6501
  "datasources count": dataSourcesCount,
6364
6502
  "datasources entities": dataSourcesEntities,
6503
+ "datasources passages": dataSourcesPassages,
6365
6504
  "datasources move": dataSourcesMove,
6366
6505
  "datasources revectorize": dataSourcesRevectorize,
6367
6506
  "datasources promote": dataSourcesPromote,
@@ -6379,6 +6518,11 @@ Subcommands:
6379
6518
  status Show a source's counts and its failed documents (--all walks every document)
6380
6519
  rm Remove a document, or every document matching a filter
6381
6520
  search Query a corpus \u2014 useful to sanity-check one you just built
6521
+ count How many passages match a filter (words, a phrase, entities, metadata)
6522
+ passages Every passage matching a filter, unranked, a page at a time
6523
+ entities Which entities the matching passages are about, most first
6524
+ dictionary push|status: send a source its dictionary of names, or show the current one
6525
+ link What the source's dictionary finds in a piece of text
6382
6526
  create Create an empty source, optionally on dedicated capacity
6383
6527
  config Show or change how a corpus is processed and searched
6384
6528
  move Move a corpus between shared and dedicated capacity, data intact
@@ -6391,6 +6535,8 @@ Subcommands:
6391
6535
  map test Run a source's mapper over real objects and show the outcomes; nothing ingested
6392
6536
  map deploy Make a build's compiled mapper the source's active one (a branch build will do)
6393
6537
  remap Re-apply the active mapper to every raw copy the source holds
6538
+ reader test|status|retry: run the dev session's reader, see its deliveries, resend failed ones
6539
+ reread Read a source's documents again with its live reader, priced first
6394
6540
  hydrate Reload an evicted index ahead of the first search
6395
6541
  reindex Recreate a dedicated source's collection with the current shape, refilled from stored vectors
6396
6542
  sample Draw a sample of a source into a new one with the same config
@@ -6401,13 +6547,14 @@ Subcommands:
6401
6547
  delete Delete a whole data source \u2014 documents, vectors and versions
6402
6548
 
6403
6549
  Usage:
6404
- remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>
6550
+ remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--budget <dollars>] [--wait] [--timeout <sec>] <file...>
6405
6551
  remy-admin datasources list
6406
6552
  remy-admin datasources status [--source <slug>] [--status error] [--all]
6407
6553
  remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)
6408
6554
  remy-admin datasources search [--source <slug>] [search options] <query>
6409
6555
  remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]
6410
6556
  remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--limit <n>] [--candidate]
6557
+ remy-admin datasources passages [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--cursor <c>] [--limit <n>] [--candidate]
6411
6558
  remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
6412
6559
  remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]
6413
6560
  remy-admin datasources move [--source <slug>] --to <resource-id|shared> [--chains <n>] [--restart] [--wait] [--timeout <sec>]
@@ -6541,6 +6688,10 @@ Notes:
6541
6688
  --metadata tags documents for search-time filtering (scalars, \u226416 keys).
6542
6689
  --entities names what a document is about, as your own ids (\u2264256, no spaces
6543
6690
  or commas); an empty --entities clears them. Also free on a re-add.
6691
+ On a mapped source, a file that maps to more than a thousand documents adds
6692
+ the first thousand and starts a job for the rest, listed under \`jobs\` in
6693
+ the output; --budget caps that job. --wait waits for the first thousand;
6694
+ follow the job with \`jobs status <id> --wait\`.
6544
6695
  rm --filter removes every matching document in pages of a thousand: k=v
6545
6696
  pairs match metadata; a JSON object takes the full selector \u2014 metadata,
6546
6697
  filename, documentIds, externalIdPrefix (the key a job or connector
@@ -9264,32 +9415,60 @@ function resolveRoute(argv) {
9264
9415
  if (sub && KEYS.has(two)) {
9265
9416
  return { key: two, argv: argv.slice(2) };
9266
9417
  }
9267
- if (group === "domains" && sub === "custom") {
9268
- return {
9269
- error: `Unknown subcommand: domains custom ${action ?? ""}. Run 'remy-admin domains --help'`
9270
- };
9418
+ if (group === "db" && sub) {
9419
+ return { key: "db query", argv: argv.slice(1) };
9271
9420
  }
9272
- if (group === "settings" && ["allowlist", "test-accounts", "frame-ancestors"].includes(sub ?? "")) {
9273
- return {
9274
- error: `Unknown subcommand: settings ${sub} ${action ?? ""}. Run 'remy-admin settings --help'`
9275
- };
9421
+ return { error: unknownSubcommand(group, sub, action) };
9422
+ }
9423
+ function keysUnder(words) {
9424
+ const prefix = `${words.join(" ")} `;
9425
+ return [...KEYS].filter((key) => key.startsWith(prefix)).sort();
9426
+ }
9427
+ function unknownSubcommand(group, sub, action) {
9428
+ const asked = [group, sub, action].filter(Boolean).join(" ");
9429
+ const nested = sub ? keysUnder([group, sub]) : [];
9430
+ if (nested.length > 0) {
9431
+ const actions = nested.map((key) => key.split(" ")[2]);
9432
+ return `Unknown subcommand: ${asked}. '${group} ${sub}' takes: ${actions.join(", ")}. Run 'remy-admin ${group} ${sub} --help' for their usage.`;
9276
9433
  }
9277
- if (group === "datasources" && (sub === "jobs" || sub === "eval" || sub === "map")) {
9278
- return {
9279
- error: `Unknown subcommand: datasources ${sub} ${action ?? ""}. Run 'remy-admin datasources --help'`
9280
- };
9434
+ const subs = [...new Set(keysUnder([group]).map((key) => key.split(" ")[1]))];
9435
+ return `Unknown subcommand: ${asked}. '${group}' takes: ${subs.join(", ")}. Run 'remy-admin ${group} --help'.`;
9436
+ }
9437
+ function leadingWords(argv) {
9438
+ const words = [];
9439
+ for (const token of argv) {
9440
+ if (token.startsWith("-")) {
9441
+ break;
9442
+ }
9443
+ words.push(token);
9281
9444
  }
9282
- if (group === "email" && ["domains", "inbound-domains"].includes(sub ?? "")) {
9283
- return {
9284
- error: `Unknown subcommand: email ${sub} ${action ?? ""}. Run 'remy-admin email --help'`
9285
- };
9445
+ return words;
9446
+ }
9447
+ function commandHelp(argv) {
9448
+ const words = leadingWords(argv);
9449
+ const group = words[0];
9450
+ const guide = `Run 'remy-admin ${group} --help' for the ${group} guide.`;
9451
+ for (let n = Math.min(words.length, 3); n >= 2; n--) {
9452
+ const key = words.slice(0, n).join(" ");
9453
+ if (KEYS.has(key)) {
9454
+ return `${SPECS[key].usage}
9455
+
9456
+ ${guide}`;
9457
+ }
9286
9458
  }
9287
- if (group === "db" && sub) {
9288
- return { key: "db query", argv: argv.slice(1) };
9459
+ if (words.length >= 2) {
9460
+ const under = keysUnder(words.slice(0, 2));
9461
+ if (under.length > 0) {
9462
+ const usages = under.map(
9463
+ (key) => ` ${SPECS[key].usage.replace(/^Usage:\s*/, "")}`
9464
+ );
9465
+ return `Usage:
9466
+ ${usages.join("\n")}
9467
+
9468
+ ${guide}`;
9469
+ }
9289
9470
  }
9290
- return {
9291
- error: `Unknown subcommand: ${group} ${sub ?? ""}. Run 'remy-admin ${group} --help'`
9292
- };
9471
+ return void 0;
9293
9472
  }
9294
9473
  function resolveHelp(argv) {
9295
9474
  const group = argv[0];
@@ -9304,7 +9483,10 @@ function resolveHelp(argv) {
9304
9483
  return groupHelp;
9305
9484
  }
9306
9485
  const scan = group === "db" ? argv.slice(1, argv[1] === "query" ? 3 : 2) : argv.slice(1);
9307
- return scan.some((t) => t === "--help" || t === "-h") ? groupHelp : void 0;
9486
+ if (!scan.some((t) => t === "--help" || t === "-h")) {
9487
+ return void 0;
9488
+ }
9489
+ return commandHelp(argv) ?? groupHelp;
9308
9490
  }
9309
9491
 
9310
9492
  // src/prod.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madewithremy/admin",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "Admin SDK + CLI for managing production Remy apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",