@reporting-bi/cli 0.1.11 → 0.1.13

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 +824 -579
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30,91 +30,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  mod
31
31
  ));
32
32
 
33
- // ../rp-client/dist/types.js
34
- var require_types = __commonJS({
35
- "../rp-client/dist/types.js"(exports2) {
36
- "use strict";
37
- Object.defineProperty(exports2, "__esModule", { value: true });
38
- }
39
- });
40
-
41
- // ../rp-client/dist/cache.js
42
- var require_cache = __commonJS({
43
- "../rp-client/dist/cache.js"(exports2) {
44
- "use strict";
45
- Object.defineProperty(exports2, "__esModule", { value: true });
46
- exports2.ServiceCache = void 0;
47
- var ServiceCache = class {
48
- constructor() {
49
- this.cache = /* @__PURE__ */ new Map();
50
- }
51
- createKey(params3) {
52
- let key = params3.path;
53
- if (params3.params) {
54
- const paramStrings = [];
55
- for (const k of Object.keys(params3.params).sort()) {
56
- paramStrings.push(`${k}=${params3.params[k]}`);
57
- }
58
- if (paramStrings.length > 0) {
59
- key += `?${paramStrings.join("&")}`;
60
- }
61
- }
62
- return key;
63
- }
64
- set(params3, data) {
65
- const key = this.createKey(params3);
66
- this.cache.set(key, data);
67
- }
68
- get(params3) {
69
- const key = this.createKey(params3);
70
- return this.cache.get(key);
71
- }
72
- has(params3) {
73
- const key = this.createKey(params3);
74
- return this.cache.has(key);
75
- }
76
- clear() {
77
- this.cache.clear();
78
- }
79
- };
80
- exports2.ServiceCache = ServiceCache;
81
- }
82
- });
83
-
84
- // ../rp-client/dist/accountingProvider.js
85
- var require_accountingProvider = __commonJS({
86
- "../rp-client/dist/accountingProvider.js"(exports2) {
87
- "use strict";
88
- Object.defineProperty(exports2, "__esModule", { value: true });
89
- exports2.AccountingProvider = void 0;
90
- var AccountingProvider2 = class {
91
- constructor() {
92
- this._a = null;
93
- this._accounting = null;
94
- }
95
- get a() {
96
- return this._a;
97
- }
98
- set a(a) {
99
- this._a = a;
100
- if (this.accounting?.accounting != a) {
101
- this._accounting = null;
102
- }
103
- }
104
- get accounting() {
105
- return this._accounting;
106
- }
107
- set accounting(accounting2) {
108
- if (accounting2) {
109
- this._a = accounting2.accounting ?? null;
110
- }
111
- this._accounting = accounting2;
112
- }
113
- };
114
- exports2.AccountingProvider = AccountingProvider2;
115
- }
116
- });
117
-
118
33
  // ../../.yarn/cache/es-errors-npm-1.3.0-fda0c9b8a8-0a61325670.zip/node_modules/es-errors/type.js
119
34
  var require_type = __commonJS({
120
35
  "../../.yarn/cache/es-errors-npm-1.3.0-fda0c9b8a8-0a61325670.zip/node_modules/es-errors/type.js"(exports2, module2) {
@@ -2628,249 +2543,6 @@ var require_lib = __commonJS({
2628
2543
  }
2629
2544
  });
2630
2545
 
2631
- // ../rp-client/dist/service.js
2632
- var require_service = __commonJS({
2633
- "../rp-client/dist/service.js"(exports2) {
2634
- "use strict";
2635
- var __importDefault = exports2 && exports2.__importDefault || function(mod) {
2636
- return mod && mod.__esModule ? mod : { "default": mod };
2637
- };
2638
- Object.defineProperty(exports2, "__esModule", { value: true });
2639
- exports2.Service = exports2.ServiceAuthException = exports2.ServiceEvent = exports2.ServiceException = exports2.serializeRequestParams = void 0;
2640
- var qs_1 = __importDefault(require_lib());
2641
- var cache_js_1 = require_cache();
2642
- function isObject2(value) {
2643
- return value !== null && typeof value === "object" && !Array.isArray(value);
2644
- }
2645
- function serializeRequestParams(params3) {
2646
- const p = {};
2647
- for (const k in params3) {
2648
- switch (k) {
2649
- case "customParams":
2650
- p["p"] = JSON.stringify(params3.customParams);
2651
- break;
2652
- default:
2653
- p[k] = isObject2(params3[k]) || Array.isArray(params3[k]) ? JSON.stringify(params3[k]) : params3[k];
2654
- break;
2655
- }
2656
- }
2657
- return qs_1.default.stringify(p, { addQueryPrefix: false });
2658
- }
2659
- exports2.serializeRequestParams = serializeRequestParams;
2660
- var ServiceException = class extends Error {
2661
- constructor(args) {
2662
- super(args?.data?.message || "Unknown error");
2663
- Object.assign(this, args);
2664
- }
2665
- };
2666
- exports2.ServiceException = ServiceException;
2667
- var ServiceEvent2 = class extends Event {
2668
- constructor(name, args) {
2669
- super(name);
2670
- Object.assign(this, args);
2671
- }
2672
- };
2673
- exports2.ServiceEvent = ServiceEvent2;
2674
- var ServiceAuthException = class extends Error {
2675
- };
2676
- exports2.ServiceAuthException = ServiceAuthException;
2677
- var Service2 = class extends EventTarget {
2678
- get a() {
2679
- return this.accountingProvider?.a;
2680
- }
2681
- get name() {
2682
- return this.options.name;
2683
- }
2684
- get cache() {
2685
- if (!this._cache)
2686
- this._cache = new cache_js_1.ServiceCache();
2687
- return this._cache;
2688
- }
2689
- constructor(options) {
2690
- super();
2691
- this.accountingProvider = null;
2692
- this.tokenProvider = null;
2693
- this._cache = null;
2694
- this.options = { ...options };
2695
- if (!/^(https?|\/)/.test(this.options.baseUrl)) {
2696
- this.options.baseUrl = `https://${this.options.baseUrl}`;
2697
- }
2698
- }
2699
- createUrl(path9 = "", params3) {
2700
- if (this.options.xdebugSession) {
2701
- params3 = params3 || {};
2702
- params3.XDEBUG_SESSION = this.options.xdebugSession === true ? "1" : this.options.xdebugSession.toString();
2703
- }
2704
- if (this.options.params && Object.keys(this.options.params).length > 0) {
2705
- params3 = { ...this.options.params, ...params3 };
2706
- }
2707
- let queryString = params3 ? serializeRequestParams(params3) : "";
2708
- if (queryString)
2709
- queryString = `?${queryString}`;
2710
- const a = params3 && params3.accounting ? params3.accounting.toString() : this.a ? this.a.toString() : null;
2711
- path9 = path9.replace(/^\//, "");
2712
- const parts = [this.options.baseUrl, a, this.options.name, path9];
2713
- return parts.filter((p) => p != null && p !== "").join("/") + queryString;
2714
- }
2715
- async createHeaders(headers) {
2716
- const defaultHeaders = {};
2717
- if (this.tokenProvider) {
2718
- const token = await this.tokenProvider();
2719
- if (token) {
2720
- defaultHeaders["Authorization"] = `Bearer ${token}`;
2721
- }
2722
- }
2723
- return { ...defaultHeaders, ...headers };
2724
- }
2725
- get credentials() {
2726
- return this.tokenProvider ? void 0 : "include";
2727
- }
2728
- async handleError(response) {
2729
- const contentType = response.headers.get("Content-Type");
2730
- let error48;
2731
- if (contentType?.includes("application/json")) {
2732
- error48 = await response.json();
2733
- error48.status = response.status;
2734
- } else {
2735
- error48 = { status: response.status, message: "Unknown error", error: "unknown" };
2736
- }
2737
- if (this.options.throwExceptions) {
2738
- throw new ServiceException({ data: error48, url: response.url });
2739
- }
2740
- }
2741
- async handleResponse(response, parse3) {
2742
- if (!response.ok) {
2743
- await this.handleError(response);
2744
- return null;
2745
- }
2746
- const contentType = response.headers.get("Content-Type");
2747
- if (contentType?.includes("application/json") && parse3 !== false) {
2748
- return await response.json();
2749
- } else {
2750
- return await response.text();
2751
- }
2752
- }
2753
- async getRaw(path9, params3, options) {
2754
- const url2 = this.createUrl(path9, params3);
2755
- this.dispatchEvent(new ServiceEvent2("beforeRequest", { url: url2, params: params3, options, method: "GET" }));
2756
- return fetch(url2, {
2757
- headers: await this.createHeaders(options?.headers),
2758
- credentials: this.credentials
2759
- });
2760
- }
2761
- async get(path9, params3, options) {
2762
- if (options?.cache) {
2763
- const cachedData = this.cache.get({ path: path9, params: params3 });
2764
- if (cachedData)
2765
- return cachedData;
2766
- }
2767
- const response = await this.getRaw(path9, params3, options);
2768
- const responseData = await this.handleResponse(response, options?.parseResponse);
2769
- if (options?.cache && responseData) {
2770
- this.cache.set({ path: path9, params: params3 }, responseData);
2771
- }
2772
- return responseData;
2773
- }
2774
- async postRaw(path9, data, params3, options) {
2775
- const url2 = this.createUrl(path9, params3);
2776
- this.dispatchEvent(new ServiceEvent2("beforeRequest", { url: url2, params: params3, options, method: "POST" }));
2777
- const json3 = isObject2(data) || Array.isArray(data);
2778
- return fetch(url2, {
2779
- method: "POST",
2780
- body: json3 ? JSON.stringify(data) : data,
2781
- headers: await this.createHeaders({
2782
- ...options?.headers ?? {},
2783
- "Content-Type": `${json3 ? "application/json" : "text/plain"}; charset=UTF-8`
2784
- }),
2785
- credentials: this.credentials
2786
- });
2787
- }
2788
- async post(path9, data, params3, options) {
2789
- const response = await this.postRaw(path9, data, params3, options);
2790
- return this.handleResponse(response);
2791
- }
2792
- async deleteRaw(path9, params3, options) {
2793
- const url2 = this.createUrl(path9, params3);
2794
- this.dispatchEvent(new ServiceEvent2("beforeRequest", { url: url2, params: params3, options, method: "DELETE" }));
2795
- return fetch(url2, {
2796
- method: "DELETE",
2797
- headers: await this.createHeaders(options?.headers),
2798
- credentials: this.credentials
2799
- });
2800
- }
2801
- async delete(path9, params3, options) {
2802
- const response = await this.deleteRaw(path9, params3, options);
2803
- return this.handleResponse(response);
2804
- }
2805
- async putRaw(path9, data, params3, options) {
2806
- const url2 = this.createUrl(path9, params3);
2807
- this.dispatchEvent(new ServiceEvent2("beforeRequest", { url: url2, params: params3, options, method: "PUT" }));
2808
- const json3 = isObject2(data) || Array.isArray(data);
2809
- return fetch(url2, {
2810
- method: "PUT",
2811
- body: json3 ? JSON.stringify(data) : data,
2812
- headers: await this.createHeaders({
2813
- ...options?.headers ?? {},
2814
- "Content-Type": `${json3 ? "application/json" : "text/plain"}; charset=UTF-8`
2815
- }),
2816
- credentials: this.credentials
2817
- });
2818
- }
2819
- async put(path9, data, params3, options) {
2820
- const response = await this.putRaw(path9, data, params3, options);
2821
- return this.handleResponse(response);
2822
- }
2823
- async patchRaw(path9, data, params3, options) {
2824
- const url2 = this.createUrl(path9, params3);
2825
- this.dispatchEvent(new ServiceEvent2("beforeRequest", { url: url2, params: params3, options, method: "PATCH" }));
2826
- const json3 = isObject2(data) || Array.isArray(data);
2827
- return fetch(url2, {
2828
- method: "PATCH",
2829
- body: json3 ? JSON.stringify(data) : data,
2830
- headers: await this.createHeaders({
2831
- ...options?.headers ?? {},
2832
- "Content-Type": `${json3 ? "application/json" : "text/plain"}; charset=UTF-8`
2833
- }),
2834
- credentials: this.credentials
2835
- });
2836
- }
2837
- async patch(path9, data, params3, options) {
2838
- const response = await this.patchRaw(path9, data, params3, options);
2839
- return this.handleResponse(response);
2840
- }
2841
- };
2842
- exports2.Service = Service2;
2843
- }
2844
- });
2845
-
2846
- // ../rp-client/dist/index.js
2847
- var require_dist = __commonJS({
2848
- "../rp-client/dist/index.js"(exports2) {
2849
- "use strict";
2850
- var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m, k, k2) {
2851
- if (k2 === void 0) k2 = k;
2852
- var desc = Object.getOwnPropertyDescriptor(m, k);
2853
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
2854
- desc = { enumerable: true, get: function() {
2855
- return m[k];
2856
- } };
2857
- }
2858
- Object.defineProperty(o, k2, desc);
2859
- } : function(o, m, k, k2) {
2860
- if (k2 === void 0) k2 = k;
2861
- o[k2] = m[k];
2862
- });
2863
- var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
2864
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
2865
- };
2866
- Object.defineProperty(exports2, "__esModule", { value: true });
2867
- __exportStar(require_types(), exports2);
2868
- __exportStar(require_cache(), exports2);
2869
- __exportStar(require_accountingProvider(), exports2);
2870
- __exportStar(require_service(), exports2);
2871
- }
2872
- });
2873
-
2874
2546
  // ../../.yarn/cache/ajv-npm-8.18.0-6e5e062765-e7517c4261.zip/node_modules/ajv/dist/compile/codegen/code.js
