@autohq/cli 0.1.478 → 0.1.479

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.
@@ -30866,7 +30866,7 @@ Object.assign(lookup, {
30866
30866
  // package.json
30867
30867
  var package_default = {
30868
30868
  name: "@autohq/cli",
30869
- version: "0.1.478",
30869
+ version: "0.1.479",
30870
30870
  license: "SEE LICENSE IN README.md",
30871
30871
  publishConfig: {
30872
30872
  access: "public"
@@ -38169,8 +38169,10 @@ var SpendCapRuntimeErrorResponseSchema = external_exports.object({
38169
38169
  issues: external_exports.array(external_exports.unknown()).optional()
38170
38170
  });
38171
38171
 
38172
- // ../../packages/schemas/src/templates/public-source.ts
38172
+ // ../../packages/schemas/src/templates/variable-contract.ts
38173
38173
  var import_yaml = __toESM(require_dist(), 1);
38174
+
38175
+ // ../../packages/schemas/src/templates/public-source.ts
38174
38176
  var MANAGED_TEMPLATE_PUBLIC_SOURCE_ORIGIN = "https://www.auto.sh";
38175
38177
  var MANAGED_TEMPLATE_PUBLIC_SOURCE_PATH = "/api/v1/templates";
38176
38178
  var SOURCE_COMMENT_PREFIX = `# Source: ${MANAGED_TEMPLATE_PUBLIC_SOURCE_ORIGIN}${MANAGED_TEMPLATE_PUBLIC_SOURCE_PATH}/`;
package/dist/index.js CHANGED
@@ -22590,12 +22590,70 @@ var init_content_source = __esm({
22590
22590
  }
22591
22591
  });
22592
22592
 
22593
- // ../../packages/schemas/src/templates/public-source.ts
22593
+ // ../../packages/schemas/src/templates/variable-contract.ts
22594
22594
  import { parseAllDocuments } from "yaml";
22595
+ function readTemplateVariableContract(document, path2) {
22596
+ const value = document.templateVariables;
22597
+ if (value === void 0) {
22598
+ return { declared: false, required: /* @__PURE__ */ new Set(), optional: /* @__PURE__ */ new Set() };
22599
+ }
22600
+ if (!isRecord(value)) {
22601
+ throw new Error(
22602
+ `Invalid templateVariables in ${path2}: expected a map with required and optional lists`
22603
+ );
22604
+ }
22605
+ const required2 = readVariableNames(value.required, "required", path2);
22606
+ const optional2 = readVariableNames(value.optional, "optional", path2);
22607
+ const overlap = [...required2].filter((name) => optional2.has(name)).sort();
22608
+ if (overlap.length > 0) {
22609
+ throw new Error(
22610
+ `Invalid templateVariables in ${path2}: variables cannot be both required and optional (${overlap.join(", ")})`
22611
+ );
22612
+ }
22613
+ return { declared: true, required: required2, optional: optional2 };
22614
+ }
22615
+ function readVariableNames(value, kind, path2) {
22616
+ if (value === void 0) {
22617
+ return /* @__PURE__ */ new Set();
22618
+ }
22619
+ if (!Array.isArray(value)) {
22620
+ throw new Error(
22621
+ `Invalid templateVariables.${kind} in ${path2}: expected a list of variable names`
22622
+ );
22623
+ }
22624
+ const names = /* @__PURE__ */ new Set();
22625
+ for (const name of value) {
22626
+ if (typeof name !== "string" || !VARIABLE_NAME_PATTERN.test(name)) {
22627
+ throw new Error(
22628
+ `Invalid templateVariables.${kind} in ${path2}: names must match [A-Za-z_][A-Za-z0-9_]*`
22629
+ );
22630
+ }
22631
+ if (names.has(name)) {
22632
+ throw new Error(
22633
+ `Invalid templateVariables.${kind} in ${path2}: duplicate variable ${name}`
22634
+ );
22635
+ }
22636
+ names.add(name);
22637
+ }
22638
+ return names;
22639
+ }
22640
+ function isRecord(value) {
22641
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22642
+ }
22643
+ var VARIABLE_NAME_PATTERN;
22644
+ var init_variable_contract = __esm({
22645
+ "../../packages/schemas/src/templates/variable-contract.ts"() {
22646
+ "use strict";
22647
+ VARIABLE_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
22648
+ }
22649
+ });
22650
+
22651
+ // ../../packages/schemas/src/templates/public-source.ts
22595
22652
  var MANAGED_TEMPLATE_PUBLIC_SOURCE_ORIGIN, MANAGED_TEMPLATE_PUBLIC_SOURCE_PATH, SOURCE_COMMENT_PREFIX;
22596
22653
  var init_public_source = __esm({
22597
22654
  "../../packages/schemas/src/templates/public-source.ts"() {
22598
22655
  "use strict";
22656
+ init_variable_contract();
22599
22657
  MANAGED_TEMPLATE_PUBLIC_SOURCE_ORIGIN = "https://www.auto.sh";
22600
22658
  MANAGED_TEMPLATE_PUBLIC_SOURCE_PATH = "/api/v1/templates";
22601
22659
  SOURCE_COMMENT_PREFIX = `# Source: ${MANAGED_TEMPLATE_PUBLIC_SOURCE_ORIGIN}${MANAGED_TEMPLATE_PUBLIC_SOURCE_PATH}/`;
@@ -54702,6 +54760,7 @@ var init_templates = __esm({
54702
54760
  init_hardcoded();
54703
54761
  init_subscription();
54704
54762
  init_catalog();
54763
+ init_variable_contract();
54705
54764
  }
54706
54765
  });
54707
54766
 
@@ -57700,7 +57759,7 @@ var init_package = __esm({
57700
57759
  "package.json"() {
57701
57760
  package_default = {
57702
57761
  name: "@autohq/cli",
57703
- version: "0.1.478",
57762
+ version: "0.1.479",
57704
57763
  license: "SEE LICENSE IN README.md",
57705
57764
  publishConfig: {
57706
57765
  access: "public"
@@ -57961,7 +58020,7 @@ function compileAgentDocument(document, path2, stack, context) {
57961
58020
  `Agent import cycle detected: ${[...stack, resolvedPath].join(" -> ")}`
57962
58021
  );
57963
58022
  }
57964
- if (!isRecord2(document)) {
58023
+ if (!isRecord3(document)) {
57965
58024
  throw new Error(`Invalid agent authoring file ${path2}: expected object`);
57966
58025
  }
57967
58026
  const imports = importPaths(document).map(
@@ -58086,25 +58145,25 @@ function authoringDocumentApplyShape(document, path2) {
58086
58145
  };
58087
58146
  }
58088
58147
  function finalizeAgentApplyShape(document, path2) {
58089
- if (!isRecord2(document) || !isRecord2(document.spec)) {
58148
+ if (!isRecord3(document) || !isRecord3(document.spec)) {
58090
58149
  return { resource: document, resources: [] };
58091
58150
  }
58092
58151
  const next = structuredClone(document);
58093
58152
  const spec = next.spec;
58094
58153
  const resources = [];
58095
- if (isRecord2(spec.environment)) {
58154
+ if (isRecord3(spec.environment)) {
58096
58155
  const environment = inlineEnvironmentResource(spec.environment, path2);
58097
58156
  spec.environment = environment.metadata.name;
58098
58157
  resources.push(environment);
58099
58158
  }
58100
- if (isRecord2(spec.identity)) {
58159
+ if (isRecord3(spec.identity)) {
58101
58160
  const identity2 = inlineIdentityResource(spec.identity, next, path2);
58102
58161
  spec.identity = identity2.metadata.name;
58103
58162
  resources.push(identity2);
58104
58163
  }
58105
58164
  if (Array.isArray(spec.triggers)) {
58106
58165
  spec.triggers = spec.triggers.map((trigger) => {
58107
- if (!isRecord2(trigger) || !("name" in trigger)) {
58166
+ if (!isRecord3(trigger) || !("name" in trigger)) {
58108
58167
  return trigger;
58109
58168
  }
58110
58169
  const compiledTrigger = { ...trigger };
@@ -58152,7 +58211,7 @@ function inlineIdentityResource(document, agentDocument, path2) {
58152
58211
  }
58153
58212
  spec[key] = value;
58154
58213
  }
58155
- if (metadata.name === void 0 && isRecord2(agentDocument.metadata) && typeof agentDocument.metadata.name === "string") {
58214
+ if (metadata.name === void 0 && isRecord3(agentDocument.metadata) && typeof agentDocument.metadata.name === "string") {
58156
58215
  metadata.name = agentDocument.metadata.name;
58157
58216
  }
58158
58217
  const parsed = IdentityApplyRequestSchema.safeParse({ metadata, spec });
@@ -58188,7 +58247,7 @@ function resolveFileBackedFields(spec, path2) {
58188
58247
  };
58189
58248
  }
58190
58249
  function resolveFileBackedString(value, input) {
58191
- if (!isRecord2(value) || !("file" in value)) {
58250
+ if (!isRecord3(value) || !("file" in value)) {
58192
58251
  return value;
58193
58252
  }
58194
58253
  const file2 = value.file;
@@ -58206,8 +58265,8 @@ function resolveFileBackedString(value, input) {
58206
58265
  return readFileSync8(resolve3(dirname8(input.path), filePath), "utf8");
58207
58266
  }
58208
58267
  function removalDirectives(document, path2) {
58209
- const raw = isRecord2(document.remove) ? document.remove : {};
58210
- if (!isRecord2(raw)) {
58268
+ const raw = isRecord3(document.remove) ? document.remove : {};
58269
+ if (!isRecord3(raw)) {
58211
58270
  return [];
58212
58271
  }
58213
58272
  const directives = [];
@@ -58229,18 +58288,18 @@ function stringList(value, label) {
58229
58288
  });
58230
58289
  }
58231
58290
  function applyRemoval(value, removal) {
58232
- if (!isRecord2(value)) {
58291
+ if (!isRecord3(value)) {
58233
58292
  return value;
58234
58293
  }
58235
58294
  const next = structuredClone(value);
58236
- if (!isRecord2(next.spec)) {
58295
+ if (!isRecord3(next.spec)) {
58237
58296
  return next;
58238
58297
  }
58239
58298
  const spec = { ...next.spec };
58240
58299
  next.spec = spec;
58241
58300
  switch (removal.target) {
58242
58301
  case "tools": {
58243
- if (!isRecord2(spec[removal.target])) {
58302
+ if (!isRecord3(spec[removal.target])) {
58244
58303
  return next;
58245
58304
  }
58246
58305
  const collection = {
@@ -58284,7 +58343,7 @@ function mergeValues2(base, override, path2) {
58284
58343
  if (Array.isArray(base) && Array.isArray(override)) {
58285
58344
  return mergeArrays(base, override, path2);
58286
58345
  }
58287
- if (isRecord2(base) && isRecord2(override)) {
58346
+ if (isRecord3(base) && isRecord3(override)) {
58288
58347
  const merged = { ...base };
58289
58348
  for (const [key, value] of Object.entries(override)) {
58290
58349
  merged[key] = mergeValues2(merged[key], value, [...path2, key]);
@@ -58327,7 +58386,7 @@ function isNamedArrayMergePath(path2) {
58327
58386
  return path2 === "spec.mounts" || path2 === "spec.triggers";
58328
58387
  }
58329
58388
  function collectionItemKey(collection, item) {
58330
- if (!isRecord2(item)) {
58389
+ if (!isRecord3(item)) {
58331
58390
  return void 0;
58332
58391
  }
58333
58392
  if (typeof item.name === "string") {
@@ -58359,7 +58418,7 @@ function isAgentFile(path2) {
58359
58418
  return false;
58360
58419
  }
58361
58420
  }
58362
- function isRecord2(value) {
58421
+ function isRecord3(value) {
58363
58422
  return typeof value === "object" && value !== null && !Array.isArray(value);
58364
58423
  }
58365
58424
  var AGENT_FILE_EXTENSIONS, AGENT_SPEC_FIELDS, AGENT_METADATA_FIELDS;
@@ -58768,7 +58827,7 @@ function sourceFileIndex(files) {
58768
58827
  function isAbsoluteSourcePath(path2) {
58769
58828
  return path2.startsWith("/");
58770
58829
  }
58771
- function isRecord3(value) {
58830
+ function isRecord4(value) {
58772
58831
  return typeof value === "object" && value !== null && !Array.isArray(value);
58773
58832
  }
58774
58833
  var APPLY_DIRECTORIES, PROJECT_APPLY_RESOURCE_DIRECTORIES;
@@ -58847,7 +58906,7 @@ function resolveTemplateImportPath(importPath, fileIndex) {
58847
58906
  return key;
58848
58907
  }
58849
58908
  function removalDirectives2(document, path2) {
58850
- const raw = isRecord3(document.remove) ? document.remove : {};
58909
+ const raw = isRecord4(document.remove) ? document.remove : {};
58851
58910
  const directives = [];
58852
58911
  for (const [target, value] of Object.entries(raw)) {
58853
58912
  const names = stringList2(value, `remove.${target}`);
@@ -58992,7 +59051,7 @@ function mergeValues3(base, override) {
58992
59051
  if (override === void 0) {
58993
59052
  return base;
58994
59053
  }
58995
- if (isRecord3(base) && isRecord3(override)) {
59054
+ if (isRecord4(base) && isRecord4(override)) {
58996
59055
  const merged = { ...base };
58997
59056
  for (const [key, value] of Object.entries(override)) {
58998
59057
  merged[key] = mergeValues3(merged[key], value);
@@ -59012,7 +59071,7 @@ function sortJson(value) {
59012
59071
  if (Array.isArray(value)) {
59013
59072
  return value.map(sortJson);
59014
59073
  }
59015
- if (isRecord3(value)) {
59074
+ if (isRecord4(value)) {
59016
59075
  return Object.fromEntries(
59017
59076
  Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortJson(nested)])
59018
59077
  );
@@ -59028,7 +59087,9 @@ var init_helpers = __esm({
59028
59087
  "import",
59029
59088
  "imports",
59030
59089
  "remove",
59031
- "variables"
59090
+ "templateVariables",
59091
+ "variables",
59092
+ "when"
59032
59093
  ]);
59033
59094
  AGENT_METADATA_FIELD_KEYS = ["name", "labels", "annotations"];
59034
59095
  }
@@ -59048,7 +59109,7 @@ function fileBackedStringField() {
59048
59109
  };
59049
59110
  }
59050
59111
  function rejectDirectiveObject(value, input) {
59051
- if (isRecord3(value) && "append" in value) {
59112
+ if (isRecord4(value) && "append" in value) {
59052
59113
  throw new Error(
59053
59114
  `Invalid agent authoring file ${input.path}: ${input.field} does not support directive objects; append is supported on ${APPEND_CAPABLE_FIELDS}`
59054
59115
  );
@@ -59056,7 +59117,7 @@ function rejectDirectiveObject(value, input) {
59056
59117
  return value;
59057
59118
  }
59058
59119
  function resolveFileBackedString2(value, input) {
59059
- if (!isRecord3(value)) {
59120
+ if (!isRecord4(value)) {
59060
59121
  return value;
59061
59122
  }
59062
59123
  if ("file" in value && !("append" in value)) {
@@ -59154,11 +59215,11 @@ function rejectUnresolvedAppendDirective(value) {
59154
59215
  );
59155
59216
  }
59156
59217
  function appendDirectiveFromMarker(value) {
59157
- if (!isRecord3(value)) {
59218
+ if (!isRecord4(value)) {
59158
59219
  return void 0;
59159
59220
  }
59160
59221
  const marker = value[APPEND_DIRECTIVE_MARKER_KEY];
59161
- if (!isRecord3(marker)) {
59222
+ if (!isRecord4(marker)) {
59162
59223
  return void 0;
59163
59224
  }
59164
59225
  return marker;
@@ -59180,7 +59241,7 @@ function inlineEnvironmentField() {
59180
59241
  target: "spec",
59181
59242
  merge: (base, override) => mergeValues3(base, override),
59182
59243
  compile: (value, context) => {
59183
- if (!isRecord3(value)) {
59244
+ if (!isRecord4(value)) {
59184
59245
  return { resources: [], value };
59185
59246
  }
59186
59247
  const resource = inlineEnvironmentResource2(value, context.path);
@@ -59193,7 +59254,7 @@ function inlineIdentityField() {
59193
59254
  target: "spec",
59194
59255
  merge: (base, override) => mergeValues3(base, override),
59195
59256
  compile: (value, context) => {
59196
- if (!isRecord3(value)) {
59257
+ if (!isRecord4(value)) {
59197
59258
  return { resources: [], value };
59198
59259
  }
59199
59260
  const resource = inlineIdentityResource2(
@@ -59206,7 +59267,7 @@ function inlineIdentityField() {
59206
59267
  };
59207
59268
  }
59208
59269
  function assertAgentAuthoringDocument(document, path2) {
59209
- if (!isRecord3(document) || !("kind" in document)) {
59270
+ if (!isRecord4(document) || !("kind" in document)) {
59210
59271
  return;
59211
59272
  }
59212
59273
  if (document.kind === "session") {
@@ -59359,12 +59420,12 @@ function compileRoutingInlineBindings(input) {
59359
59420
  const bindings = input.bindings ? cloneBindings(input.bindings) : {};
59360
59421
  const compiledTriggers = [];
59361
59422
  for (const trigger of input.triggers) {
59362
- if (!isRecord3(trigger) || trigger.kind === "heartbeat") {
59423
+ if (!isRecord4(trigger) || trigger.kind === "heartbeat") {
59363
59424
  compiledTriggers.push(trigger);
59364
59425
  continue;
59365
59426
  }
59366
59427
  const routing = trigger.routing;
59367
- if (!isRecord3(routing)) {
59428
+ if (!isRecord4(routing)) {
59368
59429
  compiledTriggers.push(trigger);
59369
59430
  continue;
59370
59431
  }
@@ -59395,7 +59456,7 @@ function extractDeliverContribution(routing, path2) {
59395
59456
  if (bindBlock === void 0) {
59396
59457
  return void 0;
59397
59458
  }
59398
- if (!isRecord3(bindBlock)) {
59459
+ if (!isRecord4(bindBlock)) {
59399
59460
  throw inlineShapeError(
59400
59461
  path2,
59401
59462
  "deliver",
@@ -59425,7 +59486,7 @@ function extractBindArmContribution(routing, path2) {
59425
59486
  }
59426
59487
  function extractSpawnArmContribution(routing, path2) {
59427
59488
  const bindBlock = routing.bind;
59428
- if (!isRecord3(bindBlock)) {
59489
+ if (!isRecord4(bindBlock)) {
59429
59490
  return void 0;
59430
59491
  }
59431
59492
  if (bindBlock.lifecycle === void 0 && bindBlock.continuity === void 0) {
@@ -59463,7 +59524,7 @@ function mergeContributionIntoBindings(bindings, contribution, path2) {
59463
59524
  }
59464
59525
  function ensureEntry(bindings, target) {
59465
59526
  const existing = bindings[target];
59466
- if (isRecord3(existing)) {
59527
+ if (isRecord4(existing)) {
59467
59528
  return existing;
59468
59529
  }
59469
59530
  const entry = {};
@@ -59485,7 +59546,7 @@ function stripInlineRoutingFields(trigger, routing) {
59485
59546
  return { ...trigger, routing: rest };
59486
59547
  }
59487
59548
  case "spawn": {
59488
- if (!isRecord3(routing.bind)) {
59549
+ if (!isRecord4(routing.bind)) {
59489
59550
  return trigger;
59490
59551
  }
59491
59552
  const {
@@ -59550,7 +59611,7 @@ function rejectSingletonTarget(target, path2, arm) {
59550
59611
  function cloneBindings(bindings) {
59551
59612
  const clone2 = {};
59552
59613
  for (const [key, value] of Object.entries(bindings)) {
59553
- clone2[key] = isRecord3(value) ? { ...value } : value;
59614
+ clone2[key] = isRecord4(value) ? { ...value } : value;
59554
59615
  }
59555
59616
  return clone2;
59556
59617
  }
@@ -59600,7 +59661,7 @@ function triggersField() {
59600
59661
  const withNamesStripped = removeAuthoringTriggerNames(value);
59601
59662
  const { triggers, bindings } = compileRoutingInlineBindings({
59602
59663
  triggers: withNamesStripped,
59603
- bindings: isRecord3(context.draft.spec.bindings) ? context.draft.spec.bindings : void 0,
59664
+ bindings: isRecord4(context.draft.spec.bindings) ? context.draft.spec.bindings : void 0,
59604
59665
  path: context.path
59605
59666
  });
59606
59667
  context.draft.spec.bindings = bindings;
@@ -59609,7 +59670,7 @@ function triggersField() {
59609
59670
  };
59610
59671
  }
59611
59672
  function mountItemKey(item) {
59612
- if (!isRecord3(item)) {
59673
+ if (!isRecord4(item)) {
59613
59674
  return void 0;
59614
59675
  }
59615
59676
  if (typeof item.name === "string") {
@@ -59621,7 +59682,7 @@ function mountItemKey(item) {
59621
59682
  return void 0;
59622
59683
  }
59623
59684
  function triggerItemKey(item) {
59624
- if (!isRecord3(item)) {
59685
+ if (!isRecord4(item)) {
59625
59686
  return void 0;
59626
59687
  }
59627
59688
  if (typeof item.name === "string") {
@@ -59674,7 +59735,7 @@ function removeAuthoringTriggerNames(value) {
59674
59735
  return value;
59675
59736
  }
59676
59737
  return value.map((trigger) => {
59677
- if (!isRecord3(trigger) || !("name" in trigger)) {
59738
+ if (!isRecord4(trigger) || !("name" in trigger)) {
59678
59739
  return trigger;
59679
59740
  }
59680
59741
  const next = { ...trigger };
@@ -59697,7 +59758,7 @@ function namedMapField() {
59697
59758
  target: "spec",
59698
59759
  merge: (base, override) => mergeValues3(base, override),
59699
59760
  remove: (value, names) => {
59700
- if (!isRecord3(value)) {
59761
+ if (!isRecord4(value)) {
59701
59762
  return value;
59702
59763
  }
59703
59764
  const next = { ...value };
@@ -59796,7 +59857,7 @@ function mergeSourceLocations(base, override) {
59796
59857
  continue;
59797
59858
  }
59798
59859
  const prefix = `${target}.${key}`;
59799
- if (!isRecord3(value)) {
59860
+ if (!isRecord4(value)) {
59800
59861
  merged = Object.fromEntries(
59801
59862
  Object.entries(merged).filter(
59802
59863
  ([path2]) => path2 !== prefix && !path2.startsWith(`${prefix}.`)
@@ -59923,14 +59984,14 @@ function readVariableScope(document, path2) {
59923
59984
  if (declared === void 0) {
59924
59985
  return /* @__PURE__ */ new Map();
59925
59986
  }
59926
- if (!isRecord3(declared)) {
59987
+ if (!isRecord4(declared)) {
59927
59988
  throw new Error(
59928
59989
  `Invalid variables in ${path2}: variables must be a map of name to value`
59929
59990
  );
59930
59991
  }
59931
59992
  const scope = /* @__PURE__ */ new Map();
59932
59993
  for (const [name, value] of Object.entries(declared)) {
59933
- if (!VARIABLE_NAME_PATTERN.test(name)) {
59994
+ if (!VARIABLE_NAME_PATTERN2.test(name)) {
59934
59995
  throw new Error(
59935
59996
  `Invalid variable name "${name}" in ${path2}: names must match [A-Za-z_][A-Za-z0-9_]*`
59936
59997
  );
@@ -59939,6 +60000,66 @@ function readVariableScope(document, path2) {
59939
60000
  }
59940
60001
  return scope;
59941
60002
  }
60003
+ function registerTemplateVariableContract(declarations, contract, path2) {
60004
+ for (const [kind, names] of [
60005
+ ["required", contract.required],
60006
+ ["optional", contract.optional]
60007
+ ]) {
60008
+ for (const name of names) {
60009
+ const existing = declarations.get(name);
60010
+ if (existing && existing.kind !== kind) {
60011
+ throw new Error(
60012
+ `Conflicting template variable declaration for "${name}": ${existing.kind} in ${existing.path}, ${kind} in ${path2}`
60013
+ );
60014
+ }
60015
+ declarations.set(name, existing ?? { kind, path: path2 });
60016
+ }
60017
+ }
60018
+ }
60019
+ function assertRequiredTemplateVariables(required2, variables, path2) {
60020
+ for (const name of [...required2].sort()) {
60021
+ if (!variables.has(name)) {
60022
+ throw new Error(
60023
+ `Required template variable "${name}" is missing for imported ${path2}`
60024
+ );
60025
+ }
60026
+ }
60027
+ }
60028
+ function includeConditionalTemplateDocument(input) {
60029
+ const optional2 = new Set(
60030
+ [...input.declarations.entries()].filter(([, declaration]) => declaration.kind === "optional").map(([name]) => name)
60031
+ );
60032
+ const referencedOptional = optionalVariableReferences(
60033
+ input.document,
60034
+ optional2
60035
+ );
60036
+ const condition = readVariableCondition(input.document.when, input.path);
60037
+ if (referencedOptional.size > 0) {
60038
+ if (!condition || referencedOptional.size !== 1 || !referencedOptional.has(condition)) {
60039
+ throw new Error(
60040
+ `Optional template variables in ${input.path} must be referenced only inside a document gated by matching when.variable`
60041
+ );
60042
+ }
60043
+ }
60044
+ if (!condition) {
60045
+ return true;
60046
+ }
60047
+ if (!optional2.has(condition)) {
60048
+ throw new Error(
60049
+ `Invalid when.variable in ${input.path}: "${condition}" must be declared in templateVariables.optional`
60050
+ );
60051
+ }
60052
+ const value = input.variables.get(condition);
60053
+ if (value === void 0) {
60054
+ return false;
60055
+ }
60056
+ if (value.trim().length === 0) {
60057
+ throw new Error(
60058
+ `Optional template variable "${condition}" in ${input.path} must be non-empty when supplied`
60059
+ );
60060
+ }
60061
+ return true;
60062
+ }
59942
60063
  function substituteVariables(document, scope, site) {
59943
60064
  if (scope.size === 0) {
59944
60065
  return document;
@@ -59952,7 +60073,7 @@ function substituteValue(value, scope, site) {
59952
60073
  if (Array.isArray(value)) {
59953
60074
  return value.map((item) => substituteValue(item, scope, site));
59954
60075
  }
59955
- if (isRecord3(value)) {
60076
+ if (isRecord4(value)) {
59956
60077
  return Object.fromEntries(
59957
60078
  Object.entries(value).map(([key, nested]) => [
59958
60079
  key,
@@ -59987,13 +60108,49 @@ function coerceVariableValue(name, value, path2) {
59987
60108
  function availableNames(scope) {
59988
60109
  return [...scope.keys()].sort().map((name) => `{{ $${name} }}`).join(", ");
59989
60110
  }
59990
- var VARIABLE_DECLARATIONS_FIELD, VARIABLE_NAME_PATTERN, VARIABLE_REFERENCE_PATTERN;
60111
+ function readVariableCondition(value, path2) {
60112
+ if (value === void 0) return void 0;
60113
+ if (!isRecord4(value) || typeof value.variable !== "string" || value.variable.length === 0 || Object.keys(value).some((key) => key !== "variable")) {
60114
+ throw new Error(
60115
+ `Invalid when in ${path2}: expected exactly { variable: <optional variable name> }`
60116
+ );
60117
+ }
60118
+ return value.variable;
60119
+ }
60120
+ function optionalVariableReferences(value, optional2) {
60121
+ const found = /* @__PURE__ */ new Set();
60122
+ collectOptionalVariableReferences(value, optional2, found);
60123
+ return found;
60124
+ }
60125
+ function collectOptionalVariableReferences(value, optional2, found) {
60126
+ if (typeof value === "string") {
60127
+ for (const match of value.matchAll(VARIABLE_REFERENCE_PATTERN)) {
60128
+ if (match[1] && optional2.has(match[1])) found.add(match[1]);
60129
+ }
60130
+ return;
60131
+ }
60132
+ if (Array.isArray(value)) {
60133
+ for (const item of value) {
60134
+ collectOptionalVariableReferences(item, optional2, found);
60135
+ }
60136
+ return;
60137
+ }
60138
+ if (isRecord4(value)) {
60139
+ for (const [key, nested] of Object.entries(value)) {
60140
+ if (key !== "templateVariables" && key !== "when") {
60141
+ collectOptionalVariableReferences(nested, optional2, found);
60142
+ }
60143
+ }
60144
+ }
60145
+ }
60146
+ var VARIABLE_DECLARATIONS_FIELD, VARIABLE_NAME_PATTERN2, VARIABLE_REFERENCE_PATTERN;
59991
60147
  var init_template_variables = __esm({
59992
60148
  "../../packages/schemas/src/project-apply-files/template-variables.ts"() {
59993
60149
  "use strict";
60150
+ init_templates();
59994
60151
  init_source();
59995
60152
  VARIABLE_DECLARATIONS_FIELD = "variables";
59996
- VARIABLE_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
60153
+ VARIABLE_NAME_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
59997
60154
  VARIABLE_REFERENCE_PATTERN = /\{\{\s*\$([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
59998
60155
  }
59999
60156
  });
@@ -60080,7 +60237,7 @@ function projectCompiledTriggerSourceLocations(locations, authoredTriggers) {
60080
60237
  );
60081
60238
  let compiledIndex = 0;
60082
60239
  for (const [authoredIndex, trigger] of authoredTriggers.entries()) {
60083
- if (!isRecord3(trigger)) {
60240
+ if (!isRecord4(trigger)) {
60084
60241
  continue;
60085
60242
  }
60086
60243
  const events = authoredTriggerEvents(trigger);
@@ -60148,9 +60305,24 @@ function validateAgentFragmentDocument(document, path2, context) {
60148
60305
  `Agent import cycle detected: ${[...context.stack, normalizedPath].join(" -> ")}`
60149
60306
  );
60150
60307
  }
60151
- if (!isRecord3(document)) {
60308
+ if (!isRecord4(document)) {
60152
60309
  throw new Error(`Invalid agent fragment file ${path2}: expected object`);
60153
60310
  }
60311
+ const variableContract = readTemplateVariableContract(
60312
+ document,
60313
+ normalizedPath
60314
+ );
60315
+ registerTemplateVariableContract(
60316
+ context.templateVariableKinds,
60317
+ variableContract,
60318
+ normalizedPath
60319
+ );
60320
+ includeConditionalTemplateDocument({
60321
+ document,
60322
+ declarations: context.templateVariableKinds,
60323
+ variables: context.variables,
60324
+ path: normalizedPath
60325
+ });
60154
60326
  for (const imported of importPaths2(document).map(
60155
60327
  (importPath) => resolveImportPath2(importPath, normalizedPath, context.fileIndex)
60156
60328
  )) {
@@ -60160,6 +60332,7 @@ function validateAgentFragmentDocument(document, path2, context) {
60160
60332
  {
60161
60333
  fileIndex: context.fileIndex,
60162
60334
  stack: [...context.stack, normalizedPath],
60335
+ templateVariableKinds: context.templateVariableKinds,
60163
60336
  variables: context.variables
60164
60337
  }
60165
60338
  );
@@ -60198,20 +60371,59 @@ function conflictingGeneratedResourceMessage(key, existing, record2) {
60198
60371
  return `Conflicting generated resource "${key}" from agent authoring: ${location}. Inline generated resources must be identical when they share a name.`;
60199
60372
  }
60200
60373
  function compileAgentDocumentValue(document, path2, fileIndex, contextVariables) {
60374
+ const templateVariableKinds = /* @__PURE__ */ new Map();
60375
+ collectTemplateVariableContracts(
60376
+ document,
60377
+ path2,
60378
+ fileIndex,
60379
+ [],
60380
+ templateVariableKinds
60381
+ );
60201
60382
  const draft = compileAgentDocument2(document, path2, {
60202
60383
  fileIndex,
60203
60384
  stack: [],
60385
+ templateVariableKinds,
60204
60386
  variables: /* @__PURE__ */ new Map(),
60205
60387
  ...contextVariables && Object.keys(contextVariables).length > 0 ? { contextVariables: new Map(Object.entries(contextVariables)) } : {}
60206
60388
  });
60207
60389
  const authoredTriggers = structuredClone(draft.spec.triggers);
60208
- const removals = isRecord3(document) ? removalDirectives2(document, normalizeSourcePath(path2)) : [];
60390
+ const removals = isRecord4(document) ? removalDirectives2(document, normalizeSourcePath(path2)) : [];
60209
60391
  return {
60210
60392
  ...compileAgentDraftResources(draft, path2, removals),
60211
60393
  sourceLocations: draft.sourceLocations,
60212
60394
  authoredTriggers
60213
60395
  };
60214
60396
  }
60397
+ function collectTemplateVariableContracts(document, path2, fileIndex, stack, declarations) {
60398
+ const normalizedPath = normalizeSourcePath(path2);
60399
+ if (stack.includes(normalizedPath)) {
60400
+ throw new Error(
60401
+ `Agent import cycle detected: ${[...stack, normalizedPath].join(" -> ")}`
60402
+ );
60403
+ }
60404
+ if (!isRecord4(document)) {
60405
+ throw new Error(`Invalid agent authoring file ${path2}: expected object`);
60406
+ }
60407
+ registerTemplateVariableContract(
60408
+ declarations,
60409
+ readTemplateVariableContract(document, normalizedPath),
60410
+ normalizedPath
60411
+ );
60412
+ const imports = [
60413
+ ...stack.length === 0 && document.name === BUILT_IN_DEFAULT_AGENT_NAME ? [BUILT_IN_DEFAULT_AGENT_TEMPLATE_IMPORT] : [],
60414
+ ...importPaths2(document)
60415
+ ];
60416
+ for (const importPath of imports) {
60417
+ const imported = resolveImportPath2(importPath, normalizedPath, fileIndex);
60418
+ collectTemplateVariableContracts(
60419
+ readSingleDocument2(imported, fileIndex),
60420
+ imported,
60421
+ fileIndex,
60422
+ [...stack, normalizedPath],
60423
+ declarations
60424
+ );
60425
+ }
60426
+ }
60215
60427
  function compileAgentDocument2(document, path2, context) {
60216
60428
  const normalizedPath = normalizeSourcePath(path2);
60217
60429
  if (context.stack.includes(normalizedPath)) {
@@ -60219,13 +60431,35 @@ function compileAgentDocument2(document, path2, context) {
60219
60431
  `Agent import cycle detected: ${[...context.stack, normalizedPath].join(" -> ")}`
60220
60432
  );
60221
60433
  }
60222
- if (!isRecord3(document)) {
60434
+ if (!isRecord4(document)) {
60223
60435
  throw new Error(`Invalid agent authoring file ${path2}: expected object`);
60224
60436
  }
60437
+ const variableContract = readTemplateVariableContract(
60438
+ document,
60439
+ normalizedPath
60440
+ );
60441
+ registerTemplateVariableContract(
60442
+ context.templateVariableKinds,
60443
+ variableContract,
60444
+ normalizedPath
60445
+ );
60225
60446
  const variables = context.stack.length === 0 ? new Map([
60226
60447
  ...context.contextVariables ?? /* @__PURE__ */ new Map(),
60227
60448
  ...readVariableScope(document, normalizedPath)
60228
60449
  ]) : context.variables;
60450
+ assertRequiredTemplateVariables(
60451
+ variableContract.required,
60452
+ variables,
60453
+ normalizedPath
60454
+ );
60455
+ if (context.stack.length > 0 && !includeConditionalTemplateDocument({
60456
+ document,
60457
+ declarations: context.templateVariableKinds,
60458
+ variables,
60459
+ path: normalizedPath
60460
+ })) {
60461
+ return emptyAgentDraft();
60462
+ }
60229
60463
  let merged = emptyAgentDraft();
60230
60464
  const imports = [
60231
60465
  ...context.stack.length === 0 && document.name === BUILT_IN_DEFAULT_AGENT_NAME ? [BUILT_IN_DEFAULT_AGENT_TEMPLATE_IMPORT] : [],
@@ -60237,16 +60471,47 @@ function compileAgentDocument2(document, path2, context) {
60237
60471
  normalizedPath,
60238
60472
  context.fileIndex
60239
60473
  );
60474
+ const rawImportedDocument = readSingleDocument2(imported, context.fileIndex);
60475
+ if (!isRecord4(rawImportedDocument)) {
60476
+ throw new Error(
60477
+ `Invalid agent authoring file ${imported}: expected object`
60478
+ );
60479
+ }
60480
+ const importedContract = readTemplateVariableContract(
60481
+ rawImportedDocument,
60482
+ imported
60483
+ );
60484
+ registerTemplateVariableContract(
60485
+ context.templateVariableKinds,
60486
+ importedContract,
60487
+ imported
60488
+ );
60489
+ assertRequiredTemplateVariables(
60490
+ importedContract.required,
60491
+ variables,
60492
+ imported
60493
+ );
60494
+ if (!includeConditionalTemplateDocument({
60495
+ document: rawImportedDocument,
60496
+ declarations: context.templateVariableKinds,
60497
+ variables,
60498
+ path: imported
60499
+ })) {
60500
+ continue;
60501
+ }
60240
60502
  const importedDocument = substituteVariables(
60241
- readSingleDocument2(imported, context.fileIndex),
60503
+ rawImportedDocument,
60242
60504
  variables,
60243
- { importPath }
60505
+ {
60506
+ importPath
60507
+ }
60244
60508
  );
60245
60509
  merged = mergeAgentDrafts(
60246
60510
  merged,
60247
60511
  compileAgentDocument2(importedDocument, imported, {
60248
60512
  fileIndex: context.fileIndex,
60249
60513
  stack: [...context.stack, normalizedPath],
60514
+ templateVariableKinds: context.templateVariableKinds,
60250
60515
  variables
60251
60516
  })
60252
60517
  );
@@ -60263,6 +60528,7 @@ var init_agent_authoring = __esm({
60263
60528
  "../../packages/schemas/src/project-apply-files/agent-authoring.ts"() {
60264
60529
  "use strict";
60265
60530
  init_default_agent();
60531
+ init_templates();
60266
60532
  init_agent_document_merge();
60267
60533
  init_agent_fields();
60268
60534
  init_source();
@@ -60508,7 +60774,7 @@ function hasBuiltInDefaultAgentDocument(files) {
60508
60774
  return files.some((file2) => {
60509
60775
  try {
60510
60776
  return readDocumentsFromSource(file2).some(
60511
- (document) => isRecord3(document) && document.name === BUILT_IN_DEFAULT_AGENT_NAME
60777
+ (document) => isRecord4(document) && document.name === BUILT_IN_DEFAULT_AGENT_NAME
60512
60778
  );
60513
60779
  } catch {
60514
60780
  return false;
@@ -60519,7 +60785,7 @@ function templateImportsInFile(file2) {
60519
60785
  try {
60520
60786
  const specifiers = [];
60521
60787
  for (const document of readDocumentsFromSource(file2)) {
60522
- if (!isRecord3(document)) {
60788
+ if (!isRecord4(document)) {
60523
60789
  continue;
60524
60790
  }
60525
60791
  for (const importPath of importPaths2(document)) {
@@ -60768,6 +61034,7 @@ function assertValidFragmentSourceFiles(files, resourceRoot, fileIndex) {
60768
61034
  validateAgentFragmentDocument(documents[0], file2.path, {
60769
61035
  fileIndex,
60770
61036
  stack: [],
61037
+ templateVariableKinds: /* @__PURE__ */ new Map(),
60771
61038
  variables: /* @__PURE__ */ new Map()
60772
61039
  });
60773
61040
  } catch (error51) {
@@ -69460,7 +69727,7 @@ var AgentFacingAutoMcpShim = class {
69460
69727
  }
69461
69728
  };
69462
69729
  function agentFacingDryRunTool(tool) {
69463
- if (!isRecord(tool) || tool.name !== DRY_RUN_TOOL_NAME) {
69730
+ if (!isRecord2(tool) || tool.name !== DRY_RUN_TOOL_NAME) {
69464
69731
  return tool;
69465
69732
  }
69466
69733
  if (typeof tool.description !== "string" || !tool.description.includes(AUTO_RESOURCES_DRY_RUN_PATHS_PROTOCOL_MARKER)) {
@@ -69489,7 +69756,7 @@ function agentFacingDryRunTool(tool) {
69489
69756
  };
69490
69757
  }
69491
69758
  function resolveAgentFacingDryRun(input) {
69492
- if (!isRecord(input.arguments)) {
69759
+ if (!isRecord2(input.arguments)) {
69493
69760
  throw new Error(`Dry-run arguments must be an object. ${correctedCall}`);
69494
69761
  }
69495
69762
  const argumentsValue = input.arguments;
@@ -69649,7 +69916,7 @@ function localImportPaths(content, importerPath) {
69649
69916
  const imports = [];
69650
69917
  for (const document of parseAllDocuments2(content)) {
69651
69918
  const value = document.toJS();
69652
- if (!isRecord(value) || !Array.isArray(value.imports)) {
69919
+ if (!isRecord2(value) || !Array.isArray(value.imports)) {
69653
69920
  continue;
69654
69921
  }
69655
69922
  for (const importPath of value.imports) {
@@ -69800,8 +70067,8 @@ async function startAutoMcpProxy(input) {
69800
70067
  return { server, url: `http://127.0.0.1:${address.port}/mcp` };
69801
70068
  }
69802
70069
  function prepareDryRunToolCall(input) {
69803
- const params = isRecord(input.message.params) ? input.message.params : {};
69804
- const argumentsValue = isRecord(params.arguments) ? params.arguments : {};
70070
+ const params = isRecord2(input.message.params) ? input.message.params : {};
70071
+ const argumentsValue = isRecord2(params.arguments) ? params.arguments : {};
69805
70072
  const hasLegacyDialect = ["files", "resources"].some(
69806
70073
  (key) => Object.hasOwn(argumentsValue, key)
69807
70074
  );
@@ -69846,7 +70113,7 @@ function transformToolsListResponse(body) {
69846
70113
  const parsed = JSON.parse(body);
69847
70114
  let compatible = false;
69848
70115
  const transformed = transformJsonRpcMessages(parsed, (message) => {
69849
- if (!isRecord(message.result) || !Array.isArray(message.result.tools)) {
70116
+ if (!isRecord2(message.result) || !Array.isArray(message.result.tools)) {
69850
70117
  return message;
69851
70118
  }
69852
70119
  const tools = message.result.tools.map((tool) => {
@@ -69863,16 +70130,16 @@ function transformToolsListResponse(body) {
69863
70130
  function transformJsonRpcMessages(value, transform2) {
69864
70131
  if (Array.isArray(value)) {
69865
70132
  return value.map(
69866
- (message) => isRecord(message) ? transform2(message) : message
70133
+ (message) => isRecord2(message) ? transform2(message) : message
69867
70134
  );
69868
70135
  }
69869
- return isRecord(value) ? transform2(value) : value;
70136
+ return isRecord2(value) ? transform2(value) : value;
69870
70137
  }
69871
70138
  function isToolsListRequest(value) {
69872
- return isRecord(value) && value.method === "tools/list";
70139
+ return isRecord2(value) && value.method === "tools/list";
69873
70140
  }
69874
70141
  function isDryRunToolCall(value) {
69875
- if (!isRecord(value) || value.method !== "tools/call" || !isRecord(value.params)) {
70142
+ if (!isRecord2(value) || value.method !== "tools/call" || !isRecord2(value.params)) {
69876
70143
  return false;
69877
70144
  }
69878
70145
  return value.params.name === DRY_RUN_TOOL_NAME;
@@ -69937,7 +70204,7 @@ function writeResponse(response, status, headers, body) {
69937
70204
  }
69938
70205
  response.end(body);
69939
70206
  }
69940
- function isRecord(value) {
70207
+ function isRecord2(value) {
69941
70208
  return typeof value === "object" && value !== null && !Array.isArray(value);
69942
70209
  }
69943
70210
 
@@ -80465,7 +80732,7 @@ async function selectRepository(context, github, explicitRepo) {
80465
80732
  const selection = github.grant.providerResourceSelection;
80466
80733
  if (selection.kind === "selected") {
80467
80734
  const repos = selection.resources.map(
80468
- (resource) => isRecord4(resource) && typeof resource.fullName === "string" ? resource.fullName : void 0
80735
+ (resource) => isRecord5(resource) && typeof resource.fullName === "string" ? resource.fullName : void 0
80469
80736
  ).filter(isDefined);
80470
80737
  if (repos.length === 1) {
80471
80738
  context.writeOutput(`Using GitHub repository ${repos[0]}.`);
@@ -80795,7 +81062,7 @@ function slackWorkspaceUrl(slack) {
80795
81062
  function organizationLabel(organization) {
80796
81063
  return `${organization.organizationName} (${organization.organizationSlug})`;
80797
81064
  }
80798
- function isRecord4(value) {
81065
+ function isRecord5(value) {
80799
81066
  return typeof value === "object" && value !== null && !Array.isArray(value);
80800
81067
  }
80801
81068
  function isDefined(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.478",
3
+ "version": "0.1.479",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"