@datarails-ai/dr-cli 1.6.73 → 1.6.75

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.73",
11
+ version: "1.6.75",
12
12
  description: "Datarails CLI",
13
13
  type: "module",
14
14
  engines: {
@@ -38,10 +38,11 @@ var package_default = {
38
38
  postinstall: "node scripts/postinstall.mjs"
39
39
  },
40
40
  dependencies: {
41
- "@datarails-ai/dr-appspace-cli": "0.9.5",
41
+ "@datarails-ai/dr-appspace-cli": "0.9.7",
42
42
  "@modelcontextprotocol/sdk": "^1.29.0",
43
43
  "@napi-rs/keyring": "^1.3.0",
44
44
  commander: "^12.1.0",
45
+ "fast-xml-parser": "^5.11.2",
45
46
  jszip: "^3.10.1",
46
47
  "node-html-parser": "^6.1.13",
47
48
  open: "^10.1.0",
@@ -66,7 +67,7 @@ var package_default = {
66
67
  };
67
68
 
68
69
  // src/buildInfo.ts
69
- var BUILD_TIME = "2026-09-30T11:34:47.859Z";
70
+ var BUILD_TIME = "2026-09-30T12:50:28.608Z";
70
71
  function formatBuildTime(iso) {
71
72
  if (iso === "") return "";
72
73
  const d = new Date(iso);
@@ -411,6 +412,7 @@ var ordered = [
411
412
  { key: "UK", host: "https://ukapp.datarails.com", subDomain: "uk" },
412
413
  { key: "CA", host: "https://caapp.datarails.com", subDomain: "canada" },
413
414
  { key: "EU", host: "https://euapp.datarails.com", subDomain: "eu" },
415
+ { key: "STUDIO", host: "https://studio.datarails.com", subDomain: "studio" },
414
416
  { key: "DEV", host: "https://dev.datarails.com", subDomain: "dev" },
415
417
  { key: "DEV-1", host: "https://dev-1.datarails.com", subDomain: "dev-1" },
416
418
  { key: "TEST", host: "https://testapp.datarails.com", subDomain: "test" },
@@ -436,7 +438,7 @@ function list() {
436
438
  function keys() {
437
439
  return ordered.map((s) => s.key);
438
440
  }
439
- var PROD_KEYS = /* @__PURE__ */ new Set(["US", "US2", "UK", "CA", "EU", "DEMO"]);
441
+ var PROD_KEYS = /* @__PURE__ */ new Set(["US", "US2", "UK", "CA", "EU", "STUDIO", "DEMO"]);
440
442
  function isProdServer(input) {
441
443
  let server;
442
444
  try {
@@ -577,11 +579,11 @@ var ScopeDeniedError = class extends Error {
577
579
  return `${this.method} ${this.path}: this identity's credential scope does not allow it${needs}; run 'dr whoami' to see the scopes this identity carries`;
578
580
  }
579
581
  };
580
- function parseScopeDenial(status2, text) {
582
+ function parseScopeDenial(status2, text2) {
581
583
  if (status2 !== 403) return void 0;
582
584
  let body;
583
585
  try {
584
- body = JSON.parse(text);
586
+ body = JSON.parse(text2);
585
587
  } catch {
586
588
  return void 0;
587
589
  }
@@ -698,10 +700,10 @@ function decodeJwtExpiration(token) {
698
700
  }
699
701
  return at;
700
702
  }
701
- function parseTokenBody(text, label) {
703
+ function parseTokenBody(text2, label) {
702
704
  let raw;
703
705
  try {
704
- raw = JSON.parse(text);
706
+ raw = JSON.parse(text2);
705
707
  } catch (err) {
706
708
  throw new Error(`jwt ${label} parse: ${msgOf3(err)}`);
707
709
  }
@@ -727,11 +729,11 @@ async function mintJwt(host, sessionId, csrfToken, options) {
727
729
  body: "{}",
728
730
  signal: options?.signal
729
731
  });
730
- const text = await res.text();
732
+ const text2 = await res.text();
731
733
  if (!res.ok) {
732
- throw new Error(`jwt mint: HTTP ${res.status}: ${truncate(text, TRUNCATE)}`);
734
+ throw new Error(`jwt mint: HTTP ${res.status}: ${truncate(text2, TRUNCATE)}`);
733
735
  }
734
- const out = parseTokenBody(text, "mint");
736
+ const out = parseTokenBody(text2, "mint");
735
737
  if (out.access === "") {
736
738
  throw new Error("jwt mint: empty access token");
737
739
  }
@@ -754,11 +756,11 @@ async function refreshJwt(host, refreshToken, options) {
754
756
  body: JSON.stringify({ refresh: refreshToken }),
755
757
  signal: options?.signal
756
758
  });
757
- const text = await res.text();
759
+ const text2 = await res.text();
758
760
  if (!res.ok) {
759
- throw new Error(`jwt refresh: HTTP ${res.status}: ${truncate(text, TRUNCATE)}`);
761
+ throw new Error(`jwt refresh: HTTP ${res.status}: ${truncate(text2, TRUNCATE)}`);
760
762
  }
761
- const out = parseTokenBody(text, "refresh");
763
+ const out = parseTokenBody(text2, "refresh");
762
764
  if (out.access === "") {
763
765
  throw new Error("jwt refresh: empty access token");
764
766
  }
@@ -778,11 +780,11 @@ async function refreshExternalJwt(host, refreshToken, options) {
778
780
  body: JSON.stringify({ refresh: refreshToken }),
779
781
  signal: options?.signal
780
782
  });
781
- const text = await res.text();
783
+ const text2 = await res.text();
782
784
  if (!res.ok) {
783
- throw new Error(`jwt external refresh: HTTP ${res.status}: ${truncate(text, TRUNCATE)}`);
785
+ throw new Error(`jwt external refresh: HTTP ${res.status}: ${truncate(text2, TRUNCATE)}`);
784
786
  }
785
- const out = parseTokenBody(text, "refresh");
787
+ const out = parseTokenBody(text2, "refresh");
786
788
  if (out.access === "") {
787
789
  throw new Error("jwt external refresh: empty access token");
788
790
  }
@@ -810,11 +812,11 @@ async function exchangeApiKey(host, apiKey, options) {
810
812
  headers: { Authorization: `Bearer ${key}`, Accept: "application/json" },
811
813
  signal: options?.signal
812
814
  });
813
- const text = await res.text();
815
+ const text2 = await res.text();
814
816
  if (!res.ok) {
815
817
  let code;
816
818
  try {
817
- const value = JSON.parse(text)["error"];
819
+ const value = JSON.parse(text2)["error"];
818
820
  if (value === "invalid_key" || value === "exchange_unavailable") code = value;
819
821
  } catch {
820
822
  }
@@ -822,7 +824,7 @@ async function exchangeApiKey(host, apiKey, options) {
822
824
  }
823
825
  let raw;
824
826
  try {
825
- raw = JSON.parse(text);
827
+ raw = JSON.parse(text2);
826
828
  } catch {
827
829
  throw new Error("api-key exchange parse: the response is not JSON");
828
830
  }
@@ -871,21 +873,21 @@ async function loginAs(host, operatorAccessToken, userId, options) {
871
873
  body: JSON.stringify(body),
872
874
  signal: options?.signal
873
875
  });
874
- const text = await res.text();
876
+ const text2 = await res.text();
875
877
  if (!res.ok) {
876
878
  let code;
877
879
  let detail;
878
880
  try {
879
- const parsed2 = JSON.parse(text);
881
+ const parsed2 = JSON.parse(text2);
880
882
  if (typeof parsed2["code"] === "string") code = parsed2["code"];
881
883
  if (typeof parsed2["detail"] === "string") detail = parsed2["detail"];
882
884
  } catch {
883
885
  }
884
- throw new LoginAsError(res.status, { code, detail, body: truncate(text, TRUNCATE) });
886
+ throw new LoginAsError(res.status, { code, detail, body: truncate(text2, TRUNCATE) });
885
887
  }
886
888
  let raw;
887
889
  try {
888
- raw = JSON.parse(text);
890
+ raw = JSON.parse(text2);
889
891
  } catch (err) {
890
892
  throw new Error(`jwt loginas parse: ${msgOf3(err)}`);
891
893
  }
@@ -2245,17 +2247,17 @@ var AuthorizedClient = class _AuthorizedClient {
2245
2247
  body: fd,
2246
2248
  signal: options?.signal
2247
2249
  });
2248
- const text = await res.text();
2249
- _AuthorizedClient.throwIfScopeDeniedBody("POST", path11, res.status, text);
2250
+ const text2 = await res.text();
2251
+ _AuthorizedClient.throwIfScopeDeniedBody("POST", path11, res.status, text2);
2250
2252
  if (res.status === 401 || res.status === 403) {
2251
2253
  throw new SessionExpiredError(`POST ${path11} (HTTP ${res.status})`);
2252
2254
  }
2253
2255
  if (res.status < 200 || res.status >= 300) {
2254
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate2(text, 200)}`);
2256
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate2(text2, 200)}`);
2255
2257
  }
2256
- if (text === "" || text === '""') return null;
2258
+ if (text2 === "" || text2 === '""') return null;
2257
2259
  try {
2258
- return JSON.parse(text);
2260
+ return JSON.parse(text2);
2259
2261
  } catch (err) {
2260
2262
  const msg = err instanceof Error ? err.message : String(err);
2261
2263
  throw new Error(`POST ${path11}: decode: ${msg}`);
@@ -2296,17 +2298,17 @@ var AuthorizedClient = class _AuthorizedClient {
2296
2298
  body: fd,
2297
2299
  signal: options?.signal
2298
2300
  });
2299
- const text = await res.text();
2300
- _AuthorizedClient.throwIfScopeDeniedBody("POST", path11, res.status, text);
2301
+ const text2 = await res.text();
2302
+ _AuthorizedClient.throwIfScopeDeniedBody("POST", path11, res.status, text2);
2301
2303
  if (res.status === 401 || res.status === 403) {
2302
2304
  throw new SessionExpiredError(`POST ${path11} (HTTP ${res.status})`);
2303
2305
  }
2304
2306
  if (res.status < 200 || res.status >= 300) {
2305
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate2(text, 200)}`);
2307
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate2(text2, 200)}`);
2306
2308
  }
2307
- if (text === "" || text === '""') return null;
2309
+ if (text2 === "" || text2 === '""') return null;
2308
2310
  try {
2309
- return JSON.parse(text);
2311
+ return JSON.parse(text2);
2310
2312
  } catch (err) {
2311
2313
  const msg = err instanceof Error ? err.message : String(err);
2312
2314
  throw new Error(`POST ${path11}: decode: ${msg}`);
@@ -2355,17 +2357,17 @@ var AuthorizedClient = class _AuthorizedClient {
2355
2357
  body: fd,
2356
2358
  signal: options?.signal
2357
2359
  });
2358
- const text = await res.text();
2359
- _AuthorizedClient.throwIfScopeDeniedBody("POST", path11, res.status, text);
2360
+ const text2 = await res.text();
2361
+ _AuthorizedClient.throwIfScopeDeniedBody("POST", path11, res.status, text2);
2360
2362
  if (res.status === 401 || res.status === 403) {
2361
2363
  throw new SessionExpiredError(`POST ${path11} (HTTP ${res.status})`);
2362
2364
  }
2363
2365
  if (res.status < 200 || res.status >= 300) {
2364
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate2(text, 200)}`);
2366
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate2(text2, 200)}`);
2365
2367
  }
2366
- if (text === "" || text === '""') return null;
2368
+ if (text2 === "" || text2 === '""') return null;
2367
2369
  try {
2368
- return JSON.parse(text);
2370
+ return JSON.parse(text2);
2369
2371
  } catch (err) {
2370
2372
  const msg = err instanceof Error ? err.message : String(err);
2371
2373
  throw new Error(`POST ${path11}: decode: ${msg}`);
@@ -2426,12 +2428,12 @@ var AuthorizedClient = class _AuthorizedClient {
2426
2428
  }
2427
2429
  }
