@datarails-ai/dr-cli 1.6.154 → 1.6.155

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/cli.js CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "@datarails-ai/dr-cli",
11
- version: "1.6.154",
11
+ version: "1.6.155",
12
12
  description: "Datarails CLI",
13
13
  type: "module",
14
14
  engines: {
@@ -69,7 +69,7 @@ var package_default = {
69
69
  };
70
70
 
71
71
  // src/buildInfo.ts
72
- var BUILD_TIME = "2026-10-22T15:15:19.668Z";
72
+ var BUILD_TIME = "2026-10-23T09:17:56.480Z";
73
73
  function formatBuildTime(iso) {
74
74
  if (iso === "") return "";
75
75
  const d = new Date(iso);
@@ -46485,12 +46485,15 @@ async function listPollItems(client, queryId, query = {}, reqOptions) {
46485
46485
  }
46486
46486
  return out;
46487
46487
  }
46488
- async function retryPollItem(client, queryId, itemId, reqOptions) {
46489
- const path13 = `/connector-queries/${queryId}/poll/items/${itemId}/retry`;
46488
+ function outputQuery(outputId) {
46489
+ return outputId === void 0 ? "" : `?output=${outputId}`;
46490
+ }
46491
+ async function retryPollItem(client, queryId, itemId, reqOptions, outputId) {
46492
+ const path13 = `/connector-queries/${queryId}/poll/items/${itemId}/retry${outputQuery(outputId)}`;
46490
46493
  return postPollJSON(client, path13, {}, "poll item retry", reqOptions);
46491
46494
  }
46492
- async function skipPollItem(client, queryId, itemId, reqOptions) {
46493
- const path13 = `/connector-queries/${queryId}/poll/items/${itemId}/skip`;
46495
+ async function skipPollItem(client, queryId, itemId, reqOptions, outputId) {
46496
+ const path13 = `/connector-queries/${queryId}/poll/items/${itemId}/skip${outputQuery(outputId)}`;
46494
46497
  return postPollJSON(client, path13, {}, "poll item skip", reqOptions);
46495
46498
  }
46496
46499
  function toControlPlaneError(method, path13, err) {
@@ -46782,6 +46785,11 @@ function renderPoll(out, headline, binding, p) {
46782
46785
  p.poll_run_rekey_to === null ? "-" : `to ${sameNameLabel(p.poll_run_rekey_to)}`
46783
46786
  );
46784
46787
  row("Items", counts === "" ? "(none)" : counts);
46788
+ const outputs = p.outputs ?? [];
46789
+ row(
46790
+ "Outputs",
46791
+ outputs.length === 0 ? "-" : outputs.map((o) => `filebox ${o.filebox_uuid} (query #${o.connector_query_id})`).join(", ")
46792
+ );
46785
46793
  }
46786
46794
  function writeTable26(out, rows) {
46787
46795
  const widths = [];
@@ -46798,8 +46806,18 @@ function writeTable26(out, rows) {
46798
46806
  `);
46799
46807
  }
46800
46808
  }
46809
+ function formatItemOutputs(it) {
46810
+ const outputs = it.outputs ?? [];
46811
+ if (outputs.length === 0) return "-";
46812
+ return outputs.map((o) => {
46813
+ const error = o.last_error ?? (o.skip_reason === null ? null : `skipped: ${o.skip_reason}`);
46814
+ return `#${o.connector_query_id}=${o.state}${error === null ? "" : ` (${truncate3(oneLine3(error), 40)})`}`;
46815
+ }).join(" ");
46816
+ }
46801
46817
  function renderItemTable(out, items) {
46802
- const rows = [["ID", "LABEL", "DATE", "STATE", "ATTEMPTS", "VERSION", "ERROR"]];
46818
+ const multi = items.some((it) => (it.outputs ?? []).length > 0);
46819
+ const header = ["ID", "LABEL", "DATE", "STATE", "ATTEMPTS", "VERSION"];
46820
+ const rows = [[...header, ...multi ? ["OUTPUTS"] : [], "ERROR"]];
46803
46821
  for (const it of items) {
46804
46822
  const error = it.last_error ?? (it.skip_reason === null ? null : `skipped: ${it.skip_reason}`);
46805
46823
  rows.push([
@@ -46809,6 +46827,7 @@ function renderItemTable(out, items) {
46809
46827
  it.state,
46810
46828
  String(it.attempts),
46811
46829
  it.version_id === null ? "-" : String(it.version_id),
46830
+ ...multi ? [formatItemOutputs(it)] : [],
46812
46831
  error === null ? "-" : truncate3(oneLine3(error), 80)
46813
46832
  ]);
46814
46833
  }
@@ -46960,10 +46979,10 @@ function pollConfirmAction(filebox, verb, scope, payload = {}) {
46960
46979
  verb
46961
46980
  };
46962
46981
  }
46963
- function pollItemConfirmAction(filebox, item, scope) {
46982
+ function pollItemConfirmAction(filebox, item, scope, output) {
46964
46983
  return {
46965
46984
  resource: "connector-poll-item",
46966
- id: `${filebox.trim()}/${item}#${confirmDigest(scope, {})}`,
46985
+ id: `${filebox.trim()}/${item}#${confirmDigest(scope, output === void 0 ? {} : { output: output.trim() })}`,
46967
46986
  verb: "skip"
46968
46987
  };
46969
46988
  }
@@ -47167,6 +47186,10 @@ More: dr connectors poll items ${cfg.filebox}${state}${limit} --after ${page.nex
47167
47186
  );
47168
47187
  }
47169
47188
  }
47189
+ async function resolveItemOutput(cfg) {
47190
+ if (cfg.output === void 0) return void 0;
47191
+ return (await resolvePollBinding(cfg.client, cfg.output, cfg.signal)).query_id;
47192
+ }
47170
47193
  function renderItemResult(cfg, binding, headline, item) {
47171
47194
  if (cfg.asJson) {
47172
47195
  writeJSONIndented(cfg.out, item);
@@ -47178,37 +47201,109 @@ function renderItemResult(cfg, binding, headline, item) {
47178
47201
  }
47179
47202
  async function runPollRetry(cfg) {
47180
47203
  const binding = await resolvePollBinding(cfg.client, cfg.filebox, cfg.signal);
47204
+ const outputId = await resolveItemOutput(cfg);
47181
47205
  const item = await explainNotPolled(
47182
47206
  cfg.filebox,
47183
- retryPollItem(cfg.client, binding.query_id, cfg.itemId, { signal: cfg.signal })
47207
+ retryPollItem(
47208
+ cfg.client,
47209
+ binding.query_id,
47210
+ cfg.itemId,
47211
+ { signal: cfg.signal },
47212
+ outputId
47213
+ )
47184
47214
  );
47185
- renderItemResult(cfg, binding, `Item ${cfg.itemId} queued for retry`, item);
47215
+ const what = outputId === void 0 ? "" : ` (output #${outputId})`;
47216
+ renderItemResult(cfg, binding, `Item ${cfg.itemId}${what} queued for retry`, item);
47186
47217
  }
47187
47218
  async function runPollSkip(cfg) {
47188
47219
  const binding = await resolvePollBinding(cfg.client, cfg.filebox, cfg.signal);
47220
+ const outputId = await resolveItemOutput(cfg);
47221
+ const outputArg = cfg.output === void 0 ? "" : ` --output ${cfg.output.trim()}`;
47189
47222
  cfg.deadline?.pause();
47190
47223
  await twoStepConfirm({
47191
47224
  out: cfg.out,
47192
- action: pollItemConfirmAction(cfg.filebox, cfg.itemId, cfg.scope),
47225
+ action: pollItemConfirmAction(cfg.filebox, cfg.itemId, cfg.scope, cfg.output),
47193
47226
  token: cfg.token,
47194
47227
  server: cfg.scope.serverKey,
47195
- confirmCmd: `dr connectors poll skip ${cfg.filebox} ${cfg.itemId}${cfg.targetArgs}`,
47228
+ confirmCmd: `dr connectors poll skip ${cfg.filebox} ${cfg.itemId}${outputArg}${cfg.targetArgs}`,
47196
47229
  describe: async () => [
47197
47230
  `item: ${cfg.itemId} of ${bindingTarget(binding)}`,
47198
- "effect: the item is never landed (dr connectors poll retry queues it again)"
47231
+ ...outputId === void 0 ? ["effect: the item is never landed (dr connectors poll retry queues it again)"] : [
47232
+ `output: query #${outputId} only`,
47233
+ "effect: the item is never landed in that output; its other outputs still land (dr connectors poll retry --output queues it again)"
47234
+ ]
47199
47235
  ],
47200
47236
  execute: async () => {
47201
47237
  cfg.deadline?.resume();
47202
47238
  const item = await explainNotPolled(
47203
47239
  cfg.filebox,
47204
- skipPollItem(cfg.client, binding.query_id, cfg.itemId, {
47205
- signal: cfg.signal
47206
- })
47240
+ skipPollItem(
47241
+ cfg.client,
47242
+ binding.query_id,
47243
+ cfg.itemId,
47244
+ { signal: cfg.signal },
47245
+ outputId
47246
+ )
47207
47247
  );
47208
- renderItemResult(cfg, binding, `Item ${cfg.itemId} skipped`, item);
47248
+ const what = outputId === void 0 ? "" : ` (output #${outputId})`;
47249
+ renderItemResult(cfg, binding, `Item ${cfg.itemId}${what} skipped`, item);
47209
47250
  }
47210
47251
  });
47211
47252
  }
47253
+ async function runPollOutputs(cfg) {
47254
+ const binding = await resolvePollBinding(cfg.client, cfg.filebox, cfg.signal);
47255
+ const current = await explainNotPolled(
47256
+ cfg.filebox,
47257
+ getPoll(cfg.client, binding.query_id, { signal: cfg.signal })
47258
+ );
47259
+ if (cfg.add.length === 0 && cfg.remove.length === 0 && !cfg.clear) {
47260
+ if (cfg.asJson) {
47261
+ writeJSONIndented(cfg.out, current.outputs ?? []);
47262
+ return;
47263
+ }
47264
+ renderPoll(cfg.out, "Polling", binding, current);
47265
+ return;
47266
+ }
47267
+ const kept = new Map(
47268
+ cfg.clear ? [] : (current.outputs ?? []).map((o) => [o.connector_query_id, o.date_tag])
47269
+ );
47270
+ for (const filebox of cfg.remove) {
47271
+ const stored2 = (current.outputs ?? []).find(
47272
+ (o) => o.filebox_uuid.toLowerCase() === filebox.trim().toLowerCase()
47273
+ );
47274
+ const queryId = stored2?.connector_query_id ?? (await resolvePollBinding(cfg.client, filebox, cfg.signal)).query_id;
47275
+ if (!kept.delete(queryId)) {
47276
+ throw new Error(`filebox ${filebox} is not an output of ${bindingTarget(binding)}`);
47277
+ }
47278
+ }
47279
+ for (const filebox of cfg.add) {
47280
+ const output = await resolvePollBinding(cfg.client, filebox, cfg.signal);
47281
+ if (output.query_id === binding.query_id) {
47282
+ throw new Error(`filebox ${filebox} is the binding's own filebox, always its first output`);
47283
+ }
47284
+ if (output.filebox_id <= 0) {
47285
+ throw new Error(
47286
+ `connector-query #${output.query_id} has no filebox id recorded, so its date tag cannot be read; record it with dr connectors query repoint ${output.query_id} --filebox-id <id>`
47287
+ );
47288
+ }
47289
+ kept.set(
47290
+ output.query_id,
47291
+ await readFileboxDateTag(cfg.monolith, output.filebox_id, cfg.signal)
47292
+ );
47293
+ }
47294
+ const outputs = [...kept].map(([id, tag]) => ({ connector_query_id: id, date_tag: tag }));
47295
+ const stored = await putPoll(
47296
+ cfg.client,
47297
+ binding.query_id,
47298
+ { outputs },
47299
+ { signal: cfg.signal }
47300
+ );
47301
+ if (cfg.asJson) {
47302
+ writeJSONIndented(cfg.out, stored);
47303
+ return;
47304
+ }
47305
+ renderPoll(cfg.out, "Polling outputs updated", binding, stored);
47306
+ }
47212
47307
  async function withMonolith(opts, action) {
47213
47308
  const monolithHost = resolve(resolveServer(opts.server)).host;
47214
47309
  const setup = await setupCall(opts.server, opts.account, {
@@ -47465,13 +47560,14 @@ ${POLL_CONFIRM_HELP}`)
47465
47560
  }
47466
47561
  );
47467
47562
  addCommonFlags(itemsCmd, "Output the raw page as JSON");
47468
- const retryCmd = poll2.command("retry").description("Queue a failed, waiting or skipped item again").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)").action(async (filebox, item, opts) => {
47563
+ const retryCmd = poll2.command("retry").description("Queue a failed, waiting or skipped item again").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)").option("--output <filebox>", "a multi-output item: retry only its landing in this filebox").action(async (filebox, item, opts) => {
47469
47564
  const itemId = parsePositiveIntArg2(item, "item id");
47470
47565
  await withConnectorsSetup(opts, async ({ client, signal }) => {
47471
47566
  await runPollRetry({
47472
47567
  client,
47473
47568
  filebox,
47474
47569
  itemId,
47570
+ ...opts.output !== void 0 ? { output: opts.output } : {},
47475
47571
  asJson: opts.json === true,
47476
47572
  signal,
47477
47573
  out: process.stdout
@@ -47480,32 +47576,67 @@ ${POLL_CONFIRM_HELP}`)
47480
47576
  });
47481
47577
  addCommonFlags(retryCmd, "Output the item as JSON");
47482
47578
  const skipCmd = addConfirmTokenFlag(
47483
- poll2.command("skip").description("Skip an item so it is never landed (two-step confirmation)").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)")
47484
- ).action(async (filebox, item, opts) => {
47485
- const itemId = parsePositiveIntArg2(item, "item id");
47486
- await preflightConfirmToken(
47487
- pollItemConfirmAction(filebox, itemId, scopeOf(opts)),
47488
- opts.confirmToken
47489
- );
47490
- await withConnectorsSetup(opts, async (setup) => {
47491
- await runPollSkip({
47492
- client: setup.client,
47493
- filebox,
47494
- itemId,
47495
- deadline: {
47496
- pause: () => setup.pauseTimeout(),
47497
- resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
47498
- },
47499
- ...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
47500
- scope: scopeOf(opts),
47501
- targetArgs: targetArgsOf(opts),
47502
- asJson: opts.json === true,
47503
- signal: setup.signal,
47504
- out: process.stdout
47579
+ poll2.command("skip").description("Skip an item so it is never landed (two-step confirmation)").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)").option("--output <filebox>", "a multi-output item: skip only its landing in this filebox")
47580
+ ).action(
47581
+ async (filebox, item, opts) => {
47582
+ const itemId = parsePositiveIntArg2(item, "item id");
47583
+ await preflightConfirmToken(
47584
+ pollItemConfirmAction(filebox, itemId, scopeOf(opts), opts.output),
47585
+ opts.confirmToken
47586
+ );
47587
+ await withConnectorsSetup(opts, async (setup) => {
47588
+ await runPollSkip({
47589
+ client: setup.client,
47590
+ filebox,
47591
+ itemId,
47592
+ ...opts.output !== void 0 ? { output: opts.output } : {},
47593
+ deadline: {
47594
+ pause: () => setup.pauseTimeout(),
47595
+ resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
47596
+ },
47597
+ ...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
47598
+ scope: scopeOf(opts),
47599
+ targetArgs: targetArgsOf(opts),
47600
+ asJson: opts.json === true,
47601
+ signal: setup.signal,
47602
+ out: process.stdout
47603
+ });
47505
47604
  });
47506
- });
47507
- });
47605
+ }
47606
+ );
47508
47607
  addCommonFlags(skipCmd, "Output the item as JSON");
47608
+ const collect2 = (value, previous) => [...previous, value];
47609
+ const outputsCmd = poll2.command("outputs").description(
47610
+ "Show or change the other fileboxes each file lands in, from one read of the source"
47611
+ ).argument("<filebox>", "the binding's filebox id or uuid").option("--add <filebox>", "land each file in this filebox too (repeatable)", collect2, []).option("--remove <filebox>", "stop landing in this filebox (repeatable)", collect2, []).option("--clear", "remove every extra output", false).addHelpText(
47612
+ "after",
47613
+ [
47614
+ "",
47615
+ "For a source that deletes a file once it is read (a bank SFTP): one binding reads each",
47616
+ "file once and lands it in its own filebox and in every output, each through its own",
47617
+ "Sync-query (e.g. a BAI2 feed). The outputs must use the same connection and code.",
47618
+ "Set the Sync-query `dated_by` to `fetch` so discover never opens a file."
47619
+ ].join("\n")
47620
+ ).action(
47621
+ async (filebox, opts) => {
47622
+ await withConnectorsSetup(opts, async ({ client, signal }) => {
47623
+ await withMonolith(opts, async (monolith) => {
47624
+ await runPollOutputs({
47625
+ client,
47626
+ monolith,
47627
+ filebox,
47628
+ add: opts.add,
47629
+ remove: opts.remove,
47630
+ clear: opts.clear === true,
47631
+ asJson: opts.json === true,
47632
+ signal,
47633
+ out: process.stdout
47634
+ });
47635
+ });
47636
+ });
47637
+ }
47638
+ );
47639
+ addCommonFlags(outputsCmd, "Output the outputs (or the stored binding) as JSON");
47509
47640
  const seedCmd = addConfirmTokenFlag(
47510
47641
  poll2.command("seed").description(
47511
47642
  "Mark items modified at or before --until as already landed (cut-over; token, no dialog)"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "cliVersion": "1.6.154",
3
+ "cliVersion": "1.6.155",
4
4
  "_contract": "Resolve a command path through `aliases` (canonical prefix + alias segment -> canonical path) before any lookup. A path listed in `pendingCommands`, or whose first segment is a key of `pending`, is UNDECIDED, not unrestricted: fail closed or skip it, and never read a path's absence from `commands` as 'no requirement'. An entry carrying `denied` is UNAVAILABLE to any AGENT-SCOPED credential — one whose `scopes` claim carries at least one `resource:action` entry; a claim that is absent, empty, capability-only or wholly unparseable is UNRESTRICTED, and this mark does not apply to it — whatever else that entry says: it is not a ceiling and it is not always-available, its `denied.reason` says why and `denied.until` names the ticket that lifts it, and the absence of `requires` on it must NEVER be read as 'no requirement'. Only an entry in `commands` with an empty `requires` means always-available.",
5
5
  "commands": {
6
6
  "login": {
@@ -2117,6 +2117,12 @@
2117
2117
  "until": "DR-56164"
2118
2118
  }
2119
2119
  },
2120
+ "connectors poll outputs": {
2121
+ "denied": {
2122
+ "reason": "The connectors control plane runs no dr-auth-sdk guard and checks no scope, so the `integrations` ceiling this command declares would be enforced by nobody. Closed to scoped credentials until that control plane adopts the SDK.",
2123
+ "until": "DR-56164"
2124
+ }
2125
+ },
2120
2126
  "connectors poll seed": {
2121
2127
  "denied": {
2122
2128
  "reason": "The connectors control plane runs no dr-auth-sdk guard and checks no scope, so the `integrations` ceiling this command declares would be enforced by nobody. Closed to scoped credentials until that control plane adopts the SDK.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarails-ai/dr-cli",
3
- "version": "1.6.154",
3
+ "version": "1.6.155",
4
4
  "description": "Datarails CLI",
5
5
  "type": "module",
6
6
  "engines": {