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