@go-labs-sg/bb 2.48.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,31 +35,31 @@ var __toESM = (mod, isNodeMode, target) => {
35
35
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
36
36
  var __require = import.meta.require;
37
37
 
38
- // ../../../node_modules/.bun/dotenv@18.0.6/node_modules/dotenv/dist/index.cjs
38
+ // ../../../node_modules/.bun/dotenv@18.0.8/node_modules/dotenv/dist/index.cjs
39
39
  var require_dist = __commonJS(function(exports, module) {
40
- var S = (e, o) => () => {
40
+ var j = (e, n) => () => {
41
41
  try {
42
- return o || e((o = { exports: {} }).exports, o), o.exports;
42
+ return n || e((n = { exports: {} }).exports, n), n.exports;
43
43
  } catch (t) {
44
- throw o = 0, t;
44
+ throw n = 0, t;
45
45
  }
46
46
  };
47
- var j = S((xe, U) => {
47
+ var k = j((xe, U) => {
48
48
  function G(e) {
49
49
  return typeof e == "string" ? !["false", "0", "no", "off", ""].includes(e.toLowerCase()) : !!e;
50
50
  }
51
- function X(e = process.env) {
52
- let o = {};
51
+ function Y(e = process.env) {
52
+ let n = {};
53
53
  for (let t of ["ENCODING", "PATH", "QUIET", "DEBUG", "OVERRIDE", "FAST"]) {
54
- let n = e[`DOTENV_${t}`] != null ? e[`DOTENV_${t}`] : e[`DOTENV_CONFIG_${t}`];
55
- n != null && (o[t.toLowerCase()] = t === "ENCODING" || t === "PATH" ? n : G(n));
54
+ let o = e[`DOTENV_${t}`] != null ? e[`DOTENV_${t}`] : e[`DOTENV_CONFIG_${t}`];
55
+ o != null && (n[t.toLowerCase()] = t === "ENCODING" || t === "PATH" ? o : G(o));
56
56
  }
57
- return o;
57
+ return n;
58
58
  }
59
- U.exports = { parseBoolean: G, optionsFromEnv: X };
59
+ U.exports = { parseBoolean: G, optionsFromEnv: Y };
60
60
  });
61
- var N = S((ye, x) => {
62
- var Y = __require("fs"), k = __require("path"), z = __require("os"), { URL: Z, fileURLToPath: ee } = __require("url"), { parseBoolean: $, optionsFromEnv: B } = j(), te = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg, b = new Uint8Array(256);
61
+ var N = j((ye, x) => {
62
+ var z = __require("fs"), I = __require("path"), Z = __require("os"), { URL: ee, fileURLToPath: te } = __require("url"), { parseBoolean: $, optionsFromEnv: B } = k(), re = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg, b = new Uint8Array(256);
63
63
  for (let e = 48;e <= 57; e++)
64
64
  b[e] = 1;
65
65
  for (let e = 65;e <= 90; e++)
@@ -69,92 +69,92 @@ var require_dist = __commonJS(function(exports, module) {
69
69
  b[45] = 1;
70
70
  b[46] = 1;
71
71
  b[95] = 1;
72
- function re(e) {
73
- let o = {}, t = e.toString();
72
+ function ne(e) {
73
+ let n = {}, t = e.toString();
74
74
  t = t.replace(/\r\n?/mg, `
75
75
  `);
76
- let n;
77
- for (;(n = te.exec(t)) != null; ) {
78
- let r = n[1], s = n[2] || "";
76
+ let o;
77
+ for (;(o = re.exec(t)) != null; ) {
78
+ let r = o[1], s = o[2] || "";
79
79
  s = s.trim();
80
80
  let i = s[0];
81
81
  s = s.replace(/^(['"`])([\s\S]*)\1$/mg, "$2"), i === '"' && (s = s.replace(/\\n/g, `
82
- `), s = s.replace(/\\r/g, "\r")), o[r] = s;
82
+ `), s = s.replace(/\\r/g, "\r")), n[r] = s;
83
83
  }
84
- return o;
84
+ return n;
85
85
  }
86
86
  function w(e) {
87
87
  return e <= 32 ? e === 32 || e >= 9 && e <= 13 : e >= 160 && (e === 160 || e === 5760 || e >= 8192 && e <= 8202 || e === 8232 || e === 8233 || e === 8239 || e === 8287 || e === 12288 || e === 65279);
88
88
  }
89
- function O(e) {
89
+ function q(e) {
90
90
  return e === 10 || e === 8232 || e === 8233;
91
91
  }
92
92
  function oe(e) {
93
- let o = {}, t = typeof e == "string" ? e : e.toString();
93
+ let n = {}, t = typeof e == "string" ? e : e.toString();
94
94
  t.indexOf("\r") !== -1 && (t = t.replace(/\r\n?/g, `
95
95
  `));
96
- let n = t.length, r = 0;
97
- for (;r < n; ) {
96
+ let o = t.length, r = 0;
97
+ for (;r < o; ) {
98
98
  let s = t.charCodeAt(r);
99
- for (;r < n && w(s); )
99
+ for (;r < o && w(s); )
100
100
  r++, s = t.charCodeAt(r);
101
- if (r >= n)
101
+ if (r >= o)
102
102
  break;
103
103
  if (s === 35) {
104
- for (;r < n && !O(t.charCodeAt(r)); )
104
+ for (;r < o && !q(t.charCodeAt(r)); )
105
105
  r++;
106
106
  continue;
107
107
  }
108
108
  let i = -1;
109
- if (s === 101 && r + 6 < n && t.charCodeAt(r + 1) === 120 && t.charCodeAt(r + 2) === 112 && t.charCodeAt(r + 3) === 111 && t.charCodeAt(r + 4) === 114 && t.charCodeAt(r + 5) === 116) {
109
+ if (s === 101 && r + 6 < o && t.charCodeAt(r + 1) === 120 && t.charCodeAt(r + 2) === 112 && t.charCodeAt(r + 3) === 111 && t.charCodeAt(r + 4) === 114 && t.charCodeAt(r + 5) === 116) {
110
110
  let v = t.charCodeAt(r + 6);
111
111
  if (w(v)) {
112
112
  let u = r + 7;
113
- for (;u < n && w(t.charCodeAt(u)); )
113
+ for (;u < o && w(t.charCodeAt(u)); )
114
114
  u++;
115
115
  b[t.charCodeAt(u)] && (i = r + 6, r = u);
116
116
  } else
117
117
  s = t.charCodeAt(r);
118
118
  }
119
119
  let l = r, d = 0;
120
- for (;r < n && (d = t.charCodeAt(r), b[d]); )
120
+ for (;r < o && (d = t.charCodeAt(r), b[d]); )
121
121
  r++;
122
122
  if (r === l) {
123
- for (;r < n && !O(t.charCodeAt(r)); )
123
+ for (;r < o && !q(t.charCodeAt(r)); )
124
124
  r++;
125
125
  continue;
126
126
  }
127
- let p = t.slice(l, r), f = r;
128
- if (r >= n && (d = 0), w(d))
127
+ let h = t.slice(l, r), f = r;
128
+ if (r >= o && (d = 0), w(d))
129
129
  do
130
- r++, d = r < n ? t.charCodeAt(r) : 0;
130
+ r++, d = r < o ? t.charCodeAt(r) : 0;
131
131
  while (w(d));
132
132
  if (d === 61)
133
133
  r++;
134
- else if (d === 58 && r === f && r + 1 < n && w(t.charCodeAt(r + 1)))
134
+ else if (d === 58 && r === f && r + 1 < o && w(t.charCodeAt(r + 1)))
135
135
  r += 2;
136
136
  else {
137
- for (r = i === -1 ? f : i;r < n && !O(t.charCodeAt(r)); )
137
+ for (r = i === -1 ? f : i;r < o && !q(t.charCodeAt(r)); )
138
138
  r++;
139
139
  continue;
140
140
  }
141
141
  let c = r, a = r;
142
- for (;a < n && w(t.charCodeAt(a)); )
142
+ for (;a < o && w(t.charCodeAt(a)); )
143
143
  a++;
144
- let g = t.charCodeAt(a), h, y = false;
144
+ let g = t.charCodeAt(a), p, y = false;
145
145
  if (g === 39 || g === 34 || g === 96) {
146
146
  let v = t[a], u = t.indexOf(v, a + 1), m = -1, C = -1;
147
147
  for (;u !== -1; ) {
148
- let q = t.charCodeAt(u - 1) === 92, E = u + 1;
149
- for (;E < n && !O(t.charCodeAt(E)) && w(t.charCodeAt(E)); )
148
+ let O = t.charCodeAt(u - 1) === 92, E = u + 1;
149
+ for (;E < o && !q(t.charCodeAt(E)) && w(t.charCodeAt(E)); )
150
150
  E++;
151
- if ((E === n || O(t.charCodeAt(E)) || t.charCodeAt(E) === 35) && (m = u, C = E), !q)
151
+ if ((E === o || q(t.charCodeAt(E)) || t.charCodeAt(E) === 35) && (m = u, C = E), !O)
152
152
  break;
153
153
  u = t.indexOf(v, u + 1);
154
154
  }
155
155
  if (m !== -1) {
156
- if (h = t.slice(a + 1, m), r = C, t.charCodeAt(r) === 35)
157
- for (;r < n && !O(t.charCodeAt(r)); )
156
+ if (p = t.slice(a + 1, m), r = C, t.charCodeAt(r) === 35)
157
+ for (;r < o && !q(t.charCodeAt(r)); )
158
158
  r++;
159
159
  y = true;
160
160
  }
@@ -162,9 +162,9 @@ var require_dist = __commonJS(function(exports, module) {
162
162
  if (!y) {
163
163
  let v = t.indexOf(`
164
164
  `, c);
165
- v === -1 && (v = n);
165
+ v === -1 && (v = o);
166
166
  let u;
167
- if (n < 4096)
167
+ if (o < 4096)
168
168
  u = t.indexOf("#", c), (u === -1 || u > v) && (u = v);
169
169
  else
170
170
  for (u = c;u < v && t.charCodeAt(u) !== 35; )
@@ -174,38 +174,41 @@ var require_dist = __commonJS(function(exports, module) {
174
174
  m++;
175
175
  for (;C > m && w(t.charCodeAt(C - 1)); )
176
176
  C--;
177
- let q = t.charCodeAt(m);
178
- if (C - m >= 2 && (q === 39 || q === 34 || q === 96) && t.charCodeAt(C - 1) === q ? h = t.slice(m + 1, C - 1) : h = t.slice(m, C), r = u, u < v)
179
- for (;r < n && !O(t.charCodeAt(r)); )
177
+ let O = t.charCodeAt(m);
178
+ if (p = t.slice(m, C), p.includes("\u2028") || p.includes("\u2029") ? p = p.replace(/^(['"`])([\s\S]*)\1$/mg, "$2") : C - m >= 2 && (O === 39 || O === 34 || O === 96) && t.charCodeAt(C - 1) === O && (p = t.slice(m + 1, C - 1)), r = u, u < v)
179
+ for (;r < o && !q(t.charCodeAt(r)); )
180
180
  r++;
181
181
  }
182
- g === 34 && (y || a < r) && h.indexOf("\\") !== -1 && (h = h.replace(/\\n/g, `
183
- `).replace(/\\r/g, "\r")), o[p] = h;
182
+ g === 34 && (y || a < r) && p.indexOf("\\") !== -1 && (p = p.replace(/\\n/g, `
183
+ `).replace(/\\r/g, "\r")), n[h] = p;
184
184
  }
185
- return o;
185
+ return n;
186
186
  }
187
- function ne(e, o) {
188
- return o && $(o.fast) ? oe(e) : re(e);
187
+ function se(e, n) {
188
+ return n && $(n.fast) ? oe(e) : ne(e);
189
189
  }
190
190
  function T(e) {
191
191
  console.log(`\u2506 ${e}`);
192
192
  }
193
- function se(e) {
193
+ function ie(e) {
194
194
  console.error(`\u25C7 ${e}`);
195
195
  }
196
196
  function V(e) {
197
- return e[0] === "~" ? k.join(z.homedir(), e.slice(1)) : e;
197
+ return e[0] === "~" ? I.join(Z.homedir(), e.slice(1)) : e;
198
198
  }
199
- function ie(e = {}) {
200
- return { ...B(), ...e };
199
+ function ce(e = {}) {
200
+ let n = { ...e };
201
+ for (let t of Object.keys(n))
202
+ n[t] === undefined && delete n[t];
203
+ return { ...B(), ...n };
201
204
  }
202
- function ce(e) {
203
- e = ie(e);
204
- let o = k.resolve(process.cwd(), ".env"), t = "utf8", n = process.env;
205
- e && e.processEnv != null && (n = e.processEnv);
205
+ function ae(e) {
206
+ e = ce(e);
207
+ let n = I.resolve(process.cwd(), ".env"), t = "utf8", o = process.env;
208
+ e && e.processEnv != null && (o = e.processEnv);
206
209
  let r = $(e && e.debug);
207
210
  e && e.encoding ? t = e.encoding : r && T("no encoding is specified (UTF-8 is used by default)");
208
- let s = [o];
211
+ let s = [n];
209
212
  if (e && e.path)
210
213
  if (!Array.isArray(e.path))
211
214
  s = [V(e.path)];
@@ -217,116 +220,116 @@ var require_dist = __commonJS(function(exports, module) {
217
220
  let i, l = {}, d = { fast: e.fast };
218
221
  for (let c of s)
219
222
  try {
220
- let a = A.parse(Y.readFileSync(c, { encoding: t }), d);
223
+ let a = A.parse(z.readFileSync(c, { encoding: t }), d);
221
224
  A.populate(l, a, e);
222
225
  } catch (a) {
223
226
  r && T(`failed to load ${c} ${a.message}`), i = a;
224
227
  }
225
- let p = A.populate(n, l, e), f = $(Object.prototype.hasOwnProperty.call(e, "quiet") ? e.quiet : B(n).quiet);
228
+ let h = A.populate(o, l, e), f = $(Object.prototype.hasOwnProperty.call(e, "quiet") ? e.quiet : B(o).quiet);
226
229
  if (r || !f) {
227
- let c = Object.keys(p).length, a = [];
230
+ let c = Object.keys(h).length, a = [];
228
231
  for (let g of s)
229
232
  try {
230
- let h = k.relative(process.cwd(), g instanceof Z ? ee(g) : g);
231
- a.push(h);
232
- } catch (h) {
233
- r && T(`failed to load ${g} ${h.message}`), i = h;
233
+ let p = I.relative(process.cwd(), g instanceof ee ? te(g) : g);
234
+ a.push(p);
235
+ } catch (p) {
236
+ r && T(`failed to load ${g} ${p.message}`), i = p;
234
237
  }
235
- se(`injected env (${c}) from ${a.join(",")}`);
238
+ ie(`injected env (${c}) from ${a.join(",")}`);
236
239
  }
237
240
  return i ? { parsed: l, error: i } : { parsed: l };
238
241
  }
239
- function ae(e) {
242
+ function le(e) {
240
243
  return A.configDotenv(e);
241
244
  }
242
- function le(e, o, t = {}) {
243
- let n = $(t && t.debug), r = $(t && t.override), s = {};
244
- if (e === null || typeof e != "object" || o === null || typeof o != "object") {
245
+ function fe(e, n, t = {}) {
246
+ let o = $(t && t.debug), r = $(t && t.override), s = {};
247
+ if (e === null || typeof e != "object" || n === null || typeof n != "object") {
245
248
  let i = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
246
249
  throw i.code = "OBJECT_REQUIRED", i;
247
250
  }
248
- for (let i of Object.keys(o))
249
- Object.prototype.hasOwnProperty.call(e, i) ? (r === true && (e[i] = o[i], s[i] = o[i]), n && T(r === true ? `"${i}" is already defined and WAS overwritten` : `"${i}" is already defined and was NOT overwritten`)) : (e[i] = o[i], s[i] = o[i]);
251
+ for (let i of Object.keys(n))
252
+ Object.prototype.hasOwnProperty.call(e, i) ? (r === true && (e[i] = n[i], s[i] = n[i]), o && T(r === true ? `"${i}" is already defined and WAS overwritten` : `"${i}" is already defined and was NOT overwritten`)) : (e[i] = n[i], s[i] = n[i]);
250
253
  return s;
251
254
  }
252
- var A = { configDotenv: ce, config: ae, parse: ne, populate: le };
255
+ var A = { configDotenv: ae, config: le, parse: se, populate: fe };
253
256
  x.exports.configDotenv = A.configDotenv;
254
257
  x.exports.config = A.config;
255
258
  x.exports.parse = A.parse;
256
259
  x.exports.populate = A.populate;
257
260
  x.exports = A;
258
261
  });
259
- var M = S((Te, W) => {
260
- var _ = __require("child_process"), fe = __require("fs"), L = __require("path");
261
- function ue(e) {
262
- let o = ['"'], t = 0;
263
- for (let n of e) {
264
- if (n === "\\") {
262
+ var M = j((Te, W) => {
263
+ var _ = __require("child_process"), ue = __require("fs"), L = __require("path");
264
+ function de(e) {
265
+ let n = ['"'], t = 0;
266
+ for (let o of e) {
267
+ if (o === "\\") {
265
268
  t++;
266
269
  continue;
267
270
  }
268
- n === '"' ? o.push("\\".repeat(t * 2 + 1), '"') : o.push("\\".repeat(t), n), t = 0;
271
+ o === '"' ? n.push("\\".repeat(t * 2 + 1), '"') : n.push("\\".repeat(t), o), t = 0;
269
272
  }
270
- return o.push("\\".repeat(t * 2), '"'), o.join("");
273
+ return n.push("\\".repeat(t * 2), '"'), n.join("");
271
274
  }
272
- function H(e, o = 1) {
273
- for (let t = 0;t < o; t++) {
274
- let n = [];
275
+ function H(e, n = 1) {
276
+ for (let t = 0;t < n; t++) {
277
+ let o = [];
275
278
  for (let r of e) {
276
279
  let s = r.charCodeAt(0), i = s >= 48 && s <= 57 || s >= 65 && s <= 90 || s >= 97 && s <= 122, l = "\\/:._-".includes(r);
277
- !i && !l && s < 128 && n.push("^"), n.push(r);
280
+ !i && !l && s < 128 && o.push("^"), o.push(r);
278
281
  }
279
- e = n.join("");
282
+ e = o.join("");
280
283
  }
281
284
  return e;
282
285
  }
283
- function P(e, o) {
284
- let t = Object.keys(e).reverse().find((n) => n.toUpperCase() === o);
286
+ function P(e, n) {
287
+ let t = Object.keys(e).reverse().find((o) => o.toUpperCase() === n);
285
288
  return t === undefined ? undefined : e[t];
286
289
  }
287
- function de(e, o, t) {
288
- let n = (P(o, "PATHEXT") || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean), s = n.some((l) => e.toLowerCase().endsWith(l.toLowerCase())) ? ["", ...n] : [...n, ""], i = /[\\/]/.test(e) ? [t] : [t, ...(P(o, "PATH") || "").split(";")];
290
+ function pe(e, n, t) {
291
+ let o = (P(n, "PATHEXT") || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean), s = o.some((l) => e.toLowerCase().endsWith(l.toLowerCase())) ? ["", ...o] : [...o, ""], i = /[\\/]/.test(e) ? [t] : [t, ...(P(n, "PATH") || "").split(";")];
289
292
  for (let l of i)
290
293
  for (let d of s) {
291
- let p = L.resolve(t, l.replace(/^"|"$/g, ""), e + d);
294
+ let h = L.resolve(t, l.replace(/^"|"$/g, ""), e + d);
292
295
  try {
293
- if (fe.statSync(p).isFile())
294
- return p;
296
+ if (ue.statSync(h).isFile())
297
+ return h;
295
298
  } catch {}
296
299
  }
297
300
  }
298
- function pe(e, o, t) {
301
+ function he(e, n, t) {
299
302
  if (process.platform !== "win32")
300
- return _.spawn(e, o, t);
301
- let n = t.env || process.env, r = de(e, n, t.cwd || process.cwd());
303
+ return _.spawn(e, n, t);
304
+ let o = t.env || process.env, r = pe(e, o, t.cwd || process.cwd());
302
305
  if (r && /\.(?:exe|com)$/i.test(r))
303
- return _.spawn(r, o, t);
306
+ return _.spawn(r, n, t);
304
307
  let s = /\.(?:bat|cmd)$/i.test(r || e), i = [H(L.normalize(r || e))];
305
- for (let d of o)
306
- i.push(H(ue(d), s ? 2 : 1));
308
+ for (let d of n)
309
+ i.push(H(de(d), s ? 2 : 1));
307
310
  let l = i.join(" ");
308
- return _.spawn(P(n, "COMSPEC") || "cmd.exe", ["/d", "/v:off", "/s", "/c", `"${l}"`], { ...t, windowsVerbatimArguments: true });
311
+ return _.spawn(P(o, "COMSPEC") || "cmd.exe", ["/d", "/v:off", "/s", "/c", `"${l}"`], { ...t, windowsVerbatimArguments: true });
309
312
  }
310
- W.exports = pe;
313
+ W.exports = he;
311
314
  });
312
- var K = S(($e, F) => {
313
- var he = __require("fs"), ge = __require("os"), Q = __require("path"), me = __require("child_process"), ve = M(), R = N(), { optionsFromEnv: Ce } = j();
315
+ var X = j(($e, F) => {
316
+ var ge = __require("fs"), me = __require("os"), Q = __require("path"), ve = __require("child_process"), Ce = M(), R = N(), { optionsFromEnv: J } = k();
314
317
  function D() {
315
318
  console.log(["Usage: dotenv run [--help] [-q|--quiet] [--debug] [--override] [--fast] [-f|--file <paths>] [--] <command> [args...]", "", "Run a command with environment variables from a .env file.", "Place dotenv options before the command; all following arguments go to the command.", "", "Options:", " -f, --file <paths> .env paths, comma-separated or repeated (default: .env)", " -q, --quiet suppress the injected env message", " --debug enable debug logging", " --override override existing environment variables", " --fast use the faster character-scanner parser", "", "Environment variables (DOTENV_CONFIG_* names remain as fallbacks):", " DOTENV_PATH, DOTENV_ENCODING, DOTENV_QUIET,", " DOTENV_DEBUG, DOTENV_OVERRIDE,", " DOTENV_FAST"].join(`
316
319
  `));
317
320
  }
318
321
  function we(e) {
319
- let o = [], t = false, n, r, s, i, l = -1;
320
- for (let p = 0;p < e.length; p++) {
321
- let f = e[p];
322
+ let n = [], t = false, o, r, s, i, l = -1;
323
+ for (let h = 0;h < e.length; h++) {
324
+ let f = e[h];
322
325
  if (f === "--") {
323
- l = p + 1;
326
+ l = h + 1;
324
327
  break;
325
328
  }
326
329
  if (f === "--help" || f === "-h")
327
330
  return { help: true };
328
331
  if (f === "--quiet" || f === "-q") {
329
- n = true;
332
+ o = true;
330
333
  continue;
331
334
  }
332
335
  if (f === "--debug") {
@@ -342,51 +345,51 @@ var require_dist = __commonJS(function(exports, module) {
342
345
  continue;
343
346
  }
344
347
  if (f === "-f" || f === "--file" || f.startsWith("-f=") || f.startsWith("--file=")) {
345
- let c = f.indexOf("="), a = c === -1 ? f : f.slice(0, c), g = c === -1 ? e[++p] : f.slice(c + 1);
348
+ let c = f.indexOf("="), a = c === -1 ? f : f.slice(0, c), g = c === -1 ? e[++h] : f.slice(c + 1);
346
349
  if (!g || g === "--")
347
350
  return { error: `${a} requires a path` };
348
- let h = g.split(",").map((y) => y.trim()).filter(Boolean);
349
- if (h.length === 0)
351
+ let p = g.split(",").map((y) => y.trim()).filter(Boolean);
352
+ if (p.length === 0)
350
353
  return { error: `${a} requires a path` };
351
- o.push(...h), t = true;
354
+ n.push(...p), t = true;
352
355
  continue;
353
356
  }
354
357
  if (f.startsWith("-"))
355
358
  return { error: `unknown option: ${f}` };
356
- l = p;
359
+ l = h;
357
360
  break;
358
361
  }
359
362
  let d = l === -1 ? [] : e.slice(l);
360
- return { paths: o, pathSet: t, quiet: n, debug: r, override: s, fast: i, command: d };
363
+ return { paths: n, pathSet: t, quiet: o, debug: r, override: s, fast: i, command: d };
361
364
  }
362
365
  function Ae(e) {
363
- return e[0] === "~" ? Q.join(ge.homedir(), e.slice(1)) : e;
366
+ return e[0] === "~" ? Q.join(me.homedir(), e.slice(1)) : e;
364
367
  }
365
368
  function Ee(e) {
366
- let o = Ce(), t = { encoding: o.encoding || "utf8", quiet: o.quiet === true, debug: o.debug === true, override: o.override === true, fast: o.fast === true, paths: [".env"], defaultPath: true };
367
- return o.path != null && (t.paths = [o.path], t.defaultPath = false), e.pathSet && (t.paths = e.paths, t.defaultPath = false), e.quiet != null && (t.quiet = e.quiet), e.debug != null && (t.debug = e.debug), e.override != null && (t.override = e.override), e.fast != null && (t.fast = e.fast), t;
369
+ let n = J(), t = { encoding: n.encoding || "utf8", quiet: n.quiet, debug: n.debug === true, override: n.override === true, fast: n.fast === true, paths: [".env"], defaultPath: true };
370
+ return n.path != null && (t.paths = [n.path], t.defaultPath = false), e.pathSet && (t.paths = e.paths, t.defaultPath = false), e.quiet != null && (t.quiet = e.quiet), e.debug != null && (t.debug = e.debug), e.override != null && (t.override = e.override), e.fast != null && (t.fast = e.fast), t;
368
371
  }
369
372
  function be(e) {
370
- let o = {}, t = [], n = { override: e.override, debug: e.debug };
373
+ let n = {}, t = [], o = { override: e.override, debug: e.debug };
371
374
  for (let s of e.paths) {
372
375
  let i = Q.resolve(process.cwd(), Ae(s));
373
376
  try {
374
- let l = R.parse(he.readFileSync(i, { encoding: e.encoding }), { fast: e.fast });
375
- R.populate(o, l, n), t.push(s);
377
+ let l = R.parse(ge.readFileSync(i, { encoding: e.encoding }), { fast: e.fast });
378
+ R.populate(n, l, o), t.push(s);
376
379
  } catch (l) {
377
380
  if (e.debug && console.log(`\u2506 failed to load ${s} ${l.message}`), !(e.defaultPath && l.code === "ENOENT"))
378
381
  throw l;
379
382
  }
380
383
  }
381
- return { injected: R.populate(process.env, o, n), loadedPaths: t };
384
+ return { injected: R.populate(process.env, n, o), loadedPaths: t };
382
385
  }
383
- function J(e) {
384
- let o = e[0];
385
- if (o === "--help" || o === "-h") {
386
+ function K(e) {
387
+ let n = e[0];
388
+ if (n === "--help" || n === "-h") {
386
389
  D();
387
390
  return;
388
391
  }
389
- if (o !== "run") {
392
+ if (n !== "run") {
390
393
  D(), process.exitCode = 1;
391
394
  return;
392
395
  }
@@ -403,22 +406,22 @@ var require_dist = __commonJS(function(exports, module) {
403
406
  D(), process.exitCode = 1;
404
407
  return;
405
408
  }
406
- let n = Ee(t);
409
+ let o = Ee(t);
407
410
  try {
408
- let c = be(n);
409
- if (!n.quiet) {
410
- let a = `\u25C7 injected env (${Object.keys(c.injected).length})`;
411
- c.loadedPaths.length > 0 && (a += ` from ${c.loadedPaths.join(", ")}`), console.error(a);
411
+ let c = be(o);
412
+ if (!(o.quiet == null ? J().quiet : o.quiet)) {
413
+ let g = `\u25C7 injected env (${Object.keys(c.injected).length})`;
414
+ c.loadedPaths.length > 0 && (g += ` from ${c.loadedPaths.join(", ")}`), console.error(g);
412
415
  }
413
416
  } catch (c) {
414
417
  console.error(`dotenv: ${c.message}`), process.exitCode = 1;
415
418
  return;
416
419
  }
417
- let r = !!process.stdin.isTTY, s = process.platform !== "win32" && !r, i = ve(t.command[0], t.command.slice(1), { stdio: "inherit", detached: s }), l = new Map, d = 0;
418
- function p(c) {
420
+ let r = !!process.stdin.isTTY, s = process.platform !== "win32" && !r, i = Ce(t.command[0], t.command.slice(1), { stdio: "inherit", detached: s }), l = new Map, d = 0;
421
+ function h(c) {
419
422
  if (!(!i.pid || i.exitCode !== null || i.signalCode !== null)) {
420
423
  if (process.platform === "win32") {
421
- me.spawnSync("taskkill", ["/pid", String(i.pid), "/T", "/F"], { stdio: "ignore" });
424
+ ve.spawnSync("taskkill", ["/pid", String(i.pid), "/T", "/F"], { stdio: "ignore" });
422
425
  return;
423
426
  }
424
427
  try {
@@ -439,11 +442,11 @@ var require_dist = __commonJS(function(exports, module) {
439
442
  if (d++, r && process.platform !== "win32" && d === 1)
440
443
  return;
441
444
  if (d > 1) {
442
- p(d === 2 ? "SIGTERM" : "SIGKILL");
445
+ h(d === 2 ? "SIGTERM" : "SIGKILL");
443
446
  return;
444
447
  }
445
448
  }
446
- p(c);
449
+ h(c);
447
450
  };
448
451
  l.set(c, a), process.on(c, a);
449
452
  }
@@ -453,16 +456,16 @@ var require_dist = __commonJS(function(exports, module) {
453
456
  f(), typeof c == "number" ? process.exit(c) : (setInterval(() => {}, 1000), process.kill(process.pid, a));
454
457
  });
455
458
  }
456
- F.exports = J;
457
- F === __require.main && J(process.argv.slice(2));
459
+ F.exports = K;
460
+ F === __require.main && K(process.argv.slice(2));
458
461
  });
459
- var I = N();
460
- var Oe = K();
461
- module.exports = I;
462
- module.exports.config = I.config;
463
- module.exports.configDotenv = I.configDotenv;
464
- module.exports.parse = I.parse;
465
- module.exports.populate = I.populate;
462
+ var S = N();
463
+ var qe = X();
464
+ module.exports = S;
465
+ module.exports.config = S.config;
466
+ module.exports.configDotenv = S.configDotenv;
467
+ module.exports.parse = S.parse;
468
+ module.exports.populate = S.populate;
466
469
  });
467
470
 
468
471
  // src/load-env.ts
@@ -509,7 +512,7 @@ var createPkcePair = () => {
509
512
  verifier
510
513
  };
511
514
  };
512
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/util.js
515
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/util.js
513
516
  function getEnumValues(entries) {
514
517
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
515
518
  const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
@@ -855,6 +858,18 @@ function prefixIssues(path, issues) {
855
858
  function unwrapMessage(message) {
856
859
  return typeof message === "string" ? message : message?.message;
857
860
  }
861
+ function sizeCaps(defChecks) {
862
+ const caps = [];
863
+ for (const ch of defChecks ?? []) {
864
+ const d = ch._zod.def;
865
+ if (!/_(length|size)$|_equals$/.test(d.check))
866
+ break;
867
+ const max = d.maximum ?? d.length ?? d.size;
868
+ if (max >= 0)
869
+ caps.push([max, ch]);
870
+ }
871
+ return caps;
872
+ }
858
873
  function attachSchema(issues, start, inst) {
859
874
  var _a;
860
875
  for (let i = start;i < issues.length; i++) {
@@ -1052,7 +1067,7 @@ function constantCatch(value) {
1052
1067
  return fn;
1053
1068
  }
1054
1069
 
1055
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/core.js
1070
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/core.js
1056
1071
  var _a;
1057
1072
  var _zodDesc = { value: undefined, enumerable: false };
1058
1073
  var _E = "captureStackTrace" in Error ? Error : null;
@@ -1170,7 +1185,7 @@ function config2(newConfig) {
1170
1185
  Object.assign(globalConfig, newConfig);
1171
1186
  return globalConfig;
1172
1187
  }
1173
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/errors.js
1188
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/errors.js
1174
1189
  function _getMessage() {
1175
1190
  const internals = this._zod;
1176
1191
  internals.message ?? (internals.message = JSON.stringify(internals.def, jsonStringifyReplacer, 2));
@@ -1285,7 +1300,7 @@ function formatError(error, mapper = (issue) => issue.message) {
1285
1300
  return fieldErrors;
1286
1301
  }
1287
1302
 
1288
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/parse.js
1303
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/parse.js
1289
1304
  function finalizeParams(callee, params) {
1290
1305
  return { callee: params?.callee ?? callee, Err: params?.Err };
1291
1306
  }
@@ -1432,7 +1447,7 @@ var _safeEncodeAsync = (_Err) => async (schema, value, _ctx) => {
1432
1447
  var _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
1433
1448
  return _safeParseAsync(_Err)(schema, value, _ctx);
1434
1449
  };
1435
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/regexes.js
1450
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/regexes.js
1436
1451
  var cuid = /^[cC][0-9a-z]{6,}$/;
1437
1452
  var cuid2 = /^[0-9a-z]+$/;
1438
1453
  var ulid = /^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/;
@@ -1490,7 +1505,7 @@ var _null = /^null$/i;
1490
1505
  var lowercase = /^[^A-Z]*$/;
1491
1506
  var uppercase = /^[^a-z]*$/;
1492
1507
 
1493
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/checks.js
1508
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/checks.js
1494
1509
  var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => {
1495
1510
  var _a;
1496
1511
  inst._zod ?? (inst._zod = {});
@@ -1806,7 +1821,7 @@ var $ZodCheckOverwrite = /* @__PURE__ */ $constructor("$ZodCheckOverwrite", (ins
1806
1821
  };
1807
1822
  });
1808
1823
 
1809
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/doc.js
1824
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/doc.js
1810
1825
  class Doc {
1811
1826
  constructor(args = [], closed = {}) {
1812
1827
  this.content = [];
@@ -1848,14 +1863,14 @@ ${content.join(`
1848
1863
  }
1849
1864
  }
1850
1865
 
1851
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/versions.js
1866
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/versions.js
1852
1867
  var version = {
1853
1868
  major: 4,
1854
1869
  minor: 6,
1855
- patch: 5
1870
+ patch: 7
1856
1871
  };
1857
1872
 
1858
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/schemas.js
1873
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/schemas.js
1859
1874
  var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
1860
1875
  var _a;
1861
1876
  inst ?? (inst = {});
@@ -2421,6 +2436,29 @@ var $ZodNever = /* @__PURE__ */ $constructor("$ZodNever", (inst, def) => {
2421
2436
  return payload;
2422
2437
  };
2423
2438
  });
2439
+ function capSize(inst, size) {
2440
+ const caps = sizeCaps(inst._zod.def.checks);
2441
+ if (!caps.length)
2442
+ return;
2443
+ const cap = Math.min(...caps.map((c) => c[0]));
2444
+ const parse = inst._zod.parse;
2445
+ inst._zod.parse = (payload, ctx) => {
2446
+ const n = size(payload.value);
2447
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
2448
+ for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
2449
+ if (n > max && ch._zod.def.when(payload)) {
2450
+ const start = payload.issues.length;
2451
+ ch._zod.check(payload);
2452
+ payload.issues[start].continue = false;
2453
+ attachSchema(payload.issues, start, inst);
2454
+ return payload;
2455
+ }
2456
+ }
2457
+ }
2458
+ return parse(payload, ctx);
2459
+ };
2460
+ }
2461
+ var arraySize = (input) => Array.isArray(input) ? input.length : -1;
2424
2462
  function handleArrayResult(result, final, index) {
2425
2463
  if (result.issues.length) {
2426
2464
  final.issues.push(...prefixIssues(index, result.issues));
@@ -2464,6 +2502,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
2464
2502
  }
2465
2503
  return payload;
2466
2504
  };
2505
+ capSize(inst, arraySize);
2467
2506
  });
2468
2507
  function handlePropertyResult(result, final, key, input, optin, optout) {
2469
2508
  const isPresent = key in input;
@@ -3246,7 +3285,7 @@ function handleRefineResult(result, payload, input, inst) {
3246
3285
  payload.issues.push(issue(_iss));
3247
3286
  }
3248
3287
  }
3249
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/memoizer.js
3288
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/memoizer.js
3250
3289
  class $ZodCyclicError extends Error {
3251
3290
  constructor() {
3252
3291
  super(`Cannot parse a reference cycle that closes through a transform`);
@@ -3515,7 +3554,7 @@ function isBackEdge(ctx, value) {
3515
3554
  const backEdges = ctx[STATE]?.backEdges;
3516
3555
  return backEdges !== undefined && isRef(value) && backEdges.has(value);
3517
3556
  }
3518
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/locales/en.js
3557
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/locales/en.js
3519
3558
  var error = () => {
3520
3559
  const Sizable = {
3521
3560
  string: { unit: "characters", verb: "to have" },
@@ -3637,7 +3676,7 @@ function en_default() {
3637
3676
  localeError: error()
3638
3677
  };
3639
3678
  }
3640
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/registries.js
3679
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/registries.js
3641
3680
  var _a2;
3642
3681
  class $ZodRegistry {
3643
3682
  constructor() {
@@ -3684,7 +3723,7 @@ function registry() {
3684
3723
  }
3685
3724
  (_a2 = globalThis).__zod_globalRegistry ?? (_a2.__zod_globalRegistry = registry());
3686
3725
  var globalRegistry = globalThis.__zod_globalRegistry;
3687
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/api.js
3726
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/api.js
3688
3727
  function snapshotChecks(def) {
3689
3728
  if (def.checks)
3690
3729
  def.checks = [...def.checks];
@@ -4138,7 +4177,7 @@ function _check(fn, params) {
4138
4177
  ch._zod.check = fn;
4139
4178
  return ch;
4140
4179
  }
4141
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/to-json-schema.js
4180
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/to-json-schema.js
4142
4181
  function assignProps(target, ...sources) {
4143
4182
  for (const source of sources) {
4144
4183
  for (const key of Reflect.ownKeys(source)) {
@@ -4661,7 +4700,7 @@ var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) =
4661
4700
  extractDefs(ctx, schema);
4662
4701
  return finalize(ctx, schema);
4663
4702
  };
4664
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/json-schema-processors.js
4703
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/core/json-schema-processors.js
4665
4704
  var narrowMin = (agg, key, value) => {
4666
4705
  if (agg[key] === undefined || value > agg[key])
4667
4706
  agg[key] = value;
@@ -5090,7 +5129,7 @@ var optionalProcessor = (schema, ctx, _json, params) => {
5090
5129
  const seen = ctx.seen.get(schema);
5091
5130
  seen.ref = def.innerType;
5092
5131
  };
5093
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/classic/errors.js
5132
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/classic/errors.js
5094
5133
  var _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);
5095
5134
  function _lazyMethod(proto, key, make) {
5096
5135
  Object.defineProperty(proto, key, {
@@ -5135,7 +5174,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, undefi
5135
5174
  Parent: Error
5136
5175
  });
5137
5176
 
5138
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/classic/parse.js
5177
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/classic/parse.js
5139
5178
  var parse2 = /* @__PURE__ */ _parse(ZodRealError);
5140
5179
  var parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);
5141
5180
  var safeParse = /* @__PURE__ */ _safeParse(ZodRealError);
@@ -5149,7 +5188,7 @@ var safeDecode = /* @__PURE__ */ _safeDecode(ZodRealError);
5149
5188
  var safeEncodeAsync = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);
5150
5189
  var safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);
5151
5190
 
5152
- // ../../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/classic/schemas.js
5191
+ // ../../../node_modules/.bun/zod@4.6.7/node_modules/zod/v4/classic/schemas.js
5153
5192
  function _ensureDefaultLocale() {
5154
5193
  if (!globalConfig.localeError)
5155
5194
  config2(en_default());
@@ -6716,6 +6755,155 @@ var logoutCliSession = async ({
6716
6755
  // src/cli.ts
6717
6756
  import { readFileSync } from "fs";
6718
6757
 
6758
+ // ../utils/src/data-table-sort-keys.ts
6759
+ var INVENTORY_SORT_IDS = [
6760
+ "itemCategory",
6761
+ "category",
6762
+ "supplier",
6763
+ "cost",
6764
+ "name",
6765
+ "createdAt",
6766
+ "updatedAt"
6767
+ ];
6768
+ var ITEM_CATEGORY_SORT_IDS = [
6769
+ "name",
6770
+ "itemCount",
6771
+ "createdAt",
6772
+ "createdBy"
6773
+ ];
6774
+
6775
+ // ../db/src/generated/prisma/enums.ts
6776
+ var EstimationMode = {
6777
+ MICRO: "MICRO",
6778
+ MANPOWER_SPECIFIED: "MANPOWER_SPECIFIED"
6779
+ };
6780
+ var ProjectStatus = {
6781
+ PITCH: "PITCH",
6782
+ WON: "WON",
6783
+ COMPLETED: "COMPLETED",
6784
+ RECONCILED: "RECONCILED",
6785
+ LOST: "LOST"
6786
+ };
6787
+ var BudgetStatus = {
6788
+ DRAFT: "DRAFT",
6789
+ PENDING_APPROVAL: "PENDING_APPROVAL",
6790
+ APPROVED: "APPROVED",
6791
+ REJECTED: "REJECTED",
6792
+ ESTIMATE_CREATED: "ESTIMATE_CREATED",
6793
+ ESTIMATE_SENT: "ESTIMATE_SENT",
6794
+ ESTIMATE_ACCEPTED: "ESTIMATE_ACCEPTED",
6795
+ ESTIMATE_REJECTED: "ESTIMATE_REJECTED",
6796
+ ESTIMATE_CLOSED: "ESTIMATE_CLOSED"
6797
+ };
6798
+ var ApprovalStatus = {
6799
+ PENDING_APPROVAL: "PENDING_APPROVAL",
6800
+ APPROVED: "APPROVED",
6801
+ REJECTED: "REJECTED",
6802
+ SUPERSEDED: "SUPERSEDED"
6803
+ };
6804
+ var ApprovalType = {
6805
+ BUDGET: "BUDGET",
6806
+ SUPPLIER: "SUPPLIER",
6807
+ BILL: "BILL",
6808
+ CUSTOMER_INVOICE: "CUSTOMER_INVOICE",
6809
+ QUOTATION: "QUOTATION"
6810
+ };
6811
+ var ErrorSeverity = {
6812
+ LOW: "LOW",
6813
+ MEDIUM: "MEDIUM",
6814
+ HIGH: "HIGH",
6815
+ CRITICAL: "CRITICAL"
6816
+ };
6817
+ var ErrorStatus = {
6818
+ UNRESOLVED: "UNRESOLVED",
6819
+ INVESTIGATING: "INVESTIGATING",
6820
+ RESOLVED: "RESOLVED",
6821
+ IGNORED: "IGNORED"
6822
+ };
6823
+ var CustomerInvoiceBatchStatus = {
6824
+ CREATING: "CREATING",
6825
+ PENDING_APPROVAL: "PENDING_APPROVAL",
6826
+ APPROVED: "APPROVED",
6827
+ REJECTED_VOIDED: "REJECTED_VOIDED",
6828
+ EXPIRED_VOIDED: "EXPIRED_VOIDED",
6829
+ PARTIAL_QBO_FAILURE: "PARTIAL_QBO_FAILURE"
6830
+ };
6831
+ var CustomerInvoiceStatus = {
6832
+ CREATED_IN_QBO: "CREATED_IN_QBO",
6833
+ APPROVED: "APPROVED",
6834
+ SENT: "SENT",
6835
+ PAID: "PAID",
6836
+ VOIDED: "VOIDED",
6837
+ QBO_SYNC_ERROR: "QBO_SYNC_ERROR"
6838
+ };
6839
+ var BillStatus = {
6840
+ DRAFT: "DRAFT",
6841
+ PENDING_APPROVAL: "PENDING_APPROVAL",
6842
+ CHECKED: "CHECKED",
6843
+ APPROVED: "APPROVED",
6844
+ PAID: "PAID",
6845
+ REJECTED: "REJECTED"
6846
+ };
6847
+
6848
+ // ../utils/src/filter-enums.ts
6849
+ var ExtendedApprovalStatus = {
6850
+ ...ApprovalStatus,
6851
+ ALL: "ALL"
6852
+ };
6853
+ var ExtendedApprovalType = {
6854
+ ...ApprovalType,
6855
+ ALL: "ALL"
6856
+ };
6857
+ var ExtendedProjectStatus = {
6858
+ ...ProjectStatus,
6859
+ ALL: "ALL"
6860
+ };
6861
+ var ExtendedBudgetStatus = {
6862
+ ...BudgetStatus,
6863
+ ALL: "ALL"
6864
+ };
6865
+ var ExtendedBillStatus = {
6866
+ ...BillStatus,
6867
+ ALL: "ALL"
6868
+ };
6869
+ var ExtendedEstimationMode = {
6870
+ ...EstimationMode,
6871
+ ALL: "ALL"
6872
+ };
6873
+ var BudgetRole = {
6874
+ ALL: "ALL",
6875
+ BD: "BD",
6876
+ CREATOR: "CREATOR",
6877
+ INSIDE_SALES: "INSIDE_SALES"
6878
+ };
6879
+ var Deals = {
6880
+ ALL: "ALL",
6881
+ SUCCESSFUL: "SUCCESSFUL",
6882
+ LOST: "LOST"
6883
+ };
6884
+ var TimeFrame = {
6885
+ ALL: "ALL",
6886
+ LAST_YEAR: "LAST_YEAR",
6887
+ LAST_6_MONTHS: "LAST_6_MONTHS",
6888
+ LAST_3_MONTHS: "LAST_3_MONTHS",
6889
+ CUSTOM: "CUSTOM"
6890
+ };
6891
+ var ChartTimeFrame = {
6892
+ LAST_30_DAYS: "last_30_days",
6893
+ LAST_3_MONTHS: "last_3_months",
6894
+ LAST_6_MONTHS: "last_6_months",
6895
+ LAST_1_YEAR: "last_1_year",
6896
+ ALL_TIME: "all_time"
6897
+ };
6898
+ var ExtendedErrorSeverity = {
6899
+ ALL: "ALL",
6900
+ ...ErrorSeverity
6901
+ };
6902
+ var ExtendedErrorStatus = {
6903
+ ALL: "ALL",
6904
+ ...ErrorStatus
6905
+ };
6906
+
6719
6907
  // src/cli-trace.ts
6720
6908
  var cliQuietSession = false;
6721
6909
  var setCliQuiet = (quiet) => {
@@ -6737,6 +6925,7 @@ var SENSITIVE_FLAG = new Set([
6737
6925
  "authorization"
6738
6926
  ]);
6739
6927
  var SENSITIVE_TRACE_FIELD = new Set([
6928
+ "secret",
6740
6929
  "paymentproof",
6741
6930
  "paymentproofattachments",
6742
6931
  "paymentproofpath",
@@ -6754,6 +6943,14 @@ var sanitizeValueForTrace = (value, seen = new WeakSet) => {
6754
6943
  if (Array.isArray(value)) {
6755
6944
  return value.map((item) => sanitizeValueForTrace(item, seen));
6756
6945
  }
6946
+ if (value instanceof Error) {
6947
+ return sanitizeValueForTrace({
6948
+ ...value,
6949
+ name: value.name,
6950
+ message: value.message,
6951
+ stack: value.stack
6952
+ }, seen);
6953
+ }
6757
6954
  const prototype = Object.getPrototypeOf(value);
6758
6955
  if (prototype !== Object.prototype && prototype !== null)
6759
6956
  return value;
@@ -6776,9 +6973,8 @@ var sanitizeFlagsForTrace = (flags) => {
6776
6973
  };
6777
6974
  var consumeCliQuietFlags = (flags) => {
6778
6975
  const fromEnv = process.env.BB_CLI_QUIET === "1";
6779
- const fromFlags = flags.quiet === true || flags.q === true;
6976
+ const fromFlags = flags.quiet === true;
6780
6977
  delete flags.quiet;
6781
- delete flags.q;
6782
6978
  return fromEnv || fromFlags;
6783
6979
  };
6784
6980
  var logCliAction = (record) => {
@@ -6864,7 +7060,7 @@ var api2 = new Proxy({}, {
6864
7060
  });
6865
7061
 
6866
7062
  // src/prisma-enums.ts
6867
- var ProjectStatus = {
7063
+ var ProjectStatus2 = {
6868
7064
  PITCH: "PITCH",
6869
7065
  WON: "WON",
6870
7066
  COMPLETED: "COMPLETED",
@@ -6878,7 +7074,7 @@ var SupplierQuotationStatus = {
6878
7074
  REJECTED: "REJECTED",
6879
7075
  SUPERSEDED: "SUPERSEDED"
6880
7076
  };
6881
- var BudgetStatus = {
7077
+ var BudgetStatus2 = {
6882
7078
  DRAFT: "DRAFT",
6883
7079
  PENDING_APPROVAL: "PENDING_APPROVAL",
6884
7080
  APPROVED: "APPROVED",
@@ -6889,13 +7085,13 @@ var BudgetStatus = {
6889
7085
  ESTIMATE_REJECTED: "ESTIMATE_REJECTED",
6890
7086
  ESTIMATE_CLOSED: "ESTIMATE_CLOSED"
6891
7087
  };
6892
- var ApprovalStatus = {
7088
+ var ApprovalStatus2 = {
6893
7089
  PENDING_APPROVAL: "PENDING_APPROVAL",
6894
7090
  APPROVED: "APPROVED",
6895
7091
  REJECTED: "REJECTED",
6896
7092
  SUPERSEDED: "SUPERSEDED"
6897
7093
  };
6898
- var ApprovalType = {
7094
+ var ApprovalType2 = {
6899
7095
  BUDGET: "BUDGET",
6900
7096
  SUPPLIER: "SUPPLIER",
6901
7097
  BILL: "BILL",
@@ -6909,7 +7105,7 @@ var UserRole = {
6909
7105
  INSIDE_SALES: "INSIDE_SALES",
6910
7106
  ACCOUNTING_TEAM: "ACCOUNTING_TEAM"
6911
7107
  };
6912
- var BillStatus = {
7108
+ var BillStatus2 = {
6913
7109
  DRAFT: "DRAFT",
6914
7110
  PENDING_APPROVAL: "PENDING_APPROVAL",
6915
7111
  CHECKED: "CHECKED",
@@ -6950,212 +7146,87 @@ import { readFile as readFile2, writeFile } from "fs/promises";
6950
7146
  import { basename } from "path";
6951
7147
  import { createInterface as createInterface2 } from "readline/promises";
6952
7148
 
6953
- // ../db/src/generated/prisma/enums.ts
6954
- var EstimationMode = {
6955
- MICRO: "MICRO",
6956
- MANPOWER_SPECIFIED: "MANPOWER_SPECIFIED"
6957
- };
6958
- var ProjectStatus2 = {
6959
- PITCH: "PITCH",
6960
- WON: "WON",
6961
- COMPLETED: "COMPLETED",
6962
- RECONCILED: "RECONCILED",
6963
- LOST: "LOST"
7149
+ // src/runtime/error.ts
7150
+ var invocationErrorCodes = new Set([
7151
+ "USAGE",
7152
+ "VALIDATION",
7153
+ "CONFIRMATION_REQUIRED"
7154
+ ]);
7155
+ var trpcCodeMap = {
7156
+ BAD_REQUEST: "VALIDATION",
7157
+ CONFLICT: "CONFLICT",
7158
+ FORBIDDEN: "AUTHORIZATION",
7159
+ INTERNAL_SERVER_ERROR: "API",
7160
+ METHOD_NOT_SUPPORTED: "API",
7161
+ NOT_FOUND: "NOT_FOUND",
7162
+ PARSE_ERROR: "VALIDATION",
7163
+ PAYLOAD_TOO_LARGE: "VALIDATION",
7164
+ PRECONDITION_FAILED: "CONFLICT",
7165
+ TIMEOUT: "NETWORK",
7166
+ TOO_MANY_REQUESTS: "API",
7167
+ UNAUTHORIZED: "AUTHENTICATION",
7168
+ UNPROCESSABLE_CONTENT: "VALIDATION"
6964
7169
  };
6965
- var BudgetStatus2 = {
6966
- DRAFT: "DRAFT",
6967
- PENDING_APPROVAL: "PENDING_APPROVAL",
6968
- APPROVED: "APPROVED",
6969
- REJECTED: "REJECTED",
6970
- ESTIMATE_CREATED: "ESTIMATE_CREATED",
6971
- ESTIMATE_SENT: "ESTIMATE_SENT",
6972
- ESTIMATE_ACCEPTED: "ESTIMATE_ACCEPTED",
6973
- ESTIMATE_REJECTED: "ESTIMATE_REJECTED",
6974
- ESTIMATE_CLOSED: "ESTIMATE_CLOSED"
6975
- };
6976
- var ApprovalStatus2 = {
6977
- PENDING_APPROVAL: "PENDING_APPROVAL",
6978
- APPROVED: "APPROVED",
6979
- REJECTED: "REJECTED",
6980
- SUPERSEDED: "SUPERSEDED"
6981
- };
6982
- var ApprovalType2 = {
6983
- BUDGET: "BUDGET",
6984
- SUPPLIER: "SUPPLIER",
6985
- BILL: "BILL",
6986
- CUSTOMER_INVOICE: "CUSTOMER_INVOICE",
6987
- QUOTATION: "QUOTATION"
6988
- };
6989
- var ErrorSeverity = {
6990
- LOW: "LOW",
6991
- MEDIUM: "MEDIUM",
6992
- HIGH: "HIGH",
6993
- CRITICAL: "CRITICAL"
6994
- };
6995
- var ErrorStatus = {
6996
- UNRESOLVED: "UNRESOLVED",
6997
- INVESTIGATING: "INVESTIGATING",
6998
- RESOLVED: "RESOLVED",
6999
- IGNORED: "IGNORED"
7000
- };
7001
- var CustomerInvoiceBatchStatus = {
7002
- CREATING: "CREATING",
7003
- PENDING_APPROVAL: "PENDING_APPROVAL",
7004
- APPROVED: "APPROVED",
7005
- REJECTED_VOIDED: "REJECTED_VOIDED",
7006
- EXPIRED_VOIDED: "EXPIRED_VOIDED",
7007
- PARTIAL_QBO_FAILURE: "PARTIAL_QBO_FAILURE"
7008
- };
7009
- var CustomerInvoiceStatus = {
7010
- CREATED_IN_QBO: "CREATED_IN_QBO",
7011
- APPROVED: "APPROVED",
7012
- SENT: "SENT",
7013
- PAID: "PAID",
7014
- VOIDED: "VOIDED",
7015
- QBO_SYNC_ERROR: "QBO_SYNC_ERROR"
7016
- };
7017
- var BillStatus2 = {
7018
- DRAFT: "DRAFT",
7019
- PENDING_APPROVAL: "PENDING_APPROVAL",
7020
- CHECKED: "CHECKED",
7021
- APPROVED: "APPROVED",
7022
- PAID: "PAID",
7023
- REJECTED: "REJECTED"
7024
- };
7025
-
7026
- // ../utils/src/filter-enums.ts
7027
- var ExtendedApprovalStatus = {
7028
- ...ApprovalStatus2,
7029
- ALL: "ALL"
7030
- };
7031
- var ExtendedApprovalType = {
7032
- ...ApprovalType2,
7033
- ALL: "ALL"
7034
- };
7035
- var ExtendedProjectStatus = {
7036
- ...ProjectStatus2,
7037
- ALL: "ALL"
7038
- };
7039
- var ExtendedBudgetStatus = {
7040
- ...BudgetStatus2,
7041
- ALL: "ALL"
7042
- };
7043
- var ExtendedBillStatus = {
7044
- ...BillStatus2,
7045
- ALL: "ALL"
7046
- };
7047
- var ExtendedEstimationMode = {
7048
- ...EstimationMode,
7049
- ALL: "ALL"
7050
- };
7051
- var BudgetRole = {
7052
- ALL: "ALL",
7053
- BD: "BD",
7054
- CREATOR: "CREATOR",
7055
- INSIDE_SALES: "INSIDE_SALES"
7056
- };
7057
- var Deals = {
7058
- ALL: "ALL",
7059
- SUCCESSFUL: "SUCCESSFUL",
7060
- LOST: "LOST"
7061
- };
7062
- var TimeFrame = {
7063
- ALL: "ALL",
7064
- LAST_YEAR: "LAST_YEAR",
7065
- LAST_6_MONTHS: "LAST_6_MONTHS",
7066
- LAST_3_MONTHS: "LAST_3_MONTHS",
7067
- CUSTOM: "CUSTOM"
7068
- };
7069
- var ExtendedErrorSeverity = {
7070
- ALL: "ALL",
7071
- ...ErrorSeverity
7072
- };
7073
- var ExtendedErrorStatus = {
7074
- ALL: "ALL",
7075
- ...ErrorStatus
7076
- };
7077
-
7078
- // src/runtime/error.ts
7079
- var invocationErrorCodes = new Set([
7080
- "USAGE",
7081
- "VALIDATION",
7082
- "CONFIRMATION_REQUIRED"
7083
- ]);
7084
- var trpcCodeMap = {
7085
- BAD_REQUEST: "VALIDATION",
7086
- CONFLICT: "CONFLICT",
7087
- FORBIDDEN: "AUTHORIZATION",
7088
- INTERNAL_SERVER_ERROR: "API",
7089
- METHOD_NOT_SUPPORTED: "API",
7090
- NOT_FOUND: "NOT_FOUND",
7091
- PARSE_ERROR: "VALIDATION",
7092
- PAYLOAD_TOO_LARGE: "VALIDATION",
7093
- PRECONDITION_FAILED: "CONFLICT",
7094
- TIMEOUT: "NETWORK",
7095
- TOO_MANY_REQUESTS: "API",
7096
- UNAUTHORIZED: "AUTHENTICATION",
7097
- UNPROCESSABLE_CONTENT: "VALIDATION"
7098
- };
7099
- var cliErrorCodes = new Set([
7100
- "USAGE",
7101
- "VALIDATION",
7102
- "CONFIRMATION_REQUIRED",
7103
- "AUTHENTICATION",
7104
- "AUTHORIZATION",
7105
- "NOT_FOUND",
7106
- "CONFLICT",
7107
- "API",
7108
- "NETWORK",
7109
- "ABORTED",
7110
- "INTERRUPTED",
7111
- "INTERNAL"
7112
- ]);
7113
-
7114
- class CliRuntimeError extends Error {
7115
- code;
7116
- details;
7117
- requestId;
7118
- constructor(code, message, options = {}) {
7119
- super(message);
7120
- this.name = "CliRuntimeError";
7121
- this.code = code;
7122
- this.details = options.details;
7123
- this.requestId = options.requestId;
7124
- }
7125
- }
7126
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7127
- var asNonEmptyString = (value) => typeof value === "string" && value.trim() !== "" ? value : undefined;
7128
- var formatValidationMessage = (message, data) => {
7129
- const zodError = isRecord(data?.zodError) ? data.zodError : undefined;
7130
- const fieldErrors = isRecord(zodError?.fieldErrors) ? zodError.fieldErrors : undefined;
7131
- const fields = fieldErrors ? Object.entries(fieldErrors).flatMap(([field, errors]) => Array.isArray(errors) ? errors.filter((entry) => typeof entry === "string").map((entry) => `${field}: ${entry}`) : []) : [];
7132
- const formErrors = Array.isArray(zodError?.formErrors) ? zodError.formErrors.filter((entry) => typeof entry === "string") : [];
7133
- const validationIssues = [...fields, ...formErrors];
7134
- if (validationIssues.length > 0)
7135
- return validationIssues.join("; ");
7136
- let parsed = message;
7137
- for (let depth = 0;depth < 3 && typeof parsed === "string"; depth++) {
7138
- try {
7139
- parsed = JSON.parse(parsed);
7140
- } catch {
7141
- return message;
7142
- }
7143
- }
7144
- if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isRecord))
7145
- return message;
7146
- const issues = parsed.map((issue) => {
7147
- if (!Array.isArray(issue.path) || typeof issue.message !== "string" || typeof issue.code !== "string")
7148
- return;
7149
- const path = issue.path.filter((part) => typeof part === "string" || typeof part === "number").join(".") || "payload";
7150
- if (issue.code === "invalid_type" && typeof issue.expected === "string" && issue.message.toLowerCase().includes("required")) {
7151
- return `${path} must be a ${issue.expected}${path === "cost" ? ', e.g. "19"' : ""}`;
7152
- }
7153
- if (issue.code === "invalid_format" && issue.format === "regex" && path === "name" && String(issue.pattern ?? issue.message).includes("A-Za-z0-9,.?@&!#'~* _;+-")) {
7154
- return `${path} contains characters that are not allowed. Allowed: letters, numbers, spaces and , . ? @ & ! # ' ~ * _ ; + -`;
7155
- }
7156
- return `${path}: ${issue.message}`;
7157
- });
7158
- return issues.every((issue) => issue !== undefined) ? issues.join("; ") : message;
7170
+ var cliErrorCodes = new Set([
7171
+ "USAGE",
7172
+ "VALIDATION",
7173
+ "CONFIRMATION_REQUIRED",
7174
+ "AUTHENTICATION",
7175
+ "AUTHORIZATION",
7176
+ "NOT_FOUND",
7177
+ "CONFLICT",
7178
+ "API",
7179
+ "NETWORK",
7180
+ "ABORTED",
7181
+ "INTERRUPTED",
7182
+ "INTERNAL"
7183
+ ]);
7184
+
7185
+ class CliRuntimeError extends Error {
7186
+ code;
7187
+ details;
7188
+ requestId;
7189
+ constructor(code, message, options = {}) {
7190
+ super(message);
7191
+ this.name = "CliRuntimeError";
7192
+ this.code = code;
7193
+ this.details = options.details;
7194
+ this.requestId = options.requestId;
7195
+ }
7196
+ }
7197
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7198
+ var asNonEmptyString = (value) => typeof value === "string" && value.trim() !== "" ? value : undefined;
7199
+ var formatValidationMessage = (message, data) => {
7200
+ const zodError = isRecord(data?.zodError) ? data.zodError : undefined;
7201
+ const fieldErrors = isRecord(zodError?.fieldErrors) ? zodError.fieldErrors : undefined;
7202
+ const fields = fieldErrors ? Object.entries(fieldErrors).flatMap(([field, errors]) => Array.isArray(errors) ? errors.filter((entry) => typeof entry === "string").map((entry) => `${field}: ${entry}`) : []) : [];
7203
+ const formErrors = Array.isArray(zodError?.formErrors) ? zodError.formErrors.filter((entry) => typeof entry === "string") : [];
7204
+ const validationIssues = [...fields, ...formErrors];
7205
+ if (validationIssues.length > 0)
7206
+ return validationIssues.join("; ");
7207
+ let parsed = message;
7208
+ for (let depth = 0;depth < 3 && typeof parsed === "string"; depth++) {
7209
+ try {
7210
+ parsed = JSON.parse(parsed);
7211
+ } catch {
7212
+ return message;
7213
+ }
7214
+ }
7215
+ if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isRecord))
7216
+ return message;
7217
+ const issues = parsed.map((issue) => {
7218
+ if (!Array.isArray(issue.path) || typeof issue.message !== "string" || typeof issue.code !== "string")
7219
+ return;
7220
+ const path = issue.path.filter((part) => typeof part === "string" || typeof part === "number").join(".") || "payload";
7221
+ if (issue.code === "invalid_type" && typeof issue.expected === "string" && issue.message.toLowerCase().includes("required")) {
7222
+ return `${path} must be a ${issue.expected}${path === "cost" ? ', e.g. "19"' : ""}`;
7223
+ }
7224
+ if (issue.code === "invalid_format" && issue.format === "regex" && path === "name" && String(issue.pattern ?? issue.message).includes("A-Za-z0-9,.?@&!#'~* _;+-")) {
7225
+ return `${path} contains characters that are not allowed. Allowed: letters, numbers, spaces and , . ? @ & ! # ' ~ * _ ; + -`;
7226
+ }
7227
+ return `${path}: ${issue.message}`;
7228
+ });
7229
+ return issues.every((issue) => issue !== undefined) ? issues.join("; ") : message;
7159
7230
  };
7160
7231
  var asDetails = (value) => {
7161
7232
  if (!isRecord(value))
@@ -7277,9 +7348,9 @@ var customerInvoiceLifecycleStatuses = [
7277
7348
  ];
7278
7349
 
7279
7350
  // src/parse-cli-enums.ts
7280
- var BUDGET_STATUS_VALUES = new Set(Object.values(BudgetStatus));
7281
- var BILL_STATUS_VALUES = new Set(Object.values(BillStatus));
7282
- var PROJECT_STATUS_VALUES = new Set(Object.values(ProjectStatus));
7351
+ var BUDGET_STATUS_VALUES = new Set(Object.values(BudgetStatus2));
7352
+ var BILL_STATUS_VALUES = new Set(Object.values(BillStatus2));
7353
+ var PROJECT_STATUS_VALUES = new Set(Object.values(ProjectStatus2));
7283
7354
  var SUPPLIER_QUOTATION_STATUS_VALUES = new Set(Object.values(SupplierQuotationStatus));
7284
7355
  var CUSTOMER_INVOICE_LIFECYCLE_STATUS_VALUES = new Set(customerInvoiceLifecycleStatuses);
7285
7356
  var USER_ROLE_VALUES = new Set(Object.values(UserRole));
@@ -7326,7 +7397,7 @@ function parseBillStatusForUpdate(raw) {
7326
7397
  if (!BILL_STATUS_VALUES.has(raw)) {
7327
7398
  throw new Error(`Invalid bill status "${raw}". Use one of: ${[...BILL_STATUS_VALUES].join(", ")}.`);
7328
7399
  }
7329
- if (raw === BillStatus.PENDING_APPROVAL) {
7400
+ if (raw === BillStatus2.PENDING_APPROVAL) {
7330
7401
  throw new Error("Cannot set PENDING_APPROVAL via update-bill-status. Use create-bill-approval <billId> instead.");
7331
7402
  }
7332
7403
  return raw;
@@ -7373,6 +7444,16 @@ var PROJECT_HUB_SORT_FIELDS = new Set([
7373
7444
  "companyName",
7374
7445
  "status"
7375
7446
  ]);
7447
+ function parseOptionalIsoDateFlag(raw, flagName, { endOfDay = false } = {}) {
7448
+ if (raw === undefined || raw === "")
7449
+ return;
7450
+ const value = /^\d{4}-\d{2}-\d{2}$/.test(raw) ? `${raw}T${endOfDay ? "23:59:59.999" : "00:00:00.000"}Z` : /T\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(raw) ? `${raw}Z` : raw;
7451
+ const parsed = new Date(value);
7452
+ if (Number.isNaN(parsed.getTime())) {
7453
+ throw new Error(`${flagName} must be an ISO date (YYYY-MM-DD) or ISO datetime.`);
7454
+ }
7455
+ return parsed;
7456
+ }
7376
7457
  function parseOptionalProjectHubSortBy(raw) {
7377
7458
  if (raw === undefined || raw === "")
7378
7459
  return;
@@ -7565,10 +7646,10 @@ function parseApprovalTypeFlag(raw) {
7565
7646
  }
7566
7647
  return u;
7567
7648
  }
7568
- var budgetStatusesForHelp = Object.values(BudgetStatus);
7569
- var billStatusesForHelp = Object.values(BillStatus);
7570
- var billStatusesForUpdateHelp = Object.values(BillStatus).filter((s) => s !== BillStatus.PENDING_APPROVAL);
7571
- var projectStatusesForHelp = Object.values(ProjectStatus);
7649
+ var budgetStatusesForHelp = Object.values(BudgetStatus2);
7650
+ var billStatusesForHelp = Object.values(BillStatus2);
7651
+ var billStatusesForUpdateHelp = Object.values(BillStatus2).filter((s) => s !== BillStatus2.PENDING_APPROVAL);
7652
+ var projectStatusesForHelp = Object.values(ProjectStatus2);
7572
7653
  var userRolesForHelp = Object.values(UserRole);
7573
7654
 
7574
7655
  // ../utils/src/rich-text.ts
@@ -8494,11 +8575,11 @@ var confirmCurrentCommand = async (input) => {
8494
8575
  };
8495
8576
  // src/commands/index.ts
8496
8577
  import { contentType } from "mime-types";
8497
- var BUDGET = ApprovalType.BUDGET;
8498
- var BILL = ApprovalType.BILL;
8499
- var SUPPLIER = ApprovalType.SUPPLIER;
8500
- var QUOTATION = ApprovalType.QUOTATION;
8501
- var CUSTOMER_INVOICE = ApprovalType.CUSTOMER_INVOICE;
8578
+ var BUDGET = ApprovalType2.BUDGET;
8579
+ var BILL = ApprovalType2.BILL;
8580
+ var SUPPLIER = ApprovalType2.SUPPLIER;
8581
+ var QUOTATION = ApprovalType2.QUOTATION;
8582
+ var CUSTOMER_INVOICE = ApprovalType2.CUSTOMER_INVOICE;
8502
8583
  var ASANA_WON_LOST_SECTION_GIDS = new Set([
8503
8584
  "1211678338364908",
8504
8585
  "1211678338364907"
@@ -8555,7 +8636,7 @@ var createApprovalEmailDeliveryFailure = (error) => ({
8555
8636
  error
8556
8637
  });
8557
8638
  function isPendingRequest(n) {
8558
- return n.notificationType === "request" && n.status === ApprovalStatus.PENDING_APPROVAL;
8639
+ return n.notificationType === "request" && n.status === ApprovalStatus2.PENDING_APPROVAL;
8559
8640
  }
8560
8641
  var out = (data) => {
8561
8642
  emitCommandResult(data);
@@ -8762,11 +8843,11 @@ var quotationAttachmentContentTypeForFileName = (fileName) => {
8762
8843
  throw new Error(`Unsupported quotation attachment type: ${fileName}. Use PDF, JPEG, or PNG.`);
8763
8844
  };
8764
8845
  var LOCKED_BUDGET_STATUSES_REQUIRING_CONFIRMATION = new Set([
8765
- BudgetStatus.ESTIMATE_CREATED,
8766
- BudgetStatus.ESTIMATE_SENT,
8767
- BudgetStatus.ESTIMATE_ACCEPTED,
8768
- BudgetStatus.ESTIMATE_REJECTED,
8769
- BudgetStatus.ESTIMATE_CLOSED
8846
+ BudgetStatus2.ESTIMATE_CREATED,
8847
+ BudgetStatus2.ESTIMATE_SENT,
8848
+ BudgetStatus2.ESTIMATE_ACCEPTED,
8849
+ BudgetStatus2.ESTIMATE_REJECTED,
8850
+ BudgetStatus2.ESTIMATE_CLOSED
8770
8851
  ]);
8771
8852
  var lockedBudgetStatusLabels = {
8772
8853
  ESTIMATE_CREATED: "Estimate Created",
@@ -8888,36 +8969,20 @@ var updateProjectWithEstimateInvalidationConfirmation = async (input) => {
8888
8969
  });
8889
8970
  }
8890
8971
  };
8891
- async function listBudgets(opts) {
8892
- const input = {
8893
- projectId: opts.projectId,
8894
- name: opts.name,
8895
- statuses: opts.statuses,
8896
- createdBy: opts.createdBy,
8897
- dateFrom: opts.dateFrom,
8898
- dateTo: opts.dateTo,
8899
- active: opts.active,
8900
- sortBy: opts.sortBy,
8901
- sortDir: opts.sortDir,
8902
- page: opts.page ?? 1,
8903
- perPage: opts.perPage ?? 20,
8904
- includeDetails: opts.includeDetails ?? true
8905
- };
8906
- const result = await api2.budget.getAllBudgets.query(input);
8907
- out(result);
8908
- }
8972
+ var listBudgets = async (input) => {
8973
+ out(await api2.budget.getAllBudgets.query(input));
8974
+ };
8975
+ var countBudgets = async (input) => {
8976
+ out(await api2.budget.getCounts.query(input));
8977
+ };
8909
8978
  async function getBudget(id, opts) {
8910
- const [budget, items] = await Promise.all([
8911
- api2.budget.getBudget.query({
8912
- id,
8913
- includeInactive: opts?.includeInactive ?? false
8914
- }),
8915
- api2.budget.getBudgetItems.query({
8916
- budgetId: id,
8917
- includeInactive: opts?.includeInactive ?? false
8918
- })
8979
+ const includeInactive = opts?.includeInactive ?? false;
8980
+ const [budget, items, totals] = await Promise.all([
8981
+ api2.budget.getBudget.query({ id, includeInactive }),
8982
+ api2.budget.getBudgetItems.query({ budgetId: id, includeInactive }),
8983
+ api2.budget.getBudgetItemTotals.query({ budgetId: id, includeInactive })
8919
8984
  ]);
8920
- out({ budget, items });
8985
+ out({ budget, items: { ...items, totals } });
8921
8986
  }
8922
8987
  async function updateBudgetStatus(budgetId, status, opts) {
8923
8988
  await assertSensitiveWorkflowConfirmed({
@@ -8944,7 +9009,7 @@ async function updateBudgetStatus(budgetId, status, opts) {
8944
9009
  });
8945
9010
  out({
8946
9011
  ...result,
8947
- invoiceSetup: result.newStatus === BudgetStatus.ESTIMATE_ACCEPTED && result.hasQuickbooksEstimate ? invoiceSplitSetupNextAction(budgetId) : null
9012
+ invoiceSetup: result.newStatus === BudgetStatus2.ESTIMATE_ACCEPTED && result.hasQuickbooksEstimate ? invoiceSplitSetupNextAction(budgetId) : null
8948
9013
  });
8949
9014
  }
8950
9015
  var assertBudgetActionReadiness = async ({
@@ -9011,22 +9076,22 @@ var runBudgetPreflight = async (budgetId, preflight) => {
9011
9076
  await updateProjectWithEstimateInvalidationConfirmation(payload);
9012
9077
  }
9013
9078
  };
9014
- async function listBills(opts) {
9015
- const input = {
9016
- projectId: opts.projectId,
9017
- budgetId: opts.budgetId,
9018
- statuses: billStatusesForApi(opts.statuses),
9019
- search: opts.search?.trim() || undefined,
9020
- isClaimable: opts.isClaimable,
9021
- createdByIds: opts.createdByIds && opts.createdByIds.length > 0 ? opts.createdByIds : undefined,
9022
- sortBy: opts.sortBy,
9023
- sortDir: opts.sortDir,
9024
- page: opts.page ?? 1,
9025
- pageSize: opts.pageSize ?? 20
9026
- };
9027
- const result = await api2.bill.getAll.query(input);
9028
- out(result);
9029
- }
9079
+ var listBills = async (input) => {
9080
+ out(await api2.bill.getAll.query({
9081
+ ...input,
9082
+ statuses: billStatusesForApi(input.statuses),
9083
+ search: input.search?.trim() || undefined,
9084
+ createdByIds: input.createdByIds?.length ? input.createdByIds : undefined
9085
+ }));
9086
+ };
9087
+ var countBills = async (input) => {
9088
+ out(await api2.bill.getCounts.query({
9089
+ ...input,
9090
+ statuses: billStatusesForApi(input.statuses),
9091
+ search: input.search?.trim() || undefined,
9092
+ createdByIds: input.createdByIds?.length ? input.createdByIds : undefined
9093
+ }));
9094
+ };
9030
9095
  async function approveBill(billId, overBudgetOverrideReason) {
9031
9096
  await assertSensitiveWorkflowConfirmed({
9032
9097
  action: "Approve bill",
@@ -9041,7 +9106,7 @@ async function approveBill(billId, overBudgetOverrideReason) {
9041
9106
  }
9042
9107
  const result = await api2.bill.updateApproval.mutate({
9043
9108
  billApprovalId: approval.id,
9044
- status: ApprovalStatus.APPROVED,
9109
+ status: ApprovalStatus2.APPROVED,
9045
9110
  overBudgetOverrideReason
9046
9111
  });
9047
9112
  if (result.processing) {
@@ -9098,20 +9163,12 @@ async function listApprovals(opts) {
9098
9163
  });
9099
9164
  out({ items });
9100
9165
  }
9101
- async function listSuppliers(opts) {
9102
- const result = await api2.supplier.getSuppliers.query({
9103
- page: opts.page ?? 1,
9104
- perPage: opts.perPage ?? 10,
9105
- sort: opts.sort ?? [{ id: "createdAt", desc: true }],
9106
- name: opts.name,
9107
- createdBy: opts.createdBy,
9108
- gstRegistered: opts.gstRegistered ?? null,
9109
- status: opts.status,
9110
- supplierTags: opts.supplierTags,
9111
- active: opts.active ?? true
9112
- });
9113
- out(result);
9114
- }
9166
+ var listSuppliers = async (opts) => {
9167
+ out(await api2.supplier.getSuppliers.query(opts));
9168
+ };
9169
+ var countSuppliers = async (opts) => {
9170
+ out(await api2.supplier.getSummary.query(opts));
9171
+ };
9115
9172
  async function createBillApproval(billId) {
9116
9173
  await assertSensitiveWorkflowConfirmed({
9117
9174
  action: "Create bill approval request",
@@ -9137,10 +9194,10 @@ async function createBillApproval(billId) {
9137
9194
  });
9138
9195
  }
9139
9196
  var validateBillStatusUpdateOptions = (opts) => {
9140
- if (opts.status === BillStatus.PAID && !opts.paymentReference?.trim()) {
9197
+ if (opts.status === BillStatus2.PAID && !opts.paymentReference?.trim()) {
9141
9198
  throw new Error("update-bill-status requires --paymentReference when status is PAID.");
9142
9199
  }
9143
- if (opts.status !== BillStatus.PAID && opts.paymentProofPath !== undefined) {
9200
+ if (opts.status !== BillStatus2.PAID && opts.paymentProofPath !== undefined) {
9144
9201
  throw new Error("--paymentProof can only be used when status is PAID.");
9145
9202
  }
9146
9203
  if (opts.paymentProofPath !== undefined && !opts.paymentProofPath.trim()) {
@@ -9148,13 +9205,10 @@ var validateBillStatusUpdateOptions = (opts) => {
9148
9205
  }
9149
9206
  };
9150
9207
  var deleteStagedPaymentProof = async (projectId, key) => {
9151
- const cleanup = await api2.attachment.deleteStagedBillAttachments.mutate({
9208
+ await api2.attachment.deleteStagedBillAttachments.mutate({
9152
9209
  projectId,
9153
9210
  keys: [key]
9154
9211
  });
9155
- if (cleanup.failedKeys.includes(key)) {
9156
- throw new Error(`Failed to delete staged payment proof ${key}.`);
9157
- }
9158
9212
  };
9159
9213
  var rethrowAfterStagedPaymentProofCleanup = async (error, cleanup) => {
9160
9214
  if (cleanup) {
@@ -9212,7 +9266,7 @@ async function updateBillStatus(opts) {
9212
9266
  entity: `bill ${opts.id}`,
9213
9267
  details: `to ${opts.status}`
9214
9268
  });
9215
- const bill = opts.paymentProofPath ? await api2.bill.getById.query({ id: opts.id }) : null;
9269
+ const bill = opts.paymentProofPath ? await api2.bill.getPayment.query({ id: opts.id }) : null;
9216
9270
  const paymentProof = bill && opts.paymentProofPath ? await stageBillPaymentProof(bill.projectId, opts.paymentProofPath) : undefined;
9217
9271
  try {
9218
9272
  const result = await api2.bill.updateStatus.mutate({
@@ -9242,7 +9296,7 @@ var recordExternalBillPayment = async (opts) => {
9242
9296
  entity: `bill ${opts.id}`,
9243
9297
  details: `already paid on ${opts.paymentDate}; reference ${opts.paymentReference}; QBO account ${opts.paymentAccount}. No money is transferred.`
9244
9298
  });
9245
- const bill = await api2.bill.getById.query({ id: opts.id });
9299
+ const bill = await api2.bill.getPayment.query({ id: opts.id });
9246
9300
  const proof = !bill.externalPayment && opts.paymentProofPath ? await stageBillPaymentProof(bill.projectId, opts.paymentProofPath) : undefined;
9247
9301
  try {
9248
9302
  out(await api2.bill.recordExternalPayment.mutate({
@@ -9254,7 +9308,7 @@ var recordExternalBillPayment = async (opts) => {
9254
9308
  }));
9255
9309
  } catch (error) {
9256
9310
  try {
9257
- const latest = await api2.bill.getById.query({ id: opts.id });
9311
+ const latest = await api2.bill.getPayment.query({ id: opts.id });
9258
9312
  if (latest.externalPayment?.paymentAccount === opts.paymentAccount && latest.paymentReference === opts.paymentReference.trim() && latest.paymentDate?.toISOString().slice(0, 10) === opts.paymentDate) {
9259
9313
  out({
9260
9314
  ...latest,
@@ -9288,10 +9342,18 @@ async function patchBillInvoiceNumber(id, invoiceNumber) {
9288
9342
  });
9289
9343
  out(result);
9290
9344
  }
9291
- async function getBillAttachments(id) {
9292
- const result = await api2.bill.getAttachments.query({ id });
9293
- out(result);
9294
- }
9345
+ var getBillAttachments = async (input) => {
9346
+ out(await api2.bill.getAttachments.query(input));
9347
+ };
9348
+ var getBillApprovals = async (input) => {
9349
+ out(await api2.bill.getApprovals.query(input));
9350
+ };
9351
+ var getBillLineItems = async (input) => {
9352
+ out(await api2.bill.getLineItems.query(input));
9353
+ };
9354
+ var getBillPayment = async (id) => {
9355
+ out(await api2.bill.getPayment.query({ id }));
9356
+ };
9295
9357
  async function stageBillAttachmentsFromPaths(projectId, filePaths) {
9296
9358
  const attachments = [];
9297
9359
  for (const filePath of filePaths) {
@@ -9499,7 +9561,7 @@ async function uploadBudgetWinProofFromPath(budgetId, filePath, opts) {
9499
9561
  });
9500
9562
  const result = await api2.budget.updateBudgetStatus.mutate({
9501
9563
  id: budgetId,
9502
- status: BudgetStatus.ESTIMATE_ACCEPTED,
9564
+ status: BudgetStatus2.ESTIMATE_ACCEPTED,
9503
9565
  ...opts?.projectManagerId !== undefined && {
9504
9566
  projectManagerId: opts.projectManagerId
9505
9567
  },
@@ -9523,11 +9585,12 @@ async function getBillDetails(id) {
9523
9585
  out(result);
9524
9586
  }
9525
9587
  async function getBudgetItemsOnly(budgetId, opts) {
9526
- const items = await api2.budget.getBudgetItems.query({
9527
- budgetId,
9528
- includeInactive: opts?.includeInactive ?? false
9529
- });
9530
- out(items);
9588
+ const includeInactive = opts?.includeInactive ?? false;
9589
+ const [items, totals] = await Promise.all([
9590
+ api2.budget.getBudgetItems.query({ budgetId, includeInactive }),
9591
+ api2.budget.getBudgetItemTotals.query({ budgetId, includeInactive })
9592
+ ]);
9593
+ out({ ...items, totals });
9531
9594
  }
9532
9595
  async function addBudgetItems(opts) {
9533
9596
  await confirmLockedBudgetChangeByBudgetId(opts.budgetId, "Add budget items");
@@ -9564,10 +9627,9 @@ var approveUnavailableItemException = async (input) => {
9564
9627
  const result = await api2.budgetItem.approveUnavailableItemException.mutate(input);
9565
9628
  out(result);
9566
9629
  };
9567
- async function getBudgetCategories() {
9568
- const categories = await api2.budget.getBudgetCategories.query();
9569
- out(categories);
9570
- }
9630
+ var getBudgetCategories = async (input) => {
9631
+ out(await api2.budget.searchBudgetCategories.query(input));
9632
+ };
9571
9633
  var getBudgetVersions = async (budgetId, options = {}) => {
9572
9634
  if (options.all && options.cursor !== undefined) {
9573
9635
  throw new CliRuntimeError("USAGE", "--all cannot be combined with --cursor.");
@@ -9581,15 +9643,15 @@ var getBudgetVersions = async (budgetId, options = {}) => {
9581
9643
  limit: options.limit,
9582
9644
  cursor
9583
9645
  });
9584
- versions.push(...page.versions);
9585
- cursor = page.nextCursor;
9586
- if (!options.all || cursor === undefined)
9646
+ versions.push(...page.items);
9647
+ cursor = page.pageInfo.nextCursor;
9648
+ if (!options.all || cursor === null)
9587
9649
  break;
9588
9650
  if (seenCursors.has(cursor)) {
9589
9651
  throw new Error("Budget history pagination did not advance; export aborted.");
9590
9652
  }
9591
9653
  seenCursors.add(cursor);
9592
- } while (cursor !== undefined);
9654
+ } while (cursor !== null);
9593
9655
  out({ versions, nextCursor: cursor ?? null });
9594
9656
  };
9595
9657
  async function createBudgetApproval(budgetId, preflight) {
@@ -9840,14 +9902,19 @@ async function createBillFromPayload(raw, draftOnlyFlag, quotationExempt = false
9840
9902
  }) : await api2.bill.create.mutate(input);
9841
9903
  out(result);
9842
9904
  }
9843
- async function listQuotations(opts) {
9844
- const result = await api2.quotation.list.query(opts);
9845
- out(result);
9846
- }
9905
+ var listQuotations = async (input) => {
9906
+ out(await api2.quotation.listPage.query(input));
9907
+ };
9908
+ var countQuotations = async (input) => {
9909
+ out(await api2.quotation.getCounts.query(input));
9910
+ };
9847
9911
  async function getQuotationDetails(id) {
9848
9912
  const result = await api2.quotation.getById.query({ id });
9849
9913
  out(result);
9850
9914
  }
9915
+ var getQuotationLineItems = async (input) => {
9916
+ out(await api2.quotation.getLineItems.query(input));
9917
+ };
9851
9918
  async function createQuotationFromPayload(raw) {
9852
9919
  const input = parseCreateQuotationPayload(raw);
9853
9920
  const result = await api2.quotation.createDraft.mutate(input);
@@ -9888,8 +9955,10 @@ var checkCustomerInvoiceReadiness = async (budgetId) => {
9888
9955
  out(result);
9889
9956
  };
9890
9957
  var listCustomerInvoices = async (input) => {
9891
- const result = await api2.customerInvoice.list.query(input);
9892
- out(result);
9958
+ out(await api2.customerInvoice.list.query(input));
9959
+ };
9960
+ var countCustomerInvoices = async (input) => {
9961
+ out(await api2.customerInvoice.getCounts.query(input));
9893
9962
  };
9894
9963
  var listEligibleCustomerInvoiceBudgets = async (projectId) => {
9895
9964
  const result = await api2.customerInvoice.getEligibleBudgets.query({
@@ -9914,6 +9983,18 @@ var getCustomerInvoice = async (batchId) => {
9914
9983
  const result = await api2.customerInvoice.getInvoiceDetail.query({ batchId });
9915
9984
  out(result);
9916
9985
  };
9986
+ var getCustomerInvoiceInvoices = async (batchId) => {
9987
+ out(await api2.customerInvoice.getInvoices.query({ batchId }));
9988
+ };
9989
+ var getCustomerInvoiceLines = async (input) => {
9990
+ out(await api2.customerInvoice.getInvoiceLines.query(input));
9991
+ };
9992
+ var getCustomerInvoiceApprovals = async (input) => {
9993
+ out(await api2.customerInvoice.getApprovals.query(input));
9994
+ };
9995
+ var getCustomerInvoiceHistory = async (input) => {
9996
+ out(await api2.customerInvoice.getHistory.query(input));
9997
+ };
9917
9998
  var getCustomerInvoiceEmailContext = async (batchId) => {
9918
9999
  const result = await api2.customerInvoice.getInvoiceEmailContext.query({
9919
10000
  batchId
@@ -9945,7 +10026,7 @@ var createCustomerInvoice = async (raw) => {
9945
10026
  await assertSensitiveWorkflowConfirmed({
9946
10027
  action: "Create customer invoice",
9947
10028
  entity: `budget ${input.budgetId}`,
9948
- details: `creates ${input.splits.length} invoice(s) in QuickBooks from the budget's remaining uninvoiced balance: ${input.splits.map((split) => `${split.label} (${split.percentage}% of the estimate), due ${new Date(split.dueDate).toISOString().slice(0, 10)}`).join("; ")}. Admin creation is auto-approved; other creators request approval. No customer email is sent.`
10029
+ details: `queues ${input.splits.length} invoice(s) for creation in QuickBooks from the budget's remaining uninvoiced balance: ${input.splits.map((split) => `${split.label} (${split.percentage}% of the estimate), due ${new Date(split.dueDate).toISOString().slice(0, 10)}`).join("; ")}. Admin creation is auto-approved; other creators request approval. No customer email is sent.`
9949
10030
  });
9950
10031
  const result = await api2.customerInvoice.createInvoiceBatch.mutate(input);
9951
10032
  out(result);
@@ -9965,7 +10046,7 @@ var deleteCustomerInvoice = async (batchId) => {
9965
10046
  await assertSensitiveWorkflowConfirmed({
9966
10047
  action: "Delete customer invoice",
9967
10048
  entity: `invoice batch ${batchId}`,
9968
- details: "deletes its invoices from QuickBooks and removes the local batch; paid invoices are blocked until their QBO payments are reversed; removing approved coverage may reopen the estimate"
10049
+ details: "queues deletion of its invoices in QuickBooks; the local batch is removed once QuickBooks confirms; paid invoices are blocked until their QBO payments are reversed; removing approved coverage may reopen the estimate"
9969
10050
  });
9970
10051
  const result = await api2.customerInvoice.deleteInvoiceBatch.mutate({
9971
10052
  batchId
@@ -9976,7 +10057,7 @@ var voidCustomerInvoice = async (batchId) => {
9976
10057
  await assertSensitiveWorkflowConfirmed({
9977
10058
  action: "Void customer invoice",
9978
10059
  entity: `invoice batch ${batchId}`,
9979
- details: "voids its invoices in QuickBooks and updates Budget Builder; paid invoices are blocked until their QBO payments are reversed; removing approved coverage may reopen the estimate"
10060
+ details: "queues a void of its invoices in QuickBooks; Budget Builder updates once QuickBooks confirms; paid invoices are blocked until their QBO payments are reversed; removing approved coverage may reopen the estimate"
9980
10061
  });
9981
10062
  const result = await api2.customerInvoice.voidInvoiceBatch.mutate({ batchId });
9982
10063
  out(result);
@@ -9996,7 +10077,7 @@ var rejectCustomerInvoice = async (batchId, rejectionReason) => {
9996
10077
  await assertSensitiveWorkflowConfirmed({
9997
10078
  action: "Reject customer invoice",
9998
10079
  entity: `invoice batch ${batchId}`,
9999
- details: `records the rejection reason, voids it in QuickBooks, notifies its creator, and reopens a prematurely closed estimate when applicable`
10080
+ details: `queues a void of its invoices in QuickBooks; once QuickBooks confirms, records the rejection reason, notifies its creator, and reopens a prematurely closed estimate when applicable`
10000
10081
  });
10001
10082
  const result = await api2.customerInvoice.rejectInvoiceBatch.mutate({
10002
10083
  batchId,
@@ -10080,7 +10161,7 @@ async function createSupplierFromPayload(raw) {
10080
10161
  throw new CliRuntimeError(normalized.code, `Supplier creation outcome is uncertain for ${JSON.stringify(input.name)}. Run list-suppliers and inspect matching supplier details before retrying; names may not be unique, so do not retry create-supplier until you can verify that no supplier was created. Original error: ${normalized.message}`);
10081
10162
  }
10082
10163
  const supplier = created.result;
10083
- if (supplier.status !== ApprovalStatus.PENDING_APPROVAL) {
10164
+ if (supplier.status !== ApprovalStatus2.PENDING_APPROVAL) {
10084
10165
  out(created);
10085
10166
  return;
10086
10167
  }
@@ -10147,6 +10228,10 @@ async function updateProjectFromPayload(raw) {
10147
10228
  const result = await updateProjectWithEstimateInvalidationConfirmation(input);
10148
10229
  out(result);
10149
10230
  }
10231
+ var getCompanyQuickBooksSyncStatus = async (companyId) => {
10232
+ const result = await api2.company.getQuickBooksSyncStatus.query({ companyId });
10233
+ out(result);
10234
+ };
10150
10235
  async function updateCompanyFromPayload(raw) {
10151
10236
  const input = parseCompanyUpdatePayload(raw);
10152
10237
  const result = await api2.company.updateCompany.mutate(input);
@@ -10398,16 +10483,14 @@ async function rejectQuotation(quotationId, reason) {
10398
10483
  });
10399
10484
  out(result);
10400
10485
  }
10401
- async function listCompanies(opts) {
10402
- const result = await api2.company.getCompanies.query({
10403
- name: opts.name,
10404
- createdBy: opts.createdBy,
10405
- active: opts.active,
10406
- page: opts.page ?? 1,
10407
- perPage: opts.perPage ?? 20
10408
- });
10486
+ var listCompanies = async (input) => {
10487
+ const result = await api2.company.getCompanies.query(input);
10409
10488
  out(result);
10410
- }
10489
+ };
10490
+ var countCompanies = async (input) => {
10491
+ const result = await api2.company.getCompanyCounts.query(input);
10492
+ out(result);
10493
+ };
10411
10494
  async function getCompany(id) {
10412
10495
  const company = await api2.company.getCompanyById.query({ id });
10413
10496
  out(company);
@@ -10416,31 +10499,26 @@ async function createCompany(opts) {
10416
10499
  const result = await api2.company.createCompany.mutate(opts);
10417
10500
  out(result);
10418
10501
  }
10419
- var searchExistingCompanies = async (query, page = 1) => {
10420
- out(await api2.company.searchExistingCompanies.query({ query, page }));
10502
+ var searchExistingCompanies = async (input) => {
10503
+ out(await api2.company.searchExistingCompanies.query(input));
10421
10504
  };
10422
10505
  var reuseExistingCompany = async (input) => {
10423
10506
  out(await api2.company.useExistingCompany.mutate(input));
10424
10507
  };
10425
- async function listProjects(opts) {
10426
- const result = await api2.project.getAllProjects.query({
10427
- companyId: opts.companyId,
10428
- name: opts.name,
10429
- status: opts.status,
10430
- active: opts.active,
10431
- page: opts.page ?? 1,
10432
- perPage: opts.perPage ?? 20
10433
- });
10508
+ var listProjects = async (input) => {
10509
+ const result = await api2.project.getAllProjects.query(input);
10434
10510
  out(result);
10435
- }
10436
- var listProjectHubs = async (opts) => {
10437
- const result = await api2.project.getAllProjectHubs.query({
10438
- ...opts,
10439
- page: opts.page ?? 1,
10440
- perPage: opts.perPage ?? 20
10441
- });
10511
+ };
10512
+ var countProjects = async (input) => {
10513
+ const result = await api2.project.getProjectCounts.query(input);
10442
10514
  out(result);
10443
10515
  };
10516
+ var listProjectHubs = async (input) => {
10517
+ out(await api2.project.getAllProjectHubs.query(input));
10518
+ };
10519
+ var countProjectHubs = async (input) => {
10520
+ out(await api2.project.getProjectHubCounts.query(input));
10521
+ };
10444
10522
  async function getProject(id) {
10445
10523
  const [project, budgetsOverview, billsOverview] = await Promise.all([
10446
10524
  api2.project.getProjectDetailsById.query({ id }),
@@ -10536,7 +10614,7 @@ var reconcileProject = async (projectId) => {
10536
10614
  out(result);
10537
10615
  };
10538
10616
  var completeProject = async (projectId) => {
10539
- await updateProjectStatus(projectId, ProjectStatus.COMPLETED);
10617
+ await updateProjectStatus(projectId, ProjectStatus2.COMPLETED);
10540
10618
  };
10541
10619
  var importQuickBooksProjectId = async (projectId, projectUrl) => {
10542
10620
  const result = await api2.project.importQboProjectId.mutate({
@@ -10545,13 +10623,70 @@ var importQuickBooksProjectId = async (projectId, projectUrl) => {
10545
10623
  });
10546
10624
  out(result);
10547
10625
  };
10548
- var listIntegrationOperations = async (input) => {
10549
- const result = await api2.integration.listOperations.query(input);
10550
- out(result);
10626
+ var listWebhookEventTypes = async () => {
10627
+ out(await api2.webhook.listEventTypes.query());
10551
10628
  };
10552
- var retryIntegrationOperation = async (operationId, confirmExternalStateReconciled, outboundEmailResolution, providerMessageId) => {
10553
- await assertSensitiveWorkflowConfirmed({
10554
- action: outboundEmailResolution === "ACCEPTED" ? "Reconcile integration operation as completed" : "Retry integration operation",
10629
+ var listWebhookEndpoints = async () => {
10630
+ out(await api2.webhook.listEndpoints.query());
10631
+ };
10632
+ var getWebhookEndpoint = async (id) => {
10633
+ out(await api2.webhook.getEndpoint.query({ id }));
10634
+ };
10635
+ var createWebhookEndpoint = async (input) => {
10636
+ const result = await api2.webhook.createEndpoint.mutate(input);
10637
+ out({
10638
+ ...result,
10639
+ note: "Store the signing secret now; Budget Builder never shows it again."
10640
+ });
10641
+ };
10642
+ var updateWebhookEndpoint = async (input) => {
10643
+ out(await api2.webhook.updateEndpoint.mutate(input));
10644
+ };
10645
+ var deleteWebhookEndpoint = async (id) => {
10646
+ await assertSensitiveWorkflowConfirmed({
10647
+ action: "Delete webhook endpoint and its delivery history",
10648
+ entity: `webhook endpoint ${id}`
10649
+ });
10650
+ out(await api2.webhook.deleteEndpoint.mutate({ id }));
10651
+ };
10652
+ var rotateWebhookSecret = async (input) => {
10653
+ await assertSensitiveWorkflowConfirmed({
10654
+ action: "Rotate webhook signing secret",
10655
+ entity: `webhook endpoint ${input.id}`,
10656
+ details: `previous secrets keep signing for ${input.previousSecretTtlHours ?? 24}h`
10657
+ });
10658
+ const result = await api2.webhook.rotateSecret.mutate(input);
10659
+ out({
10660
+ ...result,
10661
+ note: "Store the new signing secret now; Budget Builder never shows it again."
10662
+ });
10663
+ };
10664
+ var sendWebhookTestEvent = async (id) => {
10665
+ out(await api2.webhook.sendTestEvent.mutate({ id }));
10666
+ };
10667
+ var listWebhookDeliveries = async (input) => {
10668
+ out(await api2.webhook.listDeliveries.query(input));
10669
+ };
10670
+ var getWebhookDelivery = async (deliveryId) => {
10671
+ out(await api2.webhook.getDelivery.query({ deliveryId }));
10672
+ };
10673
+ var replayWebhookDelivery = async (deliveryId) => {
10674
+ out(await api2.webhook.replayDelivery.mutate({ deliveryId }));
10675
+ };
10676
+ var listIntegrationOperations = async (input) => {
10677
+ const result = await api2.integration.listOperations.query(input);
10678
+ out(result);
10679
+ };
10680
+ var getIntegrationOperation = async (operationId) => {
10681
+ const result = await api2.integration.getOperation.query({ operationId });
10682
+ out(result);
10683
+ };
10684
+ var countIntegrationOperations = async (input) => {
10685
+ out(await api2.integration.getCounts.query(input));
10686
+ };
10687
+ var retryIntegrationOperation = async (operationId, confirmExternalStateReconciled, outboundEmailResolution, providerMessageId) => {
10688
+ await assertSensitiveWorkflowConfirmed({
10689
+ action: outboundEmailResolution === "ACCEPTED" ? "Reconcile integration operation as completed" : "Retry integration operation (QuickBooks jobs with a saved attempt check QuickBooks first and never resend an applied change)",
10555
10690
  entity: `operation ${operationId}`,
10556
10691
  details: outboundEmailResolution === "ACCEPTED" ? `mark provider-accepted email sent with message ID ${providerMessageId ?? "<missing>"}` : outboundEmailResolution === "NOT_ACCEPTED_RETRY" ? "rotate the Resend idempotency key and retry provider delivery" : confirmExternalStateReconciled ? "after confirming the external state has been reconciled" : undefined
10557
10692
  });
@@ -10563,10 +10698,25 @@ var retryIntegrationOperation = async (operationId, confirmExternalStateReconcil
10563
10698
  });
10564
10699
  out(result);
10565
10700
  };
10701
+ var reconcileResourceOperation = async (operationId) => {
10702
+ await assertSensitiveWorkflowConfirmed({
10703
+ action: "Verify QuickBooks resource outcome",
10704
+ entity: `operation ${operationId}`,
10705
+ details: "verify the saved receipt or read QuickBooks when the attempt is uncertain, then queue local update/archive/reactivation finalization without repeating the provider mutation"
10706
+ });
10707
+ const result = await api2.integration.reconcileResourceOperation.mutate({
10708
+ operationId
10709
+ });
10710
+ out(result);
10711
+ };
10566
10712
  var listInboundSources = async (input) => {
10567
10713
  const result = await api2.inbound.listSources.query(input);
10568
10714
  out(result);
10569
10715
  };
10716
+ var countInboundSources = async (input) => {
10717
+ const result = await api2.inbound.countSources.query(input);
10718
+ out(result);
10719
+ };
10570
10720
  var createInboundSource = async (input) => {
10571
10721
  const result = await api2.inbound.createSource.mutate(input);
10572
10722
  out(result);
@@ -10583,6 +10733,9 @@ var listInboundSubmissions = async (input) => {
10583
10733
  const result = await api2.inbound.listSubmissions.query(input);
10584
10734
  out(result);
10585
10735
  };
10736
+ var countInboundSubmissions = async (input) => {
10737
+ out(await api2.inbound.countSubmissions.query(input));
10738
+ };
10586
10739
  var getInboundSubmission = async (submissionId) => {
10587
10740
  const result = await api2.inbound.getSubmission.query({ submissionId });
10588
10741
  out(result);
@@ -10610,39 +10763,32 @@ async function listContacts(companyId) {
10610
10763
  });
10611
10764
  out(contacts);
10612
10765
  }
10613
- async function listItems(opts) {
10614
- const filters = {
10615
- includeQuickBooksDeactivationPending: opts.includeQuickBooksDeactivationPending,
10616
- name: opts.name
10617
- };
10618
- const perPage = opts.perPage ?? 20;
10619
- const [items, { total, counts }] = await Promise.all([
10620
- api2.item.getItems.query({ ...filters, page: opts.page ?? 1, perPage }),
10621
- api2.item.getItemCounts.query(filters)
10622
- ]);
10623
- out({ items, pageCount: Math.ceil(total / perPage), counts });
10624
- }
10766
+ var listItems = async (input) => {
10767
+ out(await api2.item.getItems.query(input));
10768
+ };
10769
+ var countItems = async (input) => {
10770
+ out(await api2.item.getItemCounts.query(input));
10771
+ };
10625
10772
  async function getItem(id) {
10626
10773
  const item = await api2.item.getItem.query({ id });
10627
10774
  out(item);
10628
10775
  }
10629
- async function listItemCategories(opts) {
10630
- const result = await api2.itemCategory.getItemCategories.query({
10631
- page: opts.page ?? 1,
10632
- perPage: opts.perPage ?? 50
10633
- });
10776
+ var getItemQuickBooksSyncStatus = async (itemId) => {
10777
+ const result = await api2.item.getQuickBooksSyncStatus.query({ id: itemId });
10634
10778
  out(result);
10635
- }
10636
- async function getSupplierAnalytics(opts) {
10637
- const result = await api2.dashboard.getSupplierTableData.query({
10638
- page: opts.page ?? 1,
10639
- perPage: opts.perPage ?? 20,
10640
- sort: [],
10641
- name: opts.name,
10642
- timeFrame: opts.timeFrame ?? "ALL"
10643
- });
10644
- out(result);
10645
- }
10779
+ };
10780
+ var listItemCategories = async (input) => {
10781
+ out(await api2.itemCategory.getItemCategories.query(input));
10782
+ };
10783
+ var countItemCategories = async (input) => {
10784
+ out(await api2.itemCategory.getCounts.query(input));
10785
+ };
10786
+ var getSupplierAnalytics = async (input) => {
10787
+ out(await api2.dashboard.getSupplierTableData.query(input));
10788
+ };
10789
+ var countSupplierAnalytics = async (input) => {
10790
+ out(await api2.dashboard.getSupplierTableCounts.query(input));
10791
+ };
10646
10792
  async function createUser(input) {
10647
10793
  const user = await api2.user.create.mutate(input);
10648
10794
  out(user);
@@ -10659,10 +10805,12 @@ async function revokeApiKeyForUser(input) {
10659
10805
  const result = await api2.mcpApiKey.revoke.mutate(input);
10660
10806
  out(result);
10661
10807
  }
10662
- async function listUsers() {
10663
- const users = await api2.user.getAllUsers.query();
10664
- out(users);
10665
- }
10808
+ var listUsers = async (input) => {
10809
+ out(await api2.user.searchUsers.query(input));
10810
+ };
10811
+ var countUsers = async (input) => {
10812
+ out(await api2.user.getCounts.query(input));
10813
+ };
10666
10814
  async function whoAmI() {
10667
10815
  const user = await api2.user.getCurrentUser.query();
10668
10816
  out(user);
@@ -10726,33 +10874,37 @@ async function getFinancialOverview(input) {
10726
10874
  });
10727
10875
  out(result);
10728
10876
  }
10729
- async function getRecentErrors(opts) {
10730
- const result = await api2.errorLog.getRecentErrors.query({
10731
- page: opts.page ?? 1,
10732
- perPage: opts.perPage ?? 10,
10733
- severity: opts.severity,
10734
- status: opts.status
10735
- });
10736
- out(result);
10737
- }
10877
+ var getRecentErrors = async (input) => {
10878
+ out(await api2.errorLog.getRecentErrors.query(input));
10879
+ };
10880
+ var getError = async (errorId) => {
10881
+ out(await api2.errorLog.getErrorById.query({ errorId }));
10882
+ };
10883
+ var countErrors = async (input) => {
10884
+ out(await api2.errorLog.countErrors.query(input));
10885
+ };
10738
10886
  async function getErrorMetrics() {
10739
10887
  const result = await api2.errorLog.getErrorMetrics.query({});
10740
10888
  out(result);
10741
10889
  }
10742
10890
  async function getApprovedBudgets(opts) {
10743
- const perPage = Math.min(opts.limit ?? 50, 100);
10744
- const { budgets, totalCount } = await api2.budget.getAllBudgets.query({
10891
+ const filters = {
10745
10892
  projectId: opts.projectId,
10746
10893
  statuses: [
10747
10894
  ExtendedBudgetStatus.APPROVED,
10748
10895
  ExtendedBudgetStatus.ESTIMATE_ACCEPTED,
10749
10896
  ExtendedBudgetStatus.ESTIMATE_CLOSED
10750
- ],
10751
- page: 1,
10752
- perPage,
10753
- includeDetails: false
10754
- });
10755
- const items = (budgets ?? []).map((b) => {
10897
+ ]
10898
+ };
10899
+ const [{ items: budgets }, { totalCount }] = await Promise.all([
10900
+ api2.budget.getAllBudgets.query({
10901
+ ...filters,
10902
+ limit: Math.min(opts.limit ?? 50, 100),
10903
+ includeDetails: false
10904
+ }),
10905
+ api2.budget.getCounts.query(filters)
10906
+ ]);
10907
+ const items = budgets.map((b) => {
10756
10908
  const calc = b;
10757
10909
  const revenue = calc.totalSellingAfterDiscount ?? 0;
10758
10910
  const cost = calc.totalCostWithoutGst ?? 0;
@@ -10840,7 +10992,7 @@ function parseArgs(argv) {
10840
10992
  }
10841
10993
  }
10842
10994
  } else if (arg === "-q") {
10843
- flags.q = true;
10995
+ flags.quiet = true;
10844
10996
  } else {
10845
10997
  positional.push(arg);
10846
10998
  }
@@ -10924,7 +11076,7 @@ var payloadHelp = {
10924
11076
  },
10925
11077
  "update-project": {
10926
11078
  required: "id: string; name: string; companyId: string; contactPersonId: string; asanaTaskId: string; insideSalesId: string; businessDevelopmentId: string; venue: string; dateRange: {from: ISO date}",
10927
- optional: "dateRange.to: ISO date; projectManagerId: string; pax: string; description: string; slackChannelId/slackChannelUrl/slackChannelName: strings (supply all three); requestQboAccountantNotification: boolean; confirmEstimateInvalidation: boolean",
11079
+ optional: "dateRange.to: ISO date; projectManagerId: string; pax: string; description: string; slackChannelId/slackChannelUrl/slackChannelName: strings (supply all three); requestQboAccountantNotification: boolean; confirmEstimateInvalidation: boolean. Changing companyId requires a contactPersonId from the new company; the company change itself does not reject existing estimates or touch existing invoices, but date, pax or venue changes in the same save still require confirmEstimateInvalidation.",
10928
11080
  example: '{"id":"<project-id>","name":"Launch","companyId":"<company-id>","contactPersonId":"<contact-id>","asanaTaskId":"<asana-task-id>","insideSalesId":"<user-id>","businessDevelopmentId":"<user-id>","venue":"Hall","dateRange":{"from":"2026-10-01T09:00:00Z"}}'
10929
11081
  },
10930
11082
  "create-supplier": {
@@ -11192,6 +11344,7 @@ var financialWriteTargets = new Set([
11192
11344
  ]);
11193
11345
  var deleteTargets = new Set([
11194
11346
  "cleanup-staged-bill-attachments",
11347
+ "delete-webhook-endpoint",
11195
11348
  "cleanup-staged-quotation-attachments",
11196
11349
  "discard-customer-invoice",
11197
11350
  "complete-project",
@@ -11200,7 +11353,14 @@ var deleteTargets = new Set([
11200
11353
  "restore-budget-version",
11201
11354
  "update-project-status"
11202
11355
  ]);
11356
+ var explicitEffects = {
11357
+ "rotate-webhook-secret": ["state-change"],
11358
+ "replay-webhook-delivery": ["state-change", "external-write"]
11359
+ };
11203
11360
  var effectsFor = (legacyTarget) => {
11361
+ const explicit = explicitEffects[legacyTarget];
11362
+ if (explicit)
11363
+ return explicit;
11204
11364
  const effects = new Set;
11205
11365
  if (mutationPrefixes.some((prefix) => legacyTarget.startsWith(prefix))) {
11206
11366
  effects.add("state-change");
@@ -11218,16 +11378,78 @@ var effectsFor = (legacyTarget) => {
11218
11378
  };
11219
11379
  var titleCase = (value) => value.split("-").map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`).join(" ");
11220
11380
  var summaryFor = (legacyTarget) => {
11381
+ if (legacyTarget === "update-budget-status")
11382
+ return "Update budget status. APPROVED requires a current Lead/Admin role; budget management permission alone is insufficient. Ordinary users must request approval. Status changes invalidate old pending requests.";
11383
+ if (legacyTarget === "approve-budget" || legacyTarget === "reject-budget")
11384
+ return "Decide your assigned pending budget approval as a current Lead/Admin. Material edits supersede old requests; inspect the latest content and use a fresh request after edits.";
11385
+ if (legacyTarget === "create-budget-approval")
11386
+ return "Request budget approval for the current content, superseding older pending requests. Existing Lead/Admin autoapproval remains available.";
11387
+ if (legacyTarget === "update-budget-item-supplier")
11388
+ return "Change a budget item's eligible supplier/GST snapshot as a material financial edit. Supersede pending approvals and apply the standard budget-to-DRAFT reset policy; inspect and request approval again. An unchanged supplier is a no-op.";
11389
+ if (legacyTarget === "submit-quotation")
11390
+ return "Submit a quotation for signing and approval. Claim the captured revision and exact allocation selection; concurrent edits, budget-item deletion or restore cascades conflict instead of attaching old signed PDFs. Refresh and resubmit the current content.";
11391
+ if (legacyTarget === "approve-quotation")
11392
+ return "Approve your assigned pending quotation as a current Admin. Claim the captured revision and exact allocation selection after signing; concurrent changes conflict and roll back the decision, signed attachment and email/outbox. Refresh and review the current content before retrying.";
11221
11393
  if (legacyTarget === "approve-supplier")
11222
11394
  return "Approve a pending supplier request and send a requester reply email, or directly re-approve a rejected supplier as Admin/Lead without a pending request or email. QuickBooks is synchronized; archived suppliers remain archived. Argument: <supplierId>.";
11395
+ if (legacyTarget === "get-bill-details")
11396
+ return "Get a bill's summary as shown in the web app: status, amounts, people, payment and line totals. Use get-bill-line-items, get-bill-approvals, get-bill-attachments or get-bill-payment for more detail. Argument: <billId>.";
11397
+ if (legacyTarget === "get-bill-line-items")
11398
+ return "List a bill's line items with budget-item and quotation-allocation detail. <billId> [--limit 1-100] [--cursor <token>] Newest first. Returns { items, pageInfo }; follow nextCursor.";
11399
+ if (legacyTarget === "get-bill-approvals")
11400
+ return "List a bill's approval requests with approver, requester and responder. <billId> [--limit 1-50] [--cursor <token>] Newest first. Returns { items, pageInfo }; follow nextCursor.";
11401
+ if (legacyTarget === "get-bill-payment")
11402
+ return "Get a bill's payment status, reference, date, tracking URL, QuickBooks link and external payment. Argument: <billId>.";
11403
+ if (legacyTarget === "get-quotation")
11404
+ return "Get a quotation as shown in the web app: status, amounts, people, line totals, files and approvals. Use get-quotation-line-items for budget-item detail. Argument: <quotationId>.";
11405
+ if (legacyTarget === "get-customer-invoice")
11406
+ return "Get an invoice batch as shown in the web app: status, totals, people, approvals and invoice summaries. Use get-customer-invoice-invoices, get-customer-invoice-lines, get-customer-invoice-approvals or get-customer-invoice-history for more detail. Argument: <batchId>.";
11407
+ if (legacyTarget === "get-customer-invoice-invoices")
11408
+ return "List a batch's invoices with QuickBooks id/doc number, issue/due dates, amounts, sync state and sender. Argument: <batchId>.";
11409
+ if (legacyTarget === "get-customer-invoice-lines")
11410
+ return "List one invoice's lines (name, description, QuickBooks item/tax code, quantity, amounts). <invoiceId> [--limit 1-100] [--cursor <token>] Newest first. Returns { items, pageInfo }; follow nextCursor.";
11411
+ if (legacyTarget === "get-customer-invoice-approvals")
11412
+ return "List an invoice batch's approval requests with approver, requester and responder. <batchId> [--limit 1-50] [--cursor <token>] Newest first. Returns { items, pageInfo }; follow nextCursor.";
11413
+ if (legacyTarget === "get-customer-invoice-history")
11414
+ return "List an invoice batch's status history (batch/invoice status changes, source, note, actor). <batchId> [--limit 1-100] [--cursor <token>] Newest first. Returns { items, pageInfo }; follow nextCursor.";
11415
+ if (legacyTarget === "get-quotation-line-items")
11416
+ return "List a quotation's line items with budget-item, supplier and budget detail. <quotationId> [--limit 1-100] [--cursor <token>] Newest first. Returns { items, pageInfo }; follow nextCursor.";
11417
+ if (legacyTarget === "get-bill-attachments")
11418
+ return "List bill attachments with cursor pagination. <billId> [--limit 1-50] [--cursor <token>] Newest first. Returns { items, pageInfo }; follow nextCursor.";
11419
+ if (legacyTarget === "get-budget-categories")
11420
+ return "List budget categories with cursor pagination. [--q] [--limit 1-100] [--cursor <token>] Active categories, name A\u2013Z. Returns { items, pageInfo }; follow nextCursor with the same --q.";
11421
+ if (legacyTarget === "get-supplier-analytics")
11422
+ return "List supplier analytics rows with cursor pagination. [--name] [--status CSV] [--gstRegistered true|false] [--createdBy <text>] [--timeFrame ALL|LAST_YEAR|LAST_6_MONTHS|LAST_3_MONTHS|CUSTOM] [--startDate <ISO> --endDate <ISO>] [--sortBy name|gstRegistered|status|phone|email|updatedAt|createdAt] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward] Admin. Returns { items, pageInfo }; follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected.";
11423
+ if (legacyTarget === "list-api-keys")
11424
+ return "List API keys with cursor pagination. [--userId <userId>] [--limit 1-100] [--cursor <token>] (newest first, { items, pageInfo }; defaults to the caller; admin required for another user)";
11425
+ if (legacyTarget === "list-claims")
11426
+ return "List reimbursable claims with cursor pagination. Same flags as list-bills (--limit 1-100, --cursor, --direction forward|backward); ignores --isClaimable. Returns { items, pageInfo }.";
11427
+ if (legacyTarget === "list-bills")
11428
+ return "List bills with cursor pagination. [--projectId] [--budgetId] [--status CSV] [--search <text>] [--isClaimable true|false] [--createdByIds <csv>] [--sortBy createdAt|amount|status] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward] Returns { items, pageInfo }; pass nextCursor with --direction forward or previousCursor with --direction backward, keeping the same filters and sort. --page/--pageSize/--perPage are rejected.";
11429
+ if (legacyTarget === "list-customer-invoices")
11430
+ return "List customer invoice batches with cursor pagination. [budgetId] [--projectId <id>] [--companyIds CSV] [--projectIds CSV] [--budgetIds CSV] [--createdByIds CSV] [--status CSV] [--search] [--sortBy createdAt|status|totalInvoiceAmount|approvalExpiresAt] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward] Returns { items, pageInfo }; follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected. No scope flag lists invoices globally. --projectId uses the project scope. A positional budgetId is a backward-compatible shortcut for --budgetIds.";
11431
+ if (legacyTarget === "list-quotations")
11432
+ return "List quotations with cursor pagination. [--projectId] [--budgetId] [--supplierId] [--status CSV] [--createdBy CSV] [--search] [--limit 1-100] [--cursor <token>] [--direction forward|backward] Newest first; returns { items, pageInfo }. Follow nextCursor/previousCursor with the same filters. --page/--pageSize/--perPage are rejected; --requesterIds remains an alias for --createdBy.";
11433
+ if (legacyTarget === "list-users")
11434
+ return "List users with cursor pagination. [--q] [--limit 1-100] [--cursor <token>] Name A\u2013Z. Returns { items, pageInfo }; follow nextCursor with the same --q. --page/--perPage are rejected.";
11435
+ if (legacyTarget === "list-companies")
11436
+ return "List companies with cursor pagination. Returns { items, pageInfo }; default sort is createdAt descending with limit 20. Pass nextCursor forward or previousCursor backward unchanged. Legacy page/perPage flags are rejected.";
11437
+ if (legacyTarget === "count-companies")
11438
+ return "Count companies independently from list pagination. Returns { totalCount, counts: { active: { true, false } } }; accepts filters only and rejects pagination/sorting flags.";
11439
+ if (legacyTarget === "list-projects")
11440
+ return "List projects with cursor pagination. Returns { items, pageInfo }; default sort is createdAt descending with limit 20. Supports legacy --status plus optional --statuses, WON-date filters (--wonFrom, --wonTo, --wonWithinDays) and --sortBy wonAt; pass nextCursor forward or previousCursor backward unchanged. Legacy page/perPage flags are rejected.";
11441
+ if (legacyTarget === "count-projects")
11442
+ return "Count projects independently from list pagination. Returns { totalCount, counts: { status, active: { true, false } } }; accepts project filters only and rejects pagination/sorting flags.";
11223
11443
  if (legacyTarget === "search-existing-companies")
11224
- return "Search BB and QuickBooks companies without writes. --query <name> [--page <number>].";
11444
+ return "Search BB and QuickBooks companies without writes. --query <name> [--limit 1-50] [--cursor <token>]. Returns { items, totalCount, pageInfo }; follow pageInfo.nextCursor with the same --query. Legacy --page is rejected.";
11225
11445
  if (legacyTarget === "use-existing-company")
11226
11446
  return "Reuse a company in BB and QuickBooks, creating only its missing counterpart. Supply --companyId <bbId> and/or --quickbooksId <qboId>; both explicitly connect records with different names. --website <url> fills a missing QBO import website only. --confirmDistinct true confirms no similar record represents this entity.";
11227
11447
  if (legacyTarget === "create-company")
11228
11448
  return "Create or reuse a company in BB and QuickBooks. --name <name> --website <url>. Similar names require --confirmDistinct true; inactive companies are never reactivated.";
11229
11449
  if (legacyTarget === "create-project")
11230
11450
  return "Create a project linked to an existing Asana Deals lead task. Supply --asanaTaskId in non-interactive use; interactive use searches for and selects a lead task (optionally --asanaSearch). The Slack channel is resolved from the selected Asana task unless all three Slack channel flags are supplied. For a new direct client, create the lead task using the website form first.";
11451
+ if (legacyTarget === "create-supplier")
11452
+ return "Create a supplier using --payload JSON; pending-approval suppliers also request approval and send emails. Reusing an archived name requires Admin; normal users and Leads must ask an Admin to reactivate it before editing. Recreation preserves the existing approval status and creator.";
11231
11453
  if (legacyTarget === "preview-qbo-customer-invoice") {
11232
11454
  return "Preview an available QuickBooks invoice selected for a BB budget using read-only QBO access. Arguments: <budgetId> <qboInvoiceId>.";
11233
11455
  }
@@ -11243,8 +11465,22 @@ var summaryFor = (legacyTarget) => {
11243
11465
  if (legacyTarget === "whoami")
11244
11466
  return "Show the active API-key identity.";
11245
11467
  if (legacyTarget === "list-budgets") {
11246
- return "List budgets. Omit --active for active budgets only; use --active false or --active all for Admin-only historical reads.";
11247
- }
11468
+ return "List one cursor page of budgets ({ items, pageInfo }; --limit/--cursor/--direction). Omit --active for active budgets only; use --active false or --active all for Admin-only historical reads.";
11469
+ }
11470
+ if (legacyTarget === "list-items")
11471
+ return "List one cursor page of catalogue items ({ items, pageInfo }; --limit/--cursor/--direction, optional --sortBy/--sortDir). --page/--perPage are rejected.";
11472
+ if (legacyTarget === "list-item-categories")
11473
+ return "List one cursor page of active item categories ({ items, pageInfo }; --limit/--cursor/--direction, optional --name/--sortBy/--sortDir). --page/--perPage are rejected.";
11474
+ if (legacyTarget === "count-item-categories")
11475
+ return "Count active item categories matching --name: { totalCount }.";
11476
+ if (legacyTarget === "list-integration-operations")
11477
+ return "List one cursor page of integration operations, newest first ({ items, pageInfo }; --limit/--cursor/--direction). --page/--perPage are rejected. Admin only.";
11478
+ if (legacyTarget === "count-integration-operations")
11479
+ return "Provider filter options for integration operations: { filterOptions }. No total; filter flags are accepted but do not change the result. Admin only.";
11480
+ if (legacyTarget === "count-items")
11481
+ return "Count catalogue items matching filters: total plus category and supplier facets (each ignores its own filter).";
11482
+ if (legacyTarget === "count-budgets")
11483
+ return "Count budgets matching filters: totalCount plus status and active facets. Omit --active for active budgets only.";
11248
11484
  if (legacyTarget === "get-budget") {
11249
11485
  return "Get a budget by ID. Omit --includeInactive for active budgets only; --includeInactive is an Admin-only historical read.";
11250
11486
  }
@@ -11270,6 +11506,8 @@ var summaryFor = (legacyTarget) => {
11270
11506
  if (legacyTarget === "create-quotation") {
11271
11507
  return 'Create a quotation with an approved supplier using --payload JSON. Include projectId, budgetId, supplierId, budgetItemIds, amountWithoutGst, gstAmount, and one uploaded attachment in attachments. Multiple budget items require lineAmounts: [{"budgetItemId":"<first-id>","amount":750},{"budgetItemId":"<second-id>","amount":450}], covering every selected budget-item ID exactly once and summing to amountWithoutGst. Amounts exclude GST; use gstAmount: 0 for no GST. Example: amountWithoutGst: 1200 with line amounts 750 and 450.';
11272
11508
  }
11509
+ if (legacyTarget === "cleanup-staged-quotation-attachments")
11510
+ return "Queue independent cleanup operations for unattached staged quotation uploads; returns operation IDs and queued/skipped counts, not deletion confirmation. Arguments: <projectId> --keys <csv>.";
11273
11511
  if (legacyTarget === "update-quotation") {
11274
11512
  return 'Update a DRAFT or REJECTED quotation using --payload JSON with id and the complete creation payload. Multiple budget items require lineAmounts: [{"budgetItemId":"<first-id>","amount":750},{"budgetItemId":"<second-id>","amount":450}], covering every budgetItemIds entry exactly once and summing to amountWithoutGst. Amounts exclude GST; use gstAmount: 0 for no GST.';
11275
11513
  }
@@ -11279,6 +11517,32 @@ var summaryFor = (legacyTarget) => {
11279
11517
  if (legacyTarget === "send-customer-invoice-to-contact-person") {
11280
11518
  return "Send a customer invoice and update the project billing email.";
11281
11519
  }
11520
+ if (legacyTarget === "update-company")
11521
+ return "Save company fields in BB and queue independent QuickBooks name/website synchronization; returns operation ID, not provider completion.";
11522
+ if (legacyTarget === "get-company-quickbooks-sync-status")
11523
+ return "Read saved company synchronization status without provider calls or polling; use companyId.";
11524
+ if (legacyTarget === "get-item-quickbooks-sync-status")
11525
+ return "Read saved item QuickBooks synchronization status without provider calls or polling; use itemId.";
11526
+ if (legacyTarget === "create-webhook-endpoint")
11527
+ return "Admin-only: subscribe an HTTPS endpoint to BB events. --name --url --events <type|prefix.*|*,...>. Prints the whsec_ signing secret once; deliveries use Standard Webhooks headers.";
11528
+ if (legacyTarget === "rotate-webhook-secret")
11529
+ return "Admin-only: issue a new signing secret; older secrets keep signing for --previousSecretTtlHours (default 24). Prints the new secret once. Argument: <endpointId>.";
11530
+ if (legacyTarget === "send-webhook-test-event")
11531
+ return "Admin-only: send a signed webhook.test event to one endpoint. Argument: <endpointId>.";
11532
+ if (legacyTarget === "replay-webhook-delivery")
11533
+ return "Admin-only: resend a finished webhook delivery with the same eventId on a fresh attempt chain. Argument: <deliveryId>.";
11534
+ if (legacyTarget === "get-error")
11535
+ return "Admin-only: one error log with full diagnostics (stack, cause, request details, metadata, AI summary, resolution). Argument: <errorId>. error recent list returns table columns only.";
11536
+ if (legacyTarget === "get-webhook-delivery")
11537
+ return "Admin-only: one webhook delivery with payload and attempts (status codes, errors, durations). Argument: <deliveryId>.";
11538
+ if (legacyTarget === "list-webhook-event-types")
11539
+ return "List the outbound webhook event catalogue with group and live/planned status.";
11540
+ if (legacyTarget === "reconcile-resource-operation")
11541
+ return "Admin-only check of a saved QuickBooks invoice/company/item job outcome; reads the provider only when uncertain. Applied changes are finished locally without repeating the provider mutation; provably unapplied ones are queued again. Argument: <operationId>.";
11542
+ if (legacyTarget === "get-integration-operation")
11543
+ return "Admin-only read of one integration job: who queued it, the records it is for with app links, saved QuickBooks progress, admin actions and Retry state. No provider calls. Argument: <operationId>.";
11544
+ if (legacyTarget === "retry-integration-operation")
11545
+ return "Admin-only retry of a failed integration job. QuickBooks jobs with a saved attempt check QuickBooks first: applied changes finish without resending, provably unapplied ones are sent again, unprovable ones stay stopped with the reason. Argument: <operationId>.";
11282
11546
  if (legacyTarget.startsWith("list-")) {
11283
11547
  return `List ${legacyTarget.slice("list-".length).replaceAll("-", " ")}.`;
11284
11548
  }
@@ -11329,6 +11593,24 @@ var registry2 = [
11329
11593
  authCommand("logout", "Revoke the interactive CLI session and remove its local credential."),
11330
11594
  legacyCommand("whoami", ["auth", "whoami"]),
11331
11595
  legacyCommand("list-budgets", ["budget", "list"], [], [
11596
+ {
11597
+ name: "--active <true|false|all>",
11598
+ description: "Omit for active budgets only; false or all requires Admin access."
11599
+ },
11600
+ {
11601
+ name: "--limit <1-100>",
11602
+ description: "Rows per page. Output is { items, pageInfo }."
11603
+ },
11604
+ {
11605
+ name: "--cursor <token>",
11606
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
11607
+ },
11608
+ {
11609
+ name: "--direction <forward|backward>",
11610
+ description: "forward (default) or backward; backward needs --cursor."
11611
+ }
11612
+ ]),
11613
+ legacyCommand("count-budgets", ["budget", "count"], [], [
11332
11614
  {
11333
11615
  name: "--active <true|false|all>",
11334
11616
  description: "Omit for active budgets only; false or all requires Admin access."
@@ -11354,7 +11636,20 @@ var registry2 = [
11354
11636
  description: "Include inactive historical budgets; Admin only."
11355
11637
  }
11356
11638
  ]),
11357
- legacyCommand("get-budget-categories", ["budget", "category", "list"]),
11639
+ legacyCommand("get-budget-categories", ["budget", "category", "list"], [], [
11640
+ {
11641
+ name: "--q <text>",
11642
+ description: "Search active category names (name A\u2013Z). A bare --q is rejected; use -q/--quiet to hide logs."
11643
+ },
11644
+ {
11645
+ name: "--limit <1-100>",
11646
+ description: "Rows per page. Output is { items, pageInfo }."
11647
+ },
11648
+ {
11649
+ name: "--cursor <token>",
11650
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11651
+ }
11652
+ ]),
11358
11653
  legacyCommand("get-budget-versions", ["budget", "version", "list"], [], [
11359
11654
  {
11360
11655
  name: "--limit <1-50>",
@@ -11414,8 +11709,36 @@ var registry2 = [
11414
11709
  "placeholder-bill",
11415
11710
  "create"
11416
11711
  ]),
11417
- legacyCommand("list-bills", ["bill", "list"]),
11418
- legacyCommand("list-claims", ["claim", "list"]),
11712
+ legacyCommand("list-bills", ["bill", "list"], [], [
11713
+ {
11714
+ name: "--limit <1-100>",
11715
+ description: "Rows per page. Output is { items, pageInfo }."
11716
+ },
11717
+ {
11718
+ name: "--cursor <token>",
11719
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
11720
+ },
11721
+ {
11722
+ name: "--direction <forward|backward>",
11723
+ description: "forward (default) or backward; backward needs --cursor."
11724
+ }
11725
+ ]),
11726
+ legacyCommand("list-claims", ["claim", "list"], [], [
11727
+ {
11728
+ name: "--limit <1-100>",
11729
+ description: "Rows per page. Output is { items, pageInfo }."
11730
+ },
11731
+ {
11732
+ name: "--cursor <token>",
11733
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
11734
+ },
11735
+ {
11736
+ name: "--direction <forward|backward>",
11737
+ description: "forward (default) or backward; backward needs --cursor."
11738
+ }
11739
+ ]),
11740
+ legacyCommand("count-bills", ["bill", "count"]),
11741
+ legacyCommand("count-claims", ["claim", "count"]),
11419
11742
  legacyCommand("create-bill-approval", ["bill", "approval", "create"]),
11420
11743
  legacyCommand("create-bill", ["bill", "create"], [], [
11421
11744
  {
@@ -11471,7 +11794,16 @@ var registry2 = [
11471
11794
  "invoice-number",
11472
11795
  "patch"
11473
11796
  ]),
11474
- legacyCommand("get-bill-attachments", ["bill", "attachment", "list"]),
11797
+ legacyCommand("get-bill-attachments", ["bill", "attachment", "list"], [], [
11798
+ {
11799
+ name: "--limit <1-50>",
11800
+ description: "Rows per page. Output is { items, pageInfo }."
11801
+ },
11802
+ {
11803
+ name: "--cursor <token>",
11804
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11805
+ }
11806
+ ]),
11475
11807
  legacyCommand("stage-bill-attachment", ["bill", "attachment", "stage"]),
11476
11808
  legacyCommand("cleanup-staged-bill-attachments", [
11477
11809
  "bill",
@@ -11487,8 +11819,53 @@ var registry2 = [
11487
11819
  ]),
11488
11820
  legacyCommand("attach-bill-documents", ["bill", "document", "attach"]),
11489
11821
  legacyCommand("get-bill-details", ["bill", "get"]),
11490
- legacyCommand("list-quotations", ["quotation", "list"]),
11822
+ legacyCommand("get-bill-line-items", ["bill", "line-item", "list"], [], [
11823
+ {
11824
+ name: "--limit <1-100>",
11825
+ description: "Rows per page. Output is { items, pageInfo }."
11826
+ },
11827
+ {
11828
+ name: "--cursor <token>",
11829
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11830
+ }
11831
+ ]),
11832
+ legacyCommand("get-bill-approvals", ["bill", "approval", "list"], [], [
11833
+ {
11834
+ name: "--limit <1-50>",
11835
+ description: "Rows per page. Output is { items, pageInfo }."
11836
+ },
11837
+ {
11838
+ name: "--cursor <token>",
11839
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11840
+ }
11841
+ ]),
11842
+ legacyCommand("get-bill-payment", ["bill", "payment", "get"]),
11843
+ legacyCommand("list-quotations", ["quotation", "list"], [], [
11844
+ {
11845
+ name: "--limit <1-100>",
11846
+ description: "Rows per page. Output is { items, pageInfo }."
11847
+ },
11848
+ {
11849
+ name: "--cursor <token>",
11850
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
11851
+ },
11852
+ {
11853
+ name: "--direction <forward|backward>",
11854
+ description: "forward (default) or backward; backward needs --cursor."
11855
+ }
11856
+ ]),
11857
+ legacyCommand("count-quotations", ["quotation", "count"]),
11491
11858
  legacyCommand("get-quotation", ["quotation", "get"]),
11859
+ legacyCommand("get-quotation-line-items", ["quotation", "line-item", "list"], [], [
11860
+ {
11861
+ name: "--limit <1-100>",
11862
+ description: "Rows per page. Output is { items, pageInfo }."
11863
+ },
11864
+ {
11865
+ name: "--cursor <token>",
11866
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11867
+ }
11868
+ ]),
11492
11869
  legacyCommand("upload-quotation-attachment", [
11493
11870
  "quotation",
11494
11871
  "attachment",
@@ -11539,8 +11916,57 @@ var registry2 = [
11539
11916
  "existing",
11540
11917
  "list"
11541
11918
  ]),
11542
- legacyCommand("list-customer-invoices", ["customer-invoice", "list"]),
11919
+ legacyCommand("list-customer-invoices", ["customer-invoice", "list"], [], [
11920
+ {
11921
+ name: "--limit <1-100>",
11922
+ description: "Rows per page. Output is { items, pageInfo }."
11923
+ },
11924
+ {
11925
+ name: "--cursor <token>",
11926
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
11927
+ },
11928
+ {
11929
+ name: "--direction <forward|backward>",
11930
+ description: "forward (default) or backward; backward needs --cursor."
11931
+ }
11932
+ ]),
11933
+ legacyCommand("count-customer-invoices", ["customer-invoice", "count"]),
11543
11934
  legacyCommand("get-customer-invoice", ["customer-invoice", "get"]),
11935
+ legacyCommand("get-customer-invoice-invoices", [
11936
+ "customer-invoice",
11937
+ "invoice",
11938
+ "list"
11939
+ ]),
11940
+ legacyCommand("get-customer-invoice-lines", ["customer-invoice", "line", "list"], [], [
11941
+ {
11942
+ name: "--limit <1-100>",
11943
+ description: "Rows per page. Output is { items, pageInfo }."
11944
+ },
11945
+ {
11946
+ name: "--cursor <token>",
11947
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11948
+ }
11949
+ ]),
11950
+ legacyCommand("get-customer-invoice-approvals", ["customer-invoice", "approval", "list"], [], [
11951
+ {
11952
+ name: "--limit <1-50>",
11953
+ description: "Rows per page. Output is { items, pageInfo }."
11954
+ },
11955
+ {
11956
+ name: "--cursor <token>",
11957
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11958
+ }
11959
+ ]),
11960
+ legacyCommand("get-customer-invoice-history", ["customer-invoice", "history", "list"], [], [
11961
+ {
11962
+ name: "--limit <1-100>",
11963
+ description: "Rows per page. Output is { items, pageInfo }."
11964
+ },
11965
+ {
11966
+ name: "--cursor <token>",
11967
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
11968
+ }
11969
+ ]),
11544
11970
  legacyCommand("get-customer-invoice-email-context", [
11545
11971
  "customer-invoice",
11546
11972
  "email-context",
@@ -11578,7 +12004,62 @@ var registry2 = [
11578
12004
  legacyCommand("approve-supplier", ["supplier", "approve"]),
11579
12005
  legacyCommand("reject-supplier", ["supplier", "reject"]),
11580
12006
  legacyCommand("get-supplier-details", ["supplier", "detail", "get"]),
11581
- legacyCommand("list-companies", ["company", "list"]),
12007
+ legacyCommand("list-companies", ["company", "list"], [], [
12008
+ {
12009
+ name: "--name <text>",
12010
+ description: "Filter by company name substring."
12011
+ },
12012
+ {
12013
+ name: "--createdBy <userId>",
12014
+ description: "Filter by one creator user ID."
12015
+ },
12016
+ {
12017
+ name: "--createdByIds <csv>",
12018
+ description: "Filter by a comma-separated creator user ID list."
12019
+ },
12020
+ {
12021
+ name: "--active <true|false>",
12022
+ description: "Filter by active status; omit to include both states."
12023
+ },
12024
+ {
12025
+ name: "--sortBy <name|createdAt>",
12026
+ description: "Sort field (default createdAt)."
12027
+ },
12028
+ {
12029
+ name: "--sortDir <asc|desc>",
12030
+ description: "Sort direction (default desc)."
12031
+ },
12032
+ {
12033
+ name: "--limit <1-100>",
12034
+ description: "Bounded page size (default 20)."
12035
+ },
12036
+ {
12037
+ name: "--cursor <token>",
12038
+ description: "Opaque pageInfo cursor; pass nextCursor forward or previousCursor backward unchanged."
12039
+ },
12040
+ {
12041
+ name: "--direction <forward|backward>",
12042
+ description: "Use forward by default; backward requires --cursor."
12043
+ }
12044
+ ]),
12045
+ legacyCommand("count-companies", ["company", "count"], [], [
12046
+ {
12047
+ name: "--name <text>",
12048
+ description: "Filter by company name substring."
12049
+ },
12050
+ {
12051
+ name: "--createdBy <userId>",
12052
+ description: "Filter by one creator user ID."
12053
+ },
12054
+ {
12055
+ name: "--createdByIds <csv>",
12056
+ description: "Filter by a comma-separated creator user ID list."
12057
+ },
12058
+ {
12059
+ name: "--active <true|false>",
12060
+ description: "Filter by active status; facets omit this self-filter."
12061
+ }
12062
+ ]),
11582
12063
  legacyCommand("get-company", ["company", "get"]),
11583
12064
  legacyCommand("search-existing-companies", ["company", "search"], [], [
11584
12065
  {
@@ -11586,8 +12067,12 @@ var registry2 = [
11586
12067
  description: "Company name to search in BB and QuickBooks."
11587
12068
  },
11588
12069
  {
11589
- name: "--page",
11590
- description: "Results page (eight companies per page)."
12070
+ name: "--limit",
12071
+ description: "Matches per page, 1\u201350 (default 8)."
12072
+ },
12073
+ {
12074
+ name: "--cursor",
12075
+ description: "pageInfo.nextCursor from the previous page, unchanged."
11591
12076
  }
11592
12077
  ]),
11593
12078
  legacyCommand("use-existing-company", ["company", "use"], [], [
@@ -11615,8 +12100,94 @@ var registry2 = [
11615
12100
  }
11616
12101
  ]),
11617
12102
  legacyCommand("update-company", ["company", "update"]),
12103
+ legacyCommand("get-company-quickbooks-sync-status", [
12104
+ "company",
12105
+ "sync",
12106
+ "status"
12107
+ ]),
11618
12108
  legacyCommand("delete-company", ["company", "delete"]),
11619
- legacyCommand("list-projects", ["project", "list"]),
12109
+ legacyCommand("list-projects", ["project", "list"], [], [
12110
+ { name: "--companyId <id>", description: "Filter by company ID." },
12111
+ {
12112
+ name: "--name <text>",
12113
+ description: "Filter by project name substring."
12114
+ },
12115
+ {
12116
+ name: "--status <status|ALL>",
12117
+ description: "Legacy single-status filter; use --statuses for multiple statuses."
12118
+ },
12119
+ {
12120
+ name: "--statuses <csv>",
12121
+ description: "Filter by a comma-separated project status list."
12122
+ },
12123
+ {
12124
+ name: "--active <true|false>",
12125
+ description: "Filter by active status; omit to include both states."
12126
+ },
12127
+ {
12128
+ name: "--wonFrom <iso>",
12129
+ description: "Only projects marked WON on or after this ISO date or datetime (UTC)."
12130
+ },
12131
+ {
12132
+ name: "--wonTo <iso>",
12133
+ description: "Only projects marked WON on or before this ISO date (inclusive) or datetime (UTC)."
12134
+ },
12135
+ {
12136
+ name: "--wonWithinDays <1-3650>",
12137
+ description: "Only projects marked WON in the last N days."
12138
+ },
12139
+ {
12140
+ name: "--limit <1-100>",
12141
+ description: "Bounded page size (default 20)."
12142
+ },
12143
+ {
12144
+ name: "--cursor <token>",
12145
+ description: "Opaque pageInfo cursor; pass nextCursor forward or previousCursor backward unchanged."
12146
+ },
12147
+ {
12148
+ name: "--direction <forward|backward>",
12149
+ description: "Use forward by default; backward requires --cursor."
12150
+ },
12151
+ {
12152
+ name: "--sortBy <name|createdAt|status|companyName|wonAt>",
12153
+ description: "Sort field (default createdAt); wonAt puts unwon projects last."
12154
+ },
12155
+ {
12156
+ name: "--sortDir <asc|desc>",
12157
+ description: "Sort direction (default desc)."
12158
+ }
12159
+ ]),
12160
+ legacyCommand("count-projects", ["project", "count"], [], [
12161
+ { name: "--companyId <id>", description: "Filter by company ID." },
12162
+ {
12163
+ name: "--name <text>",
12164
+ description: "Filter by project name substring."
12165
+ },
12166
+ {
12167
+ name: "--status <status|ALL>",
12168
+ description: "Legacy single-status filter."
12169
+ },
12170
+ {
12171
+ name: "--statuses <csv>",
12172
+ description: "Filter by a comma-separated project status list."
12173
+ },
12174
+ {
12175
+ name: "--active <true|false>",
12176
+ description: "Filter by active status; facets exclude this self-filter."
12177
+ },
12178
+ {
12179
+ name: "--wonFrom <iso>",
12180
+ description: "Only projects marked WON on or after this ISO date or datetime (UTC)."
12181
+ },
12182
+ {
12183
+ name: "--wonTo <iso>",
12184
+ description: "Only projects marked WON on or before this ISO date (inclusive) or datetime (UTC)."
12185
+ },
12186
+ {
12187
+ name: "--wonWithinDays <1-3650>",
12188
+ description: "Only projects marked WON in the last N days."
12189
+ }
12190
+ ]),
11620
12191
  legacyCommand("list-project-hubs", ["project", "hub", "list"], [], [
11621
12192
  {
11622
12193
  name: "--q",
@@ -11626,14 +12197,31 @@ var registry2 = [
11626
12197
  name: "--status",
11627
12198
  description: "Filter by comma-separated project statuses."
11628
12199
  },
11629
- { name: "--page", description: "Select a results page." },
11630
- { name: "--perPage", description: "Set the results page size." },
12200
+ { name: "--limit", description: "Page size, 1-100 (default 20)." },
12201
+ {
12202
+ name: "--cursor",
12203
+ description: "Opaque nextCursor/previousCursor from the last page."
12204
+ },
12205
+ {
12206
+ name: "--direction",
12207
+ description: "forward (default) or backward; backward needs --cursor."
12208
+ },
11631
12209
  {
11632
12210
  name: "--sortBy",
11633
12211
  description: "Sort by startDate, name, companyName, or status."
11634
12212
  },
11635
12213
  { name: "--sortDir", description: "Sort in asc or desc order." }
11636
12214
  ]),
12215
+ legacyCommand("count-project-hubs", ["project", "hub", "count"], [], [
12216
+ {
12217
+ name: "--q",
12218
+ description: "Search project and company names."
12219
+ },
12220
+ {
12221
+ name: "--status",
12222
+ description: "Filter by comma-separated project statuses."
12223
+ }
12224
+ ]),
11637
12225
  legacyCommand("get-project", ["project", "get"]),
11638
12226
  legacyCommand("get-project-hub-status", ["project", "hub", "status"]),
11639
12227
  legacyCommand("setup-project-hub", ["project", "hub", "setup"]),
@@ -11657,7 +12245,67 @@ var registry2 = [
11657
12245
  legacyCommand("list-contacts", ["contact", "list"]),
11658
12246
  legacyCommand("create-contact-person", ["contact", "create"]),
11659
12247
  legacyCommand("update-contact-person", ["contact", "update"]),
11660
- legacyCommand("list-suppliers", ["supplier", "list"]),
12248
+ legacyCommand("list-suppliers", ["supplier", "list"], [], [
12249
+ { name: "--name <text>", description: "Filter supplier name or email." },
12250
+ { name: "--createdBy <csv>", description: "Filter by creator IDs." },
12251
+ {
12252
+ name: "--gstRegistered <true|false>",
12253
+ description: "Filter GST registration."
12254
+ },
12255
+ {
12256
+ name: "--status <csv>",
12257
+ description: "Filter approval statuses; ALL means no status filter."
12258
+ },
12259
+ {
12260
+ name: "--supplierTags <csv>",
12261
+ description: "Filter by supplier tag IDs."
12262
+ },
12263
+ {
12264
+ name: "--active <true|false>",
12265
+ description: "Active by default; false selects archived."
12266
+ },
12267
+ {
12268
+ name: "--limit <1-100>",
12269
+ description: "Bounded page size (default 10). Retired page/perPage flags are rejected."
12270
+ },
12271
+ {
12272
+ name: "--cursor <token>",
12273
+ description: "Opaque nextCursor or previousCursor from items/pageInfo."
12274
+ },
12275
+ {
12276
+ name: "--direction <forward|backward>",
12277
+ description: "Forward by default; backward requires a cursor."
12278
+ },
12279
+ {
12280
+ name: "--sortBy <field>",
12281
+ description: "name, email, phone, gstRegistered, status, active, createdAt or updatedAt."
12282
+ },
12283
+ {
12284
+ name: "--sortDir <asc|desc>",
12285
+ description: "Descending createdAt by default."
12286
+ },
12287
+ {
12288
+ name: "--sort <json>",
12289
+ description: "Ordered {id,desc} array for multi-column sorting; cannot combine with sortBy/sortDir."
12290
+ }
12291
+ ]),
12292
+ legacyCommand("count-suppliers", ["supplier", "count"], [], [
12293
+ { name: "--name <text>", description: "Filter supplier name or email." },
12294
+ { name: "--createdBy <csv>", description: "Filter by creator IDs." },
12295
+ {
12296
+ name: "--gstRegistered <true|false>",
12297
+ description: "Filter GST registration."
12298
+ },
12299
+ { name: "--status <csv>", description: "Filter approval statuses." },
12300
+ {
12301
+ name: "--supplierTags <csv>",
12302
+ description: "Filter by supplier tag IDs."
12303
+ },
12304
+ {
12305
+ name: "--active <true|false>",
12306
+ description: "Active by default; false selects archived. No pagination/sort flags."
12307
+ }
12308
+ ]),
11661
12309
  legacyCommand("create-supplier", ["supplier", "create"]),
11662
12310
  legacyCommand("update-supplier", ["supplier", "update"]),
11663
12311
  legacyCommand("delete-suppliers", ["supplier", "delete"]),
@@ -11674,21 +12322,87 @@ var registry2 = [
11674
12322
  ]),
11675
12323
  legacyCommand("create-supplier-role", ["supplier", "role", "create"]),
11676
12324
  legacyCommand("create-supplier-tag", ["supplier", "tag", "create"]),
11677
- legacyCommand("get-supplier-analytics", ["supplier", "analytics", "get"]),
11678
- legacyCommand("list-items", ["item", "list"]),
12325
+ legacyCommand("get-supplier-analytics", ["supplier", "analytics", "get"], [], [
12326
+ {
12327
+ name: "--limit <1-100>",
12328
+ description: "Rows per page. Output is { items, pageInfo }."
12329
+ },
12330
+ {
12331
+ name: "--cursor <token>",
12332
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
12333
+ },
12334
+ {
12335
+ name: "--direction <forward|backward>",
12336
+ description: "forward (default) or backward; backward needs --cursor."
12337
+ }
12338
+ ]),
12339
+ legacyCommand("count-supplier-analytics", ["supplier", "analytics", "count"]),
12340
+ legacyCommand("list-items", ["item", "list"], [], [
12341
+ {
12342
+ name: "--limit <1-100>",
12343
+ description: "Rows per page. Output is { items, pageInfo }."
12344
+ },
12345
+ {
12346
+ name: "--cursor <token>",
12347
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
12348
+ },
12349
+ {
12350
+ name: "--direction <forward|backward>",
12351
+ description: "forward (default) or backward; backward needs --cursor."
12352
+ }
12353
+ ]),
12354
+ legacyCommand("count-items", ["item", "count"]),
11679
12355
  legacyCommand("get-item", ["item", "get"]),
12356
+ legacyCommand("get-item-quickbooks-sync-status", ["item", "sync", "status"]),
11680
12357
  legacyCommand("create-item", ["item", "create"]),
11681
12358
  legacyCommand("update-item", ["item", "update"]),
11682
12359
  legacyCommand("archive-items", ["item", "archive"]),
11683
12360
  legacyCommand("delete-item", ["item", "delete"]),
11684
- legacyCommand("list-item-categories", ["item", "category", "list"]),
12361
+ legacyCommand("list-item-categories", ["item", "category", "list"], [], [
12362
+ {
12363
+ name: "--limit <1-100>",
12364
+ description: "Rows per page. Output is { items, pageInfo }."
12365
+ },
12366
+ {
12367
+ name: "--cursor <token>",
12368
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
12369
+ },
12370
+ {
12371
+ name: "--direction <forward|backward>",
12372
+ description: "forward (default) or backward; backward needs --cursor."
12373
+ }
12374
+ ]),
12375
+ legacyCommand("count-item-categories", ["item", "category", "count"]),
11685
12376
  legacyCommand("create-item-category", ["item", "category", "create"]),
11686
12377
  legacyCommand("update-item-category", ["item", "category", "update"]),
11687
12378
  legacyCommand("delete-item-categories", ["item", "category", "delete"]),
11688
- legacyCommand("list-users", ["user", "list"]),
12379
+ legacyCommand("list-users", ["user", "list"], [], [
12380
+ {
12381
+ name: "--q <text>",
12382
+ description: "Search user names and emails (name A\u2013Z). A bare --q is rejected; use -q/--quiet to hide logs."
12383
+ },
12384
+ {
12385
+ name: "--limit <1-100>",
12386
+ description: "Rows per page. Output is { items, pageInfo }."
12387
+ },
12388
+ {
12389
+ name: "--cursor <token>",
12390
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
12391
+ }
12392
+ ]),
12393
+ legacyCommand("count-users", ["user", "count"]),
11689
12394
  legacyCommand("create-user", ["user", "create"]),
11690
12395
  legacyCommand("create-api-key", ["user", "api-key", "create"]),
11691
- legacyCommand("list-api-keys", ["user", "api-key", "list"]),
12396
+ legacyCommand("list-api-keys", ["user", "api-key", "list"], [], [
12397
+ {
12398
+ name: "--limit <1-100>",
12399
+ description: "Rows per page. Output is { items, pageInfo }."
12400
+ },
12401
+ {
12402
+ name: "--cursor <token>",
12403
+ description: "pageInfo.nextCursor from the last page, unchanged; pages forward only."
12404
+ }
12405
+ ]),
11692
12406
  legacyCommand("revoke-api-key", ["user", "api-key", "revoke"]),
11693
12407
  legacyCommand("get-user-performance", ["user", "performance", "get"]),
11694
12408
  legacyCommand("get-dashboard", ["dashboard", "get"]),
@@ -11704,23 +12418,144 @@ var registry2 = [
11704
12418
  "financial-overview",
11705
12419
  "get"
11706
12420
  ]),
11707
- legacyCommand("get-recent-errors", ["error", "recent", "list"]),
12421
+ legacyCommand("get-recent-errors", ["error", "recent", "list"], [], [
12422
+ {
12423
+ name: "--cursor <token>",
12424
+ description: "Pass a returned pageInfo boundary unchanged, with the same filters."
12425
+ },
12426
+ {
12427
+ name: "--direction <direction>",
12428
+ description: "forward (default) uses nextCursor; backward uses previousCursor."
12429
+ },
12430
+ {
12431
+ name: "--limit <number>",
12432
+ description: "1\u2013100 rows (default 10). Returns items and pageInfo, without a total."
12433
+ },
12434
+ { name: "--severity <severity>", description: "Error severity or ALL." },
12435
+ { name: "--status <status>", description: "Error status or ALL." },
12436
+ {
12437
+ name: "--timeFrame <period>",
12438
+ description: "all_time (default), last_30_days, last_3_months, last_6_months or last_1_year."
12439
+ }
12440
+ ]),
12441
+ legacyCommand("count-errors", ["error", "recent", "count"], [], [
12442
+ { name: "--severity <severity>", description: "Error severity or ALL." },
12443
+ { name: "--status <status>", description: "Error status or ALL." },
12444
+ {
12445
+ name: "--timeFrame <period>",
12446
+ description: "Same filters as error recent list; returns independent totalCount."
12447
+ }
12448
+ ]),
12449
+ legacyCommand("get-error", ["error", "get"]),
11708
12450
  legacyCommand("get-error-metrics", ["error", "metrics", "get"]),
11709
- legacyCommand("list-integration-operations", [
12451
+ legacyCommand("list-integration-operations", ["integration", "operation", "list"], [], [
12452
+ {
12453
+ name: "--limit <1-100>",
12454
+ description: "Rows per page. Output is { items, pageInfo }."
12455
+ },
12456
+ {
12457
+ name: "--cursor <token>",
12458
+ description: "pageInfo.nextCursor (or previousCursor with --direction backward) from the last page, unchanged; keep the same filters and sort."
12459
+ },
12460
+ {
12461
+ name: "--direction <forward|backward>",
12462
+ description: "forward (default) or backward; backward needs --cursor."
12463
+ }
12464
+ ]),
12465
+ legacyCommand("count-integration-operations", [
11710
12466
  "integration",
11711
12467
  "operation",
11712
- "list"
12468
+ "count"
12469
+ ]),
12470
+ legacyCommand("get-integration-operation", [
12471
+ "integration",
12472
+ "operation",
12473
+ "get"
11713
12474
  ]),
11714
12475
  legacyCommand("retry-integration-operation", [
11715
12476
  "integration",
11716
12477
  "operation",
11717
12478
  "retry"
11718
12479
  ]),
11719
- legacyCommand("list-inbound-sources", ["inbound", "source", "list"]),
12480
+ legacyCommand("reconcile-resource-operation", [
12481
+ "integration",
12482
+ "operation",
12483
+ "reconcile-resource"
12484
+ ]),
12485
+ legacyCommand("list-webhook-event-types", ["webhook", "event-type", "list"]),
12486
+ legacyCommand("list-webhook-endpoints", ["webhook", "endpoint", "list"]),
12487
+ legacyCommand("get-webhook-endpoint", ["webhook", "endpoint", "get"]),
12488
+ legacyCommand("create-webhook-endpoint", ["webhook", "endpoint", "create"]),
12489
+ legacyCommand("update-webhook-endpoint", ["webhook", "endpoint", "update"]),
12490
+ legacyCommand("delete-webhook-endpoint", ["webhook", "endpoint", "delete"]),
12491
+ legacyCommand("rotate-webhook-secret", ["webhook", "secret", "rotate"]),
12492
+ legacyCommand("send-webhook-test-event", ["webhook", "endpoint", "test"]),
12493
+ legacyCommand("list-webhook-deliveries", ["webhook", "delivery", "list"]),
12494
+ legacyCommand("get-webhook-delivery", ["webhook", "delivery", "get"]),
12495
+ legacyCommand("replay-webhook-delivery", ["webhook", "delivery", "replay"]),
12496
+ legacyCommand("list-inbound-sources", ["inbound", "source", "list"], [], [
12497
+ {
12498
+ name: "--limit <n>",
12499
+ description: "Rows per cursor page (1..100; default 25)."
12500
+ },
12501
+ {
12502
+ name: "--cursor <token>",
12503
+ description: "Opaque source continuation token returned by pageInfo."
12504
+ },
12505
+ {
12506
+ name: "--direction <value>",
12507
+ description: "forward or backward (default forward; backward requires cursor)."
12508
+ },
12509
+ {
12510
+ name: "--search <text>",
12511
+ description: "Search source name or Typeform form ID."
12512
+ },
12513
+ {
12514
+ name: "--processingEnabled <boolean>",
12515
+ description: "Filter processing enabled state."
12516
+ },
12517
+ {
12518
+ name: "--processingMode <mode>",
12519
+ description: "ACTIVE, COLLECT_ONLY or SHADOW."
12520
+ }
12521
+ ]),
12522
+ legacyCommand("count-inbound-sources", ["inbound", "source", "count"], [], [
12523
+ {
12524
+ name: "--search <text>",
12525
+ description: "Search source name or Typeform form ID."
12526
+ },
12527
+ {
12528
+ name: "--processingEnabled <boolean>",
12529
+ description: "Filter processing enabled state."
12530
+ },
12531
+ {
12532
+ name: "--processingMode <mode>",
12533
+ description: "ACTIVE, COLLECT_ONLY or SHADOW."
12534
+ }
12535
+ ]),
11720
12536
  legacyCommand("create-inbound-source", ["inbound", "source", "create"]),
11721
12537
  legacyCommand("get-inbound-source", ["inbound", "source", "get"]),
11722
12538
  legacyCommand("update-inbound-source", ["inbound", "source", "update"]),
11723
- legacyCommand("list-inbound-submissions", ["inbound", "submission", "list"]),
12539
+ legacyCommand("list-inbound-submissions", ["inbound", "submission", "list"], [], [
12540
+ {
12541
+ name: "--limit <number>",
12542
+ description: "Page size from 1 to 100 (default 25). Returns submissions, hasMore and nextCursor; no total."
12543
+ },
12544
+ {
12545
+ name: "--cursor <token>",
12546
+ description: "Pass the previous response's nextCursor unchanged. Omit for newest submissions. Replaces --page/--perPage."
12547
+ },
12548
+ {
12549
+ name: "--sourceId <id>",
12550
+ description: "Filter submissions to this source; keep the same filter while following cursors."
12551
+ }
12552
+ ]),
12553
+ legacyCommand("count-inbound-submissions", ["inbound", "submission", "count"], [], [
12554
+ {
12555
+ name: "--sourceId <id>",
12556
+ description: "Optional source filter. Returns an independently calculated exact totalCount, not a list snapshot."
12557
+ }
12558
+ ]),
11724
12559
  legacyCommand("get-inbound-submission", ["inbound", "submission", "get"]),
11725
12560
  legacyCommand("save-inbound-mapping", ["inbound", "mapping", "save"]),
11726
12561
  legacyCommand("save-inbound-automation-config", [
@@ -11893,17 +12728,292 @@ compdef _bb bb
11893
12728
  `;
11894
12729
  }
11895
12730
  };
11896
-
11897
- // src/cli.ts
11898
- function parsePositiveIntFlag(value, flagName) {
12731
+
12732
+ // src/cli.ts
12733
+ var SUPPLIER_ANALYTICS_SORT_IDS = [
12734
+ "name",
12735
+ "gstRegistered",
12736
+ "status",
12737
+ "phone",
12738
+ "email",
12739
+ "updatedAt",
12740
+ "createdAt"
12741
+ ];
12742
+ function parsePositiveIntFlag(value, flagName) {
12743
+ if (value === undefined)
12744
+ return;
12745
+ const parsed = Number.parseInt(String(value), 10);
12746
+ if (!Number.isFinite(parsed) || parsed <= 0) {
12747
+ throw new Error(`${flagName} must be a positive integer.`);
12748
+ }
12749
+ return parsed;
12750
+ }
12751
+ var COMPANY_SORT_FIELDS = ["name", "createdAt"];
12752
+ var COMPANY_SORT_DIRECTIONS = ["asc", "desc"];
12753
+ var COMPANY_PAGE_FLAGS = [
12754
+ "page",
12755
+ "perPage",
12756
+ "pageSize",
12757
+ "page-size",
12758
+ "per-page",
12759
+ "page_size",
12760
+ "per_page"
12761
+ ];
12762
+ var rejectBareFlag = (flags, key) => {
12763
+ if (flags[key] === true) {
12764
+ throw new Error(`--${key} requires a value.`);
12765
+ }
12766
+ };
12767
+ var rejectCompanyPageFlags = (flags, command) => {
12768
+ for (const key of COMPANY_PAGE_FLAGS) {
12769
+ if (flags[key] !== undefined) {
12770
+ throw new Error(`--${key} is not supported by company ${command}; use --limit and --cursor for list or company count for totals.`);
12771
+ }
12772
+ }
12773
+ };
12774
+ var parseCompanyLimit = (value) => {
12775
+ if (value === undefined)
12776
+ return 20;
12777
+ const parsed = Number(String(value));
12778
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 100) {
12779
+ throw new Error("--limit must be an integer from 1 to 100.");
12780
+ }
12781
+ return parsed;
12782
+ };
12783
+ var parseCompanySortBy = (value) => {
12784
+ if (value === undefined)
12785
+ return "createdAt";
12786
+ if (!COMPANY_SORT_FIELDS.includes(value)) {
12787
+ throw new Error(`Invalid --sortBy "${value}". Use one of: ${COMPANY_SORT_FIELDS.join(", ")}.`);
12788
+ }
12789
+ return value;
12790
+ };
12791
+ var parseCompanySortDir = (value) => {
12792
+ if (value === undefined)
12793
+ return "desc";
12794
+ if (!COMPANY_SORT_DIRECTIONS.includes(value)) {
12795
+ throw new Error(`Invalid --sortDir "${value}". Use one of: ${COMPANY_SORT_DIRECTIONS.join(", ")}.`);
12796
+ }
12797
+ return value;
12798
+ };
12799
+ var parseCompanyDirection = (value) => {
12800
+ if (value === undefined)
12801
+ return "forward";
12802
+ if (value !== "forward" && value !== "backward") {
12803
+ throw new Error('--direction must be "forward" or "backward".');
12804
+ }
12805
+ return value;
12806
+ };
12807
+ var parseCompanyListFlags = (flags) => {
12808
+ rejectCompanyPageFlags(flags, "list");
12809
+ for (const key of [
12810
+ "name",
12811
+ "createdBy",
12812
+ "createdByIds",
12813
+ "active",
12814
+ "limit",
12815
+ "cursor",
12816
+ "direction",
12817
+ "sortBy",
12818
+ "sortDir"
12819
+ ]) {
12820
+ rejectBareFlag(flags, key);
12821
+ }
12822
+ const cursor = getFlag(flags, "cursor");
12823
+ const direction = parseCompanyDirection(getFlag(flags, "direction"));
12824
+ if (direction === "backward" && cursor === undefined) {
12825
+ throw new Error("--direction backward requires --cursor <token>.");
12826
+ }
12827
+ if (cursor === "")
12828
+ throw new Error("--cursor requires a non-empty token.");
12829
+ return {
12830
+ name: getFlag(flags, "name"),
12831
+ createdBy: getFlag(flags, "createdBy"),
12832
+ createdByIds: parseCommaSeparatedIds(getFlag(flags, "createdByIds")),
12833
+ active: parseOptionalBoolFlag(flags, "active"),
12834
+ limit: parseCompanyLimit(getFlag(flags, "limit")),
12835
+ cursor,
12836
+ direction,
12837
+ sortBy: parseCompanySortBy(getFlag(flags, "sortBy")),
12838
+ sortDir: parseCompanySortDir(getFlag(flags, "sortDir"))
12839
+ };
12840
+ };
12841
+ var parseCompanyCountFlags = (flags) => {
12842
+ rejectCompanyPageFlags(flags, "count");
12843
+ for (const key of [
12844
+ "name",
12845
+ "createdBy",
12846
+ "createdByIds",
12847
+ "active",
12848
+ "limit",
12849
+ "cursor",
12850
+ "direction",
12851
+ "sortBy",
12852
+ "sortDir"
12853
+ ]) {
12854
+ rejectBareFlag(flags, key);
12855
+ }
12856
+ if (flags.limit !== undefined || flags.cursor !== undefined || flags.direction !== undefined) {
12857
+ throw new Error("company count does not accept list pagination flags.");
12858
+ }
12859
+ if (flags.sortBy !== undefined || flags.sortDir !== undefined) {
12860
+ throw new Error("company count does not accept list sorting flags.");
12861
+ }
12862
+ return {
12863
+ name: getFlag(flags, "name"),
12864
+ createdBy: getFlag(flags, "createdBy"),
12865
+ createdByIds: parseCommaSeparatedIds(getFlag(flags, "createdByIds")),
12866
+ active: parseOptionalBoolFlag(flags, "active")
12867
+ };
12868
+ };
12869
+ var PROJECT_SORT_FIELDS = [
12870
+ "name",
12871
+ "createdAt",
12872
+ "status",
12873
+ "companyName",
12874
+ "wonAt"
12875
+ ];
12876
+ var PROJECT_SORT_DIRECTIONS = ["asc", "desc"];
12877
+ var PROJECT_PAGE_FLAGS = [
12878
+ "page",
12879
+ "perPage",
12880
+ "pageSize",
12881
+ "page-size",
12882
+ "per-page",
12883
+ "page_size",
12884
+ "per_page"
12885
+ ];
12886
+ var rejectProjectPageFlags = (flags, command) => {
12887
+ for (const key of PROJECT_PAGE_FLAGS) {
12888
+ if (flags[key] !== undefined) {
12889
+ throw new Error(`--${key} is not supported by project ${command}; use --limit and --cursor for list or project count for totals.`);
12890
+ }
12891
+ }
12892
+ };
12893
+ var parseProjectLimit = (value) => {
12894
+ if (value === undefined)
12895
+ return 20;
12896
+ const parsed = Number(String(value));
12897
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 100) {
12898
+ throw new Error("--limit must be an integer from 1 to 100.");
12899
+ }
12900
+ return parsed;
12901
+ };
12902
+ var parseProjectSortBy = (value) => {
12903
+ if (value === undefined)
12904
+ return "createdAt";
12905
+ if (!PROJECT_SORT_FIELDS.includes(value)) {
12906
+ throw new Error(`Invalid --sortBy "${value}". Use one of: ${PROJECT_SORT_FIELDS.join(", ")}.`);
12907
+ }
12908
+ return value;
12909
+ };
12910
+ var parseProjectSortDir = (value) => {
12911
+ if (value === undefined)
12912
+ return "desc";
12913
+ if (!PROJECT_SORT_DIRECTIONS.includes(value)) {
12914
+ throw new Error(`Invalid --sortDir "${value}". Use one of: ${PROJECT_SORT_DIRECTIONS.join(", ")}.`);
12915
+ }
12916
+ return value;
12917
+ };
12918
+ var parseProjectWonWithinDays = (value) => {
11899
12919
  if (value === undefined)
11900
12920
  return;
11901
- const parsed = Number.parseInt(String(value), 10);
11902
- if (!Number.isFinite(parsed) || parsed <= 0) {
11903
- throw new Error(`${flagName} must be a positive integer.`);
12921
+ const parsed = Number(value);
12922
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 3650) {
12923
+ throw new Error("--wonWithinDays must be an integer from 1 to 3650.");
11904
12924
  }
11905
12925
  return parsed;
11906
- }
12926
+ };
12927
+ var parseProjectDirection = (value) => {
12928
+ if (value === undefined)
12929
+ return "forward";
12930
+ if (value !== "forward" && value !== "backward") {
12931
+ throw new Error('--direction must be "forward" or "backward".');
12932
+ }
12933
+ return value;
12934
+ };
12935
+ var parseProjectListFlags = (flags) => {
12936
+ rejectProjectPageFlags(flags, "list");
12937
+ for (const key of [
12938
+ "companyId",
12939
+ "name",
12940
+ "status",
12941
+ "statuses",
12942
+ "active",
12943
+ "wonFrom",
12944
+ "wonTo",
12945
+ "wonWithinDays",
12946
+ "limit",
12947
+ "cursor",
12948
+ "direction",
12949
+ "sortBy",
12950
+ "sortDir"
12951
+ ]) {
12952
+ rejectBareFlag(flags, key);
12953
+ }
12954
+ const cursor = getFlag(flags, "cursor");
12955
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
12956
+ if (direction === "backward" && cursor === undefined) {
12957
+ throw new Error("--direction backward requires --cursor <token>.");
12958
+ }
12959
+ if (cursor === "")
12960
+ throw new Error("--cursor requires a non-empty token.");
12961
+ return {
12962
+ companyId: getFlag(flags, "companyId"),
12963
+ name: getFlag(flags, "name"),
12964
+ status: parseOptionalExtendedProjectStatus(getFlag(flags, "status")),
12965
+ statuses: parseCommaSeparatedProjectStatuses(getFlag(flags, "statuses")),
12966
+ active: parseOptionalBoolFlag(flags, "active"),
12967
+ wonFrom: parseOptionalIsoDateFlag(getFlag(flags, "wonFrom"), "--wonFrom"),
12968
+ wonTo: parseOptionalIsoDateFlag(getFlag(flags, "wonTo"), "--wonTo", {
12969
+ endOfDay: true
12970
+ }),
12971
+ wonWithinDays: parseProjectWonWithinDays(getFlag(flags, "wonWithinDays")),
12972
+ limit: parseProjectLimit(getFlag(flags, "limit")),
12973
+ cursor,
12974
+ direction,
12975
+ sortBy: parseProjectSortBy(getFlag(flags, "sortBy")),
12976
+ sortDir: parseProjectSortDir(getFlag(flags, "sortDir"))
12977
+ };
12978
+ };
12979
+ var parseProjectCountFlags = (flags) => {
12980
+ rejectProjectPageFlags(flags, "count");
12981
+ for (const key of [
12982
+ "companyId",
12983
+ "name",
12984
+ "status",
12985
+ "statuses",
12986
+ "active",
12987
+ "wonFrom",
12988
+ "wonTo",
12989
+ "wonWithinDays",
12990
+ "limit",
12991
+ "cursor",
12992
+ "direction",
12993
+ "sortBy",
12994
+ "sortDir"
12995
+ ]) {
12996
+ rejectBareFlag(flags, key);
12997
+ }
12998
+ if (flags.limit !== undefined || flags.cursor !== undefined || flags.direction !== undefined) {
12999
+ throw new Error("project count does not accept list pagination flags.");
13000
+ }
13001
+ if (flags.sortBy !== undefined || flags.sortDir !== undefined) {
13002
+ throw new Error("project count does not accept list sorting flags.");
13003
+ }
13004
+ return {
13005
+ companyId: getFlag(flags, "companyId"),
13006
+ name: getFlag(flags, "name"),
13007
+ status: parseOptionalExtendedProjectStatus(getFlag(flags, "status")),
13008
+ statuses: parseCommaSeparatedProjectStatuses(getFlag(flags, "statuses")),
13009
+ active: parseOptionalBoolFlag(flags, "active"),
13010
+ wonFrom: parseOptionalIsoDateFlag(getFlag(flags, "wonFrom"), "--wonFrom"),
13011
+ wonTo: parseOptionalIsoDateFlag(getFlag(flags, "wonTo"), "--wonTo", {
13012
+ endOfDay: true
13013
+ }),
13014
+ wonWithinDays: parseProjectWonWithinDays(getFlag(flags, "wonWithinDays"))
13015
+ };
13016
+ };
11907
13017
  function parseOptionalBoolFlag(flags, key) {
11908
13018
  const v = flags[key];
11909
13019
  if (v === undefined || v === true)
@@ -11954,28 +13064,90 @@ function parseCommercialRejectionProjectStatus(flags) {
11954
13064
  throw new Error("--projectStatusOnCommercialRejection requires PITCH or LOST.");
11955
13065
  }
11956
13066
  const status = String(raw).toUpperCase();
11957
- if (status === ProjectStatus.PITCH || status === ProjectStatus.LOST) {
13067
+ if (status === ProjectStatus2.PITCH || status === ProjectStatus2.LOST) {
11958
13068
  return status;
11959
13069
  }
11960
13070
  throw new Error("--projectStatusOnCommercialRejection must be PITCH or LOST.");
11961
13071
  }
11962
- function parseListBillsFlags(flags) {
13072
+ var rejectRetiredPageFlags = (flags) => {
13073
+ for (const key of PROJECT_PAGE_FLAGS) {
13074
+ if (flags[key] !== undefined)
13075
+ throw new Error(`--${key} is retired; use --limit/--cursor, or the matching count command for totals.`);
13076
+ }
13077
+ };
13078
+ var parseDetailFeedPage = (command, idLabel, positional, flags, maxLimit) => {
13079
+ const id = positional[0];
13080
+ if (!id)
13081
+ throw new Error(`${command} requires ${idLabel}`);
13082
+ rejectRetiredPageFlags(flags);
13083
+ for (const key of ["limit", "cursor"])
13084
+ rejectBareFlag(flags, key);
13085
+ if (flags.direction !== undefined)
13086
+ throw new Error(`${command} pages forward only; follow pageInfo.nextCursor with --cursor.`);
13087
+ const cursor = getFlag(flags, "cursor");
13088
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
13089
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
13090
+ const limitFlag = getFlag(flags, "limit");
13091
+ const limit = limitFlag === undefined ? undefined : Number(limitFlag);
13092
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1 || limit > maxLimit))
13093
+ throw new Error(`--limit must be an integer from 1 to ${maxLimit}.`);
13094
+ return { id, limit, cursor };
13095
+ };
13096
+ var parseBillFilterFlags = (flags) => {
11963
13097
  const statusRaw = getFlag(flags, "status");
11964
13098
  const createdByIdsRaw = getFlag(flags, "createdByIds");
11965
13099
  const searchRaw = getFlag(flags, "search");
11966
- const sortByRaw = getFlag(flags, "sortBy");
11967
- const sortDirRaw = getFlag(flags, "sortDir");
11968
13100
  return {
11969
13101
  projectId: getFlag(flags, "projectId"),
11970
13102
  budgetId: getFlag(flags, "budgetId"),
11971
13103
  statuses: parseCommaSeparatedBillStatuses(statusRaw !== undefined ? String(statusRaw) : undefined),
11972
13104
  search: searchRaw !== undefined ? String(searchRaw) : undefined,
11973
13105
  isClaimable: parseOptionalBoolFlag(flags, "isClaimable"),
11974
- createdByIds: parseCommaSeparatedIds(createdByIdsRaw !== undefined ? String(createdByIdsRaw) : undefined),
13106
+ createdByIds: parseCommaSeparatedIds(createdByIdsRaw !== undefined ? String(createdByIdsRaw) : undefined)
13107
+ };
13108
+ };
13109
+ var parseQuotationFilterFlags = (flags) => {
13110
+ const createdByRaw = getFlag(flags, "createdBy") ?? getFlag(flags, "requesterIds");
13111
+ const searchRaw = getFlag(flags, "search");
13112
+ return {
13113
+ projectId: getFlag(flags, "projectId"),
13114
+ budgetId: getFlag(flags, "budgetId"),
13115
+ supplierId: getFlag(flags, "supplierId"),
13116
+ status: parseCommaSeparatedQuotationStatuses(getFlag(flags, "status")),
13117
+ createdBy: parseCommaSeparatedIds(createdByRaw),
13118
+ search: searchRaw !== undefined ? String(searchRaw).trim() || undefined : undefined
13119
+ };
13120
+ };
13121
+ function parseListBillsFlags(flags) {
13122
+ rejectRetiredPageFlags(flags);
13123
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"])
13124
+ rejectBareFlag(flags, key);
13125
+ const sortByRaw = getFlag(flags, "sortBy");
13126
+ const sortDirRaw = getFlag(flags, "sortDir");
13127
+ const cursor = getFlag(flags, "cursor");
13128
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
13129
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
13130
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
13131
+ if (direction === "backward" && !cursor)
13132
+ throw new Error("--direction backward requires --cursor <token>.");
13133
+ return {
13134
+ ...parseBillFilterFlags(flags),
11975
13135
  sortBy: parseOptionalBillListSortBy(sortByRaw !== undefined ? String(sortByRaw) : undefined),
11976
13136
  sortDir: parseOptionalBillListSortDir(sortDirRaw !== undefined ? String(sortDirRaw) : undefined),
11977
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
11978
- pageSize: parsePositiveIntFlag(getFlag(flags, "pageSize"), "--pageSize")
13137
+ limit: parseProjectLimit(getFlag(flags, "limit")),
13138
+ cursor,
13139
+ direction
13140
+ };
13141
+ }
13142
+ function parseCountBillsFlags(flags) {
13143
+ rejectRetiredPageFlags(flags);
13144
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"]) {
13145
+ if (flags[key] !== undefined)
13146
+ throw new Error(`bill count does not accept --${key}; totals use filters only.`);
13147
+ }
13148
+ return {
13149
+ ...parseBillFilterFlags(flags),
13150
+ includeMetrics: parseOptionalBoolFlag(flags, "includeMetrics") ?? false
11979
13151
  };
11980
13152
  }
11981
13153
  var SUPPLIER_SORT_FIELDS = [
@@ -11996,25 +13168,89 @@ function parseOptionalSupplierSortBy(raw) {
11996
13168
  }
11997
13169
  return raw;
11998
13170
  }
11999
- function parseListSuppliersFlags(flags) {
12000
- const createdByRaw = getFlag(flags, "createdBy");
12001
- const statusRaw = getFlag(flags, "status");
12002
- const supplierTagsRaw = getFlag(flags, "supplierTags");
12003
- const sortBy = parseOptionalSupplierSortBy(getFlag(flags, "sortBy"));
12004
- const sortDirRaw = getFlag(flags, "sortDir");
12005
- const sortDir = parseOptionalBillListSortDir(sortDirRaw !== undefined ? String(sortDirRaw) : undefined);
13171
+ var parseSupplierFilters = (flags) => {
13172
+ for (const key of [
13173
+ "name",
13174
+ "createdBy",
13175
+ "gstRegistered",
13176
+ "status",
13177
+ "supplierTags",
13178
+ "active"
13179
+ ])
13180
+ rejectBareFlag(flags, key);
12006
13181
  return {
12007
13182
  name: getFlag(flags, "name"),
12008
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
12009
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
12010
- sort: sortBy !== undefined || sortDir !== undefined ? [{ id: sortBy ?? "createdAt", desc: (sortDir ?? "desc") === "desc" }] : undefined,
12011
- createdBy: parseCommaSeparatedIds(createdByRaw !== undefined ? String(createdByRaw) : undefined),
12012
- gstRegistered: parseOptionalBoolFlag(flags, "gstRegistered"),
12013
- status: parseCommaSeparatedSupplierStatuses(statusRaw !== undefined ? String(statusRaw) : undefined),
12014
- supplierTags: parseCommaSeparatedIds(supplierTagsRaw !== undefined ? String(supplierTagsRaw) : undefined),
12015
- active: parseOptionalBoolFlag(flags, "active")
13183
+ createdBy: parseCommaSeparatedIds(getFlag(flags, "createdBy")),
13184
+ gstRegistered: parseOptionalBoolFlag(flags, "gstRegistered") ?? null,
13185
+ status: parseCommaSeparatedSupplierStatuses(getFlag(flags, "status")),
13186
+ supplierTags: parseCommaSeparatedIds(getFlag(flags, "supplierTags")),
13187
+ active: parseOptionalBoolFlag(flags, "active") ?? true
12016
13188
  };
12017
- }
13189
+ };
13190
+ var rejectSupplierPageFlags = (flags) => {
13191
+ for (const key of PROJECT_PAGE_FLAGS) {
13192
+ if (flags[key] !== undefined)
13193
+ throw new Error(`--${key} is retired for suppliers; use --limit/--cursor or supplier count for totals.`);
13194
+ }
13195
+ };
13196
+ var parseListSuppliersFlags = (flags) => {
13197
+ rejectSupplierPageFlags(flags);
13198
+ for (const key of [
13199
+ "limit",
13200
+ "cursor",
13201
+ "direction",
13202
+ "sort",
13203
+ "sortBy",
13204
+ "sortDir"
13205
+ ])
13206
+ rejectBareFlag(flags, key);
13207
+ const limit = getFlag(flags, "limit") === undefined ? 10 : parseProjectLimit(getFlag(flags, "limit"));
13208
+ const cursor = getFlag(flags, "cursor");
13209
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
13210
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
13211
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
13212
+ if (direction === "backward" && !cursor)
13213
+ throw new Error("--direction backward requires --cursor <token>.");
13214
+ const sortRaw = getFlag(flags, "sort");
13215
+ let sort;
13216
+ if (sortRaw !== undefined) {
13217
+ if (flags.sortBy !== undefined || flags.sortDir !== undefined)
13218
+ throw new Error("Use --sort or --sortBy/--sortDir, not both.");
13219
+ const parsed = parseJsonFlag(sortRaw, "--sort");
13220
+ if (!Array.isArray(parsed) || parsed.length > 8)
13221
+ throw new Error("--sort must be a JSON array of at most eight {id, desc} entries.");
13222
+ sort = parsed.map((item) => {
13223
+ if (typeof item !== "object" || item === null || !("id" in item) || !("desc" in item) || typeof item.id !== "string" || typeof item.desc !== "boolean" || Object.keys(item).some((key) => key !== "id" && key !== "desc"))
13224
+ throw new Error("Invalid --sort entry; use {id, desc}.");
13225
+ const id = parseOptionalSupplierSortBy(item.id);
13226
+ if (!id)
13227
+ throw new Error("Invalid --sort field.");
13228
+ return { id, desc: item.desc };
13229
+ });
13230
+ } else {
13231
+ const sortBy = parseOptionalSupplierSortBy(getFlag(flags, "sortBy"));
13232
+ const sortDir = parseOptionalBillListSortDir(getFlag(flags, "sortDir"));
13233
+ sort = [
13234
+ { id: sortBy ?? "createdAt", desc: (sortDir ?? "desc") === "desc" }
13235
+ ];
13236
+ }
13237
+ return { ...parseSupplierFilters(flags), limit, cursor, direction, sort };
13238
+ };
13239
+ var parseCountSuppliersFlags = (flags) => {
13240
+ rejectSupplierPageFlags(flags);
13241
+ for (const key of [
13242
+ "limit",
13243
+ "cursor",
13244
+ "direction",
13245
+ "sort",
13246
+ "sortBy",
13247
+ "sortDir"
13248
+ ]) {
13249
+ if (flags[key] !== undefined)
13250
+ throw new Error(`supplier count does not accept --${key}; totals use filters only.`);
13251
+ }
13252
+ return parseSupplierFilters(flags);
13253
+ };
12018
13254
  function parseDashboardQueryFlags(flags) {
12019
13255
  const roleRaw = getFlag(flags, "role");
12020
13256
  const timeFrameRaw = getFlag(flags, "timeFrame");
@@ -12049,7 +13285,8 @@ pending-approver assignment. On FORBIDDEN, stop and report the identity, role,
12049
13285
  command, and target; do not switch keys or try a lower-level procedure.
12050
13286
 
12051
13287
  Sensitive workflow changes: approval decisions/requests, entity status updates,
12052
- estimate emails, customer invoices, integration retries, mark-budget-won,
13288
+ estimate emails, customer invoices, integration retries and QuickBooks outcome
13289
+ verification, mark-budget-won,
12053
13290
  submit-quotation, and supplier approval request side effects require an
12054
13291
  interactive CONFIRM prompt and abort in non-interactive shells.
12055
13292
  AI agents must inspect the entity first, summarize the exact entity, target
@@ -12060,15 +13297,20 @@ Budget mutations return a conflict while an estimate email is queued,
12060
13297
  processing, or awaiting provider reconciliation.
12061
13298
 
12062
13299
  Budgets
12063
- list-budgets [--projectId] [--name] [--status CSV] [--createdBy] [--dateFrom] [--dateTo] [--sortBy] [--sortDir] [--page] [--perPage] [--summary]
13300
+ list-budgets [--projectId] [--name] [--status CSV] [--createdBy] [--dateFrom] [--dateTo] [--sortBy lastActivityAt|createdAt|status|budget|date|createdBy] [--sortDir] [--limit 1-100] [--cursor <token>] [--direction forward|backward] [--summary]
13301
+ Returns { items, pageInfo }; follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected.
13302
+ count-budgets [same filter flags as list-budgets] totalCount plus status and active facets (each ignores its own filter)
12064
13303
  get-budget <id> Budget + line items
12065
13304
  get-budget-items <id> Line items only
12066
13305
  get-budget-details <id> Full detail (margins, suppliers, etc.)
12067
- get-budget-categories
12068
- get-budget-versions <budgetId> [--limit <1-50>] [--cursor <versionId> | --all]
13306
+ get-budget-categories [--q] [--limit 1-100] [--cursor <token>]
13307
+ Active categories, name A\u2013Z. Returns { items, pageInfo }; follow nextCursor with the same --q.
13308
+ get-budget-versions <budgetId> [--limit <1-50>] [--cursor <token> | --all]
12069
13309
  rename-budget-version <versionId> --name <text>
12070
13310
  restore-budget-version <versionId> Replace current budget data and return it to DRAFT.
12071
13311
  update-budget-status <id> <status> [--projectManagerId <id>] [--markProjectWon true|false] [--projectStatusOnCommercialRejection PITCH|LOST]
13312
+ APPROVED requires a current Lead/Admin role, regardless of budget management permissions. Ordinary users must request approval.
13313
+ Material edits and generic status changes invalidate older pending budget requests; decide only a fresh request for the content reviewed.
12072
13314
  Budget-changing commands require interactive "yes" confirmation when the current budget is ESTIMATE_CREATED, ESTIMATE_SENT, ESTIMATE_ACCEPTED, ESTIMATE_REJECTED, or ESTIMATE_CLOSED.
12073
13315
  ESTIMATE_ACCEPTED requires win proof, auto-marks PITCH or LOST projects WON, requires --projectManagerId when the project has no PM, and auto-creates the Asana project/section when enabled.
12074
13316
  ESTIMATE_REJECTED requires --projectStatusOnCommercialRejection when rejecting the only accepted/closed budget on a commercial project.
@@ -12078,7 +13320,7 @@ Budgets
12078
13320
  update-budget --payload '<json>' (budget.updateBudget; must include id)
12079
13321
  delete-budget <budgetId>
12080
13322
  create-budget-approval <budgetId> (also queues approval request emails)
12081
- create-estimate <budgetId> (creates QuickBooks estimate; supports preflight updates)
13323
+ create-estimate <budgetId> (queues QuickBooks estimate creation; budget is read-only until it settles; supports preflight updates)
12082
13324
  send-estimate-to-contact-person --payload '<json>' Send using the web contact-person workflow; payload includes recipient fields plus HTML content/signature.
12083
13325
  add-budget-items --budgetId --items '[{"itemId":"\u2026","quantity":1,"markup":30},\u2026]'
12084
13326
  update-budget-item --id <budgetItemId> [--description plain-text] [--note plain-text] [--quantity] [--markup] [--cost] [--unitPrice] [--isFreeOfCharge true|false] [--gstInclusive true|false] [--gstOutOfScope true|false]
@@ -12086,7 +13328,7 @@ Budgets
12086
13328
  replace-budget-item --budgetItemId <id> --replacementItemId <activeCatalogueItemId>
12087
13329
  approve-unavailable-item-exception --budgetItemId <id> --reason <text> Lead/Admin only; reason must be 5-1000 characters.
12088
13330
  reorder-budget-items <budgetId> --itemIds <csv>
12089
- update-budget-item-supplier --budgetItemId --supplierId
13331
+ update-budget-item-supplier --budgetItemId --supplierId Material supplier/GST edit: supersedes pending approvals and applies the standard budget reset policy; unchanged supplier is a no-op.
12090
13332
  create-budget-category --name <text>
12091
13333
  update-budget-category --id --name
12092
13334
  delete-budget-category <id>
@@ -12100,8 +13342,11 @@ Budgets
12100
13342
  upload-budget-attachment <budgetId> <filePath> Presigned S3 upload + confirm
12101
13343
 
12102
13344
  Bills
12103
- list-bills [--projectId] [--budgetId] [--status CSV] [--search <text>] [--isClaimable true|false] [--createdByIds <csv>] [--sortBy createdAt|amount|status] [--sortDir asc|desc] [--page] [--pageSize]
13345
+ list-bills [--projectId] [--budgetId] [--status CSV] [--search <text>] [--isClaimable true|false] [--createdByIds <csv>] [--sortBy createdAt|amount|status] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13346
+ Returns { items, pageInfo }; pass nextCursor with --direction forward or previousCursor with --direction backward, keeping the same filters and sort. --page/--pageSize/--perPage are rejected.
12104
13347
  list-claims same flags as list-bills; only reimbursable claims (ignores --isClaimable)
13348
+ count-bills [--projectId] [--budgetId] [--status CSV] [--search <text>] [--isClaimable true|false] [--createdByIds <csv>] [--includeMetrics true|false] totalCount, status facets (ignore --status) and optional amount metrics; no pagination/sort flags
13349
+ count-claims same flags as count-bills; only reimbursable claims
12105
13350
  Both lists return summaries; use get-bill-details <billId> for full detail.
12106
13351
  isClaimable differentiates the shared bill/claim records: false = bill, true = claim.
12107
13352
  Create supplier bills from approved quotations from the same supplier and project. Use quotationAllocations plus lineAmounts when quotations split one bill line. Eligible exemptions require explicit --quotation-exempt intent; claims do not consume or link quotations.
@@ -12112,7 +13357,7 @@ Bills
12112
13357
  validate-bill-selection --payload '<json>' [--quotationIds <csv> | --quotationId <id>] Validate an approved supplier and approved quotation coverage for each supplier-bill line. --quotationId is legacy single-quotation syntax.
12113
13358
  link-bill-quotations <billId> (--payload '<json>' | --quotationIds <csv>) Admin-only source-link repair; payload.quotationAllocations supports same-line splits.
12114
13359
  stage-bill-attachment <projectId> <filePath...> [--file <path>] [--files <csv>] Upload files before create-bill; returns attachment JSON for attachments/paymentProofAttachments
12115
- cleanup-staged-bill-attachments <projectId> --keys <csv> Delete unattached staged bill/claim uploads.
13360
+ cleanup-staged-bill-attachments <projectId> --keys <csv> Queue cleanup of unattached staged uploads; returns operation IDs.
12116
13361
  update-bill --payload '<json>' (bill.update; must include id)
12117
13362
  update-bill-payment-evidence --payload '<json>' Replace paymentReference/paymentProofAttachments for an already-paid bill.
12118
13363
  delete-bill <billId>
@@ -12123,23 +13368,29 @@ Bills
12123
13368
  patch-bill-payment <billId> [--paymentTrackingUrl] [--paymentReference] [--quickbooksBillId] [--paymentDate <ISO>]
12124
13369
  Clear a field: --clearPaymentTrackingUrl true | --clearPaymentReference true | --clearQuickbooksBillId true | --clearPaymentDate true
12125
13370
  patch-bill-invoice-number <billId> <invoiceNumber>
12126
- get-bill-attachments <billId>
13371
+ get-bill-attachments <billId> [--limit 1-50] [--cursor <token>]
13372
+ Newest first. Returns { items, pageInfo }; follow nextCursor.
12127
13373
  upload-bill-attachment <billId> <filePath...> [--file <path>] [--files <csv>] Attach invoice/supporting files as BILL attachments to any bill status, including PAID bills
12128
13374
  attach-bill-payment-receipt <billId> <filePath...> [--file <path>] [--files <csv>] Attach Wise/payment-proof PDFs as BILL_PAYMENT_PROOF attachments
12129
13375
  attach-bill-documents <billId> [--invoice <path>] [--invoices <csv>] [--paymentProof|--payment-proof|--proof <path>] [--paymentProofs|--payment-proofs|--proofs <csv>] Attach invoice and/or payment proof files with separate BB attachment types
12130
- get-bill-details <billId>
13376
+ get-bill-details <billId> Bill summary as shown in the web app (status, amounts, people, payment, line totals)
13377
+ get-bill-line-items <billId> [--limit 1-100] [--cursor <token>] Line items with budget-item/quotation detail; { items, pageInfo }
13378
+ get-bill-approvals <billId> [--limit 1-50] [--cursor <token>] Approval requests with approver/requester/responder; { items, pageInfo }
13379
+ get-bill-payment <billId> Payment status, reference, date, tracking URL, QuickBooks link and external payment
12131
13380
 
12132
13381
  Quotations
12133
- list-quotations [--projectId] [--budgetId] [--supplierId] [--status CSV] [--createdBy CSV] [--search] [--page] [--perPage]
12134
- Legacy aliases --requesterIds and --pageSize remain supported.
12135
- get-quotation <quotationId>
13382
+ list-quotations [--projectId] [--budgetId] [--supplierId] [--status CSV] [--createdBy CSV] [--search] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13383
+ Newest first; returns { items, pageInfo }. Follow nextCursor/previousCursor with the same filters. --page/--pageSize/--perPage are rejected; --requesterIds remains an alias for --createdBy.
13384
+ count-quotations [--projectId] [--budgetId] [--supplierId] [--status CSV] [--createdBy CSV] [--search] [--includeMetrics true|false] totalCount, status facets (ignore --status) and optional amount metrics
13385
+ get-quotation <quotationId> Quotation as shown in the web app (status, amounts, people, line totals, files, approvals)
13386
+ get-quotation-line-items <quotationId> [--limit 1-100] [--cursor <token>] Line items with budget-item detail; { items, pageInfo }
12136
13387
  upload-quotation-attachment <projectId> <filePath> Upload one PDF, JPEG, or PNG up to 20MB; returns attachment JSON for a quotation payload.
12137
- cleanup-staged-quotation-attachments <projectId> --keys <csv> Delete unattached staged quotation uploads.
13388
+ cleanup-staged-quotation-attachments <projectId> --keys <csv> Queue cleanup of unattached quotation uploads; returns operation IDs.
12138
13389
  create-quotation --payload '<json>' (quotation.createDraft; requires amountWithoutGst, gstAmount, and lineAmounts [{budgetItemId, amount}] for multiple budgetItemIds; amounts before GST must sum to amountWithoutGst)
12139
13390
  update-quotation --payload '<json>' Update a DRAFT or REJECTED quotation; same allocation payload plus id; use lineAmounts [{budgetItemId, amount}] for every selected item.
12140
13391
  delete-quotation <quotationId>
12141
13392
  submit-quotation <quotationId> [--overBudgetOverrideReason <text>]
12142
- approve-quotation <quotationId> [--overBudgetOverrideReason <text>]
13393
+ approve-quotation <quotationId> [--overBudgetOverrideReason <text>] (assigned pending request/current Admin; concurrent revision or allocation changes conflict)
12143
13394
  reject-quotation <quotationId> --reason <text>
12144
13395
  download-quotation-pdf <quotationId> [original|staff|final]
12145
13396
 
@@ -12148,9 +13399,15 @@ Customer invoices
12148
13399
  list-eligible-customer-invoice-budgets <projectId> Accepted budgets that are ready and still have invoice coverage remaining, including estimate total, invoiced/remaining percentages, and available amount.
12149
13400
  preview-customer-invoice-amount <budgetId> <percentage-of-estimate> Preview amount before GST, GST, and total for a percentage of the original estimate. The percentage cannot exceed the budget's remaining coverage.
12150
13401
  list-available-qbo-customer-invoices <budgetId> List unlinked QuickBooks invoices available for this accepted or closed estimate before previewing and importing one.
12151
- list-customer-invoices [budgetId] [--projectId <id>] [--companyIds CSV] [--projectIds CSV] [--budgetIds CSV] [--createdByIds CSV] [--status CSV] [--search] [--sortBy createdAt|status|totalInvoiceAmount|approvalExpiresAt] [--sortDir asc|desc] [--page] [--perPage]
13402
+ list-customer-invoices [budgetId] [--projectId <id>] [--companyIds CSV] [--projectIds CSV] [--budgetIds CSV] [--createdByIds CSV] [--status CSV] [--search] [--sortBy createdAt|status|totalInvoiceAmount|approvalExpiresAt] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13403
+ Returns { items, pageInfo }; follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected.
12152
13404
  No scope flag lists invoices globally. --projectId uses the project scope. A positional budgetId is a backward-compatible shortcut for --budgetIds.
12153
- get-customer-invoice <batchId>
13405
+ count-customer-invoices [budgetId] [same filter flags as list-customer-invoices] totalCount, lifecycle status facets (ignore --status) and amount metrics; no paging or sort flags
13406
+ get-customer-invoice <batchId> Batch as shown in the web app: status, totals, people, approvals and invoice summaries
13407
+ get-customer-invoice-invoices <batchId> Invoices with QuickBooks identity, dates and sync state
13408
+ get-customer-invoice-lines <invoiceId> [--limit 1-100] [--cursor <token>] One invoice's lines; { items, pageInfo }
13409
+ get-customer-invoice-approvals <batchId> [--limit 1-50] [--cursor <token>] Approval requests with approver/requester/responder; { items, pageInfo }
13410
+ get-customer-invoice-history <batchId> [--limit 1-100] [--cursor <token>] Status history; { items, pageInfo }
12154
13411
  get-customer-invoice-email-context <batchId> Contact, required CC, budget, and project context for composing an invoice email.
12155
13412
  preview-qbo-customer-invoice <budgetId> <qboInvoiceId> Preview an invoice selected from list-available-qbo-customer-invoices; works on accepted/closed estimates.
12156
13413
  import-qbo-customer-invoice <budgetId> <qboInvoiceId> --token <previewToken> Confirm the reviewed BB-only link; requires --allow-state-change --allow-financial-write in automation.
@@ -12174,25 +13431,33 @@ Approvals
12174
13431
  reject-supplier <supplierId> --reason <text> (also sends requester reply email)
12175
13432
 
12176
13433
  Companies & projects
12177
- list-companies [--name] [--createdBy] [--active true|false] [--page] [--perPage]
12178
- Omit --active to include both active and inactive companies.
13434
+ list-companies [--name] [--createdBy] [--createdByIds CSV] [--active true|false] [--sortBy name|createdAt] [--sortDir asc|desc] [--limit 1-100] [--cursor] [--direction forward|backward]
13435
+ Returns { items, pageInfo }; pass nextCursor with direction forward or previousCursor with direction backward. Omit --cursor to restart.
13436
+ count-companies [--name] [--createdBy] [--createdByIds CSV] [--active true|false]
13437
+ Returns independent { totalCount, counts: { active: { true, false } } }; no pagination or sorting flags.
12179
13438
  get-company <id>
12180
- search-existing-companies --query <name> [--page] (BB + QuickBooks, read-only)
13439
+ search-existing-companies --query <name> [--limit 1-50] [--cursor <token>] (BB + QuickBooks, read-only; follow pageInfo.nextCursor)
12181
13440
  use-existing-company --companyId <bbId> and/or --quickbooksId <qboId> [--website <url>] [--confirmDistinct true]
12182
13441
  Supply both IDs to explicitly connect existing records with different names.
12183
13442
  Reuses both records, creating only the missing counterpart. Website is needed only for a QBO import without one.
12184
13443
  create-company --name --website <url> [--confirmDistinct true]
12185
13444
  Exact existing companies are reused. Similar names require explicit separate-entity confirmation.
12186
- update-company --payload '<json>' (company.updateCompany; must include id, name, website)
13445
+ update-company --payload '<json>' (saves BB fields; returns queued QBO operation ID, not completion)
13446
+ get-company-quickbooks-sync-status <companyId> (saved status only; no provider call or polling)
12187
13447
  delete-company <id>
12188
- list-projects [--companyId] [--name] [--status] [--active true|false] [--page] [--perPage]
12189
- Omit --active to include both active and inactive projects.
12190
- list-project-hubs [--q] [--status CSV] [--page] [--perPage] [--sortBy startDate|name|companyName|status] [--sortDir asc|desc]
13448
+ list-projects [--companyId] [--name] [--status|--statuses CSV] [--active true|false] [--wonFrom ISO] [--wonTo ISO] [--wonWithinDays N] [--limit 1-100] [--cursor] [--direction forward|backward] [--sortBy name|createdAt|status|companyName|wonAt] [--sortDir asc|desc]
13449
+ Returns { items, pageInfo }; pass nextCursor with direction forward or previousCursor with direction backward. Legacy --page/--perPage are rejected.
13450
+ --sortBy wonAt orders by when the project was marked WON (unwon projects last). Each project includes wonAt, wonBy { id, name } and wonAtEstimated (true for backfilled dates).
13451
+ count-projects [--companyId] [--name] [--status|--statuses CSV] [--active true|false] [--wonFrom ISO] [--wonTo ISO] [--wonWithinDays N]
13452
+ Returns independent { totalCount, counts: { status, active: { true, false } } }; no pagination or sorting flags.
13453
+ list-project-hubs [--q] [--status CSV] [--sortBy startDate|name|companyName|status] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13454
+ Returns { items, pageInfo }; follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected.
13455
+ count-project-hubs [--q] [--status CSV] totalCount plus status facet
12191
13456
  List active projects that have a Project Hub integration.
12192
13457
  get-project <id>
12193
13458
  Budget overview totalRevenue includes Estimate Accepted and Estimate Closed budgets only.
12194
13459
  create-project --name --companyId --contactPersonId --insideSalesId --businessDevelopmentId --venue --startDate <ISO> [--projectManagerId <userId>] [--asanaTaskId] [--asanaSearch <text>] [--slackChannelId --slackChannelUrl --slackChannelName] [--pax] [--endDate <ISO>] [--description]
12195
- update-project --payload '<json>' (project.updateProject; warns before estimate-affecting changes reject linked estimates; optional projectManagerId and requestQboAccountantNotification)
13460
+ update-project --payload '<json>' (project.updateProject; warns before estimate-affecting changes reject linked estimates; a companyId change (with a contactPersonId from that company) does not itself reject estimates or touch invoices, but date/pax/venue changes in the same save still do; optional projectManagerId and requestQboAccountantNotification)
12196
13461
  delete-project <id>
12197
13462
  check-project-reconciliation <id> Run the same live reconciliation review as the web app without changing status.
12198
13463
  reconcile-project <id> Rerun all checks transactionally and mark an eligible WON project RECONCILED.
@@ -12209,9 +13474,10 @@ Contacts
12209
13474
  update-contact-person --payload '<json>' (contactPerson.updateContactPerson; must include id)
12210
13475
 
12211
13476
  Suppliers & items
12212
- list-suppliers [--name] [--page] [--perPage] [--sortBy createdAt|updatedAt|name|email|phone|gstRegistered|status|active] [--sortDir asc|desc] [--createdBy <csv>] [--gstRegistered true|false] [--status CSV] [--supplierTags <csv>] [--active true|false]
12213
- Defaults: --active true, --perPage 10, --sortBy createdAt, --sortDir desc (same as the web list); pass --active false for archived suppliers.
12214
- create-supplier --payload '<json>' (supplier.createSupplier; auto-requests approval + emails when status is PENDING_APPROVAL)
13477
+ list-suppliers [--name] [--limit 1-100] [--cursor <token>] [--direction forward|backward] [--sortBy createdAt|updatedAt|name|email|phone|gstRegistered|status|active] [--sortDir asc|desc | --sort JSON] [--createdBy <csv>] [--gstRegistered true|false] [--status CSV] [--supplierTags <csv>] [--active true|false]
13478
+ count-suppliers [--name] [--createdBy <csv>] [--gstRegistered true|false] [--status CSV] [--supplierTags <csv>] [--active true|false] (independent totals/facets; no pagination/sort flags)
13479
+ Defaults: --active true, --limit 10, --sortBy createdAt, --sortDir desc (same as the web list); pass --active false for archived suppliers.
13480
+ create-supplier --payload '<json>' (supplier.createSupplier; archived-name recreation is Admin-only; auto-requests approval + emails when status is PENDING_APPROVAL)
12215
13481
  update-supplier --payload '<json>' (authenticated; normal-user edits require server-enforced approval)
12216
13482
  delete-suppliers --ids <csv> (supplier.deleteSuppliers; admin; archives/deactivates supplier items)
12217
13483
  reactivate-suppliers --ids <csv> (supplier.reactivateSuppliers; admin; reactivates supplier items)
@@ -12224,20 +13490,29 @@ Suppliers & items
12224
13490
  archive-items --ids <csv> (item.archiveItems; Budget Builder only; max 100; users can archive their own items, leads/admins any item; open estimates retain snapshot lines and require resolution before send/acceptance)
12225
13491
  delete-item <id> (item.deleteItem; admin; archives in Budget Builder and makes the item inactive in QuickBooks; blocked while used by an open estimate)
12226
13492
  get-supplier-details <supplierId>
12227
- get-supplier-analytics [--name] [--page] [--perPage] [--timeFrame ALL|LAST_YEAR|\u2026] (admin)
12228
- list-items [--name] [--page] [--perPage] [--includeQuickBooksDeactivationPending true]
13493
+ get-supplier-analytics [--name] [--status CSV] [--gstRegistered true|false] [--createdBy <text>] [--timeFrame ALL|LAST_YEAR|LAST_6_MONTHS|LAST_3_MONTHS|CUSTOM] [--startDate <ISO> --endDate <ISO>] [--sortBy name|gstRegistered|status|phone|email|updatedAt|createdAt] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13494
+ Admin. Returns { items, pageInfo }; follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected.
13495
+ count-supplier-analytics [--name] [--status] [--gstRegistered] [--createdBy] [--timeFrame] [--startDate --endDate] Admin. totalCount for the same filters
13496
+ list-items [--name] [--includeQuickBooksDeactivationPending true] [--sortBy createdAt|updatedAt|name|cost|itemCategory|supplier] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13497
+ Returns { items, pageInfo }; follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected.
13498
+ count-items [same filter flags as list-items] total plus category and supplier facets
12229
13499
  get-item <id>
12230
- list-item-categories [--page] [--perPage]
13500
+ get-item-quickbooks-sync-status <itemId> (saved status only; no provider call or polling)
13501
+ list-item-categories [--name] [--sortBy createdAt|name|itemCount|createdBy] [--sortDir asc|desc] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13502
+ Active categories only; returns { items, pageInfo }. Follow nextCursor/previousCursor with the same filters and sort. --page/--perPage are rejected.
13503
+ count-item-categories [--name] totalCount of active categories
12231
13504
  create-item-category --name (admin)
12232
13505
  update-item-category --id --name (authenticated; applies immediately)
12233
13506
  delete-item-categories --ids <csv> (itemCategory.deleteItemCategories; admin; empty categories only)
12234
13507
 
12235
13508
  Dashboard & users
12236
13509
  whoami Show the user identity and role for the active API key.
12237
- list-users
13510
+ list-users [--q] [--limit 1-100] [--cursor <token>]
13511
+ Name A\u2013Z. Returns { items, pageInfo }; follow nextCursor with the same --q. --page/--perPage are rejected.
13512
+ count-users [--q] totalCount for the same search
12238
13513
  create-user --email <email> [--name <name>] [--role ${userRolesForHelp.join("|")}] (admin; API-only service identity with no Google sign-in)
12239
13514
  create-api-key --name <label> [--userId <userId>] (defaults to the caller; admin required for another user; raw key is shown once)
12240
- list-api-keys [--userId <userId>] (defaults to the caller; admin required for another user)
13515
+ list-api-keys [--userId <userId>] [--limit 1-100] [--cursor <token>] (newest first, { items, pageInfo }; defaults to the caller; admin required for another user)
12241
13516
  revoke-api-key <apiKeyId> [--userId <userId>] (defaults to the caller; admin required for another user)
12242
13517
  get-user-performance [--userId] (admin)
12243
13518
  get-dashboard [--userId] [--role BD|CREATOR|INSIDE_SALES|ALL] [--deals ALL|SUCCESSFUL|LOST] [--timeFrame] [--startDate] [--endDate] (admin)
@@ -12247,12 +13522,31 @@ Dashboard & users
12247
13522
  get-financial-overview [same optional flags as dashboard; role uses BudgetRole enum] (admin)
12248
13523
 
12249
13524
  Errors
12250
- get-recent-errors [--page] [--perPage] [--severity] [--status] (admin)
13525
+ get-recent-errors [--cursor] [--direction forward|backward] [--limit] [--severity] [--status] [--timeFrame] (admin)
13526
+ count-errors [--severity] [--status] [--timeFrame] (admin)
13527
+ get-error <errorId> Full diagnostics: stack, cause, request, metadata (admin)
12251
13528
  get-error-metrics (admin)
12252
13529
 
12253
13530
  Automation (admin)
12254
- list-integration-operations [--search] [--destination <provider,...>] [--status <PENDING|PROCESSING|FAILED|COMPLETED,...>] [--page] [--perPage]
12255
- retry-integration-operation <operationId> [--confirmExternalStateReconciled true|false] [--outboundEmailResolution ACCEPTED|NOT_ACCEPTED_RETRY] [--providerMessageId <id>]
13531
+ list-integration-operations [--search] [--destination <provider,...>] [--status <PENDING|PROCESSING|FAILED|COMPLETED,...>] [--limit 1-100] [--cursor <token>] [--direction forward|backward]
13532
+ Newest first; returns { items, pageInfo }. Follow nextCursor/previousCursor with the same filters. --page/--perPage are rejected.
13533
+ count-integration-operations [same filter flags] provider filter options only (no total; filters do not change the result)
13534
+ get-integration-operation <operationId> (one job: who queued it, what it is for, saved progress, Retry state)
13535
+ retry-integration-operation <operationId> [--confirmExternalStateReconciled true|false] [--outboundEmailResolution ACCEPTED|NOT_ACCEPTED_RETRY] [--providerMessageId <id>] (QuickBooks jobs with a saved attempt check QuickBooks first: applied changes finish without resending, unapplied ones are sent again)
13536
+ reconcile-resource-operation <operationId> (admin: check a QuickBooks invoice/company/item job outcome without retrying; same check Retry runs first)
13537
+
13538
+ Webhooks (admin)
13539
+ list-webhook-event-types (event catalogue: type, group, live|planned)
13540
+ list-webhook-endpoints
13541
+ get-webhook-endpoint <endpointId>
13542
+ create-webhook-endpoint --name <name> --url <https-url> --events <type|prefix.*|*,...> [--enabled true|false] (prints the signing secret once)
13543
+ update-webhook-endpoint <endpointId> [--name] [--url] [--events <...>] [--enabled true|false]
13544
+ delete-webhook-endpoint <endpointId>
13545
+ rotate-webhook-secret <endpointId> [--previousSecretTtlHours <0-168>] (prints the new secret once)
13546
+ send-webhook-test-event <endpointId>
13547
+ list-webhook-deliveries [--endpointId] [--status PENDING|SUCCEEDED|FAILED] [--eventType] [--cursor] [--limit]
13548
+ get-webhook-delivery <deliveryId> (attempts with response codes)
13549
+ replay-webhook-delivery <deliveryId> (same eventId, new attempt chain)
12256
13550
 
12257
13551
  Historical / benchmarks
12258
13552
  get-approved-budgets [--projectId] [--limit] [--minRevenue] [--maxRevenue]
@@ -12594,6 +13888,8 @@ Waiting for approval\u2026
12594
13888
  });
12595
13889
  }
12596
13890
  try {
13891
+ if (flags.q === true)
13892
+ throw new CliRuntimeError("USAGE", "--q needs a search value (--q <text> or --q=<text>). Use -q or --quiet to hide logs.");
12597
13893
  if (runtime.env.BB_API_KEY?.trim()) {
12598
13894
  configureAccessToken(undefined);
12599
13895
  } else {
@@ -12625,21 +13921,49 @@ Waiting for approval\u2026
12625
13921
  await whoAmI();
12626
13922
  break;
12627
13923
  }
12628
- case "list-budgets": {
13924
+ case "list-budgets":
13925
+ case "count-budgets": {
12629
13926
  const statusStr = getFlag(flags, "status");
12630
13927
  const statuses = parseCommaSeparatedBudgetStatuses(statusStr !== undefined ? String(statusStr) : undefined);
12631
- await listBudgets({
13928
+ const filters = {
12632
13929
  projectId: getFlag(flags, "projectId"),
12633
13930
  name: getFlag(flags, "name"),
12634
13931
  statuses,
12635
13932
  createdBy: getFlag(flags, "createdBy"),
12636
13933
  dateFrom: getFlag(flags, "dateFrom"),
12637
13934
  dateTo: getFlag(flags, "dateTo"),
12638
- active: parseBudgetActiveFlag(flags),
13935
+ active: parseBudgetActiveFlag(flags)
13936
+ };
13937
+ rejectRetiredPageFlags(flags);
13938
+ if (cmd === "count-budgets") {
13939
+ for (const key of [
13940
+ "limit",
13941
+ "cursor",
13942
+ "direction",
13943
+ "sortBy",
13944
+ "sortDir"
13945
+ ]) {
13946
+ if (flags[key] !== undefined)
13947
+ throw new Error(`count-budgets does not accept --${key}; totals use filters only.`);
13948
+ }
13949
+ await countBudgets(filters);
13950
+ break;
13951
+ }
13952
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"])
13953
+ rejectBareFlag(flags, key);
13954
+ const cursor = getFlag(flags, "cursor");
13955
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
13956
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
13957
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
13958
+ if (direction === "backward" && !cursor)
13959
+ throw new Error("--direction backward requires --cursor <token>.");
13960
+ await listBudgets({
13961
+ ...filters,
13962
+ limit: parseProjectLimit(getFlag(flags, "limit")),
13963
+ cursor,
13964
+ direction,
12639
13965
  sortBy: getFlag(flags, "sortBy"),
12640
13966
  sortDir: getFlag(flags, "sortDir"),
12641
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
12642
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
12643
13967
  includeDetails: flags.summary === true ? false : parseOptionalBoolFlag(flags, "includeDetails") ?? true
12644
13968
  });
12645
13969
  break;
@@ -12692,7 +14016,19 @@ Waiting for approval\u2026
12692
14016
  break;
12693
14017
  }
12694
14018
  case "get-budget-categories": {
12695
- await getBudgetCategories();
14019
+ rejectRetiredPageFlags(flags);
14020
+ for (const key of ["limit", "cursor"])
14021
+ rejectBareFlag(flags, key);
14022
+ if (flags.direction !== undefined)
14023
+ throw new Error("get-budget-categories pages forward only; follow pageInfo.nextCursor with --cursor.");
14024
+ const cursor = getFlag(flags, "cursor");
14025
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
14026
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
14027
+ await getBudgetCategories({
14028
+ q: getFlag(flags, "q"),
14029
+ limit: parseProjectLimit(getFlag(flags, "limit")),
14030
+ cursor
14031
+ });
12696
14032
  break;
12697
14033
  }
12698
14034
  case "get-budget-versions": {
@@ -12704,7 +14040,7 @@ Waiting for approval\u2026
12704
14040
  throw new CliRuntimeError("USAGE", "--limit must be an integer from 1 to 50.");
12705
14041
  }
12706
14042
  if (flags.cursor === true || flags.cursor === "") {
12707
- throw new CliRuntimeError("USAGE", "--cursor requires a non-empty version ID.");
14043
+ throw new CliRuntimeError("USAGE", "--cursor requires the nextCursor token from the previous page.");
12708
14044
  }
12709
14045
  await getBudgetVersions(id, {
12710
14046
  limit,
@@ -12985,6 +14321,14 @@ Waiting for approval\u2026
12985
14321
  });
12986
14322
  break;
12987
14323
  }
14324
+ case "count-bills": {
14325
+ await countBills(parseCountBillsFlags(flags));
14326
+ break;
14327
+ }
14328
+ case "count-claims": {
14329
+ await countBills({ ...parseCountBillsFlags(flags), isClaimable: true });
14330
+ break;
14331
+ }
12988
14332
  case "create-bill-approval": {
12989
14333
  const id = positional[0];
12990
14334
  if (!id)
@@ -13182,10 +14526,25 @@ Waiting for approval\u2026
13182
14526
  break;
13183
14527
  }
13184
14528
  case "get-bill-attachments": {
14529
+ const page = parseDetailFeedPage("get-bill-attachments", "<billId>", positional, flags, 50);
14530
+ await getBillAttachments(page);
14531
+ break;
14532
+ }
14533
+ case "get-bill-approvals": {
14534
+ const page = parseDetailFeedPage("get-bill-approvals", "<billId>", positional, flags, 50);
14535
+ await getBillApprovals(page);
14536
+ break;
14537
+ }
14538
+ case "get-bill-line-items": {
14539
+ const page = parseDetailFeedPage("get-bill-line-items", "<billId>", positional, flags, 100);
14540
+ await getBillLineItems(page);
14541
+ break;
14542
+ }
14543
+ case "get-bill-payment": {
13185
14544
  const id = positional[0];
13186
14545
  if (!id)
13187
- throw new Error("get-bill-attachments requires <billId>");
13188
- await getBillAttachments(id);
14546
+ throw new Error("get-bill-payment requires <billId>");
14547
+ await getBillPayment(id);
13189
14548
  break;
13190
14549
  }
13191
14550
  case "stage-bill-attachment": {
@@ -13278,18 +14637,32 @@ Waiting for approval\u2026
13278
14637
  break;
13279
14638
  }
13280
14639
  case "list-quotations": {
13281
- const statusRaw = getFlag(flags, "status");
13282
- const createdByRaw = getFlag(flags, "createdBy") ?? getFlag(flags, "requesterIds");
13283
- const perPageRaw = getFlag(flags, "perPage") ?? getFlag(flags, "pageSize");
14640
+ rejectRetiredPageFlags(flags);
14641
+ for (const key of ["limit", "cursor", "direction"])
14642
+ rejectBareFlag(flags, key);
14643
+ const cursor = getFlag(flags, "cursor");
14644
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
14645
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
14646
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
14647
+ if (direction === "backward" && !cursor)
14648
+ throw new Error("--direction backward requires --cursor <token>.");
13284
14649
  await listQuotations({
13285
- projectId: getFlag(flags, "projectId"),
13286
- budgetId: getFlag(flags, "budgetId"),
13287
- supplierId: getFlag(flags, "supplierId"),
13288
- status: parseCommaSeparatedQuotationStatuses(statusRaw),
13289
- createdBy: parseCommaSeparatedIds(createdByRaw),
13290
- search: getFlag(flags, "search"),
13291
- page: getFlag(flags, "page") !== undefined ? Number(getFlag(flags, "page")) : undefined,
13292
- perPage: perPageRaw !== undefined ? Number(perPageRaw) : undefined
14650
+ ...parseQuotationFilterFlags(flags),
14651
+ limit: parseProjectLimit(getFlag(flags, "limit")),
14652
+ cursor,
14653
+ direction
14654
+ });
14655
+ break;
14656
+ }
14657
+ case "count-quotations": {
14658
+ rejectRetiredPageFlags(flags);
14659
+ for (const key of ["limit", "cursor", "direction"]) {
14660
+ if (flags[key] !== undefined)
14661
+ throw new Error(`count-quotations does not accept --${key}; totals use filters only.`);
14662
+ }
14663
+ await countQuotations({
14664
+ ...parseQuotationFilterFlags(flags),
14665
+ includeMetrics: parseOptionalBoolFlag(flags, "includeMetrics") ?? false
13293
14666
  });
13294
14667
  break;
13295
14668
  }
@@ -13300,6 +14673,11 @@ Waiting for approval\u2026
13300
14673
  await getQuotationDetails(id);
13301
14674
  break;
13302
14675
  }
14676
+ case "get-quotation-line-items": {
14677
+ const page = parseDetailFeedPage("get-quotation-line-items", "<quotationId>", positional, flags, 100);
14678
+ await getQuotationLineItems(page);
14679
+ break;
14680
+ }
13303
14681
  case "upload-quotation-attachment": {
13304
14682
  const [projectId, filePath] = positional;
13305
14683
  if (!projectId || !filePath) {
@@ -13411,7 +14789,8 @@ Waiting for approval\u2026
13411
14789
  await listAvailableQboCustomerInvoices(budgetId);
13412
14790
  break;
13413
14791
  }
13414
- case "list-customer-invoices": {
14792
+ case "list-customer-invoices":
14793
+ case "count-customer-invoices": {
13415
14794
  const positionalBudgetId = positional[0];
13416
14795
  const projectId = getFlag(flags, "projectId");
13417
14796
  const projectIds = parseCommaSeparatedIds(getFlag(flags, "projectIds"));
@@ -13423,17 +14802,44 @@ Waiting for approval\u2026
13423
14802
  ...parseCommaSeparatedIds(getFlag(flags, "budgetIds")) ?? []
13424
14803
  ];
13425
14804
  const statusRaw = getFlag(flags, "status");
13426
- const sortDir = parseOptionalBillListSortDir(getFlag(flags, "sortDir"));
13427
- await listCustomerInvoices({
14805
+ const filters = {
13428
14806
  scope: projectId ? { type: "project", projectId } : { type: "global" },
13429
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page") ?? 1,
13430
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage") ?? 20,
13431
14807
  q: getFlag(flags, "search") ?? "",
13432
14808
  statuses: parseCommaSeparatedCustomerInvoiceStatuses(statusRaw !== undefined ? String(statusRaw) : undefined),
13433
14809
  companyIds: parseCommaSeparatedIds(getFlag(flags, "companyIds")),
13434
14810
  projectIds,
13435
14811
  budgetIds: budgetIds.length > 0 ? [...new Set(budgetIds)] : undefined,
13436
- createdByIds: parseCommaSeparatedIds(getFlag(flags, "createdByIds")),
14812
+ createdByIds: parseCommaSeparatedIds(getFlag(flags, "createdByIds"))
14813
+ };
14814
+ rejectRetiredPageFlags(flags);
14815
+ if (cmd === "count-customer-invoices") {
14816
+ for (const key of [
14817
+ "limit",
14818
+ "cursor",
14819
+ "direction",
14820
+ "sortBy",
14821
+ "sortDir"
14822
+ ]) {
14823
+ if (flags[key] !== undefined)
14824
+ throw new Error(`count-customer-invoices does not accept --${key}; totals use filters only.`);
14825
+ }
14826
+ await countCustomerInvoices(filters);
14827
+ break;
14828
+ }
14829
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"])
14830
+ rejectBareFlag(flags, key);
14831
+ const cursor = getFlag(flags, "cursor");
14832
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
14833
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
14834
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
14835
+ if (direction === "backward" && !cursor)
14836
+ throw new Error("--direction backward requires --cursor <token>.");
14837
+ const sortDir = parseOptionalBillListSortDir(getFlag(flags, "sortDir"));
14838
+ await listCustomerInvoices({
14839
+ ...filters,
14840
+ limit: parseProjectLimit(getFlag(flags, "limit")),
14841
+ cursor,
14842
+ direction,
13437
14843
  sortBy: parseCustomerInvoiceSortBy(getFlag(flags, "sortBy")),
13438
14844
  sortDir: sortDir ?? "desc"
13439
14845
  });
@@ -13447,6 +14853,28 @@ Waiting for approval\u2026
13447
14853
  await getCustomerInvoice(batchId);
13448
14854
  break;
13449
14855
  }
14856
+ case "get-customer-invoice-invoices": {
14857
+ const batchId = positional[0];
14858
+ if (!batchId)
14859
+ throw new Error("get-customer-invoice-invoices requires <batchId>");
14860
+ await getCustomerInvoiceInvoices(batchId);
14861
+ break;
14862
+ }
14863
+ case "get-customer-invoice-lines": {
14864
+ const page = parseDetailFeedPage("get-customer-invoice-lines", "<invoiceId>", positional, flags, 100);
14865
+ await getCustomerInvoiceLines(page);
14866
+ break;
14867
+ }
14868
+ case "get-customer-invoice-approvals": {
14869
+ const page = parseDetailFeedPage("get-customer-invoice-approvals", "<batchId>", positional, flags, 50);
14870
+ await getCustomerInvoiceApprovals(page);
14871
+ break;
14872
+ }
14873
+ case "get-customer-invoice-history": {
14874
+ const page = parseDetailFeedPage("get-customer-invoice-history", "<batchId>", positional, flags, 100);
14875
+ await getCustomerInvoiceHistory(page);
14876
+ break;
14877
+ }
13450
14878
  case "get-customer-invoice-email-context": {
13451
14879
  const batchId = positional[0];
13452
14880
  if (!batchId) {
@@ -13612,13 +15040,11 @@ Waiting for approval\u2026
13612
15040
  break;
13613
15041
  }
13614
15042
  case "list-companies": {
13615
- await listCompanies({
13616
- name: getFlag(flags, "name"),
13617
- createdBy: getFlag(flags, "createdBy"),
13618
- active: parseOptionalBoolFlag(flags, "active"),
13619
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
13620
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage")
13621
- });
15043
+ await listCompanies(parseCompanyListFlags(flags));
15044
+ break;
15045
+ }
15046
+ case "count-companies": {
15047
+ await countCompanies(parseCompanyCountFlags(flags));
13622
15048
  break;
13623
15049
  }
13624
15050
  case "get-company": {
@@ -13645,7 +15071,17 @@ Waiting for approval\u2026
13645
15071
  const query = getFlag(flags, "query");
13646
15072
  if (!query)
13647
15073
  throw new Error("search-existing-companies requires --query <name>");
13648
- await searchExistingCompanies(query, parsePositiveIntFlag(getFlag(flags, "page"), "--page"));
15074
+ rejectRetiredPageFlags(flags);
15075
+ for (const key of ["limit", "cursor"])
15076
+ rejectBareFlag(flags, key);
15077
+ const cursor = getFlag(flags, "cursor");
15078
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15079
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15080
+ const limitFlag = getFlag(flags, "limit");
15081
+ const limit = limitFlag === undefined ? undefined : Number(limitFlag);
15082
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1 || limit > 50))
15083
+ throw new Error("--limit must be an integer from 1 to 50.");
15084
+ await searchExistingCompanies({ query, limit, cursor });
13649
15085
  break;
13650
15086
  }
13651
15087
  case "use-existing-company": {
@@ -13678,6 +15114,13 @@ Waiting for approval\u2026
13678
15114
  await updateCompanyFromPayload(raw);
13679
15115
  break;
13680
15116
  }
15117
+ case "get-company-quickbooks-sync-status": {
15118
+ const id = positional[0];
15119
+ if (!id)
15120
+ throw new Error("get-company-quickbooks-sync-status requires <companyId>");
15121
+ await getCompanyQuickBooksSyncStatus(id);
15122
+ break;
15123
+ }
13681
15124
  case "delete-company": {
13682
15125
  const id = positional[0];
13683
15126
  if (!id)
@@ -13686,23 +15129,46 @@ Waiting for approval\u2026
13686
15129
  break;
13687
15130
  }
13688
15131
  case "list-projects": {
13689
- const statusRaw = getFlag(flags, "status");
13690
- await listProjects({
13691
- companyId: getFlag(flags, "companyId"),
13692
- name: getFlag(flags, "name"),
13693
- status: parseOptionalExtendedProjectStatus(statusRaw !== undefined ? String(statusRaw) : undefined),
13694
- active: parseOptionalBoolFlag(flags, "active"),
13695
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
13696
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage")
13697
- });
15132
+ await listProjects(parseProjectListFlags(flags));
13698
15133
  break;
13699
15134
  }
13700
- case "list-project-hubs": {
13701
- await listProjectHubs({
15135
+ case "count-projects": {
15136
+ await countProjects(parseProjectCountFlags(flags));
15137
+ break;
15138
+ }
15139
+ case "list-project-hubs":
15140
+ case "count-project-hubs": {
15141
+ rejectRetiredPageFlags(flags);
15142
+ const filters = {
13702
15143
  q: getFlag(flags, "q"),
13703
- statuses: parseCommaSeparatedProjectStatuses(getFlag(flags, "status")),
13704
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
13705
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
15144
+ statuses: parseCommaSeparatedProjectStatuses(getFlag(flags, "status"))
15145
+ };
15146
+ if (cmd === "count-project-hubs") {
15147
+ for (const key of [
15148
+ "limit",
15149
+ "cursor",
15150
+ "direction",
15151
+ "sortBy",
15152
+ "sortDir"
15153
+ ])
15154
+ if (flags[key] !== undefined)
15155
+ throw new Error(`count-project-hubs does not accept --${key}; totals use filters only.`);
15156
+ await countProjectHubs(filters);
15157
+ break;
15158
+ }
15159
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"])
15160
+ rejectBareFlag(flags, key);
15161
+ const cursor = getFlag(flags, "cursor");
15162
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15163
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15164
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
15165
+ if (direction === "backward" && !cursor)
15166
+ throw new Error("--direction backward requires --cursor <token>.");
15167
+ await listProjectHubs({
15168
+ ...filters,
15169
+ limit: parseProjectLimit(getFlag(flags, "limit")),
15170
+ cursor,
15171
+ direction,
13706
15172
  sortBy: parseOptionalProjectHubSortBy(getFlag(flags, "sortBy")),
13707
15173
  sortDir: parseOptionalBillListSortDir(getFlag(flags, "sortDir"))
13708
15174
  });
@@ -13871,6 +15337,10 @@ Waiting for approval\u2026
13871
15337
  await updateContactPersonFromPayload(raw);
13872
15338
  break;
13873
15339
  }
15340
+ case "count-suppliers": {
15341
+ await countSuppliers(parseCountSuppliersFlags(flags));
15342
+ break;
15343
+ }
13874
15344
  case "list-suppliers": {
13875
15345
  await listSuppliers(parseListSuppliersFlags(flags));
13876
15346
  break;
@@ -13945,22 +15415,117 @@ Waiting for approval\u2026
13945
15415
  await createSupplierTagOption(String(name));
13946
15416
  break;
13947
15417
  }
13948
- case "get-supplier-analytics": {
15418
+ case "get-supplier-analytics":
15419
+ case "count-supplier-analytics": {
15420
+ rejectRetiredPageFlags(flags);
13949
15421
  const tfRaw = getFlag(flags, "timeFrame");
13950
- await getSupplierAnalytics({
15422
+ for (const key of [
15423
+ "name",
15424
+ "status",
15425
+ "gstRegistered",
15426
+ "createdBy",
15427
+ "startDate",
15428
+ "endDate",
15429
+ "timeFrame"
15430
+ ])
15431
+ rejectBareFlag(flags, key);
15432
+ const timeFrame = parseOptionalSupplierAnalyticsTimeFrame(tfRaw !== undefined ? String(tfRaw) : undefined);
15433
+ const startDate = getFlag(flags, "startDate");
15434
+ const endDate = getFlag(flags, "endDate");
15435
+ if ((startDate || endDate) && timeFrame !== "CUSTOM")
15436
+ throw new Error("--startDate/--endDate need --timeFrame CUSTOM; other time frames ignore them.");
15437
+ const statusRaw = getFlag(flags, "status");
15438
+ const status = statusRaw?.split(",").map((value) => value.trim()).filter(Boolean);
15439
+ const badStatus = status?.find((value) => !Object.values(ApprovalStatus2).some((s) => s === value));
15440
+ if (badStatus)
15441
+ throw new Error(`Invalid --status ${badStatus}. Use a comma list of: ${Object.values(ApprovalStatus2).join(", ")}.`);
15442
+ const gstRaw = getFlag(flags, "gstRegistered");
15443
+ if (gstRaw !== undefined && gstRaw !== "true" && gstRaw !== "false")
15444
+ throw new Error("--gstRegistered must be true or false.");
15445
+ const filters = {
13951
15446
  name: getFlag(flags, "name"),
13952
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
13953
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
13954
- timeFrame: parseOptionalSupplierAnalyticsTimeFrame(tfRaw !== undefined ? String(tfRaw) : undefined)
15447
+ timeFrame,
15448
+ status: status?.length ? status : undefined,
15449
+ gstRegistered: gstRaw === undefined ? undefined : gstRaw === "true",
15450
+ createdBy: getFlag(flags, "createdBy"),
15451
+ startDate,
15452
+ endDate
15453
+ };
15454
+ if (cmd === "count-supplier-analytics") {
15455
+ for (const key of [
15456
+ "limit",
15457
+ "cursor",
15458
+ "direction",
15459
+ "sortBy",
15460
+ "sortDir"
15461
+ ])
15462
+ if (flags[key] !== undefined)
15463
+ throw new Error(`count-supplier-analytics does not accept --${key}; totals use filters only.`);
15464
+ await countSupplierAnalytics(filters);
15465
+ break;
15466
+ }
15467
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"])
15468
+ rejectBareFlag(flags, key);
15469
+ const cursor = getFlag(flags, "cursor");
15470
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15471
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15472
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
15473
+ if (direction === "backward" && !cursor)
15474
+ throw new Error("--direction backward requires --cursor <token>.");
15475
+ const sortBy = getFlag(flags, "sortBy");
15476
+ if (sortBy !== undefined && !SUPPLIER_ANALYTICS_SORT_IDS.some((id) => id === sortBy))
15477
+ throw new Error(`--sortBy must be one of: ${SUPPLIER_ANALYTICS_SORT_IDS.join(", ")}.`);
15478
+ await getSupplierAnalytics({
15479
+ ...filters,
15480
+ limit: parseProjectLimit(getFlag(flags, "limit")),
15481
+ cursor,
15482
+ direction,
15483
+ sortBy: SUPPLIER_ANALYTICS_SORT_IDS.find((id) => id === sortBy),
15484
+ sortDir: parseOptionalBillListSortDir(getFlag(flags, "sortDir"))
13955
15485
  });
13956
15486
  break;
13957
15487
  }
13958
- case "list-items": {
13959
- await listItems({
15488
+ case "list-items":
15489
+ case "count-items": {
15490
+ rejectRetiredPageFlags(flags);
15491
+ const filters = {
13960
15492
  includeQuickBooksDeactivationPending: flags.includeQuickBooksDeactivationPending === true ? true : parseOptionalBoolFlag(flags, "includeQuickBooksDeactivationPending"),
13961
- name: getFlag(flags, "name"),
13962
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
13963
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage")
15493
+ name: getFlag(flags, "name")
15494
+ };
15495
+ if (cmd === "count-items") {
15496
+ for (const key of [
15497
+ "limit",
15498
+ "cursor",
15499
+ "direction",
15500
+ "sortBy",
15501
+ "sortDir"
15502
+ ])
15503
+ if (flags[key] !== undefined)
15504
+ throw new Error(`count-items does not accept --${key}; totals use filters only.`);
15505
+ await countItems(filters);
15506
+ break;
15507
+ }
15508
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"])
15509
+ rejectBareFlag(flags, key);
15510
+ const cursor = getFlag(flags, "cursor");
15511
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15512
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15513
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
15514
+ if (direction === "backward" && !cursor)
15515
+ throw new Error("--direction backward requires --cursor <token>.");
15516
+ const sortBy = getFlag(flags, "sortBy");
15517
+ const sortId = INVENTORY_SORT_IDS.find((id) => id === sortBy);
15518
+ if (sortBy !== undefined && !sortId)
15519
+ throw new Error(`Invalid --sortBy "${sortBy}". Use one of: ${INVENTORY_SORT_IDS.join(", ")}.`);
15520
+ const sortDir = getFlag(flags, "sortDir");
15521
+ if (sortDir !== undefined && sortDir !== "asc" && sortDir !== "desc")
15522
+ throw new Error('--sortDir must be "asc" or "desc".');
15523
+ await listItems({
15524
+ ...filters,
15525
+ limit: parseProjectLimit(getFlag(flags, "limit")),
15526
+ cursor,
15527
+ direction,
15528
+ sort: sortId ? [{ id: sortId, desc: sortDir !== "asc" }] : undefined
13964
15529
  });
13965
15530
  break;
13966
15531
  }
@@ -13971,6 +15536,14 @@ Waiting for approval\u2026
13971
15536
  await getItem(id);
13972
15537
  break;
13973
15538
  }
15539
+ case "get-item-quickbooks-sync-status": {
15540
+ const id = positional[0];
15541
+ if (!id) {
15542
+ throw new Error("get-item-quickbooks-sync-status requires <itemId>");
15543
+ }
15544
+ await getItemQuickBooksSyncStatus(id);
15545
+ break;
15546
+ }
13974
15547
  case "create-item": {
13975
15548
  const payloadRaw = getFlag(flags, "payload");
13976
15549
  if (!payloadRaw) {
@@ -14008,10 +15581,45 @@ Waiting for approval\u2026
14008
15581
  await archiveItemsByIds(ids);
14009
15582
  break;
14010
15583
  }
14011
- case "list-item-categories": {
15584
+ case "list-item-categories":
15585
+ case "count-item-categories": {
15586
+ rejectRetiredPageFlags(flags);
15587
+ const filters = { name: getFlag(flags, "name") };
15588
+ if (cmd === "count-item-categories") {
15589
+ for (const key of [
15590
+ "limit",
15591
+ "cursor",
15592
+ "direction",
15593
+ "sortBy",
15594
+ "sortDir"
15595
+ ])
15596
+ if (flags[key] !== undefined)
15597
+ throw new Error(`count-item-categories does not accept --${key}; totals use filters only.`);
15598
+ await countItemCategories(filters);
15599
+ break;
15600
+ }
15601
+ for (const key of ["limit", "cursor", "direction", "sortBy", "sortDir"])
15602
+ rejectBareFlag(flags, key);
15603
+ const cursor = getFlag(flags, "cursor");
15604
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15605
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15606
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
15607
+ if (direction === "backward" && !cursor)
15608
+ throw new Error("--direction backward requires --cursor <token>.");
15609
+ const sortByRaw = getFlag(flags, "sortBy");
15610
+ const sortBy = ITEM_CATEGORY_SORT_IDS.find((id) => id === sortByRaw);
15611
+ if (sortByRaw !== undefined && !sortBy)
15612
+ throw new Error(`Invalid --sortBy "${sortByRaw}". Use one of: ${ITEM_CATEGORY_SORT_IDS.join(", ")}.`);
15613
+ const sortDir = getFlag(flags, "sortDir");
15614
+ if (sortDir !== undefined && sortDir !== "asc" && sortDir !== "desc")
15615
+ throw new Error('--sortDir must be "asc" or "desc".');
14012
15616
  await listItemCategories({
14013
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
14014
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage")
15617
+ ...filters,
15618
+ limit: parseProjectLimit(getFlag(flags, "limit")),
15619
+ cursor,
15620
+ direction,
15621
+ sortBy,
15622
+ sortDir
14015
15623
  });
14016
15624
  break;
14017
15625
  }
@@ -14043,8 +15651,29 @@ Waiting for approval\u2026
14043
15651
  await deleteItemCategoriesByIds(ids);
14044
15652
  break;
14045
15653
  }
14046
- case "list-users": {
14047
- await listUsers();
15654
+ case "list-users":
15655
+ case "count-users": {
15656
+ rejectRetiredPageFlags(flags);
15657
+ const q = getFlag(flags, "q");
15658
+ if (cmd === "count-users") {
15659
+ for (const key of ["limit", "cursor"])
15660
+ if (flags[key] !== undefined)
15661
+ throw new Error(`count-users does not accept --${key}; totals use --q only.`);
15662
+ await countUsers({ q });
15663
+ break;
15664
+ }
15665
+ for (const key of ["limit", "cursor"])
15666
+ rejectBareFlag(flags, key);
15667
+ if (flags.direction !== undefined)
15668
+ throw new Error("list-users pages forward only; follow pageInfo.nextCursor with --cursor.");
15669
+ const cursor = getFlag(flags, "cursor");
15670
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15671
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15672
+ await listUsers({
15673
+ q,
15674
+ limit: parseProjectLimit(getFlag(flags, "limit")),
15675
+ cursor
15676
+ });
14048
15677
  break;
14049
15678
  }
14050
15679
  case "create-user": {
@@ -14069,7 +15698,23 @@ Waiting for approval\u2026
14069
15698
  break;
14070
15699
  }
14071
15700
  case "list-api-keys": {
14072
- await listApiKeysForUser({ userId: getFlag(flags, "userId") });
15701
+ rejectRetiredPageFlags(flags);
15702
+ for (const key of ["limit", "cursor"])
15703
+ rejectBareFlag(flags, key);
15704
+ if (flags.direction !== undefined)
15705
+ throw new Error("list-api-keys pages forward only; follow pageInfo.nextCursor with --cursor.");
15706
+ const cursor = getFlag(flags, "cursor");
15707
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15708
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15709
+ const limitFlag = getFlag(flags, "limit");
15710
+ const limit = limitFlag === undefined ? undefined : Number(limitFlag);
15711
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1 || limit > 100))
15712
+ throw new Error("--limit must be an integer from 1 to 100.");
15713
+ await listApiKeysForUser({
15714
+ userId: getFlag(flags, "userId"),
15715
+ limit,
15716
+ cursor
15717
+ });
14073
15718
  break;
14074
15719
  }
14075
15720
  case "revoke-api-key": {
@@ -14114,29 +15759,88 @@ Waiting for approval\u2026
14114
15759
  });
14115
15760
  break;
14116
15761
  }
14117
- case "get-recent-errors": {
14118
- await getRecentErrors({
14119
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page"),
14120
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage"),
15762
+ case "get-recent-errors":
15763
+ case "count-errors": {
15764
+ if (getFlag(flags, "page") !== undefined || getFlag(flags, "perPage") !== undefined) {
15765
+ throw new Error("Error logs now use --cursor and --limit; --page/--perPage are no longer supported.");
15766
+ }
15767
+ const rawTimeFrame = getFlag(flags, "timeFrame");
15768
+ const timeFrame = Object.values(ChartTimeFrame).find((value) => value === rawTimeFrame);
15769
+ if (rawTimeFrame !== undefined && timeFrame === undefined) {
15770
+ throw new Error(`Invalid --timeFrame. Use ${Object.values(ChartTimeFrame).join(", ")}.`);
15771
+ }
15772
+ const filters = {
14121
15773
  severity: parseOptionalErrorSeverity(getFlag(flags, "severity")),
14122
- status: parseOptionalErrorStatus(getFlag(flags, "status"))
14123
- });
15774
+ status: parseOptionalErrorStatus(getFlag(flags, "status")),
15775
+ timeFrame
15776
+ };
15777
+ if (cmd === "count-errors") {
15778
+ await countErrors(filters);
15779
+ } else {
15780
+ const direction = getFlag(flags, "direction");
15781
+ if (direction !== undefined && direction !== "forward" && direction !== "backward") {
15782
+ throw new Error("--direction must be forward or backward.");
15783
+ }
15784
+ await getRecentErrors({
15785
+ ...filters,
15786
+ cursor: getFlag(flags, "cursor"),
15787
+ limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit"),
15788
+ direction
15789
+ });
15790
+ }
15791
+ break;
15792
+ }
15793
+ case "get-error": {
15794
+ const id = positional[0];
15795
+ if (!id)
15796
+ throw new Error("get-error requires <errorId>");
15797
+ await getError(id);
14124
15798
  break;
14125
15799
  }
14126
15800
  case "get-error-metrics": {
14127
15801
  await getErrorMetrics();
14128
15802
  break;
14129
15803
  }
14130
- case "list-integration-operations": {
14131
- await listIntegrationOperations({
15804
+ case "list-integration-operations":
15805
+ case "count-integration-operations": {
15806
+ rejectRetiredPageFlags(flags);
15807
+ const filters = {
14132
15808
  destination: parseCommaSeparatedIds(getFlag(flags, "destination")),
14133
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page") ?? 1,
14134
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage") ?? 25,
14135
15809
  search: getFlag(flags, "search"),
14136
15810
  status: parseCommaSeparatedIntegrationOperationStatuses(getFlag(flags, "status"))
15811
+ };
15812
+ if (cmd === "count-integration-operations") {
15813
+ for (const key of ["limit", "cursor", "direction"])
15814
+ if (flags[key] !== undefined)
15815
+ throw new Error(`count-integration-operations does not accept --${key}; it takes filter flags only.`);
15816
+ await countIntegrationOperations(filters);
15817
+ break;
15818
+ }
15819
+ for (const key of ["limit", "cursor", "direction"])
15820
+ rejectBareFlag(flags, key);
15821
+ const cursor = getFlag(flags, "cursor");
15822
+ if (cursor === "" || (cursor?.length ?? 0) > 8192)
15823
+ throw new Error("--cursor must be a non-empty opaque token of at most 8192 characters.");
15824
+ const direction = parseProjectDirection(getFlag(flags, "direction"));
15825
+ if (direction === "backward" && !cursor)
15826
+ throw new Error("--direction backward requires --cursor <token>.");
15827
+ const limitRaw = getFlag(flags, "limit");
15828
+ await listIntegrationOperations({
15829
+ ...filters,
15830
+ limit: limitRaw === undefined ? 25 : parseProjectLimit(limitRaw),
15831
+ cursor,
15832
+ direction
14137
15833
  });
14138
15834
  break;
14139
15835
  }
15836
+ case "get-integration-operation": {
15837
+ const operationId = positional[0];
15838
+ if (!operationId) {
15839
+ throw new Error("get-integration-operation requires <operationId>");
15840
+ }
15841
+ await getIntegrationOperation(operationId);
15842
+ break;
15843
+ }
14140
15844
  case "retry-integration-operation": {
14141
15845
  const operationId = positional[0];
14142
15846
  if (!operationId) {
@@ -14149,13 +15853,153 @@ Waiting for approval\u2026
14149
15853
  await retryIntegrationOperation(operationId, parseOptionalBoolFlag(flags, "confirmExternalStateReconciled") ?? false, outboundEmailResolution, getFlag(flags, "providerMessageId"));
14150
15854
  break;
14151
15855
  }
14152
- case "list-inbound-sources": {
14153
- await listInboundSources({
14154
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page") ?? 1,
14155
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage") ?? 25
15856
+ case "list-webhook-event-types": {
15857
+ await listWebhookEventTypes();
15858
+ break;
15859
+ }
15860
+ case "list-webhook-endpoints": {
15861
+ await listWebhookEndpoints();
15862
+ break;
15863
+ }
15864
+ case "get-webhook-endpoint": {
15865
+ const id = positional[0];
15866
+ if (!id)
15867
+ throw new Error("get-webhook-endpoint requires <id>");
15868
+ await getWebhookEndpoint(id);
15869
+ break;
15870
+ }
15871
+ case "create-webhook-endpoint": {
15872
+ const name = getFlag(flags, "name");
15873
+ const url = getFlag(flags, "url");
15874
+ const events = parseCommaSeparatedIds(getFlag(flags, "events"));
15875
+ if (!name || !url || !events?.length)
15876
+ throw new Error("create-webhook-endpoint requires --name, --url and --events");
15877
+ await createWebhookEndpoint({
15878
+ name,
15879
+ url,
15880
+ eventPatterns: events,
15881
+ enabled: parseOptionalBoolFlag(flags, "enabled") ?? true
15882
+ });
15883
+ break;
15884
+ }
15885
+ case "update-webhook-endpoint": {
15886
+ const id = positional[0];
15887
+ if (!id)
15888
+ throw new Error("update-webhook-endpoint requires <id>");
15889
+ const events = parseCommaSeparatedIds(getFlag(flags, "events"));
15890
+ await updateWebhookEndpoint({
15891
+ id,
15892
+ name: getFlag(flags, "name"),
15893
+ url: getFlag(flags, "url"),
15894
+ eventPatterns: events?.length ? events : undefined,
15895
+ enabled: parseOptionalBoolFlag(flags, "enabled")
15896
+ });
15897
+ break;
15898
+ }
15899
+ case "delete-webhook-endpoint": {
15900
+ const id = positional[0];
15901
+ if (!id)
15902
+ throw new Error("delete-webhook-endpoint requires <id>");
15903
+ await deleteWebhookEndpoint(id);
15904
+ break;
15905
+ }
15906
+ case "rotate-webhook-secret": {
15907
+ const id = positional[0];
15908
+ if (!id)
15909
+ throw new Error("rotate-webhook-secret requires <id>");
15910
+ const ttl = getFlag(flags, "previousSecretTtlHours");
15911
+ await rotateWebhookSecret({
15912
+ id,
15913
+ previousSecretTtlHours: ttl === undefined ? undefined : Number.parseInt(ttl, 10)
15914
+ });
15915
+ break;
15916
+ }
15917
+ case "send-webhook-test-event": {
15918
+ const id = positional[0];
15919
+ if (!id)
15920
+ throw new Error("send-webhook-test-event requires <id>");
15921
+ await sendWebhookTestEvent(id);
15922
+ break;
15923
+ }
15924
+ case "list-webhook-deliveries": {
15925
+ const status = getFlag(flags, "status");
15926
+ if (status && status !== "PENDING" && status !== "SUCCEEDED" && status !== "FAILED")
15927
+ throw new Error("--status must be PENDING, SUCCEEDED or FAILED");
15928
+ await listWebhookDeliveries({
15929
+ endpointId: getFlag(flags, "endpointId"),
15930
+ status,
15931
+ eventType: getFlag(flags, "eventType"),
15932
+ cursor: getFlag(flags, "cursor"),
15933
+ limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit")
14156
15934
  });
14157
15935
  break;
14158
15936
  }
15937
+ case "get-webhook-delivery": {
15938
+ const id = positional[0];
15939
+ if (!id)
15940
+ throw new Error("get-webhook-delivery requires <id>");
15941
+ await getWebhookDelivery(id);
15942
+ break;
15943
+ }
15944
+ case "replay-webhook-delivery": {
15945
+ const id = positional[0];
15946
+ if (!id)
15947
+ throw new Error("replay-webhook-delivery requires <id>");
15948
+ await replayWebhookDelivery(id);
15949
+ break;
15950
+ }
15951
+ case "reconcile-resource-operation": {
15952
+ const operationId = positional[0];
15953
+ if (!operationId) {
15954
+ throw new Error("reconcile-resource-operation requires <operationId>");
15955
+ }
15956
+ await reconcileResourceOperation(operationId);
15957
+ break;
15958
+ }
15959
+ case "list-inbound-sources":
15960
+ case "count-inbound-sources": {
15961
+ if (flags.page !== undefined || flags.perPage !== undefined)
15962
+ throw new Error("Inbound sources now use --limit/--cursor/--direction; --page/--perPage are no longer supported.");
15963
+ for (const key of ["search", "processingMode", "cursor", "direction"]) {
15964
+ if (flags[key] === true)
15965
+ throw new Error(`--${key} requires a value.`);
15966
+ }
15967
+ const enabled = flags.processingEnabled;
15968
+ if (enabled !== undefined && enabled !== "true" && enabled !== "false")
15969
+ throw new Error("--processingEnabled must be true or false.");
15970
+ const mode = getFlag(flags, "processingMode");
15971
+ const processingMode = Object.values(InboundProcessingMode).find((value) => value === mode);
15972
+ if (mode !== undefined && processingMode === undefined)
15973
+ throw new Error("Invalid --processingMode. Use ACTIVE, COLLECT_ONLY or SHADOW.");
15974
+ const filters = {
15975
+ search: getFlag(flags, "search"),
15976
+ processingEnabled: enabled === undefined ? undefined : enabled === "true",
15977
+ processingMode
15978
+ };
15979
+ if (cmd === "count-inbound-sources") {
15980
+ if (["limit", "cursor", "direction"].some((key) => flags[key] !== undefined))
15981
+ throw new Error("Source counts accept filters only, not pagination flags.");
15982
+ await countInboundSources(filters);
15983
+ } else {
15984
+ const direction = getFlag(flags, "direction") ?? "forward";
15985
+ if (direction !== "forward" && direction !== "backward")
15986
+ throw new Error("--direction must be forward or backward.");
15987
+ const cursor = getFlag(flags, "cursor");
15988
+ if (direction === "backward" && !cursor)
15989
+ throw new Error("Backward source pagination requires --cursor.");
15990
+ const rawLimit = flags.limit;
15991
+ const limit = rawLimit === undefined ? 25 : Number(rawLimit);
15992
+ if (rawLimit === true || !Number.isInteger(limit) || limit < 1 || limit > 100)
15993
+ throw new Error("--limit must be an integer between 1 and 100.");
15994
+ await listInboundSources({
15995
+ ...filters,
15996
+ cursor,
15997
+ direction,
15998
+ limit
15999
+ });
16000
+ }
16001
+ break;
16002
+ }
14159
16003
  case "create-inbound-source": {
14160
16004
  const name = getFlag(flags, "name");
14161
16005
  const externalSourceId = getFlag(flags, "formId");
@@ -14182,13 +16026,20 @@ Waiting for approval\u2026
14182
16026
  break;
14183
16027
  }
14184
16028
  case "list-inbound-submissions": {
16029
+ if (getFlag(flags, "page") !== undefined || getFlag(flags, "perPage") !== undefined) {
16030
+ throw new Error("Inbound submissions now use --limit and --cursor; --page/--perPage are no longer supported.");
16031
+ }
14185
16032
  await listInboundSubmissions({
14186
- page: parsePositiveIntFlag(getFlag(flags, "page"), "--page") ?? 1,
14187
- perPage: parsePositiveIntFlag(getFlag(flags, "perPage"), "--perPage") ?? 25,
16033
+ cursor: getFlag(flags, "cursor"),
16034
+ limit: parsePositiveIntFlag(getFlag(flags, "limit"), "--limit") ?? 25,
14188
16035
  sourceId: getFlag(flags, "sourceId")
14189
16036
  });
14190
16037
  break;
14191
16038
  }
16039
+ case "count-inbound-submissions": {
16040
+ await countInboundSubmissions({ sourceId: getFlag(flags, "sourceId") });
16041
+ break;
16042
+ }
14192
16043
  case "get-inbound-submission": {
14193
16044
  const submissionId = positional[0];
14194
16045
  if (!submissionId) {