harness-mcp-v2 3.2.19 → 3.2.21

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 (111) hide show
  1. package/README.md +146 -75
  2. package/build/client/harness-client.d.ts.map +1 -1
  3. package/build/client/harness-client.js +32 -6
  4. package/build/client/harness-client.js.map +1 -1
  5. package/build/config.d.ts +10 -0
  6. package/build/config.d.ts.map +1 -1
  7. package/build/config.js +15 -0
  8. package/build/config.js.map +1 -1
  9. package/build/data/schemas/v0/pipeline.d.ts.map +1 -1
  10. package/build/data/schemas/v0/pipeline.js +1388 -580
  11. package/build/data/schemas/v0/pipeline.js.map +1 -1
  12. package/build/data/schemas/v0/template.d.ts.map +1 -1
  13. package/build/data/schemas/v0/template.js +1142 -166
  14. package/build/data/schemas/v0/template.js.map +1 -1
  15. package/build/data/schemas/v1/pipeline.d.ts.map +1 -1
  16. package/build/data/schemas/v1/pipeline.js +302 -90
  17. package/build/data/schemas/v1/pipeline.js.map +1 -1
  18. package/build/data/schemas/v1/template.d.ts.map +1 -1
  19. package/build/data/schemas/v1/template.js +708 -488
  20. package/build/data/schemas/v1/template.js.map +1 -1
  21. package/build/prompts/deploy-ai-agent.d.ts +3 -0
  22. package/build/prompts/deploy-ai-agent.d.ts.map +1 -0
  23. package/build/prompts/deploy-ai-agent.js +300 -0
  24. package/build/prompts/deploy-ai-agent.js.map +1 -0
  25. package/build/prompts/feature-flag-rollout.d.ts.map +1 -1
  26. package/build/prompts/feature-flag-rollout.js +19 -8
  27. package/build/prompts/feature-flag-rollout.js.map +1 -1
  28. package/build/prompts/index.d.ts.map +1 -1
  29. package/build/prompts/index.js +2 -0
  30. package/build/prompts/index.js.map +1 -1
  31. package/build/registry/extractors.d.ts +87 -10
  32. package/build/registry/extractors.d.ts.map +1 -1
  33. package/build/registry/extractors.js +426 -26
  34. package/build/registry/extractors.js.map +1 -1
  35. package/build/registry/index.d.ts.map +1 -1
  36. package/build/registry/index.js +34 -15
  37. package/build/registry/index.js.map +1 -1
  38. package/build/registry/scope-utils.d.ts +45 -0
  39. package/build/registry/scope-utils.d.ts.map +1 -1
  40. package/build/registry/scope-utils.js +78 -0
  41. package/build/registry/scope-utils.js.map +1 -1
  42. package/build/registry/toolsets/chaos-descriptions.d.ts +72 -20
  43. package/build/registry/toolsets/chaos-descriptions.d.ts.map +1 -1
  44. package/build/registry/toolsets/chaos-descriptions.js +435 -140
  45. package/build/registry/toolsets/chaos-descriptions.js.map +1 -1
  46. package/build/registry/toolsets/chaos.d.ts.map +1 -1
  47. package/build/registry/toolsets/chaos.js +967 -193
  48. package/build/registry/toolsets/chaos.js.map +1 -1
  49. package/build/registry/toolsets/feature-flags.d.ts.map +1 -1
  50. package/build/registry/toolsets/feature-flags.js +888 -85
  51. package/build/registry/toolsets/feature-flags.js.map +1 -1
  52. package/build/registry/toolsets/governance.d.ts.map +1 -1
  53. package/build/registry/toolsets/governance.js +4 -0
  54. package/build/registry/toolsets/governance.js.map +1 -1
  55. package/build/registry/toolsets/iacm.d.ts.map +1 -1
  56. package/build/registry/toolsets/iacm.js +787 -11
  57. package/build/registry/toolsets/iacm.js.map +1 -1
  58. package/build/registry/toolsets/infrastructure.d.ts.map +1 -1
  59. package/build/registry/toolsets/infrastructure.js +30 -5
  60. package/build/registry/toolsets/infrastructure.js.map +1 -1
  61. package/build/registry/toolsets/pipelines.d.ts.map +1 -1
  62. package/build/registry/toolsets/pipelines.js +208 -5
  63. package/build/registry/toolsets/pipelines.js.map +1 -1
  64. package/build/registry/toolsets/release-management.d.ts +8 -0
  65. package/build/registry/toolsets/release-management.d.ts.map +1 -0
  66. package/build/registry/toolsets/release-management.js +783 -0
  67. package/build/registry/toolsets/release-management.js.map +1 -0
  68. package/build/registry/toolsets/sto.d.ts.map +1 -1
  69. package/build/registry/toolsets/sto.js +93 -17
  70. package/build/registry/toolsets/sto.js.map +1 -1
  71. package/build/registry/types.d.ts +52 -4
  72. package/build/registry/types.d.ts.map +1 -1
  73. package/build/resources/harness-schema.d.ts.map +1 -1
  74. package/build/resources/harness-schema.js +2 -1
  75. package/build/resources/harness-schema.js.map +1 -1
  76. package/build/tools/entity-schema/live.d.ts +6 -5
  77. package/build/tools/entity-schema/live.d.ts.map +1 -1
  78. package/build/tools/entity-schema/live.js +114 -10
  79. package/build/tools/entity-schema/live.js.map +1 -1
  80. package/build/tools/entity-schema/types.d.ts +7 -1
  81. package/build/tools/entity-schema/types.d.ts.map +1 -1
  82. package/build/tools/harness-create.d.ts.map +1 -1
  83. package/build/tools/harness-create.js +5 -1
  84. package/build/tools/harness-create.js.map +1 -1
  85. package/build/tools/harness-delete.d.ts.map +1 -1
  86. package/build/tools/harness-delete.js +5 -1
  87. package/build/tools/harness-delete.js.map +1 -1
  88. package/build/tools/harness-execute.d.ts.map +1 -1
  89. package/build/tools/harness-execute.js +15 -9
  90. package/build/tools/harness-execute.js.map +1 -1
  91. package/build/tools/harness-get.d.ts.map +1 -1
  92. package/build/tools/harness-get.js +5 -1
  93. package/build/tools/harness-get.js.map +1 -1
  94. package/build/tools/harness-list.d.ts.map +1 -1
  95. package/build/tools/harness-list.js +18 -2
  96. package/build/tools/harness-list.js.map +1 -1
  97. package/build/tools/harness-schema.d.ts +2 -1
  98. package/build/tools/harness-schema.d.ts.map +1 -1
  99. package/build/tools/harness-schema.js +10 -8
  100. package/build/tools/harness-schema.js.map +1 -1
  101. package/build/tools/harness-update.d.ts.map +1 -1
  102. package/build/tools/harness-update.js +5 -1
  103. package/build/tools/harness-update.js.map +1 -1
  104. package/build/tools/index.js +1 -1
  105. package/build/tools/index.js.map +1 -1
  106. package/build/utils/url-parser.d.ts +2 -0
  107. package/build/utils/url-parser.d.ts.map +1 -1
  108. package/build/utils/url-parser.js +55 -0
  109. package/build/utils/url-parser.js.map +1 -1
  110. package/npm-shrinkwrap.json +2 -2
  111. package/package.json +1 -1