2428
2430
  if (this.isAuthRejected(res)) {
2429
- const text2 = await res.text();
2430
- _AuthorizedClient.throwIfScopeDeniedBody(method, path11, res.status, text2);
2431
+ const text3 = await res.text();
2432
+ _AuthorizedClient.throwIfScopeDeniedBody(method, path11, res.status, text3);
2431
2433
  if (refreshed || triedSessionOnly) {
2432
2434
  const attempted = refreshed ? "session-only retry and JWT refresh" : "session-only retry";
2433
2435
  throw new Error(
2434
- `${method} ${path11}: HTTP ${res.status} after ${attempted} (authenticated, but endpoint rejected \u2014 check permissions, tenant/org context, or body): ${truncate2(text2, 200)}`
2436
+ `${method} ${path11}: HTTP ${res.status} after ${attempted} (authenticated, but endpoint rejected \u2014 check permissions, tenant/org context, or body): ${truncate2(text3, 200)}`
2435
2437
  );
2436
2438
  }
2437
2439
  if (refreshErr !== void 0) {
@@ -2440,17 +2442,17 @@ var AuthorizedClient = class _AuthorizedClient {
2440
2442
  }
2441
2443
  throw new SessionExpiredError(`${method} ${path11} (HTTP ${res.status})`);
2442
2444
  }
2443
- const text = await res.text();
2445
+ const text2 = await res.text();
2444
2446
  if (res.status < 200 || res.status >= 300) {
2445
- throw new Error(`${method} ${path11}: HTTP ${res.status}: ${truncate2(text, 200)}`);
2447
+ throw new Error(`${method} ${path11}: HTTP ${res.status}: ${truncate2(text2, 200)}`);
2446
2448
  }
2447
2449
  try {
2448
2450
  await this._store.touch(this._serverKey, this._email);
2449
2451
  } catch {
2450
2452
  }
2451
- if (text === "" || text === '""') return null;
2453
+ if (text2 === "" || text2 === '""') return null;
2452
2454
  try {
2453
- return JSON.parse(text);
2455
+ return JSON.parse(text2);
2454
2456
  } catch (err) {
2455
2457
  const msg = err instanceof Error ? err.message : String(err);
2456
2458
  throw new Error(`${method} ${path11}: decode: ${msg}`);
@@ -2522,18 +2524,18 @@ var AuthorizedClient = class _AuthorizedClient {
2522
2524
  */
2523
2525
  static async throwIfScopeDenied(method, path11, res) {
2524
2526
  if (res.status !== 403) return;
2525
- let text;
2527
+ let text2;
2526
2528
  try {
2527
- text = await res.clone().text();
2529
+ text2 = await res.clone().text();
2528
2530
  } catch {
2529
2531
  return;
2530
2532
  }
2531
- _AuthorizedClient.throwIfScopeDeniedBody(method, path11, res.status, text);
2533
+ _AuthorizedClient.throwIfScopeDeniedBody(method, path11, res.status, text2);
2532
2534
  }
2533
2535
  /** {@link AuthorizedClient.throwIfScopeDenied} for a body already in hand — the multipart
2534
2536
  * methods and `requestJson`'s final branch have read theirs and must not read it twice. */
2535
- static throwIfScopeDeniedBody(method, path11, status2, text) {
2536
- const denial = parseScopeDenial(status2, text);
2537
+ static throwIfScopeDeniedBody(method, path11, status2, text2) {
2538
+ const denial = parseScopeDenial(status2, text2);
2537
2539
  if (denial !== void 0) throw new ScopeDeniedError(method, path11, denial.code);
2538
2540
  }
2539
2541
  /** True iff `res` is the auth-rejection signal (401 or 403). */
@@ -3424,9 +3426,9 @@ async function commandExists(cmd) {
3424
3426
  const probe = process.platform === "win32" ? "where" : "which";
3425
3427
  return new Promise((resolve3) => {
3426
3428
  try {
3427
- const child = spawn(probe, [cmd], { stdio: "ignore" });
3428
- child.on("error", () => resolve3(false));
3429
- child.on("close", (code) => resolve3(code === 0));
3429
+ const child2 = spawn(probe, [cmd], { stdio: "ignore" });
3430
+ child2.on("error", () => resolve3(false));
3431
+ child2.on("close", (code) => resolve3(code === 0));
3430
3432
  } catch {
3431
3433
  resolve3(false);
3432
3434
  }
@@ -3446,20 +3448,20 @@ async function realEnv() {
3446
3448
  }
3447
3449
  function spawnCapture(cmd, args) {
3448
3450
  return new Promise((resolve3, reject) => {
3449
- const child = spawn(cmd, args, {
3451
+ const child2 = spawn(cmd, args, {
3450
3452
  stdio: ["ignore", "pipe", "pipe"],
3451
3453
  windowsHide: false
3452
3454
  });
3453
3455
  let stdout = "";
3454
3456
  let stderr = "";
3455
- child.stdout.on("data", (c) => {
3457
+ child2.stdout.on("data", (c) => {
3456
3458
  stdout += c.toString("utf8");
3457
3459
  });
3458
- child.stderr.on("data", (c) => {
3460
+ child2.stderr.on("data", (c) => {
3459
3461
  stderr += c.toString("utf8");
3460
3462
  });
3461
- child.on("error", reject);
3462
- child.on("close", (code) => resolve3({ code: code ?? 0, stdout, stderr }));
3463
+ child2.on("error", reject);
3464
+ child2.on("close", (code) => resolve3({ code: code ?? 0, stdout, stderr }));
3463
3465
  });
3464
3466
  }
3465
3467
  function ttyPrompt(spec, io = { input: process.stdin, output: process.stderr }) {
@@ -4362,6 +4364,7 @@ async function setupCall(serverFlag, accountFlag, options = {}) {
4362
4364
  let deadline = Date.now() + timeoutMs;
4363
4365
  let timer = setTimeout(() => controller.abort(), timeoutMs);
4364
4366
  let cleared = false;
4367
+ let paused = false;
4365
4368
  let client;
4366
4369
  try {
4367
4370
  client = await AuthorizedClient.create(store, key, email, {
@@ -4388,11 +4391,17 @@ async function setupCall(serverFlag, accountFlag, options = {}) {
4388
4391
  if (cleared || controller.signal.aborted) return;
4389
4392
  if (!Number.isFinite(ms) || ms <= 0) return;
4390
4393
  const next = Date.now() + ms;
4391
- if (next <= deadline) return;
4394
+ if (!paused && next <= deadline) return;
4395
+ paused = false;
4392
4396
  clearTimeout(timer);
4393
4397
  deadline = next;
4394
4398
  timer = setTimeout(() => controller.abort(), ms);
4395
4399
  },
4400
+ pauseTimeout: () => {
4401
+ if (cleared || controller.signal.aborted) return;
4402
+ paused = true;
4403
+ clearTimeout(timer);
4404
+ },
4396
4405
  cleanup: () => {
4397
4406
  if (cleared) return;
4398
4407
  cleared = true;
@@ -4589,10 +4598,10 @@ function parsePendingCommands(v, pending) {
4589
4598
  }
4590
4599
  return out;
4591
4600
  }
4592
- function parseScopesMap(text) {
4601
+ function parseScopesMap(text2) {
4593
4602
  let raw;
4594
4603
  try {
4595
- raw = JSON.parse(text);
4604
+ raw = JSON.parse(text2);
4596
4605
  } catch {
4597
4606
  return void 0;
4598
4607
  }
@@ -4626,13 +4635,13 @@ function parseScopesMap(text) {
4626
4635
  return map;
4627
4636
  }
4628
4637
  function loadScopesMap(here = path3.dirname(fileURLToPath(import.meta.url))) {
4629
- let text;
4638
+ let text2;
4630
4639
  try {
4631
- text = readFileSync(path3.join(here, "scopes-map.json"), "utf8");
4640
+ text2 = readFileSync(path3.join(here, "scopes-map.json"), "utf8");
4632
4641
  } catch {
4633
4642
  return void 0;
4634
4643
  }
4635
- return parseScopesMap(text);
4644
+ return parseScopesMap(text2);
4636
4645
  }
4637
4646
  function scopesMapKey(cmd) {
4638
4647
  const names = [];
@@ -4652,10 +4661,10 @@ function commandPathOf(argv, root) {
4652
4661
  if (takesValue(token, cmd)) i += 1;
4653
4662
  continue;
4654
4663
  }
4655
- const child = childNamed(cmd, token);
4656
- if (child === void 0) break;
4657
- names.push(child.name());
4658
- cmd = child;
4664
+ const child2 = childNamed(cmd, token);
4665
+ if (child2 === void 0) break;
4666
+ names.push(child2.name());
4667
+ cmd = child2;
4659
4668
  }
4660
4669
  return names.join(" ");
4661
4670
  }
@@ -4672,11 +4681,11 @@ function takesValue(flag, cmd) {
4672
4681
  return declaredWithValueBelow(flag, cmd);
4673
4682
  }
4674
4683
  function declaredWithValueBelow(flag, cmd) {
4675
- for (const child of cmd.commands) {
4676
- for (const opt of child.options) {
4684
+ for (const child2 of cmd.commands) {
4685
+ for (const opt of child2.options) {
4677
4686
  if ((opt.short === flag || opt.long === flag) && (opt.required || opt.optional)) return true;
4678
4687
  }
4679
- if (declaredWithValueBelow(flag, child)) return true;
4688
+ if (declaredWithValueBelow(flag, child2)) return true;
4680
4689
  }
4681
4690
  return false;
4682
4691
  }
@@ -5011,14 +5020,14 @@ async function requestDeviceCode(host, options) {
5011
5020
  body: body.toString(),
5012
5021
  signal: options?.signal
5013
5022
  });
5014
- const text = await res.text();
5023
+ const text2 = await res.text();
5015
5024
  if (!res.ok) {
5016
5025
  const hint = flow === "v2" && res.status === 429 ? " (too many logins from this address: wait a minute and run `dr login` again)" : "";
5017
- throw new Error(`device/code failed: ${res.status} ${text.trim()}${hint}`);
5026
+ throw new Error(`device/code failed: ${res.status} ${text2.trim()}${hint}`);
5018
5027
  }
5019
5028
  let raw;
5020
5029
  try {
5021
- raw = JSON.parse(text);
5030
+ raw = JSON.parse(text2);
5022
5031
  } catch (err) {
5023
5032
  const msg = err instanceof Error ? err.message : String(err);
5024
5033
  throw new Error(`device/code parse: ${msg}`);
@@ -5049,10 +5058,10 @@ var defaultSleep = (ms, signal) => new Promise((resolve3, reject) => {
5049
5058
  { once: true }
5050
5059
  );
5051
5060
  });
5052
- function parseObject(text) {
5061
+ function parseObject(text2) {
5053
5062
  let raw;
5054
5063
  try {
5055
- raw = JSON.parse(text);
5064
+ raw = JSON.parse(text2);
5056
5065
  } catch (err) {
5057
5066
  const msg = err instanceof Error ? err.message : String(err);
5058
5067
  throw new Error(`token parse: ${msg}`);
@@ -5062,8 +5071,8 @@ function parseObject(text) {
5062
5071
  }
5063
5072
  return raw;
5064
5073
  }
5065
- function parseTokenResponse(text) {
5066
- const obj = parseObject(text);
5074
+ function parseTokenResponse(text2) {
5075
+ const obj = parseObject(text2);
5067
5076
  const tok = {
5068
5077
  sessionId: typeof obj.session_id === "string" ? obj.session_id : "",
5069
5078
  csrfToken: typeof obj.csrf_token === "string" ? obj.csrf_token : ""
@@ -5073,10 +5082,10 @@ function parseTokenResponse(text) {
5073
5082
  }
5074
5083
  return tok;
5075
5084
  }
5076
- function tryParseErrorCode(text) {
5085
+ function tryParseErrorCode(text2) {
5077
5086
  let raw;
5078
5087
  try {
5079
- raw = JSON.parse(text);
5088
+ raw = JSON.parse(text2);
5080
5089
  } catch {
5081
5090
  return "";
5082
5091
  }
@@ -5085,8 +5094,8 @@ function tryParseErrorCode(text) {
5085
5094
  return typeof errVal === "string" ? errVal : "";
5086
5095
  }
5087
5096
  async function pollForToken(host, deviceCode, options = {}) {
5088
- return poll(host, PATHS.legacy.token, deviceCode, options, (text) => {
5089
- const tok = parseTokenResponse(text);
5097
+ return poll(host, PATHS.legacy.token, deviceCode, options, (text2) => {
5098
+ const tok = parseTokenResponse(text2);
5090
5099
  if (!tok.sessionId || !tok.csrfToken) {
5091
5100
  throw new Error("token response missing session_id or csrf_token");
5092
5101
  }
@@ -5094,8 +5103,8 @@ async function pollForToken(host, deviceCode, options = {}) {
5094
5103
  });
5095
5104
  }
5096
5105
  async function pollForV2Token(host, deviceCode, options = {}) {
5097
- return poll(host, PATHS.v2.token, deviceCode, options, (text) => {
5098
- const obj = parseObject(text);
5106
+ return poll(host, PATHS.v2.token, deviceCode, options, (text2) => {
5107
+ const obj = parseObject(text2);
5099
5108
  const access = typeof obj.access === "string" ? obj.access : "";
5100
5109
  const refresh = typeof obj.refresh === "string" ? obj.refresh : "";
5101
5110
  if (access === "" || refresh === "") {
@@ -5108,7 +5117,7 @@ async function poll(host, path11, deviceCode, options, onSuccess) {
5108
5117
  const interval = options.interval ?? 2e3;
5109
5118
  const maxWait = options.maxWait ?? 6e5;
5110
5119
  const now = options.now ?? Date.now;
5111
- const sleep3 = options.sleep ?? defaultSleep;
5120
+ const sleep4 = options.sleep ?? defaultSleep;
5112
5121
  const signal = options.signal;
5113
5122
  const base = host.replace(/\/+$/, "");
5114
5123
  const deadline = now() + maxWait;
@@ -5130,19 +5139,19 @@ async function poll(host, path11, deviceCode, options, onSuccess) {
5130
5139
  body: body.toString(),
5131
5140
  signal
5132
5141
  });
5133
- const text = await res.text();
5142
+ const text2 = await res.text();
5134
5143
  if (res.ok) {
5135
- return onSuccess(text);
5144
+ return onSuccess(text2);
5136
5145
  }
5137
- const errCode = tryParseErrorCode(text);
5146
+ const errCode = tryParseErrorCode(text2);
5138
5147
  if (errCode === "authorization_pending" || errCode === "slow_down") {
5139
- await sleep3(interval, signal);
5148
+ await sleep4(interval, signal);
5140
5149
  continue;
5141
5150
  }
5142
5151
  if (errCode) {
5143
5152
  throw new Error(`token error: ${errCode}`);
5144
5153
  }
5145
- throw new Error(`token HTTP ${res.status}: ${text.trim()}`);
5154
+ throw new Error(`token HTTP ${res.status}: ${text2.trim()}`);
5146
5155
  }
5147
5156
  }
5148
5157
 
@@ -5642,11 +5651,11 @@ var UserExistsError = class extends Error {
5642
5651
  async function create(client, payload, options) {
5643
5652
  const path11 = "/api/it/users";
5644
5653
  const res = await client.do("POST", path11, JSON.stringify(payload), options);
5645
- const text = await res.text();
5654
+ const text2 = await res.text();
5646
5655
  if (res.status >= 200 && res.status < 300) {
5647
5656
  let parsed2;
5648
5657
  try {
5649
- parsed2 = JSON.parse(text);
5658
+ parsed2 = JSON.parse(text2);
5650
5659
  } catch (err) {
5651
5660
  const msg = err instanceof Error ? err.message : String(err);
5652
5661
  throw new Error(`POST ${path11}: decode: ${msg}`);
@@ -5664,7 +5673,7 @@ async function create(client, payload, options) {
5664
5673
  if (res.status === 400) {
5665
5674
  let details;
5666
5675
  try {
5667
- const body = JSON.parse(text);
5676
+ const body = JSON.parse(text2);
5668
5677
  if (typeof body.details === "string") details = body.details;
5669
5678
  } catch {
5670
5679
  }
@@ -5672,23 +5681,23 @@ async function create(client, payload, options) {
5672
5681
  throw new UserExistsError(payload.email);
5673
5682
  }
5674
5683
  }
5675
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5684
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5676
5685
  }
5677
5686
  async function registerIdentity(client, id, options) {
5678
5687
  const path11 = `/api/it/users/${id}/register_identity`;
5679
5688
  const res = await client.do("PUT", path11, void 0, options);
5680
- const text = await res.text();
5689
+ const text2 = await res.text();
5681
5690
  if (res.status >= 200 && res.status < 300) {
5682
5691
  let body;
5683
5692
  try {
5684
- body = JSON.parse(text);
5693
+ body = JSON.parse(text2);
5685
5694
  } catch (err) {
5686
5695
  const msg = err instanceof Error ? err.message : String(err);
5687
5696
  throw new Error(`PUT ${path11}: decode: ${msg}`);
5688
5697
  }
5689
5698
  if (typeof body.sent !== "boolean") {
5690
5699
  throw new Error(
5691
- `PUT ${path11}: expected a boolean "sent" in the 2xx body, got ${truncate4(text, 200)}`
5700
+ `PUT ${path11}: expected a boolean "sent" in the 2xx body, got ${truncate4(text2, 200)}`
5692
5701
  );
5693
5702
  }
5694
5703
  return body.sent ? { status: "registered" } : { status: "already_exists" };
@@ -5696,42 +5705,42 @@ async function registerIdentity(client, id, options) {
5696
5705
  if (res.status === 502) {
5697
5706
  let reason;
5698
5707
  try {
5699
- const body = JSON.parse(text);
5708
+ const body = JSON.parse(text2);
5700
5709
  if (typeof body.reason === "string") reason = body.reason;
5701
5710
  } catch {
5702
5711
  }
5703
5712
  return { status: "failed", ...reason !== void 0 ? { reason } : {} };
5704
5713
  }
5705
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5714
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5706
5715
  }
5707
5716
  async function assignPermissionGroups(client, id, groupIds, options) {
5708
5717
  const path11 = `/api/it/users/${id}/permission_groups`;
5709
5718
  const body = JSON.stringify({ permission_groups: groupIds });
5710
5719
  const res = await client.do("POST", path11, body, options);
5711
- const text = await res.text();
5720
+ const text2 = await res.text();
5712
5721
  if (res.status >= 200 && res.status < 300) {
5713
5722
  return;
5714
5723
  }
5715
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5724
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5716
5725
  }
5717
5726
  async function updateUserRole(client, id, role, options) {
5718
5727
  const path11 = `/api/it/users/${id}/permissions`;
5719
5728
  const body = JSON.stringify({ role });
5720
5729
  const res = await client.do("PUT", path11, body, options);
5721
- const text = await res.text();
5730
+ const text2 = await res.text();
5722
5731
  if (res.status >= 200 && res.status < 300) {
5723
5732
  return;
5724
5733
  }
5725
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5734
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5726
5735
  }
5727
5736
  async function removePermissionGroup(client, id, groupId, options) {
5728
5737
  const path11 = `/api/it/users/${id}/permission_groups/${groupId}`;
5729
5738
  const res = await client.do("DELETE", path11, void 0, options);
5730
- const text = await res.text();
5739
+ const text2 = await res.text();
5731
5740
  if (res.status >= 200 && res.status < 300) {
5732
5741
  return;
5733
5742
  }
5734
- throw new Error(`DELETE ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5743
+ throw new Error(`DELETE ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5735
5744
  }
5736
5745
  var PasswordValidationError = class extends Error {
5737
5746
  errors;
@@ -5751,11 +5760,11 @@ async function setPassword(client, id, password, options) {
5751
5760
  await res.text();
5752
5761
  return;
5753
5762
  }
5754
- const text = await res.text();
5763
+ const text2 = await res.text();
5755
5764
  if (res.status === 400) {
5756
5765
  let parsed2;
5757
5766
  try {
5758
- parsed2 = JSON.parse(text);
5767
+ parsed2 = JSON.parse(text2);
5759
5768
  } catch {
5760
5769
  }
5761
5770
  if (typeof parsed2 === "object" && parsed2 !== null && "password" in parsed2 && Array.isArray(parsed2.password)) {
@@ -5764,7 +5773,7 @@ async function setPassword(client, id, password, options) {
5764
5773
  throw new PasswordValidationError(messages);
5765
5774
  }
5766
5775
  }
5767
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5776
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5768
5777
  }
5769
5778
  var EmailInUseError = class extends Error {
5770
5779
  email;
@@ -5782,14 +5791,14 @@ async function updateUserEmail(client, id, newEmail, options) {
5782
5791
  excel_online_email: current.profile?.excel_online_email ?? null
5783
5792
  });
5784
5793
  const res = await client.do("PUT", path11, body, options);
5785
- const text = await res.text();
5794
+ const text2 = await res.text();
5786
5795
  if (res.status >= 200 && res.status < 300) {
5787
5796
  return { id, email: newEmail, previousEmail: current.email };
5788
5797
  }
5789
- if (res.status === 400 && /already in use/i.test(text)) {
5798
+ if (res.status === 400 && /already in use/i.test(text2)) {
5790
5799
  throw new EmailInUseError(newEmail);
5791
5800
  }
5792
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5801
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5793
5802
  }
5794
5803
  var StoredEmailInvalidError = class extends Error {
5795
5804
  id;
@@ -5813,7 +5822,7 @@ async function setEmailNotifications(client, id, enabled, options) {
5813
5822
  excel_online_email: current.profile?.excel_online_email ?? null
5814
5823
  });
5815
5824
  const res = await client.do("PUT", path11, body, options);
5816
- const text = await res.text();
5825
+ const text2 = await res.text();
5817
5826
  if (res.status >= 200 && res.status < 300) {
5818
5827
  return {
5819
5828
  id,
@@ -5822,10 +5831,10 @@ async function setEmailNotifications(client, id, enabled, options) {
5822
5831
  blocked: current.profile?.is_blocked === true
5823
5832
  };
5824
5833
  }
5825
- if (res.status === 400 && /valid email address/i.test(text)) {
5834
+ if (res.status === 400 && /valid email address/i.test(text2)) {
5826
5835
  throw new StoredEmailInvalidError(id, current.email);
5827
5836
  }
5828
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
5837
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
5829
5838
  }
5830
5839
  async function getCanLoginAs(client, params = {}, options) {
5831
5840
  const qs = new URLSearchParams();
@@ -7063,66 +7072,66 @@ async function listPermissionGroups(client, options) {
7063
7072
  async function createPermissionGroup(client, payload, options) {
7064
7073
  const path11 = "/api/it/permission_groups";
7065
7074
  const res = await client.do("POST", path11, JSON.stringify(payload), options);
7066
- const text = await res.text();
7075
+ const text2 = await res.text();
7067
7076
  if (res.status < 200 || res.status >= 300) {
7068
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
7077
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
7069
7078
  }
7070
- return parsePermissionGroup("POST", path11, text);
7079
+ return parsePermissionGroup("POST", path11, text2);
7071
7080
  }
7072
- function parsePermissionGroup(method, path11, text) {
7081
+ function parsePermissionGroup(method, path11, text2) {
7073
7082
  try {
7074
- return JSON.parse(text);
7083
+ return JSON.parse(text2);
7075
7084
  } catch (err) {
7076
7085
  const msg = err instanceof Error ? err.message : String(err);
7077
- throw new Error(`${method} ${path11}: decode: ${msg} (body: ${truncate4(text, 200)})`);
7086
+ throw new Error(`${method} ${path11}: decode: ${msg} (body: ${truncate4(text2, 200)})`);
7078
7087
  }
7079
7088
  }
7080
7089
  async function getPermissionGroup(client, id, options) {
7081
7090
  const path11 = `/api/it/permission_groups/${id}`;
7082
7091
  const res = await client.do("GET", path11, void 0, options);
7083
- const text = await res.text();
7092
+ const text2 = await res.text();
7084
7093
  if (res.status < 200 || res.status >= 300) {
7085
- throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
7094
+ throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
7086
7095
  }
7087
- return parsePermissionGroup("GET", path11, text);
7096
+ return parsePermissionGroup("GET", path11, text2);
7088
7097
  }
7089
7098
  async function updatePermissionGroup(client, id, name, options) {
7090
7099
  const path11 = `/api/it/permission_groups/${id}`;
7091
7100
  const res = await client.do("PUT", path11, JSON.stringify({ name }), options);
7092
- const text = await res.text();
7101
+ const text2 = await res.text();
7093
7102
  if (res.status < 200 || res.status >= 300) {
7094
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
7103
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
7095
7104
  }
7096
- return parsePermissionGroup("PUT", path11, text);
7105
+ return parsePermissionGroup("PUT", path11, text2);
7097
7106
  }
7098
7107
  async function deletePermissionGroup(client, id, options) {
7099
7108
  const path11 = `/api/it/permission_groups/${id}`;
7100
7109
  const res = await client.do("DELETE", path11, void 0, options);
7101
7110
  if (res.status < 200 || res.status >= 300) {
7102
- const text = await res.text();
7103
- throw new Error(`DELETE ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
7111
+ const text2 = await res.text();
7112
+ throw new Error(`DELETE ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
7104
7113
  }
7105
7114
  }
7106
- function decodeJsonBody(method, path11, text) {
7115
+ function decodeJsonBody(method, path11, text2) {
7107
7116
  try {
7108
- return JSON.parse(text);
7117
+ return JSON.parse(text2);
7109
7118
  } catch (err) {
7110
7119
  const msg = err instanceof Error ? err.message : String(err);
7111
- throw new Error(`${method} ${path11}: decode: ${msg} (body: ${truncate4(text, 200)})`);
7120
+ throw new Error(`${method} ${path11}: decode: ${msg} (body: ${truncate4(text2, 200)})`);
7112
7121
  }
7113
7122
  }
7114
- function redactPasswordsInBodyText(text) {
7115
- return text.replace(/"password"\s*:\s*"(?:[^"\\]|\\.)*"/g, '"password":"[redacted]"');
7123
+ function redactPasswordsInBodyText(text2) {
7124
+ return text2.replace(/"password"\s*:\s*"(?:[^"\\]|\\.)*"/g, '"password":"[redacted]"');
7116
7125
  }
7117
7126
  async function orgMigrationRequest(client, method, path11, payload, options) {
7118
7127
  const res = await client.do(method, path11, JSON.stringify(payload), options);
7119
- const text = await res.text();
7128
+ const text2 = await res.text();
7120
7129
  if (res.status < 200 || res.status >= 300) {
7121
7130
  throw new Error(
7122
- `${method} ${path11}: HTTP ${res.status}: ${truncate4(redactPasswordsInBodyText(text), 600)}`
7131
+ `${method} ${path11}: HTTP ${res.status}: ${truncate4(redactPasswordsInBodyText(text2), 600)}`
7123
7132
  );
7124
7133
  }
7125
- return decodeJsonBody(method, path11, text);
7134
+ return decodeJsonBody(method, path11, text2);
7126
7135
  }
7127
7136
  async function exportCreate(client, payload, options) {
7128
7137
  return orgMigrationRequest(
@@ -7165,8 +7174,8 @@ async function importDeleteOrgs(client, payload, options) {
7165
7174
  import { promises as fs4 } from "fs";
7166
7175
 
7167
7176
  // src/commands/_csv.ts
7168
- function parseCsvRows(text) {
7169
- const normalized = text.replace(/\r\n/g, "\n").replace(/\n$/, "");
7177
+ function parseCsvRows(text2) {
7178
+ const normalized = text2.replace(/\r\n/g, "\n").replace(/\n$/, "");
7170
7179
  if (normalized === "") return [];
7171
7180
  const rows = [];
7172
7181
  let row = [];
@@ -7216,6 +7225,9 @@ function parseCsvRows(text) {
7216
7225
  rows.push(row);
7217
7226
  return rows;
7218
7227
  }
7228
+ function formatCsvRow(cells) {
7229
+ return cells.map((c) => /[",\r\n]/.test(c) ? `"${c.replace(/"/g, '""')}"` : c).join(",");
7230
+ }
7219
7231
 
7220
7232
  // src/commands/_userCsv.ts
7221
7233
  var EXPECTED_HEADER = [
@@ -7245,8 +7257,8 @@ async function readUsersCsv(path11) {
7245
7257
  }
7246
7258
  return parseUsersCsvText(raw);
7247
7259
  }
7248
- function parseUsersCsvText(text) {
7249
- const withoutBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
7260
+ function parseUsersCsvText(text2) {
7261
+ const withoutBom = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
7250
7262
  const rows = parseCsvRows(withoutBom);
7251
7263
  if (rows.length === 0) {
7252
7264
  throw new Error("--file: CSV is empty");
@@ -7420,11 +7432,11 @@ async function getRoles(client, segment, options) {
7420
7432
  async function grantRoleToUsers(client, appSegment, payload, options) {
7421
7433
  const path11 = `/api/${appSegment}/users/grant_permissions_bulk`;
7422
7434
  const res = await client.do("POST", path11, JSON.stringify(payload), options);
7423
- const text = await res.text();
7435
+ const text2 = await res.text();
7424
7436
  if (res.status >= 200 && res.status < 300) {
7425
7437
  return;
7426
7438
  }
7427
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
7439
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
7428
7440
  }
7429
7441
  async function getUserTableRoles(client, userId, options) {
7430
7442
  const out = await client.getJSON(
@@ -7444,11 +7456,11 @@ async function setUserPermissionFilters(client, userId, newFilters, options) {
7444
7456
  const path11 = `/api/it/users/${userId}/permission_filters`;
7445
7457
  const body = JSON.stringify({ new_filters: newFilters });
7446
7458
  const res = await client.do("POST", path11, body, options);
7447
- const text = await res.text();
7459
+ const text2 = await res.text();
7448
7460
  if (res.status >= 200 && res.status < 300) {
7449
7461
  return;
7450
7462
  }
7451
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text, 200)}`);
7463
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncate4(text2, 200)}`);
7452
7464
  }
7453
7465
  async function getTableRoles(client, options) {
7454
7466
  return getRoles(client, "table", options);
@@ -8155,8 +8167,8 @@ async function readPermissionsCsv(path11) {
8155
8167
  }
8156
8168
  return parsePermissionsCsvText(raw);
8157
8169
  }
8158
- function parsePermissionsCsvText(text) {
8159
- const withoutBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
8170
+ function parsePermissionsCsvText(text2) {
8171
+ const withoutBom = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
8160
8172
  const rows = parseCsvRows(withoutBom);
8161
8173
  if (rows.length === 0) {
8162
8174
  throw new Error("--file: CSV is empty");
@@ -8589,8 +8601,8 @@ async function readEmailRenameCsv(path11) {
8589
8601
  }
8590
8602
  return parseEmailRenameCsvText(raw);
8591
8603
  }
8592
- function parseEmailRenameCsvText(text) {
8593
- const withoutBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
8604
+ function parseEmailRenameCsvText(text2) {
8605
+ const withoutBom = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
8594
8606
  const rows = parseCsvRows(withoutBom);
8595
8607
  if (rows.length === 0) {
8596
8608
  throw new Error("--file: CSV is empty");
@@ -10223,8 +10235,8 @@ async function readGroupsCsv(path11) {
10223
10235
  }
10224
10236
  return parseGroupsCsvText(raw);
10225
10237
  }
10226
- function parseGroupsCsvText(text) {
10227
- const withoutBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
10238
+ function parseGroupsCsvText(text2) {
10239
+ const withoutBom = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
10228
10240
  const rows = parseCsvRows(withoutBom);
10229
10241
  if (rows.length === 0) {
10230
10242
  throw new Error("--file: CSV is empty");
@@ -11889,8 +11901,8 @@ function envelopeErr(success, error) {
11889
11901
  }
11890
11902
  throw new Error("finance-os returned success=false with no error message");
11891
11903
  }
11892
- function describeFinanceOsFailure(text) {
11893
- const trimmed = text.trim();
11904
+ function describeFinanceOsFailure(text2) {
11905
+ const trimmed = text2.trim();
11894
11906
  let parsed2;
11895
11907
  try {
11896
11908
  parsed2 = JSON.parse(trimmed);
@@ -11954,9 +11966,9 @@ var AGGREGATE_POLL_TIMEOUT_MS = 6e4;
11954
11966
  async function startTask(client, label, path11, body, options) {
11955
11967
  const res = await client.do("POST", path11, JSON.stringify(body), options);
11956
11968
  if (res.status !== 202) {
11957
- const text = await res.text();
11969
+ const text2 = await res.text();
11958
11970
  throw new Error(
11959
- `${label} POST: expected 202, got ${res.status}: ${describeFinanceOsFailure(text)}`
11971
+ `${label} POST: expected 202, got ${res.status}: ${describeFinanceOsFailure(text2)}`
11960
11972
  );
11961
11973
  }
11962
11974
  const location = res.headers.get("Location") ?? "";
@@ -12000,10 +12012,10 @@ async function getTaskOnce(client, label, pollPath, options) {
12000
12012
  throw new Error(`poll ${label}: ${msg}`);
12001
12013
  }
12002
12014
  if (res.status === 200) {
12003
- const text2 = await res.text();
12015
+ const text3 = await res.text();
12004
12016
  let parsed2;
12005
12017
  try {
12006
- parsed2 = text2 === "" ? null : JSON.parse(text2);
12018
+ parsed2 = text3 === "" ? null : JSON.parse(text3);
12007
12019
  } catch (err) {
12008
12020
  const msg = err instanceof Error ? err.message : String(err);
12009
12021
  throw new Error(`poll ${label}: ${msg}`);
@@ -12014,9 +12026,9 @@ async function getTaskOnce(client, label, pollPath, options) {
12014
12026
  await res.text();
12015
12027
  return { env: null, status: res.status };
12016
12028
  }
12017
- const text = await res.text();
12029
+ const text2 = await res.text();
12018
12030
  throw new Error(
12019
- `poll ${label}: unexpected status ${res.status}: ${describeFinanceOsFailure(text)}`
12031
+ `poll ${label}: unexpected status ${res.status}: ${describeFinanceOsFailure(text2)}`
12020
12032
  );
12021
12033
  }
12022
12034
  var defaultSleep2 = (ms, signal) => new Promise((resolve3, reject) => {
@@ -12037,7 +12049,7 @@ var defaultSleep2 = (ms, signal) => new Promise((resolve3, reject) => {
12037
12049
  async function pollTask(client, label, path11, options = {}) {
12038
12050
  const interval = options.pollIntervalMs ?? AGGREGATE_POLL_INTERVAL_MS;
12039
12051
  const timeout = options.timeoutMs ?? AGGREGATE_POLL_TIMEOUT_MS;
12040
- const sleep3 = options.sleep ?? defaultSleep2;
12052
+ const sleep4 = options.sleep ?? defaultSleep2;
12041
12053
  const now = options.now ?? Date.now;
12042
12054
  const inner = new AbortController();
12043
12055
  const onOuterAbort = () => inner.abort(options.signal?.reason);
@@ -12079,7 +12091,7 @@ async function pollTask(client, label, path11, options = {}) {
12079
12091
  return result.env;
12080
12092
  }
12081
12093
  try {
12082
- await sleep3(interval, inner.signal);
12094
+ await sleep4(interval, inner.signal);
12083
12095
  } catch {
12084
12096
  const reason = inner.signal.reason instanceof Error ? inner.signal.reason.message : String(inner.signal.reason ?? "aborted");
12085
12097
  throw new Error(`${label} polling timed out: ${reason}`);
@@ -14280,7 +14292,7 @@ function renderFolderTree(out, tree) {
14280
14292
  node.parent_id !== null ? String(node.parent_id) : "(root)"
14281
14293
  ]
14282
14294
  });
14283
- for (const child of node.children ?? []) walk(child, depth + 1);
14295
+ for (const child2 of node.children ?? []) walk(child2, depth + 1);
14284
14296
  };
14285
14297
  for (const root of tree) walk(root, 0);
14286
14298
  writeTable9(out, rows);
@@ -15775,6 +15787,503 @@ function collectArray3(value, previous) {
15775
15787
  return previous.concat([value]);
15776
15788
  }
15777
15789
 
15790
+ // src/commands/odata.ts
15791
+ import { once } from "events";
15792
+
15793
+ // src/api/odata.ts
15794
+ import { XMLParser } from "fast-xml-parser";
15795
+ var ODATA_VARIANTS = ["latest", "cleared", "full", "full-cleared"];
15796
+ var VARIANT_BY_SUFFIX = /* @__PURE__ */ new Map([
15797
+ ["", "latest"],
15798
+ [" (Cleared)", "cleared"],
15799
+ [" (Full)", "full"],
15800
+ [" (Full)(Cleared)", "full-cleared"]
15801
+ ]);
15802
+ var TAG = "%";
15803
+ var TEXT = "#text";
15804
+ var ATTR = "@_";
15805
+ var parser = new XMLParser({
15806
+ ignoreAttributes: false,
15807
+ attributeNamePrefix: ATTR,
15808
+ textNodeName: TEXT,
15809
+ removeNSPrefix: true,
15810
+ parseTagValue: false,
15811
+ parseAttributeValue: false,
15812
+ trimValues: false,
15813
+ htmlEntities: true,
15814
+ transformTagName: (name) => name.startsWith(TAG) ? name : `${TAG}${name}`,
15815
+ isArray: (_name, _jpath, _isLeaf, isAttribute) => isAttribute !== true
15816
+ });
15817
+ function isNode(v) {
15818
+ return typeof v === "object" && v !== null && !Array.isArray(v);
15819
+ }
15820
+ function children(node, tag) {
15821
+ if (!isNode(node)) return [];
15822
+ const v = node[`${TAG}${tag}`];
15823
+ return Array.isArray(v) ? v : [];
15824
+ }
15825
+ function child(node, tag) {
15826
+ return children(node, tag)[0];
15827
+ }
15828
+ function attr(node, name) {
15829
+ if (!isNode(node)) return void 0;
15830
+ const v = node[`${ATTR}${name}`];
15831
+ return typeof v === "string" ? v : void 0;
15832
+ }
15833
+ function text(node) {
15834
+ if (typeof node === "string") return node;
15835
+ if (isNode(node) && typeof node[TEXT] === "string") return node[TEXT];
15836
+ return "";
15837
+ }
15838
+ function parseXml(xml, what) {
15839
+ let doc;
15840
+ try {
15841
+ doc = parser.parse(xml);
15842
+ } catch (err) {
15843
+ const msg = err instanceof Error ? err.message : String(err);
15844
+ throw new Error(`not ${what}: ${msg}`);
15845
+ }
15846
+ if (!isNode(doc)) throw new Error(`not ${what}`);
15847
+ return doc;
15848
+ }
15849
+ function parseServiceDocument(xml) {
15850
+ const doc = parseXml(xml, "an OData service document");
15851
+ const service = child(doc, "service");
15852
+ if (service === void 0) {
15853
+ throw new Error("not an OData service document (no <service> root)");
15854
+ }
15855
+ const out = [];
15856
+ for (const ws of children(service, "workspace")) {
15857
+ for (const c of children(ws, "collection")) {
15858
+ const key = attr(c, "href");
15859
+ if (key === void 0) continue;
15860
+ out.push({ key, title: text(child(c, "title")), updated: text(child(c, "updated")) });
15861
+ }
15862
+ }
15863
+ return out;
15864
+ }
15865
+ function parseMetadata(xml) {
15866
+ const doc = parseXml(xml, "an OData $metadata document");
15867
+ const edmx = child(doc, "Edmx");
15868
+ if (edmx === void 0) {
15869
+ throw new Error("not an OData $metadata document (no <edmx:Edmx> root)");
15870
+ }
15871
+ const out = [];
15872
+ for (const ds of children(edmx, "DataServices")) {
15873
+ for (const schema of children(ds, "Schema")) {
15874
+ for (const et of children(schema, "EntityType")) {
15875
+ const name = attr(et, "Name");
15876
+ if (name === void 0) continue;
15877
+ const columns = [];
15878
+ for (const p of children(et, "Property")) {
15879
+ const pname = attr(p, "Name");
15880
+ if (pname === void 0) continue;
15881
+ columns.push({ name: pname, type: attr(p, "Type") ?? "" });
15882
+ }
15883
+ out.push({ name, columns });
15884
+ }
15885
+ }
15886
+ }
15887
+ return out;
15888
+ }
15889
+ function parseFeed(xml) {
15890
+ const tooLarge = /^\s*\[\{'error': 'too_large_data'(?:, 'rows_count': (\d+))?(?:, 'cols_count': (\d+))?/.exec(
15891
+ xml
15892
+ );
15893
+ if (tooLarge !== null) {
15894
+ const dims = tooLarge[1] !== void 0 && tooLarge[2] !== void 0 ? ` (${tooLarge[1]} rows \xD7 ${tooLarge[2]} columns)` : "";
15895
+ throw new Error(
15896
+ `the server refused the page as too large${dims}: lower --page-size or narrow --select`
15897
+ );
15898
+ }
15899
+ if (!/<\/(?:[\w.-]+:)?feed>\s*$/.test(xml)) {
15900
+ throw new Error("not an OData Atom feed: the body ends before </feed> (truncated response)");
15901
+ }
15902
+ const doc = parseXml(xml, "an OData Atom feed");
15903
+ const feed = child(doc, "feed");
15904
+ if (feed === void 0) {
15905
+ throw new Error("not an OData Atom feed (no <feed> root)");
15906
+ }
15907
+ const out = [];
15908
+ for (const e of children(feed, "entry")) {
15909
+ const row = /* @__PURE__ */ new Map();
15910
+ const props = child(child(e, "content"), "properties");
15911
+ if (isNode(props)) {
15912
+ for (const [k, v] of Object.entries(props)) {
15913
+ if (!k.startsWith(TAG) || !Array.isArray(v)) continue;
15914
+ const value = v[0];
15915
+ row.set(k.slice(TAG.length), attr(value, "null") === "true" ? null : text(value));
15916
+ }
15917
+ }
15918
+ out.push(row);
15919
+ }
15920
+ return out;
15921
+ }
15922
+ function parseODataTitle(title) {
15923
+ const m = /^(.*)_(\d+)( \(Full\)\(Cleared\)| \(Cleared\)| \(Full\))?$/s.exec(title);
15924
+ if (m === null) return null;
15925
+ const variant = VARIANT_BY_SUFFIX.get(m[3] ?? "");
15926
+ if (variant === void 0) return null;
15927
+ return { baseTitle: m[1] ?? "", tableId: Number(m[2]), variant };
15928
+ }
15929
+ function findByTableId(items, titleOf, tableId, variant) {
15930
+ const hits = items.filter((it) => {
15931
+ const p = parseODataTitle(titleOf(it));
15932
+ return p !== null && p.tableId === tableId && p.variant === variant;
15933
+ });
15934
+ if (hits.length > 1) {
15935
+ const titles = hits.map((h) => `"${titleOf(h)}"`).join(", ");
15936
+ throw new Error(`table ${tableId} (${variant}) is ambiguous over OData: ${titles}`);
15937
+ }
15938
+ return hits[0];
15939
+ }
15940
+ function tablePagePath(key, q) {
15941
+ let path11 = `/OData.svc/${key}?$top=${q.top}&$skip=${q.skip}`;
15942
+ if (q.select !== void 0 && q.select.length > 0) {
15943
+ path11 += `&$select=${encodeURIComponent(q.select.join(","))}`;
15944
+ }
15945
+ if (q.filter !== void 0 && q.filter !== "") {
15946
+ path11 += `&$filter=${encodeURIComponent(q.filter)}`;
15947
+ }
15948
+ return path11;
15949
+ }
15950
+ async function getXml(client, path11, accept, options) {
15951
+ const res = await client.do("GET", path11, void 0, {
15952
+ ...options,
15953
+ headers: { ...options?.headers, Accept: accept }
15954
+ });
15955
+ const body = await res.text();
15956
+ if (res.status < 200 || res.status >= 300) {
15957
+ throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncate4(body, 200)}`);
15958
+ }
15959
+ return body;
15960
+ }
15961
+ async function getServiceDocument(client, options) {
15962
+ return parseServiceDocument(await getXml(client, "/OData.svc", "application/xml", options));
15963
+ }
15964
+ async function getMetadata(client, options) {
15965
+ return parseMetadata(await getXml(client, "/OData.svc/$metadata", "application/xml", options));
15966
+ }
15967
+ async function getTablePage(client, key, q, options) {
15968
+ return parseFeed(await getXml(client, tablePagePath(key, q), "application/atom+xml", options));
15969
+ }
15970
+
15971
+ // src/commands/odata.ts
15972
+ var ODATA_DEFAULT_PAGE_SIZE = 3e4;
15973
+ var ODATA_REQUEST_TIMEOUT_MS = 12e4;
15974
+ function notVisible(tableId, variant, where) {
15975
+ return new Error(
15976
+ `table ${tableId} (${variant}) is not visible to this user over OData (not in ${where})`
15977
+ );
15978
+ }
15979
+ async function runODataTables(cfg) {
15980
+ const collections = await getServiceDocument(cfg.client, { signal: cfg.signal });
15981
+ const byId = /* @__PURE__ */ new Map();
15982
+ for (const c of collections) {
15983
+ const parts = parseODataTitle(c.title);
15984
+ if (parts === null) continue;
15985
+ let info = byId.get(parts.tableId);
15986
+ if (info === void 0) {
15987
+ info = { id: parts.tableId, title: parts.baseTitle, variants: [] };
15988
+ byId.set(parts.tableId, info);
15989
+ }
15990
+ info.variants.push({ variant: parts.variant, title: c.title, key: c.key, updated: c.updated });
15991
+ }
15992
+ const order = ODATA_VARIANTS;
15993
+ const tables = [...byId.values()].sort((a, b) => a.id - b.id);
15994
+ for (const t of tables) {
15995
+ t.variants.sort((a, b) => order.indexOf(a.variant) - order.indexOf(b.variant));
15996
+ }
15997
+ if (cfg.asJson) {
15998
+ writeJSONIndented(cfg.out, tables);
15999
+ return;
16000
+ }
16001
+ if (tables.length === 0) {
16002
+ cfg.out.write("(no tables visible over OData)\n");
16003
+ return;
16004
+ }
16005
+ const rows = [["ID", "TITLE", "VARIANTS", "UPDATED"]];
16006
+ for (const t of tables) {
16007
+ const updated = (t.variants.find((v) => v.variant === "latest") ?? t.variants[0])?.updated;
16008
+ rows.push([String(t.id), t.title, t.variants.map((v) => v.variant).join(","), updated ?? ""]);
16009
+ }
16010
+ for (const line of formatTable(rows, columnWidths(rows))) cfg.out.write(`${line}
16011
+ `);
16012
+ }
16013
+ async function runODataMetadata(cfg) {
16014
+ const types = await getMetadata(cfg.client, { signal: cfg.signal });
16015
+ const entity = findByTableId(types, (t) => t.name, cfg.tableId, cfg.variant);
16016
+ if (entity === void 0) throw notVisible(cfg.tableId, cfg.variant, "$metadata");
16017
+ if (cfg.asJson) {
16018
+ writeJSONIndented(cfg.out, entity);
16019
+ return;
16020
+ }
16021
+ cfg.out.write(`${entity.name}
16022
+ `);
16023
+ if (entity.columns.length === 0) {
16024
+ cfg.out.write("(no columns)\n");
16025
+ return;
16026
+ }
16027
+ cfg.out.write("\n");
16028
+ const rows = [["COLUMN", "TYPE"], ...entity.columns.map((c) => [c.name, c.type])];
16029
+ for (const line of formatTable(rows, columnWidths(rows))) cfg.out.write(`${line}
16030
+ `);
16031
+ }
16032
+ async function runODataData(cfg) {
16033
+ const req = { signal: cfg.signal };
16034
+ const budget = () => cfg.extendTimeout?.(cfg.requestTimeoutMs ?? ODATA_REQUEST_TIMEOUT_MS);
16035
+ let key = cfg.key;
16036
+ if (key === void 0) {
16037
+ budget();
16038
+ const collections = await getServiceDocument(cfg.client, req);
16039
+ const hit = findByTableId(collections, (c) => c.title, cfg.tableId, cfg.variant);
16040
+ if (hit === void 0) throw notVisible(cfg.tableId, cfg.variant, "the service document");
16041
+ key = hit.key;
16042
+ }
16043
+ budget();
16044
+ const types = await getMetadata(cfg.client, req);
16045
+ const entity = findByTableId(types, (t) => t.name, cfg.tableId, cfg.variant);
16046
+ if (entity === void 0) throw notVisible(cfg.tableId, cfg.variant, "$metadata");
16047
+ const columns = selectColumns(entity.columns, cfg.select, cfg.tableId, cfg.variant);
16048
+ const names = columns.map((c) => c.name);
16049
+ const pause = () => cfg.pauseTimeout?.();
16050
+ const sink = createSink(cfg.format, names, cfg.out, cfg.signal, pause, budget);
16051
+ await sink.start();
16052
+ let skip = 0;
16053
+ let remaining = cfg.limit;
16054
+ for (; ; ) {
16055
+ const top = remaining === null ? cfg.pageSize : Math.min(cfg.pageSize, remaining);
16056
+ budget();
16057
+ const entries = await getTablePage(
16058
+ cfg.client,
16059
+ key,
16060
+ { top, skip, select: cfg.select === void 0 ? void 0 : names, filter: cfg.filter },
16061
+ req
16062
+ );
16063
+ const page = entries.slice(0, top);
16064
+ await sink.page(page.map((e) => names.map((n) => e.get(n) ?? null)));
16065
+ skip += page.length;
16066
+ if (remaining !== null) remaining -= page.length;
16067
+ if (page.length < top || remaining === 0) break;
16068
+ }
16069
+ await sink.end();
16070
+ }
16071
+ function selectColumns(all, select, tableId, variant) {
16072
+ if (select === void 0) return [...all];
16073
+ const known = new Set(all.map((c) => c.name));
16074
+ for (const s of select) {
16075
+ if (!known.has(s)) {
16076
+ throw new Error(
16077
+ `--select: unknown column "${s}" for table ${tableId} (${variant}); names are the OData-sanitized ones from \`dr odata metadata ${tableId}\``
16078
+ );
16079
+ }
16080
+ }
16081
+ const wanted = new Set(select);
16082
+ return all.filter((c) => wanted.has(c.name));
16083
+ }
16084
+ async function emitTo(out, s, signal, pause, rearm) {
16085
+ if (!out.write(s)) {
16086
+ pause();
16087
+ await once(out, "drain", { signal });
16088
+ rearm();
16089
+ }
16090
+ }
16091
+ function createSink(format, names, out, signal, pause = () => {
16092
+ }, rearm = () => {
16093
+ }) {
16094
+ const emit = (s) => emitTo(out, s, signal, pause, rearm);
16095
+ if (format === "csv") {
16096
+ return {
16097
+ start: () => emit(`${formatCsvRow(names)}
16098
+ `),
16099
+ page: async (rows) => {
16100
+ for (const r of rows) await emit(`${formatCsvRow(r.map((c) => c ?? ""))}
16101
+ `);
16102
+ },
16103
+ end: async () => {
16104
+ }
16105
+ };
16106
+ }
16107
+ if (format === "json") {
16108
+ let count = 0;
16109
+ return {
16110
+ start: () => emit("["),
16111
+ page: async (rows) => {
16112
+ for (const r of rows) {
16113
+ const obj = Object.fromEntries(names.map((n, i) => [n, r[i] ?? null]));
16114
+ await emit(`${count === 0 ? "\n" : ",\n"}${JSON.stringify(obj)}`);
16115
+ count++;
16116
+ }
16117
+ },
16118
+ end: () => emit(count === 0 ? "]\n" : "\n]\n")
16119
+ };
16120
+ }
16121
+ let widths = null;
16122
+ return {
16123
+ start: async () => {
16124
+ },
16125
+ page: async (rows) => {
16126
+ const cells = rows.map((r) => r.map((c) => c ?? ""));
16127
+ if (widths === null) {
16128
+ if (cells.length === 0) return;
16129
+ widths = columnWidths([names, ...cells]);
16130
+ await emit(`${formatTable([names], widths)[0]}
16131
+ `);
16132
+ }
16133
+ for (const line of formatTable(cells, widths)) await emit(`${line}
16134
+ `);
16135
+ },
16136
+ end: async () => {
16137
+ if (widths === null) await emit("(no rows)\n");
16138
+ }
16139
+ };
16140
+ }
16141
+ function columnWidths(rows) {
16142
+ const widths = [];
16143
+ for (const r of rows) {
16144
+ r.forEach((cell, i) => {
16145
+ if ((widths[i] ?? 0) < cell.length) widths[i] = cell.length;
16146
+ });
16147
+ }
16148
+ return widths;
16149
+ }
16150
+ function formatTable(rows, widths) {
16151
+ return rows.map(
16152
+ (r) => r.map((cell, i) => i === r.length - 1 ? cell : cell.padEnd((widths[i] ?? 0) + 2)).join("")
16153
+ );
16154
+ }
16155
+ function parseVariant(raw) {
16156
+ if (raw === void 0) return "latest";
16157
+ const hit = ODATA_VARIANTS.find((v) => v === raw);
16158
+ if (hit === void 0) {
16159
+ throw new Error(
16160
+ `invalid --variant "${raw}" (expected one of: ${ODATA_VARIANTS.join(", ")})`
16161
+ );
16162
+ }
16163
+ return hit;
16164
+ }
16165
+ var KEY_PATTERN = /^[A-Za-z0-9+/_-]+={0,2}$/;
16166
+ function resolveDataOptions(tableIdStr, f) {
16167
+ const tableId = parsePositiveIntArg(tableIdStr, "<table-id>");
16168
+ const variant = parseVariant(f.variant);
16169
+ if (f.top !== void 0 && f.all === true) {
16170
+ throw new Error("--top and --all cannot be combined");
16171
+ }
16172
+ if (f.csv === true && f.json === true) {
16173
+ throw new Error("--csv and --json cannot be combined");
16174
+ }
16175
+ const pageSize = f.pageSize === void 0 ? ODATA_DEFAULT_PAGE_SIZE : parsePositiveIntArg(f.pageSize, "--page-size");
16176
+ let limit = pageSize;
16177
+ if (f.all === true) limit = null;
16178
+ else if (f.top !== void 0) limit = parsePositiveIntArg(f.top, "--top");
16179
+ if (f.key !== void 0 && !KEY_PATTERN.test(f.key)) {
16180
+ throw new Error(
16181
+ `invalid --key "${f.key}" (expected a base64 key, see \`dr odata tables --json\`)`
16182
+ );
16183
+ }
16184
+ let select;
16185
+ if (f.select !== void 0) {
16186
+ select = f.select.split(",").map((s) => s.trim()).filter((s) => s !== "");
16187
+ if (select.length === 0) throw new Error("--select must name at least one column");
16188
+ }
16189
+ if (f.filter !== void 0 && f.filter === "") {
16190
+ throw new Error("--filter must not be empty");
16191
+ }
16192
+ const requestTimeoutMs = parsePositiveSecondsFlag(
16193
+ f.timeout,
16194
+ "--timeout",
16195
+ ODATA_REQUEST_TIMEOUT_MS
16196
+ );
16197
+ if (requestTimeoutMs > BATCH_TIMEOUT_MAX_SECONDS * 1e3) {
16198
+ throw new Error(`--timeout must be at most ${BATCH_TIMEOUT_MAX_SECONDS} seconds`);
16199
+ }
16200
+ return {
16201
+ tableId,
16202
+ variant,
16203
+ key: f.key,
16204
+ limit,
16205
+ pageSize,
16206
+ select,
16207
+ filter: f.filter,
16208
+ format: f.csv === true ? "csv" : f.json === true ? "json" : "table",
16209
+ requestTimeoutMs
16210
+ };
16211
+ }
16212
+ var VARIANT_HELP = "latest (default), cleared, full (all versions) or full-cleared \u2014 the four collections OData clients see";
16213
+ function registerODataCommand(root) {
16214
+ const od = root.command("odata").description(
16215
+ "Read tables over OData V1 exactly as Excel / Power BI / SQLConnector receive them (read-only)"
16216
+ );
16217
+ const tablesCmd = od.command("tables").description("List the tables this user can read over OData, with their variants").option("--json", "Output JSON, including each variant key", false).action(async (opts) => {
16218
+ const setup = await setupCall(opts.server, opts.account);
16219
+ try {
16220
+ await runODataTables({
16221
+ client: setup.client,
16222
+ asJson: opts.json === true,
16223
+ signal: setup.signal,
16224
+ out: process.stdout
16225
+ });
16226
+ } finally {
16227
+ setup.cleanup();
16228
+ }
16229
+ });
16230
+ addServerFlag(tablesCmd);
16231
+ addAccountFlag(tablesCmd);
16232
+ const metadataCmd = od.command("metadata").description("Columns and EDM types of a table, in $metadata order").argument("<table-id>", "table (template) id").option("--variant <variant>", VARIANT_HELP).option("--json", "Output JSON", false).action(async (idStr, opts) => {
16233
+ const tableId = parsePositiveIntArg(idStr, "<table-id>");
16234
+ const variant = parseVariant(opts.variant);
16235
+ const setup = await setupCall(opts.server, opts.account);
16236
+ try {
16237
+ await runODataMetadata({
16238
+ client: setup.client,
16239
+ tableId,
16240
+ variant,
16241
+ asJson: opts.json === true,
16242
+ signal: setup.signal,
16243
+ out: process.stdout
16244
+ });
16245
+ } finally {
16246
+ setup.cleanup();
16247
+ }
16248
+ });
16249
+ addServerFlag(metadataCmd);
16250
+ addAccountFlag(metadataCmd);
16251
+ const dataCmd = od.command("data").description(
16252
+ "Read table rows over OData. Without --top/--all: one page of --page-size rows. --top N: at most N rows. --all: page until the table is exhausted. Rows stream as each page arrives."
16253
+ ).argument("<table-id>", "table (template) id").option("--variant <variant>", VARIANT_HELP).option(
16254
+ "--key <base64>",
16255
+ "Use this OData key verbatim instead of the one the service document lists"
16256
+ ).option("--top <n>", "Return at most N rows").option("--all", "Page until the table is exhausted", false).option("--page-size <n>", `Rows per request ($top) (default ${ODATA_DEFAULT_PAGE_SIZE})`).option("--select <columns>", "Comma-separated OData column names (see `dr odata metadata`)").option("--filter <expr>", "OData $filter expression, passed through to the server").option("--csv", "Output CSV", false).option("--json", "Output a JSON array, streamed", false).option(
16257
+ "--timeout <seconds>",
16258
+ `Budget for each request in seconds (default ${ODATA_REQUEST_TIMEOUT_MS / 1e3})`
16259
+ ).action(async (idStr, opts) => {
16260
+ const r = resolveDataOptions(idStr, opts);
16261
+ const setup = await setupCall(opts.server, opts.account, { timeoutMs: r.requestTimeoutMs });
16262
+ try {
16263
+ await runODataData({
16264
+ client: setup.client,
16265
+ tableId: r.tableId,
16266
+ variant: r.variant,
16267
+ key: r.key,
16268
+ limit: r.limit,
16269
+ pageSize: r.pageSize,
16270
+ select: r.select,
16271
+ filter: r.filter,
16272
+ format: r.format,
16273
+ signal: setup.signal,
16274
+ out: process.stdout,
16275
+ extendTimeout: setup.extendTimeout,
16276
+ pauseTimeout: setup.pauseTimeout,
16277
+ requestTimeoutMs: r.requestTimeoutMs
16278
+ });
16279
+ } finally {
16280
+ setup.cleanup();
16281
+ }
16282
+ });
16283
+ addServerFlag(dataCmd);
16284
+ addAccountFlag(dataCmd);
16285
+ }
16286
+
15778
16287
  // src/api/storyboards.ts
15779
16288
  async function list13(client, options) {
15780
16289
  const out = await client.getJSON("/api/storytelling/", options);
@@ -16373,7 +16882,7 @@ var LUT_SAVE_POLL_TIMEOUT_MS = 3e5;
16373
16882
  async function saveMappings(client, documentId, body, options = {}) {
16374
16883
  const interval = options.pollIntervalMs ?? LUT_SAVE_POLL_INTERVAL_MS;
16375
16884
  const timeout = options.timeoutMs ?? LUT_SAVE_POLL_TIMEOUT_MS;
16376
- const sleep3 = options.sleep ?? defaultPollSleep;
16885
+ const sleep4 = options.sleep ?? defaultPollSleep;
16377
16886
  const now = options.now ?? Date.now;
16378
16887
  const deadline = now() + timeout;
16379
16888
  const path11 = `/api/documents/${documentId}/web_update_lut`;
@@ -16395,7 +16904,7 @@ async function saveMappings(client, documentId, body, options = {}) {
16395
16904
  `lut save-mappings: the server is still writing after ${Math.round(timeout / 1e3)}s (task ${cacheKey}). The write is still running server-side \u2014 re-run the same command to rejoin it, or raise --timeout.`
16396
16905
  );
16397
16906
  }
16398
- await sleep3(interval, options.signal);
16907
+ await sleep4(interval, options.signal);
16399
16908
  const tick = await postOnce(client, pollPath, payload, options.signal);
16400
16909
  if (tick.status === 200) {
16401
16910
  return toResult(tick.text);
@@ -16404,22 +16913,22 @@ async function saveMappings(client, documentId, body, options = {}) {
16404
16913
  }
16405
16914
  async function postOnce(client, path11, payload, signal) {
16406
16915
  const res = await client.do("POST", path11, payload, { signal });
16407
- const text = await res.text();
16916
+ const text2 = await res.text();
16408
16917
  if (res.status === 200 || res.status === 409) {
16409
- return { status: res.status, text };
16918
+ return { status: res.status, text: text2 };
16410
16919
  }
16411
- throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncateBody(text)}`);
16920
+ throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncateBody(text2)}`);
16412
16921
  }
16413
- function extractCacheKey(text) {
16922
+ function extractCacheKey(text2) {
16414
16923
  try {
16415
- const parsed2 = JSON.parse(text);
16924
+ const parsed2 = JSON.parse(text2);
16416
16925
  return typeof parsed2.cache_key === "string" ? parsed2.cache_key : "";
16417
16926
  } catch {
16418
16927
  return "";
16419
16928
  }
16420
16929
  }
16421
- function toResult(text) {
16422
- let message = text.trim();
16930
+ function toResult(text2) {
16931
+ let message = text2.trim();
16423
16932
  try {
16424
16933
  const parsed2 = JSON.parse(message);
16425
16934
  if (typeof parsed2 === "string") message = parsed2;
@@ -16454,8 +16963,8 @@ async function getLutFile(client, documentId, options) {
16454
16963
  const path11 = `/api/documents/${documentId}/lut-file`;
16455
16964
  const res = await client.do("GET", path11, void 0, options);
16456
16965
  if (res.status < 200 || res.status >= 300) {
16457
- const text = await res.text();
16458
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
16966
+ const text2 = await res.text();
16967
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
16459
16968
  throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncated}`);
16460
16969
  }
16461
16970
  const ab = await res.arrayBuffer();
@@ -16493,8 +17002,8 @@ async function downloadDebugZip(client, docVersionId, type, reqOptions) {
16493
17002
  const path11 = `/api/documents/download_debug_zip_file/${docVersionId}?type=${type}`;
16494
17003
  const res = await client.do("GET", path11, void 0, reqOptions);
16495
17004
  if (res.status < 200 || res.status >= 300) {
16496
- const text = await res.text();
16497
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
17005
+ const text2 = await res.text();
17006
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
16498
17007
  throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncated}`);
16499
17008
  }
16500
17009
  const ab = await res.arrayBuffer();
@@ -16620,19 +17129,19 @@ async function getDocumentJobStatusOnce(client, basePath, requestId, reqOptions)
16620
17129
  await res.text();
16621
17130
  return { kind: "notFound" };
16622
17131
  }
16623
- const text = await res.text();
16624
- throw new Error(`document-job status: unexpected HTTP ${res.status}: ${text.trim()}`);
17132
+ const text2 = await res.text();
17133
+ throw new Error(`document-job status: unexpected HTTP ${res.status}: ${text2.trim()}`);
16625
17134
  }
16626
17135
  async function readAutoCalcError(res) {
16627
- const text = await res.text();
17136
+ const text2 = await res.text();
16628
17137
  try {
16629
- const parsed2 = JSON.parse(text);
17138
+ const parsed2 = JSON.parse(text2);
16630
17139
  if (typeof parsed2.error === "string" && parsed2.error !== "") {
16631
17140
  return parsed2.error;
16632
17141
  }
16633
17142
  } catch {
16634
17143
  }
16635
- return text.trim();
17144
+ return text2.trim();
16636
17145
  }
16637
17146
  var defaultSleep3 = (ms, signal) => new Promise((resolve3, reject) => {
16638
17147
  if (signal?.aborted) {
@@ -16693,7 +17202,7 @@ async function exportDocument(client, id, destinationOrgIds, override, reqOption
16693
17202
  async function pollDocumentJob(client, basePath, label, requestId, options = {}) {
16694
17203
  const interval = options.pollIntervalMs ?? AUTO_CALC_POLL_INTERVAL_MS;
16695
17204
  const timeout = options.timeoutMs ?? AUTO_CALC_POLL_TIMEOUT_MS;
16696
- const sleep3 = options.sleep ?? defaultSleep3;
17205
+ const sleep4 = options.sleep ?? defaultSleep3;
16697
17206
  const now = options.now ?? Date.now;
16698
17207
  const deadline = now() + timeout;
16699
17208
  for (; ; ) {
@@ -16721,7 +17230,7 @@ async function pollDocumentJob(client, basePath, label, requestId, options = {})
16721
17230
  `${label} polling timed out after ${timeout}ms (request ${requestId} still processing)`
16722
17231
  );
16723
17232
  }
16724
- await sleep3(interval, options.signal);
17233
+ await sleep4(interval, options.signal);
16725
17234
  }
16726
17235
  }
16727
17236
 
@@ -16873,13 +17382,13 @@ async function getAutoCalculateSettings(client, docId, reqOptions) {
16873
17382
  await res.text().catch(() => "");
16874
17383
  return null;
16875
17384
  }
16876
- const text = await res.text();
17385
+ const text2 = await res.text();
16877
17386
  if (res.status < 200 || res.status >= 300) {
16878
- throw new Error(`GET ${path11}: HTTP ${res.status}: ${text.slice(0, 200)}`);
17387
+ throw new Error(`GET ${path11}: HTTP ${res.status}: ${text2.slice(0, 200)}`);
16879
17388
  }
16880
- if (text === "" || text === '""') return null;
17389
+ if (text2 === "" || text2 === '""') return null;
16881
17390
  try {
16882
- return JSON.parse(text);
17391
+ return JSON.parse(text2);
16883
17392
  } catch (err) {
16884
17393
  const msg = err instanceof Error ? err.message : String(err);
16885
17394
  throw new Error(`GET ${path11}: decode: ${msg}`);
@@ -19946,7 +20455,7 @@ async function waitForReAggregation(cfg) {
19946
20455
  let interval = cfg.pollIntervalMs ?? REAGG_POLL_INTERVAL_MS;
19947
20456
  const maxInterval = cfg.maxPollIntervalMs ?? REAGG_POLL_MAX_INTERVAL_MS;
19948
20457
  const timeout = cfg.timeoutMs ?? REAGG_POLL_TIMEOUT_MS;
19949
- const sleep3 = cfg.sleep ?? defaultSleep4;
20458
+ const sleep4 = cfg.sleep ?? defaultSleep4;
19950
20459
  const now = cfg.now ?? Date.now;
19951
20460
  const accepted = [...cfg.accepted];
19952
20461
  const observedProcessing = /* @__PURE__ */ new Set();
@@ -19981,7 +20490,7 @@ async function waitForReAggregation(cfg) {
19981
20490
  break;
19982
20491
  }
19983
20492
  try {
19984
- await sleep3(interval, cfg.signal);
20493
+ await sleep4(interval, cfg.signal);
19985
20494
  } catch {
19986
20495
  aborted = true;
19987
20496
  break;
@@ -20970,15 +21479,15 @@ function parseSuppressionDimension(d, source, i) {
20970
21479
  return { name, mappings };
20971
21480
  }
20972
21481
  async function loadSuppression(path11) {
20973
- let text;
21482
+ let text2;
20974
21483
  try {
20975
- text = await readFile2(path11, "utf8");
21484
+ text2 = await readFile2(path11, "utf8");
20976
21485
  } catch (err) {
20977
21486
  throw new Error(`--suppression: cannot read ${path11} (${errText(err)})`);
20978
21487
  }
20979
21488
  let parsed2;
20980
21489
  try {
20981
- parsed2 = JSON.parse(text);
21490
+ parsed2 = JSON.parse(text2);
20982
21491
  } catch (err) {
20983
21492
  throw new Error(`--suppression: ${path11} is not valid JSON (${errText(err)})`);
20984
21493
  }
@@ -22588,7 +23097,7 @@ async function waitForSuggestionRun(r, cfg, runId) {
22588
23097
  let interval = cfg.pollIntervalMs ?? SUGGESTIONS_POLL_INTERVAL_MS;
22589
23098
  const maxInterval = cfg.maxPollIntervalMs ?? SUGGESTIONS_POLL_MAX_INTERVAL_MS;
22590
23099
  const timeout = cfg.timeoutMs ?? SUGGESTIONS_POLL_TIMEOUT_MS;
22591
- const sleep3 = cfg.sleep ?? defaultSleep5;
23100
+ const sleep4 = cfg.sleep ?? defaultSleep5;
22592
23101
  const now = cfg.now ?? Date.now;
22593
23102
  const errOut = cfg.errOut ?? process.stderr;
22594
23103
  const start = now();
@@ -22618,7 +23127,7 @@ async function waitForSuggestionRun(r, cfg, runId) {
22618
23127
  break;
22619
23128
  }
22620
23129
  try {
22621
- await sleep3(interval, cfg.signal);
23130
+ await sleep4(interval, cfg.signal);
22622
23131
  } catch {
22623
23132
  aborted = true;
22624
23133
  break;
@@ -24778,18 +25287,18 @@ async function getTemplateFields(client, templateId, options) {
24778
25287
  }
24779
25288
  async function putDataOnce(client, path11, body, options) {
24780
25289
  const res = await client.do("PUT", path11, body, options);
24781
- const text = await res.text();
25290
+ const text2 = await res.text();
24782
25291
  if (res.status === 401 || res.status === 403) {
24783
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate5(text, 200)}`);
25292
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate5(text2, 200)}`);
24784
25293
  }
24785
25294
  if (res.status !== 200 && res.status !== 409 && (res.status < 200 || res.status >= 300)) {
24786
- throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate5(text, 200)}`);
25295
+ throw new Error(`PUT ${path11}: HTTP ${res.status}: ${truncate5(text2, 200)}`);
24787
25296
  }
24788
- if (text === "" || text === '""') {
25297
+ if (text2 === "" || text2 === '""') {
24789
25298
  return { status: res.status, parsed: null };
24790
25299
  }
24791
25300
  try {
24792
- return { status: res.status, parsed: JSON.parse(text) };
25301
+ return { status: res.status, parsed: JSON.parse(text2) };
24793
25302
  } catch (err) {
24794
25303
  const msg = err instanceof Error ? err.message : String(err);
24795
25304
  throw new Error(`PUT ${path11}: decode: ${msg}`);
@@ -24813,7 +25322,7 @@ var defaultSleep6 = (ms, signal) => new Promise((resolve3, reject) => {
24813
25322
  });
24814
25323
  async function putDataWithPolling(client, dashboardId, id, payload, options = {}) {
24815
25324
  const interval = options.pollIntervalMs ?? WIDGET_DATA_POLL_INTERVAL_MS;
24816
- const sleep3 = options.sleep ?? defaultSleep6;
25325
+ const sleep4 = options.sleep ?? defaultSleep6;
24817
25326
  const path11 = `/api/dashboards/${dashboardId}/widgets/${id}/data`;
24818
25327
  const body = JSON.stringify(payload);
24819
25328
  const first = await putDataOnce(client, path11, body, options);
@@ -24830,7 +25339,7 @@ async function putDataWithPolling(client, dashboardId, id, payload, options = {}
24830
25339
  const pollPath = `${path11}?cache_key=${encodeURIComponent(cacheKey)}`;
24831
25340
  for (; ; ) {
24832
25341
  try {
24833
- await sleep3(interval, options.signal);
25342
+ await sleep4(interval, options.signal);
24834
25343
  } catch (err) {
24835
25344
  const reason = err instanceof Error ? err.message : String(err);
24836
25345
  throw new Error(`widget data polling timed out: ${reason}`);
@@ -26696,8 +27205,8 @@ async function exportCsv(client, options) {
26696
27205
  const path11 = `${BASE}export-csv/`;
26697
27206
  const res = await client.do("GET", path11, void 0, options);
26698
27207
  if (res.status < 200 || res.status >= 300) {
26699
- const text = await res.text();
26700
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
27208
+ const text2 = await res.text();
27209
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
26701
27210
  throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncated}`);
26702
27211
  }
26703
27212
  return res.text();
@@ -28485,13 +28994,13 @@ ${MARKER_END}`;
28485
28994
  }
28486
28995
  function renderInlineBlock(skills, managedBy, installRoot) {
28487
28996
  const sections = skills.map((s) => {
28488
- const text = Buffer.from(routerBytes(s)).toString("utf8");
28489
- const trailingNl = text.endsWith("\n") ? "" : "\n";
28997
+ const text2 = Buffer.from(routerBytes(s)).toString("utf8");
28998
+ const trailingNl = text2.endsWith("\n") ? "" : "\n";
28490
28999
  const refs = s.files.filter((f) => f.relPath !== ROUTER_FILE).map((f) => f.relPath);
28491
29000
  const pointer = refs.length === 0 ? "" : `<!-- dr-cli skill ${s.name}: ${refs.length} reference file(s) installed under ${installRoot}/${s.name}/ (${refs.join(", ")}) \u2014 read on demand -->
28492
29001
  `;
28493
29002
  return `<!-- dr-cli skill: ${s.name} -->
28494
- ${text}${trailingNl}${pointer}`;
29003
+ ${text2}${trailingNl}${pointer}`;
28495
29004
  });
28496
29005
  return `${MARKER_BEGIN}
28497
29006
  <!-- dr-cli \u2014 Datarails CLI skills (auto-managed by ${managedBy}) -->
@@ -28591,6 +29100,9 @@ function registerSkillCommand(root) {
28591
29100
  });
28592
29101
  }
28593
29102
 
29103
+ // src/commands/datamappers.ts
29104
+ import { setTimeout as sleep3 } from "timers/promises";
29105
+
28594
29106
  // src/api/datamappers.ts
28595
29107
  async function list20(client, tableId, options) {
28596
29108
  const out = await client.getJSON(`/api/templates/${tableId}/mappers`, options);
@@ -28608,6 +29120,14 @@ async function get14(client, tableId, id, options) {
28608
29120
  }
28609
29121
  return out;
28610
29122
  }
29123
+ async function getDetails2(client, id, options) {
29124
+ const path11 = `/api/templates/mappers/${id}`;
29125
+ const out = await client.getJSON(path11, options);
29126
+ if (out === null) {
29127
+ throw new Error(`GET ${path11}: empty response from datamapper details`);
29128
+ }
29129
+ return out;
29130
+ }
28611
29131
  async function create12(client, tableId, body, options) {
28612
29132
  const path11 = `/api/templates/${tableId}/mappers`;
28613
29133
  const out = await client.postJSON(path11, body, options);
@@ -29356,13 +29876,15 @@ async function runDatamappersUpdate(cfg) {
29356
29876
  body = decodedBody;
29357
29877
  }
29358
29878
  validateCalcFields(extractCalcFields(body));
29879
+ const err = cfg.err ?? process.stderr;
29880
+ const scanBaseline = rescanValue === "latest" && cfg.verifyScanDispatch !== void 0 ? await captureScanBaseline(cfg.client, cfg.id, cfg.signal, err) : void 0;
29359
29881
  let updated;
29360
29882
  try {
29361
29883
  updated = await update8(cfg.client, cfg.id, body, {
29362
29884
  signal: cfg.signal
29363
29885
  });
29364
- } catch (err) {
29365
- const msg = err instanceof Error ? err.message : String(err);
29886
+ } catch (err2) {
29887
+ const msg = err2 instanceof Error ? err2.message : String(err2);
29366
29888
  if (/HTTP 500/.test(msg)) {
29367
29889
  const hint = "hint: the server logs an `ETLError: Custom Columns: The creation of '<field>' field has failed due to incorrect formula syntax.` line for this case but the response body is a bare 500 HTML page. Common causes the client-side guard cannot detect: (1) a [field] reference in a calc formula that does not resolve to any header `map_to`, calc-field `name`, or system field (`[FileBox_ID]`, `[System_Year]`, `[Posting Date]`, \u2026), (2) an unsupported function name (try copying the function spelling from another working calc field via `dr datamappers get`), (3) a string literal that's a plain-English placeholder (e.g. `\"Customer or Client name\"`) which parses but the ETL refuses. Iterate on calc-field formulas one at a time. If every formula looks correct and PUT still 500s, fall back to the workaround: `dr datamappers create from-file <table> --file <new.json>` then `dr datamappers delete <old_id> --table <table>` (note: this orphans references \u2014 LUTs/dashboards/functions pointing at the old mapper id break).";
29368
29890
  const responseBody = extractResponseBodyFromError(msg);
@@ -29388,13 +29910,106 @@ Full request + response captured to: ${logPath}
29388
29910
 
29389
29911
  ${hint}${logSuffix}`);
29390
29912
  }
29391
- throw err;
29913
+ throw err2;
29392
29914
  }
29393
29915
  if (cfg.asJson) {
29394
29916
  writeJSONIndented(cfg.out, updated);
29395
- return;
29917
+ } else {
29918
+ renderMappersTable(cfg.out, [updated]);
29396
29919
  }
29397
- renderMappersTable(cfg.out, [updated]);
29920
+ if (scanBaseline !== void 0) {
29921
+ const opts = cfg.verifyScanDispatch ?? {};
29922
+ const started2 = await waitForNewScan(cfg.client, scanBaseline, cfg.signal, opts);
29923
+ if (!started2) {
29924
+ const docIds = await mappedDocumentIds(
29925
+ cfg.client,
29926
+ scanBaseline.templateId,
29927
+ cfg.id,
29928
+ cfg.signal
29929
+ );
29930
+ err.write(
29931
+ formatNoScanWarning({
29932
+ mapperId: cfg.id,
29933
+ templateId: scanBaseline.templateId,
29934
+ timeoutMs: opts.timeoutMs ?? DEFAULT_SCAN_DISPATCH_TIMEOUT_MS,
29935
+ docIds
29936
+ })
29937
+ );
29938
+ }
29939
+ }
29940
+ }
29941
+ var DEFAULT_SCAN_DISPATCH_TIMEOUT_MS = 3e4;
29942
+ var DEFAULT_SCAN_DISPATCH_POLL_MS = 2e3;
29943
+ function scanFingerprint(report) {
29944
+ let maxScanId = 0;
29945
+ for (const doc of report.documents ?? []) {
29946
+ for (const v of doc.versions ?? []) {
29947
+ if (typeof v.scan_id === "number" && v.scan_id > maxScanId) maxScanId = v.scan_id;
29948
+ }
29949
+ }
29950
+ return `${report.scans_status?.last_updated ?? ""}|${maxScanId}`;
29951
+ }
29952
+ async function captureScanBaseline(client, mapperId, signal, err) {
29953
+ try {
29954
+ const details = await getDetails2(client, mapperId, { signal });
29955
+ const templateId = details.template.id;
29956
+ const report = await getStatus(client, templateId, { signal });
29957
+ return { templateId, fingerprint: scanFingerprint(report) };
29958
+ } catch (e) {
29959
+ if (signal.aborted) throw e;
29960
+ err.write(
29961
+ "note: could not read the scan status before the update, so dr cannot confirm a rescan starts. Check with `dr scans status <template_id>` afterwards.\n"
29962
+ );
29963
+ return void 0;
29964
+ }
29965
+ }
29966
+ async function waitForNewScan(client, baseline, signal, opts) {
29967
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_SCAN_DISPATCH_TIMEOUT_MS;
29968
+ const pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_SCAN_DISPATCH_POLL_MS;
29969
+ const deadline = Date.now() + timeoutMs;
29970
+ for (; ; ) {
29971
+ try {
29972
+ const report = await getStatus(client, baseline.templateId, { signal });
29973
+ if (scanFingerprint(report) !== baseline.fingerprint) return true;
29974
+ } catch (e) {
29975
+ if (signal.aborted) throw e;
29976
+ }
29977
+ const remaining = deadline - Date.now();
29978
+ if (remaining <= 0) return false;
29979
+ await sleep3(Math.min(pollIntervalMs, remaining), void 0, { signal });
29980
+ }
29981
+ }
29982
+ async function mappedDocumentIds(client, templateId, mapperId, signal) {
29983
+ try {
29984
+ const mapper = await get14(client, templateId, mapperId, { signal });
29985
+ return Array.isArray(mapper.mapped_documents_ids) ? mapper.mapped_documents_ids : void 0;
29986
+ } catch (e) {
29987
+ if (signal.aborted) throw e;
29988
+ return void 0;
29989
+ }
29990
+ }
29991
+ function formatNoScanWarning(p) {
29992
+ const secs = Math.round(p.timeoutMs / 1e3);
29993
+ const lines = [
29994
+ `warning: mapper ${p.mapperId} was saved, but no new scan showed up on template ${p.templateId} within ${secs}s.`,
29995
+ ' Either the scan is still queued, or this server predates the DR-54200 fix and ignored rescan="latest", so existing data still has the old config.',
29996
+ ` Check first: dr scans status ${p.templateId}`
29997
+ ];
29998
+ if (p.docIds !== void 0 && p.docIds.length === 0) {
29999
+ lines.push(" The mapper has no bound documents, so there is nothing to scan.");
30000
+ } else {
30001
+ const docFlags = p.docIds !== void 0 ? p.docIds.map((d) => `--doc ${d}`).join(" ") : "--doc <doc_id> [--doc <doc_id> ...]";
30002
+ lines.push(
30003
+ " If no new scan appears there, re-bind the mapper to its current documents with a rescan:"
30004
+ );
30005
+ if (p.docIds === void 0) {
30006
+ lines.push(
30007
+ ` dr datamappers get ${p.mapperId} --table ${p.templateId} --json # read mapped_documents_ids`
30008
+ );
30009
+ }
30010
+ lines.push(` dr datamappers bind ${p.mapperId} ${docFlags} --rescan latest`);
30011
+ }
30012
+ return lines.join("\n") + "\n";
29398
30013
  }
29399
30014
  async function buildConveniencePayload(client, tableId, name, versionId, sheet, flags, signal) {
29400
30015
  const fieldTypes = parseFieldOverrides(flags.fieldOverrides);
@@ -29709,7 +30324,7 @@ function renderMappersTable(out, mappers) {
29709
30324
  }
29710
30325
  ];
29711
30326
  for (const m of mappers) {
29712
- const ordinal = m.ordinal === null ? "-" : String(m.ordinal);
30327
+ const ordinal = m.ordinal == null ? "-" : String(m.ordinal);
29713
30328
  rows.push({
29714
30329
  cells: [
29715
30330
  String(m.id),
@@ -30007,7 +30622,7 @@ function registerDatamappersCommand(root) {
30007
30622
  addServerFlag(fromFileCmd);
30008
30623
  addAccountFlag(fromFileCmd);
30009
30624
  const updateCmd = dm.command("update").description(
30010
- 'Update a datamapper (payload-file only \u2014 server accepts config+sheet_name+rescan). `--rescan latest` (the default) mirrors the UI "Publish and scan": it re-extracts only the LATEST document version onto the live template. For a full historical rescan (every calculated field re-executed against every document version ever uploaded), use `dr rescan table <template_id>` instead \u2014 that is far more expensive and higher-blast.'
30625
+ "Update a datamapper in place (payload-file only \u2014 server accepts config+sheet_name+rescan). `--rescan latest` (the default) re-extracts the LATEST version of each document bound to the mapper. Servers without the DR-54200 fix save the config and start no scan, so after the update dr watches the template for a new scan and, when none shows up, prints how to check `dr scans status` and the `dr datamappers bind` fallback. For a full historical rescan (every calculated field re-executed against every document version ever uploaded), use `dr rescan table <template_id>` instead \u2014 that is far more expensive and higher-blast."
30011
30626
  ).argument("<mapper_id>", "mapper id (positive integer)").option("--payload-file <path>", "Path to a JSON file with the PUT body (required)", "").option(
30012
30627
  "--field <NAME:TYPE>",
30013
30628
  'Set a column type via fields_to_change (Text|Float|Date|Integer). Repeatable. Mirrors the UI "Publish and scan" so a calc column referencing a LUT-injected column materialises instead of 500ing on inferred types. Note: the server blocks a type change (HTTP 400) when the template has more than one mapper.',
@@ -30030,7 +30645,8 @@ function registerDatamappersCommand(root) {
30030
30645
  serverFlag: opts.server ?? "",
30031
30646
  asJson: opts.json === true,
30032
30647
  signal: setup.signal,
30033
- out: process.stdout
30648
+ out: process.stdout,
30649
+ verifyScanDispatch: {}
30034
30650
  });
30035
30651
  } finally {
30036
30652
  setup.cleanup();
@@ -30554,14 +31170,14 @@ async function runSplitCreate(cfg) {
30554
31170
  writeJSONIndented(cfg.out, result);
30555
31171
  return;
30556
31172
  }
30557
- const children = Array.isArray(result.split_documents) ? result.split_documents : [];
31173
+ const children2 = Array.isArray(result.split_documents) ? result.split_documents : [];
30558
31174
  const parentName = result.document !== null && typeof result.document?.name === "string" ? result.document.name : "-";
30559
31175
  cfg.out.write(
30560
- `Split created (id=${result.id}): ${children.length} child file(s) under parent ${JSON.stringify(parentName)}.
31176
+ `Split created (id=${result.id}): ${children2.length} child file(s) under parent ${JSON.stringify(parentName)}.
30561
31177
  `
30562
31178
  );
30563
- for (const child of children) {
30564
- const name = typeof child.name === "string" ? child.name : "(unnamed)";
31179
+ for (const child2 of children2) {
31180
+ const name = typeof child2.name === "string" ? child2.name : "(unnamed)";
30565
31181
  cfg.out.write(` - ${name}
30566
31182
  `);
30567
31183
  }
@@ -30642,11 +31258,11 @@ function renderSplitsTable(out, splits) {
30642
31258
  const rows = [["ID", "PARENT", "CHILDREN", "INPUT_METHOD", "DATA_MODE"]];
30643
31259
  for (const s of splits) {
30644
31260
  const parent = s.document !== null && typeof s.document?.name === "string" ? s.document.name : "-";
30645
- const children = Array.isArray(s.split_documents) ? String(s.split_documents.length) : "0";
31261
+ const children2 = Array.isArray(s.split_documents) ? String(s.split_documents.length) : "0";
30646
31262
  rows.push([
30647
31263
  String(s.id),
30648
31264
  parent,
30649
- children,
31265
+ children2,
30650
31266
  s.input_method ?? "-",
30651
31267
  s.export_as_values === true ? "fixed" : "live"
30652
31268
  ]);
@@ -31719,21 +32335,21 @@ async function queryCheckStatus(client, queryIds, reqOptions) {
31719
32335
  }
31720
32336
  async function getErrorExplanation(client, path11, reqOptions) {
31721
32337
  const res = await client.do("GET", path11, void 0, reqOptions);
31722
- const text = await res.text();
32338
+ const text2 = await res.text();
31723
32339
  if (res.status === 200) {
31724
32340
  let parsed2;
31725
32341
  try {
31726
- parsed2 = JSON.parse(text);
32342
+ parsed2 = JSON.parse(text2);
31727
32343
  } catch (err) {
31728
32344
  const msg = err instanceof Error ? err.message : String(err);
31729
32345
  throw new Error(`decode error-explanation: ${msg}`);
31730
32346
  }
31731
- return { status: 200, body: parsed2, rawText: text };
32347
+ return { status: 200, body: parsed2, rawText: text2 };
31732
32348
  }
31733
32349
  if (res.status === 404) {
31734
- return { status: 404, body: null, rawText: text };
32350
+ return { status: 404, body: null, rawText: text2 };
31735
32351
  }
31736
- throw new Error(`error explanation: unexpected status ${res.status}: ${text.trim()}`);
32352
+ throw new Error(`error explanation: unexpected status ${res.status}: ${text2.trim()}`);
31737
32353
  }
31738
32354
  async function resync(client, id, reqOptions) {
31739
32355
  const out = await client.postJSON(
@@ -31751,8 +32367,8 @@ async function testConnection(client, rawBody, reqOptions) {
31751
32367
  rawBody,
31752
32368
  reqOptions
31753
32369
  );
31754
- const text = await res.text();
31755
- return { status: res.status, rawText: text };
32370
+ const text2 = await res.text();
32371
+ return { status: res.status, rawText: text2 };
31756
32372
  }
31757
32373
  async function create15(client, body, reqOptions) {
31758
32374
  const path11 = "/api/v3/data_integrations/";
@@ -31772,8 +32388,8 @@ async function update9(client, id, body, reqOptions) {
31772
32388
  }
31773
32389
  async function deleteById13(client, id, reqOptions) {
31774
32390
  const res = await client.do("DELETE", `/api/v3/data_integrations/${id}/`, void 0, reqOptions);
31775
- const text = await res.text();
31776
- return { status: res.status, rawText: text };
32391
+ const text2 = await res.text();
32392
+ return { status: res.status, rawText: text2 };
31777
32393
  }
31778
32394
  async function revoke(client, id, reqOptions) {
31779
32395
  const res = await client.do(
@@ -31852,14 +32468,14 @@ async function itUpdate(client, id, body, reqOptions) {
31852
32468
  }
31853
32469
  async function itDelete(client, id, reqOptions) {
31854
32470
  const res = await client.do("DELETE", `/api/it/integrations/${id}`, void 0, reqOptions);
31855
- const text = await res.text();
31856
- return { status: res.status, rawText: text };
32471
+ const text2 = await res.text();
32472
+ return { status: res.status, rawText: text2 };
31857
32473
  }
31858
32474
  async function itResync(client, id, reqOptions) {
31859
32475
  const res = await client.do("GET", `/api/it/integrations/${id}/resync`, void 0, reqOptions);
31860
- const text = await res.text();
32476
+ const text2 = await res.text();
31861
32477
  if (res.status < 200 || res.status >= 300) {
31862
- throw new Error(`resync: HTTP ${res.status}: ${text.trim()}`);
32478
+ throw new Error(`resync: HTTP ${res.status}: ${text2.trim()}`);
31863
32479
  }
31864
32480
  }
31865
32481
 
@@ -32196,7 +32812,7 @@ async function runIntegrationsSync(cfg) {
32196
32812
  throw new Error("integration id must be positive");
32197
32813
  }
32198
32814
  const interval = cfg.pollIntervalMs ?? INTEGRATIONS_SYNC_POLL_INTERVAL_MS;
32199
- const sleep3 = cfg.sleep ?? defaultSleep7;
32815
+ const sleep4 = cfg.sleep ?? defaultSleep7;
32200
32816
  const resp = await resync(cfg.client, cfg.id, {
32201
32817
  signal: cfg.signal
32202
32818
  });
@@ -32245,7 +32861,7 @@ async function runIntegrationsSync(cfg) {
32245
32861
  return;
32246
32862
  }
32247
32863
  try {
32248
- await sleep3(interval, inner.signal);
32864
+ await sleep4(interval, inner.signal);
32249
32865
  } catch {
32250
32866
  cfg.out.write(
32251
32867
  `Timed out waiting for sync to finish \u2014 last status was ${orDash(lastStatus)}
@@ -37019,7 +37635,7 @@ async function runFileboxSetVersionFileStatus(cfg) {
37019
37635
  throw new Error("version id must be positive");
37020
37636
  }
37021
37637
  const err = cfg.err ?? process.stderr;
37022
- const sleep3 = cfg.sleep ?? defaultCycleTagSleep;
37638
+ const sleep4 = cfg.sleep ?? defaultCycleTagSleep;
37023
37639
  const settleMs = cfg.tagSettleDelayMs ?? CYCLE_TAG_SETTLE_DELAY_MS;
37024
37640
  const details = await getDetails(cfg.client, cfg.docId, { signal: cfg.signal });
37025
37641
  const tag = (details.tags_config ?? []).find(isFileStatusTag);
@@ -37112,7 +37728,7 @@ async function runFileboxSetVersionFileStatus(cfg) {
37112
37728
  }
37113
37729
  };
37114
37730
  const readBack = async () => {
37115
- await sleep3(settleMs, cfg.signal);
37731
+ await sleep4(settleMs, cfg.signal);
37116
37732
  return readVersionForConfirm(cfg.client, cfg.versionId, cfg.docId, cfg.signal);
37117
37733
  };
37118
37734
  await write();
@@ -40846,8 +41462,8 @@ async function getEvidenceFile(client, params, options) {
40846
41462
  const path11 = params.accountId !== void 0 && params.accountId !== "" ? `/api/reconciliation/processes/${params.processId}/account/${params.accountId}/evidences/${params.evidenceId}/file` : `/api/reconciliation/processes/${params.processId}/evidences/${params.evidenceId}/file`;
40847
41463
  const res = await client.do("GET", path11, void 0, options);
40848
41464
  if (res.status < 200 || res.status >= 300) {
40849
- const text = await res.text();
40850
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
41465
+ const text2 = await res.text();
41466
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
40851
41467
  throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncated}`);
40852
41468
  }
40853
41469
  const ab = await res.arrayBuffer();
@@ -40905,19 +41521,19 @@ async function deleteFileboxConnection(client, connectionId, options) {
40905
41521
  async function getFileboxSyncProgress(client, params, options) {
40906
41522
  const path11 = `/api/reconciliation/filebox/connections/${params.connectionId}/sync/progress`;
40907
41523
  const res = await client.do("GET", path11, void 0, options);
40908
- const text = await res.text();
41524
+ const text2 = await res.text();
40909
41525
  if (res.status === 200 || res.status === 409) {
40910
- if (text === "" || text === '""') {
41526
+ if (text2 === "" || text2 === '""') {
40911
41527
  return { status: "", remaining_files: 0, message: "" };
40912
41528
  }
40913
41529
  try {
40914
- return JSON.parse(text);
41530
+ return JSON.parse(text2);
40915
41531
  } catch (err) {
40916
41532
  const msg = err instanceof Error ? err.message : String(err);
40917
41533
  throw new Error(`GET ${path11}: decode: ${msg}`);
40918
41534
  }
40919
41535
  }
40920
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
41536
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
40921
41537
  throw new Error(`GET ${path11}: HTTP ${res.status}: ${truncated}`);
40922
41538
  }
40923
41539
  async function getFileboxUnlinkedCount(client, params, options) {
@@ -41074,8 +41690,8 @@ async function readAssigneeCsv(path11) {
41074
41690
  }
41075
41691
  return parseAssigneeCsvText(raw);
41076
41692
  }
41077
- function parseAssigneeCsvText(text) {
41078
- const withoutBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
41693
+ function parseAssigneeCsvText(text2) {
41694
+ const withoutBom = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
41079
41695
  const rows = parseCsvRows(withoutBom);
41080
41696
  if (rows.length === 0) {
41081
41697
  throw new Error("--file: CSV is empty");
@@ -44451,8 +45067,8 @@ async function deleteWithBody(client, path11, body, options) {
44451
45067
  const encoded = JSON.stringify(body);
44452
45068
  const res = await client.do("DELETE", path11, encoded, options);
44453
45069
  if (res.status < 200 || res.status >= 300) {
44454
- const text = await res.text();
44455
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
45070
+ const text2 = await res.text();
45071
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
44456
45072
  throw new Error(`DELETE ${path11}: HTTP ${res.status}: ${truncated}`);
44457
45073
  }
44458
45074
  await res.text();
@@ -44900,16 +45516,16 @@ async function runProbe(client, options) {
44900
45516
  return;
44901
45517
  }
44902
45518
  if (res.status === 403 || res.status === 404) {
44903
- const text2 = await res.text();
44904
- const lower = text2.toLowerCase();
45519
+ const text3 = await res.text();
45520
+ const lower = text3.toLowerCase();
44905
45521
  if (V2_NOT_ENABLED_MARKERS.some((marker) => lower.includes(marker))) {
44906
45522
  throw new WorkflowV2NotEnabledError();
44907
45523
  }
44908
- const truncated2 = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
45524
+ const truncated2 = text3.length > 200 ? `${text3.slice(0, 200)}\u2026` : text3;
44909
45525
  throw new Error(`GET ${WORKFLOW_V2_PROBE_PATH}: HTTP ${res.status}: ${truncated2}`);
44910
45526
  }
44911
- const text = await res.text();
44912
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
45527
+ const text2 = await res.text();
45528
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
44913
45529
  throw new Error(`GET ${WORKFLOW_V2_PROBE_PATH}: HTTP ${res.status}: ${truncated}`);
44914
45530
  }
44915
45531
  var MEC_NOT_ENABLED_MARKERS = [
@@ -44982,16 +45598,16 @@ async function runMecProbe(client, options) {
44982
45598
  return;
44983
45599
  }
44984
45600
  if (res.status === 403 || res.status === 404) {
44985
- const text2 = await res.text();
44986
- const lower = text2.toLowerCase();
45601
+ const text3 = await res.text();
45602
+ const lower = text3.toLowerCase();
44987
45603
  if (MEC_NOT_ENABLED_MARKERS.some((marker) => lower.includes(marker))) {
44988
45604
  throw new MecNotEnabledError();
44989
45605
  }
44990
- const truncated2 = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
45606
+ const truncated2 = text3.length > 200 ? `${text3.slice(0, 200)}\u2026` : text3;
44991
45607
  throw new Error(`GET ${MEC_PROBE_PATH}: HTTP ${res.status}: ${truncated2}`);
44992
45608
  }
44993
- const text = await res.text();
44994
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
45609
+ const text2 = await res.text();
45610
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
44995
45611
  throw new Error(`GET ${MEC_PROBE_PATH}: HTTP ${res.status}: ${truncated}`);
44996
45612
  }
44997
45613
  var SHARE_DATA_FLAG = "--yes-share-data";
@@ -51539,8 +52155,8 @@ async function putTransactions(client, params, payload, options) {
51539
52155
  async function rescanTemplateRules(client, params, options) {
51540
52156
  const path11 = `/api/templates/${params.templateId}/rules/rescan`;
51541
52157
  const res = await client.do("POST", path11, void 0, options);
51542
- const text = await res.text();
51543
- let body = text;
52158
+ const text2 = await res.text();
52159
+ let body = text2;
51544
52160
  if (body.startsWith('"') && body.endsWith('"') && body.length >= 2) {
51545
52161
  try {
51546
52162
  body = JSON.parse(body);
@@ -51616,8 +52232,8 @@ async function exportRulesCsv(client, params, options) {
51616
52232
  const path11 = `/api/templates/${params.templateId}/rules/export_to_csv${qs}`;
51617
52233
  const res = await client.do("POST", path11, void 0, options);
51618
52234
  if (res.status < 200 || res.status >= 300) {
51619
- const text = await res.text();
51620
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
52235
+ const text2 = await res.text();
52236
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
51621
52237
  throw new Error(`POST ${path11}: HTTP ${res.status}: ${truncated}`);
51622
52238
  }
51623
52239
  const csv = await res.text();
@@ -51643,8 +52259,8 @@ async function deleteHiddenRules(client, params, customerTransactionIds, options
51643
52259
  const body = JSON.stringify(customerTransactionIds);
51644
52260
  const res = await client.do("DELETE", path11, body, options);
51645
52261
  if (res.status < 200 || res.status >= 300) {
51646
- const text = await res.text();
51647
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
52262
+ const text2 = await res.text();
52263
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
51648
52264
  throw new Error(`DELETE ${path11}: HTTP ${res.status}: ${truncated}`);
51649
52265
  }
51650
52266
  await res.text();
@@ -51652,8 +52268,8 @@ async function deleteHiddenRules(client, params, customerTransactionIds, options
51652
52268
  async function triggerAiRules(client, options) {
51653
52269
  const path11 = `/api/templates/ai-rules`;
51654
52270
  const res = await client.do("POST", path11, void 0, options);
51655
- const text = await res.text();
51656
- let body = text;
52271
+ const text2 = await res.text();
52272
+ let body = text2;
51657
52273
  if (body.startsWith('"') && body.endsWith('"') && body.length >= 2) {
51658
52274
  try {
51659
52275
  body = JSON.parse(body);
@@ -51963,8 +52579,8 @@ async function readCsvImportPayload(path11) {
51963
52579
  }
51964
52580
  return parseCsvImportText(raw);
51965
52581
  }
51966
- function parseCsvImportText(text) {
51967
- const rows = parseCsvRows(text);
52582
+ function parseCsvImportText(text2) {
52583
+ const rows = parseCsvRows(text2);
51968
52584
  if (rows.length === 0) {
51969
52585
  throw new Error("--file: CSV is empty");
51970
52586
  }
@@ -52069,24 +52685,24 @@ function parseClause(clause, lineNo, clauseIdx) {
52069
52685
  args: values[0]
52070
52686
  };
52071
52687
  }
52072
- function parseQuotedValueList(text, lineNo, clauseIdx) {
52688
+ function parseQuotedValueList(text2, lineNo, clauseIdx) {
52073
52689
  const out = [];
52074
52690
  let i = 0;
52075
- while (i < text.length) {
52076
- while (i < text.length && (text[i] === " " || text[i] === ",")) i++;
52077
- if (i >= text.length) break;
52078
- if (text[i] !== "'") {
52691
+ while (i < text2.length) {
52692
+ while (i < text2.length && (text2[i] === " " || text2[i] === ",")) i++;
52693
+ if (i >= text2.length) break;
52694
+ if (text2[i] !== "'") {
52079
52695
  throw new Error(
52080
- `--file: line ${lineNo} clause ${clauseIdx + 1}: expected single-quoted value at offset ${i} in "${truncate7(text, 80)}"`
52696
+ `--file: line ${lineNo} clause ${clauseIdx + 1}: expected single-quoted value at offset ${i} in "${truncate7(text2, 80)}"`
52081
52697
  );
52082
52698
  }
52083
52699
  i++;
52084
52700
  let v = "";
52085
52701
  let closed = false;
52086
- while (i < text.length) {
52087
- const ch = text[i];
52702
+ while (i < text2.length) {
52703
+ const ch = text2[i];
52088
52704
  if (ch === "'") {
52089
- if (i + 1 < text.length && text[i + 1] === "'") {
52705
+ if (i + 1 < text2.length && text2[i + 1] === "'") {
52090
52706
  v += "'";
52091
52707
  i += 2;
52092
52708
  continue;
@@ -52100,27 +52716,27 @@ function parseQuotedValueList(text, lineNo, clauseIdx) {
52100
52716
  }
52101
52717
  if (!closed) {
52102
52718
  throw new Error(
52103
- `--file: line ${lineNo} clause ${clauseIdx + 1}: unterminated single-quoted value in "${truncate7(text, 80)}"`
52719
+ `--file: line ${lineNo} clause ${clauseIdx + 1}: unterminated single-quoted value in "${truncate7(text2, 80)}"`
52104
52720
  );
52105
52721
  }
52106
52722
  out.push(v);
52107
52723
  }
52108
52724
  if (out.length === 0) {
52109
52725
  throw new Error(
52110
- `--file: line ${lineNo} clause ${clauseIdx + 1}: no quoted values found in "${truncate7(text, 80)}"`
52726
+ `--file: line ${lineNo} clause ${clauseIdx + 1}: no quoted values found in "${truncate7(text2, 80)}"`
52111
52727
  );
52112
52728
  }
52113
52729
  return out;
52114
52730
  }
52115
- function splitOutsideQuotes(text, sep) {
52731
+ function splitOutsideQuotes(text2, sep) {
52116
52732
  const out = [];
52117
52733
  let buf = "";
52118
52734
  let i = 0;
52119
52735
  let inQuote = false;
52120
- while (i < text.length) {
52121
- const ch = text[i];
52736
+ while (i < text2.length) {
52737
+ const ch = text2[i];
52122
52738
  if (ch === "'") {
52123
- if (inQuote && i + 1 < text.length && text[i + 1] === "'") {
52739
+ if (inQuote && i + 1 < text2.length && text2[i + 1] === "'") {
52124
52740
  buf += "''";
52125
52741
  i += 2;
52126
52742
  continue;
@@ -52130,7 +52746,7 @@ function splitOutsideQuotes(text, sep) {
52130
52746
  i++;
52131
52747
  continue;
52132
52748
  }
52133
- if (!inQuote && text.startsWith(sep, i)) {
52749
+ if (!inQuote && text2.startsWith(sep, i)) {
52134
52750
  out.push(buf);
52135
52751
  buf = "";
52136
52752
  i += sep.length;
@@ -53633,35 +54249,35 @@ var REGISTRY = "https://datarails.jfrog.io/artifactory/api/npm/dr-cli-virtual";
53633
54249
  var PACKAGE = "@datarails-ai/dr-cli";
53634
54250
  function spawnInherit(command, args) {
53635
54251
  return new Promise((resolve3) => {
53636
- const child = spawn2(command, args, {
54252
+ const child2 = spawn2(command, args, {
53637
54253
  stdio: "inherit",
53638
54254
  // Windows resolves `npm` as `npm.cmd`; `shell: true` lets cmd.exe do
53639
54255
  // the resolution without us having to special-case the suffix.
53640
54256
  shell: true
53641
54257
  });
53642
- child.on("close", (code) => {
54258
+ child2.on("close", (code) => {
53643
54259
  resolve3(code ?? 1);
53644
54260
  });
53645
- child.on("error", () => {
54261
+ child2.on("error", () => {
53646
54262
  resolve3(1);
53647
54263
  });
53648
54264
  });
53649
54265
  }
53650
54266
  function captureVersion() {
53651
54267
  return new Promise((resolve3) => {
53652
- const child = spawn2("dr", ["--version"], { shell: true });
54268
+ const child2 = spawn2("dr", ["--version"], { shell: true });
53653
54269
  let out = "";
53654
- child.stdout.on("data", (chunk) => {
54270
+ child2.stdout.on("data", (chunk) => {
53655
54271
  out += chunk.toString();
53656
54272
  });
53657
- child.on("close", (code) => {
54273
+ child2.on("close", (code) => {
53658
54274
  if (code !== 0) {
53659
54275
  resolve3(null);
53660
54276
  return;
53661
54277
  }
53662
54278
  resolve3(out.trim());
53663
54279
  });
53664
- child.on("error", () => resolve3(null));
54280
+ child2.on("error", () => resolve3(null));
53665
54281
  });
53666
54282
  }
53667
54283
  async function runUpdate() {
@@ -53733,8 +54349,8 @@ async function deleteEntity(client, body, options) {
53733
54349
  const path11 = "/bo/pre_kickoff/qbo_manage_companies/v2";
53734
54350
  const res = await client.do("DELETE", path11, JSON.stringify(body), options);
53735
54351
  if (res.status < 200 || res.status >= 300) {
53736
- const text = await res.text();
53737
- const truncated = text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
54352
+ const text2 = await res.text();
54353
+ const truncated = text2.length > 200 ? `${text2.slice(0, 200)}\u2026` : text2;
53738
54354
  throw new Error(`DELETE ${path11}: HTTP ${res.status}: ${truncated}`);
53739
54355
  }
53740
54356
  }
@@ -57864,7 +58480,7 @@ async function defaultSleep8(ms, signal) {
57864
58480
  async function pollExtractionStatus(client, contractFileIds, options = {}) {
57865
58481
  const interval = options.pollIntervalMs ?? EXTRACT_POLL_INTERVAL_MS;
57866
58482
  const timeout = options.timeoutMs ?? EXTRACT_POLL_TIMEOUT_MS;
57867
- const sleep3 = options.sleep ?? defaultSleep8;
58483
+ const sleep4 = options.sleep ?? defaultSleep8;
57868
58484
  const now = options.now ?? Date.now;
57869
58485
  const resolved = /* @__PURE__ */ new Map();
57870
58486
  if (contractFileIds.length === 0) return { resolved, pending: [] };
@@ -57914,7 +58530,7 @@ async function pollExtractionStatus(client, contractFileIds, options = {}) {
57914
58530
  options.onTick?.(resolved.size, contractFileIds.length);
57915
58531
  if (resolved.size >= contractFileIds.length) break;
57916
58532
  if (now() > deadline || inner.signal.aborted) break;
57917
- await sleep3(interval, inner.signal);
58533
+ await sleep4(interval, inner.signal);
57918
58534
  }
57919
58535
  } finally {
57920
58536
  clearTimeout(deadlineTimer);
@@ -59712,11 +60328,11 @@ async function extractSync(cfg, parts) {
59712
60328
  async function emitExtractionDraft(cfg, allResults, sourceFiles, outcomes, abortedMidRun) {
59713
60329
  const failed = outcomes.filter((o) => !o.ok);
59714
60330
  if (cfg.asRawJson) {
59715
- const text2 = JSON.stringify(allResults, null, 2);
60331
+ const text3 = JSON.stringify(allResults, null, 2);
59716
60332
  if (cfg.outputPath !== void 0) {
59717
- await fsPromises3.writeFile(cfg.outputPath, text2 + "\n", "utf8");
60333
+ await fsPromises3.writeFile(cfg.outputPath, text3 + "\n", "utf8");
59718
60334
  } else {
59719
- cfg.out.write(text2 + "\n");
60335
+ cfg.out.write(text3 + "\n");
59720
60336
  }
59721
60337
  reportFileOutcomes(cfg.err, outcomes, abortedMidRun);
59722
60338
  throwIfAnyFileFailed(failed, outcomes.length);
@@ -59728,13 +60344,13 @@ async function emitExtractionDraft(cfg, allResults, sourceFiles, outcomes, abort
59728
60344
  tenantServer: cfg.serverKey,
59729
60345
  defaults: cfg.defaults
59730
60346
  });
59731
- const text = JSON.stringify(draft, null, 2);
60347
+ const text2 = JSON.stringify(draft, null, 2);
59732
60348
  if (cfg.outputPath !== void 0) {
59733
- await fsPromises3.writeFile(cfg.outputPath, text + "\n", "utf8");
60349
+ await fsPromises3.writeFile(cfg.outputPath, text2 + "\n", "utf8");
59734
60350
  cfg.err.write(`wrote ${cfg.outputPath}
59735
60351
  `);
59736
60352
  } else {
59737
- cfg.out.write(text + "\n");
60353
+ cfg.out.write(text2 + "\n");
59738
60354
  }
59739
60355
  const review = draft._meta?.review_required ?? [];
59740
60356
  const failures = draft._meta?.ai_failures ?? [];
@@ -60223,14 +60839,14 @@ async function collectResourceIds(args, fromPath, labels = {}) {
60223
60839
  raw.push(...a.split(",").map((s) => s.trim()));
60224
60840
  }
60225
60841
  if (fromPath !== void 0 && fromPath !== "") {
60226
- let text;
60842
+ let text2;
60227
60843
  try {
60228
- text = await fsPromises3.readFile(fromPath, "utf8");
60844
+ text2 = await fsPromises3.readFile(fromPath, "utf8");
60229
60845
  } catch (err) {
60230
60846
  const m = err instanceof Error ? err.message : String(err);
60231
60847
  throw new Error(`read --from: ${m}`);
60232
60848
  }
60233
- for (const line of text.split(/\r?\n/)) {
60849
+ for (const line of text2.split(/\r?\n/)) {
60234
60850
  const stripped = line.split("#")[0] ?? "";
60235
60851
  raw.push(...stripped.split(",").map((s) => s.trim()));
60236
60852
  }
@@ -63816,27 +64432,27 @@ function groupAnswer(group, below) {
63816
64432
  function filterBelow(node, scopes, map, armed) {
63817
64433
  const here = { count: 0, answer: void 0, required: [], denials: [] };
63818
64434
  let allAnswered = node.commands.length > 0;
63819
- for (const child of node.commands) {
63820
- if (child.commands.length > 0) {
63821
- const below = filterBelow(child, scopes, map, armed);
64435
+ for (const child2 of node.commands) {
64436
+ if (child2.commands.length > 0) {
64437
+ const below = filterBelow(child2, scopes, map, armed);
63822
64438
  here.count += below.count;
63823
64439
  if (below.answer === void 0) {
63824
64440
  allAnswered = false;
63825
64441
  continue;
63826
64442
  }
63827
- interceptGroup(child, below.answer);
63828
- hide(child);
64443
+ interceptGroup(child2, below.answer);
64444
+ hide(child2);
63829
64445
  addRequired(here, below.required);
63830
64446
  addDenials(here, below.denials);
63831
64447
  continue;
63832
64448
  }
63833
- const answer = leafAnswer(child, scopes, map);
64449
+ const answer = leafAnswer(child2, scopes, map);
63834
64450
  if (answer === void 0) {
63835
64451
  allAnswered = false;
63836
64452
  continue;
63837
64453
  }
63838
- intercept(child, answer.err, armed);
63839
- hide(child);
64454
+ intercept(child2, answer.err, armed);
64455
+ hide(child2);
63840
64456
  here.count += 1;
63841
64457
  addRequired(here, answer.required);
63842
64458
  if (answer.denial !== void 0) addDenials(here, [answer.denial]);
@@ -63959,7 +64575,7 @@ function registersServerFlag(cmd) {
63959
64575
  }
63960
64576
  function isServerBound(cmd) {
63961
64577
  if (cmd.commands.length === 0) return registersServerFlag(cmd);
63962
- return cmd.commands.every((child) => isServerBound(child));
64578
+ return cmd.commands.every((child2) => isServerBound(child2));
63963
64579
  }
63964
64580
  function isOpenWithoutServer(cmd) {
63965
64581
  return SERVER_OPTIONAL_COMMANDS.has(cmd.name()) || !isServerBound(cmd);
@@ -64264,6 +64880,7 @@ function buildRootCommand() {
64264
64880
  registerDynamicRangesCommand(root);
64265
64881
  registerFunctionsCommand(root);
64266
64882
  registerSemanticTablesCommand(root);
64883
+ registerODataCommand(root);
64267
64884
  registerStoryboardsCommand(root);
64268
64885
  registerExTablesCommand(root);
64269
64886
  registerLutCommand(root);
@@ -64416,8 +65033,8 @@ function redactUnknownOption(match, prefix, token) {
64416
65033
  if (!looksLikeFlag(name)) return `${prefix}<redacted>'`;
64417
65034
  return eq === -1 ? match : `${prefix}${name}=<redacted>'`;
64418
65035
  }
64419
- function redactCommanderValues(text) {
64420
- return text.replace(UNKNOWN_OPTION_PATTERN, redactUnknownOption).replace(UNKNOWN_COMMAND_PATTERN, "$1<redacted>'").replace(INVALID_OPTION_ARGUMENT_PATTERN, "$1<redacted>$2").replace(INVALID_ENV_VALUE_PATTERN, "$1<redacted>$2").replace(INVALID_COMMAND_ARGUMENT_PATTERN, "$1<redacted>$2");
65036
+ function redactCommanderValues(text2) {
65037
+ return text2.replace(UNKNOWN_OPTION_PATTERN, redactUnknownOption).replace(UNKNOWN_COMMAND_PATTERN, "$1<redacted>'").replace(INVALID_OPTION_ARGUMENT_PATTERN, "$1<redacted>$2").replace(INVALID_ENV_VALUE_PATTERN, "$1<redacted>$2").replace(INVALID_COMMAND_ARGUMENT_PATTERN, "$1<redacted>$2");
64421
65038
  }
64422
65039
  async function main() {
64423
65040
  let root;