@wayai/cli 0.3.136 → 0.3.137

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
@@ -236,7 +236,7 @@ var init_mask_secrets = __esm({
236
236
  "use strict";
237
237
  CREDENTIAL_KEY_RE = /token|secret|credential|password|passwd|api[_-]?key|authorization|private[_-]?key|session[_-]?id|cookie|jwt/i;
238
238
  NUMERIC_CREDENTIAL_KEY_RE = /(?:password|passwd|pin|token|secret|credential|api[_-]?key|authorization|private[_-]?key|session[_-]?id|cookie|jwt)$/i;
239
- FULLY_MASKED_VALUE_RE = /^(?:(?:Bearer|Basic)\s+)?(?:way_|wst_)?\[REDACTED(?:_JWT|_BASE64)?\]$/;
239
+ FULLY_MASKED_VALUE_RE = /^(?:(?:Bearer|Basic)\s+)?(?:way_|wst_|rec_)?\[REDACTED(?:_JWT|_BASE64)?\]$/;
240
240
  isCredentialKey = (key) => CREDENTIAL_KEY_RE.test(key);
241
241
  isBase64UrlChar = (char) => {
242
242
  const code = char.charCodeAt(0);
@@ -249,6 +249,12 @@ var init_mask_secrets = __esm({
249
249
  [/\bway_[A-Za-z0-9_-]{8,}/g, "way_[REDACTED]"],
250
250
  // wst_ single-use WebSocket tickets (same opaque base64url format).
251
251
  [/\bwst_[A-Za-z0-9_-]{8,}/g, "wst_[REDACTED]"],
252
+ // rec_ Data-surface API tokens. Same opaque base64url shape, and needed for
253
+ // the same reason as way_: the credential-KEY rules below only fire when the
254
+ // token sits behind a recognisable key, so a `rec_` token that appears bare in
255
+ // an error body's prose would otherwise pass through unmasked. Unpadded
256
+ // base64url, so the trailing base64 rule does not cover it either.
257
+ [/\brec_[A-Za-z0-9_-]{8,}/g, "rec_[REDACTED]"],
252
258
  // Stripe API keys and webhook signing secrets. Keep this aligned with
253
259
  // packages/core/src/redact.ts; both modules protect observability sinks.
254
260
  [
@@ -15052,7 +15058,7 @@ var init_registry = __esm({
15052
15058
  disposition: "namespaced",
15053
15059
  surviving: "bases tokens",
15054
15060
  clause: 3,
15055
- shipped: false,
15061
+ shipped: true,
15056
15062
  reason: "Mints base tokens; WayAI's own token surface mints platform tokens."
15057
15063
  },
15058
15064
  {
@@ -15060,7 +15066,7 @@ var init_registry = __esm({
15060
15066
  disposition: "namespaced",
15061
15067
  surviving: "bases secrets",
15062
15068
  clause: 3,
15063
- shipped: false,
15069
+ shipped: true,
15064
15070
  reason: "Base org vault; WayAI has connection credentials."
15065
15071
  },
15066
15072
  {
@@ -15068,7 +15074,7 @@ var init_registry = __esm({
15068
15074
  disposition: "namespaced",
15069
15075
  surviving: "bases sql",
15070
15076
  clause: 3,
15071
- shipped: false,
15077
+ shipped: true,
15072
15078
  reason: "WayAI already has tenant SQL over conversation analytics."
15073
15079
  },
15074
15080
  {
@@ -15076,7 +15082,7 @@ var init_registry = __esm({
15076
15082
  disposition: "namespaced",
15077
15083
  surviving: "bases import",
15078
15084
  clause: 3,
15079
- shipped: false,
15085
+ shipped: true,
15080
15086
  reason: "WayAI has outbound-contact import."
15081
15087
  },
15082
15088
  {
@@ -15084,7 +15090,7 @@ var init_registry = __esm({
15084
15090
  disposition: "namespaced",
15085
15091
  surviving: "bases batch",
15086
15092
  clause: 3,
15087
- shipped: false,
15093
+ shipped: true,
15088
15094
  reason: "Too generic to stand alone."
15089
15095
  },
15090
15096
  {
@@ -15092,7 +15098,7 @@ var init_registry = __esm({
15092
15098
  disposition: "namespaced",
15093
15099
  surviving: "bases providers",
15094
15100
  clause: 3,
15095
- shipped: false,
15101
+ shipped: true,
15096
15102
  reason: "WayAI's provider is a connector."
15097
15103
  },
15098
15104
  {
@@ -15101,14 +15107,14 @@ var init_registry = __esm({
15101
15107
  surviving: "admin bases",
15102
15108
  clause: 3,
15103
15109
  shipped: false,
15104
- reason: "Clause 3's group exception: `wayai admin` is an existing group with its own gating and subgroups, so the operator surface joins it as a peer rather than sitting under `bases`."
15110
+ reason: "Clause 3's group exception: `wayai admin` is an existing group with its own gating and subgroups, so the operator surface joins it as a peer rather than sitting under `bases`. UNSHIPPED pending a transport: the Data backend's operator routes live at `/admin/*`, a SIBLING of `/v1/*`, and the merged CLI reaches Data only through WayAI's `/api/data` proxy, which contains every forwarded path under `/v1` by construction (`rekor/upstream-url.ts`). The standalone CLI could call them because it held a second `api_url` pointed straight at the Data backend \u2014 the thing the merge deliberately removed. Shipping needs a proxy channel for operator routes plus platform-admin gating on it, which is a backend change, not a CLI one."
15105
15111
  },
15106
15112
  {
15107
15113
  original: "report",
15108
15114
  disposition: "namespaced",
15109
15115
  surviving: "bases report",
15110
15116
  clause: 3,
15111
- shipped: false,
15117
+ shipped: true,
15112
15118
  reason: "Two report queues still exist, so the merged CLI must be able to file to either. The two collapse when the queues do."
15113
15119
  },
15114
15120
  // --- Neither: one verb each, routing by workspace subtree. ---
@@ -16868,15 +16874,15 @@ function scanResourceFiles(dir, prefix = "") {
16868
16874
  files.push(...scanResourceFiles(path12.join(dir, entry.name), relPath));
16869
16875
  } else if (entry.isFile()) {
16870
16876
  const fullPath = path12.join(dir, entry.name);
16871
- const stat = fs10.statSync(fullPath);
16872
- if (stat.size > MAX_RESOURCE_FILE_SIZE3) {
16873
- console.warn(` Warning: skipping ${relPath} (${(stat.size / 1024 / 1024).toFixed(1)}MB exceeds 10MB limit)`);
16877
+ const stat2 = fs10.statSync(fullPath);
16878
+ if (stat2.size > MAX_RESOURCE_FILE_SIZE3) {
16879
+ console.warn(` Warning: skipping ${relPath} (${(stat2.size / 1024 / 1024).toFixed(1)}MB exceeds 10MB limit)`);
16874
16880
  continue;
16875
16881
  }
16876
16882
  const fileEntry = {
16877
16883
  path: relPath,
16878
16884
  mime_type: guessMimeType(entry.name),
16879
- file_size: stat.size
16885
+ file_size: stat2.size
16880
16886
  };
16881
16887
  const data = fs10.readFileSync(fullPath);
16882
16888
  fileEntry.hash = computeHash(data);
@@ -17068,34 +17074,34 @@ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
17068
17074
  if (abs !== root && !abs.startsWith(root + path13.sep)) {
17069
17075
  throw expected(`${label}: attachment "${entry}" escapes the hub folder.`);
17070
17076
  }
17071
- let stat;
17077
+ let stat2;
17072
17078
  try {
17073
- stat = fs11.statSync(abs);
17079
+ stat2 = fs11.statSync(abs);
17074
17080
  } catch {
17075
17081
  }
17076
- if (!stat?.isFile()) {
17082
+ if (!stat2?.isFile()) {
17077
17083
  throw expected(`${label}: attachment "${entry}" not found.`);
17078
17084
  }
17079
17085
  if (!realpathContained(root, abs)) {
17080
17086
  throw expected(`${label}: attachment "${entry}" resolves outside the hub folder (symlinks are not allowed).`);
17081
17087
  }
17082
- if (stat.size > MAX_RESOURCE_FILE_SIZE3) {
17088
+ if (stat2.size > MAX_RESOURCE_FILE_SIZE3) {
17083
17089
  throw expected(
17084
- `${label}: attachment "${entry}" is ${(stat.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
17090
+ `${label}: attachment "${entry}" is ${(stat2.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
17085
17091
  );
17086
17092
  }
17087
17093
  const data = fs11.readFileSync(abs);
17088
17094
  const hash = computeHash(data);
17089
17095
  const fileName = path13.basename(abs);
17090
17096
  const mimeType = guessMimeType(fileName) ?? "application/octet-stream";
17091
- refs.push({ hash, file_name: fileName, mime_type: mimeType, file_size: stat.size });
17097
+ refs.push({ hash, file_name: fileName, mime_type: mimeType, file_size: stat2.size });
17092
17098
  if (!bytesByHash.has(hash)) {
17093
17099
  bytesByHash.set(hash, {
17094
17100
  hash,
17095
17101
  content_base64: data.toString("base64"),
17096
17102
  file_name: fileName,
17097
17103
  mime_type: mimeType,
17098
- file_size: stat.size
17104
+ file_size: stat2.size
17099
17105
  });
17100
17106
  }
17101
17107
  }
@@ -19396,18 +19402,18 @@ __export(send_message_exports, {
19396
19402
  import * as fs19 from "fs";
19397
19403
  import * as path25 from "path";
19398
19404
  function statAttachment(filePath) {
19399
- let stat;
19405
+ let stat2;
19400
19406
  try {
19401
- stat = fs19.statSync(filePath);
19407
+ stat2 = fs19.statSync(filePath);
19402
19408
  } catch {
19403
19409
  console.error(`Error: file not found: ${filePath}`);
19404
19410
  process.exit(1);
19405
19411
  }
19406
- if (stat.isDirectory()) {
19412
+ if (stat2.isDirectory()) {
19407
19413
  console.error(`Error: not a file: ${filePath}`);
19408
19414
  process.exit(1);
19409
19415
  }
19410
- return { filePath, size: stat.size };
19416
+ return { filePath, size: stat2.size };
19411
19417
  }
19412
19418
  function readAttachment(filePath, size) {
19413
19419
  const fileName = path25.basename(filePath);
@@ -20674,7 +20680,7 @@ async function stopEvalSessionWithRetries(stop, opts = {}) {
20674
20680
  const attempts = opts.attempts ?? STOP_ATTEMPTS;
20675
20681
  const attemptTimeoutMs = opts.attemptTimeoutMs ?? STOP_ATTEMPT_TIMEOUT_MS;
20676
20682
  const backoffMs = opts.backoffMs ?? STOP_BACKOFF_MS;
20677
- const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
20683
+ const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
20678
20684
  let lastError = new Error("Stop was never attempted");
20679
20685
  for (let attempt = 1; attempt <= attempts; attempt++) {
20680
20686
  let timeout;
@@ -20695,7 +20701,7 @@ async function stopEvalSessionWithRetries(stop, opts = {}) {
20695
20701
  if (timeout !== void 0) clearTimeout(timeout);
20696
20702
  }
20697
20703
  const delay2 = backoffMs[attempt - 1];
20698
- if (attempt < attempts && delay2) await sleep2(delay2);
20704
+ if (attempt < attempts && delay2) await sleep3(delay2);
20699
20705
  }
20700
20706
  throw lastError;
20701
20707
  }
@@ -24981,8 +24987,8 @@ async function createDataClient(orgId) {
24981
24987
  }
24982
24988
  const org = selected ?? readRepoConfig()?.organization_id;
24983
24989
  return {
24984
- async request(method, v1Path, body) {
24985
- const envelope = await api.dataRequest(method, v1Path, body, org);
24990
+ async request(method, path31, body) {
24991
+ const envelope = await api.dataRequest(method, path31, body, org);
24986
24992
  return envelope.data;
24987
24993
  },
24988
24994
  async collectPages(makePath) {
@@ -25011,6 +25017,23 @@ async function createDataClient(orgId) {
25011
25017
  }
25012
25018
  };
25013
25019
  }
25020
+ function dataErrorEnvelope(err) {
25021
+ if (!(err instanceof ApiError)) return null;
25022
+ try {
25023
+ const parsed = JSON.parse(err.body);
25024
+ return parsed?.error ?? null;
25025
+ } catch {
25026
+ return null;
25027
+ }
25028
+ }
25029
+ function dataErrorCode(err) {
25030
+ const code = dataErrorEnvelope(err)?.code;
25031
+ return typeof code === "string" && code ? code : null;
25032
+ }
25033
+ function dataErrorDetails(err) {
25034
+ const details = dataErrorEnvelope(err)?.details;
25035
+ return details && typeof details === "object" ? details : void 0;
25036
+ }
25014
25037
  var MAX_PAGES;
25015
25038
  var init_client = __esm({
25016
25039
  "src/data/client.ts"() {
@@ -25080,6 +25103,36 @@ function printFields(obj) {
25080
25103
  console.log(`${key.padEnd(width)} ${formatCell2(value)}`);
25081
25104
  }
25082
25105
  }
25106
+ function printBatchResult(response) {
25107
+ const obj = response && typeof response === "object" ? response : {};
25108
+ const results = Array.isArray(obj.results) ? obj.results : [];
25109
+ const batchId = typeof obj.batch_id === "string" ? obj.batch_id : "(none)";
25110
+ console.log(
25111
+ `batch_id: ${sanitizeTerminalText(batchId)} \xB7 ${results.length} operation${results.length === 1 ? "" : "s"}`
25112
+ );
25113
+ if (results.length === 0) return;
25114
+ printRows(
25115
+ results.map((r, i) => ({
25116
+ "#": String(i),
25117
+ operation: r?.type ?? "",
25118
+ result: summarizeBatchResult(r)
25119
+ }))
25120
+ );
25121
+ }
25122
+ function summarizeBatchResult(r) {
25123
+ if (!r || typeof r !== "object") return String(r ?? "");
25124
+ if (r.deleted === true) return "deleted";
25125
+ const entity = r.record ?? r.relationship ?? r.record_type;
25126
+ if (entity && typeof entity === "object") {
25127
+ const e = entity;
25128
+ const id = typeof e.id === "string" ? e.id : void 0;
25129
+ const label = typeof e.record_type === "string" ? e.record_type : typeof e.rel_type === "string" ? e.rel_type : typeof e.name === "string" ? e.name : void 0;
25130
+ const version = e.version != null ? ` v${String(e.version)}` : "";
25131
+ if (id && label) return `${label}/${id}${version}`;
25132
+ if (id) return `${id}${version}`;
25133
+ }
25134
+ return JSON.stringify(r);
25135
+ }
25083
25136
  function cellAt(row, column) {
25084
25137
  return formatCell2(row?.[column]);
25085
25138
  }
@@ -25126,11 +25179,23 @@ function foreignSegment(value, label = "id") {
25126
25179
  function pathSegments(value, label = "path") {
25127
25180
  return value.split("/").map((segment) => foreignSegment(segment, label)).join("/");
25128
25181
  }
25129
- function parseData(data) {
25130
- if (data.startsWith("@")) {
25131
- return JSON.parse(readFileSync19(data.slice(1), "utf-8"));
25182
+ function parseData(data, flag) {
25183
+ const prefix = flag ? `${flag}: ` : "";
25184
+ const source = data.startsWith("@") ? data.slice(1) : void 0;
25185
+ let text = data;
25186
+ if (source !== void 0) {
25187
+ try {
25188
+ text = readFileSync19(source, "utf-8");
25189
+ } catch (e) {
25190
+ throw expected(`${prefix}${source}: ${e instanceof Error ? e.message : "could not be read"}`);
25191
+ }
25192
+ }
25193
+ try {
25194
+ return JSON.parse(text);
25195
+ } catch (e) {
25196
+ const where = source !== void 0 ? ` in ${source}` : "";
25197
+ throw expected(`${prefix}invalid JSON${where}: ${e instanceof Error ? e.message : String(e)}`);
25132
25198
  }
25133
- return JSON.parse(data);
25134
25199
  }
25135
25200
  function toolsetMcpUrl(slug) {
25136
25201
  return `https://data-mcp.wayai.pro/t/${slug}/mcp`;
@@ -25140,8 +25205,17 @@ function globals(cmd) {
25140
25205
  while (namespace.parent?.parent) namespace = namespace.parent;
25141
25206
  return namespace.opts();
25142
25207
  }
25208
+ function withBaseOption(command2) {
25209
+ return command2.option("--base <id>", "Base id (or set WAYAI_BASE)");
25210
+ }
25211
+ function baseOptionHolder(cmd) {
25212
+ for (let c = cmd; c; c = c.parent ?? void 0) {
25213
+ if (c.options.some((o) => o.long === "--base")) return c;
25214
+ }
25215
+ return void 0;
25216
+ }
25143
25217
  function findBase(cmd) {
25144
- return globals(cmd).base;
25218
+ return baseOptionHolder(cmd)?.opts()?.base;
25145
25219
  }
25146
25220
  function requireBase(cmd) {
25147
25221
  const base = findBase(cmd) ?? process.env.WAYAI_BASE ?? "";
@@ -25176,12 +25250,35 @@ async function readSecret(source, label) {
25176
25250
  if (!value) throw expected(`${label} is required.`);
25177
25251
  return value;
25178
25252
  }
25253
+ function isInteractive() {
25254
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
25255
+ }
25256
+ function parseDuration(input, flag = "duration") {
25257
+ const match = DURATION_RE.exec(input.trim());
25258
+ const value = Number(match?.[1] ?? 0);
25259
+ const unit = match?.[2];
25260
+ const unitMs = unit ? DURATION_UNIT_MS[unit] : void 0;
25261
+ if (!unitMs || value <= 0) {
25262
+ throw expected(
25263
+ `Invalid ${flag} ${JSON.stringify(input)} \u2014 use <number><unit> with unit s/m/h/d (e.g. 10m, 2h, 30d).`
25264
+ );
25265
+ }
25266
+ return value * unitMs;
25267
+ }
25268
+ var DURATION_RE, DURATION_UNIT_MS;
25179
25269
  var init_helpers = __esm({
25180
25270
  "src/data/helpers.ts"() {
25181
25271
  "use strict";
25182
25272
  init_base_id();
25183
25273
  init_expected();
25184
25274
  init_utils();
25275
+ DURATION_RE = /^(\d+)([smhd])$/;
25276
+ DURATION_UNIT_MS = {
25277
+ s: 1e3,
25278
+ m: 6e4,
25279
+ h: 36e5,
25280
+ d: 864e5
25281
+ };
25185
25282
  }
25186
25283
  });
25187
25284
 
@@ -25363,6 +25460,1031 @@ var init_attachments = __esm({
25363
25460
  }
25364
25461
  });
25365
25462
 
25463
+ // src/data/commands/batch.ts
25464
+ import { Command as Command3 } from "commander";
25465
+ function buildBasesBatchCommand() {
25466
+ return withBaseOption(new Command3("batch")).description("Execute atomic batch operations (up to 1,000 operations)").requiredOption("--operations <json>", "Operations array (inline JSON or @filename)").action(async function(opts) {
25467
+ const base = pathSegment(requireBase(this), "--base");
25468
+ const operations = parseData(opts.operations, "--operations");
25469
+ const client = await createDataClient();
25470
+ const data = await client.request("POST", `/v1/${base}/batch`, {
25471
+ operations: Array.isArray(operations) ? operations : [operations]
25472
+ });
25473
+ const format = outputFormat(this);
25474
+ if (format === "json") {
25475
+ printOutput(data, format);
25476
+ return;
25477
+ }
25478
+ printBatchResult(data);
25479
+ });
25480
+ }
25481
+ var init_batch = __esm({
25482
+ "src/data/commands/batch.ts"() {
25483
+ "use strict";
25484
+ init_client();
25485
+ init_output();
25486
+ init_helpers();
25487
+ }
25488
+ });
25489
+
25490
+ // src/data/commands/import.ts
25491
+ import { createReadStream } from "fs";
25492
+ import { access, constants, stat } from "fs/promises";
25493
+ import { createInterface as createInterface3 } from "readline";
25494
+ import { randomUUID } from "crypto";
25495
+ import { Command as Command4 } from "commander";
25496
+ function isRetryable(err) {
25497
+ if (isNetworkError(err)) return true;
25498
+ if (!(err instanceof ApiError)) return false;
25499
+ const code = dataErrorCode(err);
25500
+ if (code && RETRYABLE_CODES.has(code)) return true;
25501
+ return err.status >= 500 && err.status !== 501;
25502
+ }
25503
+ function isExpiredSession(err) {
25504
+ if (!(err instanceof ApiError) || err.status !== 409) return false;
25505
+ if (dataErrorCode(err) !== "IMPORT_SESSION_CLOSED") return false;
25506
+ return dataErrorDetails(err)?.status === "expired";
25507
+ }
25508
+ async function withRetry(state, fn, what) {
25509
+ let lastErr;
25510
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
25511
+ try {
25512
+ return await fn();
25513
+ } catch (err) {
25514
+ lastErr = err;
25515
+ if (!isRetryable(err) || attempt === MAX_ATTEMPTS) throw err;
25516
+ const code = err instanceof ApiError ? dataErrorCode(err) : null;
25517
+ if (!code || !RETRYABLE_CODES.has(code)) state.sawAmbiguous = true;
25518
+ const delay2 = BASE_BACKOFF_MS * 2 ** (attempt - 1);
25519
+ console.error(
25520
+ ` ${what}: ${err.message} \u2014 retrying in ${delay2}ms (${attempt}/${MAX_ATTEMPTS - 1})`
25521
+ );
25522
+ await sleep2(delay2);
25523
+ }
25524
+ }
25525
+ throw lastErr;
25526
+ }
25527
+ async function* readChunks(file, size, startChunk) {
25528
+ const rl = createInterface3({ input: createReadStream(file, "utf8"), crlfDelay: Infinity });
25529
+ let rows = [];
25530
+ let index = 0;
25531
+ let lineNo = 0;
25532
+ let inChunk = 0;
25533
+ for await (const line of rl) {
25534
+ lineNo++;
25535
+ const trimmed = line.trim();
25536
+ if (!trimmed) continue;
25537
+ if (index < startChunk) {
25538
+ if (++inChunk === size) {
25539
+ inChunk = 0;
25540
+ index++;
25541
+ }
25542
+ continue;
25543
+ }
25544
+ let parsed;
25545
+ try {
25546
+ parsed = JSON.parse(trimmed);
25547
+ } catch {
25548
+ throw expected(`${file}:${lineNo}: not valid JSON. Each line must be one JSON object.`);
25549
+ }
25550
+ rows.push(parsed);
25551
+ if (rows.length === size) {
25552
+ yield { index, rows };
25553
+ rows = [];
25554
+ index++;
25555
+ }
25556
+ }
25557
+ if (rows.length > 0) yield { index, rows };
25558
+ }
25559
+ async function beginSession(state, client, base, recordType2, requestKey) {
25560
+ const res = await withRetry(
25561
+ state,
25562
+ () => client.request("POST", `/v1/${base}/import/begin`, {
25563
+ request_key: requestKey,
25564
+ record_type: recordType2
25565
+ }),
25566
+ "begin"
25567
+ );
25568
+ return res.session;
25569
+ }
25570
+ function buildBasesImportCommand() {
25571
+ const importCmd = withBaseOption(new Command4("import")).description(
25572
+ "Bulk historical import: load an NDJSON backfill, list sessions, or undo one"
25573
+ );
25574
+ importCmd.command("run <record_type>").description(
25575
+ "Load an NDJSON file into a record type (one JSON object per line, each {external_id, data})"
25576
+ ).requiredOption("--file <path>", "NDJSON file to import").option(
25577
+ "--chunk-size <n>",
25578
+ "Rows per request, max 1000. Must match the original run when resuming.",
25579
+ String(MAX_CHUNK_ROWS)
25580
+ ).option(
25581
+ "--request-key <key>",
25582
+ "Idempotency key for this logical run (default: generated). Re-use it to resume."
25583
+ ).option(
25584
+ "--start-chunk <n>",
25585
+ "Skip chunks before this index \u2014 resume a run that stopped partway",
25586
+ "0"
25587
+ ).option("--no-finalize", "Leave the session open (resume later with the same --request-key)").action(async function(recordType2, opts) {
25588
+ const base = pathSegment(requireBase(this), "--base");
25589
+ const state = { sawAmbiguous: false };
25590
+ const chunkSize = Number(opts.chunkSize);
25591
+ if (!Number.isInteger(chunkSize) || chunkSize < 1 || chunkSize > MAX_CHUNK_ROWS) {
25592
+ throw expected(`--chunk-size must be an integer between 1 and ${MAX_CHUNK_ROWS}.`);
25593
+ }
25594
+ const startChunk = Number(opts.startChunk);
25595
+ if (!Number.isInteger(startChunk) || startChunk < 0) {
25596
+ throw expected("--start-chunk must be a non-negative integer.");
25597
+ }
25598
+ if (startChunk > 0 && !this.getOptionValueSource("chunkSize")?.startsWith("cli")) {
25599
+ throw expected(
25600
+ "--start-chunk requires --chunk-size to be given explicitly, and it must match the original run."
25601
+ );
25602
+ }
25603
+ try {
25604
+ if ((await stat(opts.file)).isDirectory()) throw new Error("is a directory");
25605
+ await access(opts.file, constants.R_OK);
25606
+ } catch {
25607
+ throw expected(`--file ${opts.file}: not a readable file.`);
25608
+ }
25609
+ const client = await createDataClient();
25610
+ const requestKey = opts.requestKey ?? `cli-${randomUUID()}`;
25611
+ if (!opts.requestKey) {
25612
+ console.error(`request_key: ${requestKey} (pass --request-key to resume this run)`);
25613
+ }
25614
+ let session = await beginSession(state, client, base, recordType2, requestKey);
25615
+ console.error(`import_id: ${session.import_id}`);
25616
+ const importIds = [session.import_id];
25617
+ let written = 0;
25618
+ let skipped = 0;
25619
+ let sent = 0;
25620
+ let nextIndex = startChunk;
25621
+ try {
25622
+ for await (const { index, rows } of readChunks(opts.file, chunkSize, startChunk)) {
25623
+ nextIndex = index;
25624
+ const body = { import_id: session.import_id, chunk_index: index, rows };
25625
+ let result;
25626
+ try {
25627
+ result = await withRetry(
25628
+ state,
25629
+ () => client.request("POST", `/v1/${base}/import/chunk`, body),
25630
+ `chunk ${index}`
25631
+ );
25632
+ } catch (err) {
25633
+ if (!isExpiredSession(err)) throw err;
25634
+ console.error(
25635
+ ` chunk ${index}: session expired (month boundary) \u2014 starting a fresh session and re-sending.`
25636
+ );
25637
+ console.error(
25638
+ ` NOTE: this run now spans two import_ids; undo needs both. Previous: ${session.import_id}`
25639
+ );
25640
+ session = await beginSession(state, client, base, recordType2, requestKey);
25641
+ importIds.push(session.import_id);
25642
+ console.error(` import_id: ${session.import_id}`);
25643
+ result = await withRetry(
25644
+ state,
25645
+ () => client.request("POST", `/v1/${base}/import/chunk`, {
25646
+ ...body,
25647
+ import_id: session.import_id
25648
+ }),
25649
+ `chunk ${index}`
25650
+ );
25651
+ }
25652
+ written += result.rows_written ?? 0;
25653
+ skipped += result.rows_skipped ?? 0;
25654
+ sent++;
25655
+ nextIndex = index + 1;
25656
+ if (sent % 10 === 0) {
25657
+ console.error(` ${sent} chunks \u2014 ${written} written, ${skipped} unchanged`);
25658
+ }
25659
+ }
25660
+ if (opts.finalize) {
25661
+ await withRetry(
25662
+ state,
25663
+ () => client.request(
25664
+ "POST",
25665
+ `/v1/${base}/import/${pathSegment(session.import_id, "import id")}/finalize`
25666
+ ),
25667
+ "finalize"
25668
+ );
25669
+ }
25670
+ } catch (err) {
25671
+ console.error(
25672
+ `
25673
+ Stopped at chunk ${nextIndex}. To resume, re-run the SAME command with:
25674
+ --start-chunk ${nextIndex} --chunk-size ${chunkSize} --request-key ${requestKey}
25675
+ (keep every other flag you passed \u2014 the base and org decide where the rows land, and the chunk size decides which rows each index covers.)`
25676
+ );
25677
+ throw err;
25678
+ }
25679
+ printOutput(
25680
+ {
25681
+ import_ids: importIds,
25682
+ rows_written: written,
25683
+ rows_skipped: skipped,
25684
+ chunks_sent: sent,
25685
+ finalized: opts.finalize
25686
+ },
25687
+ outputFormat(this)
25688
+ );
25689
+ });
25690
+ importCmd.command("list").description("List import sessions, newest first \u2014 find a stuck run or the id to undo").option("--limit <n>", "Maximum sessions to return").option("--offset <n>", "Sessions to skip").action(async function(opts) {
25691
+ const base = pathSegment(requireBase(this), "--base");
25692
+ const qs = new URLSearchParams();
25693
+ if (opts.limit) qs.set("limit", opts.limit);
25694
+ if (opts.offset) qs.set("offset", opts.offset);
25695
+ const suffix = qs.toString() ? `?${qs}` : "";
25696
+ const client = await createDataClient();
25697
+ printOutput(
25698
+ await client.request("GET", `/v1/${base}/import/sessions${suffix}`),
25699
+ outputFormat(this)
25700
+ );
25701
+ });
25702
+ importCmd.command("rollback <import_id>").description(
25703
+ "Undo an import \u2014 deletes only the rows it CREATED. Re-importing corrected data is usually the better fix."
25704
+ ).option("--yes", "Skip the confirmation prompt").action(async function(importId, opts) {
25705
+ const base = pathSegment(requireBase(this), "--base");
25706
+ const format = outputFormat(this);
25707
+ const state = { sawAmbiguous: false };
25708
+ const client = await createDataClient();
25709
+ if (!opts.yes && !isInteractive()) {
25710
+ throw expected(
25711
+ "Refusing to roll back an import without confirmation \u2014 pass --yes to run non-interactively."
25712
+ );
25713
+ }
25714
+ if (!opts.yes) {
25715
+ const plan = await client.request("GET", `/v1/${base}/import/${pathSegment(importId, "import id")}/rollback-plan?counts=true`);
25716
+ const n = plan.matched_records ?? 0;
25717
+ if (n === 0) {
25718
+ console.error("No rows left to undo \u2014 closing the import session.");
25719
+ } else {
25720
+ const ok = await confirm(
25721
+ `Delete ${n} record${n === 1 ? "" : "s"} created by this import into "${sanitizeTerminalText(String(plan.record_type))}", plus every relationship pointing at them?`
25722
+ );
25723
+ if (!ok) {
25724
+ console.error("Aborted.");
25725
+ return;
25726
+ }
25727
+ }
25728
+ }
25729
+ let records = 0;
25730
+ let relationships = 0;
25731
+ let calls = 0;
25732
+ let incomplete = false;
25733
+ for (let sweep = 0; ; sweep++) {
25734
+ const res = await withRetry(
25735
+ state,
25736
+ () => client.request("POST", `/v1/${base}/import/${pathSegment(importId, "import id")}/rollback`),
25737
+ "rollback"
25738
+ );
25739
+ records += res.swept.records;
25740
+ relationships += res.swept.relationships;
25741
+ calls++;
25742
+ if (res.complete) break;
25743
+ const sweptNothing = res.swept.records === 0 && res.swept.relationships === 0;
25744
+ if (sweptNothing || sweep + 1 >= MAX_ROLLBACK_SWEEPS) {
25745
+ incomplete = true;
25746
+ console.error(
25747
+ sweptNothing ? " the sweep stopped making progress before the server reported it complete \u2014 re-run to continue." : ` stopped after ${MAX_ROLLBACK_SWEEPS} sweeps without completing \u2014 re-run to continue.`
25748
+ );
25749
+ break;
25750
+ }
25751
+ if (format !== "json") {
25752
+ console.error(` swept ${records} records, ${relationships} relationships so far\u2026`);
25753
+ }
25754
+ }
25755
+ if (state.sawAmbiguous && format !== "json") {
25756
+ console.error(
25757
+ "Note: a request was re-sent after its outcome was unknown, so the counts below are a lower bound \u2014 a page that committed before its response was lost is not included."
25758
+ );
25759
+ }
25760
+ printOutput(
25761
+ {
25762
+ import_id: importId,
25763
+ swept: { records, relationships },
25764
+ requests: calls,
25765
+ // The server's own completion claim, carried so a script can tell an
25766
+ // undo that FINISHED from one that stopped early and needs re-running.
25767
+ complete: !incomplete,
25768
+ ...state.sawAmbiguous ? { counts_are_lower_bound: true } : {}
25769
+ },
25770
+ format
25771
+ );
25772
+ if (incomplete) return process.exit(1);
25773
+ });
25774
+ return importCmd;
25775
+ }
25776
+ var MAX_CHUNK_ROWS, MAX_ATTEMPTS, MAX_ROLLBACK_SWEEPS, BASE_BACKOFF_MS, RETRYABLE_CODES, sleep2;
25777
+ var init_import = __esm({
25778
+ "src/data/commands/import.ts"() {
25779
+ "use strict";
25780
+ init_client();
25781
+ init_output();
25782
+ init_helpers();
25783
+ init_api_client();
25784
+ init_network_error();
25785
+ init_terminal_output();
25786
+ init_utils();
25787
+ init_expected();
25788
+ MAX_CHUNK_ROWS = 1e3;
25789
+ MAX_ATTEMPTS = 5;
25790
+ MAX_ROLLBACK_SWEEPS = 1e3;
25791
+ BASE_BACKOFF_MS = 500;
25792
+ RETRYABLE_CODES = /* @__PURE__ */ new Set(["BASE_OVERLOADED", "SERVICE_UNAVAILABLE", "RATE_LIMITED"]);
25793
+ sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
25794
+ }
25795
+ });
25796
+
25797
+ // src/data/commands/providers.ts
25798
+ import { Command as Command5 } from "commander";
25799
+ import { writeFileSync as writeFileSync15 } from "fs";
25800
+ function providerSegment(provider) {
25801
+ if (!VALID_PROVIDERS.includes(provider)) {
25802
+ throw expected(`Unknown provider ${JSON.stringify(provider)}. Expected one of: ${VALID_PROVIDERS_HELP}.`);
25803
+ }
25804
+ return pathSegment(provider, "provider");
25805
+ }
25806
+ function buildBasesProvidersCommand() {
25807
+ const providers = withBaseOption(new Command5("providers")).description(
25808
+ "Import/export tool definitions between LLM providers and record types"
25809
+ );
25810
+ providers.command("import <provider>").description(`Import tool definitions as record types. Providers: ${VALID_PROVIDERS_HELP}`).requiredOption("--tools <json>", "Tool definitions (inline JSON or @filename)").action(async function(provider, opts) {
25811
+ const base = pathSegment(requireBase(this), "--base");
25812
+ const tools = parseData(opts.tools, "--tools");
25813
+ const client = await createDataClient();
25814
+ printOutput(
25815
+ await client.request("POST", `/v1/${base}/providers/${providerSegment(provider)}/import`, {
25816
+ tools: Array.isArray(tools) ? tools : [tools]
25817
+ }),
25818
+ outputFormat(this)
25819
+ );
25820
+ });
25821
+ providers.command("export <provider>").description(`Export record types as tool definitions. Providers: ${VALID_PROVIDERS_HELP}`).option("--record-types <ids>", "Comma-separated record type ids (omit for all)").option("--to <file>", "Write the exported definitions to a file instead of stdout").action(async function(provider, opts) {
25822
+ const base = pathSegment(requireBase(this), "--base");
25823
+ const recordTypes = opts.recordTypes ? splitList(opts.recordTypes) : [];
25824
+ const query = recordTypes.length ? `?record_types=${encodeURIComponent(recordTypes.join(","))}` : "";
25825
+ const client = await createDataClient();
25826
+ const data = await client.request(
25827
+ "GET",
25828
+ `/v1/${base}/providers/${providerSegment(provider)}/export${query}`
25829
+ );
25830
+ if (opts.to) {
25831
+ try {
25832
+ writeFileSync15(opts.to, JSON.stringify(data, null, 2));
25833
+ } catch (e) {
25834
+ throw expected(`--to ${opts.to}: ${e instanceof Error ? e.message : "could not be written"}`);
25835
+ }
25836
+ printOutput({ written_to: opts.to }, outputFormat(this));
25837
+ return;
25838
+ }
25839
+ printOutput(data, outputFormat(this));
25840
+ });
25841
+ providers.command("import-call <provider> <record_type>").description(
25842
+ `Create a record from a tool call in provider format. Providers: ${VALID_PROVIDERS_HELP}`
25843
+ ).requiredOption("--data <json>", "Tool call data in provider format (inline JSON or @filename)").option("--external-id <id>", "External id for idempotent upsert").option("--external-source <source>", "External source identifier").action(async function(provider, recordType2, opts) {
25844
+ const base = pathSegment(requireBase(this), "--base");
25845
+ const callData = parseData(opts.data, "--data");
25846
+ const queryParts = [];
25847
+ if (opts.externalId) queryParts.push(`external_id=${encodeURIComponent(opts.externalId)}`);
25848
+ if (opts.externalSource) {
25849
+ queryParts.push(`external_source=${encodeURIComponent(opts.externalSource)}`);
25850
+ }
25851
+ const qs = queryParts.length ? `?${queryParts.join("&")}` : "";
25852
+ const client = await createDataClient();
25853
+ printOutput(
25854
+ await client.request(
25855
+ "POST",
25856
+ `/v1/${base}/providers/${providerSegment(provider)}/records/${pathSegment(recordType2, "record_type")}${qs}`,
25857
+ callData
25858
+ ),
25859
+ outputFormat(this)
25860
+ );
25861
+ });
25862
+ return providers;
25863
+ }
25864
+ var VALID_PROVIDERS, VALID_PROVIDERS_HELP;
25865
+ var init_providers = __esm({
25866
+ "src/data/commands/providers.ts"() {
25867
+ "use strict";
25868
+ init_client();
25869
+ init_output();
25870
+ init_helpers();
25871
+ init_expected();
25872
+ VALID_PROVIDERS = ["openai", "anthropic", "google", "mcp"];
25873
+ VALID_PROVIDERS_HELP = VALID_PROVIDERS.join(", ");
25874
+ }
25875
+ });
25876
+
25877
+ // src/data/commands/report.ts
25878
+ import { Command as Command6 } from "commander";
25879
+ import { readFileSync as readFileSync21 } from "fs";
25880
+ import { dirname as dirname9, join as join27 } from "path";
25881
+ import { fileURLToPath as fileURLToPath2 } from "url";
25882
+ function resolveCliVersion() {
25883
+ for (const candidate of [
25884
+ join27(here, "..", "package.json"),
25885
+ join27(here, "..", "..", "..", "package.json")
25886
+ ]) {
25887
+ try {
25888
+ const version = JSON.parse(readFileSync21(candidate, "utf-8")).version;
25889
+ if (typeof version === "string" && version) return version;
25890
+ } catch {
25891
+ }
25892
+ }
25893
+ return "unknown";
25894
+ }
25895
+ function resolveReportClientVersions(projectRoot, cliVersion) {
25896
+ const versions = { cli_version: cliVersion };
25897
+ try {
25898
+ const skillVersion = lowestInstalledVersion(findInstalledSkills(projectRoot));
25899
+ if (skillVersion) versions.skill_version = skillVersion;
25900
+ } catch {
25901
+ }
25902
+ return versions;
25903
+ }
25904
+ function reportActionError2(err) {
25905
+ if (err instanceof ApiError) {
25906
+ if (err.status === 404) {
25907
+ console.error("Report not found, or it was not filed by you.");
25908
+ } else if (err.status === 403) {
25909
+ console.error("This report is agent-authored and can only be handled by a platform admin.");
25910
+ } else if (err.status === 409) {
25911
+ console.error(err.message);
25912
+ } else if (err.status === 429) {
25913
+ console.error("Rate limit exceeded. Please try again later.");
25914
+ } else {
25915
+ console.error(`Request failed: ${err.message}`);
25916
+ }
25917
+ return process.exit(1);
25918
+ }
25919
+ throw err;
25920
+ }
25921
+ function buildBasesReportCommand() {
25922
+ const report = new Command6("report").description(
25923
+ "File a report to the Data triage queue and verify the outcome"
25924
+ );
25925
+ report.command("create").description("Submit a report to the Data triage queue (deduplicated)").requiredOption("--title <title>", "Short summary").requiredOption("--description <text>", "What happened").option("--source <source>", "Origin: cli_report (default) | review | security_audit").option("--classification <c>", "Nature: bug (default) | flaky_test | security | enhancement").option(
25926
+ "--dedup-key <key>",
25927
+ 'Stable dedup key (e.g. "<file>::<test>"); collapses repeat occurrences'
25928
+ ).option("--severity <level>", "low | medium | high | critical").option("--steps <text>", "Steps to reproduce").option("--error-message <text>", "Exact error text if any").option("--context <text>", "Additional context (logs, request ids)").option("--locale <code>", "Notification locale (en | pt | es)", "en").option("--reporter-email <addr>", "Override reporter email (defaults to session email)").option("--base-id <id>", "Base the error relates to (defaults to the configured base)").option("--record-type <name>", "Record type the error relates to").option("--record-id <id>", "Record id (UUID) the error relates to").option("--external-id <id>", "External id of the related record").option("--external-source <src>", "External source of the related record").option("--relationship-id <id>", "Relationship id the error relates to").action(async function(opts) {
25929
+ const body = {
25930
+ title: opts.title,
25931
+ description: opts.description,
25932
+ locale: opts.locale
25933
+ };
25934
+ if (opts.source) body.source = opts.source;
25935
+ if (opts.classification) body.classification = opts.classification;
25936
+ if (opts.dedupKey) body.dedup_key = opts.dedupKey;
25937
+ if (opts.severity) body.severity = opts.severity;
25938
+ if (opts.steps) body.steps = opts.steps;
25939
+ if (opts.errorMessage) body.error_message = opts.errorMessage;
25940
+ if (opts.context) body.context = opts.context;
25941
+ if (opts.reporterEmail) body.reporter_email = opts.reporterEmail;
25942
+ const baseId = opts.baseId ?? findBase(this) ?? process.env.WAYAI_BASE;
25943
+ if (baseId) body.base_id = baseId;
25944
+ if (opts.recordType) body.record_type = opts.recordType;
25945
+ if (opts.recordId) body.record_id = opts.recordId;
25946
+ if (opts.externalId) body.external_id = opts.externalId;
25947
+ if (opts.externalSource) body.external_source = opts.externalSource;
25948
+ if (opts.relationshipId) body.relationship_id = opts.relationshipId;
25949
+ const versions = resolveReportClientVersions(
25950
+ findGitRoot() ?? process.cwd(),
25951
+ resolveCliVersion()
25952
+ );
25953
+ body.cli_version = versions.cli_version;
25954
+ if (versions.skill_version) body.skill_version = versions.skill_version;
25955
+ const client = await createDataClient();
25956
+ const data = await client.request("POST", "/v1/reports", body);
25957
+ if (outputFormat(this) === "json") {
25958
+ printOutput(data, "json");
25959
+ return;
25960
+ }
25961
+ if (data.created) {
25962
+ console.log(`Report submitted (id: ${data.report_id}).`);
25963
+ console.log("Our team will review and follow up by email if you provided one.");
25964
+ } else if (data.recurrence?.reopened) {
25965
+ console.log(`Report reopened for another look (id: ${data.report_id}).`);
25966
+ } else if (data.recurrence) {
25967
+ console.log(
25968
+ `A matching report remains closed (id: ${data.report_id}); this occurrence was recorded.`
25969
+ );
25970
+ } else {
25971
+ console.log(
25972
+ `A matching report already exists (id: ${data.report_id}) \u2014 your submission was merged into it.`
25973
+ );
25974
+ }
25975
+ });
25976
+ report.command("list").description("List your reports, newest first").option("--status <s>", "Filter by status (e.g. shipped \u2014 awaiting your verification)").action(async function(opts) {
25977
+ const client = await createDataClient();
25978
+ const qs = opts.status ? `?status=${encodeURIComponent(opts.status)}` : "";
25979
+ try {
25980
+ const rows = await client.request("GET", `/v1/reports${qs}`);
25981
+ if (outputFormat(this) === "json") {
25982
+ printOutput(rows, "json");
25983
+ return;
25984
+ }
25985
+ if (rows.length === 0) {
25986
+ console.log("No reports found.");
25987
+ return;
25988
+ }
25989
+ for (const r of rows) {
25990
+ const title = sanitizeTerminalText(r.title?.replace(/\s+/g, " ").trim() || "(untitled)");
25991
+ console.log(
25992
+ `${r.report_id} ${r.status.padEnd(10)} ${(r.classification ?? "bug").padEnd(12)} ${title}`
25993
+ );
25994
+ }
25995
+ } catch (err) {
25996
+ reportActionError2(err);
25997
+ }
25998
+ });
25999
+ report.command("get <report_id>").description("Show one of your reports: status + the message thread").action(async function(reportId) {
26000
+ const client = await createDataClient();
26001
+ try {
26002
+ const data = await client.request("GET", `/v1/reports/${pathSegment(reportId, "report id")}`);
26003
+ if (outputFormat(this) === "json") {
26004
+ printOutput(data, "json");
26005
+ return;
26006
+ }
26007
+ const { report: row, messages } = data;
26008
+ console.log(`Report ${row.report_id}`);
26009
+ console.log(` title: ${sanitizeTerminalText(row.title ?? "(unavailable)")}`);
26010
+ console.log(
26011
+ ` description: ${sanitizeTerminalText(row.description ?? "(unavailable)")}`
26012
+ );
26013
+ console.log(` status: ${row.status}`);
26014
+ console.log(` classification: ${row.classification ?? "bug"}`);
26015
+ if (row.contest_count > 0) console.log(` contests: ${row.contest_count}`);
26016
+ if (row.dismissal_final) console.log(" dismissal: final (non-contestable)");
26017
+ if (messages.length > 0) {
26018
+ console.log("\n Thread:");
26019
+ for (const m of messages) {
26020
+ console.log(` [${m.author_role}] ${sanitizeTerminalText(m.body)}`);
26021
+ }
26022
+ }
26023
+ if (row.status === "shipped") {
26024
+ console.log(
26025
+ `
26026
+ This fix is shipped. Run \`wayai bases report accept ${row.report_id}\` if it works,`
26027
+ );
26028
+ console.log(
26029
+ ` or \`wayai bases report contest ${row.report_id} --reason "..."\` if it does not.`
26030
+ );
26031
+ }
26032
+ } catch (err) {
26033
+ reportActionError2(err);
26034
+ }
26035
+ });
26036
+ report.command("accept <report_id>").description("Accept a shipped fix (\u2192 addressed)").action(async function(reportId) {
26037
+ const client = await createDataClient();
26038
+ try {
26039
+ const data = await client.request(
26040
+ "POST",
26041
+ `/v1/reports/${pathSegment(reportId, "report id")}/accept`
26042
+ );
26043
+ if (outputFormat(this) === "json") {
26044
+ printOutput(data, "json");
26045
+ return;
26046
+ }
26047
+ console.log(`Accepted. Report ${data.report.report_id} \u2192 ${data.report.status}.`);
26048
+ } catch (err) {
26049
+ reportActionError2(err);
26050
+ }
26051
+ });
26052
+ report.command("contest <report_id>").description("Contest a shipped fix or a dismissal (\u2192 back to triage)").requiredOption("--reason <text>", "Why the fix/dismissal is wrong").action(async function(reportId, opts) {
26053
+ const client = await createDataClient();
26054
+ try {
26055
+ const data = await client.request(
26056
+ "POST",
26057
+ `/v1/reports/${pathSegment(reportId, "report id")}/contest`,
26058
+ { reason: opts.reason }
26059
+ );
26060
+ if (outputFormat(this) === "json") {
26061
+ printOutput(data, "json");
26062
+ return;
26063
+ }
26064
+ console.log(
26065
+ `Contested. Report ${data.report.report_id} \u2192 ${data.report.status} (back to triage).`
26066
+ );
26067
+ } catch (err) {
26068
+ reportActionError2(err);
26069
+ }
26070
+ });
26071
+ return report;
26072
+ }
26073
+ var here;
26074
+ var init_report2 = __esm({
26075
+ "src/data/commands/report.ts"() {
26076
+ "use strict";
26077
+ init_client();
26078
+ init_output();
26079
+ init_helpers();
26080
+ init_api_client();
26081
+ init_terminal_output();
26082
+ init_workspace();
26083
+ init_skill_version();
26084
+ here = dirname9(fileURLToPath2(import.meta.url));
26085
+ }
26086
+ });
26087
+
26088
+ // src/data/commands/secrets.ts
26089
+ import { Command as Command7 } from "commander";
26090
+ import { readFileSync as readFileSync22 } from "fs";
26091
+ function withValueSourceOptions(cmd, what) {
26092
+ return cmd.option(
26093
+ "--file <path>",
26094
+ `Read ${what} from a file and base64-encode it (certificates, keystores, service-account JSON)`
26095
+ ).option("--value-stdin", `Read ${what} from stdin (recommended for CI)`).option("--value-prompt", `Prompt for ${what}, masked`);
26096
+ }
26097
+ async function resolveValue(opts, label) {
26098
+ if (opts.file !== void 0) {
26099
+ if (opts.valueStdin || opts.valuePrompt) {
26100
+ throw expected("--file cannot be combined with --value-stdin or --value-prompt \u2014 pass one.");
26101
+ }
26102
+ try {
26103
+ return readFileSync22(opts.file).toString("base64");
26104
+ } catch (e) {
26105
+ throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
26106
+ }
26107
+ }
26108
+ return readSecret(
26109
+ {
26110
+ stdin: opts.valueStdin,
26111
+ prompt: opts.valuePrompt,
26112
+ stdinFlag: "--value-stdin",
26113
+ promptHint: "--value-prompt"
26114
+ },
26115
+ label
26116
+ );
26117
+ }
26118
+ function listAllSecrets(client, expiringDays) {
26119
+ return client.collectPages((cursor) => {
26120
+ const params = new URLSearchParams({ limit: String(SECRET_LIST_PAGE_LIMIT) });
26121
+ if (expiringDays !== void 0) params.set("expiring_within_days", expiringDays);
26122
+ if (cursor) params.set("cursor", cursor);
26123
+ return `/v1/vault/secrets?${params.toString()}`;
26124
+ });
26125
+ }
26126
+ function buildBasesSecretsCommand() {
26127
+ const secrets = new Command7("secrets").description("Manage organization vault secrets");
26128
+ withValueSourceOptions(
26129
+ secrets.command("create").description("Store a vault secret (value read from stdin, a masked prompt, or --file)").requiredOption("--name <name>", "Secret name").option("--content-type <mime>", "MIME type of the value (e.g. application/x-pkcs12)").option(
26130
+ "--expires-at <date>",
26131
+ "Expiry as ISO-8601 (e.g. 2027-01-01T00:00:00Z) \u2014 surfaced by `list --expiring`"
26132
+ ).option("--tags <tags>", "Comma-separated tags"),
26133
+ "the secret value"
26134
+ ).action(async function(opts) {
26135
+ const body = {
26136
+ name: opts.name,
26137
+ value: await resolveValue(opts, "Secret value")
26138
+ };
26139
+ if (opts.contentType) body.content_type = opts.contentType;
26140
+ if (opts.expiresAt) body.expires_at = opts.expiresAt;
26141
+ if (opts.tags) body.tags = splitList(opts.tags);
26142
+ const client = await createDataClient();
26143
+ printOutput(await client.request("POST", "/v1/vault/secrets", body), outputFormat(this));
26144
+ });
26145
+ secrets.command("list").description("List vault secrets (values masked)").option("--expiring", "Only secrets expiring within --days (default 30)").option("--days <n>", "Window for --expiring, in days", "30").action(async function(opts) {
26146
+ const client = await createDataClient();
26147
+ const secretRows = await listAllSecrets(client, opts.expiring ? opts.days : void 0);
26148
+ printOutput(secretRows, outputFormat(this));
26149
+ });
26150
+ secrets.command("get <id>").description("Get vault secret metadata (value masked)").action(async function(id) {
26151
+ const client = await createDataClient();
26152
+ printOutput(
26153
+ await client.request("GET", `/v1/vault/secrets/${pathSegment(id, "secret id")}`),
26154
+ outputFormat(this)
26155
+ );
26156
+ });
26157
+ withValueSourceOptions(
26158
+ secrets.command("rotate <id>").description("Rotate a secret by installing a new caller-supplied value").option("--expires-at <date>", "Set the rotated credential's new expiry (ISO-8601)"),
26159
+ "the new secret value"
26160
+ ).action(async function(id, opts) {
26161
+ const body = { value: await resolveValue(opts, "New secret value") };
26162
+ if (opts.expiresAt) body.expires_at = opts.expiresAt;
26163
+ const client = await createDataClient();
26164
+ printOutput(
26165
+ await client.request("POST", `/v1/vault/secrets/${pathSegment(id, "secret id")}/rotate`, body),
26166
+ outputFormat(this)
26167
+ );
26168
+ });
26169
+ secrets.command("delete <id>").description("Delete a vault secret").option("-y, --yes", "Skip confirmation prompt").action(async function(id, opts) {
26170
+ if (!opts.yes) {
26171
+ if (!isInteractive()) {
26172
+ throw expected("Refusing to delete without confirmation \u2014 pass --yes to delete non-interactively.");
26173
+ }
26174
+ if (!await confirm(`Delete vault secret ${id}? This cannot be undone.`)) {
26175
+ console.log("Aborted");
26176
+ return;
26177
+ }
26178
+ }
26179
+ const client = await createDataClient();
26180
+ await client.request("DELETE", `/v1/vault/secrets/${pathSegment(id, "secret id")}`);
26181
+ printOutput({ id, deleted: true }, outputFormat(this));
26182
+ });
26183
+ return secrets;
26184
+ }
26185
+ var SECRET_LIST_PAGE_LIMIT;
26186
+ var init_secrets = __esm({
26187
+ "src/data/commands/secrets.ts"() {
26188
+ "use strict";
26189
+ init_client();
26190
+ init_output();
26191
+ init_helpers();
26192
+ init_utils();
26193
+ init_expected();
26194
+ SECRET_LIST_PAGE_LIMIT = 250;
26195
+ }
26196
+ });
26197
+
26198
+ // src/data/commands/sql.ts
26199
+ import { Command as Command8 } from "commander";
26200
+ import { readFileSync as readFileSync23 } from "fs";
26201
+ function buildBasesSqlCommand() {
26202
+ return withBaseOption(new Command8("sql")).description("Execute a read-only SQL query against base data").argument("[query]", "SQL query (SELECT only)").option("--file <path>", "Read SQL from a file instead of the argument").option(
26203
+ "--param <kv...>",
26204
+ "Named parameters as key=value pairs (e.g. --param status=issued)"
26205
+ ).action(async function(queryArg, opts) {
26206
+ const base = pathSegment(requireBase(this), "--base");
26207
+ let query;
26208
+ if (opts.file) {
26209
+ try {
26210
+ query = readFileSync23(opts.file, "utf-8").trim();
26211
+ } catch (e) {
26212
+ throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
26213
+ }
26214
+ if (!query) throw expected(`--file ${opts.file} is empty \u2014 it must contain a SELECT query.`);
26215
+ } else if (queryArg) {
26216
+ query = queryArg;
26217
+ } else {
26218
+ throw expected("Provide a SQL query as an argument or via --file.");
26219
+ }
26220
+ const params = {};
26221
+ for (const kv of opts.param ?? []) {
26222
+ const eqIdx = kv.indexOf("=");
26223
+ if (eqIdx === -1) {
26224
+ throw expected(`Invalid --param ${JSON.stringify(kv)}, expected key=value.`);
26225
+ }
26226
+ params[kv.slice(0, eqIdx)] = kv.slice(eqIdx + 1);
26227
+ }
26228
+ const body = { query };
26229
+ if (Object.keys(params).length > 0) body.params = params;
26230
+ const client = await createDataClient();
26231
+ printOutput(await client.request("POST", `/v1/${base}/sql`, body), outputFormat(this));
26232
+ });
26233
+ }
26234
+ var init_sql = __esm({
26235
+ "src/data/commands/sql.ts"() {
26236
+ "use strict";
26237
+ init_client();
26238
+ init_output();
26239
+ init_helpers();
26240
+ init_expected();
26241
+ }
26242
+ });
26243
+
26244
+ // src/data/commands/tokens.ts
26245
+ import { Command as Command9 } from "commander";
26246
+ function addTokenLifecycleOptions(cmd) {
26247
+ return cmd.option("--expires-at <date>", "Expiration date (ISO 8601, e.g. 2026-06-01T00:00:00Z)").option(
26248
+ "--ttl <duration>",
26249
+ "Relative expiry, e.g. 10m, 2h, 7d (use for disposable probe/test tokens)"
26250
+ ).option(
26251
+ "--description <text>",
26252
+ 'What holds this token (e.g. "behind credential X"); shown in list and revoke warnings'
26253
+ ).addHelpText("after", TOKEN_OUTPUT_HELP);
26254
+ }
26255
+ function tokenCreationOutput(cmd, opts) {
26256
+ return opts.tokenOnly ? "token" : outputFormat(cmd);
26257
+ }
26258
+ function printCreatedToken(data, output) {
26259
+ if (output !== "token") {
26260
+ printOutput(data, output);
26261
+ return;
26262
+ }
26263
+ const token = typeof data === "object" && data !== null && "token" in data ? data.token : void 0;
26264
+ if (typeof token !== "string" || !RAW_TOKEN_PATTERN.test(token)) {
26265
+ throw new Error("Token creation response did not contain a valid rec_ token");
26266
+ }
26267
+ console.log(token);
26268
+ }
26269
+ function resolveExpiresAt(opts) {
26270
+ if (opts.ttl && opts.expiresAt) {
26271
+ throw expected("--ttl and --expires-at are mutually exclusive \u2014 pass one.");
26272
+ }
26273
+ if (opts.ttl) return new Date(Date.now() + parseDuration(opts.ttl, "--ttl")).toISOString();
26274
+ return opts.expiresAt;
26275
+ }
26276
+ function applyTokenLifecycleOpts(body, opts) {
26277
+ const expiresAt = resolveExpiresAt(opts);
26278
+ if (expiresAt) body.expires_at = expiresAt;
26279
+ if (opts.description) body.description = opts.description;
26280
+ }
26281
+ function listAllTokens(client) {
26282
+ return client.collectPages(
26283
+ (cursor) => `/v1/tokens?limit=${TOKEN_LIST_PAGE_LIMIT}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`
26284
+ );
26285
+ }
26286
+ function pruneRow(t, reason) {
26287
+ return {
26288
+ token_id: t.token_id,
26289
+ name: t.name,
26290
+ reason,
26291
+ last_used: t.last_used_at ?? "never"
26292
+ };
26293
+ }
26294
+ function buildBasesTokensCommand() {
26295
+ const tokens = new Command9("tokens").description("Manage API tokens");
26296
+ addTokenLifecycleOptions(
26297
+ tokens.command("create").description("Create a scoped API token").requiredOption("--name <name>", "Token name").requiredOption("--grants <json>", "Grant definitions as JSON array").option(
26298
+ "--token-only",
26299
+ "Print only the raw rec_ token for secure command substitution (shown once)"
26300
+ )
26301
+ ).action(async function(opts) {
26302
+ const grants = parseData(opts.grants, "--grants");
26303
+ const body = { name: opts.name, grants };
26304
+ applyTokenLifecycleOpts(body, opts);
26305
+ const client = await createDataClient();
26306
+ const data = await client.request("POST", "/v1/tokens", body);
26307
+ printCreatedToken(data, tokenCreationOutput(this, opts));
26308
+ });
26309
+ addTokenLifecycleOptions(
26310
+ // The only token verb that addresses a base, so it alone declares `--base`.
26311
+ // Putting it on the `tokens` group would advertise a flag that does nothing
26312
+ // on `list`, `revoke` and `prune` — the defect `withBaseOption` exists to
26313
+ // avoid.
26314
+ withBaseOption(tokens.command("create-for-toolset <slug>")).description(
26315
+ "Create a toolset-bound (consumer) token whose authorization IS the toolset tool surface \u2014 it can call exactly the tools that toolset exposes (those record types + operations, relationships, batch, and sql only if enabled) and nothing else. Binds to the configured --base."
26316
+ ).option("--name <name>", 'Token name (default: "<slug> consumer")').option(
26317
+ "--token-only",
26318
+ "Print only the raw rec_ token for secure command substitution (shown once)"
26319
+ )
26320
+ ).action(async function(slug, opts) {
26321
+ const base = requireBase(this);
26322
+ const body = {
26323
+ name: opts.name ?? `${slug} consumer`,
26324
+ toolset_binding: { base_id: base, toolset_id: slug }
26325
+ };
26326
+ applyTokenLifecycleOpts(body, opts);
26327
+ const client = await createDataClient();
26328
+ const data = await client.request("POST", "/v1/tokens", body);
26329
+ const output = tokenCreationOutput(this, opts);
26330
+ printCreatedToken(data, output);
26331
+ if (output === "table") {
26332
+ console.log(`
26333
+ MCP URL: ${toolsetMcpUrl(slug)}`);
26334
+ }
26335
+ });
26336
+ tokens.command("list").description("List API tokens (status, last_used_at, expires_at, description)").action(async function() {
26337
+ const client = await createDataClient();
26338
+ printOutput(await listAllTokens(client), outputFormat(this));
26339
+ });
26340
+ tokens.command("revoke <token_id>").description(
26341
+ "Revoke an API token (permanent). A recently-used or toolset-bound token requires --force."
26342
+ ).option("--force", "Revoke even if the token appears to be in live use").action(async function(tokenId, opts) {
26343
+ const client = await createDataClient();
26344
+ const id = pathSegment(tokenId, "token id");
26345
+ const path31 = (force) => `/v1/tokens/${id}${force ? "?force=true" : ""}`;
26346
+ const format = outputFormat(this);
26347
+ const revoked = (forced) => printOutput({ token_id: tokenId, revoked: true, forced }, format);
26348
+ if (opts.force) {
26349
+ await client.request("DELETE", path31(true));
26350
+ revoked(true);
26351
+ return;
26352
+ }
26353
+ try {
26354
+ await client.request("DELETE", path31(false));
26355
+ revoked(false);
26356
+ } catch (err) {
26357
+ if (!(err instanceof ApiError) || err.status !== 409 || dataErrorDetails(err)?.requires_force !== true) {
26358
+ throw err;
26359
+ }
26360
+ console.error(err.message);
26361
+ if (!isInteractive()) {
26362
+ console.error("\nRe-run with --force to revoke anyway.");
26363
+ return process.exit(1);
26364
+ }
26365
+ if (!await confirm("Revoke anyway?")) {
26366
+ console.error("Aborted.");
26367
+ return process.exit(1);
26368
+ }
26369
+ await client.request("DELETE", path31(true));
26370
+ revoked(true);
26371
+ }
26372
+ });
26373
+ tokens.command("prune").description(
26374
+ "Revoke stale tokens, selected by staleness only (expired, or unused past --unused-since) \u2014 never by name. Previews by default; revoking requires --yes or an interactive confirmation."
26375
+ ).option(
26376
+ "--unused-since <duration>",
26377
+ "Also prune tokens with no activity for this long (e.g. 30d). Skips toolset-bound and legacy usage-untracked tokens."
26378
+ ).option("--include-bound", "Allow pruning stale toolset-bound (consumer) tokens").option("-y, --yes", "Revoke without prompting (required when non-interactive)").action(async function(opts) {
26379
+ const staleMs = opts.unusedSince ? parseDuration(opts.unusedSince, "--unused-since") : void 0;
26380
+ const client = await createDataClient();
26381
+ const allTokens = await listAllTokens(client);
26382
+ const now = Date.now();
26383
+ const targets = [];
26384
+ const skipped = [];
26385
+ for (const t of allTokens) {
26386
+ const expired = t.status ? t.status === "expired" : !!t.expires_at && Date.parse(t.expires_at) <= now;
26387
+ if (expired) {
26388
+ targets.push(pruneRow(t, "expired"));
26389
+ continue;
26390
+ }
26391
+ if (staleMs === void 0) continue;
26392
+ const lastActivity = Date.parse(t.last_used_at ?? t.created_at);
26393
+ if (!(now - lastActivity > staleMs)) continue;
26394
+ if (t.usage_tracked !== true) {
26395
+ skipped.push(
26396
+ pruneRow(
26397
+ t,
26398
+ "usage untracked (legacy token) \u2014 revoke individually if truly unused"
26399
+ )
26400
+ );
26401
+ } else if (t.toolset_binding && !opts.includeBound) {
26402
+ skipped.push(
26403
+ pruneRow(
26404
+ t,
26405
+ `toolset-bound ('${t.toolset_binding.toolset_id}') \u2014 pass --include-bound to prune`
26406
+ )
26407
+ );
26408
+ } else {
26409
+ targets.push(pruneRow(t, `unused for over ${opts.unusedSince}`));
26410
+ }
26411
+ }
26412
+ const format = outputFormat(this);
26413
+ const json = format === "json";
26414
+ const note = (line) => json ? console.error(line) : console.log(line);
26415
+ const printPreview = () => {
26416
+ if (targets.length === 0) {
26417
+ note("Nothing to prune.");
26418
+ } else {
26419
+ note(`Would revoke ${targets.length} token(s):`);
26420
+ if (!json) printOutput(targets, format);
26421
+ }
26422
+ if (skipped.length === 0) return;
26423
+ note(`
26424
+ Skipped ${skipped.length} stale token(s):`);
26425
+ if (!json) printOutput(skipped, format);
26426
+ };
26427
+ printPreview();
26428
+ if (targets.length === 0) {
26429
+ if (json) printOutput({ revoked: 0, failed: 0, targets, skipped }, format);
26430
+ return;
26431
+ }
26432
+ if (!opts.yes) {
26433
+ if (!isInteractive()) {
26434
+ console.error("\nPreview only \u2014 re-run with --yes to revoke these tokens.");
26435
+ if (json) printOutput({ preview: true, revoked: 0, failed: 0, targets, skipped }, format);
26436
+ return process.exit(1);
26437
+ }
26438
+ if (!await confirm(`
26439
+ Revoke ${targets.length} token(s)?`)) {
26440
+ console.error("Aborted.");
26441
+ return process.exit(1);
26442
+ }
26443
+ }
26444
+ let revoked = 0;
26445
+ let failed = 0;
26446
+ for (const t of targets) {
26447
+ try {
26448
+ await client.request(
26449
+ "DELETE",
26450
+ `/v1/tokens/${pathSegment(t.token_id, "token id")}?force=true`
26451
+ );
26452
+ revoked++;
26453
+ } catch (err) {
26454
+ failed++;
26455
+ console.error(
26456
+ `Failed to revoke ${t.token_id} ('${sanitizeTerminalText(t.name)}'): ${err instanceof Error ? err.message : String(err)}`
26457
+ );
26458
+ }
26459
+ }
26460
+ if (json) printOutput({ revoked, failed, targets, skipped }, format);
26461
+ else console.log(`Revoked ${revoked} token(s)${failed > 0 ? `, ${failed} failed` : ""}.`);
26462
+ if (failed > 0) return process.exit(1);
26463
+ });
26464
+ return tokens;
26465
+ }
26466
+ var RAW_TOKEN_PATTERN, TOKEN_OUTPUT_HELP, TOKEN_LIST_PAGE_LIMIT;
26467
+ var init_tokens = __esm({
26468
+ "src/data/commands/tokens.ts"() {
26469
+ "use strict";
26470
+ init_client();
26471
+ init_output();
26472
+ init_helpers();
26473
+ init_api_client();
26474
+ init_terminal_output();
26475
+ init_utils();
26476
+ init_expected();
26477
+ RAW_TOKEN_PATTERN = /^rec_[A-Za-z0-9_-]{43}$/;
26478
+ TOKEN_OUTPUT_HELP = `
26479
+ Output:
26480
+ --json prints the unwrapped response object; the secret is at top-level .token, never .data.token.
26481
+ --token-only prints only a validated rec_ token and overrides table/JSON formatting.
26482
+ The raw token is shown once. Capture it securely and do not log it.
26483
+ `;
26484
+ TOKEN_LIST_PAGE_LIMIT = 250;
26485
+ }
26486
+ });
26487
+
25366
26488
  // src/lib/base-binding.ts
25367
26489
  var binding2, getBaseBindingPath, readBaseBinding, writeBaseBinding, clearBaseBinding, autoBindBaseIfUnbound, assertBaseMatchesBinding, assertNoBindingBlocksBaseCreation;
25368
26490
  var init_base_binding = __esm({
@@ -25389,7 +26511,7 @@ var init_base_binding = __esm({
25389
26511
  });
25390
26512
 
25391
26513
  // src/data/commands/bases.ts
25392
- import { Command as Command3 } from "commander";
26514
+ import { Command as Command10 } from "commander";
25393
26515
  import * as fs24 from "fs";
25394
26516
  import * as path30 from "path";
25395
26517
  import * as yaml9 from "js-yaml";
@@ -25405,7 +26527,7 @@ function parseEnum(flag, value, allowed) {
25405
26527
  return value;
25406
26528
  }
25407
26529
  function buildBasesCommand() {
25408
- const bases = new Command3("bases").description("Manage bases");
26530
+ const bases = new Command10("bases").description("Manage bases");
25409
26531
  bases.command("list").description("List all bases").option("--tag <tag>", "Filter by tag").action(async function(opts) {
25410
26532
  const client = await createDataClient();
25411
26533
  const qs = opts.tag ? `?tag=${encodeURIComponent(opts.tag)}` : "";
@@ -25612,6 +26734,13 @@ function buildBasesCommand() {
25612
26734
  previous ? `Worktree unbound from base (was: ${previous}).` : "No base binding to clear."
25613
26735
  );
25614
26736
  });
26737
+ bases.addCommand(buildBasesTokensCommand());
26738
+ bases.addCommand(buildBasesSecretsCommand());
26739
+ bases.addCommand(buildBasesSqlCommand());
26740
+ bases.addCommand(buildBasesImportCommand());
26741
+ bases.addCommand(buildBasesBatchCommand());
26742
+ bases.addCommand(buildBasesProvidersCommand());
26743
+ bases.addCommand(buildBasesReportCommand());
25615
26744
  return bases;
25616
26745
  }
25617
26746
  function resolveSelectorToBaseId(selector) {
@@ -25632,6 +26761,13 @@ var BASE_META_FILE, BASE_ID_FIELD;
25632
26761
  var init_bases = __esm({
25633
26762
  "src/data/commands/bases.ts"() {
25634
26763
  "use strict";
26764
+ init_batch();
26765
+ init_import();
26766
+ init_providers();
26767
+ init_report2();
26768
+ init_secrets();
26769
+ init_sql();
26770
+ init_tokens();
25635
26771
  init_client();
25636
26772
  init_output();
25637
26773
  init_helpers();
@@ -25648,9 +26784,9 @@ var init_bases = __esm({
25648
26784
  });
25649
26785
 
25650
26786
  // src/data/commands/file-types.ts
25651
- import { Command as Command4 } from "commander";
26787
+ import { Command as Command11 } from "commander";
25652
26788
  function buildFileTypesCommand() {
25653
- const fileTypes = new Command4("file-types").description(
26789
+ const fileTypes = new Command11("file-types").description(
25654
26790
  "Manage file types (Buckets) \u2014 storage policy + optional metadata schema for files"
25655
26791
  );
25656
26792
  fileTypes.command("list").description("List all file types in a base").action(async function() {
@@ -25706,8 +26842,8 @@ var init_file_types = __esm({
25706
26842
  });
25707
26843
 
25708
26844
  // src/data/commands/files.ts
25709
- import { Command as Command5 } from "commander";
25710
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync15 } from "fs";
26845
+ import { Command as Command12 } from "commander";
26846
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync16 } from "fs";
25711
26847
  import { basename as basename14 } from "path";
25712
26848
  function renderFileDiff(fileType, filePath, from, to, d) {
25713
26849
  console.log(sanitizeTerminalText(`${fileType}/${filePath}: v${from} \u2192 v${to}`));
@@ -25749,14 +26885,14 @@ function downloadTarget(remotePath, to) {
25749
26885
  return derived;
25750
26886
  }
25751
26887
  function buildFilesCommand() {
25752
- const files = new Command5("files").description(
26888
+ const files = new Command12("files").description(
25753
26889
  "Manage files (path-addressed, versioned content) within a file type"
25754
26890
  );
25755
26891
  files.command("put <file_type> <path>").description(
25756
26892
  "Upload a local file to a path (e.g. wayai files put reports q3/summary.pdf --file ./summary.pdf)"
25757
26893
  ).requiredOption("--file <local>", "Local file to upload").option("--content-type <type>", "MIME type", "application/octet-stream").action(async function(fileType, filePath, opts) {
25758
26894
  const base = pathSegment(requireBase(this), "--base");
25759
- const body = readFileSync22(opts.file);
26895
+ const body = readFileSync25(opts.file);
25760
26896
  const client = await createDataClient();
25761
26897
  printOutput(
25762
26898
  await client.upload(
@@ -25787,7 +26923,7 @@ function buildFilesCommand() {
25787
26923
  const { bytes } = await client.download(
25788
26924
  `/v1/${base}/files/${pathSegment(fileType, "file_type")}/${encoded}${versionQs ? `?${versionQs}` : ""}`
25789
26925
  );
25790
- writeFileSync15(out, bytes);
26926
+ writeFileSync16(out, bytes);
25791
26927
  console.log(`Downloaded to ${out}`);
25792
26928
  });
25793
26929
  files.command("history <file_type> <path>").description("List the content versions of a file (newest first)").option("--limit <n>", "Max versions to return").option("--offset <n>", "Pagination offset").action(async function(fileType, filePath, opts) {
@@ -25908,10 +27044,10 @@ var init_files = __esm({
25908
27044
  });
25909
27045
 
25910
27046
  // src/data/commands/inbound-webhooks.ts
25911
- import { randomUUID } from "crypto";
25912
- import { Command as Command6 } from "commander";
27047
+ import { randomUUID as randomUUID2 } from "crypto";
27048
+ import { Command as Command13 } from "commander";
25913
27049
  function buildInboundWebhooksCommand() {
25914
- const inboundWebhooks = new Command6("inbound-webhooks").description(
27050
+ const inboundWebhooks = new Command13("inbound-webhooks").description(
25915
27051
  "Manage inbound webhook endpoints"
25916
27052
  );
25917
27053
  inboundWebhooks.command("create").description("Create a new inbound webhook").requiredOption("--name <name>", "Inbound webhook name").option("--secret-stdin", "Read the HMAC shared secret from stdin (recommended for CI)").option("--id <id>", "Inbound webhook ID (auto-generated if omitted)").option("--record-type-scope <record_types>", "Comma-separated record_type scope").option(
@@ -25949,7 +27085,7 @@ function buildInboundWebhooksCommand() {
25949
27085
  if (opts.hydration) body.hydration = parseData(opts.hydration);
25950
27086
  const base = pathSegment(requireBase(this), "--base");
25951
27087
  const client = await createDataClient();
25952
- const inboundWebhookId = opts.id ?? randomUUID();
27088
+ const inboundWebhookId = opts.id ?? randomUUID2();
25953
27089
  printOutput(
25954
27090
  await client.request(
25955
27091
  "PUT",
@@ -26009,9 +27145,9 @@ var init_inbound_webhooks = __esm({
26009
27145
  });
26010
27146
 
26011
27147
  // src/data/commands/query-relationships.ts
26012
- import { Command as Command7 } from "commander";
27148
+ import { Command as Command14 } from "commander";
26013
27149
  function buildQueryRelationshipsCommand() {
26014
- return new Command7("query-relationships").description("Query related records").argument("<record_type>", "RecordType of the source record").argument("<id>", "Source record ID").option("--type <type>", "Filter by relationship type").option("--direction <dir>", "Direction: outgoing, incoming, or both", "both").option("--limit <n>", "Max results", "50").option("--offset <n>", "Skip results", "0").action(async function(recordType2, id, opts) {
27150
+ return new Command14("query-relationships").description("Query related records").argument("<record_type>", "RecordType of the source record").argument("<id>", "Source record ID").option("--type <type>", "Filter by relationship type").option("--direction <dir>", "Direction: outgoing, incoming, or both", "both").option("--limit <n>", "Max results", "50").option("--offset <n>", "Skip results", "0").action(async function(recordType2, id, opts) {
26015
27151
  const base = pathSegment(requireBase(this), "--base");
26016
27152
  const client = await createDataClient();
26017
27153
  const params = new URLSearchParams();
@@ -26044,9 +27180,9 @@ var init_query_relationships = __esm({
26044
27180
  });
26045
27181
 
26046
27182
  // src/data/commands/record-types.ts
26047
- import { Command as Command8 } from "commander";
27183
+ import { Command as Command15 } from "commander";
26048
27184
  function buildRecordTypesCommand() {
26049
- const recordTypes = new Command8("record-types").description("Manage record_types");
27185
+ const recordTypes = new Command15("record-types").description("Manage record_types");
26050
27186
  recordTypes.command("list").description("List all record_types in a base").action(async function() {
26051
27187
  const base = pathSegment(requireBase(this), "--base");
26052
27188
  const client = await createDataClient();
@@ -26104,12 +27240,12 @@ var init_record_types = __esm({
26104
27240
  });
26105
27241
 
26106
27242
  // src/data/commands/records.ts
26107
- import { Command as Command9 } from "commander";
27243
+ import { Command as Command16 } from "commander";
26108
27244
  function recordKey(id, externalSource) {
26109
27245
  return externalSource ? foreignSegment(id, "external id") : pathSegment(id, "record id");
26110
27246
  }
26111
27247
  function buildRecordsCommand() {
26112
- const records = new Command9("records").description("Manage records");
27248
+ const records = new Command16("records").description("Manage records");
26113
27249
  records.command("upsert <record_type>").description("Create or update a record").requiredOption("--data <json>", "Record data (inline JSON or @filename)").option("--id <id>", "Internal record ID (UUID) to update a known record").option("--external-id <id>", "External/agent-supplied ID for idempotent upsert").option("--external-source <source>", "Source system for external_id (e.g. stripe)").action(async function(recordType2, opts) {
26114
27250
  const base = pathSegment(requireBase(this), "--base");
26115
27251
  const client = await createDataClient();
@@ -26198,9 +27334,9 @@ var init_records = __esm({
26198
27334
  });
26199
27335
 
26200
27336
  // src/data/commands/relationship-types.ts
26201
- import { Command as Command10 } from "commander";
27337
+ import { Command as Command17 } from "commander";
26202
27338
  function buildRelationshipTypesCommand() {
26203
- const relationshipTypes = new Command10("relationship-types").description(
27339
+ const relationshipTypes = new Command17("relationship-types").description(
26204
27340
  "Manage relationship types (the schema for a relationship's metadata)"
26205
27341
  );
26206
27342
  relationshipTypes.command("list").description("List all relationship types in a base").action(async function() {
@@ -26277,7 +27413,7 @@ var init_relationship_types = __esm({
26277
27413
  });
26278
27414
 
26279
27415
  // src/data/commands/relationships.ts
26280
- import { Command as Command11 } from "commander";
27416
+ import { Command as Command18 } from "commander";
26281
27417
  function relationshipKey(id, relType) {
26282
27418
  return relType ? foreignSegment(id, "external id") : pathSegment(id, "relationship id");
26283
27419
  }
@@ -26313,7 +27449,7 @@ function byKeyQuery(relType, externalSource) {
26313
27449
  return q ? `?${q}` : "";
26314
27450
  }
26315
27451
  function buildRelationshipsCommand() {
26316
- const relationships = new Command11("relationships").description(
27452
+ const relationships = new Command18("relationships").description(
26317
27453
  "Manage relationships between records"
26318
27454
  );
26319
27455
  relationships.command("upsert").description("Create or update a relationship").option("--source <record_type/id>", "Source record by internal id").option(
@@ -26398,9 +27534,9 @@ var init_relationships = __esm({
26398
27534
  });
26399
27535
 
26400
27536
  // src/data/commands/seed.ts
26401
- import { Command as Command12 } from "commander";
27537
+ import { Command as Command19 } from "commander";
26402
27538
  function buildSeedCommand() {
26403
- const seed = new Command12("seed").description(
27539
+ const seed = new Command19("seed").description(
26404
27540
  "Apply, reset, or clear a seed fixture on a preview base (hermetic eval data)"
26405
27541
  );
26406
27542
  seed.command("apply <name>").description(
@@ -26492,7 +27628,7 @@ var init_seed = __esm({
26492
27628
  });
26493
27629
 
26494
27630
  // src/data/commands/toolsets.ts
26495
- import { Command as Command13 } from "commander";
27631
+ import { Command as Command20 } from "commander";
26496
27632
  function collect(value, previous) {
26497
27633
  return [...previous, value];
26498
27634
  }
@@ -26515,7 +27651,7 @@ function parseRelSpec(spec) {
26515
27651
  return { rel_type: key, operations: ops };
26516
27652
  }
26517
27653
  function buildToolsetsCommand() {
26518
- const toolsets = new Command13("toolsets").description("Manage MCP toolsets");
27654
+ const toolsets = new Command20("toolsets").description("Manage MCP toolsets");
26519
27655
  toolsets.command("upsert <slug>").description("Create or update an MCP toolset").option("--name <name>", "Toolset display name (required unless using --config)").option("--description <desc>", "Toolset description").option(
26520
27656
  "--action <ref>",
26521
27657
  "Reference an Action by id, optionally renamed: <action_id> or <action_id>=<surface_name> (repeatable). Create Actions first with `wayai actions upsert`.",
@@ -26606,10 +27742,10 @@ var init_toolsets = __esm({
26606
27742
  });
26607
27743
 
26608
27744
  // src/data/commands/triggers.ts
26609
- import { randomUUID as randomUUID2 } from "crypto";
26610
- import { Command as Command14 } from "commander";
27745
+ import { randomUUID as randomUUID3 } from "crypto";
27746
+ import { Command as Command21 } from "commander";
26611
27747
  function buildTriggersCommand() {
26612
- const triggers = new Command14("triggers").description(
27748
+ const triggers = new Command21("triggers").description(
26613
27749
  "Manage triggers (outbound webhooks, internal writes, external writes)"
26614
27750
  );
26615
27751
  triggers.command("create").description(
@@ -26652,7 +27788,7 @@ function buildTriggersCommand() {
26652
27788
  }
26653
27789
  const base = pathSegment(requireBase(this), "--base");
26654
27790
  const client = await createDataClient();
26655
- const triggerId = opts.id ?? randomUUID2();
27791
+ const triggerId = opts.id ?? randomUUID3();
26656
27792
  printOutput(
26657
27793
  await client.request("PUT", `/v1/${base}/triggers/${pathSegment(triggerId, "--id")}`, body),
26658
27794
  outputFormat(this)
@@ -26708,26 +27844,37 @@ var program_exports = {};
26708
27844
  __export(program_exports, {
26709
27845
  DATA_NAMESPACES: () => DATA_NAMESPACES,
26710
27846
  buildDataProgram: () => buildDataProgram,
27847
+ handleParserExit: () => handleParserExit,
26711
27848
  isDataNamespace: () => isDataNamespace,
27849
+ routeErrorsToCli: () => routeErrorsToCli,
26712
27850
  runDataCommand: () => runDataCommand,
26713
27851
  withBaseOption: () => withBaseOption,
26714
27852
  withDataGlobals: () => withDataGlobals
26715
27853
  });
26716
- import { Command as Command15 } from "commander";
27854
+ import { Command as Command22 } from "commander";
26717
27855
  function withDataGlobals(command2) {
26718
27856
  return command2.option("--org <uuid>", "Organization to operate against (overrides .wayai.yaml)").option("--output <format>", "Output format: json or table", "table").option("--json", "Shorthand for --output json");
26719
27857
  }
26720
- function withBaseOption(command2) {
26721
- return command2.option("--base <id>", "Base id (or set WAYAI_BASE)");
26722
- }
26723
27858
  function routeErrorsToCli(command2) {
26724
27859
  command2.exitOverride().configureOutput({ outputError: () => {
26725
27860
  } });
26726
27861
  for (const child of command2.commands) routeErrorsToCli(child);
26727
27862
  return command2;
26728
27863
  }
27864
+ function handleParserExit(err) {
27865
+ const { code, exitCode } = err ?? {};
27866
+ if (code === "commander.helpDisplayed" || code === "commander.version") return;
27867
+ if (code === "commander.help") {
27868
+ process.exitCode = exitCode || 1;
27869
+ return;
27870
+ }
27871
+ if (typeof code === "string" && code.startsWith("commander.")) {
27872
+ throw expected(err instanceof Error ? err.message : String(err));
27873
+ }
27874
+ throw err;
27875
+ }
26729
27876
  function buildDataProgram() {
26730
- const program = new Command15("wayai");
27877
+ const program = new Command22("wayai");
26731
27878
  program.addCommand(withDataGlobals(buildBasesCommand()));
26732
27879
  for (const build of [
26733
27880
  buildRecordsCommand,
@@ -26759,16 +27906,7 @@ async function runDataCommand(namespace, args2) {
26759
27906
  try {
26760
27907
  await program.parseAsync(["node", "wayai", namespace, ...args2]);
26761
27908
  } catch (err) {
26762
- const { code, exitCode } = err ?? {};
26763
- if (code === "commander.helpDisplayed" || code === "commander.version") return;
26764
- if (code === "commander.help") {
26765
- process.exitCode = exitCode || 1;
26766
- return;
26767
- }
26768
- if (typeof code === "string" && code.startsWith("commander.")) {
26769
- throw expected(err instanceof Error ? err.message : String(err));
26770
- }
26771
- throw err;
27909
+ handleParserExit(err);
26772
27910
  }
26773
27911
  }
26774
27912
  var init_program = __esm({
@@ -26793,6 +27931,7 @@ var init_program = __esm({
26793
27931
  init_org_context();
26794
27932
  init_registry();
26795
27933
  init_registry();
27934
+ init_helpers();
26796
27935
  }
26797
27936
  });
26798
27937
 
@@ -26802,9 +27941,9 @@ init_errors2();
26802
27941
  init_mask_secrets();
26803
27942
  init_utils();
26804
27943
  init_registry();
26805
- import { readFileSync as readFileSync23 } from "fs";
26806
- import { fileURLToPath as fileURLToPath2 } from "url";
26807
- import { dirname as dirname9, join as join28 } from "path";
27944
+ import { readFileSync as readFileSync26 } from "fs";
27945
+ import { fileURLToPath as fileURLToPath3 } from "url";
27946
+ import { dirname as dirname10, join as join29 } from "path";
26808
27947
 
26809
27948
  // src/lib/version-refresh.ts
26810
27949
  init_version_cache();
@@ -26959,8 +28098,8 @@ Run \`wayai admin skill install\` to update.`);
26959
28098
  }
26960
28099
 
26961
28100
  // src/index.ts
26962
- var __dirname = dirname9(fileURLToPath2(import.meta.url));
26963
- var pkg = JSON.parse(readFileSync23(join28(__dirname, "..", "package.json"), "utf-8"));
28101
+ var __dirname = dirname10(fileURLToPath3(import.meta.url));
28102
+ var pkg = JSON.parse(readFileSync26(join29(__dirname, "..", "package.json"), "utf-8"));
26964
28103
  var [, , command, ...args] = process.argv;
26965
28104
  var isBackgroundRefresh = command === REFRESH_COMMAND;
26966
28105
  if (!isBackgroundRefresh) initSentry(command, pkg.version);
@@ -27305,7 +28444,7 @@ async function runForeground() {
27305
28444
  try {
27306
28445
  await main();
27307
28446
  if (command && !SKIP_UPDATE_CHECK.includes(command) && !wantsHelp(args)) {
27308
- showUpdateNudges(pkg.version, fileURLToPath2(import.meta.url));
28447
+ showUpdateNudges(pkg.version, fileURLToPath3(import.meta.url));
27309
28448
  }
27310
28449
  await closeSentry();
27311
28450
  } catch (err) {