@@ -43,6 +43,17 @@ export const pageExtract = (raw) => {
43
43
  total: r.data?.totalElements ?? r.data?.totalItems ?? 0,
44
44
  };
45
45
  };
46
+ /**
47
+ * Spring Data page at the response root (no NG `{ data }` envelope):
48
+ * `{ content, totalElements }`. Used by Release Management (RMG) list APIs.
49
+ */
50
+ export const springPageExtract = (raw) => {
51
+ const r = raw;
52
+ return {
53
+ items: Array.isArray(r.content) ? r.content : [],
54
+ total: typeof r.totalElements === "number" ? r.totalElements : 0,
55
+ };
56
+ };
46
57
  /** Extract `data` from NG API and wrap primitive values in an object for structuredContent compatibility. */
47
58
  export const countExtract = (raw) => {
48
59
  const r = raw;
@@ -125,8 +136,8 @@ export const stoExemptionsExtract = (raw, input) => {
125
136
  };
126
137
  };
127
138
  /**
128
- * STO SAST remediation DiffOccurrences
129
- * (`GET /sto/api/v2/sast-remediation/diff-occurrences`).
139
+ * STO Remediation DiffOccurrences
140
+ * (`GET /sto/api/v2/remediation-agent/diff-occurrences`).
130
141
  *
131
142
  * API shape:
132
143
  * { validationScanId, existingOccurrences: [...], newOccurrences: [...],
@@ -136,7 +147,7 @@ export const stoExemptionsExtract = (raw, input) => {
136
147
  * tell still-present vs newly introduced occurrences without two list calls.
137
148
  * Fingerprint is not on the wire (Diff matching is server-side only).
138
149
  */
