@moda-ai/cli 1.55.26 → 1.55.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/{agent-nqmha8ck.js → agent-0w9rtdsw.js} +22 -16
  2. package/dist/cli-3n0gz4d0.js +17971 -0
  3. package/dist/{cli-b4740tnv.js → cli-3retgh64.js} +85 -1165
  4. package/dist/{cli-1mbb0ct0.js → cli-97jew729.js} +1 -1
  5. package/dist/{cli-sqsjd08w.js → cli-99p2jh28.js} +105 -893
  6. package/dist/{cli-k6q8dbpk.js → cli-9bgk5jeh.js} +8 -6
  7. package/dist/{cli-q6f6vrrr.js → cli-9bgnvth2.js} +5 -3
  8. package/dist/cli-9kfxkxmk.js +6994 -0
  9. package/dist/cli-cesds766.js +1516 -0
  10. package/dist/{cli-cdrvjva5.js → cli-dr6x0zv7.js} +28 -6
  11. package/dist/{cli-jtyvbd8d.js → cli-e23g9ywa.js} +1 -1
  12. package/dist/cli-fwpqghgc.js +569 -0
  13. package/dist/cli-k4tqwah4.js +89 -0
  14. package/dist/{cli-8tfk8qeb.js → cli-mg5zv4s6.js} +18 -15
  15. package/dist/{cli-jgfm7w1r.js → cli-n2hng3gv.js} +9 -6
  16. package/dist/cli-ns5fcnkz.js +341 -0
  17. package/dist/{cli-5g1tv80h.js → cli-ny48a8de.js} +12 -9
  18. package/dist/{cli-d577kq6h.js → cli-qpq84eee.js} +5220 -32715
  19. package/dist/cli-xq45etg1.js +1098 -0
  20. package/dist/cli-yf39avhh.js +754 -0
  21. package/dist/cli-yz8xcpyk.js +12 -0
  22. package/dist/cli.js +355 -283
  23. package/dist/doctor-k3ta6h2y.js +24 -0
  24. package/dist/{fix-crkwdkt4.js → fix-w5z02wt4.js} +47 -50
  25. package/dist/{harness-x3e7d68b.js → harness-7azmzytc.js} +15 -7
  26. package/dist/{harness-github-actions-fjhvhgpj.js → harness-github-actions-bv2qrnqb.js} +11 -5
  27. package/dist/harness-report-rd9c4e9v.js +54 -0
  28. package/dist/{index-2kf3fnnb.js → index-d1z50dsq.js} +45 -36
  29. package/dist/{production-intelligence-7n83c701.js → production-intelligence-wvsgjxth.js} +21 -18
  30. package/dist/{profile-commands-ct2t0bfr.js → profile-commands-h8evz9x0.js} +9 -5
  31. package/dist/prompt-attribution-1nytc9wj.js +148 -0
  32. package/dist/prompt-source-mcp.js +14 -7
  33. package/dist/prompt-sources-07tczyqa.js +19 -0
  34. package/dist/{prompts-92aqqb2k.js → prompts-6y9wfq5z.js} +8 -7
  35. package/dist/prompts-ab-ezrd63sk.js +70 -0
  36. package/dist/prompts-propose-2k3enm2q.js +255 -0
  37. package/dist/{provision-gn8raqpt.js → provision-y5bdbh4y.js} +9 -6
  38. package/dist/{registry-ex162j5y.js → registry-96aze5wp.js} +7 -4
  39. package/dist/{skills-2mbsj9bt.js → skills-j8865009.js} +7 -5
  40. package/dist/{sync-kghmmmk5.js → sync-ew7yvy0e.js} +16 -10
  41. package/package.json +1 -1
  42. package/skills/integration/index.json +2 -2
  43. package/dist/cli-bb7133m7.js +0 -144
  44. package/dist/doctor-w913fj4g.js +0 -22
@@ -1,3 +1,19 @@
1
+ import {
2
+ CliAccessError,
3
+ CliAuthError,
4
+ CliCancellationError,
5
+ CliInputError,
6
+ CliInputRequiredError,
7
+ CliNotFoundError,
8
+ apiErrorStatus,
9
+ inputDetailsOf,
10
+ isApiLikeError,
11
+ isNetworkLikeError
12
+ } from "./cli-k4tqwah4.js";
13
+ import {
14
+ redactDeep
15
+ } from "./cli-ns5fcnkz.js";
16
+
1
17
  // src/profiles.ts
2
18
  import {
3
19
  chmodSync,
@@ -449,6 +465,14 @@ function resolveBaseUrl(options = {}) {
449
465
  return PROD_BASE_URL;
450
466
  }
451
467
  }
468
+ function resolveBaseUrlStrict(options = {}) {
469
+ try {
470
+ getActiveProfile(options);
471
+ } catch {
472
+ return;
473
+ }
474
+ return resolveBaseUrl(options);
475
+ }
452
476
  function resolveIngestUrl(options = {}) {
453
477
  const environment = activeProfileEnvironment(options);
454
478
  const envIngest = optionalString(getEnv(options).MODA_INGEST_URL);
@@ -531,94 +555,6 @@ function writeSecretJsonFile(path, value) {
531
555
  } catch {}
532
556
  }
533
557
 
534
- // src/errors.ts
535
- class CliInputError extends Error {
536
- usage;
537
- reason;
538
- details;
539
- constructor(message, usage, reason, details) {
540
- super(message);
541
- this.usage = usage;
542
- this.reason = reason;
543
- this.details = details;
544
- this.name = "CliInputError";
545
- }
546
- }
547
- var inputDetails = new WeakMap;
548
- function attachInputDetails(error, details) {
549
- inputDetails.set(error, details);
550
- }
551
- function inputDetailsOf(error) {
552
- if (error instanceof CliInputError && error.details)
553
- return error.details;
554
- return error !== null && typeof error === "object" ? inputDetails.get(error) : undefined;
555
- }
556
-
557
- class CliAccessError extends Error {
558
- guidance;
559
- constructor(message, options = {}) {
560
- super(message);
561
- this.name = "CliAccessError";
562
- this.guidance = options.guidance;
563
- }
564
- }
565
-
566
- class CliInputRequiredError extends Error {
567
- input_request;
568
- constructor(message, input_request, usage) {
569
- super(message);
570
- this.input_request = input_request;
571
- this.name = "CliInputRequiredError";
572
- if (usage)
573
- this.usage = usage;
574
- }
575
- usage;
576
- }
577
-
578
- class CliAuthError extends Error {
579
- auth;
580
- constructor(message, auth, usage) {
581
- super(message);
582
- this.auth = auth;
583
- this.name = "CliAuthError";
584
- if (usage)
585
- this.usage = usage;
586
- }
587
- usage;
588
- }
589
-
590
- class CliNotFoundError extends Error {
591
- entity;
592
- id;
593
- usage;
594
- constructor(entity, id, message, usage) {
595
- super(message ?? `No ${entity} found for id "${id}" in this tenant.`);
596
- this.entity = entity;
597
- this.id = id;
598
- this.usage = usage;
599
- this.name = "CliNotFoundError";
600
- }
601
- }
602
-
603
- class CliCancellationError extends Error {
604
- constructor(message) {
605
- super(message);
606
- this.name = "CliCancellationError";
607
- }
608
- }
609
- function isNetworkLikeError(error) {
610
- return error instanceof Error && (error.message.startsWith("Network error:") || error.message.startsWith("Request timed out") || error.name === "AbortError" || /(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|socket hang up)/i.test(error.message));
611
- }
612
- function isApiLikeError(error) {
613
- return error instanceof Error && (error.name === "ApiError" || ("statusCode" in error) && typeof error.statusCode === "number");
614
- }
615
- function apiErrorStatus(error) {
616
- if (!isApiLikeError(error))
617
- return;
618
- const status = error.statusCode;
619
- return typeof status === "number" ? status : undefined;
620
- }
621
-
622
558
  // src/config.ts
