@madewithremy/admin 0.1.30 → 0.1.31

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
@@ -36,10 +36,10 @@ function fatal(message) {
36
36
  throw new CliError(message);
37
37
  }
38
38
  var AdminApiError = class extends Error {
39
- constructor(method, path7, status2, body) {
40
- super(`API ${method} ${path7} returned ${status2}: ${JSON.stringify(body)}`);
39
+ constructor(method, path8, status2, body) {
40
+ super(`API ${method} ${path8} returned ${status2}: ${JSON.stringify(body)}`);
41
41
  this.method = method;
42
- this.path = path7;
42
+ this.path = path8;
43
43
  this.status = status2;
44
44
  this.body = body;
45
45
  this.name = "AdminApiError";
@@ -1036,6 +1036,13 @@ function summarizeRelease(release) {
1036
1036
  publishedAt: release.publishedAt ?? null,
1037
1037
  ...release.previewUrl ? { previewUrl: release.previewUrl } : {}
1038
1038
  };
1039
+ const readers = (release.dataSources ?? []).filter((d) => d.reader);
1040
+ if (readers.length > 0 && release.status === "live") {
1041
+ summary2.readers = readers.map((d) => ({
1042
+ slug: d.slug,
1043
+ unread: d.readerUnread ?? 0
1044
+ }));
1045
+ }
1039
1046
  if (release.status !== "failed") {
1040
1047
  return summary2;
1041
1048
  }
@@ -3092,8 +3099,8 @@ Notes:
3092
3099
  - Public image URLs accept transform params, e.g. ?w=400&fit=cover.`;
3093
3100
 
3094
3101
  // src/commands/dataSources.ts
3095
- import fs5 from "fs";
3096
- import path5 from "path";
3102
+ import fs6 from "fs";
3103
+ import path6 from "path";
3097
3104
 
3098
3105
  // src/ops/dataSources.ts
3099
3106
  import { createHash as createHash2 } from "crypto";
@@ -3111,7 +3118,7 @@ var summarize = (d) => ({
3111
3118
  ...d.errorMessage ? { error: d.errorMessage } : {}
3112
3119
  });
3113
3120
  async function addDocument(ctx, params) {
3114
- const { slug, filename, content, metadata, onProgress } = params;
3121
+ const { slug, filename, content, metadata, entities: entities2, onProgress } = params;
3115
3122
  const contentHash = createHash2("sha256").update(content).digest("hex");
3116
3123
  const token = await call(
3117
3124
  ctx,
@@ -3121,7 +3128,8 @@ async function addDocument(ctx, params) {
3121
3128
  slug,
3122
3129
  filename,
3123
3130
  contentHash,
3124
- ...metadata ? { metadata } : {}
3131
+ ...metadata ? { metadata } : {},
3132
+ ...entities2 ? { entities: entities2 } : {}
3125
3133
  }
3126
3134
  );
3127
3135
  if (token.alreadyCurrent) {
@@ -3147,7 +3155,8 @@ async function addDocument(ctx, params) {
3147
3155
  contentHash,
3148
3156
  contentType: token.contentType,
3149
3157
  size: content.length,
3150
- ...metadata ? { metadata } : {}
3158
+ ...metadata ? { metadata } : {},
3159
+ ...entities2 ? { entities: entities2 } : {}
3151
3160
  }
3152
3161
  );
3153
3162
  return {
@@ -3324,6 +3333,20 @@ function count(ctx, params) {
3324
3333
  ...candidate ? { candidate: true } : {}
3325
3334
  });
3326
3335
  }
3336
+ function entities(ctx, params) {
3337
+ const { slug, filter, limit, candidate } = params;
3338
+ return call(
3339
+ ctx,
3340
+ "POST",
3341
+ `${base(ctx.appId)}/entities`,
3342
+ {
3343
+ slug,
3344
+ ...filter ? { filter } : {},
3345
+ ...limit !== void 0 ? { limit } : {},
3346
+ ...candidate ? { candidate: true } : {}
3347
+ }
3348
+ );
3349
+ }
3327
3350
  function configGet(ctx, slug) {
3328
3351
  return call(
3329
3352
  ctx,
@@ -3485,6 +3508,67 @@ function deleteSource(ctx, slug) {
3485
3508
  { slug }
3486
3509
  );
3487
3510
  }
3511
+ var DICTIONARY_PAGE_ENTRIES = 1e4;
3512
+ var DICTIONARY_PAGE_BYTES = 4e6;
3513
+ async function pushDictionary(ctx, params) {
3514
+ const { slug, entries } = params;
3515
+ const stage = await call(
3516
+ ctx,
3517
+ "POST",
3518
+ `${base(ctx.appId)}/dictionary/stage`,
3519
+ {
3520
+ slug,
3521
+ ...params.linkerVersion !== void 0 ? { linkerVersion: params.linkerVersion } : {}
3522
+ }
3523
+ );
3524
+ const perPage = Math.min(
3525
+ DICTIONARY_PAGE_ENTRIES,
3526
+ stage.pageMaxEntries || DICTIONARY_PAGE_ENTRIES
3527
+ );
3528
+ let pages2 = 0;
3529
+ let start2 = 0;
3530
+ while (start2 < entries.length) {
3531
+ let end = start2;
3532
+ let bytes = 0;
3533
+ while (end < entries.length && end - start2 < perPage) {
3534
+ const size = JSON.stringify(entries[end]).length;
3535
+ if (end > start2 && bytes + size > DICTIONARY_PAGE_BYTES) {
3536
+ break;
3537
+ }
3538
+ bytes += size;
3539
+ end += 1;
3540
+ }
3541
+ await call(ctx, "POST", `${base(ctx.appId)}/dictionary/page`, {
3542
+ slug,
3543
+ stageId: stage.stageId,
3544
+ page: pages2,
3545
+ entries: entries.slice(start2, end)
3546
+ });
3547
+ pages2 += 1;
3548
+ start2 = end;
3549
+ params.onProgress?.(start2, entries.length);
3550
+ }
3551
+ return call(
3552
+ ctx,
3553
+ "POST",
3554
+ `${base(ctx.appId)}/dictionary/commit`,
3555
+ { slug, stageId: stage.stageId, pages: pages2 }
3556
+ );
3557
+ }
3558
+ function dictionary(ctx, params) {
3559
+ return call(
3560
+ ctx,
3561
+ "POST",
3562
+ `${base(ctx.appId)}/dictionary`,
3563
+ { slug: params.slug }
3564
+ );
3565
+ }
3566
+ function link(ctx, params) {
3567
+ return call(ctx, "POST", `${base(ctx.appId)}/link`, {
3568
+ slug: params.slug,
3569
+ text: params.text
3570
+ });
3571
+ }
3488
3572
 
3489
3573
  // src/ops/dataSourceJobs.ts
3490
3574
  var DEFAULT_WAIT_TIMEOUT_MS2 = 60 * 60 * 1e3;
@@ -3643,6 +3727,39 @@ function describeProgress(job, start2) {
3643
3727
  // src/commands/_shared/dataSources.ts
3644
3728
  var DEFAULT_SOURCE = "default";
3645
3729
  var sourceOf = (a) => a.str("source") || DEFAULT_SOURCE;
3730
+ function parseKeyValuePairs(raw, flag) {
3731
+ if (raw === void 0) {
3732
+ return void 0;
3733
+ }
3734
+ const pairs2 = {};
3735
+ for (const part of raw.split(",")) {
3736
+ const trimmed = part.trim();
3737
+ if (!trimmed) {
3738
+ continue;
3739
+ }
3740
+ const eq = trimmed.indexOf("=");
3741
+ if (eq <= 0) {
3742
+ fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
3743
+ }
3744
+ const key = trimmed.slice(0, eq).trim();
3745
+ const value = trimmed.slice(eq + 1).trim();
3746
+ pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
3747
+ }
3748
+ if (Object.keys(pairs2).length === 0) {
3749
+ fatal(`--${flag} needs at least one key=value pair.`);
3750
+ }
3751
+ return pairs2;
3752
+ }
3753
+ function documentSelectorOf(raw) {
3754
+ if (raw.trimStart().startsWith("{")) {
3755
+ try {
3756
+ return JSON.parse(raw);
3757
+ } catch (err) {
3758
+ fatal(`--filter is not valid JSON: ${err.message}`);
3759
+ }
3760
+ }
3761
+ return { metadata: parseKeyValuePairs(raw, "filter") };
3762
+ }
3646
3763
  var timeoutOf = (a) => {
3647
3764
  const seconds = a.str("timeout");
3648
3765
  return seconds ? { timeoutMs: Number(seconds) * 1e3 } : {};
@@ -4658,9 +4775,116 @@ Retrieval evals:
4658
4775
  with --candidate, compare, promote or drop.
4659
4776
  `;
4660
4777
 
4661
- // src/commands/dataSourceJobs.ts
4778
+ // src/commands/dataSourceDictionary.ts
4662
4779
  import fs4 from "fs";
4663
4780
  import path4 from "path";
4781
+ var dataSourceDictionarySpecs = {
4782
+ "datasources dictionary push": {
4783
+ usage: "Usage: remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>",
4784
+ positionals: [{ name: "file", required: true }],
4785
+ flags: {
4786
+ source: { type: "string" },
4787
+ "linker-version": { type: "number", min: 1 }
4788
+ }
4789
+ },
4790
+ "datasources dictionary status": {
4791
+ usage: "Usage: remy-admin datasources dictionary status [--source <slug>]",
4792
+ flags: {
4793
+ source: { type: "string" }
4794
+ }
4795
+ },
4796
+ "datasources link": {
4797
+ usage: "Usage: remy-admin datasources link [--source <slug>] <text>",
4798
+ positionals: [{ name: "text", required: true }],
4799
+ flags: {
4800
+ source: { type: "string" }
4801
+ }
4802
+ }
4803
+ };
4804
+ function readEntries(file) {
4805
+ const abs = path4.isAbsolute(file) ? file : path4.join(WORKSPACE_DIR, file);
4806
+ let content;
4807
+ try {
4808
+ content = fs4.readFileSync(abs, "utf8");
4809
+ } catch (err) {
4810
+ fatal(`Could not read "${file}": ${err.message}`);
4811
+ }
4812
+ const entries = [];
4813
+ content.split("\n").forEach((line, i) => {
4814
+ const trimmed = line.trim();
4815
+ if (!trimmed) {
4816
+ return;
4817
+ }
4818
+ let parsed;
4819
+ try {
4820
+ parsed = JSON.parse(trimmed);
4821
+ } catch {
4822
+ fatal(`${file}:${i + 1} is not valid JSON.`);
4823
+ }
4824
+ if (typeof parsed?.id !== "string" || !Array.isArray(parsed?.names) || !parsed.names.every((name) => typeof name === "string")) {
4825
+ fatal(`${file}:${i + 1} needs { "id": "...", "names": ["...", ...] }.`);
4826
+ }
4827
+ entries.push({ id: parsed.id, names: parsed.names });
4828
+ });
4829
+ if (entries.length === 0) {
4830
+ fatal(`"${file}" has no entries.`);
4831
+ }
4832
+ return entries;
4833
+ }
4834
+ async function dictionaryPush(ctx, a) {
4835
+ const entries = readEntries(a.req("file"));
4836
+ const linkerVersion = a.num("linker-version");
4837
+ out(
4838
+ await pushDictionary(ctx, {
4839
+ slug: sourceOf(a),
4840
+ entries,
4841
+ ...linkerVersion !== void 0 ? { linkerVersion } : {},
4842
+ onProgress: (sent, total) => progress(`pushing ${sent.toLocaleString()}/${total.toLocaleString()}\u2026`)
4843
+ })
4844
+ );
4845
+ }
4846
+ async function dictionaryStatus(ctx, a) {
4847
+ out(await dictionary(ctx, { slug: sourceOf(a) }));
4848
+ }
4849
+ async function link2(ctx, a) {
4850
+ out(await link(ctx, { slug: sourceOf(a), text: a.req("text") }));
4851
+ }
4852
+ var dataSourceDictionaryHandlers = {
4853
+ "datasources dictionary push": dictionaryPush,
4854
+ "datasources dictionary status": dictionaryStatus,
4855
+ "datasources link": link2
4856
+ };
4857
+ var dataSourceDictionaryHelp = `
4858
+ Dictionary:
4859
+ remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>
4860
+ remy-admin datasources dictionary status [--source <slug>]
4861
+ remy-admin datasources link [--source <slug>] <text>
4862
+
4863
+ A dictionary is your own entity ids and the names each goes by, a
4864
+ projection of your tables: one JSONL line per entry,
4865
+ {"id": "P-101A", "names": ["P-101A", "Main Feed Pump"]}. Push it whole; it
4866
+ replaces the source's dictionary. From then on every passage carries the
4867
+ ids whose names its text mentions, beside the ones its document asserts,
4868
+ so --entities on search and count, and \`datasources entities\`, find a tag
4869
+ wherever it's written. Passages indexed before are re-tagged in the
4870
+ background (a relink; \`dictionary status\` shows it), with nothing
4871
+ re-embedded. An unchanged dictionary commits nothing.
4872
+
4873
+ Matching ignores case and a tag's separators (P-101A, P101A, p 101a), takes
4874
+ whole words only (P-101AB is not P-101A), and prefers the longest name; a
4875
+ name two ids claim tags both. A name needs three letters or digits (two in
4876
+ 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.
4879
+
4880
+ \`link\` shows what the current dictionary finds in any text: check one
4881
+ there before relying on it. --linker-version moves a dictionary to a newer
4882
+ matching rule; without it a push keeps the one its dictionary has.
4883
+ `;
4884
+
4885
+ // src/commands/dataSourceJobs.ts
4886
+ import fs5 from "fs";
4887
+ import path5 from "path";
4664
4888
  var dataSourceJobsSpecs = {
4665
4889
  "datasources jobs start": {
4666
4890
  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>]",
@@ -4738,10 +4962,10 @@ async function jobsStart(ctx, a) {
4738
4962
  }
4739
4963
  let source;
4740
4964
  if (manifest) {
4741
- const abs = path4.isAbsolute(manifest) ? manifest : path4.join(WORKSPACE_DIR, manifest);
4965
+ const abs = path5.isAbsolute(manifest) ? manifest : path5.join(WORKSPACE_DIR, manifest);
4742
4966
  let content;
4743
4967
  try {
4744
- content = fs4.readFileSync(abs);
4968
+ content = fs5.readFileSync(abs);
4745
4969
  } catch (err) {
4746
4970
  fatal(`Could not read manifest "${manifest}": ${err.message}`);
4747
4971
  }
@@ -4867,12 +5091,13 @@ Bulk ingestion (jobs):
4867
5091
  A job loads a whole corpus at once. It reads either every object under a
4868
5092
  prefix of one of the app's file stores (\`files put\` fills one; an app can
4869
5093
  write one at runtime) or a manifest you upload: JSONL, one line per document,
4870
- {"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026}}.
4871
- filename, externalId and metadata are optional; the URL must be public.
5094
+ {"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026},
5095
+ "entities": ["\u2026"]}. Everything but the URL is optional; it must be public.
4872
5096
 
4873
- Nothing is spent until you say so. The platform counts the source, reads a
4874
- sample of about a hundred documents, and writes a plan: documents, chunks,
4875
- cost per stage (embedding, extraction, contextual) at today's rates, storage,
5097
+ Nothing past the sample runs until you say so. The platform counts the
5098
+ source, reads a sample of about a hundred documents, and writes a plan:
5099
+ documents, chunks, cost per stage (embedding, extraction, contextual, and
5100
+ the mapper's own model calls, priced from the sample's) at today's rates, storage,
4876
5101
  whether it fits where the source lives, and a duration. \`jobs status\` shows
4877
5102
  it; \`jobs approve\` starts the run. Two gates: the corpus has to fit the
4878
5103
  source's placement (a shared-pool source over the per-source cap answers
@@ -4982,6 +5207,7 @@ function quarantine(ctx, params) {
4982
5207
  "GET",
4983
5208
  `${base5(ctx.appId)}/jobs/${params.id}/quarantine${qs({
4984
5209
  ...params.kind ? { kind: params.kind } : {},
5210
+ ...params.stage ? { stage: params.stage } : {},
4985
5211
  ...params.reason ? { reason: params.reason } : {},
4986
5212
  ...params.cursor ? { cursor: params.cursor } : {},
4987
5213
  ...params.limit !== void 0 ? { limit: params.limit } : {}
@@ -5050,10 +5276,11 @@ var dataSourceMappersSpecs = {
5050
5276
  }
5051
5277
  },
5052
5278
  "datasources jobs quarantine": {
5053
- usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]",
5279
+ usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]",
5054
5280
  positionals: [{ name: "id", required: true }],
5055
5281
  flags: {
5056
5282
  kind: { type: "string" },
5283
+ stage: { type: "string" },
5057
5284
  reason: { type: "string" },
5058
5285
  cursor: { type: "string" },
5059
5286
  limit: { type: "number", min: 1 }
@@ -5206,18 +5433,32 @@ async function quarantine2(ctx, a) {
5206
5433
  const id = a.req("id");
5207
5434
  const limit = a.num("limit");
5208
5435
  const kind = kindOf(a);
5436
+ const stage = a.str("stage");
5437
+ if (stage !== void 0 && stage !== "map" && stage !== "read") {
5438
+ fatal(`--stage must be "map" or "read" (got "${stage}").`);
5439
+ }
5209
5440
  const reason = a.str("reason");
5210
5441
  const cursor = a.str("cursor");
5211
5442
  const result = await quarantine(ctx, {
5212
5443
  id,
5213
5444
  ...kind ? { kind } : {},
5445
+ ...stage ? { stage } : {},
5214
5446
  ...reason ? { reason } : {},
5215
5447
  ...cursor ? { cursor } : {},
5216
5448
  ...limit !== void 0 ? { limit } : {}
5217
5449
  });
5450
+ const { skipped, errors, readErrors } = result.counts;
5451
+ const notes = [
5452
+ ...skipped + errors > 0 ? [
5453
+ `Mapping: fix the mapper, push, \`datasources map deploy\`, then \`datasources jobs replay ${id}\` to run these through it again.`
5454
+ ] : [],
5455
+ ...readErrors > 0 ? [
5456
+ `Reading: fix the reader and publish, then \`datasources reader retry --source ${result.dataSource.slug} --job ${id}\` reads these again.`
5457
+ ] : []
5458
+ ];
5218
5459
  out({
5219
5460
  ...result,
5220
- note: result.counts.skipped + result.counts.errors === 0 ? "Nothing quarantined: the mapper produced documents (or deletions) for every object." : `Fix the mapper, push, \`datasources map deploy\`, then \`datasources jobs replay ${id}\` to run these through it again.`
5461
+ note: notes.length === 0 ? "Nothing quarantined: the mapper produced documents (or deletions) for every object, and the reader read every document." : notes.join(" ")
5221
5462
  });
5222
5463
  }
5223
5464
  async function replay2(ctx, a) {
@@ -5258,7 +5499,7 @@ Mapping raw objects into documents (mappers):
5258
5499
  remy-admin datasources map test [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>] [--dev] [--full]
5259
5500
  remy-admin datasources map deploy [--source <slug>] [--release <id>]
5260
5501
  remy-admin datasources remap [--source <slug>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
5261
- remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]
5502
+ remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]
5262
5503
  remy-admin datasources jobs replay <id> [--kind skip|error] [--wait] [--timeout <sec>]
5263
5504
 
5264
5505
  A source takes every object it is given as one document. When the file is
@@ -5299,6 +5540,228 @@ Mapping raw objects into documents (mappers):
5299
5540
  pauses with pauseReason 'skips' for a look at the quarantine.
5300
5541
  `;
5301
5542
 
5543
+ // src/ops/dataSourceReaders.ts
5544
+ function base6(appId) {
5545
+ return `/_internal/v2/apps/${appId}/datasources`;
5546
+ }
5547
+ function readerTest(ctx, params) {
5548
+ return call(
5549
+ ctx,
5550
+ "POST",
5551
+ `${base6(ctx.appId)}/reader/test`,
5552
+ {
5553
+ slug: params.slug,
5554
+ ...params.documents ? { documents: params.documents } : {},
5555
+ ...params.limit !== void 0 ? { limit: params.limit } : {}
5556
+ }
5557
+ );
5558
+ }
5559
+ function readerStatus(ctx, params) {
5560
+ return call(
5561
+ ctx,
5562
+ "POST",
5563
+ `${base6(ctx.appId)}/reader/status`,
5564
+ { slug: params.slug }
5565
+ );
5566
+ }
5567
+ function readerRetry(ctx, params) {
5568
+ return call(
5569
+ ctx,
5570
+ "POST",
5571
+ `${base6(ctx.appId)}/reader/retry`,
5572
+ {
5573
+ slug: params.slug,
5574
+ ...params.jobId ? { jobId: params.jobId } : {}
5575
+ }
5576
+ );
5577
+ }
5578
+ function reread(ctx, params) {
5579
+ return call(
5580
+ ctx,
5581
+ "POST",
5582
+ `${base6(ctx.appId)}/reread`,
5583
+ {
5584
+ slug: params.slug,
5585
+ ...params.unread ? { unread: true } : {},
5586
+ ...params.filter ? { filter: params.filter } : {},
5587
+ ...params.budgetDollars !== void 0 ? { budgetDollars: params.budgetDollars } : {},
5588
+ ...params.limit !== void 0 ? { limit: params.limit } : {}
5589
+ }
5590
+ );
5591
+ }
5592
+
5593
+ // src/commands/dataSourceReaders.ts
5594
+ var dataSourceReadersSpecs = {
5595
+ "datasources reader test": {
5596
+ usage: "Usage: remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2,...] [--limit <n>]",
5597
+ flags: {
5598
+ source: { type: "string" },
5599
+ dev: { type: "boolean" },
5600
+ documents: { type: "string" },
5601
+ limit: { type: "number", min: 1 }
5602
+ }
5603
+ },
5604
+ "datasources reader status": {
5605
+ usage: "Usage: remy-admin datasources reader status [--source <slug>]",
5606
+ flags: {
5607
+ source: { type: "string" }
5608
+ }
5609
+ },
5610
+ "datasources reader retry": {
5611
+ usage: "Usage: remy-admin datasources reader retry [--source <slug>] [--job <id>]",
5612
+ flags: {
5613
+ source: { type: "string" },
5614
+ job: { type: "string" }
5615
+ }
5616
+ },
5617
+ "datasources reread": {
5618
+ usage: "Usage: remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v,...|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]",
5619
+ flags: {
5620
+ source: { type: "string" },
5621
+ unread: { type: "boolean" },
5622
+ filter: { type: "string" },
5623
+ budget: { type: "number", min: 0.01 },
5624
+ limit: { type: "number", min: 1 },
5625
+ wait: { type: "boolean" },
5626
+ timeout: { type: "string" }
5627
+ }
5628
+ }
5629
+ };
5630
+ async function readerTest2(ctx, a) {
5631
+ if (!a.bool("dev")) {
5632
+ fatal(
5633
+ "reader test runs the dev session's reader only: pass --dev. The live reader reads through deliveries and `datasources reread`."
5634
+ );
5635
+ }
5636
+ const slug = sourceOf(a);
5637
+ const raw = a.str("documents");
5638
+ const documents2 = raw?.split(",").map((id) => id.trim()).filter(Boolean);
5639
+ if (raw !== void 0 && documents2?.length === 0) {
5640
+ fatal("--documents needs at least one document id.");
5641
+ }
5642
+ const limit = a.num("limit");
5643
+ const result = await readerTest(ctx, {
5644
+ slug,
5645
+ ...documents2 ? { documents: documents2 } : {},
5646
+ ...limit !== void 0 ? { limit } : {}
5647
+ });
5648
+ out({
5649
+ dataSource: slug,
5650
+ ...result,
5651
+ note: "Ran the LOCAL reader through the dev session, writing into the dev session's database. Query its tables to check what it wrote; publish to make it the live reader, then `datasources reread --unread` reads what it has not."
5652
+ });
5653
+ if (result.failed > 0) {
5654
+ throw new CliError(
5655
+ `The reader failed on ${result.failed} of ${result.documents.length} document(s).`,
5656
+ EXIT.buildFailed
5657
+ );
5658
+ }
5659
+ }
5660
+ async function readerStatus2(ctx, a) {
5661
+ const slug = sourceOf(a);
5662
+ const result = await readerStatus(ctx, { slug });
5663
+ const { reader, deliveries, documents: documents2 } = result;
5664
+ out({
5665
+ dataSource: slug,
5666
+ ...result,
5667
+ note: !reader ? `The live release declares no reader for "${slug}". Declare one in mindstudio.json's dataSources and publish.` : deliveries.parked > 0 ? `${deliveries.parked} delivery(ies) failed six times and parked. Fix the reader and publish, then \`datasources reader retry\`.` : documents2.unread > 0 ? `${documents2.unread.toLocaleString()} live document(s) not read yet. \`datasources reread --unread\` plans reading them.` : "Every live document has been read."
5668
+ });
5669
+ }
5670
+ async function readerRetry2(ctx, a) {
5671
+ const slug = sourceOf(a);
5672
+ const jobId = a.str("job");
5673
+ const result = await readerRetry(ctx, {
5674
+ slug,
5675
+ ...jobId ? { jobId } : {}
5676
+ });
5677
+ out({
5678
+ dataSource: slug,
5679
+ ...jobId ? { job: jobId } : {},
5680
+ ...result,
5681
+ note: result.retried === 0 ? jobId ? `Job ${jobId}'s reader failed on nothing.` : "Nothing parked." : `Queued ${result.retried.toLocaleString()} delivery(ies) again; \`datasources reader status\` shows them drain.`
5682
+ });
5683
+ }
5684
+ async function reread2(ctx, a) {
5685
+ const slug = sourceOf(a);
5686
+ const raw = a.str("filter");
5687
+ const budget = a.num("budget");
5688
+ const limit = a.num("limit");
5689
+ const { job } = await reread(ctx, {
5690
+ slug,
5691
+ ...a.bool("unread") ? { unread: true } : {},
5692
+ ...raw !== void 0 ? { filter: documentSelectorOf(raw) } : {},
5693
+ ...budget !== void 0 ? { budgetDollars: budget } : {},
5694
+ ...limit !== void 0 ? { limit } : {}
5695
+ });
5696
+ if (!a.bool("wait")) {
5697
+ out({
5698
+ dataSource: slug,
5699
+ job,
5700
+ note: `Planning: the reread prices reading from the reader's history, or reads a first slice for real to measure it. \`datasources jobs status ${job.id}\` shows the plan; \`datasources jobs approve ${job.id}\` runs it.`
5701
+ });
5702
+ return;
5703
+ }
5704
+ await waitAndReport(ctx, a, job.id, "plan", {
5705
+ dataSource: slug,
5706
+ action: "reread"
5707
+ });
5708
+ }
5709
+ var dataSourceReadersHandlers = {
5710
+ "datasources reader test": readerTest2,
5711
+ "datasources reader status": readerStatus2,
5712
+ "datasources reader retry": readerRetry2,
5713
+ "datasources reread": reread2
5714
+ };
5715
+ var dataSourceReadersHelp = `
5716
+ Reading documents into the app (readers):
5717
+ remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2] [--limit <n>]
5718
+ remy-admin datasources reader status [--source <slug>]
5719
+ remy-admin datasources reader retry [--source <slug>] [--job <id>]
5720
+ remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
5721
+
5722
+ A READER is app code the platform runs over every document a source
5723
+ builds, writing what it finds into the app's own tables: the extraction an
5724
+ app would otherwise run from a cron over allDocuments(), with the
5725
+ platform's batching, retries, budgets and pauses. Declare it in
5726
+ mindstudio.json:
5727
+ "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
5734
+ read more than once, so read() replaces the document's rows as a set,
5735
+ keyed by its id, and removed() deletes them. Options on the declaration:
5736
+ timeoutMs (per document, default 60000, max 300000) and concurrency
5737
+ (frames in flight for the source, default 16, max 64).
5738
+
5739
+ The reader always runs against the LIVE release, its code and its
5740
+ database: nothing reads until the app publishes one. From then on every
5741
+ document whose build lands on the active version is read, every removed
5742
+ document delivered as removed, whether it came through add(), a sync or a
5743
+ job. A job over a reader source reads as its last stage: the job stays
5744
+ running until its documents are read, \`jobs status\` says "reading N of M",
5745
+ a document the reader fails on lands in the job's quarantine (stage read),
5746
+ and a reader failing on far more documents than it did at first pauses the
5747
+ job with pauseReason 'reader'.
5748
+
5749
+ The loop: \`reader test --dev\` runs the LOCAL reader through the dev
5750
+ session over real documents, writing into the dev session's database, and
5751
+ prints each outcome; publish, and \`releases wait\` reports each reader's
5752
+ unread documents; \`reread --unread\` reads them. \`reread\` is a job that
5753
+ plans first: it prices reading from what the live reader has done (after a
5754
+ hundred documents), or reads a first slice for real and measures spend,
5755
+ failures and the live database's growth, then waits for \`jobs approve\`.
5756
+ Without --unread it reads every built document again (a changed reader).
5757
+ \`reader status\` shows the reader, its deliveries (pending, in flight,
5758
+ retrying, parked, recent errors), unread documents, the measured cost per
5759
+ document and the live database's size. Deliveries outside jobs retry after
5760
+ 1, 5, 15, 60 and 240 minutes, then park; \`reader retry\` sends parked ones
5761
+ again, and \`reader retry --job <id>\` the documents a job's reader failed
5762
+ on.
5763
+ `;
5764
+
5302
5765
  // src/commands/dataSources.ts
5303
5766
  var INGEST_FLAGS = {
5304
5767
  chunking: { type: "string" },
@@ -5329,11 +5792,12 @@ var CONFIG_FLAGS = {
5329
5792
  };
5330
5793
  var dataSourcesSpecs = {
5331
5794
  "datasources add": {
5332
- usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>",
5795
+ usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>",
5333
5796
  positionals: [{ name: "file", required: true, variadic: true }],
5334
5797
  flags: {
5335
5798
  source: { type: "string" },
5336
5799
  metadata: { type: "string" },
5800
+ entities: { type: "string" },
5337
5801
  wait: { type: "boolean" },
5338
5802
  timeout: { type: "string" }
5339
5803
  }
@@ -5362,7 +5826,7 @@ var dataSourcesSpecs = {
5362
5826
  }
5363
5827
  },
5364
5828
  "datasources search": {
5365
- usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
5829
+ usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--entities <id,...>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
5366
5830
  positionals: [{ name: "query", required: true }],
5367
5831
  flags: {
5368
5832
  source: { type: "string" },
@@ -5372,6 +5836,7 @@ var dataSourcesSpecs = {
5372
5836
  hybrid: { type: "string" },
5373
5837
  mode: { type: "string" },
5374
5838
  filter: { type: "string" },
5839
+ entities: { type: "string" },
5375
5840
  phrase: { type: "string" },
5376
5841
  contains: { type: "string" },
5377
5842
  "max-per-document": { type: "number", min: 1 },
@@ -5398,12 +5863,25 @@ var dataSourcesSpecs = {
5398
5863
  }
5399
5864
  },
5400
5865
  "datasources count": {
5401
- usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]",
5866
+ usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]",
5867
+ flags: {
5868
+ source: { type: "string" },
5869
+ filter: { type: "string" },
5870
+ entities: { type: "string" },
5871
+ contains: { type: "string" },
5872
+ phrase: { type: "string" },
5873
+ candidate: { type: "boolean" }
5874
+ }
5875
+ },
5876
+ "datasources entities": {
5877
+ usage: "Usage: remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--limit <n>] [--candidate]",
5402
5878
  flags: {
5403
5879
  source: { type: "string" },
5404
5880
  filter: { type: "string" },
5881
+ entities: { type: "string" },
5405
5882
  contains: { type: "string" },
5406
5883
  phrase: { type: "string" },
5884
+ limit: { type: "number", min: 1 },
5407
5885
  candidate: { type: "boolean" }
5408
5886
  }
5409
5887
  },
@@ -5449,22 +5927,24 @@ async function dataSourcesAdd(ctx, a) {
5449
5927
  const files = a.rest("file");
5450
5928
  const slug = sourceOf(a);
5451
5929
  const metadata = parseKeyValuePairs(a.str("metadata"), "metadata");
5930
+ const entities2 = parseIdList(a.str("entities"));
5452
5931
  const results = [];
5453
5932
  for (const file of files) {
5454
- const abs = path5.isAbsolute(file) ? file : path5.join(WORKSPACE_DIR, file);
5933
+ const abs = path6.isAbsolute(file) ? file : path6.join(WORKSPACE_DIR, file);
5455
5934
  let bytes;
5456
5935
  try {
5457
- bytes = fs5.readFileSync(abs);
5936
+ bytes = fs6.readFileSync(abs);
5458
5937
  } catch (err) {
5459
5938
  fatal(`Could not read file "${file}": ${err.message}`);
5460
5939
  }
5461
- const filename = path5.basename(file);
5940
+ const filename = path6.basename(file);
5462
5941
  results.push(
5463
5942
  await addDocument(ctx, {
5464
5943
  slug,
5465
5944
  filename,
5466
5945
  content: bytes,
5467
5946
  ...metadata ? { metadata } : {},
5947
+ ...entities2 ? { entities: entities2 } : {},
5468
5948
  onProgress: progress
5469
5949
  })
5470
5950
  );
@@ -5535,6 +6015,7 @@ async function dataSourcesStatus(ctx, a) {
5535
6015
  activeVersion: source.activeVersion,
5536
6016
  candidate: source.candidate,
5537
6017
  job: source.job,
6018
+ relink: source.relink,
5538
6019
  failures: (failures?.documents ?? []).map(summarize),
5539
6020
  moreFailures: !!failures?.nextCursor
5540
6021
  });
@@ -5550,16 +6031,7 @@ async function dataSourcesRm(ctx, a) {
5550
6031
  out({ deleted: documentId });
5551
6032
  return;
5552
6033
  }
5553
- let filter;
5554
- if (rawFilter.trimStart().startsWith("{")) {
5555
- try {
5556
- filter = JSON.parse(rawFilter);
5557
- } catch (err) {
5558
- fatal(`--filter is not valid JSON: ${err.message}`);
5559
- }
5560
- } else {
5561
- filter = { metadata: parseKeyValuePairs(rawFilter, "filter") };
5562
- }
6034
+ const filter = documentSelectorOf(rawFilter);
5563
6035
  const slug = sourceOf(a);
5564
6036
  const result = await rmWhereAll(ctx, {
5565
6037
  slug,
@@ -5614,6 +6086,24 @@ async function dataSourcesCount(ctx, a) {
5614
6086
  })
5615
6087
  );
5616
6088
  }
6089
+ async function dataSourcesEntities(ctx, a) {
6090
+ const filter = filterFromFlags(a);
6091
+ const limit = a.num("limit");
6092
+ out(
6093
+ await entities(ctx, {
6094
+ slug: sourceOf(a),
6095
+ ...filter ? { filter } : {},
6096
+ ...limit !== void 0 ? { limit } : {},
6097
+ ...a.bool("candidate") ? { candidate: true } : {}
6098
+ })
6099
+ );
6100
+ }
6101
+ function parseIdList(raw) {
6102
+ if (raw === void 0) {
6103
+ return void 0;
6104
+ }
6105
+ return raw.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
6106
+ }
5617
6107
  function filterFromFlags(a) {
5618
6108
  let filter = {};
5619
6109
  const raw = a.str("filter");
@@ -5631,6 +6121,10 @@ function filterFromFlags(a) {
5631
6121
  }
5632
6122
  }
5633
6123
  }
6124
+ const entities2 = parseIdList(a.str("entities"));
6125
+ if (entities2?.length) {
6126
+ filter.entities = entities2;
6127
+ }
5634
6128
  const phrase = a.str("phrase");
5635
6129
  if (phrase) {
5636
6130
  filter.phrase = phrase;
@@ -5641,29 +6135,6 @@ function filterFromFlags(a) {
5641
6135
  }
5642
6136
  return Object.keys(filter).length ? filter : void 0;
5643
6137
  }
5644
- function parseKeyValuePairs(raw, flag) {
5645
- if (raw === void 0) {
5646
- return void 0;
5647
- }
5648
- const pairs2 = {};
5649
- for (const part of raw.split(",")) {
5650
- const trimmed = part.trim();
5651
- if (!trimmed) {
5652
- continue;
5653
- }
5654
- const eq = trimmed.indexOf("=");
5655
- if (eq <= 0) {
5656
- fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
5657
- }
5658
- const key = trimmed.slice(0, eq).trim();
5659
- const value = trimmed.slice(eq + 1).trim();
5660
- pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
5661
- }
5662
- if (Object.keys(pairs2).length === 0) {
5663
- fatal(`--${flag} needs at least one key=value pair.`);
5664
- }
5665
- return pairs2;
5666
- }
5667
6138
  function triState2(a, flag, key) {
5668
6139
  const raw = a.str(flag);
5669
6140
  if (raw === void 0) {
@@ -5890,6 +6361,7 @@ var dataSourcesHandlers = {
5890
6361
  "datasources create": dataSourcesCreate,
5891
6362
  "datasources config": dataSourcesConfig,
5892
6363
  "datasources count": dataSourcesCount,
6364
+ "datasources entities": dataSourcesEntities,
5893
6365
  "datasources move": dataSourcesMove,
5894
6366
  "datasources revectorize": dataSourcesRevectorize,
5895
6367
  "datasources promote": dataSourcesPromote,
@@ -5929,12 +6401,13 @@ Subcommands:
5929
6401
  delete Delete a whole data source \u2014 documents, vectors and versions
5930
6402
 
5931
6403
  Usage:
5932
- remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>
6404
+ remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>
5933
6405
  remy-admin datasources list
5934
6406
  remy-admin datasources status [--source <slug>] [--status error] [--all]
5935
6407
  remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)
5936
6408
  remy-admin datasources search [--source <slug>] [search options] <query>
5937
- remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]
6409
+ remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]
6410
+ remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--limit <n>] [--candidate]
5938
6411
  remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
5939
6412
  remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]
5940
6413
  remy-admin datasources move [--source <slug>] --to <resource-id|shared> [--chains <n>] [--restart] [--wait] [--timeout <sec>]
@@ -5955,6 +6428,15 @@ Count:
5955
6428
  count (none exists), and not the set search returns. The SDK's
5956
6429
  \`Source.count(filter)\` is the same call for an app.
5957
6430
 
6431
+ Entities:
6432
+ A document can assert what it is about as your own ids (\`add --entities\`,
6433
+ a mapper's \`entities\`, a manifest line's \`entities\`), and every passage
6434
+ carries them. \`--entities a,b\` on search and count narrows to passages
6435
+ about any of them. \`entities\` lists which entities the matching passages
6436
+ are about, most first, with how many passages carry each (approximate, and
6437
+ passages, not documents): filter on one entity to see what appears beside
6438
+ it. The SDK's \`Source.entities({ filter, limit })\` is the same call.
6439
+
5958
6440
  Search options:
5959
6441
  --top-k <n> Results to return (default 5, max 50)
5960
6442
  --mode <m> hybrid (default) | semantic | lexical. Lexical is
@@ -5963,11 +6445,12 @@ Search options:
5963
6445
  --filter <k=v,...> Match document metadata set at add time. Values
5964
6446
  coerce: true/false and numbers become typed.
5965
6447
  Pass a JSON object for the full grammar:
5966
- metadata, filename, documentIds, pages, contains,
5967
- phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
6448
+ metadata, filename, documentIds, entities, pages,
6449
+ contains, phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
5968
6450
  Metadata takes numeric ranges via {gte,lte}: store
5969
6451
  dates as integers (YYYYMMDD or epoch seconds), then
5970
6452
  --filter '{"metadata":{"date":{"gte":20250101}}}'
6453
+ --entities <id,...> Chunk must be about any of these entities
5971
6454
  --phrase <text> Chunk must contain this exact word sequence
5972
6455
  --contains <words> Chunk must contain ALL these words, any order
5973
6456
  --max-per-document <n> Cap hits per document; backfills from others
@@ -6056,6 +6539,8 @@ Notes:
6056
6539
  Re-adding an unchanged file is free: no upload, no re-embedding. Re-adding
6057
6540
  with a different --metadata updates the tags in place, also free.
6058
6541
  --metadata tags documents for search-time filtering (scalars, \u226416 keys).
6542
+ --entities names what a document is about, as your own ids (\u2264256, no spaces
6543
+ or commas); an empty --entities clears them. Also free on a re-add.
6059
6544
  rm --filter removes every matching document in pages of a thousand: k=v
6060
6545
  pairs match metadata; a JSON object takes the full selector \u2014 metadata,
6061
6546
  filename, documentIds, externalIdPrefix (the key a job or connector
@@ -6095,7 +6580,7 @@ Examples:
6095
6580
  remy-admin datasources revectorize --source archive-sample --max-chars 900 --wait
6096
6581
  remy-admin datasources eval run --set <setId> --candidate --label small-chunks --wait
6097
6582
  remy-admin datasources eval compare <runA> <runB>
6098
- ${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceEvalsHelp}`;
6583
+ ${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceReadersHelp}${dataSourceEvalsHelp}${dataSourceDictionaryHelp}`;
6099
6584
 
6100
6585
  // src/ops/voice.ts
6101
6586
  function normalizeE164(input) {
@@ -7932,21 +8417,21 @@ Examples:
7932
8417
  // src/ops/infra.ts
7933
8418
  var DEFAULT_WAIT_TIMEOUT_MS4 = 10 * 60 * 1e3;
7934
8419
  var POLL_MS5 = 3e3;
7935
- function base6(appId) {
8420
+ function base7(appId) {
7936
8421
  return `/_internal/v2/apps/${appId}/infra`;
7937
8422
  }
7938
8423
  function list14(ctx, params) {
7939
8424
  return call(
7940
8425
  ctx,
7941
8426
  "GET",
7942
- `${base6(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
8427
+ `${base7(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
7943
8428
  );
7944
8429
  }
7945
8430
  function get12(ctx, params) {
7946
8431
  return call(
7947
8432
  ctx,
7948
8433
  "GET",
7949
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}`
8434
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}`
7950
8435
  );
7951
8436
  }
7952
8437
  function logs(ctx, params) {
@@ -7961,11 +8446,11 @@ function logs(ctx, params) {
7961
8446
  return call(
7962
8447
  ctx,
7963
8448
  "GET",
7964
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
8449
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
7965
8450
  );
7966
8451
  }
7967
8452
  function provision(ctx, params) {
7968
- return call(ctx, "POST", base6(ctx.appId), {
8453
+ return call(ctx, "POST", base7(ctx.appId), {
7969
8454
  offeringId: params.offeringId,
7970
8455
  ...params.name ? { name: params.name } : {}
7971
8456
  });
@@ -7974,28 +8459,28 @@ function hibernate(ctx, params) {
7974
8459
  return call(
7975
8460
  ctx,
7976
8461
  "POST",
7977
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
8462
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
7978
8463
  );
7979
8464
  }
7980
8465
  function resume2(ctx, params) {
7981
8466
  return call(
7982
8467
  ctx,
7983
8468
  "POST",
7984
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
8469
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
7985
8470
  );
7986
8471
  }
7987
8472
  function destroy(ctx, params) {
7988
8473
  return call(
7989
8474
  ctx,
7990
8475
  "POST",
7991
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
8476
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
7992
8477
  );
7993
8478
  }
7994
8479
  function resize(ctx, params) {
7995
8480
  return call(
7996
8481
  ctx,
7997
8482
  "POST",
7998
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/resize`,
8483
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resize`,
7999
8484
  { offeringId: params.offeringId }
8000
8485
  );
8001
8486
  }
@@ -8003,7 +8488,7 @@ function rename(ctx, params) {
8003
8488
  return call(
8004
8489
  ctx,
8005
8490
  "POST",
8006
- `${base6(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
8491
+ `${base7(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
8007
8492
  { name: params.name }
8008
8493
  );
8009
8494
  }
@@ -8572,7 +9057,9 @@ var SPECS = {
8572
9057
  ...dataSourceJobsSpecs,
8573
9058
  ...dataSourceConnectorsSpecs,
8574
9059
  ...dataSourceEvalsSpecs,
9060
+ ...dataSourceDictionarySpecs,
8575
9061
  ...dataSourceMappersSpecs,
9062
+ ...dataSourceReadersSpecs,
8576
9063
  ...voiceSpecs,
8577
9064
  ...agentSpecs,
8578
9065
  ...tasksSpecs,
@@ -8603,7 +9090,9 @@ var HANDLERS = {
8603
9090
  ...dataSourceJobsHandlers,
8604
9091
  ...dataSourceConnectorsHandlers,
8605
9092
  ...dataSourceEvalsHandlers,
9093
+ ...dataSourceDictionaryHandlers,
8606
9094
  ...dataSourceMappersHandlers,
9095
+ ...dataSourceReadersHandlers,
8607
9096
  ...voiceHandlers,
8608
9097
  ...agentHandlers,
8609
9098
  ...tasksHandlers,
@@ -8644,8 +9133,8 @@ var GROUP_HELP = {
8644
9133
  };
8645
9134
 
8646
9135
  // src/ctx.ts
8647
- import fs6 from "fs";
8648
- import path6 from "path";
9136
+ import fs7 from "fs";
9137
+ import path7 from "path";
8649
9138
 
8650
9139
  // src/parseJsonConfig.ts
8651
9140
  import JSON5 from "json5";
@@ -8674,10 +9163,10 @@ function parseJsonConfig(raw) {
8674
9163
  // src/ctx.ts
8675
9164
  var DEFAULT_BASE_URL = "https://api.mindstudio.ai";
8676
9165
  function loadWorkspaceAppId(workspaceDir = WORKSPACE_DIR) {
8677
- const manifestPath = path6.join(workspaceDir, "mindstudio.json");
9166
+ const manifestPath = path7.join(workspaceDir, "mindstudio.json");
8678
9167
  let raw;
8679
9168
  try {
8680
- raw = fs6.readFileSync(manifestPath, "utf-8");
9169
+ raw = fs7.readFileSync(manifestPath, "utf-8");
8681
9170
  } catch (err) {
8682
9171
  if (err.code === "ENOENT") {
8683
9172
  throw new Error(`mindstudio.json not found at ${manifestPath}`);