@styx-api/core 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -8,20 +8,20 @@ function findDeepName$1(node) {
8
8
  if (name) return name;
9
9
  }
10
10
  }
11
- function isObject$2(x) {
11
+ function isObject$3(x) {
12
12
  return typeof x === "object" && x !== null && !Array.isArray(x);
13
13
  }
14
- function isString$2(x) {
14
+ function isString$3(x) {
15
15
  return typeof x === "string";
16
16
  }
17
17
  function isNumber$1(x) {
18
18
  return typeof x === "number";
19
19
  }
20
- function isArray$2(x) {
20
+ function isArray$3(x) {
21
21
  return Array.isArray(x);
22
22
  }
23
23
  function isArgparseMarker(x) {
24
- return isObject$2(x) && isString$2(x.__argparse__);
24
+ return isObject$3(x) && isString$3(x.__argparse__);
25
25
  }
26
26
  function isSuppressed(x) {
27
27
  return isArgparseMarker(x) && x.__argparse__ === "SUPPRESS";
@@ -55,7 +55,7 @@ var ArgdumpParser = class {
55
55
  this.error(e instanceof SyntaxError ? e.message : "Invalid JSON");
56
56
  return null;
57
57
  }
58
- if (!isObject$2(parsed)) {
58
+ if (!isObject$3(parsed)) {
59
59
  this.error("JSON source is not an object");
60
60
  return null;
61
61
  }
@@ -63,11 +63,11 @@ var ArgdumpParser = class {
63
63
  }
64
64
  buildAppMeta(descriptor) {
65
65
  const prog = descriptor.prog;
66
- if (!isString$2(prog)) return void 0;
66
+ if (!isString$3(prog)) return void 0;
67
67
  let id = prog || void 0;
68
68
  if (!id) {
69
69
  const desc = descriptor.description;
70
- if (isString$2(desc)) {
70
+ if (isString$3(desc)) {
71
71
  const match = desc.match(/^(\S+)/);
72
72
  if (match) id = match[1].replace(/[:;,]+$/, "") || void 0;
73
73
  }
@@ -77,8 +77,8 @@ var ArgdumpParser = class {
77
77
  const epilog = descriptor.epilog;
78
78
  let versionStr;
79
79
  const actions = descriptor.actions;
80
- if (isArray$2(actions)) {
81
- for (const action of actions) if (isObject$2(action) && action.action_type === "version" && isString$2(action.version)) {
80
+ if (isArray$3(actions)) {
81
+ for (const action of actions) if (isObject$3(action) && action.action_type === "version" && isString$3(action.version)) {
82
82
  versionStr = action.version.replace(/%%?\(prog\)s\s*/g, "").trim();
83
83
  if (!versionStr) versionStr = void 0;
84
84
  break;
@@ -87,9 +87,9 @@ var ArgdumpParser = class {
87
87
  return {
88
88
  id,
89
89
  ...versionStr && { version: versionStr },
90
- ...(isString$2(description) || isString$2(epilog)) && { doc: {
91
- ...isString$2(description) && { description },
92
- ...isString$2(epilog) && { comment: epilog }
90
+ ...(isString$3(description) || isString$3(epilog)) && { doc: {
91
+ ...isString$3(description) && { description },
92
+ ...isString$3(epilog) && { comment: epilog }
93
93
  } }
94
94
  };
95
95
  }
@@ -97,9 +97,9 @@ var ArgdumpParser = class {
97
97
  const typeInfo = action.type_info;
98
98
  const fileTypeInfo = action.file_type_info;
99
99
  const choices = action.choices;
100
- if (isArray$2(choices) && choices.length > 0) {
100
+ if (isArray$3(choices) && choices.length > 0) {
101
101
  const alts = [];
102
- for (const choice of choices) if (isString$2(choice) || isNumber$1(choice)) alts.push({
102
+ for (const choice of choices) if (isString$3(choice) || isNumber$1(choice)) alts.push({
103
103
  kind: "literal",
104
104
  attrs: { str: String(choice) }
105
105
  });
@@ -110,13 +110,13 @@ var ArgdumpParser = class {
110
110
  attrs: { alts }
111
111
  };
112
112
  }
113
- if (isObject$2(fileTypeInfo)) return {
113
+ if (isObject$3(fileTypeInfo)) return {
114
114
  kind: "path",
115
115
  attrs: {}
116
116
  };
117
- if (isObject$2(typeInfo)) {
117
+ if (isObject$3(typeInfo)) {
118
118
  const name = typeInfo.name;
119
- if (!isString$2(name)) return this.inferFromSamples(action) ?? {
119
+ if (!isString$3(name)) return this.inferFromSamples(action) ?? {
120
120
  kind: "str",
121
121
  attrs: {}
122
122
  };
@@ -165,7 +165,7 @@ var ArgdumpParser = class {
165
165
  };
166
166
  }
167
167
  resolveByModule(mod) {
168
- if (!isString$2(mod)) return null;
168
+ if (!isString$3(mod)) return null;
169
169
  if (mod === "pathlib" || mod === "os.path" || mod.includes("path")) return {
170
170
  kind: "path",
171
171
  attrs: {}
@@ -180,7 +180,7 @@ var ArgdumpParser = class {
180
180
  inferFromSamples(action) {
181
181
  const samples = [];
182
182
  const def = action.default;
183
- if (isArray$2(def)) samples.push(...def);
183
+ if (isArray$3(def)) samples.push(...def);
184
184
  else samples.push(def);
185
185
  samples.push(action.const);
186
186
  let sawNumber = false;
@@ -249,10 +249,10 @@ var ArgdumpParser = class {
249
249
  const dest = action.dest;
250
250
  const help = action.help;
251
251
  const defaultVal = action.default;
252
- const name = this.preferredName(action) ?? (isString$2(dest) ? dest : void 0);
252
+ const name = this.preferredName(action) ?? (isString$3(dest) ? dest : void 0);
253
253
  const hasName = name !== void 0;
254
- const hasHelp = isString$2(help) && !isSuppressed(help);
255
- const hasDefault = (isString$2(defaultVal) || isNumber$1(defaultVal) || typeof defaultVal === "boolean") && !isSuppressed(defaultVal);
254
+ const hasHelp = isString$3(help) && !isSuppressed(help);
255
+ const hasDefault = (isString$3(defaultVal) || isNumber$1(defaultVal) || typeof defaultVal === "boolean") && !isSuppressed(defaultVal);
256
256
  if (!hasName && !hasHelp && !hasDefault) return void 0;
257
257
  return {
258
258
  ...hasName && { name },
@@ -262,20 +262,20 @@ var ArgdumpParser = class {
262
262
  }
263
263
  getOptionFlag(action) {
264
264
  const optionStrings = action.option_strings;
265
- if (!isArray$2(optionStrings) || optionStrings.length === 0) return null;
266
- for (const opt of optionStrings) if (isString$2(opt) && opt.startsWith("--")) return opt;
265
+ if (!isArray$3(optionStrings) || optionStrings.length === 0) return null;
266
+ for (const opt of optionStrings) if (isString$3(opt) && opt.startsWith("--")) return opt;
267
267
  const first = optionStrings[0];
268
- return isString$2(first) ? first : null;
268
+ return isString$3(first) ? first : null;
269
269
  }
270
270
  /** Prefer the first --long flag (without leading dashes) over dest. */
271
271
  preferredName(action) {
272
272
  const optionStrings = action.option_strings;
273
- if (!isArray$2(optionStrings)) return void 0;
274
- for (const opt of optionStrings) if (isString$2(opt) && opt.startsWith("--") && opt.length > 2) return opt.slice(2);
273
+ if (!isArray$3(optionStrings)) return void 0;
274
+ for (const opt of optionStrings) if (isString$3(opt) && opt.startsWith("--") && opt.length > 2) return opt.slice(2);
275
275
  }
276
276
  isPositional(action) {
277
277
  const optionStrings = action.option_strings;
278
- return !isArray$2(optionStrings) || optionStrings.length === 0;
278
+ return !isArray$3(optionStrings) || optionStrings.length === 0;
279
279
  }
280
280
  buildAction(action) {
281
281
  const actionType = action.action_type ?? "store";
@@ -293,7 +293,7 @@ var ArgdumpParser = class {
293
293
  case "help":
294
294
  case "version": return null;
295
295
  case "unknown":
296
- this.warn(`Unknown action type for '${action.dest}'` + (isString$2(action.custom_action_class) ? ` (custom class: ${action.custom_action_class})` : "") + ", treating as store");
296
+ this.warn(`Unknown action type for '${action.dest}'` + (isString$3(action.custom_action_class) ? ` (custom class: ${action.custom_action_class})` : "") + ", treating as store");
297
297
  return this.buildStore(action);
298
298
  default:
299
299
  this.warn(`Unrecognized action_type '${actionType}' for '${action.dest}', skipping`);
@@ -405,20 +405,20 @@ var ArgdumpParser = class {
405
405
  }
406
406
  buildBooleanOptional(action) {
407
407
  const optionStrings = action.option_strings;
408
- if (!isArray$2(optionStrings) || optionStrings.length === 0) {
408
+ if (!isArray$3(optionStrings) || optionStrings.length === 0) {
409
409
  this.error(`boolean_optional action '${action.dest}' has no option strings`);
410
410
  return null;
411
411
  }
412
412
  let posFlag = null;
413
413
  let negFlag = null;
414
414
  for (const opt of optionStrings) {
415
- if (!isString$2(opt)) continue;
415
+ if (!isString$3(opt)) continue;
416
416
  if (opt.startsWith("--no-")) negFlag = opt;
417
417
  else if (opt.startsWith("--")) posFlag = opt;
418
418
  }
419
419
  if (!posFlag) {
420
- posFlag = isString$2(optionStrings[0]) ? optionStrings[0] : null;
421
- negFlag = isString$2(optionStrings[1]) ? optionStrings[1] : null;
420
+ posFlag = isString$3(optionStrings[0]) ? optionStrings[0] : null;
421
+ negFlag = isString$3(optionStrings[1]) ? optionStrings[1] : null;
422
422
  }
423
423
  if (!posFlag) {
424
424
  this.error(`boolean_optional action '${action.dest}' has no valid option strings`);
@@ -545,14 +545,14 @@ var ArgdumpParser = class {
545
545
  }
546
546
  buildSubparsers(action) {
547
547
  const subparsers = action.subparsers;
548
- if (!isObject$2(subparsers)) {
548
+ if (!isObject$3(subparsers)) {
549
549
  this.error(`parsers action '${action.dest}' has no subparsers`);
550
550
  return null;
551
551
  }
552
- const aliases = isObject$2(action.subparsers_aliases) ? action.subparsers_aliases : {};
552
+ const aliases = isObject$3(action.subparsers_aliases) ? action.subparsers_aliases : {};
553
553
  const alts = [];
554
554
  for (const [name, parserInfo] of Object.entries(subparsers)) {
555
- if (!isObject$2(parserInfo)) {
555
+ if (!isObject$3(parserInfo)) {
556
556
  this.warn(`Skipping non-object subparser '${name}'`);
557
557
  continue;
558
558
  }
@@ -566,8 +566,8 @@ var ArgdumpParser = class {
566
566
  }, ...subExpr.attrs.nodes] }
567
567
  };
568
568
  const subAliases = aliases[name];
569
- const aliasDoc = isArray$2(subAliases) && subAliases.length > 0 ? ` (aliases: ${subAliases.filter(isString$2).join(", ")})` : "";
570
- const description = isString$2(parserInfo.description) ? parserInfo.description : void 0;
569
+ const aliasDoc = isArray$3(subAliases) && subAliases.length > 0 ? ` (aliases: ${subAliases.filter(isString$3).join(", ")})` : "";
570
+ const description = isString$3(parserInfo.description) ? parserInfo.description : void 0;
571
571
  const docStr = description ? description + aliasDoc : aliasDoc ? aliasDoc.slice(1) : void 0;
572
572
  seq.meta = {
573
573
  name,
@@ -600,11 +600,11 @@ var ArgdumpParser = class {
600
600
  applyMutualExclusion(actionsByDest, groups, nodes, nodesByDest) {
601
601
  const excluded = /* @__PURE__ */ new Set();
602
602
  for (const group of groups) {
603
- if (!isObject$2(group)) continue;
603
+ if (!isObject$3(group)) continue;
604
604
  const groupActions = group.actions;
605
- if (!isArray$2(groupActions)) continue;
605
+ if (!isArray$3(groupActions)) continue;
606
606
  const memberDests = [];
607
- for (const dest of groupActions) if (isString$2(dest) && nodesByDest.has(dest)) memberDests.push(dest);
607
+ for (const dest of groupActions) if (isString$3(dest) && nodesByDest.has(dest)) memberDests.push(dest);
608
608
  if (memberDests.length < 2) continue;
609
609
  const altMembers = [];
610
610
  for (const dest of memberDests) {
@@ -634,7 +634,7 @@ var ArgdumpParser = class {
634
634
  kind: "optional",
635
635
  attrs: { node: alt }
636
636
  };
637
- const groupName = (isString$2(group.title) ? group.title : void 0) ?? (memberDests.length === 2 ? `${memberDests[0]}_or_${memberDests[1]}` : `${memberDests[0]}_choice`);
637
+ const groupName = (isString$3(group.title) ? group.title : void 0) ?? (memberDests.length === 2 ? `${memberDests[0]}_or_${memberDests[1]}` : `${memberDests[0]}_choice`);
638
638
  groupNode.meta = {
639
639
  ...groupNode.meta,
640
640
  name: groupName
@@ -651,7 +651,7 @@ var ArgdumpParser = class {
651
651
  }
652
652
  parseParserInfo(descriptor) {
653
653
  const actions = descriptor.actions;
654
- if (!isArray$2(actions)) return {
654
+ if (!isArray$3(actions)) return {
655
655
  kind: "sequence",
656
656
  attrs: { nodes: [] }
657
657
  };
@@ -660,11 +660,11 @@ var ArgdumpParser = class {
660
660
  const actionsByDest = /* @__PURE__ */ new Map();
661
661
  const nodesByDest = /* @__PURE__ */ new Map();
662
662
  for (const rawAction of actions) {
663
- if (!isObject$2(rawAction)) continue;
663
+ if (!isObject$3(rawAction)) continue;
664
664
  const node = this.buildAction(rawAction);
665
665
  if (!node) continue;
666
666
  const dest = rawAction.dest;
667
- if (isString$2(dest)) {
667
+ if (isString$3(dest)) {
668
668
  actionsByDest.set(dest, rawAction);
669
669
  nodesByDest.set(dest, node);
670
670
  }
@@ -673,7 +673,7 @@ var ArgdumpParser = class {
673
673
  }
674
674
  let allNodes = [...positionals, ...optionals];
675
675
  const mutexGroups = descriptor.mutually_exclusive_groups;
676
- if (isArray$2(mutexGroups) && mutexGroups.length > 0) allNodes = this.applyMutualExclusion(actionsByDest, mutexGroups, allNodes, nodesByDest);
676
+ if (isArray$3(mutexGroups) && mutexGroups.length > 0) allNodes = this.applyMutualExclusion(actionsByDest, mutexGroups, allNodes, nodesByDest);
677
677
  return {
678
678
  kind: "sequence",
679
679
  attrs: { nodes: allNodes }
@@ -715,7 +715,7 @@ var ArgdumpParser = class {
715
715
  };
716
716
  }
717
717
  const prog = descriptor.prog;
718
- if (isString$2(prog) && prog) expr.attrs.nodes.unshift({
718
+ if (isString$3(prog) && prog) expr.attrs.nodes.unshift({
719
719
  kind: "literal",
720
720
  attrs: { str: prog }
721
721
  });
@@ -847,16 +847,16 @@ function boutiquesSplitCommand(command) {
847
847
 
848
848
  //#endregion
849
849
  //#region src/frontend/boutiques/parser.ts
850
- function isObject$1(x) {
850
+ function isObject$2(x) {
851
851
  return typeof x === "object" && x !== null && !Array.isArray(x);
852
852
  }
853
- function isString$1(x) {
853
+ function isString$2(x) {
854
854
  return typeof x === "string";
855
855
  }
856
856
  function isNumber(x) {
857
857
  return typeof x === "number";
858
858
  }
859
- function isArray$1(x) {
859
+ function isArray$2(x) {
860
860
  return Array.isArray(x);
861
861
  }
862
862
  var InputTypePrimitive = /* @__PURE__ */ function(InputTypePrimitive) {
@@ -898,7 +898,7 @@ var BoutiquesParser = class {
898
898
  this.error(e instanceof SyntaxError ? e.message : "Invalid JSON");
899
899
  return null;
900
900
  }
901
- if (!isObject$1(parsed)) {
901
+ if (!isObject$2(parsed)) {
902
902
  this.error("JSON source is not an object");
903
903
  return null;
904
904
  }
@@ -910,9 +910,9 @@ var BoutiquesParser = class {
910
910
  this.error(`type is missing for input: '${btInput.id}'`);
911
911
  return null;
912
912
  }
913
- if (isObject$1(btType)) return InputTypePrimitive.SubCommand;
914
- if (isArray$1(btType)) return InputTypePrimitive.SubCommandUnion;
915
- const typeName = isString$1(btType) ? btType : String(btType);
913
+ if (isObject$2(btType)) return InputTypePrimitive.SubCommand;
914
+ if (isArray$2(btType)) return InputTypePrimitive.SubCommandUnion;
915
+ const typeName = isString$2(btType) ? btType : String(btType);
916
916
  switch (typeName) {
917
917
  case "String": return InputTypePrimitive.String;
918
918
  case "File": return InputTypePrimitive.File;
@@ -951,43 +951,43 @@ var BoutiquesParser = class {
951
951
  const title = btInput.name;
952
952
  const description = btInput.description;
953
953
  const defaultValue = btInput["default-value"];
954
- const hasDefault = isString$1(defaultValue) || isNumber(defaultValue) || typeof defaultValue === "boolean";
955
- if (!isString$1(name) && !isString$1(title) && !isString$1(description) && !hasDefault) return;
954
+ const hasDefault = isString$2(defaultValue) || isNumber(defaultValue) || typeof defaultValue === "boolean";
955
+ if (!isString$2(name) && !isString$2(title) && !isString$2(description) && !hasDefault) return;
956
956
  return {
957
- ...isString$1(name) && { name },
958
- ...(isString$1(title) || isString$1(description)) && { doc: {
959
- ...isString$1(title) && { title },
960
- ...isString$1(description) && { description }
957
+ ...isString$2(name) && { name },
958
+ ...(isString$2(title) || isString$2(description)) && { doc: {
959
+ ...isString$2(title) && { title },
960
+ ...isString$2(description) && { description }
961
961
  } },
962
962
  ...hasDefault && { defaultValue }
963
963
  };
964
964
  }
965
965
  buildStreamMeta(bt) {
966
966
  const id = bt.id;
967
- if (!isString$1(id)) return void 0;
967
+ if (!isString$2(id)) return void 0;
968
968
  const name = bt.name;
969
969
  const description = bt.description;
970
970
  return {
971
971
  name: id,
972
- ...(isString$1(name) || isString$1(description)) && { doc: {
973
- ...isString$1(name) && { title: name },
974
- ...isString$1(description) && { description }
972
+ ...(isString$2(name) || isString$2(description)) && { doc: {
973
+ ...isString$2(name) && { title: name },
974
+ ...isString$2(description) && { description }
975
975
  } }
976
976
  };
977
977
  }
978
978
  buildOutput(out, lookup, idOptional) {
979
979
  const id = out.id;
980
- if (!isString$1(id)) {
980
+ if (!isString$2(id)) {
981
981
  this.error("output-files entry missing id");
982
982
  return null;
983
983
  }
984
984
  const template = out["path-template"];
985
- if (!isString$1(template)) {
985
+ if (!isString$2(template)) {
986
986
  this.error(`output-files entry '${id}' missing path-template`);
987
987
  return null;
988
988
  }
989
989
  const stripRaw = out["path-template-stripped-extensions"];
990
- const stripExtensions = isArray$1(stripRaw) && stripRaw.every(isString$1) && stripRaw.length > 0 ? stripRaw : void 0;
990
+ const stripExtensions = isArray$2(stripRaw) && stripRaw.every(isString$2) && stripRaw.length > 0 ? stripRaw : void 0;
991
991
  const tokens = destructTemplate(template, lookup).map((part) => {
992
992
  if (typeof part === "string") return {
993
993
  kind: "literal",
@@ -1006,9 +1006,9 @@ var BoutiquesParser = class {
1006
1006
  name: id,
1007
1007
  tokens
1008
1008
  };
1009
- if (isString$1(title) || isString$1(description)) output.doc = {
1010
- ...isString$1(title) && { title },
1011
- ...isString$1(description) && { description }
1009
+ if (isString$2(title) || isString$2(description)) output.doc = {
1010
+ ...isString$2(title) && { title },
1011
+ ...isString$2(description) && { description }
1012
1012
  };
1013
1013
  return { output };
1014
1014
  }
@@ -1019,18 +1019,18 @@ var BoutiquesParser = class {
1019
1019
  */
1020
1020
  attachOutputs(rootSeq, bt) {
1021
1021
  const outputFiles = bt["output-files"];
1022
- if (!isArray$1(outputFiles)) return;
1022
+ if (!isArray$2(outputFiles)) return;
1023
1023
  const lookup = {};
1024
1024
  const idOptional = /* @__PURE__ */ new Set();
1025
1025
  const inputs = bt["inputs"];
1026
- if (isArray$1(inputs)) {
1027
- for (const input of inputs) if (isObject$1(input) && isString$1(input["value-key"]) && isString$1(input.id)) {
1026
+ if (isArray$2(inputs)) {
1027
+ for (const input of inputs) if (isObject$2(input) && isString$2(input["value-key"]) && isString$2(input.id)) {
1028
1028
  lookup[input["value-key"]] = nodeRef(input.id);
1029
1029
  if (input.optional === true) idOptional.add(input.id);
1030
1030
  }
1031
1031
  }
1032
1032
  for (const out of outputFiles) {
1033
- if (!isObject$1(out)) {
1033
+ if (!isObject$2(out)) {
1034
1034
  this.warn("Skipping non-object output-files entry");
1035
1035
  continue;
1036
1036
  }
@@ -1042,7 +1042,7 @@ var BoutiquesParser = class {
1042
1042
  }
1043
1043
  buildAppMeta(bt) {
1044
1044
  const id = bt.id ?? bt.name;
1045
- if (!isString$1(id)) return void 0;
1045
+ if (!isString$2(id)) return void 0;
1046
1046
  const name = bt.name;
1047
1047
  const description = bt.description;
1048
1048
  const version = bt["tool-version"];
@@ -1052,26 +1052,26 @@ var BoutiquesParser = class {
1052
1052
  const stdout = bt["stdout-output"];
1053
1053
  const stderr = bt["stderr-output"];
1054
1054
  const doc = {
1055
- ...isString$1(name) && { title: name },
1056
- ...isString$1(description) && { description },
1057
- ...isString$1(author) && { authors: [author] },
1058
- ...isString$1(url) && { urls: [url] }
1055
+ ...isString$2(name) && { title: name },
1056
+ ...isString$2(description) && { description },
1057
+ ...isString$2(author) && { authors: [author] },
1058
+ ...isString$2(url) && { urls: [url] }
1059
1059
  };
1060
1060
  return {
1061
1061
  id,
1062
- ...isString$1(version) && { version },
1062
+ ...isString$2(version) && { version },
1063
1063
  ...Object.keys(doc).length > 0 && { doc },
1064
- ...isObject$1(container) && isString$1(container.image) && { container: {
1064
+ ...isObject$2(container) && isString$2(container.image) && { container: {
1065
1065
  image: container.image,
1066
- ...isString$1(container.type) && { type: container.type }
1066
+ ...isString$2(container.type) && { type: container.type }
1067
1067
  } },
1068
- ...isObject$1(stdout) && { stdout: this.buildStreamMeta(stdout) },
1069
- ...isObject$1(stderr) && { stderr: this.buildStreamMeta(stderr) }
1068
+ ...isObject$2(stdout) && { stdout: this.buildStreamMeta(stdout) },
1069
+ ...isObject$2(stderr) && { stderr: this.buildStreamMeta(stderr) }
1070
1070
  };
1071
1071
  }
1072
1072
  buildEnumAlternative(choices, meta) {
1073
1073
  const alts = [];
1074
- for (const choice of choices) if (isString$1(choice)) alts.push({
1074
+ for (const choice of choices) if (isString$2(choice)) alts.push({
1075
1075
  kind: "literal",
1076
1076
  attrs: { str: choice }
1077
1077
  });
@@ -1092,7 +1092,7 @@ var BoutiquesParser = class {
1092
1092
  const meta = this.buildNodeMeta(btInput);
1093
1093
  if (inputType.isEnum) {
1094
1094
  const choices = btInput["value-choices"];
1095
- if (!isArray$1(choices)) {
1095
+ if (!isArray$2(choices)) {
1096
1096
  this.error(`Invalid value-choices for '${btInput.id}'`);
1097
1097
  return null;
1098
1098
  }
@@ -1146,7 +1146,7 @@ var BoutiquesParser = class {
1146
1146
  }
1147
1147
  case InputTypePrimitive.Flag: {
1148
1148
  const flag = btInput["command-line-flag"];
1149
- if (!isString$1(flag)) {
1149
+ if (!isString$2(flag)) {
1150
1150
  this.error(`Flag input '${btInput.id}' missing command-line-flag`);
1151
1151
  return null;
1152
1152
  }
@@ -1164,7 +1164,7 @@ var BoutiquesParser = class {
1164
1164
  }
1165
1165
  case InputTypePrimitive.SubCommand: {
1166
1166
  const nested = btInput.type;
1167
- if (!isObject$1(nested)) {
1167
+ if (!isObject$2(nested)) {
1168
1168
  this.error(`Invalid subcommand type for '${btInput.id}'`);
1169
1169
  return null;
1170
1170
  }
@@ -1174,14 +1174,14 @@ var BoutiquesParser = class {
1174
1174
  }
1175
1175
  case InputTypePrimitive.SubCommandUnion: {
1176
1176
  const alts = btInput.type;
1177
- if (!isArray$1(alts)) {
1177
+ if (!isArray$2(alts)) {
1178
1178
  this.error(`Invalid subcommand union type for '${btInput.id}'`);
1179
1179
  return null;
1180
1180
  }
1181
1181
  const parsedAlts = [];
1182
1182
  const usedTags = /* @__PURE__ */ new Set();
1183
1183
  for (const alt of alts) {
1184
- if (!isObject$1(alt)) {
1184
+ if (!isObject$2(alt)) {
1185
1185
  this.warn("Skipping non-object subcommand alternative");
1186
1186
  continue;
1187
1187
  }
@@ -1233,7 +1233,7 @@ var BoutiquesParser = class {
1233
1233
  kind: "repeat",
1234
1234
  attrs: {
1235
1235
  node,
1236
- ...isString$1(btInput["list-separator"]) && { join: btInput["list-separator"] },
1236
+ ...isString$2(btInput["list-separator"]) && { join: btInput["list-separator"] },
1237
1237
  ...isNumber(btInput["min-list-entries"]) && { countMin: btInput["min-list-entries"] },
1238
1238
  ...isNumber(btInput["max-list-entries"]) && { countMax: btInput["max-list-entries"] }
1239
1239
  }
@@ -1241,7 +1241,7 @@ var BoutiquesParser = class {
1241
1241
  }
1242
1242
  wrapWithFlag(node, btInput) {
1243
1243
  const flag = btInput["command-line-flag"];
1244
- if (!isString$1(flag)) return node;
1244
+ if (!isString$2(flag)) return node;
1245
1245
  return {
1246
1246
  kind: "sequence",
1247
1247
  attrs: { nodes: [{
@@ -1288,11 +1288,11 @@ var BoutiquesParser = class {
1288
1288
  parseDescriptor(bt) {
1289
1289
  const inputs = bt["inputs"];
1290
1290
  const inputsLookup = /* @__PURE__ */ new Map();
1291
- if (isArray$1(inputs)) {
1292
- for (const input of inputs) if (isObject$1(input) && isString$1(input["value-key"])) inputsLookup.set(input["value-key"], input);
1291
+ if (isArray$2(inputs)) {
1292
+ for (const input of inputs) if (isObject$2(input) && isString$2(input["value-key"])) inputsLookup.set(input["value-key"], input);
1293
1293
  }
1294
1294
  const commandLine = bt["command-line"];
1295
- const segments = isString$1(commandLine) ? this.parseCommandLineTemplate(commandLine, inputsLookup) : [];
1295
+ const segments = isString$2(commandLine) ? this.parseCommandLineTemplate(commandLine, inputsLookup) : [];
1296
1296
  const rootSeq = {
1297
1297
  kind: "sequence",
1298
1298
  attrs: { nodes: [] }
@@ -1305,7 +1305,7 @@ var BoutiquesParser = class {
1305
1305
  join: ""
1306
1306
  }
1307
1307
  };
1308
- for (const elem of segment) if (isObject$1(elem)) {
1308
+ for (const elem of segment) if (isObject$2(elem)) {
1309
1309
  const inputType = this.getInputType(elem);
1310
1310
  if (inputType === null) continue;
1311
1311
  let node = this.buildTerminal(elem, inputType);
@@ -1477,6 +1477,382 @@ function camelCase(s) {
1477
1477
  return pascal.charAt(0).toLowerCase() + pascal.slice(1);
1478
1478
  }
1479
1479
 
1480
+ //#endregion
1481
+ //#region src/frontend/mrtrix/parser.ts
1482
+ function isObject$1(x) {
1483
+ return typeof x === "object" && x !== null && !Array.isArray(x);
1484
+ }
1485
+ function isString$1(x) {
1486
+ return typeof x === "string";
1487
+ }
1488
+ function isArray$1(x) {
1489
+ return Array.isArray(x);
1490
+ }
1491
+ /** Coerce a possibly-missing list field to an array. */
1492
+ function asArray$1(x) {
1493
+ return isArray$1(x) ? x : [];
1494
+ }
1495
+ /** Input file types: each maps to a plain path terminal. */
1496
+ const INPUT_TYPES = new Set([
1497
+ "file in",
1498
+ "image in",
1499
+ "directory in",
1500
+ "tracks in"
1501
+ ]);
1502
+ /** Output file types: each maps to a `str` (the user-supplied filename) + an `Output`. */
1503
+ const OUTPUT_TYPES = new Set([
1504
+ "file out",
1505
+ "image out",
1506
+ "directory out",
1507
+ "tracks out"
1508
+ ]);
1509
+ /** A fresh empty root sequence for error returns (IR passes mutate in place,
1510
+ * so callers must not share a single instance). */
1511
+ function emptyExpr$1() {
1512
+ return {
1513
+ kind: "sequence",
1514
+ attrs: { nodes: [] }
1515
+ };
1516
+ }
1517
+ /**
1518
+ * Parser for MRtrix3 C++ command definitions (`mrtrix.json`), as dumped by the
1519
+ * `__print_usage_json__` hook (see niwrap `extraction/mrtrix/`).
1520
+ *
1521
+ * The format is flat: positional `arguments`, plus `option_groups[].options`
1522
+ * where each option is a single-dash switch (`-id`) carrying 0..N positional
1523
+ * `arguments`. There are no unions, conditionals, or nested options. We lower
1524
+ * it onto the same `Expr` shapes the Boutiques/Workbench parsers emit:
1525
+ *
1526
+ * <command> <positionals...> [-option ...] ...
1527
+ *
1528
+ * - argument (input type) -> typed terminal
1529
+ * - argument (`* out`) -> str terminal + an Output entry referencing it
1530
+ * - option, 0 args -> opt(lit(-switch)) (bool flag)
1531
+ * - option, 1 arg -> opt(seq(lit(-switch), value)) (flat optional)
1532
+ * - option, >1 arg / multi -> opt|rep(seq(lit(-switch), ...)) (sub-struct)
1533
+ *
1534
+ * Type mapping mirrors the v1 `mrt2bt.js` converter's `set_type`. The dump does
1535
+ * not carry choice values, so a `choice` argument degrades to a plain string
1536
+ * (as it did in v1). Per-command quirks v1 hand-coded that the flat dump cannot
1537
+ * express (e.g. dwi2fod/mtnormalise paired in/out args) are intentionally NOT
1538
+ * special-cased here - they are patched on the niwrap side post-dump, keeping
1539
+ * this frontend format-general.
1540
+ */
1541
+ var MrtrixParser = class {
1542
+ name = "mrtrix";
1543
+ extensions = ["json"];
1544
+ errors = [];
1545
+ warnings = [];
1546
+ usedNames = /* @__PURE__ */ new Set();
1547
+ reset() {
1548
+ this.errors = [];
1549
+ this.warnings = [];
1550
+ this.usedNames = /* @__PURE__ */ new Set();
1551
+ }
1552
+ /** Reserve a unique sibling name, suffixing `_2`, `_3`, ... on collision. */
1553
+ uniqueName(base) {
1554
+ if (!this.usedNames.has(base)) {
1555
+ this.usedNames.add(base);
1556
+ return base;
1557
+ }
1558
+ let n = 2;
1559
+ while (this.usedNames.has(`${base}_${n}`)) n++;
1560
+ const name = `${base}_${n}`;
1561
+ this.usedNames.add(name);
1562
+ this.warn(`Duplicate id '${base}'; renamed a sibling binding to '${name}'`);
1563
+ return name;
1564
+ }
1565
+ error(message, location) {
1566
+ this.errors.push({
1567
+ message,
1568
+ location
1569
+ });
1570
+ }
1571
+ warn(message, location) {
1572
+ this.warnings.push({
1573
+ message,
1574
+ location
1575
+ });
1576
+ }
1577
+ parseJSON(source) {
1578
+ let parsed;
1579
+ try {
1580
+ parsed = JSON.parse(source);
1581
+ } catch (e) {
1582
+ this.error(e instanceof SyntaxError ? e.message : "Invalid JSON");
1583
+ return null;
1584
+ }
1585
+ if (!isObject$1(parsed)) {
1586
+ this.error("JSON source is not an object");
1587
+ return null;
1588
+ }
1589
+ return parsed;
1590
+ }
1591
+ docFrom(description) {
1592
+ return isString$1(description) && description.length > 0 ? { description } : void 0;
1593
+ }
1594
+ buildAppMeta(cmd) {
1595
+ const name = cmd.name;
1596
+ if (!isString$1(name) || name.length === 0) {
1597
+ this.error("MRtrix descriptor missing required 'name' string");
1598
+ return;
1599
+ }
1600
+ const synopsis = cmd.synopsis;
1601
+ const paragraphs = asArray$1(cmd.description).filter(isString$1).join("\n\n");
1602
+ const author = cmd.author;
1603
+ const references = asArray$1(cmd.references).filter(isString$1);
1604
+ const version = cmd.version;
1605
+ const doc = {
1606
+ ...isString$1(synopsis) && synopsis.length > 0 && { title: synopsis },
1607
+ ...paragraphs.length > 0 && { description: paragraphs },
1608
+ ...isString$1(author) && author.length > 0 && { authors: [author] },
1609
+ ...references.length > 0 && { literature: references },
1610
+ urls: [`https://mrtrix.readthedocs.io/en/latest/reference/commands/${name}.html`]
1611
+ };
1612
+ return {
1613
+ id: name,
1614
+ ...isString$1(version) && version.length > 0 && { version },
1615
+ doc
1616
+ };
1617
+ }
1618
+ /**
1619
+ * Lower one MRtrix argument to its terminal node (carrying name + doc) and,
1620
+ * for output types, an accompanying `Output`.
1621
+ */
1622
+ buildArgTerminal(arg, meta) {
1623
+ const argType = arg.type;
1624
+ if (!isString$1(argType)) {
1625
+ this.error(`MRtrix argument '${String(arg.id)}' missing 'type'`);
1626
+ return null;
1627
+ }
1628
+ const name = meta.name;
1629
+ switch (argType) {
1630
+ case "integer": return { node: int(meta) };
1631
+ case "float": return { node: float(meta) };
1632
+ case "text":
1633
+ case "choice":
1634
+ case "undefined": return { node: str(meta) };
1635
+ case "int seq": {
1636
+ const node = repJoin(",", int());
1637
+ node.meta = meta;
1638
+ return { node };
1639
+ }
1640
+ case "float seq": {
1641
+ const node = repJoin(",", float());
1642
+ node.meta = meta;
1643
+ return { node };
1644
+ }
1645
+ case "various": {
1646
+ const stringArm = seq(str({ name: "obj" }));
1647
+ stringArm.meta = {
1648
+ name: "VariousString",
1649
+ variantTag: "VariousString"
1650
+ };
1651
+ const fileArm = seq(path({ name: "obj" }));
1652
+ fileArm.meta = {
1653
+ name: "VariousFile",
1654
+ variantTag: "VariousFile"
1655
+ };
1656
+ const node = alt(stringArm, fileArm);
1657
+ node.meta = meta;
1658
+ return { node };
1659
+ }
1660
+ default:
1661
+ if (INPUT_TYPES.has(argType)) return { node: path(meta) };
1662
+ if (OUTPUT_TYPES.has(argType)) return {
1663
+ node: str(meta),
1664
+ output: {
1665
+ name,
1666
+ ...meta.doc && { doc: meta.doc },
1667
+ tokens: [{
1668
+ kind: "ref",
1669
+ target: nodeRef(name)
1670
+ }],
1671
+ mediaTypes: [`mrtrix/${argType}`]
1672
+ }
1673
+ };
1674
+ this.error(`Unknown MRtrix type '${argType}' for '${name}'`);
1675
+ return null;
1676
+ }
1677
+ }
1678
+ /**
1679
+ * A positional argument: typed terminal, wrapped for cardinality. Output-type
1680
+ * positionals push their `Output` onto the root's `outputs` collector.
1681
+ */
1682
+ buildPositional(arg, rootOutputs) {
1683
+ if (!isObject$1(arg)) {
1684
+ this.warn("Skipping non-object argument");
1685
+ return null;
1686
+ }
1687
+ const id = arg.id;
1688
+ if (!isString$1(id)) {
1689
+ this.error("MRtrix argument missing 'id'");
1690
+ return null;
1691
+ }
1692
+ const meta = {
1693
+ name: this.uniqueName(snakeCase(id)),
1694
+ ...this.docMeta(arg.description)
1695
+ };
1696
+ const built = this.buildArgTerminal(arg, meta);
1697
+ if (!built) return null;
1698
+ if (built.output) rootOutputs.push(built.output);
1699
+ return this.applyCardinality(built.node, arg, built.output !== void 0);
1700
+ }
1701
+ /** Wrap a terminal for `allow_multiple` (repeat) and `optional` (optional). */
1702
+ applyCardinality(node, arg, isOutput) {
1703
+ let result = node;
1704
+ if (arg.allow_multiple === true && !isOutput && result.kind !== "repeat") result = rep(result);
1705
+ else if (arg.allow_multiple === true && isOutput) this.warn(`Output argument '${String(arg.id)}' is allow_multiple; treating as single`);
1706
+ if (arg.optional === true) result = opt(result);
1707
+ return result;
1708
+ }
1709
+ docMeta(description) {
1710
+ const doc = this.docFrom(description);
1711
+ return doc ? { doc } : {};
1712
+ }
1713
+ /**
1714
+ * An option `-{id}` with 0..N arguments. Returns the node plus any outputs
1715
+ * that must live on the ROOT (collapsing single-value options); sub-struct
1716
+ * options carry their own outputs on the struct sequence's meta.
1717
+ */
1718
+ buildOption(option) {
1719
+ if (!isObject$1(option)) {
1720
+ this.warn("Skipping non-object option");
1721
+ return null;
1722
+ }
1723
+ const id = option.id;
1724
+ if (!isString$1(id)) {
1725
+ this.error("MRtrix option missing 'id'");
1726
+ return null;
1727
+ }
1728
+ const flag = `-${id}`;
1729
+ const name = this.uniqueName(snakeCase(id));
1730
+ const optDoc = this.docFrom(option.description);
1731
+ const args = asArray$1(option.arguments);
1732
+ const required = option.optional === false;
1733
+ if (args.length === 0) {
1734
+ if (option.allow_multiple === true) return {
1735
+ node: rep(lit(flag), {
1736
+ name,
1737
+ ...optDoc && { doc: optDoc }
1738
+ }),
1739
+ rootOutputs: []
1740
+ };
1741
+ const meta = {
1742
+ name,
1743
+ ...optDoc && { doc: optDoc },
1744
+ defaultValue: false
1745
+ };
1746
+ return {
1747
+ node: opt(lit(flag), meta),
1748
+ rootOutputs: []
1749
+ };
1750
+ }
1751
+ if (args.length === 1 && option.allow_multiple !== true) {
1752
+ const arg = args[0];
1753
+ if (!isObject$1(arg)) {
1754
+ this.error(`MRtrix option '${id}' has a non-object argument`);
1755
+ return null;
1756
+ }
1757
+ const meta = {
1758
+ name,
1759
+ ...optDoc && { doc: optDoc }
1760
+ };
1761
+ const built = this.buildArgTerminal(arg, meta);
1762
+ if (!built) return null;
1763
+ const valueNode = this.applyCardinality(built.node, arg, built.output !== void 0);
1764
+ const flagSeq = seq(lit(flag), valueNode);
1765
+ return {
1766
+ node: required ? flagSeq : opt(flagSeq),
1767
+ rootOutputs: built.output ? [built.output] : []
1768
+ };
1769
+ }
1770
+ const inner = [lit(flag)];
1771
+ const structOutputs = [];
1772
+ for (const rawArg of args) {
1773
+ if (!isObject$1(rawArg)) {
1774
+ this.warn(`Skipping non-object argument of option '${id}'`);
1775
+ continue;
1776
+ }
1777
+ const argId = rawArg.id;
1778
+ if (!isString$1(argId)) {
1779
+ this.error(`MRtrix option '${id}' has an argument missing 'id'`);
1780
+ continue;
1781
+ }
1782
+ const argDoc = this.docFrom(rawArg.description) ?? optDoc;
1783
+ const meta = {
1784
+ name: snakeCase(argId),
1785
+ ...argDoc && { doc: argDoc }
1786
+ };
1787
+ const built = this.buildArgTerminal(rawArg, meta);
1788
+ if (!built) continue;
1789
+ const valueNode = this.applyCardinality(built.node, rawArg, built.output !== void 0);
1790
+ inner.push(valueNode);
1791
+ if (built.output) structOutputs.push(built.output);
1792
+ }
1793
+ const structSeq = seq(...inner);
1794
+ structSeq.meta = {
1795
+ name,
1796
+ ...structOutputs.length > 0 && { outputs: structOutputs }
1797
+ };
1798
+ const wrapperMeta = optDoc ? { doc: optDoc } : void 0;
1799
+ let node;
1800
+ if (option.allow_multiple === true) node = rep(structSeq, wrapperMeta);
1801
+ else if (required) {
1802
+ if (optDoc) structSeq.meta = {
1803
+ ...structSeq.meta,
1804
+ doc: optDoc
1805
+ };
1806
+ node = structSeq;
1807
+ } else node = opt(structSeq, wrapperMeta);
1808
+ return {
1809
+ node,
1810
+ rootOutputs: []
1811
+ };
1812
+ }
1813
+ parse(source, _filename) {
1814
+ this.reset();
1815
+ const cmd = this.parseJSON(source);
1816
+ if (!cmd) return {
1817
+ expr: emptyExpr$1(),
1818
+ errors: this.errors,
1819
+ warnings: this.warnings
1820
+ };
1821
+ const meta = this.buildAppMeta(cmd);
1822
+ if (!meta || !isString$1(cmd.name)) return {
1823
+ expr: emptyExpr$1(),
1824
+ errors: this.errors,
1825
+ warnings: this.warnings
1826
+ };
1827
+ const nodes = [lit(cmd.name)];
1828
+ const rootOutputs = [];
1829
+ for (const arg of asArray$1(cmd.arguments)) {
1830
+ const node = this.buildPositional(arg, rootOutputs);
1831
+ if (node) nodes.push(node);
1832
+ }
1833
+ for (const group of asArray$1(cmd.option_groups)) {
1834
+ if (!isObject$1(group)) continue;
1835
+ for (const option of asArray$1(group.options)) {
1836
+ const built = this.buildOption(option);
1837
+ if (!built) continue;
1838
+ nodes.push(built.node);
1839
+ rootOutputs.push(...built.rootOutputs);
1840
+ }
1841
+ }
1842
+ const rootSeq = seq(...nodes);
1843
+ rootSeq.meta = {
1844
+ name: meta.id,
1845
+ ...rootOutputs.length > 0 && { outputs: rootOutputs }
1846
+ };
1847
+ return {
1848
+ meta,
1849
+ expr: rootSeq,
1850
+ errors: this.errors,
1851
+ warnings: this.warnings
1852
+ };
1853
+ }
1854
+ };
1855
+
1480
1856
  //#endregion
1481
1857
  //#region src/frontend/workbench/parser.ts
1482
1858
  function isObject(x) {
@@ -1808,6 +2184,7 @@ function detectFormat(source) {
1808
2184
  const obj = parsed;
1809
2185
  if (typeof obj.$schema === "string" && obj.$schema.includes("argdump")) return "argdump";
1810
2186
  if (typeof obj.command === "string" && typeof obj.short_description === "string") return "workbench";
2187
+ if (typeof obj.synopsis === "string" && Array.isArray(obj.option_groups) && Array.isArray(obj.arguments)) return "mrtrix";
1811
2188
  if ("command-line" in obj || Array.isArray(obj.inputs) && "name" in obj) return "boutiques";
1812
2189
  if (Array.isArray(obj.actions) && "prog" in obj) return "argdump";
1813
2190
  return null;
@@ -7869,7 +8246,7 @@ function compile(source, filenameOrOptions) {
7869
8246
  errors: [{ message: "Could not detect input format. Specify format explicitly." }],
7870
8247
  warnings: []
7871
8248
  };
7872
- return (format === "argdump" ? new ArgdumpParser() : format === "workbench" ? new WorkbenchParser() : new BoutiquesParser()).parse(source, options.filename);
8249
+ return (format === "argdump" ? new ArgdumpParser() : format === "workbench" ? new WorkbenchParser() : format === "mrtrix" ? new MrtrixParser() : new BoutiquesParser()).parse(source, options.filename);
7873
8250
  }
7874
8251
 
7875
8252
  //#endregion