2875
2547
  var require_code = __commonJS({
2876
2548
  "../../.yarn/cache/ajv-npm-8.18.0-6e5e062765-e7517c4261.zip/node_modules/ajv/dist/compile/codegen/code.js"(exports2) {
@@ -5822,7 +5494,7 @@ var require_compile = __commonJS({
5822
5494
  const schOrFunc = root.refs[ref];
5823
5495
  if (schOrFunc)
5824
5496
  return schOrFunc;
5825
- let _sch = resolve7.call(this, root, ref);
5497
+ let _sch = resolve8.call(this, root, ref);
5826
5498
  if (_sch === void 0) {
5827
5499
  const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
5828
5500
  const { schemaId } = this.opts;
@@ -5849,7 +5521,7 @@ var require_compile = __commonJS({
5849
5521
  function sameSchemaEnv(s1, s2) {
5850
5522
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
5851
5523
  }
5852
- function resolve7(root, ref) {
5524
+ function resolve8(root, ref) {
5853
5525
  let sch;
5854
5526
  while (typeof (sch = this.refs[ref]) == "string")
5855
5527
  ref = sch;
@@ -6424,7 +6096,7 @@ var require_fast_uri = __commonJS({
6424
6096
  }
6425
6097
  return uri;
6426
6098
  }
6427
- function resolve7(baseURI, relativeURI, options) {
6099
+ function resolve8(baseURI, relativeURI, options) {
6428
6100
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
6429
6101
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
6430
6102
  schemelessOptions.skipEscape = true;
@@ -6651,7 +6323,7 @@ var require_fast_uri = __commonJS({
6651
6323
  var fastUri = {
6652
6324
  SCHEMES,
6653
6325
  normalize,
6654
- resolve: resolve7,
6326
+ resolve: resolve8,
6655
6327
  resolveComponent,
6656
6328
  equal,
6657
6329
  serialize,
@@ -8978,7 +8650,7 @@ var require_draft7 = __commonJS({
8978
8650
  });
8979
8651
 
8980
8652
  // ../../.yarn/cache/ajv-npm-8.18.0-6e5e062765-e7517c4261.zip/node_modules/ajv/dist/vocabularies/discriminator/types.js
8981
- var require_types2 = __commonJS({
8653
+ var require_types = __commonJS({
8982
8654
  "../../.yarn/cache/ajv-npm-8.18.0-6e5e062765-e7517c4261.zip/node_modules/ajv/dist/vocabularies/discriminator/types.js"(exports2) {
8983
8655
  "use strict";
8984
8656
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -8997,7 +8669,7 @@ var require_discriminator = __commonJS({
8997
8669
  "use strict";
8998
8670
  Object.defineProperty(exports2, "__esModule", { value: true });
8999
8671
  var codegen_1 = require_codegen();
9000
- var types_1 = require_types2();
8672
+ var types_1 = require_types();
9001
8673
  var compile_1 = require_compile();
9002
8674
  var ref_error_1 = require_ref_error();
9003
8675
  var util_1 = require_util();
@@ -9599,7 +9271,7 @@ var require_limit = __commonJS({
9599
9271
  });
9600
9272
 
9601
9273
  // ../../.yarn/__virtual__/ajv-formats-virtual-7723216a4c/0/cache/ajv-formats-npm-3.0.1-2662cf5b12-168d6bca1e.zip/node_modules/ajv-formats/dist/index.js
9602
- var require_dist2 = __commonJS({
9274
+ var require_dist = __commonJS({
9603
9275
  "../../.yarn/__virtual__/ajv-formats-virtual-7723216a4c/0/cache/ajv-formats-npm-3.0.1-2662cf5b12-168d6bca1e.zip/node_modules/ajv-formats/dist/index.js"(exports2, module2) {
9604
9276
  "use strict";
9605
9277
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -9649,7 +9321,7 @@ var fs2 = __toESM(require("node:fs"));
9649
9321
  var path = __toESM(require("node:path"));
9650
9322
  var os2 = __toESM(require("node:os"));
9651
9323
 
9652
- // ../rp-auth-client-v2/dist/client.js
9324
+ // ../rp-auth-client-v2/src/client.ts
9653
9325
  var DEFAULT_OPTIONS = {
9654
9326
  serviceUrl: null,
9655
9327
  redirectUrl: typeof window !== "undefined" ? window.location.href : null,
@@ -9663,8 +9335,7 @@ var ReportingAuthClientException = class extends Error {
9663
9335
  }
9664
9336
  };
9665
9337
  function accountingServiceUrl(serviceUrl, accounting2) {
9666
- if (accounting2 == null)
9667
- return serviceUrl;
9338
+ if (accounting2 == null) return serviceUrl;
9668
9339
  const match = /^(.*)\/ms\/auth\/?$/.exec(serviceUrl);
9669
9340
  if (!match) {
9670
9341
  throw new ReportingAuthClientException({
@@ -9674,12 +9345,26 @@ function accountingServiceUrl(serviceUrl, accounting2) {
9674
9345
  return `${match[1]}/ms/${accounting2}/auth`;
9675
9346
  }
9676
9347
  var ReportingAuthClient = class {
9348
+ constructor(options) {
9349
+ this.options = DEFAULT_OPTIONS;
9350
+ /**
9351
+ * Body of the last response, already parsed. A fetch body may be read only once, so every
9352
+ * method that consumes it parks the result here for readCredentials() to hand out — reading
9353
+ * it a second time throws "Body is unusable", which used to cost rp-cli the tokens it had
9354
+ * just been issued.
9355
+ *
9356
+ * Boxed because the parsed value may legitimately be null, and a body that never held
9357
+ * credentials (token info, an error) parks { value: null } so it is not read again either.
9358
+ */
9359
+ this.parsedResponse = null;
9360
+ this._expiresAt = null;
9361
+ this.setOptions(options);
9362
+ }
9677
9363
  get expiresAt() {
9678
9364
  return this._expiresAt;
9679
9365
  }
9680
9366
  get expiresIn() {
9681
- if (this._expiresAt === null)
9682
- return null;
9367
+ if (this._expiresAt === null) return null;
9683
9368
  return this._expiresAt - Math.floor(Date.now() / 1e3);
9684
9369
  }
9685
9370
  async getAuthInit() {
@@ -9700,17 +9385,28 @@ var ReportingAuthClient = class {
9700
9385
  }
9701
9386
  });
9702
9387
  }
9388
+ /** Keeps the parked body in step with the response it was read from */
9389
+ setResponse(response) {
9390
+ this.response = response;
9391
+ this.parsedResponse = null;
9392
+ return response;
9393
+ }
9394
+ /**
9395
+ * Reads the error body of the last response. It carries no credentials and the body is spent
9396
+ * afterwards, so it is parked as such. The HTTP status is attached because callers need to
9397
+ * tell a rejected token (401) from a service that is merely unwell (5xx).
9398
+ */
9399
+ async readErrorBody() {
9400
+ const body = await this.response.json();
9401
+ this.parsedResponse = { value: null };
9402
+ return { status: this.response.status, ...body };
9403
+ }
9703
9404
  setOptions(options) {
9704
9405
  Object.assign(this.options, options);
9705
9406
  if (!/^(https?|\/)/.test(this.options.serviceUrl)) {
9706
9407
  this.options.serviceUrl = `https://${this.options.serviceUrl}`;
9707
9408
  }
9708
9409
  }
9709
- constructor(options) {
9710
- this.options = DEFAULT_OPTIONS;
9711
- this._expiresAt = null;
9712
- this.setOptions(options);
9713
- }
9714
9410
  /**
9715
9411
  * Issues a token for another user. Like impersonate(), the accounting belongs in the
9716
9412
  * URL: delegateToken checks the caller's Admin right against the accounting of the
@@ -9721,7 +9417,7 @@ var ReportingAuthClient = class {
9721
9417
  const scope = options.scope ? Array.isArray(options.scope) ? options.scope.join(" ") : options.scope : "";
9722
9418
  const accounting2 = options.accounting ?? this.options.accounting;
9723
9419
  const serviceUrl = accountingServiceUrl(this.options.serviceUrl, accounting2);
9724
- this.response = await this.fetchWithAuth(`${serviceUrl}/delegateToken`, {
9420
+ this.setResponse(await this.fetchWithAuth(`${serviceUrl}/delegateToken`, {
9725
9421
  method: "POST",
9726
9422
  headers: {
9727
9423
  "Content-Type": "application/json"
@@ -9734,8 +9430,10 @@ var ReportingAuthClient = class {
9734
9430
  "scope": scope,
9735
9431
  "accounting": accounting2
9736
9432
  }))
9737
- });
9738
- return await this.response.json();
9433
+ }));
9434
+ const credentials = await this.response.json();
9435
+ this.parsedResponse = { value: credentials };
9436
+ return credentials;
9739
9437
  }
9740
9438
  /**
9741
9439
  * Impersonates the given user. The accounting is required in practice: rights are
@@ -9778,7 +9476,7 @@ var ReportingAuthClient = class {
9778
9476
  }
9779
9477
  }
9780
9478
  async authorize(authorizationCode) {
9781
- this.response = await this.fetchWithAuth(`${this.options.serviceUrl}/token`, {
9479
+ this.setResponse(await this.fetchWithAuth(`${this.options.serviceUrl}/token`, {
9782
9480
  method: "POST",
9783
9481
  headers: {
9784
9482
  "Content-Type": "application/json"
@@ -9789,10 +9487,9 @@ var ReportingAuthClient = class {
9789
9487
  "grant_type": "code",
9790
9488
  "code": authorizationCode
9791
9489
  }))
9792
- });
9490
+ }));
9793
9491
  if (!this.response.ok) {
9794
- const err = await this.response.json();
9795
- throw new ReportingAuthClientException(err);
9492
+ throw new ReportingAuthClientException(await this.readErrorBody());
9796
9493
  }
9797
9494
  return this.response;
9798
9495
  }
@@ -9816,17 +9513,16 @@ var ReportingAuthClient = class {
9816
9513
  window.location.href = url2;
9817
9514
  }
9818
9515
  async getTokenInfo() {
9819
- this.response = await this.fetchWithAuth(`${this.options.serviceUrl}/info`, {
9516
+ this.setResponse(await this.fetchWithAuth(`${this.options.serviceUrl}/info`, {
9820
9517
  method: "GET",
9821
9518
  headers: {
9822
9519
  "Content-Type": "application/json"
9823
9520
  }
9824
- });
9521
+ }));
9825
9522
  this._expiresAt = null;
9826
9523
  if (!this.response.ok) {
9827
9524
  if (this.response.headers.get("content-type")?.includes("application/json")) {
9828
- const err = await this.response.json();
9829
- throw new ReportingAuthClientException(err);
9525
+ throw new ReportingAuthClientException(await this.readErrorBody());
9830
9526
  } else {
9831
9527
  throw new ReportingAuthClientException({
9832
9528
  message: "Gateway communication error"
@@ -9834,6 +9530,7 @@ var ReportingAuthClient = class {
9834
9530
  }
9835
9531
  }
9836
9532
  const tokenInfo = await this.response.json();
9533
+ this.parsedResponse = { value: null };
9837
9534
  if (tokenInfo.expires_in != null) {
9838
9535
  this._expiresAt = Math.floor(Date.now() / 1e3) + tokenInfo.expires_in;
9839
9536
  }
@@ -9848,10 +9545,8 @@ var ReportingAuthClient = class {
9848
9545
  });
9849
9546
  }
9850
9547
  redirectToLogin() {
9851
- if (typeof window === "undefined")
9852
- throw new Error("Not in browser environment");
9853
- if (!this.options.loginUrl)
9854
- throw new Error("Login URL not set");
9548
+ if (typeof window === "undefined") throw new Error("Not in browser environment");
9549
+ if (!this.options.loginUrl) throw new Error("Login URL not set");
9855
9550
  let url2 = this.options.loginUrl;
9856
9551
  const queryParams = [];
9857
9552
  if (this.options.clientId) {
@@ -9866,9 +9561,11 @@ var ReportingAuthClient = class {
9866
9561
  window.location.href = url2;
9867
9562
  }
9868
9563
  async readCredentials() {
9869
- if (!this.response)
9870
- return null;
9871
- return await this.response.json();
9564
+ if (!this.response) return null;
9565
+ if (!this.parsedResponse) {
9566
+ this.parsedResponse = { value: await this.response.json() };
9567
+ }
9568
+ return this.parsedResponse.value;
9872
9569
  }
9873
9570
  async refreshToken() {
9874
9571
  const body = { grant_type: "refresh_token" };
@@ -9878,26 +9575,24 @@ var ReportingAuthClient = class {
9878
9575
  body.refresh_token = token.refresh_token;
9879
9576
  }
9880
9577
  }
9881
- if (this.options.clientId)
9882
- body.client_id = this.options.clientId;
9883
- if (this.options.clientSecret)
9884
- body.client_secret = this.options.clientSecret;
9885
- this.response = await fetch(`${this.options.serviceUrl}/token`, {
9578
+ if (this.options.clientId) body.client_id = this.options.clientId;
9579
+ if (this.options.clientSecret) body.client_secret = this.options.clientSecret;
9580
+ this.setResponse(await fetch(`${this.options.serviceUrl}/token`, {
9886
9581
  method: "POST",
9887
9582
  headers: {
9888
9583
  "Content-Type": "application/json"
9889
9584
  },
9890
9585
  credentials: body.refresh_token ? "omit" : "include",
9891
9586
  body: JSON.stringify(body)
9892
- });
9587
+ }));
9893
9588
  if (this.response.ok) {
9894
9589
  const result = await this.response.json();
9590
+ this.parsedResponse = { value: result };
9895
9591
  if (result?.expires_in) {
9896
9592
  this._expiresAt = Math.floor(Date.now() / 1e3) + result.expires_in;
9897
9593
  }
9898
9594
  } else {
9899
- const err = await this.response.json();
9900
- throw new ReportingAuthClientException(err);
9595
+ throw new ReportingAuthClientException(await this.readErrorBody());
9901
9596
  }
9902
9597
  return this.response;
9903
9598
  }
@@ -9908,8 +9603,260 @@ var ReportingAuthClient = class {
9908
9603
  }
9909
9604
  };
9910
9605
 
9911
- // src/config.ts
9912
- var import_rp_client = __toESM(require_dist());
9606
+ // ../rp-client/src/cache.ts
9607
+ var ServiceCache = class {
9608
+ constructor() {
9609
+ this.cache = /* @__PURE__ */ new Map();
9610
+ }
9611
+ createKey(params3) {
9612
+ let key = params3.path;
9613
+ if (params3.params) {
9614
+ const paramStrings = [];
9615
+ for (const k of Object.keys(params3.params).sort()) {
9616
+ paramStrings.push(`${k}=${params3.params[k]}`);
9617
+ }
9618
+ if (paramStrings.length > 0) {
9619
+ key += `?${paramStrings.join("&")}`;
9620
+ }
9621
+ }
9622
+ return key;
9623
+ }
9624
+ set(params3, data) {
9625
+ const key = this.createKey(params3);
9626
+ this.cache.set(key, data);
9627
+ }
9628
+ get(params3) {
9629
+ const key = this.createKey(params3);
9630
+ return this.cache.get(key);
9631
+ }
9632
+ has(params3) {
9633
+ const key = this.createKey(params3);
9634
+ return this.cache.has(key);
9635
+ }
9636
+ clear() {
9637
+ this.cache.clear();
9638
+ }
9639
+ };
9640
+
9641
+ // ../rp-client/src/accountingProvider.ts
9642
+ var AccountingProvider = class {
9643
+ constructor() {
9644
+ this._a = null;
9645
+ this._accounting = null;
9646
+ }
9647
+ get a() {
9648
+ return this._a;
9649
+ }
9650
+ set a(a) {
9651
+ this._a = a;
9652
+ if (this.accounting?.accounting != a) {
9653
+ this._accounting = null;
9654
+ }
9655
+ }
9656
+ get accounting() {
9657
+ return this._accounting;
9658
+ }
9659
+ set accounting(accounting2) {
9660
+ if (accounting2) {
9661
+ this._a = accounting2.accounting ?? null;
9662
+ }
9663
+ this._accounting = accounting2;
9664
+ }
9665
+ };
9666
+
9667
+ // ../rp-client/src/service.ts
9668
+ var import_qs = __toESM(require_lib());
9669
+ function isObject(value) {
9670
+ return value !== null && typeof value === "object" && !Array.isArray(value);
9671
+ }
9672
+ function serializeRequestParams(params3) {
9673
+ const p = {};
9674
+ for (const k in params3) {
9675
+ switch (k) {
9676
+ case "customParams":
9677
+ p["p"] = JSON.stringify(params3.customParams);
9678
+ break;
9679
+ default:
9680
+ p[k] = isObject(params3[k]) || Array.isArray(params3[k]) ? JSON.stringify(params3[k]) : params3[k];
9681
+ break;
9682
+ }
9683
+ }
9684
+ return import_qs.default.stringify(p, { addQueryPrefix: false });
9685
+ }
9686
+ var ServiceException = class extends Error {
9687
+ constructor(args) {
9688
+ super(args?.data?.message || "Unknown error");
9689
+ Object.assign(this, args);
9690
+ }
9691
+ };
9692
+ var ServiceEvent = class extends Event {
9693
+ constructor(name, args) {
9694
+ super(name);
9695
+ Object.assign(this, args);
9696
+ }
9697
+ };
9698
+ var Service = class extends EventTarget {
9699
+ constructor(options) {
9700
+ super();
9701
+ this.accountingProvider = null;
9702
+ this.tokenProvider = null;
9703
+ this._cache = null;
9704
+ this.options = { ...options };
9705
+ if (!/^(https?|\/)/.test(this.options.baseUrl)) {
9706
+ this.options.baseUrl = `https://${this.options.baseUrl}`;
9707
+ }
9708
+ }
9709
+ get a() {
9710
+ return this.accountingProvider?.a;
9711
+ }
9712
+ get name() {
9713
+ return this.options.name;
9714
+ }
9715
+ get cache() {
9716
+ if (!this._cache) this._cache = new ServiceCache();
9717
+ return this._cache;
9718
+ }
9719
+ createUrl(path9 = "", params3) {
9720
+ if (this.options.xdebugSession) {
9721
+ params3 = params3 || {};
9722
+ params3.XDEBUG_SESSION = this.options.xdebugSession === true ? "1" : this.options.xdebugSession.toString();
9723
+ }
9724
+ if (this.options.params && Object.keys(this.options.params).length > 0) {
9725
+ params3 = { ...this.options.params, ...params3 };
9726
+ }
9727
+ let queryString = params3 ? serializeRequestParams(params3) : "";
9728
+ if (queryString) queryString = `?${queryString}`;
9729
+ const a = params3 && params3.accounting ? params3.accounting.toString() : this.a ? this.a.toString() : null;
9730
+ path9 = path9.replace(/^\//, "");
9731
+ const parts = [this.options.baseUrl, a, this.options.name, path9];
9732
+ return parts.filter((p) => p != null && p !== "").join("/") + queryString;
9733
+ }
9734
+ async createHeaders(headers) {
9735
+ const defaultHeaders = {};
9736
+ if (this.tokenProvider) {
9737
+ const token = await this.tokenProvider();
9738
+ if (token) {
9739
+ defaultHeaders["Authorization"] = `Bearer ${token}`;
9740
+ }
9741
+ }
9742
+ return { ...defaultHeaders, ...headers };
9743
+ }
9744
+ get credentials() {
9745
+ return this.tokenProvider ? void 0 : "include";
9746
+ }
9747
+ async handleError(response) {
9748
+ const contentType = response.headers.get("Content-Type");
9749
+ let error48;
9750
+ if (contentType?.includes("application/json")) {
9751
+ error48 = await response.json();
9752
+ error48.status = response.status;
9753
+ } else {
9754
+ error48 = { status: response.status, message: "Unknown error", error: "unknown" };
9755
+ }
9756
+ if (this.options.throwExceptions) {
9757
+ throw new ServiceException({ data: error48, url: response.url });
9758
+ }
9759
+ }
9760
+ async handleResponse(response, parse3) {
9761
+ if (!response.ok) {
9762
+ await this.handleError(response);
9763
+ return null;
9764
+ }
9765
+ const contentType = response.headers.get("Content-Type");
9766
+ if (contentType?.includes("application/json") && parse3 !== false) {
9767
+ return await response.json();
9768
+ } else {
9769
+ return await response.text();
9770
+ }
9771
+ }
9772
+ async getRaw(path9, params3, options) {
9773
+ const url2 = this.createUrl(path9, params3);
9774
+ this.dispatchEvent(new ServiceEvent("beforeRequest", { url: url2, params: params3, options, method: "GET" }));
9775
+ return fetch(url2, {
9776
+ headers: await this.createHeaders(options?.headers),
9777
+ credentials: this.credentials
9778
+ });
9779
+ }
9780
+ async get(path9, params3, options) {
9781
+ if (options?.cache) {
9782
+ const cachedData = this.cache.get({ path: path9, params: params3 });
9783
+ if (cachedData) return cachedData;
9784
+ }
9785
+ const response = await this.getRaw(path9, params3, options);
9786
+ const responseData = await this.handleResponse(response, options?.parseResponse);
9787
+ if (options?.cache && responseData) {
9788
+ this.cache.set({ path: path9, params: params3 }, responseData);
9789
+ }
9790
+ return responseData;
9791
+ }
9792
+ async postRaw(path9, data, params3, options) {
9793
+ const url2 = this.createUrl(path9, params3);
9794
+ this.dispatchEvent(new ServiceEvent("beforeRequest", { url: url2, params: params3, options, method: "POST" }));
9795
+ const json3 = isObject(data) || Array.isArray(data);
9796
+ return fetch(url2, {
9797
+ method: "POST",
9798
+ body: json3 ? JSON.stringify(data) : data,
9799
+ headers: await this.createHeaders({
9800
+ ...options?.headers ?? {},
9801
+ "Content-Type": `${json3 ? "application/json" : "text/plain"}; charset=UTF-8`
9802
+ }),
9803
+ credentials: this.credentials
9804
+ });
9805
+ }
9806
+ async post(path9, data, params3, options) {
9807
+ const response = await this.postRaw(path9, data, params3, options);
9808
+ return this.handleResponse(response);
9809
+ }
9810
+ async deleteRaw(path9, params3, options) {
9811
+ const url2 = this.createUrl(path9, params3);
9812
+ this.dispatchEvent(new ServiceEvent("beforeRequest", { url: url2, params: params3, options, method: "DELETE" }));
9813
+ return fetch(url2, {
9814
+ method: "DELETE",
9815
+ headers: await this.createHeaders(options?.headers),
9816
+ credentials: this.credentials
9817
+ });
9818
+ }
9819
+ async delete(path9, params3, options) {
9820
+ const response = await this.deleteRaw(path9, params3, options);
9821
+ return this.handleResponse(response);
9822
+ }
9823
+ async putRaw(path9, data, params3, options) {
9824
+ const url2 = this.createUrl(path9, params3);
9825
+ this.dispatchEvent(new ServiceEvent("beforeRequest", { url: url2, params: params3, options, method: "PUT" }));
9826
+ const json3 = isObject(data) || Array.isArray(data);
9827
+ return fetch(url2, {
9828
+ method: "PUT",
9829
+ body: json3 ? JSON.stringify(data) : data,
9830
+ headers: await this.createHeaders({
9831
+ ...options?.headers ?? {},
9832
+ "Content-Type": `${json3 ? "application/json" : "text/plain"}; charset=UTF-8`
9833
+ }),
9834
+ credentials: this.credentials
9835
+ });
9836
+ }
9837
+ async put(path9, data, params3, options) {
9838
+ const response = await this.putRaw(path9, data, params3, options);
9839
+ return this.handleResponse(response);
9840
+ }
9841
+ async patchRaw(path9, data, params3, options) {
9842
+ const url2 = this.createUrl(path9, params3);
9843
+ this.dispatchEvent(new ServiceEvent("beforeRequest", { url: url2, params: params3, options, method: "PATCH" }));
9844
+ const json3 = isObject(data) || Array.isArray(data);
9845
+ return fetch(url2, {
9846
+ method: "PATCH",
9847
+ body: json3 ? JSON.stringify(data) : data,
9848
+ headers: await this.createHeaders({
9849
+ ...options?.headers ?? {},
9850
+ "Content-Type": `${json3 ? "application/json" : "text/plain"}; charset=UTF-8`
9851
+ }),
9852
+ credentials: this.credentials
9853
+ });
9854
+ }
9855
+ async patch(path9, data, params3, options) {
9856
+ const response = await this.patchRaw(path9, data, params3, options);
9857
+ return this.handleResponse(response);
9858
+ }
9859
+ };
9913
9860
 
9914
9861
  // src/crypto.ts
9915
9862
  var crypto = __toESM(require("node:crypto"));
@@ -9971,6 +9918,44 @@ var CONFIG_DIR = path.join(os2.homedir(), ".config", "reporting-bi");
9971
9918
  var CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
9972
9919
  var CLIENT_ID = "0D6693D4-4358-4C08-BA80-A94A97B7F47C";
9973
9920
  var CLIENT_SECRET = "48481427819bb41798bd74fa109c90b184ca4d64d20e60dfbe8430299f06f73e";
9921
+ var EXIT_LOGIN_REQUIRED = 2;
9922
+ var LOGIN_REQUIRED_MARKER = "rpbi: login_required";
9923
+ var SPID_MARKER = "rpbi: spid";
9924
+ var LoginRequiredError = class extends Error {
9925
+ constructor(domain2, detail) {
9926
+ super(`Session expired. Run "rpbi login ${domain2}" again.`);
9927
+ this.domain = domain2;
9928
+ this.detail = detail;
9929
+ }
9930
+ };
9931
+ var mcpMode = false;
9932
+ function setMcpMode() {
9933
+ mcpMode = true;
9934
+ }
9935
+ function tokenExpiration(token) {
9936
+ if (!token) return void 0;
9937
+ try {
9938
+ const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString("utf-8"));
9939
+ return typeof payload.exp === "number" ? payload.exp : void 0;
9940
+ } catch {
9941
+ return void 0;
9942
+ }
9943
+ }
9944
+ function sessionExpiration(tokens) {
9945
+ return tokens.refresh_expires_at ?? tokenExpiration(tokens.refresh_token);
9946
+ }
9947
+ function accessExpiration(tokens) {
9948
+ return tokens.expires_at || tokenExpiration(tokens.access_token);
9949
+ }
9950
+ function sessionState(tokens, now = Math.floor(Date.now() / 1e3)) {
9951
+ const accessEnd = accessExpiration(tokens);
9952
+ const accessValid = accessEnd === void 0 || accessEnd > now;
9953
+ if (!tokens.refresh_token) return accessValid ? "unknown" : "expired";
9954
+ const sessionEnd = sessionExpiration(tokens);
9955
+ if (sessionEnd === void 0) return "unknown";
9956
+ if (sessionEnd <= now) return "expired";
9957
+ return accessValid ? "ok" : "stale";
9958
+ }
9974
9959
  function loadCredentials() {
9975
9960
  try {
9976
9961
  const raw = fs2.readFileSync(CREDENTIALS_FILE, "utf-8");
@@ -9999,21 +9984,29 @@ function findLocalConfig(startDir) {
9999
9984
  dir = parent;
10000
9985
  }
10001
9986
  }
10002
- function getActiveConnection(cwd) {
9987
+ function getActiveConnection(cwd, connection) {
10003
9988
  const creds = loadCredentials();
10004
9989
  const local = findLocalConfig(cwd);
10005
- const domain2 = local?.domain ?? creds.active;
9990
+ const domain2 = connection ?? local?.domain ?? creds.active;
10006
9991
  if (!domain2 || !creds.connections[domain2]) return null;
10007
9992
  const tokens = { ...creds.connections[domain2] };
10008
- if (local?.accounting) {
9993
+ if (local?.accounting && local.domain === domain2) {
10009
9994
  tokens.accounting = local.accounting;
10010
9995
  }
10011
9996
  return { domain: domain2, tokens };
10012
9997
  }
10013
- function storeTokens(domain2, tokens) {
9998
+ function storeTokens(domain2, tokens, activate = true) {
10014
9999
  const creds = loadCredentials();
10015
- creds.connections[domain2] = tokens;
10016
- creds.active = domain2;
10000
+ const previous = creds.connections[domain2];
10001
+ creds.connections[domain2] = {
10002
+ ...previous,
10003
+ ...tokens,
10004
+ // spelled out because both are optional: a refresh token with no readable expiry must
10005
+ // not inherit the previous one, while an unspecified accounting keeps the current pick
10006
+ refresh_expires_at: tokens.refresh_expires_at,
10007
+ accounting: tokens.accounting ?? previous?.accounting
10008
+ };
10009
+ if (activate) creds.active = domain2;
10017
10010
  saveCredentials(creds);
10018
10011
  }
10019
10012
  function removeTokens(domain2) {
@@ -10035,6 +10028,31 @@ function setActive(domain2, accounting2) {
10035
10028
  saveCredentials(creds);
10036
10029
  return true;
10037
10030
  }
10031
+ function statusReport(cwd, now = Math.floor(Date.now() / 1e3)) {
10032
+ const creds = loadCredentials();
10033
+ const local = findLocalConfig(cwd);
10034
+ const activeDomain = local?.domain ?? creds.active;
10035
+ return {
10036
+ active: creds.active,
10037
+ local,
10038
+ connections: Object.keys(creds.connections).map((domain2) => {
10039
+ const tokens = creds.connections[domain2];
10040
+ const accessEnd = accessExpiration(tokens);
10041
+ const sessionEnd = sessionExpiration(tokens);
10042
+ return {
10043
+ domain: domain2,
10044
+ active: domain2 === activeDomain,
10045
+ accounting: tokens.accounting ?? null,
10046
+ state: sessionState(tokens, now),
10047
+ token_expires_at: accessEnd ?? null,
10048
+ token_expires_in: accessEnd === void 0 ? null : accessEnd - now,
10049
+ session_expires_at: sessionEnd ?? null,
10050
+ session_expires_in: sessionEnd === void 0 ? null : sessionEnd - now,
10051
+ has_refresh_token: !!tokens.refresh_token
10052
+ };
10053
+ })
10054
+ };
10055
+ }
10038
10056
  function createAuthClient(domain2, tokens) {
10039
10057
  return new ReportingAuthClient({
10040
10058
  serviceUrl: `${domain2}/auth`,
@@ -10043,49 +10061,107 @@ function createAuthClient(domain2, tokens) {
10043
10061
  tokenProvider: tokens ? () => ({ access_token: tokens.access_token, refresh_token: tokens.refresh_token }) : void 0
10044
10062
  });
10045
10063
  }
10064
+ var DEAD_TOKEN_ERRORS = ["invalid_grant", "invalid_jwt", "invalid_token"];
10065
+ function isSessionRefused(err) {
10066
+ return err.data?.status === 401 || DEAD_TOKEN_ERRORS.includes(err.data?.error ?? "");
10067
+ }
10068
+ function refusalReason(err) {
10069
+ if (err instanceof ReportingAuthClientException) {
10070
+ return err.message || err.data?.error_description || err.data?.error || "refresh refused";
10071
+ }
10072
+ return err instanceof Error ? err.message : String(err);
10073
+ }
10074
+ async function awaitRotatedTokens(domain2, spentRefreshToken) {
10075
+ for (let attempt = 0; attempt < 6; attempt++) {
10076
+ const stored = loadCredentials().connections[domain2];
10077
+ if (stored?.refresh_token && stored.refresh_token !== spentRefreshToken) return stored;
10078
+ await new Promise((resolve8) => setTimeout(resolve8, 100));
10079
+ }
10080
+ return null;
10081
+ }
10082
+ async function refreshTokens(conn, now) {
10083
+ const client = createAuthClient(conn.domain, conn.tokens);
10084
+ await client.refreshToken();
10085
+ const credentials = await client.readCredentials();
10086
+ if (!credentials?.access_token) throw new LoginRequiredError(conn.domain, "no access token in the refresh response");
10087
+ conn.tokens.access_token = credentials.access_token;
10088
+ if (credentials.refresh_token) {
10089
+ conn.tokens.refresh_token = credentials.refresh_token;
10090
+ conn.tokens.refresh_expires_at = tokenExpiration(credentials.refresh_token);
10091
+ }
10092
+ conn.tokens.expires_at = credentials.expires_in ? now + credentials.expires_in : 0;
10093
+ storeTokens(conn.domain, conn.tokens, false);
10094
+ }
10046
10095
  async function ensureValidToken(conn) {
10047
10096
  const now = Math.floor(Date.now() / 1e3);
10048
10097
  const margin = 60;
10049
- if (conn.tokens.expires_at && conn.tokens.expires_at > now + margin) return;
10098
+ const accessEnd = accessExpiration(conn.tokens);
10099
+ if (accessEnd !== void 0 && accessEnd > now + margin) return;
10100
+ if (sessionState(conn.tokens, now) === "expired") throw new LoginRequiredError(conn.domain);
10050
10101
  if (!conn.tokens.refresh_token) return;
10051
- const client = createAuthClient(conn.domain, conn.tokens);
10052
- try {
10053
- await client.refreshToken();
10054
- const credentials = await client.readCredentials();
10055
- if (credentials?.access_token) {
10056
- conn.tokens.access_token = credentials.access_token;
10057
- if (credentials.refresh_token) conn.tokens.refresh_token = credentials.refresh_token;
10058
- conn.tokens.expires_at = credentials.expires_in ? now + credentials.expires_in : 0;
10059
- storeTokens(conn.domain, conn.tokens);
10102
+ for (let attempt = 0; attempt < 3; attempt++) {
10103
+ try {
10104
+ await refreshTokens(conn, now);
10105
+ return;
10106
+ } catch (err) {
10107
+ if (err instanceof LoginRequiredError) throw err;
10108
+ if (!(err instanceof ReportingAuthClientException) || !isSessionRefused(err)) {
10109
+ console.error("Token refresh failed:", refusalReason(err));
10110
+ return;
10111
+ }
10112
+ const stored = await awaitRotatedTokens(conn.domain, conn.tokens.refresh_token);
10113
+ if (!stored) throw new LoginRequiredError(conn.domain, refusalReason(err));
10114
+ conn.tokens.access_token = stored.access_token;
10115
+ conn.tokens.refresh_token = stored.refresh_token;
10116
+ conn.tokens.expires_at = stored.expires_at;
10117
+ conn.tokens.refresh_expires_at = stored.refresh_expires_at;
10118
+ const adoptedEnd = accessExpiration(conn.tokens);
10119
+ if (adoptedEnd !== void 0 && adoptedEnd > now + margin) return;
10060
10120
  }
10061
- } catch (err) {
10062
- console.error("Token refresh failed:", err instanceof Error ? err.message : err);
10063
- console.error(`Session expired. Run "rpbi login ${conn.domain}" again.`);
10064
10121
  }
10122
+ throw new LoginRequiredError(conn.domain, "the refresh token was refused repeatedly");
10123
+ }
10124
+ function abort(message) {
10125
+ if (mcpMode) throw new Error(message);
10126
+ console.error(message);
10127
+ process.exit(1);
10128
+ }
10129
+ function globalFlagValue(flag) {
10130
+ const index = process.argv.indexOf(`--${flag}`);
10131
+ if (index === -1) return void 0;
10132
+ const value = process.argv[index + 1];
10133
+ return value && !value.startsWith("-") ? value : void 0;
10065
10134
  }
10066
10135
  async function createService(name, options) {
10067
10136
  const cwd = typeof options === "object" ? options.cwd : void 0;
10068
- const conn = getActiveConnection(cwd);
10137
+ const connection = (typeof options === "object" ? options.connection : void 0) ?? globalFlagValue("connection");
10138
+ const conn = getActiveConnection(cwd, connection);
10069
10139
  if (!conn) {
10070
- console.error('Not logged in. Run "rpbi login" first.');
10071
- process.exit(1);
10140
+ abort(connection ? `Not logged in to "${connection}". Run "rpbi login ${connection}" first.` : 'Not logged in. Run "rpbi login" first.');
10072
10141
  }
10073
10142
  const accountingOverride = typeof options === "number" ? options : void 0;
10074
10143
  const accountingOptional = typeof options === "object" && options.accountingOptional;
10075
10144
  const accounting2 = accountingOverride ?? conn.tokens.accounting;
10076
- if (!accounting2 && !accountingOptional) {
10077
- console.error('No accounting set. Run "rpbi use <domain> --accounting <id>" first.');
10078
- process.exit(1);
10079
- }
10080
- await ensureValidToken(conn);
10081
- const service = new import_rp_client.Service({
10145
+ if (!accounting2 && !accountingOptional) abort('No accounting set. Run "rpbi use <domain> --accounting <id>" first.');
10146
+ try {
10147
+ await ensureValidToken(conn);
10148
+ } catch (err) {
10149
+ if (mcpMode || !(err instanceof LoginRequiredError)) throw err;
10150
+ const detail = err.detail ? `Token refresh failed: ${err.detail}
10151
+ ` : "";
10152
+ fs2.writeSync(2, `${detail}${LOGIN_REQUIRED_MARKER} ${conn.domain}
10153
+ ${err.message}
10154
+ `);
10155
+ process.exit(EXIT_LOGIN_REQUIRED);
10156
+ }
10157
+ const service = new Service({
10082
10158
  name,
10083
10159
  baseUrl: `${conn.domain}/ms`,
10084
10160
  throwExceptions: true
10085
10161
  });
10086
10162
  service.tokenProvider = () => conn.tokens.access_token;
10087
10163
  if (accounting2) {
10088
- const ap = new import_rp_client.AccountingProvider();
10164
+ const ap = new AccountingProvider();
10089
10165
  ap.a = accounting2;
10090
10166
  service.accountingProvider = ap;
10091
10167
  }
@@ -10194,6 +10270,14 @@ function output(data, flags) {
10194
10270
  console.log(data);
10195
10271
  }
10196
10272
  }
10273
+ function errorMessage(err) {
10274
+ const data = err?.data;
10275
+ return data?.message || data?.error || (err instanceof Error ? err.message : String(err));
10276
+ }
10277
+ function fail(err, context3) {
10278
+ console.error(context3 ? `${context3}: ${errorMessage(err)}` : errorMessage(err));
10279
+ process.exit(1);
10280
+ }
10197
10281
 
10198
10282
  // src/commands/accounting.ts
10199
10283
  async function fetchAccountingName(accountingId) {
@@ -10309,6 +10393,8 @@ async function login(args) {
10309
10393
  access_token: credentials.access_token,
10310
10394
  refresh_token: credentials.refresh_token || "",
10311
10395
  expires_at: expiresAt,
10396
+ // only the JWT carries the session length, the token response does not
10397
+ refresh_expires_at: tokenExpiration(credentials.refresh_token),
10312
10398
  accounting: accounting2
10313
10399
  });
10314
10400
  console.log(`Logged in to ${domain2} successfully.`);
@@ -10324,12 +10410,12 @@ async function login(args) {
10324
10410
  }
10325
10411
  }
10326
10412
  function getAuthorizationCode(domain2) {
10327
- return new Promise((resolve7) => {
10413
+ return new Promise((resolve8) => {
10328
10414
  let timeout;
10329
10415
  const done = (result) => {
10330
10416
  clearTimeout(timeout);
10331
10417
  server.close();
10332
- resolve7(result);
10418
+ resolve8(result);
10333
10419
  };
10334
10420
  const server = http.createServer((req, res) => {
10335
10421
  const url2 = new URL(req.url || "", "http://localhost");
@@ -10400,41 +10486,47 @@ function printLogo() {
10400
10486
  }
10401
10487
 
10402
10488
  // src/commands/status.ts
10403
- function status() {
10489
+ function formatDuration(seconds) {
10490
+ const days = Math.floor(seconds / 86400);
10491
+ const hours = Math.floor(seconds % 86400 / 3600);
10492
+ const minutes = Math.floor(seconds % 3600 / 60);
10493
+ if (days > 0) return `${days}d ${hours}h`;
10494
+ if (hours > 0) return `${hours}h ${minutes}m`;
10495
+ return `${minutes}m`;
10496
+ }
10497
+ function tokenExpiry(expiresIn) {
10498
+ if (expiresIn === null) return "unknown";
10499
+ return expiresIn <= 0 ? "expired" : formatDuration(expiresIn);
10500
+ }
10501
+ function sessionExpiry(connection) {
10502
+ if (connection.state === "expired") return `expired \u2014 run "rpbi login ${connection.domain}"`;
10503
+ if (connection.session_expires_in === null) return "unknown";
10504
+ return `${formatDuration(connection.session_expires_in)} left`;
10505
+ }
10506
+ function status(args = []) {
10507
+ const { flags } = parseArgs(args);
10508
+ const report2 = statusReport();
10509
+ if (flags.json) {
10510
+ console.log(JSON.stringify(report2, null, 2));
10511
+ return;
10512
+ }
10404
10513
  printLogo();
10405
- const creds = loadCredentials();
10406
- const local = findLocalConfig();
10407
- const domains = Object.keys(creds.connections);
10408
- if (local) {
10514
+ if (report2.local) {
10409
10515
  console.log(`Local config (.rpbi/config.json):`);
10410
- console.log(` domain: ${local.domain}`);
10411
- console.log(` accounting: ${local.accounting ?? "not set"}`);
10516
+ console.log(` domain: ${report2.local.domain}`);
10517
+ console.log(` accounting: ${report2.local.accounting ?? "not set"}`);
10412
10518
  console.log();
10413
10519
  }
10414
- if (domains.length === 0) {
10520
+ if (report2.connections.length === 0) {
10415
10521
  console.log("Not logged in.");
10416
10522
  return;
10417
10523
  }
10418
- for (const domain2 of domains) {
10419
- const tokens = creds.connections[domain2];
10420
- const isActive = local ? local.domain === domain2 : creds.active === domain2;
10421
- const now = Math.floor(Date.now() / 1e3);
10422
- const expiresIn = tokens.expires_at - now;
10423
- let expiry;
10424
- if (tokens.expires_at === 0) {
10425
- expiry = "unknown";
10426
- } else if (expiresIn <= 0) {
10427
- expiry = "expired";
10428
- } else {
10429
- const minutes = Math.floor(expiresIn / 60);
10430
- const hours = Math.floor(minutes / 60);
10431
- expiry = hours > 0 ? `${hours}h ${minutes % 60}m` : `${minutes}m`;
10432
- }
10433
- const marker = isActive ? " (active)" : "";
10434
- console.log(`${domain2}${marker}`);
10435
- console.log(` accounting: ${tokens.accounting ?? "not set"}`);
10436
- console.log(` token expires: ${expiry}`);
10437
- console.log(` refresh token: ${tokens.refresh_token ? "yes" : "no"}`);
10524
+ for (const connection of report2.connections) {
10525
+ console.log(`${connection.domain}${connection.active ? " (active)" : ""}`);
10526
+ console.log(` accounting: ${connection.accounting ?? "not set"}`);
10527
+ console.log(` token expires: ${tokenExpiry(connection.token_expires_in)}`);
10528
+ console.log(` session: ${sessionExpiry(connection)}`);
10529
+ console.log(` refresh token: ${connection.has_refresh_token ? "yes" : "no"}`);
10438
10530
  }
10439
10531
  }
10440
10532
 
@@ -10599,6 +10691,43 @@ function walkDir(dir, root, knownPaths, result) {
10599
10691
  }
10600
10692
  }
10601
10693
  }
10694
+ function removeEmptyDirs(dir, root) {
10695
+ dir = path3.resolve(dir);
10696
+ root = path3.resolve(root);
10697
+ while (dir.startsWith(root) && dir !== root) {
10698
+ try {
10699
+ if (fs4.readdirSync(dir).length > 0) return;
10700
+ fs4.rmdirSync(dir);
10701
+ } catch {
10702
+ return;
10703
+ }
10704
+ dir = path3.dirname(dir);
10705
+ }
10706
+ }
10707
+ function removeVanishedFiles(root, previousIndex, serverPaths, computeCurrentHash) {
10708
+ for (const entry of previousIndex) {
10709
+ if (serverPaths.has(entry.path) || isInternalPath(entry.path)) continue;
10710
+ const absPath = path3.join(root, entry.path);
10711
+ if (!fs4.existsSync(absPath)) continue;
10712
+ try {
10713
+ if (computeCurrentHash(absPath, entry) !== entry.hash) {
10714
+ console.log(` Kept ${entry.path} (deleted on server, but modified locally)`);
10715
+ continue;
10716
+ }
10717
+ fs4.rmSync(absPath);
10718
+ removeEmptyDirs(path3.dirname(absPath), root);
10719
+ console.log(` Removed ${entry.path} (deleted on server)`);
10720
+ } catch (err) {
10721
+ console.error(` Failed to remove ${entry.path}: ${err instanceof Error ? err.message : err}`);
10722
+ }
10723
+ }
10724
+ }
10725
+ function entriesToDelete(root, index, relPath) {
10726
+ const prefix = `${relPath}/`;
10727
+ return index.filter(
10728
+ (e) => (e.path === relPath || e.path.startsWith(prefix)) && !fs4.existsSync(path3.join(root, e.path))
10729
+ );
10730
+ }
10602
10731
  function detectModifiedFiles(root, index, getPath, computeCurrentHash, getStoredHash) {
10603
10732
  const modified = [];
10604
10733
  for (const entry of index) {
@@ -10694,7 +10823,7 @@ function printRemoteStatus(label, root, config2, flags, result) {
10694
10823
  for (const key of Object.keys(result)) {
10695
10824
  result[key] = result[key].filter((f) => !isInternalPath(f));
10696
10825
  }
10697
- const { modifiedLocally, modifiedOnServer, conflict, new: newFiles, localOnly, serverOnly } = result;
10826
+ const { modifiedLocally, modifiedOnServer, conflict, new: newFiles, deletedLocally, localOnly, serverOnly } = result;
10698
10827
  if (flags.json === "true") {
10699
10828
  console.log(JSON.stringify(result));
10700
10829
  return;
@@ -10702,7 +10831,7 @@ function printRemoteStatus(label, root, config2, flags, result) {
10702
10831
  console.log(`${label} workspace: ${root}`);
10703
10832
  console.log(`Domain: ${config2?.domain} | Accounting: ${config2?.accounting}`);
10704
10833
  console.log("");
10705
- const total = modifiedLocally.length + modifiedOnServer.length + conflict.length + newFiles.length + localOnly.length + serverOnly.length;
10834
+ const total = modifiedLocally.length + modifiedOnServer.length + conflict.length + newFiles.length + deletedLocally.length + localOnly.length + serverOnly.length;
10706
10835
  if (total === 0) {
10707
10836
  console.log("In sync with server.");
10708
10837
  return;
@@ -10723,8 +10852,12 @@ function printRemoteStatus(label, root, config2, flags, result) {
10723
10852
  console.log("New (untracked):");
10724
10853
  for (const f of newFiles) console.log(` + ${f}`);
10725
10854
  }
10855
+ if (deletedLocally.length > 0) {
10856
+ console.log("Deleted locally (push deletes them on the server):");
10857
+ for (const f of deletedLocally) console.log(` D ${f}`);
10858
+ }
10726
10859
  if (localOnly.length > 0) {
10727
- console.log("Local only (deleted on server):");
10860
+ console.log("Deleted on server (pull removes them locally):");
10728
10861
  for (const f of localOnly) console.log(` - ${f}`);
10729
10862
  }
10730
10863
  if (serverOnly.length > 0) {
@@ -10770,7 +10903,9 @@ async function pull(flags) {
10770
10903
  const wikiService = await createService("wiki", { cwd: dir });
10771
10904
  const langs = flags.langs ? flags.langs.split(",") : SYNC_LANGS;
10772
10905
  console.log(`Pulling wiki to ${dir}...`);
10906
+ const previousIndex = readIndex(dir);
10773
10907
  const index = [];
10908
+ const serverPaths = /* @__PURE__ */ new Set();
10774
10909
  for (const lang of langs) {
10775
10910
  console.log(`Pulling pages for language: ${lang}`);
10776
10911
  const pages = await wikiService.get("pages", {
@@ -10783,6 +10918,7 @@ async function pull(flags) {
10783
10918
  if (!page.id_wiki_page || !page.name) continue;
10784
10919
  const relPath = pageToRelPath({ ...page, lang });
10785
10920
  const absPath = path4.join(dir, relPath);
10921
+ serverPaths.add(relPath);
10786
10922
  try {
10787
10923
  const content4 = await wikiService.get(
10788
10924
  `pages/${page.id_wiki_page}/content`,
@@ -10817,6 +10953,7 @@ async function pull(flags) {
10817
10953
  const lang = file2.lang || "cs";
10818
10954
  const relPath = fileToRelPath({ ...file2, lang });
10819
10955
  const absPath = path4.join(dir, relPath);
10956
+ serverPaths.add(relPath);
10820
10957
  try {
10821
10958
  const response = await wikiService.getRaw(`download/${file2.id_wiki_file}`);
10822
10959
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
@@ -10836,6 +10973,18 @@ async function pull(flags) {
10836
10973
  }
10837
10974
  }
10838
10975
  console.log(` Pulled ${pulledFiles} files`);
10976
+ for (const entry of previousIndex) {
10977
+ if (entry.type === "page" && !langs.includes(entry.lang)) {
10978
+ index.push(entry);
10979
+ serverPaths.add(entry.path);
10980
+ }
10981
+ }
10982
+ removeVanishedFiles(
10983
+ dir,
10984
+ previousIndex,
10985
+ serverPaths,
10986
+ (absPath, entry) => entry.type === "page" ? computePageHash(absPath) : computeFileHash(absPath)
10987
+ );
10839
10988
  writeConfig(dir, {
10840
10989
  domain: conn.domain,
10841
10990
  accounting: conn.tokens.accounting,
@@ -10874,8 +11023,12 @@ async function push(flags, files) {
10874
11023
  for (const relPath of filesToPush) {
10875
11024
  const absPath = path4.join(root, relPath);
10876
11025
  if (!fs5.existsSync(absPath)) {
10877
- const entry2 = indexMap.get(relPath);
10878
- if (entry2) {
11026
+ const entries = entriesToDelete(root, index, relPath);
11027
+ if (entries.length === 0) {
11028
+ console.log(` Skipping ${relPath} (file not found, not in index)`);
11029
+ continue;
11030
+ }
11031
+ for (const entry2 of entries) {
10879
11032
  try {
10880
11033
  if (entry2.type === "page") {
10881
11034
  await wikiService.delete(`pages/${entry2.id}`, { lang: entry2.lang });
@@ -10884,13 +11037,11 @@ async function push(flags, files) {
10884
11037
  }
10885
11038
  const idx = index.indexOf(entry2);
10886
11039
  if (idx !== -1) index.splice(idx, 1);
10887
- indexMap.delete(relPath);
10888
- console.log(` Deleted: ${relPath}`);
11040
+ indexMap.delete(entry2.path);
11041
+ console.log(` Deleted: ${entry2.path}`);
10889
11042
  } catch (err) {
10890
- console.error(` Failed to delete ${relPath}: ${err instanceof Error ? err.message : err}`);
11043
+ console.error(` Failed to delete ${entry2.path}: ${err instanceof Error ? err.message : err}`);
10891
11044
  }
10892
- } else {
10893
- console.log(` Skipping ${relPath} (file not found, not in index)`);
10894
11045
  }
10895
11046
  continue;
10896
11047
  }
@@ -11023,6 +11174,7 @@ async function statusRemote(root, config2, index, flags) {
11023
11174
  const modifiedLocally = [];
11024
11175
  const modifiedOnServer = [];
11025
11176
  const conflict = [];
11177
+ const deletedLocally = [];
11026
11178
  const localOnly = [];
11027
11179
  const serverOnly = [];
11028
11180
  for (const lang of langs) {
@@ -11040,7 +11192,11 @@ async function statusRemote(root, config2, index, flags) {
11040
11192
  serverOnly.push(relPath);
11041
11193
  } else {
11042
11194
  const absPath = path4.join(root, relPath);
11043
- const localChanged = fs5.existsSync(absPath) && computePageHash(absPath) !== localEntry.hash;
11195
+ if (!fs5.existsSync(absPath)) {
11196
+ deletedLocally.push(relPath);
11197
+ continue;
11198
+ }
11199
+ const localChanged = computePageHash(absPath) !== localEntry.hash;
11044
11200
  const serverChanged = page.date_created != null && localEntry.serverDate != null && page.date_created !== localEntry.serverDate;
11045
11201
  if (localChanged && serverChanged) {
11046
11202
  conflict.push(relPath);
@@ -11052,7 +11208,7 @@ async function statusRemote(root, config2, index, flags) {
11052
11208
  }
11053
11209
  }
11054
11210
  for (const [relPath, entry] of localPages) {
11055
- if (entry.lang === lang && !serverPagePaths.has(relPath)) {
11211
+ if (entry.lang === lang && !serverPagePaths.has(relPath) && fs5.existsSync(path4.join(root, relPath))) {
11056
11212
  localOnly.push(relPath);
11057
11213
  }
11058
11214
  }
@@ -11071,7 +11227,11 @@ async function statusRemote(root, config2, index, flags) {
11071
11227
  serverOnly.push(relPath);
11072
11228
  } else {
11073
11229
  const absPath = path4.join(root, relPath);
11074
- const localChanged = fs5.existsSync(absPath) && computeFileHash(absPath) !== localEntry.hash;
11230
+ if (!fs5.existsSync(absPath)) {
11231
+ deletedLocally.push(relPath);
11232
+ continue;
11233
+ }
11234
+ const localChanged = computeFileHash(absPath) !== localEntry.hash;
11075
11235
  const serverChanged = file2.date_created != null && localEntry.serverDate != null && file2.date_created !== localEntry.serverDate;
11076
11236
  if (localChanged && serverChanged) {
11077
11237
  conflict.push(relPath);
@@ -11083,12 +11243,12 @@ async function statusRemote(root, config2, index, flags) {
11083
11243
  }
11084
11244
  }
11085
11245
  for (const [relPath] of localFiles) {
11086
- if (!serverFilePaths.has(relPath)) {
11246
+ if (!serverFilePaths.has(relPath) && fs5.existsSync(path4.join(root, relPath))) {
11087
11247
  localOnly.push(relPath);
11088
11248
  }
11089
11249
  }
11090
11250
  const newFiles = detectNewFiles2(root, index);
11091
- printRemoteStatus("Wiki", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, localOnly, serverOnly });
11251
+ printRemoteStatus("Wiki", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, deletedLocally, localOnly, serverOnly });
11092
11252
  }
11093
11253
  function watch3(flags) {
11094
11254
  return watch2("wiki", flags, push);
@@ -11126,11 +11286,11 @@ async function readContent(flags) {
11126
11286
  return flags.content;
11127
11287
  }
11128
11288
  if (!process.stdin.isTTY) {
11129
- return new Promise((resolve7) => {
11289
+ return new Promise((resolve8) => {
11130
11290
  let data = "";
11131
11291
  process.stdin.setEncoding("utf-8");
11132
11292
  process.stdin.on("data", (chunk) => data += chunk);
11133
- process.stdin.on("end", () => resolve7(data));
11293
+ process.stdin.on("end", () => resolve8(data));
11134
11294
  });
11135
11295
  }
11136
11296
  console.error("No content provided. Use --file, --content, or pipe to stdin.");
@@ -11337,12 +11497,15 @@ async function pull2(flags) {
11337
11497
  if (allThreads.length > threads.length) {
11338
11498
  console.log(` Skipped ${allThreads.length - threads.length} shared thread(s) from another accounting`);
11339
11499
  }
11500
+ const previousIndex = readIndex(dir);
11340
11501
  const index = [];
11502
+ const serverPaths = /* @__PURE__ */ new Set();
11341
11503
  let pulled = 0;
11342
11504
  for (const thread of threads) {
11343
11505
  if (!thread.id_thread || !thread.type || !thread.description) continue;
11344
11506
  const relPath = threadToRelPath(thread);
11345
11507
  const absPath = path5.join(dir, relPath);
11508
+ serverPaths.add(relPath);
11346
11509
  try {
11347
11510
  const content4 = await etlService.get(
11348
11511
  `threads/${thread.id_thread}/content`,
@@ -11396,6 +11559,12 @@ async function pull2(flags) {
11396
11559
  if (!flags["no-context"]) {
11397
11560
  await pullContext(dir, etlService, defService);
11398
11561
  }
11562
+ removeVanishedFiles(
11563
+ dir,
11564
+ previousIndex,
11565
+ serverPaths,
11566
+ (absPath) => computeHash(fs7.readFileSync(absPath, "utf-8"))
11567
+ );
11399
11568
  writeConfig(dir, {
11400
11569
  domain: conn.domain,
11401
11570
  accounting: conn.tokens.accounting,
@@ -11449,19 +11618,21 @@ async function push2(flags, files) {
11449
11618
  for (const relPath of filesToPush) {
11450
11619
  const absPath = path5.join(root, relPath);
11451
11620
  if (!fs7.existsSync(absPath)) {
11452
- const entry2 = indexMap.get(relPath);
11453
- if (entry2) {
11621
+ const entries = entriesToDelete(root, index, relPath);
11622
+ if (entries.length === 0) {
11623
+ console.log(` Skipping ${relPath} (file not found, not in index)`);
11624
+ continue;
11625
+ }
11626
+ for (const entry2 of entries) {
11454
11627
  try {
11455
11628
  await etlService.delete(`threads/${entry2.id_thread}`);
11456
11629
  const idx = index.indexOf(entry2);
11457
11630
  if (idx !== -1) index.splice(idx, 1);
11458
- indexMap.delete(relPath);
11459
- console.log(` Deleted: ${relPath}`);
11631
+ indexMap.delete(entry2.path);
11632
+ console.log(` Deleted: ${entry2.path}`);
11460
11633
  } catch (err) {
11461
- console.error(` Failed to delete ${relPath}: ${err instanceof Error ? err.message : err}`);
11634
+ console.error(` Failed to delete ${entry2.path}: ${err instanceof Error ? err.message : err}`);
11462
11635
  }
11463
- } else {
11464
- console.log(` Skipping ${relPath} (file not found, not in index)`);
11465
11636
  }
11466
11637
  continue;
11467
11638
  }
@@ -11599,6 +11770,7 @@ async function statusRemote2(root, config2, index, flags) {
11599
11770
  const modifiedLocally = [];
11600
11771
  const modifiedOnServer = [];
11601
11772
  const conflict = [];
11773
+ const deletedLocally = [];
11602
11774
  const localOnly = [];
11603
11775
  const serverOnly = [];
11604
11776
  const serverPaths = /* @__PURE__ */ new Set();
@@ -11611,7 +11783,11 @@ async function statusRemote2(root, config2, index, flags) {
11611
11783
  serverOnly.push(relPath);
11612
11784
  } else {
11613
11785
  const absPath = path5.join(root, relPath);
11614
- const localChanged = fs7.existsSync(absPath) && computeHash(fs7.readFileSync(absPath, "utf-8")) !== localEntry.hash;
11786
+ if (!fs7.existsSync(absPath)) {
11787
+ deletedLocally.push(relPath);
11788
+ continue;
11789
+ }
11790
+ const localChanged = computeHash(fs7.readFileSync(absPath, "utf-8")) !== localEntry.hash;
11615
11791
  const serverChanged = thread.date_created != null && localEntry.serverDate != null && thread.date_created !== localEntry.serverDate;
11616
11792
  if (localChanged && serverChanged) {
11617
11793
  conflict.push(relPath);
@@ -11623,12 +11799,12 @@ async function statusRemote2(root, config2, index, flags) {
11623
11799
  }
11624
11800
  }
11625
11801
  for (const entry of index) {
11626
- if (!serverPaths.has(entry.path)) {
11802
+ if (!serverPaths.has(entry.path) && fs7.existsSync(path5.join(root, entry.path))) {
11627
11803
  localOnly.push(entry.path);
11628
11804
  }
11629
11805
  }
11630
11806
  const newFiles = detectNewFiles3(root, index);
11631
- printRemoteStatus("ETL", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, localOnly, serverOnly });
11807
+ printRemoteStatus("ETL", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, deletedLocally, localOnly, serverOnly });
11632
11808
  }
11633
11809
  function watch4(flags) {
11634
11810
  return watch2("etl", flags, push2);
@@ -11664,8 +11840,14 @@ async function exec2(id, flags) {
11664
11840
  csv_delimiter: " ",
11665
11841
  csv_header: "1",
11666
11842
  csv_header_json: "1",
11667
- csv_null_value: "NULL"
11843
+ csv_null_value: "NULL",
11844
+ // makes the server flush the response headers before the query finishes, so X-Spid
11845
+ // is readable while it still runs (IIS holds headers back until the first body flush)
11846
+ flush_headers: "1"
11668
11847
  }, { headers: { accept: "text/csv" } });
11848
+ const spid = response.headers.get("x-spid");
11849
+ if (spid) fs8.writeSync(2, `${SPID_MARKER} ${spid}
11850
+ `);
11669
11851
  if (!response.ok) {
11670
11852
  const text3 = await response.text();
11671
11853
  console.error(text3);
@@ -11678,6 +11860,11 @@ async function exec2(id, flags) {
11678
11860
  process.stdout.write(result || "");
11679
11861
  }
11680
11862
  }
11863
+ async function abort2(spid, flags) {
11864
+ const service = await createService("etl", { cwd: flags.dir || "." });
11865
+ const result = await service.get(`abort/${spid}`);
11866
+ output(result, flags);
11867
+ }
11681
11868
  async function search2(query, flags) {
11682
11869
  const service = await createService("etl");
11683
11870
  const results = await service.get("search", { ...getListParams(flags, DEFAULT_FIELDS), q: query });
@@ -11695,11 +11882,11 @@ async function params(id, flags) {
11695
11882
  if (flags.save) {
11696
11883
  const data = JSON.parse(fs8.readFileSync(flags.save, "utf-8"));
11697
11884
  data.id_thread ??= parseInt(id);
11698
- const result = await service.post(`threads/${id}/parameters`, data);
11885
+ const result = await service.post(`threads/${id}/parameters`, data).catch((err) => fail(err, "Parameter not saved"));
11699
11886
  await refreshYaml();
11700
11887
  output(result, flags);
11701
11888
  } else if (flags.delete) {
11702
- await service.delete(`threads/${id}/parameters/${flags.delete}`);
11889
+ await service.delete(`threads/${id}/parameters/${flags.delete}`).catch((err) => fail(err, "Parameter not deleted"));
11703
11890
  await refreshYaml();
11704
11891
  console.log(`Parameter ${flags.delete} deleted.`);
11705
11892
  } else {
@@ -11731,6 +11918,12 @@ async function etl(args) {
11731
11918
  process.exit(1);
11732
11919
  }
11733
11920
  return exec2(rest[0], flags);
11921
+ case "abort":
11922
+ if (!rest[0]) {
11923
+ console.error("Usage: rpbi etl abort <spid>");
11924
+ process.exit(1);
11925
+ }
11926
+ return abort2(rest[0], flags);
11734
11927
  case "search":
11735
11928
  if (!rest[0]) {
11736
11929
  console.error("Usage: rpbi etl search <query>");
@@ -11754,7 +11947,7 @@ async function etl(args) {
11754
11947
  case "watch":
11755
11948
  return watch4(flags);
11756
11949
  default:
11757
- console.log("Usage: rpbi etl <list|get|content|exec|search|params|pull|push|context|status|watch> [options]");
11950
+ console.log("Usage: rpbi etl <list|get|content|exec|abort|search|params|pull|push|context|status|watch> [options]");
11758
11951
  break;
11759
11952
  }
11760
11953
  }
@@ -11822,12 +12015,15 @@ async function pull3(flags) {
11822
12015
  order: [["type", "asc"], ["description", "asc"]]
11823
12016
  }) || [];
11824
12017
  console.log(`Found ${threads.length} threads`);
12018
+ const previousIndex = readIndex(dir);
11825
12019
  const index = [];
12020
+ const serverPaths = /* @__PURE__ */ new Set();
11826
12021
  let pulled = 0;
11827
12022
  for (const thread of threads) {
11828
12023
  if (!thread.id_thread || !thread.type || !thread.description) continue;
11829
12024
  const relPath = threadToRelPath2(thread);
11830
12025
  const absPath = path6.join(dir, relPath);
12026
+ serverPaths.add(relPath);
11831
12027
  try {
11832
12028
  const content4 = await service.get(
11833
12029
  `threads/${thread.id_thread}/content`,
@@ -11851,6 +12047,7 @@ async function pull3(flags) {
11851
12047
  }
11852
12048
  if (thread.has_doc) {
11853
12049
  const docPath = threadToDocPath(thread);
12050
+ serverPaths.add(docPath);
11854
12051
  try {
11855
12052
  const doc2 = await service.get(
11856
12053
  `threads/${thread.id_thread}/doc`,
@@ -11874,6 +12071,12 @@ async function pull3(flags) {
11874
12071
  }
11875
12072
  }
11876
12073
  }
12074
+ removeVanishedFiles(
12075
+ dir,
12076
+ previousIndex,
12077
+ serverPaths,
12078
+ (absPath) => computeHash(fs9.readFileSync(absPath, "utf-8"))
12079
+ );
11877
12080
  writeConfig(dir, {
11878
12081
  domain: conn.domain,
11879
12082
  accounting: conn.tokens.accounting,
@@ -11913,25 +12116,28 @@ async function push3(flags, files) {
11913
12116
  const absPath = path6.join(root, relPath);
11914
12117
  const entry = indexMap.get(relPath);
11915
12118
  if (!fs9.existsSync(absPath)) {
11916
- if (!entry) {
12119
+ const entries = entriesToDelete(root, index, relPath);
12120
+ if (entries.length === 0) {
11917
12121
  console.log(` Skipping ${relPath} (file not found, not in index)`);
11918
12122
  continue;
11919
12123
  }
11920
- try {
11921
- if (entry.kind === "doc") {
11922
- await service.patch(`threads/${entry.id_thread}/doc`, "", { accounting: entryAccounting(entry) });
11923
- console.log(` Cleared doc: ${relPath}`);
11924
- } else {
11925
- const current = await service.get(`threads/${entry.id_thread}`, { fields: ["idz"] });
11926
- if (!current?.idz) throw new Error("Thread not found on server");
11927
- await service.delete(`threads/${current.idz}`, { accounting: entryAccounting(entry) });
11928
- console.log(` Deleted: ${relPath}`);
11929
- }
11930
- const idx = index.indexOf(entry);
11931
- if (idx !== -1) index.splice(idx, 1);
11932
- indexMap.delete(relPath);
11933
- } catch (err) {
11934
- console.error(` Failed to delete ${relPath}: ${err instanceof Error ? err.message : err}`);
12124
+ for (const gone of entries) {
12125
+ try {
12126
+ if (gone.kind === "doc") {
12127
+ await service.patch(`threads/${gone.id_thread}/doc`, "", { accounting: entryAccounting(gone) });
12128
+ console.log(` Cleared doc: ${gone.path}`);
12129
+ } else {
12130
+ const current = await service.get(`threads/${gone.id_thread}`, { fields: ["idz"] });
12131
+ if (!current?.idz) throw new Error("Thread not found on server");
12132
+ await service.delete(`threads/${current.idz}`, { accounting: entryAccounting(gone) });
12133
+ console.log(` Deleted: ${gone.path}`);
12134
+ }
12135
+ const idx = index.indexOf(gone);
12136
+ if (idx !== -1) index.splice(idx, 1);
12137
+ indexMap.delete(gone.path);
12138
+ } catch (err) {
12139
+ console.error(` Failed to delete ${gone.path}: ${err instanceof Error ? err.message : err}`);
12140
+ }
11935
12141
  }
11936
12142
  continue;
11937
12143
  }
@@ -12048,16 +12254,21 @@ async function statusRemote3(root, config2, index, flags) {
12048
12254
  const modifiedLocally = [];
12049
12255
  const modifiedOnServer = [];
12050
12256
  const conflict = [];
12257
+ const deletedLocally = [];
12051
12258
  const localOnly = [];
12052
12259
  const serverOnly = [];
12053
- const compareEntry = (relPath, serverDate, isServerOnly) => {
12260
+ const compareEntry = (relPath, serverDate, onServer) => {
12054
12261
  const localEntry = indexMap.get(relPath);
12055
12262
  if (!localEntry) {
12056
- if (isServerOnly) serverOnly.push(relPath);
12263
+ if (onServer) serverOnly.push(relPath);
12057
12264
  return;
12058
12265
  }
12059
12266
  const absPath = path6.join(root, relPath);
12060
- const localChanged = fs9.existsSync(absPath) && computeHash(fs9.readFileSync(absPath, "utf-8")) !== localEntry.hash;
12267
+ if (!fs9.existsSync(absPath)) {
12268
+ if (onServer) deletedLocally.push(relPath);
12269
+ return;
12270
+ }
12271
+ const localChanged = computeHash(fs9.readFileSync(absPath, "utf-8")) !== localEntry.hash;
12061
12272
  const serverChanged = serverDate != null && localEntry.serverDate != null && serverDate !== localEntry.serverDate;
12062
12273
  if (localChanged && serverChanged) {
12063
12274
  conflict.push(relPath);
@@ -12083,12 +12294,12 @@ async function statusRemote3(root, config2, index, flags) {
12083
12294
  }
12084
12295
  }
12085
12296
  for (const entry of index) {
12086
- if (!serverPaths.has(entry.path)) {
12297
+ if (!serverPaths.has(entry.path) && fs9.existsSync(path6.join(root, entry.path))) {
12087
12298
  localOnly.push(entry.path);
12088
12299
  }
12089
12300
  }
12090
12301
  const newFiles = detectNewFiles4(root, index);
12091
- printRemoteStatus("Deployment", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, localOnly, serverOnly });
12302
+ printRemoteStatus("Deployment", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, deletedLocally, localOnly, serverOnly });
12092
12303
  }
12093
12304
  function watch5(flags) {
12094
12305
  return watch2("deployment", flags, push3);
@@ -12282,7 +12493,9 @@ async function pull4(flags) {
12282
12493
  const projects2 = await fetchProjects(service);
12283
12494
  const projectNames = buildProjectMap(projects2);
12284
12495
  console.log(`Found ${projects2.length} projects`);
12496
+ const previousIndex = readIndex(dir);
12285
12497
  const index = [];
12498
+ const serverPaths = /* @__PURE__ */ new Set();
12286
12499
  const resources2 = await service.get("resources", {
12287
12500
  fields: RESOURCE_FIELDS,
12288
12501
  order: [["name", "asc"]]
@@ -12297,6 +12510,7 @@ async function pull4(flags) {
12297
12510
  continue;
12298
12511
  }
12299
12512
  const relPath = resourceToRelPath(projectName, resource);
12513
+ serverPaths.add(relPath);
12300
12514
  try {
12301
12515
  const content4 = await service.get(
12302
12516
  `resourceContent/${resource.id_custom_resource}`,
@@ -12328,6 +12542,7 @@ async function pull4(flags) {
12328
12542
  const projectName = query.id_custom_page ? projectNames.get(query.id_custom_page) : void 0;
12329
12543
  if (!projectName) continue;
12330
12544
  const relPath = queryToRelPath(projectName, query);
12545
+ serverPaths.add(relPath);
12331
12546
  try {
12332
12547
  const content4 = await service.get(
12333
12548
  `queryContent/${query.id_thread}`,
@@ -12354,6 +12569,12 @@ async function pull4(flags) {
12354
12569
  if (!project.id_custom_page || !project.name) continue;
12355
12570
  await refreshProjectMeta(service, dir, project, queries2.filter((q) => q.id_custom_page === project.id_custom_page));
12356
12571
  }
12572
+ removeVanishedFiles(
12573
+ dir,
12574
+ previousIndex,
12575
+ serverPaths,
12576
+ (absPath) => computeHash(fs11.readFileSync(absPath, "utf-8"))
12577
+ );
12357
12578
  writeConfig(dir, {
12358
12579
  domain: conn.domain,
12359
12580
  accounting: conn.tokens.accounting,
@@ -12406,18 +12627,21 @@ async function push4(flags, files) {
12406
12627
  const absPath = path7.join(root, relPath);
12407
12628
  const entry = indexMap.get(relPath);
12408
12629
  if (!fs11.existsSync(absPath)) {
12409
- if (!entry) {
12630
+ const entries = entriesToDelete(root, index, relPath);
12631
+ if (entries.length === 0) {
12410
12632
  console.log(` Skipping ${relPath} (file not found, not in index)`);
12411
12633
  continue;
12412
12634
  }
12413
- try {
12414
- await service.delete(entry.kind === "query" ? `queries/${entry.id}` : `resources/${entry.id}`);
12415
- const idx = index.indexOf(entry);
12416
- if (idx !== -1) index.splice(idx, 1);
12417
- indexMap.delete(relPath);
12418
- console.log(` Deleted: ${relPath}`);
12419
- } catch (err) {
12420
- console.error(` Failed to delete ${relPath}: ${err instanceof Error ? err.message : err}`);
12635
+ for (const gone of entries) {
12636
+ try {
12637
+ await service.delete(gone.kind === "query" ? `queries/${gone.id}` : `resources/${gone.id}`);
12638
+ const idx = index.indexOf(gone);
12639
+ if (idx !== -1) index.splice(idx, 1);
12640
+ indexMap.delete(gone.path);
12641
+ console.log(` Deleted: ${gone.path}`);
12642
+ } catch (err) {
12643
+ console.error(` Failed to delete ${gone.path}: ${err instanceof Error ? err.message : err}`);
12644
+ }
12421
12645
  }
12422
12646
  continue;
12423
12647
  }
@@ -12532,6 +12756,7 @@ async function statusRemote4(root, config2, index, flags) {
12532
12756
  const modifiedLocally = [];
12533
12757
  const modifiedOnServer = [];
12534
12758
  const conflict = [];
12759
+ const deletedLocally = [];
12535
12760
  const localOnly = [];
12536
12761
  const serverOnly = [];
12537
12762
  const compareEntry = (relPath, serverDate) => {
@@ -12541,7 +12766,11 @@ async function statusRemote4(root, config2, index, flags) {
12541
12766
  return;
12542
12767
  }
12543
12768
  const absPath = path7.join(root, relPath);
12544
- const localChanged = fs11.existsSync(absPath) && computeHash(fs11.readFileSync(absPath, "utf-8")) !== localEntry.hash;
12769
+ if (!fs11.existsSync(absPath)) {
12770
+ deletedLocally.push(relPath);
12771
+ return;
12772
+ }
12773
+ const localChanged = computeHash(fs11.readFileSync(absPath, "utf-8")) !== localEntry.hash;
12545
12774
  const serverChanged = serverDate != null && localEntry.serverDate != null && serverDate !== localEntry.serverDate;
12546
12775
  if (localChanged && serverChanged) {
12547
12776
  conflict.push(relPath);
@@ -12571,12 +12800,12 @@ async function statusRemote4(root, config2, index, flags) {
12571
12800
  compareEntry(relPath, query.date_created);
12572
12801
  }
12573
12802
  for (const entry of index) {
12574
- if (!serverPaths.has(entry.path)) {
12803
+ if (!serverPaths.has(entry.path) && fs11.existsSync(path7.join(root, entry.path))) {
12575
12804
  localOnly.push(entry.path);
12576
12805
  }
12577
12806
  }
12578
12807
  const newFiles = detectNewFiles5(root, index);
12579
- printRemoteStatus("Extensions", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, localOnly, serverOnly });
12808
+ printRemoteStatus("Extensions", root, config2, flags, { modifiedLocally, modifiedOnServer, conflict, new: newFiles, deletedLocally, localOnly, serverOnly });
12580
12809
  }
12581
12810
  function watch6(flags) {
12582
12811
  return watch2("ext", flags, push4);
@@ -12631,11 +12860,11 @@ async function params2(id, flags) {
12631
12860
  if (flags.save) {
12632
12861
  const data = JSON.parse(fs12.readFileSync(flags.save, "utf-8"));
12633
12862
  data.id_thread ??= parseInt(id);
12634
- const result = await service.post("parameters", data);
12863
+ const result = await service.post("parameters", data).catch((err) => fail(err, "Parameter not saved"));
12635
12864
  await refreshMeta();
12636
12865
  output(result, flags);
12637
12866
  } else if (flags.delete) {
12638
- await service.delete(`parameters/${flags.delete}`);
12867
+ await service.delete(`parameters/${flags.delete}`).catch((err) => fail(err, "Parameter not deleted"));
12639
12868
  await refreshMeta();
12640
12869
  console.log(`Parameter ${flags.delete} deleted.`);
12641
12870
  } else {
@@ -12703,8 +12932,14 @@ async function exec4(id, flags) {
12703
12932
  csv_delimiter: " ",
12704
12933
  csv_header: "1",
12705
12934
  csv_header_json: "1",
12706
- csv_null_value: "NULL"
12935
+ csv_null_value: "NULL",
12936
+ // makes the server flush the response headers before the query finishes, so X-Spid
12937
+ // is readable while it still runs (IIS holds headers back until the first body flush)
12938
+ flush_headers: "1"
12707
12939
  }, { headers: { accept: "text/csv" } });
12940
+ const spid = response.headers.get("x-spid");
12941
+ if (spid) fs12.writeSync(2, `${SPID_MARKER} ${spid}
12942
+ `);
12708
12943
  if (!response.ok) {
12709
12944
  const text3 = await response.text();
12710
12945
  console.error(text3);
@@ -12717,6 +12952,11 @@ async function exec4(id, flags) {
12717
12952
  process.stdout.write(result || "");
12718
12953
  }
12719
12954
  }
12955
+ async function abort3(spid, flags) {
12956
+ const service = await createService("extensions", { cwd: flags.dir || "." });
12957
+ const result = await service.get(`abort/${spid}`);
12958
+ output(result, flags);
12959
+ }
12720
12960
  async function ext(args) {
12721
12961
  const { positional, flags } = parseArgs(args);
12722
12962
  const [subcommand, ...rest] = positional;
@@ -12739,6 +12979,12 @@ async function ext(args) {
12739
12979
  process.exit(1);
12740
12980
  }
12741
12981
  return exec4(rest[0], flags);
12982
+ case "abort":
12983
+ if (!rest[0]) {
12984
+ console.error("Usage: rpbi ext abort <spid>");
12985
+ process.exit(1);
12986
+ }
12987
+ return abort3(rest[0], flags);
12742
12988
  case "params":
12743
12989
  if (!rest[0]) {
12744
12990
  console.error("Usage: rpbi ext params <query_id> [--save <json file>] [--delete <idz>]");
@@ -12756,7 +13002,7 @@ async function ext(args) {
12756
13002
  case "watch":
12757
13003
  return watch6(flags);
12758
13004
  default:
12759
- console.log("Usage: rpbi ext <projects|resources|queries|content|exec|params|deps|pull|push|status|watch> [options]");
13005
+ console.log("Usage: rpbi ext <projects|resources|queries|content|exec|abort|params|deps|pull|push|status|watch> [options]");
12760
13006
  break;
12761
13007
  }
12762
13008
  }
@@ -13473,15 +13719,15 @@ var makeIssue = (params3) => {
13473
13719
  message: issueData.message
13474
13720
  };
13475
13721
  }
13476
- let errorMessage = "";
13722
+ let errorMessage2 = "";
13477
13723
  const maps = errorMaps.filter((m) => !!m).slice().reverse();
13478
13724
  for (const map2 of maps) {
13479
- errorMessage = map2(fullIssue, { data, defaultError: errorMessage }).message;
13725
+ errorMessage2 = map2(fullIssue, { data, defaultError: errorMessage2 }).message;
13480
13726
  }
13481
13727
  return {
13482
13728
  ...issueData,
13483
13729
  path: fullPath,
13484
- message: errorMessage
13730
+ message: errorMessage2
13485
13731
  };
13486
13732
  };
13487
13733
  function addIssueToContext(ctx, issueData) {
@@ -17366,7 +17612,7 @@ __export(util_exports, {
17366
17612
  getParsedType: () => getParsedType2,
17367
17613
  getSizableOrigin: () => getSizableOrigin,
17368
17614
  hexToUint8Array: () => hexToUint8Array,
17369
- isObject: () => isObject,
17615
+ isObject: () => isObject2,
17370
17616
  isPlainObject: () => isPlainObject,
17371
17617
  issue: () => issue,
17372
17618
  joinValues: () => joinValues,
@@ -17536,7 +17782,7 @@ function slugify(input) {
17536
17782
  }
17537
17783
  var captureStackTrace = "captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => {
17538
17784
  };
17539
- function isObject(data) {
17785
+ function isObject2(data) {
17540
17786
  return typeof data === "object" && data !== null && !Array.isArray(data);
17541
17787
  }
17542
17788
  var allowsEval = cached(() => {
@@ -17552,7 +17798,7 @@ var allowsEval = cached(() => {
17552
17798
  }
17553
17799
  });
17554
17800
  function isPlainObject(o) {
17555
- if (isObject(o) === false)
17801
+ if (isObject2(o) === false)
17556
17802
  return false;
17557
17803
  const ctor = o.constructor;
17558
17804
  if (ctor === void 0)
@@ -17560,7 +17806,7 @@ function isPlainObject(o) {
17560
17806
  if (typeof ctor !== "function")
17561
17807
  return true;
17562
17808
  const prot = ctor.prototype;
17563
- if (isObject(prot) === false)
17809
+ if (isObject2(prot) === false)
17564
17810
  return false;
17565
17811
  if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
17566
17812
  return false;
@@ -19718,13 +19964,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def2) => {
19718
19964
  }
19719
19965
  return propValues;
19720
19966
  });
19721
- const isObject2 = isObject;
19967
+ const isObject3 = isObject2;
19722
19968
  const catchall = def2.catchall;
19723
19969
  let value;
19724
19970
  inst._zod.parse = (payload, ctx) => {
19725
19971
  value ?? (value = _normalized.value);
19726
19972
  const input = payload.value;
19727
- if (!isObject2(input)) {
19973
+ if (!isObject3(input)) {
19728
19974
  payload.issues.push({
19729
19975
  expected: "object",
19730
19976
  code: "invalid_type",
@@ -19822,7 +20068,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def2) =
19822
20068
  return (payload, ctx) => fn(shape, payload, ctx);
19823
20069
  };
19824
20070
  let fastpass;
19825
- const isObject2 = isObject;
20071
+ const isObject3 = isObject2;
19826
20072
  const jit = !globalConfig.jitless;
19827
20073
  const allowsEval2 = allowsEval;
19828
20074
  const fastEnabled = jit && allowsEval2.value;
@@ -19831,7 +20077,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def2) =
19831
20077
  inst._zod.parse = (payload, ctx) => {
19832
20078
  value ?? (value = _normalized.value);
19833
20079
  const input = payload.value;
19834
- if (!isObject2(input)) {
20080
+ if (!isObject3(input)) {
19835
20081
  payload.issues.push({
19836
20082
  expected: "object",
19837
20083
  code: "invalid_type",
@@ -20009,7 +20255,7 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
20009
20255
  });
20010
20256
  inst._zod.parse = (payload, ctx) => {
20011
20257
  const input = payload.value;
20012
- if (!isObject(input)) {
20258
+ if (!isObject2(input)) {
20013
20259
  payload.issues.push({
20014
20260
  code: "invalid_type",
20015
20261
  expected: "object",
@@ -32513,19 +32759,19 @@ var getRefs = (options) => {
32513
32759
  };
32514
32760
 
32515
32761
  // ../../.yarn/__virtual__/zod-to-json-schema-virtual-00ec010bf6/0/cache/zod-to-json-schema-npm-3.25.1-850d9216b4-711b30e34d.zip/node_modules/zod-to-json-schema/dist/esm/errorMessages.js
32516
- function addErrorMessage(res, key, errorMessage, refs) {
32762
+ function addErrorMessage(res, key, errorMessage2, refs) {
32517
32763
  if (!refs?.errorMessages)
32518
32764
  return;
32519
- if (errorMessage) {
32765
+ if (errorMessage2) {
32520
32766
  res.errorMessage = {
32521
32767
  ...res.errorMessage,
32522
- [key]: errorMessage
32768
+ [key]: errorMessage2
32523
32769
  };
32524
32770
  }
32525
32771
  }
32526
- function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
32772
+ function setResponseValueAndErrors(res, key, value, errorMessage2, refs) {
32527
32773
  res[key] = value;
32528
- addErrorMessage(res, key, errorMessage, refs);
32774
+ addErrorMessage(res, key, errorMessage2, refs);
32529
32775
  }
32530
32776
 
32531
32777
  // ../../.yarn/__virtual__/zod-to-json-schema-virtual-00ec010bf6/0/cache/zod-to-json-schema-npm-3.25.1-850d9216b4-711b30e34d.zip/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
@@ -33836,8 +34082,8 @@ var Protocol = class {
33836
34082
  if (queuedMessage.type === "response") {
33837
34083
  resolver(message);
33838
34084
  } else {
33839
- const errorMessage = message;
33840
- const error48 = new McpError(errorMessage.error.code, errorMessage.error.message, errorMessage.error.data);
34085
+ const errorMessage2 = message;
34086
+ const error48 = new McpError(errorMessage2.error.code, errorMessage2.error.message, errorMessage2.error.data);
33841
34087
  resolver(error48);
33842
34088
  }
33843
34089
  } else {
@@ -34277,7 +34523,7 @@ var Protocol = class {
34277
34523
  return;
34278
34524
  }
34279
34525
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
34280
- await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
34526
+ await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
34281
34527
  options?.signal?.throwIfAborted();
34282
34528
  }
34283
34529
  } catch (error48) {
@@ -34294,7 +34540,7 @@ var Protocol = class {
34294
34540
  */
34295
34541
  request(request, resultSchema, options) {
34296
34542
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
34297
- return new Promise((resolve7, reject) => {
34543
+ return new Promise((resolve8, reject) => {
34298
34544
  const earlyReject = (error48) => {
34299
34545
  reject(error48);
34300
34546
  };
@@ -34372,7 +34618,7 @@ var Protocol = class {
34372
34618
  if (!parseResult.success) {
34373
34619
  reject(parseResult.error);
34374
34620
  } else {
34375
- resolve7(parseResult.data);
34621
+ resolve8(parseResult.data);
34376
34622
  }
34377
34623
  } catch (error48) {
34378
34624
  reject(error48);
@@ -34633,12 +34879,12 @@ var Protocol = class {
34633
34879
  }
34634
34880
  } catch {
34635
34881
  }
34636
- return new Promise((resolve7, reject) => {
34882
+ return new Promise((resolve8, reject) => {
34637
34883
  if (signal.aborted) {
34638
34884
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
34639
34885
  return;
34640
34886
  }
34641
- const timeoutId = setTimeout(resolve7, interval);
34887
+ const timeoutId = setTimeout(resolve8, interval);
34642
34888
  signal.addEventListener("abort", () => {
34643
34889
  clearTimeout(timeoutId);
34644
34890
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -34733,7 +34979,7 @@ function mergeCapabilities(base, additional) {
34733
34979
 
34734
34980
  // ../../.yarn/__virtual__/@modelcontextprotocol-sdk-virtual-4beea6215a/0/cache/@modelcontextprotocol-sdk-npm-1.26.0-380479a6f8-b4098789d9.zip/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
34735
34981
  var import_ajv = __toESM(require_ajv(), 1);
34736
- var import_ajv_formats = __toESM(require_dist2(), 1);
34982
+ var import_ajv_formats = __toESM(require_dist(), 1);
34737
34983
  function createDefaultAjvInstance() {
34738
34984
  const ajv = new import_ajv.default({
34739
34985
  strict: false,
@@ -34990,23 +35236,23 @@ var Server = class extends Protocol {
34990
35236
  const wrappedHandler = async (request, extra) => {
34991
35237
  const validatedRequest = safeParse2(CallToolRequestSchema, request);
34992
35238
  if (!validatedRequest.success) {
34993
- const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
34994
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
35239
+ const errorMessage2 = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
35240
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage2}`);
34995
35241
  }
34996
35242
  const { params: params3 } = validatedRequest.data;
34997
35243
  const result = await Promise.resolve(handler(request, extra));
34998
35244
  if (params3.task) {
34999
35245
  const taskValidationResult = safeParse2(CreateTaskResultSchema, result);
35000
35246
  if (!taskValidationResult.success) {
35001
- const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
35002
- throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
35247
+ const errorMessage2 = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
35248
+ throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage2}`);
35003
35249
  }
35004
35250
  return taskValidationResult.data;
35005
35251
  }
35006
35252
  const validationResult = safeParse2(CallToolResultSchema, result);
35007
35253
  if (!validationResult.success) {
35008
- const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
35009
- throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
35254
+ const errorMessage2 = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
35255
+ throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage2}`);
35010
35256
  }
35011
35257
  return validationResult.data;
35012
35258
  };
@@ -35500,12 +35746,12 @@ var McpServer = class {
35500
35746
  * @param errorMessage - The error message.
35501
35747
  * @returns The tool error result.
35502
35748
  */
35503
- createToolError(errorMessage) {
35749
+ createToolError(errorMessage2) {
35504
35750
  return {
35505
35751
  content: [
35506
35752
  {
35507
35753
  type: "text",
35508
- text: errorMessage
35754
+ text: errorMessage2
35509
35755
  }
35510
35756
  ],
35511
35757
  isError: true
@@ -35523,8 +35769,8 @@ var McpServer = class {
35523
35769
  const parseResult = await safeParseAsync2(schemaToParse, args);
35524
35770
  if (!parseResult.success) {
35525
35771
  const error48 = "error" in parseResult ? parseResult.error : "Unknown error";
35526
- const errorMessage = getParseErrorMessage(error48);
35527
- throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`);
35772
+ const errorMessage2 = getParseErrorMessage(error48);
35773
+ throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage2}`);
35528
35774
  }
35529
35775
  return parseResult.data;
35530
35776
  }
@@ -35548,8 +35794,8 @@ var McpServer = class {
35548
35794
  const parseResult = await safeParseAsync2(outputObj, result.structuredContent);
35549
35795
  if (!parseResult.success) {
35550
35796
  const error48 = "error" in parseResult ? parseResult.error : "Unknown error";
35551
- const errorMessage = getParseErrorMessage(error48);
35552
- throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
35797
+ const errorMessage2 = getParseErrorMessage(error48);
35798
+ throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage2}`);
35553
35799
  }
35554
35800
  }
35555
35801
  /**
@@ -35597,7 +35843,7 @@ var McpServer = class {
35597
35843
  let task = createTaskResult.task;
35598
35844
  const pollInterval = task.pollInterval ?? 5e3;
35599
35845
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
35600
- await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
35846
+ await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
35601
35847
  const updatedTask = await extra.taskStore.getTask(taskId);
35602
35848
  if (!updatedTask) {
35603
35849
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -35761,8 +36007,8 @@ var McpServer = class {
35761
36007
  const parseResult = await safeParseAsync2(argsObj, request.params.arguments);
35762
36008
  if (!parseResult.success) {
35763
36009
  const error48 = "error" in parseResult ? parseResult.error : "Unknown error";
35764
- const errorMessage = getParseErrorMessage(error48);
35765
- throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage}`);
36010
+ const errorMessage2 = getParseErrorMessage(error48);
36011
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage2}`);
35766
36012
  }
35767
36013
  const args = parseResult.data;
35768
36014
  const cb = prompt.callback;
@@ -36240,12 +36486,12 @@ var StdioServerTransport = class {
36240
36486
  this.onclose?.();
36241
36487
  }
36242
36488
  send(message) {
36243
- return new Promise((resolve7) => {
36489
+ return new Promise((resolve8) => {
36244
36490
  const json3 = serializeMessage(message);
36245
36491
  if (this._stdout.write(json3)) {
36246
- resolve7();
36492
+ resolve8();
36247
36493
  } else {
36248
- this._stdout.once("drain", resolve7);
36494
+ this._stdout.once("drain", resolve8);
36249
36495
  }
36250
36496
  });
36251
36497
  }
@@ -36283,31 +36529,19 @@ function text(data) {
36283
36529
  return { content: [{ type: "text", text: data }] };
36284
36530
  }
36285
36531
  async function mcp() {
36532
+ setMcpMode();
36286
36533
  const server = new McpServer({
36287
36534
  name: "reporting-bi",
36288
- version: "0.1.11"
36535
+ version: "0.1.13"
36289
36536
  });
36290
36537
  server.tool(
36291
36538
  "status",
36292
- "Show active connection, accounting, and token expiry for all logged-in domains",
36539
+ "Show active connection, accounting, and token/session expiry for all logged-in domains",
36293
36540
  {},
36294
36541
  async () => {
36295
- const creds = loadCredentials();
36296
- const domains = Object.keys(creds.connections);
36297
- if (domains.length === 0) return text("Not logged in.");
36298
- const result = domains.map((domain2) => {
36299
- const tokens = creds.connections[domain2];
36300
- const now = Math.floor(Date.now() / 1e3);
36301
- const expiresIn = tokens.expires_at - now;
36302
- return {
36303
- domain: domain2,
36304
- active: creds.active === domain2,
36305
- accounting: tokens.accounting ?? null,
36306
- token_expires_in_seconds: tokens.expires_at === 0 ? null : expiresIn,
36307
- has_refresh_token: !!tokens.refresh_token
36308
- };
36309
- });
36310
- return json2(result);
36542
+ const report2 = statusReport();
36543
+ if (report2.connections.length === 0) return text("Not logged in.");
36544
+ return json2(report2);
36311
36545
  }
36312
36546
  );
36313
36547
  server.tool(
@@ -36788,26 +37022,26 @@ function saveUpdateCheck(data) {
36788
37022
  fs13.writeFileSync(UPDATE_CHECK_FILE, JSON.stringify(data, null, 2));
36789
37023
  }
36790
37024
  function fetchLatestVersion() {
36791
- return new Promise((resolve7) => {
37025
+ return new Promise((resolve8) => {
36792
37026
  const req = https.get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
36793
37027
  if (res.statusCode !== 200) {
36794
37028
  res.resume();
36795
- return resolve7(null);
37029
+ return resolve8(null);
36796
37030
  }
36797
37031
  let body = "";
36798
37032
  res.on("data", (chunk) => body += chunk);
36799
37033
  res.on("end", () => {
36800
37034
  try {
36801
- resolve7(JSON.parse(body).version ?? null);
37035
+ resolve8(JSON.parse(body).version ?? null);
36802
37036
  } catch {
36803
- resolve7(null);
37037
+ resolve8(null);
36804
37038
  }
36805
37039
  });
36806
37040
  });
36807
- req.on("error", () => resolve7(null));
37041
+ req.on("error", () => resolve8(null));
36808
37042
  req.on("timeout", () => {
36809
37043
  req.destroy();
36810
- resolve7(null);
37044
+ resolve8(null);
36811
37045
  });
36812
37046
  });
36813
37047
  }
@@ -36831,10 +37065,10 @@ async function checkForUpdate() {
36831
37065
  saveUpdateCheck({ lastCheck: now });
36832
37066
  const latest = await fetchLatestVersion();
36833
37067
  if (!latest) return;
36834
- if (!isNewer(latest, "0.1.11")) return;
36835
- console.log(`\x1B[33mUpdating ${PACKAGE_NAME} ${"0.1.11"} \u2192 ${latest}...\x1B[0m`);
36836
- (0, import_node_child_process3.execSync)(npmInstallCmd(latest), { stdio: "inherit" });
36837
- console.log(`\x1B[32mUpdated to ${latest}.\x1B[0m`);
37068
+ if (!isNewer(latest, "0.1.13")) return;
37069
+ console.error(`\x1B[33mUpdating ${PACKAGE_NAME} ${"0.1.13"} \u2192 ${latest}...\x1B[0m`);
37070
+ (0, import_node_child_process3.execSync)(npmInstallCmd(latest), { stdio: ["ignore", 2, 2] });
37071
+ console.error(`\x1B[32mUpdated to ${latest}.\x1B[0m`);
36838
37072
  } catch {
36839
37073
  }
36840
37074
  }
@@ -36844,11 +37078,11 @@ async function forceUpdate() {
36844
37078
  console.error("Could not fetch latest version from npm.");
36845
37079
  process.exit(1);
36846
37080
  }
36847
- if (!isNewer(latest, "0.1.11")) {
36848
- console.log(`Already up to date (${"0.1.11"}).`);
37081
+ if (!isNewer(latest, "0.1.13")) {
37082
+ console.log(`Already up to date (${"0.1.13"}).`);
36849
37083
  return;
36850
37084
  }
36851
- console.log(`Updating ${PACKAGE_NAME} ${"0.1.11"} \u2192 ${latest}...`);
37085
+ console.log(`Updating ${PACKAGE_NAME} ${"0.1.13"} \u2192 ${latest}...`);
36852
37086
  (0, import_node_child_process3.execSync)(npmInstallCmd(latest), { stdio: "inherit" });
36853
37087
  console.log(`\x1B[32mUpdated to ${latest}.\x1B[0m`);
36854
37088
  saveUpdateCheck({ lastCheck: Date.now() });
@@ -36859,11 +37093,20 @@ var USAGE_default = `Reporting.bi CLI
36859
37093
 
36860
37094
  Usage: rpbi <command> [options]
36861
37095
 
37096
+ Global options:
37097
+ --connection <domain> Run against this login instead of the active one
37098
+ --json Machine-readable output
37099
+ --verbose Log every request to stderr
37100
+
37101
+ Workspace sync (etl/wiki/deployment/ext pull, push, status) mirrors deletions both ways:
37102
+ pull removes local files deleted on the server (a locally modified file is kept), push
37103
+ deletes on the server what was deleted locally \u2014 single files or whole folders.
37104
+
36862
37105
  Commands:
36863
37106
  login [domain] [--accounting <id>] Login to a Reporting.bi instance (opens browser)
36864
37107
  logout [domain] Logout and revoke tokens (default: active connection)
36865
37108
  use <domain> [--accounting <id>] Switch active connection and/or set accounting
36866
- status Show all connections and active status
37109
+ status [--json] Show all connections, token and session expiry
36867
37110
  init [--connection <domain>] [--accounting <id>] [--type etl|wiki|deployment|ext] Initialize .rpbi/config.json in current directory
36868
37111
  update Force update to latest version from npm
36869
37112
  mcp Start MCP server (for AI agents, stdio transport)
@@ -36876,8 +37119,9 @@ ETL:
36876
37119
  etl get <id> Get thread metadata
36877
37120
  etl content <id> Get thread SQL content (plain text)
36878
37121
  etl exec <id> [--sql "..."|--file f] Execute thread (optionally with custom SQL)
37122
+ etl abort <spid> Abort a running exec (exec reports "rpbi: spid <n>" on stderr)
36879
37123
  etl search <query> Search threads
36880
- etl params <id> [--save <json>|--delete <idz>] List, save or delete thread parameters
37124
+ etl params <id> [--save <json>|--delete <idz>] List, save or delete thread parameters (save creates, or updates when the JSON carries "idz"; a name already declared alongside is refused)
36881
37125
  etl pull [--dir <path>] [--no-context] Pull all ETL threads, parameters, metadata & schema to local files
36882
37126
  etl push [files...] [--dir <path>] [--force] Push modified/new .sql files back to server
36883
37127
  etl status [--dir <path>] [--remote] [--json] Show local changes (--remote compares with server)
@@ -36901,7 +37145,8 @@ Extensions (requires the "custom.define" right):
36901
37145
  ext queries List queries
36902
37146
  ext content <id> [--query] Get resource content (--query for query SQL)
36903
37147
  ext exec <query_id> [--sql "..."|--file f] Execute query (optionally with custom SQL)
36904
- ext params <query_id> [--save <json>|--delete <idz>] List, save or delete query parameters
37148
+ ext abort <spid> Abort a running exec (exec reports "rpbi: spid <n>" on stderr)
37149
+ ext params <query_id> [--save <json>|--delete <idz>] List, save or delete query parameters (save creates, or updates when the JSON carries "idz"; a name already declared alongside is refused)
36905
37150
  ext deps --project <id>|--query <id>|--resource <id> List SQL dependencies (resource \u2194 query links)
36906
37151
  ext deps --query <id> --add <resource_id> Link a query to a resource (or --resource <id> --add <query_id>)
36907
37152
  ext deps --remove <idz> Unlink a dependency
@@ -37025,7 +37270,7 @@ Examples:
37025
37270
  (async () => {
37026
37271
  const [command, ...args] = process.argv.slice(2);
37027
37272
  if (command === "--version" || command === "-v") {
37028
- console.log("0.1.11");
37273
+ console.log("0.1.13");
37029
37274
  return;
37030
37275
  }
37031
37276
  await checkForUpdate();
@@ -37082,7 +37327,7 @@ Examples:
37082
37327
  helpdesk(args);
37083
37328
  break;
37084
37329
  case "status":
37085
- status();
37330
+ status(args);
37086
37331
  break;
37087
37332
  case "mcp":
37088
37333
  mcp();