@reporting-bi/cli 0.0.16 → 0.0.18

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.
Files changed (2) hide show
  1. package/dist/index.js +174 -113
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9803,9 +9803,9 @@ function saveCredentials(data) {
9803
9803
  function findLocalConfig(startDir) {
9804
9804
  let dir = path.resolve(startDir || process.cwd());
9805
9805
  while (true) {
9806
- const manifestPath = path.join(dir, ".rpbi", "manifest.json");
9806
+ const configPath = path.join(dir, ".rpbi", "config.json");
9807
9807
  try {
9808
- const data = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
9808
+ const data = JSON.parse(fs.readFileSync(configPath, "utf-8"));
9809
9809
  if (data.domain) return { domain: data.domain, accounting: data.accounting };
9810
9810
  } catch {
9811
9811
  }
@@ -10197,7 +10197,7 @@ function status() {
10197
10197
  const local = findLocalConfig();
10198
10198
  const domains = Object.keys(creds.connections);
10199
10199
  if (local) {
10200
- console.log(`Local config (.rpbi/manifest.json):`);
10200
+ console.log(`Local config (.rpbi/config.json):`);
10201
10201
  console.log(` domain: ${local.domain}`);
10202
10202
  console.log(` accounting: ${local.accounting ?? "not set"}`);
10203
10203
  console.log();
@@ -10379,41 +10379,124 @@ var fs3 = __toESM(require("node:fs"));
10379
10379
  var path2 = __toESM(require("node:path"));
10380
10380
  var crypto = __toESM(require("node:crypto"));
10381
10381
  var RPBI_DIR = ".rpbi";
10382
- var MANIFEST_FILE = "manifest.json";
10382
+ var CONFIG_FILE = "config.json";
10383
+ var INDEX_FILE = "index.json";
10383
10384
  function computeHash(content2) {
10384
10385
  return crypto.createHash("sha256").update(content2, "utf-8").digest("hex");
10385
10386
  }
10386
- function getManifestPath(dir) {
10387
- return path2.join(dir, RPBI_DIR, MANIFEST_FILE);
10387
+ function getConfigPath(dir) {
10388
+ return path2.join(dir, RPBI_DIR, CONFIG_FILE);
10388
10389
  }
10389
- function readManifest(dir) {
10390
- const filePath = getManifestPath(dir);
10390
+ function getIndexPath(dir) {
10391
+ return path2.join(dir, RPBI_DIR, INDEX_FILE);
10392
+ }
10393
+ function readConfig(dir) {
10391
10394
  try {
10392
- const data = fs3.readFileSync(filePath, "utf-8");
10395
+ const data = fs3.readFileSync(getConfigPath(dir), "utf-8");
10393
10396
  return JSON.parse(data);
10394
10397
  } catch {
10395
10398
  return null;
10396
10399
  }
10397
10400
  }
10398
- function writeManifest(dir, manifest) {
10401
+ function writeConfig(dir, config2) {
10399
10402
  const rpbiDir = path2.join(dir, RPBI_DIR);
10400
10403
  fs3.mkdirSync(rpbiDir, { recursive: true });
10401
10404
  fs3.writeFileSync(
10402
- path2.join(rpbiDir, MANIFEST_FILE),
10403
- JSON.stringify(manifest, null, 2)
10405
+ path2.join(rpbiDir, CONFIG_FILE),
10406
+ JSON.stringify(config2, null, 2)
10407
+ );
10408
+ }
10409
+ function readIndex(dir) {
10410
+ try {
10411
+ const data = fs3.readFileSync(getIndexPath(dir), "utf-8");
10412
+ return JSON.parse(data);
10413
+ } catch {
10414
+ return [];
10415
+ }
10416
+ }
10417
+ function writeIndex(dir, index) {
10418
+ const rpbiDir = path2.join(dir, RPBI_DIR);
10419
+ fs3.mkdirSync(rpbiDir, { recursive: true });
10420
+ fs3.writeFileSync(
10421
+ path2.join(rpbiDir, INDEX_FILE),
10422
+ JSON.stringify(index, null, 2)
10404
10423
  );
10405
10424
  }
10406
10425
  function findEtlRoot(startDir) {
10407
10426
  let dir = path2.resolve(startDir);
10408
10427
  while (true) {
10409
- if (fs3.existsSync(getManifestPath(dir))) return dir;
10428
+ if (fs3.existsSync(getConfigPath(dir))) return dir;
10410
10429
  const parent = path2.dirname(dir);
10411
10430
  if (parent === dir) return null;
10412
10431
  dir = parent;
10413
10432
  }
10414
10433
  }
10415
10434
 
10435
+ // src/yaml.ts
10436
+ function toYaml(data, indent = 0) {
10437
+ if (data === null || data === void 0) return "null";
10438
+ if (typeof data === "boolean") return data ? "true" : "false";
10439
+ if (typeof data === "number") return String(data);
10440
+ if (typeof data === "string") {
10441
+ if (data.includes("\n")) {
10442
+ const prefix = " ".repeat(indent);
10443
+ const lines = data.split("\n").map((l) => prefix + " " + l).join("\n");
10444
+ return "|\n" + lines;
10445
+ }
10446
+ if (data === "" || /[:{}\[\],&*?|>!'"%@`#]/.test(data) || /^\s|\s$/.test(data)) {
10447
+ return JSON.stringify(data);
10448
+ }
10449
+ return data;
10450
+ }
10451
+ if (Array.isArray(data)) {
10452
+ if (data.length === 0) return "[]";
10453
+ const prefix = " ".repeat(indent);
10454
+ return data.map((item) => {
10455
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
10456
+ const entries = Object.entries(item);
10457
+ if (entries.length === 0) return `${prefix}- {}`;
10458
+ const [firstKey, firstVal] = entries[0];
10459
+ const firstLine = `${prefix}- ${firstKey}: ${toYaml(firstVal, indent + 2)}`;
10460
+ const rest = entries.slice(1).map(
10461
+ ([k, v]) => `${prefix} ${k}: ${toYaml(v, indent + 2)}`
10462
+ );
10463
+ return [firstLine, ...rest].join("\n");
10464
+ }
10465
+ return `${prefix}- ${toYaml(item, indent + 2)}`;
10466
+ }).join("\n");
10467
+ }
10468
+ if (typeof data === "object") {
10469
+ const entries = Object.entries(data);
10470
+ if (entries.length === 0) return "{}";
10471
+ const prefix = " ".repeat(indent);
10472
+ return entries.map(
10473
+ ([k, v]) => `${prefix}${k}: ${toYaml(v, indent)}`
10474
+ ).join("\n");
10475
+ }
10476
+ return String(data);
10477
+ }
10478
+
10416
10479
  // src/commands/etl-sync.ts
10480
+ var TYPE_DIR_NAMES = {
10481
+ "IA": "threads_import",
10482
+ "IM": "threads_import_manual",
10483
+ "O": "threads_export",
10484
+ "A": "agent_parameters",
10485
+ "AC": "agent_config",
10486
+ "EC": "excel_config",
10487
+ "D": "threads_delete",
10488
+ "L": "lists",
10489
+ "S": "scripts"
10490
+ };
10491
+ var DIR_NAME_TYPES = Object.fromEntries(
10492
+ Object.entries(TYPE_DIR_NAMES).map(([k, v]) => [v, k])
10493
+ );
10494
+ function typeToDirName(type) {
10495
+ return TYPE_DIR_NAMES[type] || "unknown";
10496
+ }
10497
+ function dirNameToType(dirName) {
10498
+ return DIR_NAME_TYPES[dirName] || null;
10499
+ }
10417
10500
  var PARAMETER_TYPES = ["IA", "IM", "O", "D"];
10418
10501
  var PARAMETER_EXCLUDE_FIELDS = ["accounting", "id_thread", "idz", "validate_flag", "date_created"];
10419
10502
  var THREAD_EXCLUDE_FIELDS = ["command_txt", "command_preview", "accounting", "id_thread", "type", "idz", "validate_flag", "date_created"];
@@ -10466,15 +10549,15 @@ function getFilePath(description, extension = ".sql") {
10466
10549
  }
10467
10550
  function threadToRelPath(thread) {
10468
10551
  const segments = getFilePath(thread.description || "");
10469
- return [thread.type, ...segments].join("/");
10552
+ return [typeToDirName(thread.type), ...segments].join("/");
10470
10553
  }
10471
10554
  function threadToParamPath(thread) {
10472
10555
  const segments = getFilePath(thread.description || "", ".yaml");
10473
- return [".parameters", thread.type, ...segments].join("/");
10556
+ return [".rpbi", "parameters", typeToDirName(thread.type), ...segments].join("/");
10474
10557
  }
10475
10558
  function threadToMetaPath(thread) {
10476
10559
  const segments = getFilePath(thread.description || "", ".yaml");
10477
- return [".threads", thread.type, ...segments].join("/");
10560
+ return [".rpbi", "threads", typeToDirName(thread.type), ...segments].join("/");
10478
10561
  }
10479
10562
  function writeFileSync4(filePath, content2) {
10480
10563
  fs4.mkdirSync(path3.dirname(filePath), { recursive: true });
@@ -10489,48 +10572,6 @@ function stripFields(obj, excludeFields) {
10489
10572
  }
10490
10573
  return result;
10491
10574
  }
10492
- function toYaml(data, indent = 0) {
10493
- if (data === null || data === void 0) return "null";
10494
- if (typeof data === "boolean") return data ? "true" : "false";
10495
- if (typeof data === "number") return String(data);
10496
- if (typeof data === "string") {
10497
- if (data.includes("\n")) {
10498
- const prefix = " ".repeat(indent);
10499
- const lines = data.split("\n").map((l) => prefix + " " + l).join("\n");
10500
- return "|\n" + lines;
10501
- }
10502
- if (data === "" || /[:{}\[\],&*?|>!'"%@`#]/.test(data) || /^\s|\s$/.test(data)) {
10503
- return JSON.stringify(data);
10504
- }
10505
- return data;
10506
- }
10507
- if (Array.isArray(data)) {
10508
- if (data.length === 0) return "[]";
10509
- const prefix = " ".repeat(indent);
10510
- return data.map((item) => {
10511
- if (typeof item === "object" && item !== null && !Array.isArray(item)) {
10512
- const entries = Object.entries(item);
10513
- if (entries.length === 0) return `${prefix}- {}`;
10514
- const [firstKey, firstVal] = entries[0];
10515
- const firstLine = `${prefix}- ${firstKey}: ${toYaml(firstVal, indent + 2)}`;
10516
- const rest = entries.slice(1).map(
10517
- ([k, v]) => `${prefix} ${k}: ${toYaml(v, indent + 2)}`
10518
- );
10519
- return [firstLine, ...rest].join("\n");
10520
- }
10521
- return `${prefix}- ${toYaml(item, indent + 2)}`;
10522
- }).join("\n");
10523
- }
10524
- if (typeof data === "object") {
10525
- const entries = Object.entries(data);
10526
- if (entries.length === 0) return "{}";
10527
- const prefix = " ".repeat(indent);
10528
- return entries.map(
10529
- ([k, v]) => `${prefix}${k}: ${toYaml(v, indent)}`
10530
- ).join("\n");
10531
- }
10532
- return String(data);
10533
- }
10534
10575
  async function pull(flags) {
10535
10576
  const dir = path3.resolve(flags.dir || ".");
10536
10577
  const conn = getActiveConnection();
@@ -10546,12 +10587,7 @@ async function pull(flags) {
10546
10587
  order: [["type", "asc"], ["description", "asc"]]
10547
10588
  }) || [];
10548
10589
  console.log(`Found ${threads.length} threads`);
10549
- const manifest = {
10550
- domain: conn.domain,
10551
- accounting: conn.tokens.accounting,
10552
- pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
10553
- threads: {}
10554
- };
10590
+ const index = [];
10555
10591
  let pulled = 0;
10556
10592
  for (const thread of threads) {
10557
10593
  if (!thread.id_thread || !thread.type || !thread.description) continue;
@@ -10564,11 +10600,12 @@ async function pull(flags) {
10564
10600
  { parseResponse: false }
10565
10601
  ) || "";
10566
10602
  writeFileSync4(absPath, content2);
10567
- manifest.threads[relPath] = {
10603
+ index.push({
10604
+ path: relPath,
10568
10605
  id_thread: thread.id_thread,
10569
10606
  hash: computeHash(content2),
10570
10607
  type: thread.type
10571
- };
10608
+ });
10572
10609
  pulled++;
10573
10610
  if (pulled % 50 === 0) console.log(` ${pulled}/${threads.length} threads...`);
10574
10611
  } catch (err) {
@@ -10608,24 +10645,30 @@ async function pull(flags) {
10608
10645
  if (!flags["no-context"]) {
10609
10646
  await pullContext(dir, etlService, defService);
10610
10647
  }
10611
- writeManifest(dir, manifest);
10648
+ writeConfig(dir, {
10649
+ domain: conn.domain,
10650
+ accounting: conn.tokens.accounting,
10651
+ pulledAt: (/* @__PURE__ */ new Date()).toISOString()
10652
+ });
10653
+ writeIndex(dir, index);
10612
10654
  console.log(`Done. Pulled ${pulled} threads to ${dir}`);
10613
10655
  }
10614
10656
  async function push(flags, files) {
10615
10657
  const startDir = flags.dir || ".";
10616
10658
  const root = findEtlRoot(startDir);
10617
10659
  if (!root) {
10618
- console.error('No .rpbi/manifest.json found. Run "rpbi etl pull" first.');
10660
+ console.error('No .rpbi/config.json found. Run "rpbi etl pull" first.');
10619
10661
  process.exit(1);
10620
10662
  }
10621
- const manifest = readManifest(root);
10663
+ const index = readIndex(root);
10664
+ const indexMap = new Map(index.map((e) => [e.path, e]));
10622
10665
  const etlService = await createService("etl");
10623
10666
  const force = flags.force === "true";
10624
10667
  let filesToPush;
10625
10668
  if (files.length > 0) {
10626
10669
  filesToPush = files.map((f) => path3.relative(root, path3.resolve(f)));
10627
10670
  } else {
10628
- filesToPush = detectModifiedFiles(root, manifest);
10671
+ filesToPush = detectModifiedFiles(root, index);
10629
10672
  }
10630
10673
  if (filesToPush.length === 0) {
10631
10674
  console.log("Nothing to push.");
@@ -10639,7 +10682,7 @@ async function push(flags, files) {
10639
10682
  continue;
10640
10683
  }
10641
10684
  const localContent = fs4.readFileSync(absPath, "utf-8");
10642
- const entry = manifest.threads[relPath];
10685
+ const entry = indexMap.get(relPath);
10643
10686
  if (entry) {
10644
10687
  if (!force) {
10645
10688
  try {
@@ -10657,11 +10700,16 @@ async function push(flags, files) {
10657
10700
  }
10658
10701
  }
10659
10702
  await etlService.post(`threadContent/${entry.id_thread}`, localContent);
10660
- manifest.threads[relPath].hash = computeHash(localContent);
10703
+ entry.hash = computeHash(localContent);
10661
10704
  console.log(` Updated: ${relPath}`);
10662
10705
  } else {
10663
10706
  const parts = relPath.split("/");
10664
- const type = parts[0];
10707
+ const dirName = parts[0];
10708
+ const type = dirNameToType(dirName);
10709
+ if (!type) {
10710
+ console.error(` Unknown thread type directory: ${dirName}`);
10711
+ continue;
10712
+ }
10665
10713
  const description = parts.slice(1).join("/").replace(/\.sql$/, "");
10666
10714
  const newThread = await etlService.post("threads", {
10667
10715
  type,
@@ -10670,22 +10718,23 @@ async function push(flags, files) {
10670
10718
  });
10671
10719
  if (newThread?.id_thread) {
10672
10720
  await etlService.post(`threadContent/${newThread.id_thread}`, localContent);
10673
- manifest.threads[relPath] = {
10721
+ index.push({
10722
+ path: relPath,
10674
10723
  id_thread: newThread.id_thread,
10675
10724
  hash: computeHash(localContent),
10676
10725
  type
10677
- };
10726
+ });
10678
10727
  console.log(` Created: ${relPath} (thread #${newThread.id_thread})`);
10679
10728
  } else {
10680
10729
  console.error(` Failed to create thread for ${relPath}`);
10681
10730
  }
10682
10731
  }
10683
10732
  }
10684
- writeManifest(path3.resolve(root), manifest);
10733
+ writeIndex(path3.resolve(root), index);
10685
10734
  console.log("Push complete.");
10686
10735
  }
10687
10736
  async function pullContext(dir, etlService, defService) {
10688
- const contextDir = path3.join(dir, ".context");
10737
+ const contextDir = path3.join(dir, ".rpbi", "context");
10689
10738
  console.log("Pulling context (schema & modules)...");
10690
10739
  try {
10691
10740
  const modules2 = await defService.get("accountingModules");
@@ -10722,39 +10771,40 @@ async function context(flags) {
10722
10771
  await pullContext(dir, etlService, defService);
10723
10772
  console.log("Context pull complete.");
10724
10773
  }
10725
- function detectModifiedFiles(root, manifest) {
10774
+ function detectModifiedFiles(root, index) {
10726
10775
  const modified = [];
10727
- for (const [relPath, entry] of Object.entries(manifest.threads)) {
10728
- const absPath = path3.join(root, relPath);
10776
+ for (const entry of index) {
10777
+ const absPath = path3.join(root, entry.path);
10729
10778
  if (!fs4.existsSync(absPath)) {
10730
- modified.push(relPath);
10779
+ modified.push(entry.path);
10731
10780
  continue;
10732
10781
  }
10733
10782
  const content2 = fs4.readFileSync(absPath, "utf-8");
10734
10783
  if (computeHash(content2) !== entry.hash) {
10735
- modified.push(relPath);
10784
+ modified.push(entry.path);
10736
10785
  }
10737
10786
  }
10738
10787
  return modified;
10739
10788
  }
10740
- function detectNewFiles(root, manifest) {
10789
+ function detectNewFiles(root, index) {
10741
10790
  const newFiles = [];
10791
+ const knownPaths = new Set(index.map((e) => e.path));
10742
10792
  const entries = fs4.readdirSync(root, { withFileTypes: true });
10743
10793
  for (const entry of entries) {
10744
10794
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
10745
- walkDir(path3.join(root, entry.name), root, manifest, newFiles);
10795
+ walkDir(path3.join(root, entry.name), root, knownPaths, newFiles);
10746
10796
  }
10747
10797
  return newFiles;
10748
10798
  }
10749
- function walkDir(dir, root, manifest, result) {
10799
+ function walkDir(dir, root, knownPaths, result) {
10750
10800
  const entries = fs4.readdirSync(dir, { withFileTypes: true });
10751
10801
  for (const entry of entries) {
10752
10802
  const full = path3.join(dir, entry.name);
10753
10803
  if (entry.isDirectory()) {
10754
- walkDir(full, root, manifest, result);
10804
+ walkDir(full, root, knownPaths, result);
10755
10805
  } else if (entry.name.endsWith(".sql")) {
10756
10806
  const rel = path3.relative(root, full);
10757
- if (!manifest.threads[rel]) {
10807
+ if (!knownPaths.has(rel)) {
10758
10808
  result.push(rel);
10759
10809
  }
10760
10810
  }
@@ -10764,16 +10814,17 @@ async function status2(flags) {
10764
10814
  const startDir = flags.dir || ".";
10765
10815
  const root = findEtlRoot(startDir);
10766
10816
  if (!root) {
10767
- console.error('No .rpbi/manifest.json found. Run "rpbi etl pull" first.');
10817
+ console.error('No .rpbi/config.json found. Run "rpbi etl pull" first.');
10768
10818
  process.exit(1);
10769
10819
  }
10770
- const manifest = readManifest(root);
10820
+ const config2 = readConfig(root);
10821
+ const index = readIndex(root);
10771
10822
  console.log(`ETL workspace: ${root}`);
10772
- console.log(`Domain: ${manifest.domain} | Accounting: ${manifest.accounting}`);
10773
- console.log(`Pulled at: ${manifest.pulledAt}`);
10823
+ console.log(`Domain: ${config2?.domain} | Accounting: ${config2?.accounting}`);
10824
+ console.log(`Pulled at: ${config2?.pulledAt}`);
10774
10825
  console.log("");
10775
- const modified = detectModifiedFiles(root, manifest);
10776
- const newFiles = detectNewFiles(root, manifest);
10826
+ const modified = detectModifiedFiles(root, index);
10827
+ const newFiles = detectNewFiles(root, index);
10777
10828
  const deleted = modified.filter((f) => !fs4.existsSync(path3.join(root, f)));
10778
10829
  const changed = modified.filter((f) => fs4.existsSync(path3.join(root, f)));
10779
10830
  if (changed.length === 0 && newFiles.length === 0 && deleted.length === 0) {
@@ -34395,7 +34446,7 @@ function text(data) {
34395
34446
  async function mcp() {
34396
34447
  const server = new McpServer({
34397
34448
  name: "reporting-bi",
34398
- version: "0.0.16"
34449
+ version: "0.0.18"
34399
34450
  });
34400
34451
  server.tool(
34401
34452
  "status",
@@ -34845,17 +34896,16 @@ function init(args) {
34845
34896
  console.error(`Not logged in to "${domain2}". Run "rpbi login ${domain2}" first.`);
34846
34897
  process.exit(1);
34847
34898
  }
34848
- const existing = readManifest(process.cwd());
34849
- const manifest = {
34899
+ const existing = readConfig(process.cwd());
34900
+ const config2 = {
34850
34901
  domain: domain2,
34851
34902
  accounting: flags.accounting ? parseInt(flags.accounting) : existing?.accounting ?? creds.connections[domain2].accounting ?? 0,
34852
- ...existing?.pulledAt && { pulledAt: existing.pulledAt },
34853
- ...existing?.threads && { threads: existing.threads }
34903
+ ...existing?.pulledAt && { pulledAt: existing.pulledAt }
34854
34904
  };
34855
- writeManifest(process.cwd(), manifest);
34856
- console.log(`Initialized .rpbi/manifest.json`);
34857
- console.log(` domain: ${manifest.domain}`);
34858
- console.log(` accounting: ${manifest.accounting || "not set"}`);
34905
+ writeConfig(process.cwd(), config2);
34906
+ console.log(`Initialized .rpbi/config.json`);
34907
+ console.log(` domain: ${config2.domain}`);
34908
+ console.log(` accounting: ${config2.accounting || "not set"}`);
34859
34909
  }
34860
34910
 
34861
34911
  // src/autoupdate.ts
@@ -34870,11 +34920,14 @@ var CHECK_INTERVAL = 60 * 60 * 1e3;
34870
34920
  var PACKAGE_NAME = "@reporting-bi/cli";
34871
34921
  var INSTALL_PREFIX = detectPrefix();
34872
34922
  function detectPrefix() {
34873
- const binDir = path4.dirname(process.argv[1]);
34874
- const libIndex = binDir.indexOf(path4.join("lib", "node_modules"));
34875
- if (libIndex > 0) {
34876
- const prefix = binDir.slice(0, libIndex - 1);
34877
- if (prefix !== "/usr" && prefix !== "/usr/local") return prefix;
34923
+ try {
34924
+ const realPath = fs6.realpathSync(process.argv[1]);
34925
+ const libIndex = realPath.indexOf(path4.join("lib", "node_modules"));
34926
+ if (libIndex > 0) {
34927
+ const prefix = realPath.slice(0, libIndex - 1);
34928
+ if (prefix !== "/usr" && prefix !== "/usr/local") return prefix;
34929
+ }
34930
+ } catch {
34878
34931
  }
34879
34932
  return null;
34880
34933
  }
@@ -34937,8 +34990,8 @@ async function checkForUpdate() {
34937
34990
  saveUpdateCheck({ lastCheck: now });
34938
34991
  const latest = await fetchLatestVersion();
34939
34992
  if (!latest) return;
34940
- if (!isNewer(latest, "0.0.16")) return;
34941
- console.log(`\x1B[33mUpdating ${PACKAGE_NAME} ${"0.0.16"} \u2192 ${latest}...\x1B[0m`);
34993
+ if (!isNewer(latest, "0.0.18")) return;
34994
+ console.log(`\x1B[33mUpdating ${PACKAGE_NAME} ${"0.0.18"} \u2192 ${latest}...\x1B[0m`);
34942
34995
  (0, import_node_child_process2.execSync)(npmInstallCmd(latest), { stdio: "inherit" });
34943
34996
  console.log(`\x1B[32mUpdated to ${latest}.\x1B[0m`);
34944
34997
  } catch {
@@ -34950,11 +35003,11 @@ async function forceUpdate() {
34950
35003
  console.error("Could not fetch latest version from npm.");
34951
35004
  process.exit(1);
34952
35005
  }
34953
- if (!isNewer(latest, "0.0.16")) {
34954
- console.log(`Already up to date (${"0.0.16"}).`);
35006
+ if (!isNewer(latest, "0.0.18")) {
35007
+ console.log(`Already up to date (${"0.0.18"}).`);
34955
35008
  return;
34956
35009
  }
34957
- console.log(`Updating ${PACKAGE_NAME} ${"0.0.16"} \u2192 ${latest}...`);
35010
+ console.log(`Updating ${PACKAGE_NAME} ${"0.0.18"} \u2192 ${latest}...`);
34958
35011
  (0, import_node_child_process2.execSync)(npmInstallCmd(latest), { stdio: "inherit" });
34959
35012
  console.log(`\x1B[32mUpdated to ${latest}.\x1B[0m`);
34960
35013
  saveUpdateCheck({ lastCheck: Date.now() });
@@ -34983,6 +35036,10 @@ ETL:
34983
35036
  etl content <id> Get thread SQL content (plain text)
34984
35037
  etl exec <id> [--sql "..."|--file f] Execute thread (optionally with custom SQL)
34985
35038
  etl search <query> Search threads
35039
+ etl pull [--dir <path>] [--no-context] Pull all ETL threads, parameters, metadata & schema to local files
35040
+ etl push [files...] [--dir <path>] [--force] Push modified/new .sql files back to server
35041
+ etl status [--dir <path>] Show local changes (modified, new, deleted)
35042
+ etl context [--dir <path>] Pull context only (schema & modules)
34986
35043
 
34987
35044
  Jobs:
34988
35045
  jobs list List available jobs
@@ -35052,6 +35109,10 @@ Examples:
35052
35109
  rpbi etl list
35053
35110
  rpbi etl exec 42
35054
35111
  rpbi etl content 42
35112
+ rpbi etl pull
35113
+ rpbi etl push
35114
+ rpbi etl push IA/folder/script.sql
35115
+ rpbi etl status
35055
35116
 
35056
35117
  rpbi jobs list
35057
35118
  rpbi jobs start "My Job"
@@ -35084,7 +35145,7 @@ Examples:
35084
35145
  (async () => {
35085
35146
  const [command, ...args] = process.argv.slice(2);
35086
35147
  if (command === "--version" || command === "-v") {
35087
- console.log("0.0.16");
35148
+ console.log("0.0.18");
35088
35149
  return;
35089
35150
  }
35090
35151
  await checkForUpdate();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reporting-bi/cli",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "license": "UNLICENSED",
5
5
  "description": "Reporting.bi CLI — command-line interface for the Reporting.bi platform",
6
6
  "main": "./dist/index.js",