139
- export const stoSastRemediationDiffExtract = (raw) => {
150
+ export const stoRemediationDiffExtract = (raw) => {
140
151
  if (raw === null || raw === undefined || typeof raw !== "object")
141
152
  return raw;
142
153
  const r = raw;
@@ -779,18 +790,53 @@ export const chaosK8sInfraListExtract = (raw) => {
779
790
  total: r.totalNoOfInfrastructures ?? (Array.isArray(r.infras) ? r.infras.length : 0),
780
791
  };
781
792
  };
782
- // Read a well-known input value from the inputs[] slice by name.
783
- const readLoadtestInput = (inputs, name) => (inputs ?? []).find((i) => i?.name === name)?.value;
793
+ // Pick the tool-specific sub-object out of toolConfig; loadTestManager also
794
+ // accepts flat toolConfig at the root for forward-compat, so fall back to that.
795
+ const pickToolBlock = (toolType, toolConfig) => {
796
+ if (!toolConfig)
797
+ return undefined;
798
+ const key = typeof toolType === "string" ? toolType.toLowerCase() : "";
799
+ const nested = key
800
+ ? toolConfig[key]
801
+ : undefined;
802
+ return nested ?? toolConfig;
803
+ };
804
+ const readTunable = (tc, name) => {
805
+ if (!tc)
806
+ return undefined;
807
+ const tunables = tc.tunables;
808
+ return tunables?.[name];
809
+ };
810
+ const readScriptField = (tc, name) => {
811
+ if (!tc)
812
+ return undefined;
813
+ const script = tc.script;
814
+ return script?.[name];
815
+ };
816
+ // K6 rpsLimit has two historical storage locations: tunables.rpsLimit (current
817
+ // authoring surface) and options.rpsLimit (legacy/UI-authored path — see
818
+ // loadTestManager k6.go LoadOptions.RPSLimit). Mirror the backend's own
819
+ // dispatch-time fallback precedence (loadtest_handlers.go) so read-side scalars
820
+ // stay truthful regardless of which shape a given record was persisted with.
821
+ const readK6RpsLimit = (tc) => {
822
+ const fromTunables = readTunable(tc, "rpsLimit");
823
+ if (fromTunables != null && fromTunables !== 0)
824
+ return fromTunables;
825
+ const options = tc?.options;
826
+ const fromOptions = options?.rpsLimit;
827
+ if (fromOptions != null && fromOptions !== 0)
828
+ return fromOptions;
829
+ return undefined;
830
+ };
784
831
  /**
785
- * Project a single load test (InternalApiLoadTestResponse) to a stable shape.
832
+ * Project a single load test (LoadTestResponse) to a stable shape.
786
833
  *
787
- * The new wire format carries every recognised tunable inside `inputs[]`
788
- * (TemplateInput) and user-defined non-secrets inside `variables[]`
789
- * (TemplateVariable). We pass both arrays through as-is for fidelity AND
790
- * surface derived convenience scalars (target_url / users / duration_sec / ...)
791
- * that mirror the create-side LLM surface so round-trips are intuitive.
834
+ * Since the variables migration, all tunables and custom env vars live under
835
+ * toolConfig.<tool>.tunables / toolConfig.<tool>.variables (see LocustSpec /
836
+ * K6Spec / JMeterSpec in loadTestManager/internal/domain). We pass toolConfig
837
+ * through as-is for fidelity AND surface derived convenience scalars mirroring
838
+ * the create-side LLM surface so round-trips are intuitive.
792
839
  *
793
- * Drops the large base64 scriptContent and backend user-detail envelopes.
794
840
  * Mirrors `identity` into `loadtestId` so the deep-link resolver fills
795
841
  * {loadtestId} instead of falling back to the name.
796
842
  */
@@ -798,35 +844,64 @@ export const chaosLoadTestExtract = (raw) => {
798
844
  if (!raw || typeof raw !== "object" || Array.isArray(raw))
799
845
  return raw;
800
846
  const t = raw;
801
- const inputs = Array.isArray(t.inputs) ? t.inputs : undefined;
802
- const variables = Array.isArray(t.variables) ? t.variables : undefined;
847
+ const toolConfig = t.toolConfig;
848
+ const toolBlock = pickToolBlock(t.toolType, toolConfig);
849
+ const variables = toolBlock?.variables;
850
+ const envVars = toolBlock?.envVars;
803
851
  return {
804
852
  loadtestId: t.identity,
853
+ uniqueId: t.uniqueId,
854
+ parentUniqueId: t.parentUniqueId,
805
855
  identity: t.identity,
806
856
  name: t.name,
807
857
  description: t.description,
808
858
  tags: t.tags,
859
+ serviceReferences: t.serviceReferences,
809
860
  environmentIdentifier: t.environmentIdentifier,
810
861
  infraIdentifier: t.infraIdentifier,
862
+ infraType: t.infraType,
811
863
  targetType: t.targetType,
812
864
  toolType: t.toolType,
813
865
  scriptSource: t.scriptSource,
814
- // Canonical slices — passed through as-is for fidelity.
815
- inputs,
866
+ importType: t.importType,
867
+ templateReference: t.templateReference,
868
+ templateUpdateAvailable: t.templateUpdateAvailable,
869
+ latestRevisionIdentifier: t.latestRevisionIdentifier,
870
+ maxDurationSec: t.maxDurationSec,
871
+ cleanupPolicy: t.cleanupPolicy,
872
+ resources: t.resources,
873
+ // Canonical tool config — passed through as-is for fidelity.
874
+ toolConfig,
816
875
  variables,
817
- yaml: t.yaml,
818
- // Derived convenience scalars (read-side mirror of the create-side LLM surface).
819
- target_url: readLoadtestInput(inputs, "targetUrl"),
820
- users: readLoadtestInput(inputs, "targetUsers"),
821
- duration_sec: readLoadtestInput(inputs, "durationSeconds"),
822
- ramp_up_sec: readLoadtestInput(inputs, "rampUpTimeSec"),
823
- worker_count: readLoadtestInput(inputs, "workerCount"),
824
- script_image: readLoadtestInput(inputs, "scriptImage"),
825
- script_entrypoint: readLoadtestInput(inputs, "scriptEntrypoint"),
826
- load_args: readLoadtestInput(inputs, "loadArgs"),
876
+ envVars,
877
+ // Denormalised display strings the backend emits for table renders.
878
+ targetUsersDisplay: t.targetUsers,
879
+ durationSecondsDisplay: t.durationSeconds,
880
+ // Derived convenience scalars (typed tunables under toolConfig.<tool>.tunables).
881
+ target_url: readTunable(toolBlock, "targetUrl"),
882
+ users: readTunable(toolBlock, "targetUsers"),
883
+ duration_sec: readTunable(toolBlock, "durationSeconds"),
884
+ ramp_up_sec: readTunable(toolBlock, "rampUpTimeSec"),
885
+ worker_count: readTunable(toolBlock, "workerCount"),
886
+ spawn_rate: readTunable(toolBlock, "spawnRate"),
887
+ host_url: readTunable(toolBlock, "hostUrl"),
888
+ iterations: readTunable(toolBlock, "iterations"),
889
+ rps_limit: readK6RpsLimit(toolBlock),
890
+ // Derived convenience scalars from toolConfig.<tool>.script (image mode).
891
+ script_image: readScriptField(toolBlock, "image"),
892
+ script_entrypoint: readScriptField(toolBlock, "entrypoint"),
893
+ load_args: readScriptField(toolBlock, "loadArgs"),
894
+ image_pull_secret: readScriptField(toolBlock, "imagePullSecret"),
895
+ recentRuns: t.recentRuns,
896
+ isSampleTest: t.isSampleTest,
897
+ isApiGenerated: t.isApiGenerated,
898
+ isAIUsed: t.isAIUsed,
827
899
  lastExecuted: t.lastExecuted,
828
900
  createdAt: t.createdAt,
901
+ createdBy: t.createdBy,
829
902
  updatedAt: t.updatedAt,
903
+ updatedBy: t.updatedBy,
904
+ yaml: t.yaml,
830
905
  };
831
906
  };
832
907
  /**
@@ -838,6 +913,16 @@ export const chaosLoadTestListExtract = (raw) => {
838
913
  const items = (r.items ?? []).map(chaosLoadTestExtract);
839
914
  return { items, total: r.pagination?.totalItems ?? items.length };
840
915
  };
916
+ /**
917
+ * Chaos Service (v3) list response: { data: [...], correlationID, pagination: { index, limit, totalPages, totalItems } }.
918
+ * Distinct from chaos_loadtest's { items, pagination } envelope — the v3 chaos-services API
919
+ * wraps the array under `data` per shared PaginationResponse contract.
920
+ */
921
+ export const chaosServiceListExtract = (raw) => {
922
+ const r = raw;
923
+ const items = r.data ?? [];
924
+ return { items, total: r.pagination?.totalItems ?? items.length };
925
+ };
841
926
  /**
842
927
  * Extract chaos hub list response: { items: [...], pagination: { totalItems } }
843
928
  */
@@ -906,6 +991,20 @@ export function flattenTrafficType(item) {
906
991
  }
907
992
  }
