@moda-ai/cli 1.38.0 → 1.39.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.
@@ -7,7 +7,7 @@ import {
7
7
  saveProfileConfig,
8
8
  secretFilePathFromRef,
9
9
  writeSecretJsonFile
10
- } from "./cli-q79sq80a.js";
10
+ } from "./cli-rp912ypf.js";
11
11
  import {
12
12
  __require
13
13
  } from "./cli-0v6na3yp.js";
@@ -254,6 +254,33 @@ function normalizeProfileConfig(raw, options = {}) {
254
254
  migrations: inspectProfileMigrationState(options)
255
255
  }, options);
256
256
  }
257
+ function profileBaseUrlOrigin(name, options = {}) {
258
+ const raw = safeReadRecord(getModaPaths(options).profileConfigPath);
259
+ const profiles = raw && typeof raw.profiles === "object" && raw.profiles !== null ? raw.profiles : {};
260
+ let stored;
261
+ for (const [key, value] of Object.entries(profiles)) {
262
+ if (!isRecord(value))
263
+ continue;
264
+ if ((optionalString(value.name) ?? key) === name)
265
+ stored = value;
266
+ }
267
+ const url = optionalString(stored?.base_url);
268
+ if (!url)
269
+ return "default";
270
+ const environment = normalizeEnvironment(stored?.environment);
271
+ return sanitizeProfileBaseUrl(url, environment) === normalizeModaBaseUrl(url) ? "profile" : "default";
272
+ }
273
+ function describeBaseUrlSource(options) {
274
+ const envBase = optionalString(getEnv(options).MODA_BASE_URL);
275
+ if (envBase) {
276
+ const environment = activeProfileEnvironment(options);
277
+ if (!environmentAllowsLoopback(environment) && isLoopbackBaseUrl(normalizeModaBaseUrl(envBase))) {
278
+ return `default (env:MODA_BASE_URL loopback ignored for ${environment} profile)`;
279
+ }
280
+ return "env:MODA_BASE_URL";
281
+ }
282
+ return profileBaseUrlOrigin(options.profile, options);
283
+ }
257
284
  function loadProfileConfig(options = {}) {
258
285
  const path = getModaPaths(options).profileConfigPath;
259
286
  const parsed = safeReadRecord(path);
@@ -600,6 +627,10 @@ function resolveTenantId(options = {}) {
600
627
  const env = options.env ?? process.env;
601
628
  return env.MODA_TENANT_ID || resolveTenantIdFromActiveProfile(options) || loadCliConfig(options).tenant_id || undefined;
602
629
  }
630
+ function resolveApiTenantId(options = {}) {
631
+ const apiKey = resolveApiKey(options);
632
+ return (apiKey ? embeddedTenantIdFromApiKey(apiKey) : undefined) || resolveTenantId(options);
633
+ }
603
634
  var MODA_API_KEY = process.env.MODA_API_KEY;
604
635
  var MODA_INGEST_URL = process.env.MODA_INGEST_URL || DEFAULT_INGEST_URL;
605
636
  function resolveModaBaseUrl(options = {}) {
@@ -1160,6 +1191,10 @@ function createCliOutput(options) {
1160
1191
  }
1161
1192
  safeWriteStdout(`${JSON.stringify(redactDeep(data), null, 2)}
1162
1193
  `);
1194
+ for (const warning of writeOptions.warnings ?? []) {
1195
+ safeWriteStderr(`Warning: ${warning}
1196
+ `);
1197
+ }
1163
1198
  },
1164
1199
  writeEvent(event) {
1165
1200
  if (options.quiet)
@@ -1242,8 +1277,8 @@ function createCliOutput(options) {
1242
1277
  }
1243
1278
  function createAgentEnvelope(options) {
1244
1279
  const safeData = redactDeep(options.data);
1245
- const bounded = boundForAgent(safeData);
1246
- const envelope = {
1280
+ const maxBytes = agentMaxOutputBytes();
1281
+ const build = (bounded) => ({
1247
1282
  schema_version: AGENT_SCHEMA_VERSION,
1248
1283
  command: options.command,
1249
1284
  status: options.status,
@@ -1255,14 +1290,31 @@ function createAgentEnvelope(options) {
1255
1290
  recommended_actions: options.recommendedActions ?? [],
1256
1291
  next_commands: normalizeNextCommands(options.nextCommands ?? []),
1257
1292
  artifacts: options.artifacts ?? [],
1258
- warnings: options.warnings ?? inferWarnings(safeData),
1293
+ warnings: [
1294
+ ...options.warnings ?? inferWarnings(safeData),
1295
+ ...bounded.warning ? [bounded.warning] : []
1296
+ ],
1259
1297
  errors: options.errors ?? [],
1260
1298
  data: bounded.data,
1261
1299
  ...options.meta ? { meta: options.meta } : {},
1262
1300
  ...bounded.truncation ? { truncation: bounded.truncation } : {}
1263
- };
1301
+ });
1302
+ let envelope = build(boundForAgent(safeData, maxBytes));
1303
+ const envelopeBytes = Buffer.byteLength(JSON.stringify(envelope), "utf8");
1304
+ if (envelopeBytes > maxBytes) {
1305
+ const overhead = envelopeBytes - Buffer.byteLength(JSON.stringify(envelope.data) ?? "", "utf8");
1306
+ envelope = build(boundForAgent(safeData, Math.max(1024, maxBytes - overhead - 1024)));
1307
+ }
1264
1308
  return redactDeep(envelope);
1265
1309
  }
1310
+ var AGENT_ENVELOPE_RESERVE_BYTES = 2048;
1311
+ function agentEnvelopeFits(command, data, options = {}) {
1312
+ const maxBytes = agentMaxOutputBytes() - AGENT_ENVELOPE_RESERVE_BYTES;
1313
+ if (Buffer.byteLength(JSON.stringify(data) ?? "", "utf8") > maxBytes)
1314
+ return false;
1315
+ const envelope = createAgentEnvelope({ command, status: "ok", data, ...options });
1316
+ return !envelope.truncation && Buffer.byteLength(JSON.stringify(envelope), "utf8") <= maxBytes;
1317
+ }
1266
1318
  function createAgentErrorEnvelope(command, error, runId) {
1267
1319
  return createAgentEnvelope({
1268
1320
  command,
@@ -1277,7 +1329,7 @@ function createAgentErrorEnvelope(command, error, runId) {
1277
1329
  nextCommands: (error.suggested_commands ?? []).map((commandText) => ({
1278
1330
  command: commandText,
1279
1331
  purpose: "Suggested recovery command.",
1280
- mutability: commandText.includes(" sync") || commandText.startsWith("moda sync") ? "sync" : "read",
1332
+ mutability: recoveryCommandMutability(commandText),
1281
1333
  requires_approval: commandText.includes(" --yes") || commandText.includes(" sync")
1282
1334
  }))
1283
1335
  });
@@ -1469,8 +1521,15 @@ function normalizeNextCommands(commands) {
1469
1521
  };
1470
1522
  });
1471
1523
  }
1524
+ function recoveryCommandMutability(command) {
1525
+ if (command.includes(" sync") || command.startsWith("moda sync"))
1526
+ return "sync";
1527
+ if (/\s--yes(\s|=|$)/.test(command))
1528
+ return "write";
1529
+ return inferMutability(command);
1530
+ }
1472
1531
  function inferMutability(command) {
1473
- if (/\b(sync|approve|init|install|pull|gen|remove|promote)\b/.test(command)) {
1532
+ if (/\b(sync|approve|init|install|pull|gen|remove|promote|delete)\b/.test(command)) {
1474
1533
  if (/\bsync\b/.test(command))
1475
1534
  return "sync";
1476
1535
  return "write";
@@ -1488,25 +1547,108 @@ function inferWarnings(data) {
1488
1547
  }
1489
1548
  return [];
1490
1549
  }
1491
- function boundForAgent(data) {
1492
- const maxBytes = Number(process.env.MODA_AGENT_MAX_OUTPUT_BYTES ?? 200000);
1493
- const serialized = JSON.stringify(data);
1494
- const bytes = Buffer.byteLength(serialized, "utf8");
1495
- if (bytes <= maxBytes)
1550
+ var AGENT_MAX_STRING_CHARS = 2000;
1551
+ var PROTECTED_STRING_KEY = /(cursor|token|_id$|^id$|url$|href$|^key$|hash$)/i;
1552
+ function agentMaxOutputBytes() {
1553
+ return Number(process.env.MODA_AGENT_MAX_OUTPUT_BYTES ?? 200000);
1554
+ }
1555
+ function boundForAgent(data, maxBytes = agentMaxOutputBytes()) {
1556
+ const sizeOf = (value) => Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
1557
+ const serialized = JSON.stringify(data) ?? "";
1558
+ const originalBytes = Buffer.byteLength(serialized, "utf8");
1559
+ if (originalBytes <= maxBytes)
1496
1560
  return { data };
1497
- const preview = serialized.slice(0, Math.max(0, maxBytes - 256));
1561
+ let shortenedStrings = 0;
1562
+ const shorten = (value, limit, key) => {
1563
+ if (typeof value === "string") {
1564
+ if (value.length <= limit || key !== undefined && PROTECTED_STRING_KEY.test(key))
1565
+ return value;
1566
+ shortenedStrings += 1;
1567
+ return `${value.slice(0, limit)}… [truncated ${value.length - limit} chars]`;
1568
+ }
1569
+ if (Array.isArray(value))
1570
+ return value.map((item) => shorten(item, limit));
1571
+ if (value && typeof value === "object") {
1572
+ return Object.fromEntries(Object.entries(value).map(([entryKey, item]) => [entryKey, shorten(item, limit, entryKey)]));
1573
+ }
1574
+ return value;
1575
+ };
1576
+ let bounded = data;
1577
+ let boundedBytes = originalBytes;
1578
+ for (const limit of [AGENT_MAX_STRING_CHARS, 500, 200]) {
1579
+ shortenedStrings = 0;
1580
+ bounded = shorten(data, limit);
1581
+ boundedBytes = sizeOf(bounded);
1582
+ if (boundedBytes <= maxBytes)
1583
+ break;
1584
+ }
1585
+ const droppedItems = {};
1586
+ const collectArrays = (value, path, out) => {
1587
+ if (Array.isArray(value)) {
1588
+ if (value.length > 1)
1589
+ out.push({ path, array: value, itemBytes: value.map((item) => sizeOf(item) + 1) });
1590
+ value.forEach((item, index) => {
1591
+ if (item && typeof item === "object")
1592
+ collectArrays(item, `${path}[${index}]`, out);
1593
+ });
1594
+ } else if (value && typeof value === "object") {
1595
+ for (const [key, item] of Object.entries(value)) {
1596
+ if (item && typeof item === "object")
1597
+ collectArrays(item, path ? `${path}.${key}` : key, out);
1598
+ }
1599
+ }
1600
+ };
1601
+ for (let round = 0;round < 25 && boundedBytes > maxBytes; round++) {
1602
+ const arrays = [];
1603
+ collectArrays(bounded, "", arrays);
1604
+ const largest = arrays.map((entry) => ({ ...entry, total: entry.itemBytes.reduce((sum, bytes) => sum + bytes, 0) })).sort((left, right) => right.total - left.total)[0];
1605
+ if (!largest)
1606
+ break;
1607
+ let overshoot = boundedBytes - maxBytes;
1608
+ let drop = 0;
1609
+ for (let index = largest.array.length - 1;index >= 1 && overshoot > 0; index--) {
1610
+ overshoot -= largest.itemBytes[index];
1611
+ drop += 1;
1612
+ }
1613
+ if (drop === 0)
1614
+ break;
1615
+ largest.array.splice(largest.array.length - drop, drop);
1616
+ const key = largest.path || "(root)";
1617
+ droppedItems[key] = (droppedItems[key] ?? 0) + drop;
1618
+ boundedBytes = sizeOf(bounded);
1619
+ }
1620
+ const dropped = Object.entries(droppedItems);
1621
+ const parts = [
1622
+ shortenedStrings > 0 ? `shortened ${shortenedStrings} long text field(s)` : "",
1623
+ ...dropped.map(([path, count]) => `dropped the last ${count} item(s) of data.${path}`)
1624
+ ].filter(Boolean);
1625
+ const howToGetMore = "For complete results narrow the query (a smaller --limit, with --offset or --cursor paging where the command supports it), " + "rerun with --json (no byte limit), or raise MODA_AGENT_MAX_OUTPUT_BYTES.";
1626
+ if (boundedBytes > maxBytes) {
1627
+ const stub = { truncated: true, original_bytes: originalBytes, max_bytes: maxBytes, preview: serialized.slice(0, Math.max(0, maxBytes - 256)) };
1628
+ for (let stubBytes = sizeOf(stub);stubBytes > maxBytes && stub.preview.length > 0; stubBytes = sizeOf(stub)) {
1629
+ stub.preview = stub.preview.slice(0, Math.max(0, stub.preview.length - (stubBytes - maxBytes) - 16));
1630
+ }
1631
+ return {
1632
+ data: stub,
1633
+ warning: `Output was ${originalBytes} bytes, over the ${maxBytes}-byte agent limit, and could not be reduced while keeping its structure; data is a string preview. ${howToGetMore}`,
1634
+ truncation: {
1635
+ data: { original_bytes: originalBytes, max_bytes: maxBytes, reason: "agent_output_byte_limit", structure_preserved: false, complete: false }
1636
+ }
1637
+ };
1638
+ }
1498
1639
  return {
1499
- data: {
1500
- truncated: true,
1501
- original_bytes: bytes,
1502
- max_bytes: maxBytes,
1503
- preview
1504
- },
1640
+ data: bounded,
1641
+ warning: `Output was ${originalBytes} bytes, over the ${maxBytes}-byte agent limit: ${parts.join("; ")}. ${howToGetMore}`,
1505
1642
  truncation: {
1506
1643
  data: {
1507
- original_bytes: bytes,
1644
+ original_bytes: originalBytes,
1645
+ bounded_bytes: boundedBytes,
1508
1646
  max_bytes: maxBytes,
1509
- reason: "agent_output_byte_limit"
1647
+ reason: "agent_output_byte_limit",
1648
+ shortened_strings: shortenedStrings,
1649
+ dropped_items: droppedItems,
1650
+ structure_preserved: true,
1651
+ complete: false
1510
1652
  }
1511
1653
  }
1512
1654
  };
@@ -1566,4 +1708,4 @@ function createCommandContext(input, options = {}) {
1566
1708
  };
1567
1709
  }
1568
1710
 
1569
- export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, assertValidProfileName, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, CliAccessError, CliInputRequiredError, CliAuthError, CliCancellationError, isNetworkLikeError, isApiLikeError, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveModaBaseUrl, validateConfig, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, resolveOutputMode, createCliOutput, stringOption, createCommandContext };
1711
+ export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, assertValidProfileName, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, CliAccessError, CliInputRequiredError, CliAuthError, CliCancellationError, isNetworkLikeError, isApiLikeError, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, validateConfig, MAX_SCANNED_FILE_BYTES, DEFAULT_MAX_INVENTORY_FILES, DEFAULT_MAX_INVENTORY_TEXT_BYTES, MAX_RECORDED_SKIPS, SKIPPED_DIRS, isSkippedDir, isSkippedPathPrefix, isEvalLikePath, shouldInventoryFile, isTextFile, isSecretishPath, SECRET_KEYS, redactDeep, redactString, packSnapshot, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, resolveOutputMode, createCliOutput, agentEnvelopeFits, stringOption, createCommandContext };