623
559
  import { readFileSync as readFileSync2 } from "node:fs";
624
560
  var pkg = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf8"));
@@ -663,11 +599,12 @@ function resolveApiTenantId(options = {}) {
663
599
  const apiKey = resolveApiKey(options);
664
600
  return (apiKey ? embeddedTenantIdFromApiKey(apiKey) : undefined) || resolveTenantId(options);
665
601
  }
666
- var MODA_API_KEY = process.env.MODA_API_KEY;
667
- var MODA_INGEST_URL = process.env.MODA_INGEST_URL || DEFAULT_INGEST_URL;
668
602
  function resolveModaBaseUrl(options = {}) {
669
603
  return resolveBaseUrl(options) || PROD_BASE_URL;
670
604
  }
605
+ function resolveModaBaseUrlStrict(options = {}) {
606
+ return resolveBaseUrlStrict(options);
607
+ }
671
608
  function validateConfig() {
672
609
  if (!resolveApiKey()) {
673
610
  throw new CliAuthError('No Moda API key found. Set MODA_API_KEY=moda_sk_... in the environment, save one with "moda auth login --api-key", or run "moda init" for interactive setup.', {
@@ -677,6 +614,31 @@ function validateConfig() {
677
614
  }
678
615
  }
679
616
 
617
+ // src/api-key-headers.ts
618
+ var PLATFORM_API_KEY_PREFIX = "moda_pk_";
619
+ var TENANT_HEADER = "x-moda-tenant-id";
620
+ function isPlatformApiKey(key) {
621
+ return Boolean(key?.startsWith(PLATFORM_API_KEY_PREFIX));
622
+ }
623
+ var platformTenantOverride;
624
+ function setPlatformTenantOverride(tenantId) {
625
+ platformTenantOverride = tenantId;
626
+ }
627
+ var TENANT_PATH = /\/tenants\/([^/?#]+)/;
628
+ function tenantFromUrl(url) {
629
+ const match = url ? TENANT_PATH.exec(url) : null;
630
+ return match ? decodeURIComponent(match[1]) : undefined;
631
+ }
632
+ function apiKeyHeaders(key, options = {}, url) {
633
+ const headers = { "x-api-key": key };
634
+ if (isPlatformApiKey(key)) {
635
+ const tenantId = tenantFromUrl(url) || platformTenantOverride || resolveTenantId(options);
636
+ if (tenantId)
637
+ headers[TENANT_HEADER] = tenantId;
638
+ }
639
+ return headers;
640
+ }
641
+
680
642
  // src/api-request-count.ts
681
643
  var apiRequestCount = 0;
682
644
  function recordApiRequest() {
@@ -689,765 +651,13 @@ function resetApiRequestCount() {
689
651
  apiRequestCount = 0;
690
652
  }
691
653
 
692
- // ../../packages/harness-snapshot/src/policy.ts
693
- var MAX_SCANNED_FILE_BYTES = 512 * 1024;
694
- var DEFAULT_MAX_INVENTORY_FILES = 12000;
695
- var DEFAULT_MAX_INVENTORY_TEXT_BYTES = 48 * 1024 * 1024;
696
- var MAX_SNAPSHOT_BODY_BYTES = 4500000;
697
- var MAX_RECORDED_SKIPS = 1000;
698
- var SKIPPED_DIRS = new Set([
699
- ".angular",
700
- ".cache",
701
- ".expo",
702
- ".git",
703
- ".hg",
704
- ".idea",
705
- ".svn",
706
- ".next",
707
- ".nuxt",
708
- ".output",
709
- ".parcel-cache",
710
- ".pytest_cache",
711
- ".ruff_cache",
712
- ".serverless",
713
- ".sst",
714
- ".turbo",
715
- ".wrangler",
716
- ".wrangler-shared",
717
- ".moda",
718
- ".moda-test",
719
- ".moda-test-home",
720
- ".moda-cli-workspaces",
721
- ".moda-cli-benchmarks",
722
- "node_modules",
723
- "dist",
724
- "build",
725
- "coverage",
726
- "out",
727
- "target",
728
- "logs",
729
- "tmp",
730
- "temp",
731
- "__pycache__",
732
- ".venv",
733
- "venv"
734
- ]);
735
- var SKIPPED_PATH_PREFIXES = [
736
- ".claude/worktrees",
737
- ".codex/environments"
738
- ];
739
- function posixNormalize(path) {
740
- const absolute = path.startsWith("/");
741
- const parts = path.split("/").filter((part) => part !== "" && part !== ".");
742
- const normalized = parts.join("/");
743
- return absolute ? `/${normalized}` : normalized;
744
- }
745
- function basename(path) {
746
- const normalized = posixNormalize(path);
747
- const index = normalized.lastIndexOf("/");
748
- return index === -1 ? normalized : normalized.slice(index + 1);
749
- }
750
- function extname(path) {
751
- const name = basename(path);
752
- const index = name.lastIndexOf(".");
753
- return index <= 0 ? "" : name.slice(index);
754
- }
755
- function isSkippedDir(path) {
756
- return SKIPPED_DIRS.has(basename(path));
757
- }
758
- function isSkippedPathPrefix(path) {
759
- const normalized = posixNormalize(path);
760
- return SKIPPED_PATH_PREFIXES.some((prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`));
761
- }
762
- function isSourceFile(path) {
763
- return /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i.test(path);
764
- }
765
- function isPackageOrLockfile(path) {
766
- return path === "package.json" || path === "pyproject.toml" || path === "bun.lock" || path === "bun.lockb" || path === "pnpm-lock.yaml" || path === "yarn.lock";
767
- }
768
- function isPromptLikePath(path) {
769
- return /(^|\/)prompts?\//i.test(path) && /\.(prompt\.)?(md|json|ya?ml)$/i.test(path);
770
- }
771
- function isSkillLikePath(path) {
772
- return /(^|\/)(\.claude\/skills|\.cursor\/rules|skills?)\//i.test(path) && /\.(md|mdc)$/i.test(path);
773
- }
774
- function isEvalLikePath(path) {
775
- return /(^|\/)(evals?|tests?|__tests__)\//i.test(path) && /\.(ts|tsx|js|mjs|py|json|ya?ml)$/i.test(path);
776
- }
777
- function isDeploymentLikePath(path) {
778
- return /(^|\/)(wrangler\.toml|vercel\.json|render\.ya?ml|railway\.json|Dockerfile|docker-compose\.ya?ml)$/i.test(path) || /^\.github\/workflows\/[^/]+\.ya?ml$/i.test(path);
779
- }
780
- function isDeveloperAgentConfigPath(path) {
781
- return path === ".claude/settings.json" || path === ".codex/hooks.json" || path === ".cursor/hooks.json";
782
- }
783
- function isReadmeOrAgents(path) {
784
- const name = basename(path);
785
- return name === "README.md" || name === "AGENTS.md";
786
- }
787
- function shouldInventoryFile(path) {
788
- const normalized = posixNormalize(path);
789
- const name = basename(normalized);
790
- const extension = extname(normalized).toLowerCase();
791
- if (isSourceFile(normalized))
792
- return true;
793
- if (isPackageOrLockfile(normalized))
794
- return true;
795
- if (isPromptLikePath(normalized))
796
- return true;
797
- if (isSkillLikePath(normalized))
798
- return true;
799
- if (isEvalLikePath(normalized))
800
- return true;
801
- if (isDeploymentLikePath(normalized))
802
- return true;
803
- if (isDeveloperAgentConfigPath(normalized))
804
- return true;
805
- if (isReadmeOrAgents(normalized))
806
- return true;
807
- return extension === ".toml" && (name === "pyproject.toml" || name === "wrangler.toml");
808
- }
809
- function isTextFile(path) {
810
- const extension = extname(path).toLowerCase();
811
- return [
812
- "",
813
- ".cjs",
814
- ".js",
815
- ".json",
816
- ".jsx",
817
- ".md",
818
- ".mdc",
819
- ".mjs",
820
- ".py",
821
- ".toml",
822
- ".ts",
823
- ".tsx",
824
- ".txt",
825
- ".yaml",
826
- ".yml"
827
- ].includes(extension);
828
- }
829
- function isSecretishPath(path) {
830
- const name = basename(path).toLowerCase();
831
- return name === ".env" || name.startsWith(".env.") || name.includes("secret") || name.includes("private-key") || name.endsWith(".pem");
832
- }
833
-
834
- // ../../packages/harness-snapshot/src/redact.ts
835
- var REDACTED = "[REDACTED]";
836
- var MAX_DEPTH = 256;
837
- var SECRET_KEYS = new Set([
838
- "api_key",
839
- "apikey",
840
- "token",
841
- "access_token",
842
- "authorization",
843
- "secret",
844
- "password",
845
- "cli_token"
846
- ]);
847
- var SECRET_PATTERNS = [
848
- { pattern: /\bmoda_sk_[A-Za-z0-9_-]+\b/g, prefixLength: 8 },
849
- { pattern: /\bmoda_pk_[A-Za-z0-9_-]+\b/g, prefixLength: 8 },
850
- { pattern: /\bmoda_ct_[A-Za-z0-9_-]+\b/g, prefixLength: 8 },
851
- { pattern: /\bBearer\s+([A-Za-z0-9._~+/-]+=*)\b/gi, prefixLength: 8 },
852
- { pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, prefixLength: 8 },
853
- { pattern: /\bsk-ant-api\d{2}-[A-Za-z0-9_-]{20,}\b/g, prefixLength: 12 },
854
- { pattern: /\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}\b/g, prefixLength: 8 },
855
- { pattern: /\bAKIA[A-Z0-9]{16}\b/g, prefixLength: 8 },
856
- { pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g, prefixLength: 8 },
857
- { pattern: /\bgh[pousr]_[A-Za-z0-9]{36,}\b/g, prefixLength: 8 },
858
- { pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, prefixLength: 8 }
859
- ];
860
- function redactDeep(value) {
861
- return redactValue(value, 0, new WeakSet);
862
- }
863
- function redactValue(value, depth, seen) {
864
- if (typeof value === "string")
865
- return redactString(value);
866
- if (value === null || typeof value !== "object")
867
- return value;
868
- if (depth >= MAX_DEPTH)
869
- return "[MaxDepth]";
870
- if (seen.has(value))
871
- return "[Circular]";
872
- seen.add(value);
873
- if (Array.isArray(value)) {
874
- try {
875
- return value.map((item) => redactValue(item, depth + 1, seen));
876
- } finally {
877
- seen.delete(value);
878
- }
879
- }
880
- const out = {};
881
- try {
882
- for (const [key, item] of Object.entries(value)) {
883
- out[key] = shouldRedactKey(key) ? redactSecretValue(item) : redactValue(item, depth + 1, seen);
884
- }
885
- } finally {
886
- seen.delete(value);
887
- }
888
- return out;
889
- }
890
- function shouldRedactKey(key) {
891
- return SECRET_KEYS.has(key.replace(/[-\s]/g, "_").toLowerCase());
892
- }
893
- function redactSecretValue(value) {
894
- if (typeof value !== "string")
895
- return REDACTED;
896
- const redacted = redactString(value);
897
- if (redacted !== value)
898
- return redacted;
899
- if (/^(configured|missing|not_configured|none|present|absent)$/i.test(value))
900
- return value;
901
- return REDACTED;
902
- }
903
- function redactStringWithCount(input) {
904
- let text = input;
905
- let redactionCount = 0;
906
- for (const { pattern, prefixLength } of SECRET_PATTERNS) {
907
- text = text.replace(pattern, (match) => {
908
- redactionCount += 1;
909
- if (/^Bearer\s+/i.test(match)) {
910
- const token = match.replace(/^Bearer\s+/i, "");
911
- return `Bearer ${token.slice(0, prefixLength)}...[REDACTED]`;
912
- }
913
- return `${match.slice(0, prefixLength)}...[REDACTED]`;
914
- });
915
- }
916
- return { text, redactionCount };
917
- }
918
- function redactString(input) {
919
- return redactStringWithCount(input).text;
920
- }
921
- function redactSnapshotText(text) {
922
- const parts = text.split(/(\r\n|\r|\n)/);
923
- let redactionCount = 0;
924
- for (let index = 0;index < parts.length; index += 2) {
925
- const redacted = redactStringWithCount(parts[index] ?? "");
926
- parts[index] = redacted.text;
927
- redactionCount += redacted.redactionCount;
928
- }
929
- return { text: parts.join(""), redactionCount };
930
- }
931
-
932
- // ../../packages/harness-snapshot/src/pack.ts
933
- function utf8ByteLength(value) {
934
- let bytes = 0;
935
- for (let index = 0;index < value.length; index += 1) {
936
- const code = value.charCodeAt(index);
937
- if (code <= 127) {
938
- bytes += 1;
939
- } else if (code <= 2047) {
940
- bytes += 2;
941
- } else if (code >= 55296 && code <= 56319) {
942
- const next = value.charCodeAt(index + 1);
943
- if (next >= 56320 && next <= 57343) {
944
- bytes += 4;
945
- index += 1;
946
- } else {
947
- bytes += 3;
948
- }
949
- } else {
950
- bytes += 3;
951
- }
952
- }
953
- return bytes;
954
- }
955
- function isValidRelativePath(path) {
956
- if (path.length === 0 || path.startsWith("/") || path.includes("\\"))
957
- return false;
958
- return !path.split("/").some((part) => part === "..");
959
- }
960
- function packSnapshot(inputs, opts) {
961
- const maxFiles = opts.maxFiles ?? DEFAULT_MAX_INVENTORY_FILES;
962
- const maxTextBytes = opts.maxTextBytes ?? DEFAULT_MAX_INVENTORY_TEXT_BYTES;
963
- const maxSnapshotBytes = opts.maxSnapshotBytes ?? MAX_SNAPSHOT_BODY_BYTES;
964
- const files = [];
965
- const skips = [];
966
- let inventoryTextBytes = 0;
967
- let serializedBytes = 0;
968
- let byteCount = 0;
969
- let truncated = false;
970
- const recordSkip = (path, reason) => {
971
- if (skips.length < MAX_RECORDED_SKIPS)
972
- skips.push({ path, reason });
973
- };
974
- const sorted = [...inputs].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
975
- for (const input of sorted) {
976
- if (!isValidRelativePath(input.path)) {
977
- recordSkip(input.path, "invalid relative path");
978
- continue;
979
- }
980
- const path = posixNormalize(input.path);
981
- if (input.isSymlink) {
982
- recordSkip(path, "symlink skipped for safety");
983
- continue;
984
- }
985
- if (isSecretishPath(path)) {
986
- recordSkip(path, "secret-bearing path");
987
- continue;
988
- }
989
- if (!shouldInventoryFile(path)) {
990
- recordSkip(path, "not an inventory file");
991
- continue;
992
- }
993
- if (!isTextFile(path)) {
994
- recordSkip(path, "non-text file");
995
- continue;
996
- }
997
- if (input.byteSize > MAX_SCANNED_FILE_BYTES) {
998
- recordSkip(path, "file too large");
999
- truncated = true;
1000
- continue;
1001
- }
1002
- if (files.length >= maxFiles || inventoryTextBytes + input.byteSize > maxTextBytes) {
1003
- recordSkip(path, "inventory budget");
1004
- truncated = true;
1005
- continue;
1006
- }
1007
- const text = opts.redact === false ? input.text : redactSnapshotText(input.text).text;
1008
- const file = { path, text };
1009
- const serializedCost = utf8ByteLength(JSON.stringify(file)) + 1;
1010
- if (serializedBytes + serializedCost > maxSnapshotBytes) {
1011
- recordSkip(path, "snapshot budget");
1012
- truncated = true;
1013
- break;
1014
- }
1015
- files.push(file);
1016
- inventoryTextBytes += input.byteSize;
1017
- serializedBytes += serializedCost;
1018
- byteCount += utf8ByteLength(text);
1019
- }
1020
- return {
1021
- snapshot: {
1022
- repo: { name: opts.repoName, root: "." },
1023
- files,
1024
- truncated,
1025
- fileCount: files.length,
1026
- byteCount,
1027
- ...opts.gitCommit ? { gitCommit: opts.gitCommit } : {}
1028
- },
1029
- skips
1030
- };
1031
- }
1032
- // src/batch-reads.ts
1033
- import { readFile } from "node:fs/promises";
1034
- var BATCH_MAX_TARGETS = 25;
1035
- var BATCH_CONCURRENCY = 4;
1036
- var ANCHOR_SUFFIX = /^(.+)@(\d+)$/;
1037
- function parseBatchTarget(text, anchors, usage) {
1038
- const raw = text.trim();
1039
- if (raw.startsWith("{")) {
1040
- let parsed;
1041
- try {
1042
- parsed = JSON.parse(raw);
1043
- } catch {
1044
- throw new CliInputError(`Could not parse the JSON target ${JSON.stringify(raw)}.`, usage, "bad_batch_target");
1045
- }
1046
- const record = parsed;
1047
- const id = record?.id ?? record?.conversation_id ?? record?.trace_id;
1048
- if (typeof id !== "string" || id.trim() === "") {
1049
- throw new CliInputError(`JSON target ${JSON.stringify(raw)} has no "id" string.`, usage, "bad_batch_target");
1050
- }
1051
- const index = record.msg_index ?? record.message_index;
1052
- if (index !== undefined && index !== null) {
1053
- if (!anchors) {
1054
- throw new CliInputError(`JSON target ${JSON.stringify(raw)} has a msg_index, which this command does not take.`, usage, "bad_batch_target");
1055
- }
1056
- if (typeof index !== "number" || !Number.isSafeInteger(index) || index < 0) {
1057
- throw new CliInputError(`JSON target ${JSON.stringify(raw)} has an invalid msg_index (want an integer >= 0).`, usage, "bad_batch_target");
1058
- }
1059
- return { raw, id, msgIndex: index };
1060
- }
1061
- return { raw, id };
1062
- }
1063
- if (anchors) {
1064
- const match = ANCHOR_SUFFIX.exec(raw);
1065
- if (match) {
1066
- const msgIndex = Number(match[2]);
1067
- if (!Number.isSafeInteger(msgIndex)) {
1068
- throw new CliInputError(`Target ${JSON.stringify(raw)} has an @msg_index past ${Number.MAX_SAFE_INTEGER}. ` + 'If the id itself ends in @<digits>, pass it as {"id": "..."}.', usage, "bad_batch_target");
1069
- }
1070
- return { raw, id: match[1], msgIndex };
1071
- }
1072
- }
1073
- return { raw, id: raw };
1074
- }
1075
- function parseTargetLines(text, anchors, usage) {
1076
- return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line !== "").map((line) => parseBatchTarget(line, anchors, usage));
1077
- }
1078
- async function readStdinText() {
1079
- if (process.stdin.isTTY) {
1080
- throw new CliInputError("--ids-from=- reads targets from stdin, but stdin is a terminal. Pipe ids in (one per line) or pass a file path.", undefined, "ids_from_tty");
1081
- }
1082
- const chunks = [];
1083
- for await (const chunk of process.stdin)
1084
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
1085
- return Buffer.concat(chunks).toString("utf8");
1086
- }
1087
- async function readIdsFrom(source, usage) {
1088
- if (source === "" || source === "true") {
1089
- throw new CliInputError("--ids-from needs a value: - (stdin) or a file path.", usage, "missing_flag_value");
1090
- }
1091
- if (source === "-")
1092
- return readStdinText();
1093
- try {
1094
- return await readFile(source, "utf8");
1095
- } catch (error) {
1096
- const code = error.code;
1097
- throw new CliInputError(`Could not read --ids-from file ${JSON.stringify(source)}${typeof code === "string" ? ` (${code})` : ""}.`, usage, "ids_from_unreadable");
1098
- }
1099
- }
1100
- async function collectBatchTargets(positionals, idsFrom, options) {
1101
- const targets = positionals.map((value) => parseBatchTarget(value, options.anchors, options.usage));
1102
- if (idsFrom !== undefined) {
1103
- targets.push(...parseTargetLines(await readIdsFrom(idsFrom, options.usage), options.anchors, options.usage));
1104
- }
1105
- const seen = new Set;
1106
- const unique = targets.filter((target) => {
1107
- const key = `${target.id}\x00${target.msgIndex ?? ""}`;
1108
- if (seen.has(key))
1109
- return false;
1110
- seen.add(key);
1111
- return true;
1112
- });
1113
- if (unique.length === 0) {
1114
- throw new CliInputError(`No targets: --ids-from gave no ids (one per line).`, options.usage, "missing_positional");
1115
- }
1116
- if (unique.length > BATCH_MAX_TARGETS) {
1117
- throw new CliInputError(`Got ${unique.length} targets; 'moda ${options.command}' reads at most ${BATCH_MAX_TARGETS} per call, so nothing was read. ` + `Split the ids into calls of ${BATCH_MAX_TARGETS} or fewer.`, options.usage, "batch_too_large");
1118
- }
1119
- return unique;
1120
- }
1121
- async function mapWithConcurrency(items, limit, fn, options = {}) {
1122
- const results = new Array(items.length);
1123
- let next = 0;
1124
- let failure;
1125
- const worker = async () => {
1126
- while (!failure && next < items.length && (options.shouldStart?.(next) ?? true)) {
1127
- const index = next++;
1128
- try {
1129
- results[index] = await fn(items[index], index);
1130
- } catch (error) {
1131
- failure ??= { error };
1132
- }
1133
- }
1134
- };
1135
- await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
1136
- if (failure)
1137
- throw failure.error;
1138
- return next < items.length ? results.slice(0, next) : results;
1139
- }
1140
- function idNeedsJsonTarget(id) {
1141
- return ANCHOR_SUFFIX.test(id);
1142
- }
1143
- function contextTarget(id) {
1144
- const verbatim = id === id.trim() && !id.startsWith("{") && !idNeedsJsonTarget(id);
1145
- return verbatim ? id : JSON.stringify({ id });
1146
- }
1147
-
1148
- // src/transforms.ts
1149
- function asRecord(value) {
1150
- if (value && typeof value === "object" && !Array.isArray(value)) {
654
+ // src/shell-quote.ts
655
+ function shellArg(value) {
656
+ if (/^[A-Za-z0-9_.,:@%+/=-]+$/.test(value))
1151
657
  return value;
1152
- }
1153
- return null;
1154
- }
1155
- function asString(value) {
1156
- return typeof value === "string" ? value : null;
1157
- }
1158
- function asNumber(value) {
1159
- return typeof value === "number" && Number.isFinite(value) ? value : null;
1160
- }
1161
- function frustrationAnchorIndex(row) {
1162
- if (!row)
1163
- return null;
1164
- const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
1165
- const firstQuote = asRecord(userQuotes[0]);
1166
- const resolved = asNumber(firstQuote?.msg_index) ?? asNumber(row.anchor_msg_index);
1167
- if (resolved !== null)
1168
- return resolved;
1169
- const keyTurns = Array.isArray(row.key_turns) ? row.key_turns : [];
1170
- const firstTurn = keyTurns.find((t) => typeof t === "number");
1171
- if (firstTurn !== undefined)
1172
- return firstTurn;
1173
- return asNumber(firstQuote?.turn);
1174
- }
1175
- function buildFrustrationAnchor(row) {
1176
- const conversation_id = asString(row.conversation_id) ?? "";
1177
- const userQuotes = Array.isArray(row.user_quotes) ? row.user_quotes : [];
1178
- const firstQuote = asRecord(userQuotes[0]);
1179
- const keyTurnsRaw = Array.isArray(row.key_turns) ? row.key_turns : [];
1180
- const all_turns = keyTurnsRaw.filter((t) => typeof t === "number");
1181
- const msg_index = frustrationAnchorIndex(row);
1182
- let signal = null;
1183
- let quote_preview = null;
1184
- if (firstQuote) {
1185
- signal = asString(firstQuote.signal);
1186
- const quote = asString(firstQuote.quote);
1187
- if (quote !== null) {
1188
- let end = 0;
1189
- let points = 0;
1190
- for (const ch of quote) {
1191
- if (points++ === 200)
1192
- break;
1193
- end += ch.length;
1194
- }
1195
- quote_preview = quote.slice(0, end);
1196
- }
1197
- }
1198
- return {
1199
- kind: "frustration",
1200
- conversation_id,
1201
- msg_index,
1202
- signal,
1203
- quote_preview,
1204
- all_turns,
1205
- no_anchor: msg_index === null
1206
- };
1207
- }
1208
- function buildToolFailureAnchor(row, toolName) {
1209
- const conversation_id = asString(row.conversation_id) ?? "";
1210
- const msg_index = asNumber(row.msg_index);
1211
- const tool_use_id = asString(row.tool_use_id);
1212
- const error_subtype = asString(row.error_subtype);
1213
- return {
1214
- kind: "tool_failure",
1215
- conversation_id,
1216
- msg_index,
1217
- tool_name: toolName,
1218
- tool_use_id,
1219
- error_subtype,
1220
- no_anchor: msg_index === null
1221
- };
1222
- }
1223
- function frustrationsFromEmotions(response) {
1224
- if (!Array.isArray(response.detections) || Array.isArray(response.frustrations))
1225
- return response;
1226
- const summary = asRecord(response.summary) ?? {};
1227
- const pagination = asRecord(response.pagination) ?? {};
1228
- const byFamily = asRecord(summary.by_family) ?? {};
1229
- const totalAnalyzed = asNumber(summary.conversations_analyzed) ?? 0;
1230
- const byFamilyConversations = asRecord(summary.conversations_by_family);
1231
- const frustrated = asNumber(byFamilyConversations?.frustration) ?? asNumber(byFamily.frustration) ?? 0;
1232
- const frustrations = response.detections.map((item) => asRecord(item)).filter((row) => row !== null && (row.family === undefined || row.family === "frustration")).map(({ conversation_context, score, is_detected, ...row }) => ({
1233
- ...row,
1234
- is_frustrated: is_detected === true,
1235
- frustration_score: score,
1236
- target: row.elicitor === "bot" || row.elicitor === "external" ? row.elicitor : "ambiguous",
1237
- conversation: conversation_context ?? null
1238
- }));
1239
- const atRisk = asNumber(pagination.at_risk) ?? Math.max(0, (asNumber(pagination.total) ?? 0) - frustrated);
1240
- const signalBreakdown = {};
1241
- for (const item of Array.isArray(response.signal_breakdown) ? response.signal_breakdown : []) {
1242
- const row = asRecord(item);
1243
- if (row?.family === "frustration" && typeof row.signal === "string") {
1244
- signalBreakdown[row.signal] = asNumber(row.count) ?? 0;
1245
- }
1246
- }
1247
- const { detections: _detections, ...rest } = response;
1248
- return {
1249
- ...rest,
1250
- summary: {
1251
- total_analyzed: totalAnalyzed,
1252
- frustrated_count: frustrated,
1253
- at_risk_count: atRisk,
1254
- frustration_rate_pct: totalAnalyzed > 0 ? Math.round(frustrated / totalAnalyzed * 1000) / 10 : 0
1255
- },
1256
- signal_breakdown: signalBreakdown,
1257
- frustrations,
1258
- source: "emotion_detections"
1259
- };
1260
- }
1261
- function withFrustrationAnchors(response) {
1262
- if (!Array.isArray(response.frustrations))
1263
- return response;
1264
- const frustrations = response.frustrations.map((item) => {
1265
- const row = asRecord(item);
1266
- if (!row)
1267
- return item;
1268
- const anchor = buildFrustrationAnchor(row);
1269
- return { ...row, anchor };
1270
- });
1271
- return { ...response, frustrations };
1272
- }
1273
- var TOOL_FAILURE_CONTEXT_RADIUS = 2;
1274
- function shellWord(value) {
1275
- return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
1276
- }
1277
- function trimFailureWindow(value, radius, fallbackCenter) {
1278
- const conv = asRecord(value);
1279
- if (!conv)
1280
- return value ?? null;
1281
- const { coverage: _coverage, context, ...convRest } = conv;
1282
- const ctx = asRecord(context);
1283
- if (!ctx)
1284
- return convRest;
1285
- const center = asNumber(ctx.center_index) ?? fallbackCenter;
1286
- const messages = (Array.isArray(ctx.messages) ? ctx.messages : []).map((message) => asRecord(message)).filter((message) => {
1287
- const index = asNumber(message?.index);
1288
- return message !== null && (center === null || index === null || Math.abs(index - center) <= radius);
1289
- }).map(({ content_blocks, ...message }) => {
1290
- const covered = typeof message.content === "string" && message.content.length > 0 || Array.isArray(message.tool_calls) && message.tool_calls.length > 0 || Array.isArray(message.tool_results) && message.tool_results.length > 0;
1291
- return covered || content_blocks === undefined ? message : { ...message, content_blocks };
1292
- });
1293
- const indexes = messages.map((message) => asNumber(message.index)).filter((index) => index !== null);
1294
- return {
1295
- ...convRest,
1296
- context: {
1297
- ...ctx,
1298
- ...indexes.length > 0 ? { from_index: Math.min(...indexes), to_index: Math.max(...indexes) } : {},
1299
- messages
1300
- }
1301
- };
1302
- }
1303
- function trimToolFailureConversations(response, opts) {
1304
- if (!Array.isArray(response.examples))
1305
- return response;
1306
- const examples = response.examples.map((item) => {
1307
- const row = asRecord(item);
1308
- if (!row)
1309
- return item;
1310
- const { conversation, window, ...rest } = row;
1311
- const conversationId = asString(row.conversation_id);
1312
- const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? asNumber(row.msg_index);
1313
- return {
1314
- ...rest,
1315
- ...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${TOOL_FAILURE_CONTEXT_RADIUS}` } : {},
1316
- ...opts.drop ? {} : { conversation: trimFailureWindow(conversation, TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) },
1317
- ..."window" in row ? { window: trimFailureWindow(window, opts.windowRadius ?? TOOL_FAILURE_CONTEXT_RADIUS, msgIndex) } : {}
1318
- };
1319
- });
1320
- return { ...response, examples };
1321
- }
1322
- function withToolFailureAnchors(response) {
1323
- if (!Array.isArray(response.examples))
1324
- return response;
1325
- const toolName = asString(response.tool_name) ?? "";
1326
- const examples = response.examples.map((item) => {
1327
- const row = asRecord(item);
1328
- if (!row)
1329
- return item;
1330
- const anchor = buildToolFailureAnchor(row, toolName);
1331
- return { ...row, anchor };
1332
- });
1333
- return { ...response, examples };
1334
- }
1335
- function embeddedWindowSize(conversation) {
1336
- if (!conversation)
1337
- return null;
1338
- const ctx = asRecord(conversation.context);
1339
- if (!ctx)
1340
- return null;
1341
- const center = asNumber(ctx.center_index);
1342
- const from = asNumber(ctx.from_index);
1343
- const to = asNumber(ctx.to_index);
1344
- if (center === null || from === null || to === null)
1345
- return null;
1346
- return Math.min(center - from, to - center);
1347
- }
1348
- var DETECTION_CONTEXT_RADIUS = 2;
1349
- function withoutDetectionWindows(response, listKey, windowKey) {
1350
- const list = response[listKey];
1351
- if (!Array.isArray(list))
1352
- return response;
1353
- const rows = list.map((item) => {
1354
- const row = asRecord(item);
1355
- if (!row)
1356
- return item;
1357
- const { [windowKey]: _window, ...rest } = row;
1358
- const conversationId = asString(row.conversation_id);
1359
- const msgIndex = asNumber(asRecord(row.anchor)?.msg_index) ?? frustrationAnchorIndex(row);
1360
- return {
1361
- ...rest,
1362
- ...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex} --window=${DETECTION_CONTEXT_RADIUS}` } : {}
1363
- };
1364
- });
1365
- return { ...response, [listKey]: rows };
1366
- }
1367
- var PROBLEM_COMPACT_EVIDENCE = 5;
1368
- var PROBLEM_COMPACT_OMITTED_BLOCKS = [
1369
- "rubric",
1370
- "trend",
1371
- "sub_problems",
1372
- "affected_conversations",
1373
- "remainder",
1374
- "confidence",
1375
- "change_history",
1376
- "stories"
1377
- ];
1378
- function readCommand(command, purpose) {
1379
- return { command, purpose, mutability: "read", requires_approval: false };
1380
- }
1381
- function evidenceMsgIndex(row) {
1382
- return asNumber(row.anchor_msg_index) ?? asNumber(asRecord(asRecord(row.snippet_parsed)?.anchor)?.msg_index);
1383
- }
1384
- function compactProblemDossier(response, problemId) {
1385
- const id = shellWord(asString(response.problem_id) ?? problemId);
1386
- const evidence = (Array.isArray(response.evidence) ? response.evidence : []).map((item) => asRecord(item)).filter((row) => row !== null);
1387
- const topEvidence = evidence.slice(0, PROBLEM_COMPACT_EVIDENCE).map((row) => {
1388
- const conversationId = asString(row.conversation_id);
1389
- const msgIndex = evidenceMsgIndex(row);
1390
- return {
1391
- attribution_id: row.attribution_id ?? null,
1392
- conversation_id: conversationId,
1393
- msg_index: msgIndex,
1394
- signal_family: row.signal_family ?? null,
1395
- signal_type: row.signal_type ?? null,
1396
- confidence: row.confidence ?? null,
1397
- causal_rationale: row.causal_rationale ?? null,
1398
- door: row.door ?? null,
1399
- replay_verified: row.replay_verified ?? null,
1400
- ...conversationId && msgIndex !== null ? { context_command: `moda traces get ${shellWord(contextTarget(conversationId))} --msg-index=${msgIndex}` } : {}
1401
- };
1402
- });
1403
- const reportsBlock = asRecord(response.investigation_reports);
1404
- const reports = (Array.isArray(reportsBlock?.items) ? reportsBlock.items : []).map((item) => asRecord(item)).filter((row) => row !== null).map((row) => ({
1405
- report_id: row.report_id ?? null,
1406
- conversation_id: row.conversation_id ?? null,
1407
- mechanism: row.mechanism ?? null,
1408
- suspected_surface: row.suspected_surface ?? null,
1409
- severity: row.severity ?? null,
1410
- problem_found: row.problem_found ?? null
1411
- }));
1412
- const verification = asRecord(response.verification);
1413
- const { samples: _samples, ...verificationCounts } = verification ?? {};
1414
- const dossier = asRecord(response.dossier);
1415
- const { representative_story_keys: _storyKeys, ...dossierSummary } = dossier ?? {};
1416
- const trend = Array.isArray(response.trend) ? response.trend : [];
1417
- const data = {
1418
- found: response.found ?? true,
1419
- problem_id: response.problem_id ?? problemId,
1420
- requested_problem_id: response.requested_problem_id ?? problemId,
1421
- alias_resolved: response.alias_resolved ?? false,
1422
- view: "compact",
1423
- header: response.header ?? null,
1424
- dossier: dossier ? dossierSummary : null,
1425
- verification: verification ? verificationCounts : null,
1426
- ...response.impact_identity !== undefined ? { impact_identity: response.impact_identity } : {},
1427
- ...response.hierarchy !== undefined ? { hierarchy: response.hierarchy } : {},
1428
- ...trend.length > 0 ? { trend_latest: trend[trend.length - 1], trend_points: trend.length } : {},
1429
- top_evidence: topEvidence,
1430
- evidence_in_dossier: evidence.length,
1431
- reports,
1432
- omitted: {
1433
- evidence_rows: Math.max(0, evidence.length - topEvidence.length),
1434
- report_narratives: reports.length,
1435
- reports_has_more: reportsBlock?.has_more === true,
1436
- blocks: PROBLEM_COMPACT_OMITTED_BLOCKS.filter((key) => (key in response))
1437
- },
1438
- full_command: `moda problems get ${id} --full`
1439
- };
1440
- const nextCommands = [
1441
- readCommand(`moda problems get ${id} --evidence --limit=20`, "Page every evidence row with its anchor (follow next_cursor)."),
1442
- readCommand(`moda problems get ${id} --reports`, "Page investigation reports with narratives and citations."),
1443
- readCommand(`moda problems get ${id} --traces`, "Page affected traces with their latest anchor."),
1444
- readCommand(`moda problems get ${id} --full`, "The complete dossier, every block (large).")
1445
- ];
1446
- const first = topEvidence.find((row) => typeof row.context_command === "string");
1447
- if (first?.context_command) {
1448
- nextCommands.push(readCommand(first.context_command, "Read the trace around the top evidence anchor."));
1449
- }
1450
- return { data, nextCommands };
658
+ if (!/["$`\\!]/.test(value))
659
+ return `"${value}"`;
660
+ return `'${value.replace(/'/g, `'\\''`)}'`;
1451
661
  }
1452
662
 
1453
663
  // src/legacy-names.ts
@@ -1618,8 +828,8 @@ function canonicalCommandText(text) {
1618
828
 
1619
829
  // src/output.ts
1620
830
  import { randomUUID } from "node:crypto";
1621
- import { lstatSync, mkdirSync as mkdirSync2, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
1622
- import { tmpdir } from "node:os";
831
+ import { chmodSync as chmodSync2, lstatSync, mkdirSync as mkdirSync2, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
832
+ import { tmpdir, userInfo } from "node:os";
1623
833
  import { join as join2 } from "node:path";
1624
834
  var SUGGESTION_KEY = /(_commands?|^hints?|^suggestions?)$/i;
1625
835
  function canonicalDeep(value, rewriteStrings) {
@@ -1713,7 +923,7 @@ var CUSTOMER_CONTENT_KEYS = new Set([
1713
923
  "response"
1714
924
  ]);
1715
925
  function withProfileOnCommands(value, profile) {
1716
- const flag = `--profile=${shellWord(profile)}`;
926
+ const flag = `--profile=${shellArg(profile)}`;
1717
927
  const visit = (node, key, inCommandList) => {
1718
928
  const isCommandKey = key !== undefined && (GENERATED_COMMAND_KEY.test(key) || inCommandList && key === "command");
1719
929
  if (typeof node === "string")
@@ -1722,7 +932,7 @@ function withProfileOnCommands(value, profile) {
1722
932
  const list = key !== undefined && GENERATED_COMMAND_LIST_KEY.test(key);
1723
933
  return node.map((item) => typeof item === "string" ? visit(item, isCommandKey ? key : undefined, false) : visit(item, undefined, list));
1724
934
  }
1725
- const record = asRecord2(node);
935
+ const record = asRecord(node);
1726
936
  if (!record)
1727
937
  return node;
1728
938
  return Object.fromEntries(Object.entries(record).map(([entryKey, entry]) => [
@@ -2493,11 +1703,11 @@ function normalizeSummary(summary, command, status, data) {
2493
1703
  return inferSummary(command, status, data);
2494
1704
  }
2495
1705
  function inferSummary(command, status, data) {
2496
- const record = asRecord2(data);
1706
+ const record = asRecord(data);
2497
1707
  if (status === "error") {
2498
1708
  return { text: `${command} failed.`, confidence: "high" };
2499
1709
  }
2500
- const summary = asRecord2(record?.summary);
1710
+ const summary = asRecord(record?.summary);
2501
1711
  if (summary) {
2502
1712
  const statusValue = typeof summary.status === "string" ? summary.status : undefined;
2503
1713
  const fail = typeof summary.fail === "number" ? summary.fail : undefined;
@@ -2549,11 +1759,11 @@ function inferMutability(command) {
2549
1759
  return "read";
2550
1760
  }
2551
1761
  function inferWarnings(data) {
2552
- const record = asRecord2(data);
1762
+ const record = asRecord(data);
2553
1763
  const direct = record?.warnings;
2554
1764
  if (Array.isArray(direct))
2555
1765
  return direct.filter((item) => typeof item === "string");
2556
- const validation = asRecord2(record?.validation);
1766
+ const validation = asRecord(record?.validation);
2557
1767
  if (Array.isArray(validation?.warnings)) {
2558
1768
  return validation.warnings.filter((item) => typeof item === "string");
2559
1769
  }
@@ -2591,10 +1801,37 @@ function pruneAgentFullOutputs(dir, keep, now = Date.now()) {
2591
1801
  } catch {}
2592
1802
  }
2593
1803
  }
1804
+ function currentUserTag() {
1805
+ if (typeof process.getuid === "function")
1806
+ return String(process.getuid());
1807
+ try {
1808
+ return userInfo().username.replace(/[^A-Za-z0-9_.-]+/g, "_") || "user";
1809
+ } catch {
1810
+ return "user";
1811
+ }
1812
+ }
1813
+ function defaultAgentOutputDir() {
1814
+ return join2(tmpdir(), `moda-agent-output-${currentUserTag()}`);
1815
+ }
1816
+ function ensurePrivateDir(dir) {
1817
+ mkdirSync2(dir, { recursive: true, mode: 448 });
1818
+ const stat = lstatSync(dir);
1819
+ if (!stat.isDirectory())
1820
+ throw new Error(`${dir} is not a directory.`);
1821
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid()) {
1822
+ throw new Error(`${dir} is owned by another user.`);
1823
+ }
1824
+ if (process.platform !== "win32" && (stat.mode & 63) !== 0)
1825
+ chmodSync2(dir, 448);
1826
+ }
2594
1827
  function writeAgentFullOutput(command, data) {
2595
1828
  try {
2596
- const dir = process.env.MODA_AGENT_OUTPUT_DIR || join2(tmpdir(), "moda-agent-output");
2597
- mkdirSync2(dir, { recursive: true, mode: 448 });
1829
+ const configured = process.env.MODA_AGENT_OUTPUT_DIR;
1830
+ const dir = configured || defaultAgentOutputDir();
1831
+ if (configured)
1832
+ mkdirSync2(dir, { recursive: true, mode: 448 });
1833
+ else
1834
+ ensurePrivateDir(dir);
2598
1835
  const slug = command.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "output";
2599
1836
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
2600
1837
  const path = join2(dir, `${slug}-${stamp}-${randomUUID().slice(0, 8)}.json`);
@@ -2615,7 +1852,7 @@ function contentBlocksRedundant(message) {
2615
1852
  const hasToolResults = Array.isArray(message.tool_results) && message.tool_results.length > 0;
2616
1853
  const texts = [];
2617
1854
  for (const item of blocks) {
2618
- const block = asRecord2(item);
1855
+ const block = asRecord(item);
2619
1856
  if (!block)
2620
1857
  return false;
2621
1858
  if (block.type === "text") {
@@ -2647,7 +1884,7 @@ function shedRedundantContentBlocks(value) {
2647
1884
  visit(item);
2648
1885
  return;
2649
1886
  }
2650
- const record = asRecord2(node);
1887
+ const record = asRecord(node);
2651
1888
  if (!record)
2652
1889
  return;
2653
1890
  if ("content_blocks" in record && contentBlocksRedundant(record)) {
@@ -2669,17 +1906,17 @@ function continuationCommand(invocation, overrides) {
2669
1906
  const dashAt = invocation.positionals.findIndex((value) => value.startsWith("-"));
2670
1907
  const leading = dashAt === -1 ? invocation.positionals : invocation.positionals.slice(0, dashAt);
2671
1908
  const trailing = dashAt === -1 ? [] : invocation.positionals.slice(dashAt);
2672
- const parts = ["moda", invocation.command, ...leading.map((value) => shellWord(value))];
1909
+ const parts = ["moda", invocation.command, ...leading.map((value) => shellArg(value))];
2673
1910
  for (const [key, value] of Object.entries(invocation.flags)) {
2674
1911
  const flag = normalize2(key);
2675
1912
  if (overridden.has(flag) || CONTINUATION_SECRET_FLAGS.has(flag))
2676
1913
  continue;
2677
- parts.push(bare.has(flag) && value === "true" ? `--${flag}` : `--${flag}=${shellWord(String(value))}`);
1914
+ parts.push(bare.has(flag) && value === "true" ? `--${flag}` : `--${flag}=${shellArg(String(value))}`);
2678
1915
  }
2679
1916
  for (const [key, value] of Object.entries(overrides))
2680
- parts.push(`--${normalize2(key)}=${shellWord(String(value))}`);
1917
+ parts.push(`--${normalize2(key)}=${shellArg(String(value))}`);
2681
1918
  if (trailing.length > 0)
2682
- parts.push("--", ...trailing.map((value) => shellWord(value)));
1919
+ parts.push("--", ...trailing.map((value) => shellArg(value)));
2683
1920
  return canonicalCommandText(parts.join(" "));
2684
1921
  }
2685
1922
  var PAGED_LIST_BY_COMMAND = {
@@ -2700,7 +1937,7 @@ function rewritePagination(list, isTopLevel, invocation) {
2700
1937
  const { parent, array, originalLength } = list;
2701
1938
  if (!parent)
2702
1939
  return;
2703
- const nested = asRecord2(parent.pagination);
1940
+ const nested = asRecord(parent.pagination);
2704
1941
  const inline = !nested && !isTopLevel && (("has_more" in parent) || ("next_cursor" in parent)) ? parent : undefined;
2705
1942
  const pagination = nested ?? inline;
2706
1943
  if (!pagination)
@@ -2840,7 +2077,7 @@ function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
2840
2077
  fullOutputPath
2841
2078
  };
2842
2079
  }
2843
- const root = asRecord2(bounded);
2080
+ const root = asRecord(bounded);
2844
2081
  const notes = [];
2845
2082
  const nextCommands = [];
2846
2083
  for (const list of droppedLists.values()) {
@@ -2878,7 +2115,7 @@ function boundForAgent(data, maxBytes = agentMaxOutputBytes(), options = {}) {
2878
2115
  ...nextCommands.length > 0 ? { nextCommands } : {}
2879
2116
  };
2880
2117
  }
2881
- function asRecord2(value) {
2118
+ function asRecord(value) {
2882
2119
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
2883
2120
  }
2884
2121
  function extractSuggestedCommands(text) {
@@ -2886,29 +2123,4 @@ function extractSuggestedCommands(text) {
2886
2123
  return commands.length > 0 ? commands : undefined;
2887
2124
  }
2888
2125
 
2889
- // src/api-key-headers.ts
2890
- var PLATFORM_API_KEY_PREFIX = "moda_pk_";
2891
- var TENANT_HEADER = "x-moda-tenant-id";
2892
- function isPlatformApiKey(key) {
2893
- return Boolean(key?.startsWith(PLATFORM_API_KEY_PREFIX));
2894
- }
2895
- var platformTenantOverride;
2896
- function setPlatformTenantOverride(tenantId) {
2897
- platformTenantOverride = tenantId;
2898
- }
2899
- var TENANT_PATH = /\/tenants\/([^/?#]+)/;
2900
- function tenantFromUrl(url) {
2901
- const match = url ? TENANT_PATH.exec(url) : null;
2902
- return match ? decodeURIComponent(match[1]) : undefined;
2903
- }
2904
- function apiKeyHeaders(key, options = {}, url) {
2905
- const headers = { "x-api-key": key };
2906
- if (isPlatformApiKey(key)) {
2907
- const tenantId = tenantFromUrl(url) || platformTenantOverride || resolveTenantId(options);
2908
- if (tenantId)
2909
- headers[TENANT_HEADER] = tenantId;
2910
- }
2911
- return headers;
2912
- }
2913
-
2914
- export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, CliInputError, attachInputDetails, inputDetailsOf, CliAccessError, CliInputRequiredError, CliAuthError, CliNotFoundError, CliCancellationError, isNetworkLikeError, isApiLikeError, apiErrorStatus, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, validateConfig, isPlatformApiKey, setPlatformTenantOverride, apiKeyHeaders, recordApiRequest, getApiRequestCount, resetApiRequestCount, 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, BATCH_MAX_TARGETS, BATCH_CONCURRENCY, parseBatchTarget, collectBatchTargets, mapWithConcurrency, contextTarget, asRecord, asString, asNumber, frustrationAnchorIndex, frustrationsFromEmotions, withFrustrationAnchors, shellWord, trimToolFailureConversations, withToolFailureAnchors, embeddedWindowSize, withoutDetectionWindows, compactProblemDossier, LEGACY_NAMES, canonicalCommandText, setAgentUpdateNotice, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, outputModeFromFlags, resolveOutputMode, createCliOutput, agentEnvelopeFits, errorToAgentError, agentMaxOutputBytes, continuationCommand };
2126
+ export { PROD_BASE_URL, DEFAULT_INGEST_URL, getModaPaths, getModaDir, isLoopbackBaseUrl, describeBaseUrlSource, loadProfileConfig, saveProfileConfig, getActiveProfile, setActiveProfile, upsertProfile, clearStoredApiKeyCredentials, secretFilePathFromRef, resolveSecretReference, resolveApiKeyFromActiveProfile, resolveTenantIdFromActiveProfile, ensureSecretFilePermissions, writeSecretJsonFile, VERSION, MODA_BASE_URL, loadCliConfig, saveCliConfig, resolveApiKey, embeddedTenantIdFromApiKey, resolveIngestUrl2 as resolveIngestUrl, resolveTenantId, resolveApiTenantId, resolveModaBaseUrl, resolveModaBaseUrlStrict, validateConfig, isPlatformApiKey, setPlatformTenantOverride, apiKeyHeaders, recordApiRequest, getApiRequestCount, resetApiRequestCount, shellArg, LEGACY_NAMES, canonicalCommandText, setAgentUpdateNotice, EXIT_OK, EXIT_ERROR, EXIT_DEGRADED, EXIT_AUTH_REQUIRED, EXIT_INPUT_REQUIRED, EXIT_CANCELLED, AGENT_ERROR_CODES, wasOutputCancelled, safeWriteStdout, safeWriteStderr, explicitOutputMode, outputModeFromFlags, resolveOutputMode, createCliOutput, agentEnvelopeFits, errorToAgentError, agentMaxOutputBytes, continuationCommand };