908
993
  }
994
+ /**
995
+ * Public v4 paginated lists (`EnvironmentListResponse`, `TrafficTypeListResponse` / `RolloutStatusListResponse`):
996
+ * `{ data, limit, offset, totalCount }`. Promote `data`→`items` and `totalCount`→`total`
997
+ * so harness_list compact/output schema see a full total, not the current page length.
998
+ */
999
+ export const fmeV4PaginatedListExtract = (raw) => {
1000
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
1001
+ return raw;
1002
+ const r = raw;
1003
+ if (!Array.isArray(r.data))
1004
+ return raw;
1005
+ const total = typeof r.totalCount === "number" ? r.totalCount : r.data.length;
1006
+ return { ...r, items: r.data, total };
1007
+ };
909
1008
  /** Extract FME feature flag list — passthrough with trafficType.id flattened on each item. */
910
1009
  export const fmeListExtract = (raw) => {
911
1010
  if (raw && typeof raw === "object") {
@@ -928,4 +1027,305 @@ export const fmeGetExtract = (raw) => {
928
1027
  }
929
1028
  return raw;
930
1029
  };
1030
+ // ── Release Management (RMG) ──────────────────────────────────────────────
1031
+ const RMG_API_PREFIX = "/api";
1032
+ export const RMG_DEFAULT_DAYS_BACK = 30;
1033
+ export const RMG_MAX_DAYS_BACK = 365;
1034
+ /** Days of look-ahead included in the window so scheduled releases stay visible. */
1035
+ export const RMG_DAYS_FORWARD = 7;
1036
+ const RMG_DAY_MS = 86_400_000;
1037
+ export const RMG_DEFAULT_TASK_LIMIT = 50;
1038
+ export const RMG_MAX_TASK_LIMIT = 100;
1039
+ function rmgClampInt(value, min, max, fallback) {
1040
+ const n = typeof value === "number" ? value : Number(value);
1041
+ if (!Number.isFinite(n))
1042
+ return fallback;
1043
+ return Math.min(max, Math.max(min, Math.trunc(n)));
1044
+ }
1045
+ function rmgAsRecord(value) {
1046
+ return value !== null && typeof value === "object" ? value : {};
1047
+ }
1048
+ /** First key holding an array — RMG list payloads vary by gateway version. */
1049
+ function rmgFirstArrayAt(source, keys) {
1050
+ for (const key of keys) {
1051
+ const value = source[key];
1052
+ if (Array.isArray(value))
1053
+ return value;
1054
+ }
1055
+ return undefined;
1056
+ }
1057
+ /**
1058
+ * Copy HARNESS_ORG / HARNESS_PROJECT into input when scopeOptional resources
1059
+ * omit explicit org_id/project_id (same pattern as release list preflight).
1060
+ */
1061
+ export async function releaseFillScopeFromConfig({ input, registry }) {
1062
+ if (!input.org_id && registry.orgId)
1063
+ input.org_id = registry.orgId;
1064
+ if (!input.project_id && registry.projectId)
1065
+ input.project_id = registry.projectId;
1066
+ }
1067
+ /**
1068
+ * Resolve the effective scope and time window before the body/query are built.
1069
+ * The registry only merges HARNESS_ORG/HARNESS_PROJECT into `input` when the
1070
+ * caller passes `resource_scope`, but the body always needs them.
1071
+ */
1072
+ export async function releaseListPreflight(ctx) {
1073
+ await releaseFillScopeFromConfig(ctx);
1074
+ const { input } = ctx;
1075
+ const now = Date.now();
1076
+ const daysBack = rmgClampInt(input.days_back, 1, RMG_MAX_DAYS_BACK, RMG_DEFAULT_DAYS_BACK);
1077
+ if (input.start_ts === undefined || input.start_ts === "") {
1078
+ input.start_ts = now - daysBack * RMG_DAY_MS;
1079
+ }
1080
+ if (input.end_ts === undefined || input.end_ts === "") {
1081
+ input.end_ts = now + RMG_DAYS_FORWARD * RMG_DAY_MS;
1082
+ }
1083
+ }
1084
+ /** POST /api/release/list — scope travels in the body as `scopes`, not as query params. */
1085
+ export function releaseListBody(input) {
1086
+ const scope = {};
1087
+ if (typeof input.org_id === "string" && input.org_id)
1088
+ scope.orgIdentifier = input.org_id;
1089
+ if (typeof input.project_id === "string" && input.project_id) {
1090
+ scope.projectIdentifier = input.project_id;
1091
+ }
1092
+ return { scopes: Object.keys(scope).length > 0 ? [scope] : [] };
1093
+ }
1094
+ /**
1095
+ * Normalize the list payload and apply the optional status filter client-side
1096
+ * (the endpoint takes no status param). Status matching is case-insensitive
1097
+ * because RMG returns both `Running` and `RUNNING` depending on the field.
1098
+ */
1099
+ export function releaseListExtract(raw, input) {
1100
+ const root = rmgAsRecord(raw);
1101
+ const payload = Array.isArray(raw)
1102
+ ? { content: raw }
1103
+ : rmgFirstArrayAt(root, ["content", "releases", "items"])
1104
+ ? root
1105
+ : rmgAsRecord(root.data);
1106
+ let items = rmgFirstArrayAt(payload, ["content", "releases", "items"]) ?? [];
1107
+ const pageItemCount = items.length;
1108
+ const requestedStatus = typeof input?.status === "string" ? input.status.trim() : "";
1109
+ if (requestedStatus) {
1110
+ const wanted = requestedStatus.toLowerCase();
1111
+ items = items.filter((rel) => {
1112
+ const status = rel.status ?? rel.releaseStatus;
1113
+ return typeof status === "string" && status.toLowerCase() === wanted;
1114
+ });
1115
+ }
1116
+ const total = ["totalElements", "totalItems", "totalCount", "total"]
1117
+ .map((key) => payload[key])
1118
+ .find((value) => typeof value === "number");
1119
+ return {
1120
+ items,
1121
+ total: requestedStatus ? items.length : (total ?? items.length),
1122
+ ...(requestedStatus
1123
+ ? {
1124
+ status_filter: requestedStatus,
1125
+ _hint: "status is filtered client-side on this page only; total is the filtered count for this page, " +
1126
+ `not the account (${pageItemCount} unfiltered items on this page). ` +
1127
+ "Increment page (keep size and other filters) if matches may exist on later pages.",
1128
+ }
1129
+ : {}),
1130
+ };
1131
+ }
1132
+ /** Flatten GET /api/release/{id} → { release: releaseInfo, ... }. */
1133
+ export function releaseGetExtract(raw) {
1134
+ const r = raw;
1135
+ if (r.releaseInfo && typeof r.releaseInfo === "object") {
1136
+ return { release: r.releaseInfo };
1137
+ }
1138
+ return raw;
1139
+ }
1140
+ function rmgSplitCsvFilter(value) {
1141
+ if (typeof value !== "string" || !value.trim())
1142
+ return undefined;
1143
+ const parts = value.split(",").map((s) => s.trim()).filter(Boolean);
1144
+ return parts.length > 0 ? parts : undefined;
1145
+ }
1146
+ /** Normalize sort/status/type filters before query param mapping (GET list). */
1147
+ export function normalizeReleaseActivityExecutionInput(input) {
1148
+ const sortField = typeof input.sort_field === "string" && input.sort_field.length > 0
1149
+ ? input.sort_field
1150
+ : "start_ts";
1151
+ const sortDirection = input.sort_direction === "asc" ? "asc" : "desc";
1152
+ input.sort = [sortField, sortDirection];
1153
+ const statusParts = rmgSplitCsvFilter(input.status);
1154
+ if (statusParts)
1155
+ input.status = statusParts;
1156
+ const typeParts = rmgSplitCsvFilter(input.activity_type);
1157
+ if (typeParts)
1158
+ input.activity_type = typeParts;
1159
+ }
1160
+ export function normalizeReleaseTaskLimit(input) {
1161
+ if (input.limit === undefined && input.size !== undefined) {
1162
+ input.limit = input.size;
1163
+ }
1164
+ input.limit = rmgClampInt(input.limit, 1, RMG_MAX_TASK_LIMIT, RMG_DEFAULT_TASK_LIMIT);
1165
+ }
1166
+ /** GET /api/release/{id}/tasks → standard list envelope with cursor pagination. */
1167
+ export function releaseTaskListExtract(raw, input) {
1168
+ const r = raw;
1169
+ const items = Array.isArray(r.tasks) ? r.tasks : [];
1170
+ const limit = rmgClampInt(input?.limit ?? input?.size, 1, RMG_MAX_TASK_LIMIT, RMG_DEFAULT_TASK_LIMIT);
1171
+ return {
1172
+ items,
1173
+ total: items.length,
1174
+ limit_applied: limit,
1175
+ status_filter: typeof input?.status === "string" && input.status ? input.status : "all",
1176
+ pagination: {
1177
+ cursor: typeof input?.cursor === "string" ? input.cursor : undefined,
1178
+ next_cursor: r.nextRequest?.cursor,
1179
+ last: r.last,
1180
+ },
1181
+ };
1182
+ }
1183
+ /** GET /api/release/{id}/execution/activities — Spring page at root. */
1184
+ export function releaseActivityExecutionListExtract(raw) {
1185
+ const r = raw;
1186
+ const items = Array.isArray(r.content) ? r.content : [];
1187
+ return {
1188
+ items,
1189
+ total: typeof r.totalElements === "number" ? r.totalElements : items.length,
1190
+ pagination: {
1191
+ page: r.number,
1192
+ size: r.size,
1193
+ total_pages: r.totalPages,
1194
+ total_elements: r.totalElements,
1195
+ number_of_elements: r.numberOfElements,
1196
+ first: r.first,
1197
+ last: r.last,
1198
+ },
1199
+ };
1200
+ }
1201
+ function requireReleaseAndPhase(input) {
1202
+ const releaseId = input.release_id;
1203
+ const phaseId = input.phase_identifier;
1204
+ if (typeof releaseId !== "string" || !releaseId) {
1205
+ throw new Error("release_id is required");
1206
+ }
1207
+ if (typeof phaseId !== "string" || !phaseId) {
1208
+ throw new Error("phase_identifier is required — pass via params on harness_get (from harness_list release_execution_phase)");
1209
+ }
1210
+ return { releaseId, phaseId };
1211
+ }
1212
+ export function releaseExecutionPhaseOutputPath(input) {
1213
+ const { releaseId, phaseId } = requireReleaseAndPhase(input);
1214
+ const enc = encodeURIComponent;
1215
+ return `${RMG_API_PREFIX}/orchestration/execution/release/${enc(releaseId)}/phase/${enc(phaseId)}/output`;
1216
+ }
1217
+ export function releaseExecutionPhaseInputPath(input) {
1218
+ const { releaseId, phaseId } = requireReleaseAndPhase(input);
1219
+ const enc = encodeURIComponent;
1220
+ return `${RMG_API_PREFIX}/orchestration/execution/release/${enc(releaseId)}/phase/${enc(phaseId)}/input`;
1221
+ }
1222
+ export function releaseExecutionActivityOutputPath(input) {
1223
+ const { releaseId, phaseId } = requireReleaseAndPhase(input);
1224
+ const activityId = input.activity_identifier;
1225
+ if (typeof activityId !== "string" || !activityId) {
1226
+ throw new Error("activity_identifier is required — pass via params on harness_get (from harness_list release_execution_activity)");
1227
+ }
1228
+ const enc = encodeURIComponent;
1229
+ return `${RMG_API_PREFIX}/orchestration/execution/release/${enc(releaseId)}/phase/${enc(phaseId)}/activity/${enc(activityId)}/output`;
1230
+ }
1231
+ function releasePhaseIoGetExtract(raw, input, field) {
1232
+ const r = raw;
1233
+ const items = Array.isArray(r[field]) ? r[field] : [];
1234
+ return {
1235
+ [field]: items,
1236
+ phase_identifier: input?.phase_identifier,
1237
+ [`total_${field}`]: items.length,
1238
+ };
1239
+ }
1240
+ export function releasePhaseOutputGetExtract(raw, input) {
1241
+ return releasePhaseIoGetExtract(raw, input, "outputs");
1242
+ }
1243
+ export function releasePhaseInputGetExtract(raw, input) {
1244
+ return releasePhaseIoGetExtract(raw, input, "inputs");
1245
+ }
1246
+ export function releaseActivityOutputGetExtract(raw, input) {
1247
+ const r = raw;
1248
+ const items = Array.isArray(r.outputs) ? r.outputs : [];
1249
+ return {
1250
+ outputs: items,
1251
+ phase_identifier: input?.phase_identifier,
1252
+ activity_identifier: input?.activity_identifier,
1253
+ total_outputs: items.length,
1254
+ };
1255
+ }
1256
+ export function releaseActivityInputGetExtract(raw) {
1257
+ const r = raw;
1258
+ const items = Array.isArray(r.inputs) ? r.inputs : [];
1259
+ return {
1260
+ inputs: items,
1261
+ total_inputs: items.length,
1262
+ };
1263
+ }
1264
+ export function releaseInputGetExtract(raw) {
1265
+ const r = raw;
1266
+ return {
1267
+ release_id: r.release_id,
1268
+ process_execution_id: r.process_execution_id,
1269
+ yaml: r.yaml,
1270
+ };
1271
+ }
1272
+ /** GET /api/orchestration/execution/{id}/phases → standard list envelope. */
1273
+ export function releasePhaseListExtract(raw) {
1274
+ const r = raw;
1275
+ const items = Array.isArray(r.phases) ? r.phases : [];
1276
+ return {
1277
+ items,
1278
+ total: items.length,
1279
+ ...(r.release_id ? { release_id: r.release_id } : {}),
1280
+ ...(typeof r.total_running_phases === "number" ? { total_running_phases: r.total_running_phases } : {}),
1281
+ };
1282
+ }
1283
+ /** Project RMG process/activity YAML entity responses to a stable agent shape. */
1284
+ export function rmgYamlEntityExtract(raw) {
1285
+ if (!isRecord(raw))
1286
+ return raw;
1287
+ const gitDetails = raw.git_details ?? raw.gitDetails;
1288
+ const out = {};
1289
+ if (typeof raw.identifier === "string")
1290
+ out.identifier = raw.identifier;
1291
+ if (typeof raw.name === "string")
1292
+ out.name = raw.name;
1293
+ if (typeof raw.yaml === "string")
1294
+ out.yaml = raw.yaml;
1295
+ if (typeof raw.orgIdentifier === "string")
1296
+ out.orgIdentifier = raw.orgIdentifier;
1297
+ if (typeof raw.projectIdentifier === "string")
1298
+ out.projectIdentifier = raw.projectIdentifier;
1299
+ if (gitDetails !== undefined)
1300
+ out.git_details = gitDetails;
1301
+ if (Object.keys(out).length > 0)
1302
+ return out;
1303
+ return raw;
1304
+ }
1305
+ /** Normalize RMG delete responses to a stable confirmation shape. */
1306
+ export function rmgYamlEntityDeleteExtract(raw) {
1307
+ if (raw === null || raw === undefined)
1308
+ return { deleted: true };
1309
+ if (!isRecord(raw))
1310
+ return { deleted: true };
1311
+ const out = { deleted: true };
1312
+ if (typeof raw.identifier === "string")
1313
+ out.identifier = raw.identifier;
1314
+ if (raw.status !== undefined)
1315
+ out.status = raw.status;
1316
+ return out;
1317
+ }
1318
+ export function yamlWriteBody(input) {
1319
+ const b = input.body;
1320
+ if (!b || typeof b !== "object") {
1321
+ throw new Error("body is required and must be an object with yaml (and optional git_details)");
1322
+ }
1323
+ if (typeof b.yaml !== "string" || b.yaml.length === 0) {
1324
+ throw new Error("body.yaml is required (non-empty YAML string)");
1325
+ }
1326
+ const out = { yaml: b.yaml };
1327
+ if (b.git_details !== undefined)
1328
+ out.git_details = b.git_details;
1329
+ return out;
1330
+ }
931
1331
  //# sourceMappingURL=extractors.js.map