@runveric/cli 0.1.1 → 0.2.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/runveric.mjs CHANGED
@@ -212,14 +212,18 @@ var require_wire_identity = __commonJS({
212
212
  Object.defineProperty(exports, "__esModule", {
213
213
  value: true
214
214
  });
215
- exports.PROBLEM_TYPE_BASE = exports.RUNNER_PROTOCOL_VERSION = exports.PACKAGE_WORK_ITEM_VERSION = exports.ACTION_MANIFEST_API_VERSION = exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = exports.PACKAGE_RESULT_PROTOCOL_VERSION = void 0;
215
+ exports.PROBLEM_TYPE_BASE = exports.CATALOG_ARTIFACT_MEDIA_TYPE = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V1 = exports.CATALOG_RELEASE_SCHEMA_V1 = exports.EVIDENCE_PACK_SCHEMA_VERSION = exports.RUNNER_PROTOCOL_VERSION = exports.PACKAGE_WORK_ITEM_VERSION = exports.ACTION_MANIFEST_API_VERSION = exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = exports.PACKAGE_RESULT_PROTOCOL_VERSION = void 0;
216
216
  exports.problemType = problemType;
217
- exports.PACKAGE_RESULT_PROTOCOL_VERSION = "opera.dev/package-result/v1";
218
- exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = "opera.dev/package-execution/v1";
219
- exports.ACTION_MANIFEST_API_VERSION = "opera.dev/action/v1";
220
- exports.PACKAGE_WORK_ITEM_VERSION = "opera.dev/package-work-item/v1";
221
- exports.RUNNER_PROTOCOL_VERSION = "opera.dev/runner-channel/v2";
222
- exports.PROBLEM_TYPE_BASE = "https://opera.cloud/errors";
217
+ exports.PACKAGE_RESULT_PROTOCOL_VERSION = "runveric.dev/package-result/v1";
218
+ exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = "runveric.dev/package-execution/v1";
219
+ exports.ACTION_MANIFEST_API_VERSION = "runveric.dev/action/v1";
220
+ exports.PACKAGE_WORK_ITEM_VERSION = "runveric.dev/package-work-item/v1";
221
+ exports.RUNNER_PROTOCOL_VERSION = "runveric.dev/runner-channel/v2";
222
+ exports.EVIDENCE_PACK_SCHEMA_VERSION = "runveric.dev/evidence-pack/v1";
223
+ exports.CATALOG_RELEASE_SCHEMA_V1 = "runveric.dev/catalog-release/v1";
224
+ exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V1 = "runveric.dev/catalog-action/v1";
225
+ exports.CATALOG_ARTIFACT_MEDIA_TYPE = "application/vnd.runveric.catalog-artifact+json";
226
+ exports.PROBLEM_TYPE_BASE = "https://runveric.dev/errors";
223
227
  function problemType(slug) {
224
228
  return `${exports.PROBLEM_TYPE_BASE}/${slug}`;
225
229
  }
@@ -301,6 +305,13 @@ var require_permissions = __commonJS({
301
305
  Object.defineProperty(exports, "__esModule", {
302
306
  value: true
303
307
  });
308
+ exports.PLATFORM_PERMISSIONS = void 0;
309
+ exports.PLATFORM_PERMISSIONS = [
310
+ // Which catalog release is live for every workspace (docs/121). Pinning one changes what EVERY
311
+ // workspace can run, so it is neither `policy:manage` (one workspace's own governance) nor
312
+ // `catalog:read` (the right to browse).
313
+ "catalog:release"
314
+ ];
304
315
  }
305
316
  });
306
317
 
@@ -443,6 +454,41 @@ var require_groups = __commonJS({
443
454
  }
444
455
  });
445
456
 
457
+ // ../contracts/dist/catalog-policy.js
458
+ var require_catalog_policy = __commonJS({
459
+ "../contracts/dist/catalog-policy.js"(exports) {
460
+ "use strict";
461
+ Object.defineProperty(exports, "__esModule", {
462
+ value: true
463
+ });
464
+ exports.CATALOG_RULE_EFFECTS = exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = exports.CATALOG_DEFAULT_MODES = void 0;
465
+ exports.effectiveCatalogAccess = effectiveCatalogAccess;
466
+ var CATALOG_DEFAULT_MODE_SET = {
467
+ allow_all: true,
468
+ deny_all: true
469
+ };
470
+ exports.CATALOG_DEFAULT_MODES = Object.keys(CATALOG_DEFAULT_MODE_SET);
471
+ exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = "allow_all";
472
+ var CATALOG_RULE_EFFECT_SET = {
473
+ allow: true,
474
+ deny: true
475
+ };
476
+ exports.CATALOG_RULE_EFFECTS = Object.keys(CATALOG_RULE_EFFECT_SET);
477
+ var RULE_ACCESS = {
478
+ allow: "allowed",
479
+ deny: "blocked"
480
+ };
481
+ var UNRULED_ACCESS = {
482
+ allow_all: "allowed",
483
+ deny_all: "blocked"
484
+ };
485
+ function effectiveCatalogAccess(defaultMode, rule) {
486
+ return rule !== null ? RULE_ACCESS[rule] : UNRULED_ACCESS[defaultMode];
487
+ }
488
+ __name(effectiveCatalogAccess, "effectiveCatalogAccess");
489
+ }
490
+ });
491
+
446
492
  // ../contracts/dist/catalog.js
447
493
  var require_catalog = __commonJS({
448
494
  "../contracts/dist/catalog.js"(exports) {
@@ -450,7 +496,7 @@ var require_catalog = __commonJS({
450
496
  Object.defineProperty(exports, "__esModule", {
451
497
  value: true
452
498
  });
453
- exports.NEW_ACTION_WINDOW_DAYS = exports.PARAMETER_CONTROLS = exports.ACTION_LIFECYCLE_LABELS = exports.INACTIVE_ACTION_LIFECYCLES = exports.RUNNABLE_ACTION_LIFECYCLES = exports.LISTED_ACTION_LIFECYCLES = exports.ACTION_LIFECYCLE_STANDING = exports.ACTION_LIFECYCLES = void 0;
499
+ exports.NEW_ACTION_WINDOW_DAYS = exports.PARAMETER_ORDER_KEY = exports.PARAMETER_CONTROLS = exports.ACTION_LIFECYCLE_LABELS = exports.INACTIVE_ACTION_LIFECYCLES = exports.RUNNABLE_ACTION_LIFECYCLES = exports.LISTED_ACTION_LIFECYCLES = exports.ACTION_LIFECYCLE_STANDING = exports.ACTION_LIFECYCLES = void 0;
454
500
  exports.orderedParameterEntries = orderedParameterEntries;
455
501
  exports.withParameterOrder = withParameterOrder;
456
502
  exports.isNewAction = isNewAction;
@@ -513,9 +559,10 @@ var require_catalog = __commonJS({
513
559
  "switch",
514
560
  "info"
515
561
  ];
562
+ exports.PARAMETER_ORDER_KEY = "x-runveric-order";
516
563
  function orderedParameterEntries(schema) {
517
564
  const properties = schema.properties ?? {};
518
- const declared = schema["x-opera-order"] ?? [];
565
+ const declared = schema[exports.PARAMETER_ORDER_KEY] ?? [];
519
566
  const seen = /* @__PURE__ */ new Set();
520
567
  const ordered = [];
521
568
  for (const id of declared) {
@@ -540,7 +587,7 @@ var require_catalog = __commonJS({
540
587
  function withParameterOrder(schema) {
541
588
  return {
542
589
  ...schema,
543
- "x-opera-order": Object.keys(schema.properties ?? {})
590
+ [exports.PARAMETER_ORDER_KEY]: Object.keys(schema.properties ?? {})
544
591
  };
545
592
  }
546
593
  __name(withParameterOrder, "withParameterOrder");
@@ -557,6 +604,315 @@ var require_catalog = __commonJS({
557
604
  }
558
605
  });
559
606
 
607
+ // ../contracts/dist/catalog-release.js
608
+ var require_catalog_release = __commonJS({
609
+ "../contracts/dist/catalog-release.js"(exports) {
610
+ "use strict";
611
+ Object.defineProperty(exports, "__esModule", {
612
+ value: true
613
+ });
614
+ exports.ARTIFACT_DIGEST_PATTERN = exports.ARTIFACT_REPOSITORY_PATTERN = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = exports.CATALOG_RELEASE_SCHEMA_VERSIONS = void 0;
615
+ var wire_identity_1 = require_wire_identity();
616
+ exports.CATALOG_RELEASE_SCHEMA_VERSIONS = [
617
+ wire_identity_1.CATALOG_RELEASE_SCHEMA_V1
618
+ ];
619
+ exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = [
620
+ wire_identity_1.CATALOG_ACTION_ARTIFACT_SCHEMA_V1
621
+ ];
622
+ exports.ARTIFACT_REPOSITORY_PATTERN = /^[a-z0-9][a-z0-9.-]*(:[0-9]+)?(\/[a-z0-9][a-z0-9._-]*)+$/;
623
+ exports.ARTIFACT_DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/;
624
+ }
625
+ });
626
+
627
+ // ../contracts/dist/action-repository.js
628
+ var require_action_repository = __commonJS({
629
+ "../contracts/dist/action-repository.js"(exports) {
630
+ "use strict";
631
+ Object.defineProperty(exports, "__esModule", {
632
+ value: true
633
+ });
634
+ exports.ACTION_REPOSITORY_PREFIX = void 0;
635
+ exports.actionKeyRefusal = actionKeyRefusal;
636
+ exports.isActionKey = isActionKey;
637
+ exports.actionRepositoryName = actionRepositoryName;
638
+ exports.actionKeyOf = actionKeyOf;
639
+ exports.actionPackageRefusal = actionPackageRefusal3;
640
+ exports.ACTION_REPOSITORY_PREFIX = "action-";
641
+ var WORD = /^[a-z][a-z0-9]*$/;
642
+ var VERB = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*$/;
643
+ function actionKeyRefusal(key) {
644
+ const shape = "an action key is <provider>.<resource>.<verb>: provider and resource are one lowercase word (a-z, then a-z or 0-9), the verb is camelCase with no two capitals in a row \u2014 so the key maps to exactly one repository name and back.";
645
+ const segments = key.split(".");
646
+ if (segments.length !== 3) {
647
+ return `"${key}" is not an action key: it has ${segments.length} dot-separated segment(s), not 3; ${shape}`;
648
+ }
649
+ const [provider, resource, verb] = segments;
650
+ const not = /* @__PURE__ */ __name((what) => `"${key}" is not an action key: ${what}; ${shape}`, "not");
651
+ if (!WORD.test(provider)) return not(`its provider "${provider}" is not one lowercase word`);
652
+ if (!WORD.test(resource)) return not(`its resource "${resource}" is not one lowercase word`);
653
+ if (!VERB.test(verb)) return not(`its verb "${verb}" is not camelCase without two capitals in a row`);
654
+ return null;
655
+ }
656
+ __name(actionKeyRefusal, "actionKeyRefusal");
657
+ function isActionKey(key) {
658
+ return actionKeyRefusal(key) === null;
659
+ }
660
+ __name(isActionKey, "isActionKey");
661
+ var kebab = /* @__PURE__ */ __name((verb) => verb.replace(/[A-Z]/g, (capital) => `-${capital.toLowerCase()}`), "kebab");
662
+ function actionRepositoryName(key) {
663
+ const refusal = actionKeyRefusal(key);
664
+ if (refusal !== null) throw new Error(refusal);
665
+ const [provider, resource, verb] = key.split(".");
666
+ return `${exports.ACTION_REPOSITORY_PREFIX}${provider}-${resource}-${kebab(verb)}`;
667
+ }
668
+ __name(actionRepositoryName, "actionRepositoryName");
669
+ function actionKeyOf(name) {
670
+ if (!name.startsWith(exports.ACTION_REPOSITORY_PREFIX)) return null;
671
+ const [provider, resource, first2, ...rest] = name.slice(exports.ACTION_REPOSITORY_PREFIX.length).split("-");
672
+ if (provider === void 0 || resource === void 0 || first2 === void 0) return null;
673
+ const verb = first2 + rest.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
674
+ const key = `${provider}.${resource}.${verb}`;
675
+ return isActionKey(key) && actionRepositoryName(key) === name ? key : null;
676
+ }
677
+ __name(actionKeyOf, "actionKeyOf");
678
+ function actionPackageRefusal3(key, artifactRef) {
679
+ const keyRefusal = actionKeyRefusal(key);
680
+ if (keyRefusal !== null) return `${keyRefusal} No package can carry it.`;
681
+ const cut = artifactRef.lastIndexOf("/");
682
+ const named = artifactRef.slice(cut + 1);
683
+ const expected = actionRepositoryName(key);
684
+ if (named === expected) return null;
685
+ return `action "${key}" is published only from a package named "${expected}" (${artifactRef.slice(0, cut + 1)}${expected}), not "${named}". An action's package carries its key's name, so an artifact cannot claim an identity its package does not carry.`;
686
+ }
687
+ __name(actionPackageRefusal3, "actionPackageRefusal");
688
+ }
689
+ });
690
+
691
+ // ../contracts/dist/catalog-release-spec.js
692
+ var require_catalog_release_spec = __commonJS({
693
+ "../contracts/dist/catalog-release-spec.js"(exports) {
694
+ "use strict";
695
+ Object.defineProperty(exports, "__esModule", {
696
+ value: true
697
+ });
698
+ exports.CatalogReleaseSpecError = void 0;
699
+ exports.splitArtifactReference = splitArtifactReference;
700
+ exports.parseCatalogReleaseSpec = parseCatalogReleaseSpec2;
701
+ var CatalogReleaseSpecError = class CatalogReleaseSpecError extends Error {
702
+ static {
703
+ __name(this, "CatalogReleaseSpecError");
704
+ }
705
+ constructor(message) {
706
+ super(message);
707
+ this.name = "CatalogReleaseSpecError";
708
+ }
709
+ };
710
+ exports.CatalogReleaseSpecError = CatalogReleaseSpecError;
711
+ function splitArtifactReference(reference) {
712
+ const at = reference.indexOf("@");
713
+ if (at <= 0 || at === reference.length - 1) return null;
714
+ return {
715
+ artifactRef: reference.slice(0, at),
716
+ digest: reference.slice(at + 1)
717
+ };
718
+ }
719
+ __name(splitArtifactReference, "splitArtifactReference");
720
+ var isMapping2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
721
+ function parseCatalogReleaseSpec2(value) {
722
+ if (!isMapping2(value)) {
723
+ throw new CatalogReleaseSpecError("release.yaml is not a mapping of `version` and `actions`.");
724
+ }
725
+ const unknown = Object.keys(value).filter((key) => key !== "version" && key !== "actions");
726
+ if (unknown.length > 0) {
727
+ throw new CatalogReleaseSpecError(`release.yaml carries ${unknown.map((key) => `"${key}"`).join(", ")}; it has exactly two fields, \`version\` and \`actions\`.`);
728
+ }
729
+ const { version: version2, actions } = value;
730
+ if (typeof version2 !== "string" || version2.trim().length === 0) {
731
+ throw new CatalogReleaseSpecError(`release.yaml's version is ${JSON.stringify(version2 ?? null)}, not a label. Write it as a quoted string \u2014 version: "2026.10.1" \u2014 because YAML reads an unquoted 2026.10 as a number.`);
732
+ }
733
+ if (actions === null || actions === void 0 || isMapping2(actions) && Object.keys(actions).length === 0) {
734
+ throw new CatalogReleaseSpecError("release.yaml declares no action. An empty release is refused: pinned, it would declare a catalog with nothing in it.");
735
+ }
736
+ if (!isMapping2(actions)) {
737
+ throw new CatalogReleaseSpecError("release.yaml's actions is not a mapping of action key to <repository>@<digest>.");
738
+ }
739
+ return {
740
+ version: version2,
741
+ actions: Object.entries(actions).map(([key, reference]) => {
742
+ const split = typeof reference === "string" ? splitArtifactReference(reference) : null;
743
+ if (split === null) {
744
+ throw new CatalogReleaseSpecError(`release.yaml's ${key} is ${JSON.stringify(reference)}, not <repository>@<digest>.`);
745
+ }
746
+ return {
747
+ key,
748
+ ...split
749
+ };
750
+ })
751
+ };
752
+ }
753
+ __name(parseCatalogReleaseSpec2, "parseCatalogReleaseSpec");
754
+ }
755
+ });
756
+
757
+ // ../contracts/dist/catalog-lock.js
758
+ var require_catalog_lock = __commonJS({
759
+ "../contracts/dist/catalog-lock.js"(exports) {
760
+ "use strict";
761
+ Object.defineProperty(exports, "__esModule", {
762
+ value: true
763
+ });
764
+ exports.CatalogLockError = exports.CATALOG_LOCK_FILE = exports.CATALOG_LOCK_SCHEMA_V1 = void 0;
765
+ exports.parseCatalogLock = parseCatalogLock;
766
+ exports.catalogLockText = catalogLockText;
767
+ exports.authoredElsewhere = authoredElsewhere;
768
+ var action_repository_1 = require_action_repository();
769
+ var catalog_release_1 = require_catalog_release();
770
+ exports.CATALOG_LOCK_SCHEMA_V1 = "runveric.catalog-lock/v1";
771
+ exports.CATALOG_LOCK_FILE = "release.lock.json";
772
+ var CatalogLockError = class CatalogLockError extends Error {
773
+ static {
774
+ __name(this, "CatalogLockError");
775
+ }
776
+ constructor(message) {
777
+ super(message);
778
+ this.name = "CatalogLockError";
779
+ }
780
+ };
781
+ exports.CatalogLockError = CatalogLockError;
782
+ var SHA256_HEX = /^[0-9a-f]{64}$/;
783
+ function objectAt(path, value, keys) {
784
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
785
+ throw new CatalogLockError(`${path} is not an object.`);
786
+ }
787
+ const unknown = Object.keys(value).filter((key) => !keys.includes(key));
788
+ if (unknown.length > 0) {
789
+ throw new CatalogLockError(`${path} carries ${unknown.map((key) => `"${key}"`).join(", ")}, which a lock does not have. A lock is written by catalog:vendor and never edited; re-vendor instead.`);
790
+ }
791
+ return value;
792
+ }
793
+ __name(objectAt, "objectAt");
794
+ function stringAt(path, value, pattern, expected) {
795
+ if (typeof value !== "string" || value.length === 0) {
796
+ throw new CatalogLockError(`${path} is not a non-empty string.`);
797
+ }
798
+ if (pattern !== void 0 && !pattern.test(value)) {
799
+ throw new CatalogLockError(`${path} is "${value}", which is not ${expected}.`);
800
+ }
801
+ return value;
802
+ }
803
+ __name(stringAt, "stringAt");
804
+ function parseCatalogLock(value) {
805
+ const root = objectAt("the lock", value, [
806
+ "schemaVersion",
807
+ "release",
808
+ "actions"
809
+ ]);
810
+ if (root.schemaVersion !== exports.CATALOG_LOCK_SCHEMA_V1) {
811
+ throw new CatalogLockError(`the lock declares schemaVersion ${JSON.stringify(root.schemaVersion)}; this build reads ${exports.CATALOG_LOCK_SCHEMA_V1} only.`);
812
+ }
813
+ const release = objectAt("release", root.release, [
814
+ "artifactRef",
815
+ "digest",
816
+ "version"
817
+ ]);
818
+ const lock = {
819
+ schemaVersion: exports.CATALOG_LOCK_SCHEMA_V1,
820
+ release: {
821
+ artifactRef: stringAt("release.artifactRef", release.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
822
+ digest: stringAt("release.digest", release.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
823
+ version: stringAt("release.version", release.version)
824
+ },
825
+ actions: []
826
+ };
827
+ if (!Array.isArray(root.actions) || root.actions.length === 0) {
828
+ throw new CatalogLockError("actions is not a non-empty array. A release names at least one action.");
829
+ }
830
+ root.actions.forEach((entry, index) => {
831
+ const path = `actions[${index}]`;
832
+ const action = objectAt(path, entry, [
833
+ "key",
834
+ "artifactRef",
835
+ "digest",
836
+ "manifestSha256",
837
+ "provenance"
838
+ ]);
839
+ const key = stringAt(`${path}.key`, action.key);
840
+ const artifactRef = stringAt(`${path}.artifactRef`, action.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository");
841
+ const misnamed = (0, action_repository_1.actionPackageRefusal)(key, artifactRef);
842
+ if (misnamed !== null) throw new CatalogLockError(`${path}: ${misnamed}`);
843
+ const previous = lock.actions[lock.actions.length - 1];
844
+ if (previous !== void 0 && !(previous.key < key)) {
845
+ throw new CatalogLockError(`${path}.key "${key}" follows "${previous.key}": a lock names each action once, sorted by key.`);
846
+ }
847
+ const provenance2 = objectAt(`${path}.provenance`, action.provenance, [
848
+ "repository",
849
+ "commit"
850
+ ]);
851
+ lock.actions.push({
852
+ key,
853
+ artifactRef,
854
+ digest: stringAt(`${path}.digest`, action.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
855
+ manifestSha256: stringAt(`${path}.manifestSha256`, action.manifestSha256, SHA256_HEX, "64 lowercase hex characters"),
856
+ provenance: {
857
+ repository: stringAt(`${path}.provenance.repository`, provenance2.repository),
858
+ commit: stringAt(`${path}.provenance.commit`, provenance2.commit)
859
+ }
860
+ });
861
+ });
862
+ return lock;
863
+ }
864
+ __name(parseCatalogLock, "parseCatalogLock");
865
+ function catalogLockText(lock) {
866
+ const ordered = {
867
+ schemaVersion: lock.schemaVersion,
868
+ release: {
869
+ artifactRef: lock.release.artifactRef,
870
+ digest: lock.release.digest,
871
+ version: lock.release.version
872
+ },
873
+ actions: [
874
+ ...lock.actions
875
+ ].sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map((action) => ({
876
+ key: action.key,
877
+ artifactRef: action.artifactRef,
878
+ digest: action.digest,
879
+ manifestSha256: action.manifestSha256,
880
+ provenance: {
881
+ repository: action.provenance.repository,
882
+ commit: action.provenance.commit
883
+ }
884
+ }))
885
+ };
886
+ return `${JSON.stringify(ordered, null, 2)}
887
+ `;
888
+ }
889
+ __name(catalogLockText, "catalogLockText");
890
+ function authoredElsewhere(lock, key) {
891
+ const namespace = lock.release.artifactRef.slice(0, lock.release.artifactRef.lastIndexOf("/"));
892
+ const organisation = namespace.slice(namespace.lastIndexOf("/") + 1);
893
+ return `${key} is authored in ${organisation}/${(0, action_repository_1.actionRepositoryName)(key)} \u2014 change it there, publish, assemble, vendor`;
894
+ }
895
+ __name(authoredElsewhere, "authoredElsewhere");
896
+ }
897
+ });
898
+
899
+ // ../contracts/dist/catalog-manifest-placeholders.js
900
+ var require_catalog_manifest_placeholders = __commonJS({
901
+ "../contracts/dist/catalog-manifest-placeholders.js"(exports) {
902
+ "use strict";
903
+ Object.defineProperty(exports, "__esModule", {
904
+ value: true
905
+ });
906
+ exports.MANIFEST_PLACEHOLDER_PATTERN = exports.MANIFEST_PLACEHOLDERS = void 0;
907
+ var brand_1 = require_brand();
908
+ exports.MANIFEST_PLACEHOLDERS = {
909
+ BRAND_SHORT_NAME: brand_1.BRAND_SHORT_NAME,
910
+ BRAND_NAME: brand_1.BRAND_NAME
911
+ };
912
+ exports.MANIFEST_PLACEHOLDER_PATTERN = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
913
+ }
914
+ });
915
+
560
916
  // ../contracts/dist/change-control.js
561
917
  var require_change_control = __commonJS({
562
918
  "../contracts/dist/change-control.js"(exports) {
@@ -856,8 +1212,8 @@ var require_package_provenance = __commonJS({
856
1212
  pair.accepted,
857
1213
  pair.revoked
858
1214
  ]);
859
- function poolProvenanceAuditAction(provenance, accepted) {
860
- const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[provenance];
1215
+ function poolProvenanceAuditAction(provenance2, accepted) {
1216
+ const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[provenance2];
861
1217
  return accepted ? pair.accepted : pair.revoked;
862
1218
  }
863
1219
  __name(poolProvenanceAuditAction, "poolProvenanceAuditAction");
@@ -916,7 +1272,9 @@ var require_packages = __commonJS({
916
1272
  Object.defineProperty(exports, "__esModule", {
917
1273
  value: true
918
1274
  });
919
- exports.PACKAGE_IMPORT_WARNING_REASONS = exports.PACKAGE_IMPORT_SKIP_REASON_LABELS = exports.PACKAGE_IMPORT_SKIP_REASONS = exports.PACKAGE_IMPORT_LIMITS = exports.PACKAGE_DRAFT_FILTER_LABELS = exports.PACKAGE_DRAFT_FILTERS = exports.PACKAGE_INTERPRETER_LABEL = exports.PARAMETER_DETECTION_UNAVAILABLE = exports.PARAMETER_DETECTION_SOURCES = exports.PACKAGE_PARAMETERS_UNRESOLVED_COPY = exports.PACKAGE_PARAMETERS_UNRESOLVED_REASONS = exports.PACKAGE_DRAFT_STATES = exports.PACKAGE_VERSION_MODES = exports.ACTION_UNRUNNABLE_REASONS = exports.PACKAGE_SOURCE_RECONNECT_OUTCOMES = exports.ADOPTION_REVIEW_DIRECTIONS = exports.ADOPTION_CHANGE_SEVERITY = exports.ADOPTION_CHANGE_KINDS = exports.ADOPTION_CHANGE_SEVERITIES = exports.ADOPTION_STATUSES = exports.PACKAGE_INTERPRETERS = exports.PACKAGE_RUNTIME_KINDS = exports.PACKAGE_RUNNER_CONNECTOR_KEY = void 0;
1275
+ exports.PACKAGE_IMPORT_WARNING_REASONS = exports.PACKAGE_IMPORT_SKIP_REASON_LABELS = exports.PACKAGE_IMPORT_SKIP_REASONS = exports.PACKAGE_IMPORT_LIMITS = exports.PACKAGE_DRAFT_FILTER_LABELS = exports.PACKAGE_DRAFT_FILTERS = exports.PACKAGE_INTERPRETER_LABEL = exports.PARAMETER_DETECTION_UNAVAILABLE = exports.PARAMETER_DETECTION_SOURCES = exports.PACKAGE_PARAMETERS_UNRESOLVED_COPY = exports.PACKAGE_PARAMETERS_UNRESOLVED_REASONS = exports.PACKAGE_DRAFT_STATES = exports.PACKAGE_VERSION_MODES = exports.ACTION_UNRUNNABLE_REASONS = exports.PACKAGE_SOURCE_RECONNECT_OUTCOMES = exports.ADOPTION_REVIEW_DIRECTIONS = exports.ADOPTION_CHANGE_SEVERITY = exports.ADOPTION_CHANGE_KINDS = exports.ADOPTION_CHANGE_SEVERITIES = exports.ADOPTION_STATUSES = exports.PACKAGE_INTERPRETERS = exports.PACKAGE_RUNTIME_KINDS = exports.LEGACY_PACKAGE_RUNNER_CONNECTOR_KEY = exports.PACKAGE_RUNNER_CONNECTOR_KEY = void 0;
1276
+ exports.isPackageRunnerConnectorKey = isPackageRunnerConnectorKey;
1277
+ exports.canonicalConnectorKey = canonicalConnectorKey;
920
1278
  exports.packageSourceConnectedRemovalMessage = packageSourceConnectedRemovalMessage;
921
1279
  exports.packageVersionAdoptedRemovalMessage = packageVersionAdoptedRemovalMessage;
922
1280
  exports.actionUnrunnableCopy = actionUnrunnableCopy;
@@ -927,7 +1285,16 @@ var require_packages = __commonJS({
927
1285
  __exportStar(require_package_provenance(), exports);
928
1286
  __exportStar(require_package_tier(), exports);
929
1287
  var brand_1 = require_brand();
930
- exports.PACKAGE_RUNNER_CONNECTOR_KEY = "opera.package-runner";
1288
+ exports.PACKAGE_RUNNER_CONNECTOR_KEY = "runveric.package-runner";
1289
+ exports.LEGACY_PACKAGE_RUNNER_CONNECTOR_KEY = "opera.package-runner";
1290
+ function isPackageRunnerConnectorKey(connectorKey) {
1291
+ return connectorKey === exports.PACKAGE_RUNNER_CONNECTOR_KEY || connectorKey === exports.LEGACY_PACKAGE_RUNNER_CONNECTOR_KEY;
1292
+ }
1293
+ __name(isPackageRunnerConnectorKey, "isPackageRunnerConnectorKey");
1294
+ function canonicalConnectorKey(connectorKey) {
1295
+ return isPackageRunnerConnectorKey(connectorKey) ? exports.PACKAGE_RUNNER_CONNECTOR_KEY : connectorKey;
1296
+ }
1297
+ __name(canonicalConnectorKey, "canonicalConnectorKey");
931
1298
  exports.PACKAGE_RUNTIME_KINDS = [
932
1299
  "interpreter",
933
1300
  "oci"
@@ -1248,7 +1615,7 @@ var require_packages = __commonJS({
1248
1615
  "editor_metadata",
1249
1616
  "os_metadata",
1250
1617
  "lockfile",
1251
- "opera_scratch",
1618
+ "runveric_scratch",
1252
1619
  "not_text"
1253
1620
  ];
1254
1621
  exports.PACKAGE_IMPORT_SKIP_REASON_LABELS = {
@@ -1258,7 +1625,7 @@ var require_packages = __commonJS({
1258
1625
  editor_metadata: "editor settings",
1259
1626
  os_metadata: "operating-system metadata",
1260
1627
  lockfile: "a dependency lockfile",
1261
- opera_scratch: "scratch written by `runveric test`",
1628
+ runveric_scratch: "scratch written by `runveric test`",
1262
1629
  not_text: "not UTF-8 text"
1263
1630
  };
1264
1631
  exports.PACKAGE_IMPORT_WARNING_REASONS = [
@@ -1274,7 +1641,7 @@ var require_validation = __commonJS({
1274
1641
  Object.defineProperty(exports, "__esModule", {
1275
1642
  value: true
1276
1643
  });
1277
- exports.TARGET_COMPARISON_KINDS = exports.TARGETS_NO_SCOPE = exports.TARGETS_NOT_COUNTED = exports.TARGET_COUNT_ABSENCES = void 0;
1644
+ exports.TARGET_COMPARISON_KINDS = exports.DECISION_CODES = exports.REFUSAL_CODES = exports.CHECK_LEVELS = exports.TARGETS_NO_SCOPE = exports.TARGETS_NOT_COUNTED = exports.TARGET_COUNT_ABSENCES = void 0;
1278
1645
  exports.isTargetCountAbsence = isTargetCountAbsence;
1279
1646
  exports.singleTarget = singleTarget;
1280
1647
  exports.noTargetsResolved = noTargetsResolved;
@@ -1284,6 +1651,9 @@ var require_validation = __commonJS({
1284
1651
  exports.eligibleTargetCount = eligibleTargetCount;
1285
1652
  exports.targetsValidationCheck = targetsValidationCheck;
1286
1653
  exports.targetSummaryOf = targetSummaryOf;
1654
+ exports.isCheckLevel = isCheckLevel;
1655
+ exports.isRefusalCode = isRefusalCode;
1656
+ exports.isDecisionCode = isDecisionCode;
1287
1657
  exports.scopeFailureDetail = scopeFailureDetail;
1288
1658
  exports.targetsUnavailableFrom = targetsUnavailableFrom;
1289
1659
  exports.compareTargets = compareTargets;
@@ -1400,6 +1770,38 @@ var require_validation = __commonJS({
1400
1770
  return exports.TARGETS_NOT_COUNTED;
1401
1771
  }
1402
1772
  __name(targetSummaryOf, "targetSummaryOf");
1773
+ exports.CHECK_LEVELS = [
1774
+ "info",
1775
+ "warn",
1776
+ "error"
1777
+ ];
1778
+ function isCheckLevel(value) {
1779
+ return typeof value === "string" && exports.CHECK_LEVELS.includes(value);
1780
+ }
1781
+ __name(isCheckLevel, "isCheckLevel");
1782
+ exports.REFUSAL_CODES = [
1783
+ "disk.notUnattached",
1784
+ "disk.locked",
1785
+ "disk.shared",
1786
+ "disk.inRecoveryPlan",
1787
+ "disk.keyUnusable",
1788
+ "disk.unverifiable"
1789
+ ];
1790
+ function isRefusalCode(value) {
1791
+ return typeof value === "string" && exports.REFUSAL_CODES.includes(value);
1792
+ }
1793
+ __name(isRefusalCode, "isRefusalCode");
1794
+ exports.DECISION_CODES = [
1795
+ "disk.recentlyDetached",
1796
+ "disk.customerKey",
1797
+ "disk.customerKeyUnverified",
1798
+ "disk.guestEncrypted",
1799
+ "disk.noSnapshot"
1800
+ ];
1801
+ function isDecisionCode(value) {
1802
+ return typeof value === "string" && exports.DECISION_CODES.includes(value);
1803
+ }
1804
+ __name(isDecisionCode, "isDecisionCode");
1403
1805
  function scopeFailureDetail(error) {
1404
1806
  const raw = error instanceof Error ? error.message : String(error);
1405
1807
  const oneLine = raw.replace(/\s+/g, " ").trim().replace(/[.!]+$/, "");
@@ -1470,7 +1872,7 @@ var require_execution = __commonJS({
1470
1872
  Object.defineProperty(exports, "__esModule", {
1471
1873
  value: true
1472
1874
  });
1473
- exports.ACCEPTING_A_PROVENANCE_IS_NOT_A_CAPABILITY = exports.RUNNER_PACKAGE_RUNTIME_NOT_PINNED = exports.CONNECTOR_UNAVAILABLE_MESSAGE = exports.ACTION_RUNNABILITY_CAUSE_KINDS = exports.EXECUTION_LOG_ACTIVITY_PHRASE = exports.EXECUTION_LOG_CONNECTION_PHRASE = exports.EXECUTION_LOG_STREAM_PHASES = exports.EXECUTION_LOG_ACTIVITIES = exports.targetsValidationCheck = exports.targetSummarySentence = exports.targetSummaryOf = exports.targetSummaryLabel = exports.targetsUnavailableFrom = exports.targetsUnavailable = exports.targetComparisonSentence = exports.targetComparisonLevel = exports.targetComparisonLabel = exports.TARGETS_NOT_COUNTED = exports.TARGETS_NO_SCOPE = exports.TARGET_COUNT_ABSENCES = exports.TARGET_COMPARISON_KINDS = exports.singleTarget = exports.scopeFailureDetail = exports.noTargetsResolved = exports.isTargetCountAbsence = exports.eligibleTargetCount = exports.compareTargets = exports.SUBSCRIPTION_ID_FIELD_RENAMED = void 0;
1875
+ exports.ACCEPTING_A_PROVENANCE_IS_NOT_A_CAPABILITY = exports.RUNNER_PACKAGE_RUNTIME_NOT_PINNED = exports.CONNECTOR_UNAVAILABLE_MESSAGE = exports.ACTION_RUNNABILITY_CAUSE_KINDS = exports.EXECUTION_LOG_ACTIVITY_PHRASE = exports.EXECUTION_LOG_CONNECTION_PHRASE = exports.EXECUTION_LOG_STREAM_PHASES = exports.EXECUTION_LOG_ACTIVITIES = exports.targetsValidationCheck = exports.targetSummarySentence = exports.targetSummaryOf = exports.targetSummaryLabel = exports.targetsUnavailableFrom = exports.targetsUnavailable = exports.targetComparisonSentence = exports.targetComparisonLevel = exports.targetComparisonLabel = exports.TARGETS_NOT_COUNTED = exports.TARGETS_NO_SCOPE = exports.TARGET_COUNT_ABSENCES = exports.TARGET_COMPARISON_KINDS = exports.singleTarget = exports.scopeFailureDetail = exports.noTargetsResolved = exports.isTargetCountAbsence = exports.eligibleTargetCount = exports.compareTargets = exports.REFUSAL_CODES = exports.isRefusalCode = exports.isDecisionCode = exports.isCheckLevel = exports.DECISION_CODES = exports.CHECK_LEVELS = exports.SUBSCRIPTION_ID_FIELD_RENAMED = void 0;
1474
1876
  exports.executionLogStreamSentence = executionLogStreamSentence;
1475
1877
  exports.actionRunnabilityCopy = actionRunnabilityCopy;
1476
1878
  exports.actionRunnabilityHeadline = actionRunnabilityHeadline;
@@ -1481,6 +1883,42 @@ var require_execution = __commonJS({
1481
1883
  var brand_1 = require_brand();
1482
1884
  exports.SUBSCRIPTION_ID_FIELD_RENAMED = "`subscriptionId` is not accepted on this route. It named an `azure_subscription.id` while this route has always resolved a `connection_scope.id`. The two were the same value while one shim gave a scope the registry row's id; a scope discovered since has an id of its own, and the registry id is either nobody's scope or another connector's. Send `connectionScopeId` \u2014 the value `ExecutionDto.connectionScopeId` reports for a run.";
1483
1885
  var validation_1 = require_validation();
1886
+ Object.defineProperty(exports, "CHECK_LEVELS", {
1887
+ enumerable: true,
1888
+ get: /* @__PURE__ */ __name(function() {
1889
+ return validation_1.CHECK_LEVELS;
1890
+ }, "get")
1891
+ });
1892
+ Object.defineProperty(exports, "DECISION_CODES", {
1893
+ enumerable: true,
1894
+ get: /* @__PURE__ */ __name(function() {
1895
+ return validation_1.DECISION_CODES;
1896
+ }, "get")
1897
+ });
1898
+ Object.defineProperty(exports, "isCheckLevel", {
1899
+ enumerable: true,
1900
+ get: /* @__PURE__ */ __name(function() {
1901
+ return validation_1.isCheckLevel;
1902
+ }, "get")
1903
+ });
1904
+ Object.defineProperty(exports, "isDecisionCode", {
1905
+ enumerable: true,
1906
+ get: /* @__PURE__ */ __name(function() {
1907
+ return validation_1.isDecisionCode;
1908
+ }, "get")
1909
+ });
1910
+ Object.defineProperty(exports, "isRefusalCode", {
1911
+ enumerable: true,
1912
+ get: /* @__PURE__ */ __name(function() {
1913
+ return validation_1.isRefusalCode;
1914
+ }, "get")
1915
+ });
1916
+ Object.defineProperty(exports, "REFUSAL_CODES", {
1917
+ enumerable: true,
1918
+ get: /* @__PURE__ */ __name(function() {
1919
+ return validation_1.REFUSAL_CODES;
1920
+ }, "get")
1921
+ });
1484
1922
  Object.defineProperty(exports, "compareTargets", {
1485
1923
  enumerable: true,
1486
1924
  get: /* @__PURE__ */ __name(function() {
@@ -1622,6 +2060,7 @@ var require_execution = __commonJS({
1622
2060
  }
1623
2061
  __name(executionLogStreamSentence, "executionLogStreamSentence");
1624
2062
  var ACTION_RUNNABILITY_CAUSE_KIND_SET = {
2063
+ catalog_blocked: true,
1625
2064
  connector_unregistered: true,
1626
2065
  package: true,
1627
2066
  runner_pool: true,
@@ -1631,8 +2070,12 @@ var require_execution = __commonJS({
1631
2070
  };
1632
2071
  exports.ACTION_RUNNABILITY_CAUSE_KINDS = Object.keys(ACTION_RUNNABILITY_CAUSE_KIND_SET);
1633
2072
  exports.CONNECTOR_UNAVAILABLE_MESSAGE = "This action is not yet available to run.";
2073
+ var CATALOG_BLOCKED_CAUSE = "This workspace does not permit this action.";
2074
+ var CATALOG_BLOCKED_REMEDY = "An administrator of this workspace decides which catalog actions may run here, and can allow it.";
1634
2075
  function actionRunnabilityCopy(cause) {
1635
2076
  switch (cause.kind) {
2077
+ case "catalog_blocked":
2078
+ return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY}`;
1636
2079
  case "package":
1637
2080
  return (0, packages_2.actionUnrunnableCopy)(cause.detail);
1638
2081
  case "runner_pool": {
@@ -1664,7 +2107,7 @@ var require_execution = __commonJS({
1664
2107
  }
1665
2108
  __name(describeRoutingPlaces, "describeRoutingPlaces");
1666
2109
  function runnerPoolClauses(cause) {
1667
- const provenance = cause.provenance ? `is ${packages_1.PACKAGE_PROVENANCE_CLAUSES[cause.provenance]}` : (
2110
+ const provenance2 = cause.provenance ? `is ${packages_1.PACKAGE_PROVENANCE_CLAUSES[cause.provenance]}` : (
1668
2111
  // every other value this sentence could take is a claim about where bytes came from.
1669
2112
  "has no recorded provenance"
1670
2113
  );
@@ -1682,7 +2125,7 @@ var require_execution = __commonJS({
1682
2125
  * credential it acts with. A refusal that dropped those would be telling an admin the
1683
2126
  * decision no longer matters.
1684
2127
  */
1685
- cause: `${provenance}, and would run on the private runner pool "${cause.poolName}", because that is where runs using this connection execute \u2014 on a machine you own, in a container that shares its host's kernel and acts with this connection's credential.`,
2128
+ cause: `${provenance2}, and would run on the private runner pool "${cause.poolName}", because that is where runs using this connection execute \u2014 on a machine you own, in a container that shares its host's kernel and acts with this connection's credential.`,
1686
2129
  remedy: `${acceptedClause(cause.accepts)} ${POOL_CONSENT_REMEDY}`
1687
2130
  };
1688
2131
  }
@@ -1721,6 +2164,8 @@ var require_execution = __commonJS({
1721
2164
  var POOL_CONSENT_REMEDY = `${brand_1.BRAND_SHORT_NAME} does not vouch for what runs on your machines \u2014 the owner of this pool does. An administrator can accept this provenance under Settings \u2192 Runners, and until they do, nothing of this provenance will be sent there. Built-in actions still run in that pool.`;
1722
2165
  function actionRunnabilityHeadline(cause) {
1723
2166
  switch (cause.kind) {
2167
+ case "catalog_blocked":
2168
+ return "this workspace does not permit its action \u2014 an administrator of this workspace can allow it";
1724
2169
  case "package":
1725
2170
  return (0, packages_2.actionUnrunnableHeadline)(cause.detail);
1726
2171
  case "runner_pool":
@@ -1738,6 +2183,8 @@ var require_execution = __commonJS({
1738
2183
  __name(actionRunnabilityHeadline, "actionRunnabilityHeadline");
1739
2184
  function actionRunnabilityRefusal(cause) {
1740
2185
  switch (cause.kind) {
2186
+ case "catalog_blocked":
2187
+ return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY} Nothing was started.`;
1741
2188
  case "package":
1742
2189
  return (0, packages_2.actionUnrunnableRefusal)(cause.detail, cause);
1743
2190
  case "runner_pool": {
@@ -1762,6 +2209,80 @@ var require_execution = __commonJS({
1762
2209
  }
1763
2210
  });
1764
2211
 
2212
+ // ../contracts/dist/decisions.js
2213
+ var require_decisions = __commonJS({
2214
+ "../contracts/dist/decisions.js"(exports) {
2215
+ "use strict";
2216
+ Object.defineProperty(exports, "__esModule", {
2217
+ value: true
2218
+ });
2219
+ exports.DECISION_ACKNOWLEDGEMENT = void 0;
2220
+ exports.recordedDecisionsOf = recordedDecisionsOf;
2221
+ exports.raisedDecisionCodes = raisedDecisionCodes;
2222
+ exports.decisionMismatch = decisionMismatch;
2223
+ exports.decisionMismatchSentence = decisionMismatchSentence;
2224
+ var validation_1 = require_validation();
2225
+ function recordedDecisionsOf(stored) {
2226
+ if (!Array.isArray(stored)) return [];
2227
+ const read = [];
2228
+ for (const entry of stored) {
2229
+ if (typeof entry !== "object" || entry === null) continue;
2230
+ const { code, acknowledgedBy, acknowledgedAt } = entry;
2231
+ if (!(0, validation_1.isDecisionCode)(code)) continue;
2232
+ if (typeof acknowledgedBy !== "string" || acknowledgedBy.length === 0) continue;
2233
+ if (typeof acknowledgedAt !== "string" || Number.isNaN(Date.parse(acknowledgedAt))) continue;
2234
+ if (read.some((decision) => decision.code === code)) continue;
2235
+ read.push({
2236
+ code,
2237
+ acknowledgedBy,
2238
+ acknowledgedAt
2239
+ });
2240
+ }
2241
+ return read;
2242
+ }
2243
+ __name(recordedDecisionsOf, "recordedDecisionsOf");
2244
+ function raisedDecisionCodes(checks) {
2245
+ const raised = [];
2246
+ for (const check of checks) {
2247
+ if (check.decision && !raised.includes(check.decision)) raised.push(check.decision);
2248
+ }
2249
+ return raised;
2250
+ }
2251
+ __name(raisedDecisionCodes, "raisedDecisionCodes");
2252
+ function decisionMismatch(raised, acknowledged) {
2253
+ const unacknowledged = raised.filter((code) => !acknowledged.includes(code));
2254
+ const notRaised = acknowledged.filter((code) => !raised.includes(code));
2255
+ return unacknowledged.length === 0 && notRaised.length === 0 ? null : {
2256
+ unacknowledged,
2257
+ notRaised
2258
+ };
2259
+ }
2260
+ __name(decisionMismatch, "decisionMismatch");
2261
+ function decisionMismatchSentence(mismatch) {
2262
+ const parts = [];
2263
+ if (mismatch.unacknowledged.length > 0) {
2264
+ parts.push(`the plan raised ${quoteCodes(mismatch.unacknowledged)}, which ${mismatch.unacknowledged.length === 1 ? "was" : "were"} not acknowledged`);
2265
+ }
2266
+ if (mismatch.notRaised.length > 0) {
2267
+ parts.push(`${quoteCodes(mismatch.notRaised)} ${mismatch.notRaised.length === 1 ? "was" : "were"} acknowledged but the plan does not raise ${mismatch.notRaised.length === 1 ? "it" : "them"}`);
2268
+ }
2269
+ return `This run was not submitted: ${parts.join(", and ")}. The checks were run again at submit and decide what needs acknowledging \u2014 review the plan and submit again.`;
2270
+ }
2271
+ __name(decisionMismatchSentence, "decisionMismatchSentence");
2272
+ function quoteCodes(codes) {
2273
+ return codes.map((code) => `"${code}"`).join(", ");
2274
+ }
2275
+ __name(quoteCodes, "quoteCodes");
2276
+ exports.DECISION_ACKNOWLEDGEMENT = {
2277
+ "disk.recentlyDetached": "Delete it even though it was detached recently",
2278
+ "disk.customerKey": "Delete it \u2014 restoring later depends on this key staying enabled",
2279
+ "disk.customerKeyUnverified": "Delete it without confirmation that the key is enabled",
2280
+ "disk.guestEncrypted": "Delete it \u2014 restoring later depends on the guest's encryption secrets",
2281
+ "disk.noSnapshot": "Delete it with no snapshot \u2014 this cannot be undone"
2282
+ };
2283
+ }
2284
+ });
2285
+
1765
2286
  // ../contracts/dist/execution-site.js
1766
2287
  var require_execution_site = __commonJS({
1767
2288
  "../contracts/dist/execution-site.js"(exports) {
@@ -1846,7 +2367,13 @@ var require_resources = __commonJS({
1846
2367
  Object.defineProperty(exports, "__esModule", {
1847
2368
  value: true
1848
2369
  });
1849
- exports.DISCOVERY_TYPE_PATTERN = exports.DISCOVERABLE_RESOURCE_TYPES = exports.DISCOVERY_VOCABULARY = void 0;
2370
+ exports.DISCOVERY_TYPE_PATTERN = exports.LEGACY_DISCOVERY_PROVIDER = exports.DISCOVERY_VOCABULARY = void 0;
2371
+ exports.discoveryTypesOf = discoveryTypesOf;
2372
+ exports.declaredDiscoveryTypes = declaredDiscoveryTypes;
2373
+ exports.liveDiscoveryTypes = liveDiscoveryTypes;
2374
+ exports.canonicalDiscoveryId = canonicalDiscoveryId;
2375
+ exports.resolveDiscoveryType = resolveDiscoveryType;
2376
+ exports.isSecretReference = isSecretReference;
1850
2377
  exports.parseDiscoveryType = parseDiscoveryType;
1851
2378
  exports.formatDiscoveryType = formatDiscoveryType;
1852
2379
  exports.discoveryTypeSpec = discoveryTypeSpec;
@@ -1862,16 +2389,20 @@ var require_resources = __commonJS({
1862
2389
  resourceGroup: {
1863
2390
  requires: [],
1864
2391
  routable: false,
1865
- identity: null
2392
+ identity: null,
2393
+ secretRef: false
1866
2394
  },
1867
2395
  // ROUTABLE BUT NOT NAMABLE, and the two are different questions. A rule matches a vault by
1868
2396
  // its NAME and its tags, which discovery supplies; an ARM vault id needs the resource group
1869
2397
  // this vocabulary never asks for (`requires` is empty so vaults list subscription-wide). See
1870
2398
  // `identity` on the spec above — null is the honest answer, not an omission.
2399
+ // NOT a secret reference: a vault CONTAINS secrets, and naming one reveals nothing a run
2400
+ // could read. The secret inside it is the reference (`keyVaultSecret` below).
1871
2401
  keyVault: {
1872
2402
  requires: [],
1873
2403
  routable: true,
1874
- identity: null
2404
+ identity: null,
2405
+ secretRef: false
1875
2406
  },
1876
2407
  // ★ A DATA-PLANE URI, NOT AN ARM PATH, AND IT IS WHAT DISCOVERY ALREADY RETURNS. The Key
1877
2408
  // Vault SDK reports `SecretProperties.id` as `https://<vault>.vault.azure.net/secrets/<name>`
@@ -1883,27 +2414,33 @@ var require_resources = __commonJS({
1883
2414
  "vault"
1884
2415
  ],
1885
2416
  routable: true,
1886
- identity: "https://{vault}.vault.azure.net/secrets/{name}"
2417
+ identity: "https://{vault}.vault.azure.net/secrets/{name}",
2418
+ // ★ THE ONE TYPE TODAY WHOSE VALUE POINTS AT A SECRET (docs/122 §3.5). The parameter holds
2419
+ // the secret's NAME within its vault; nothing in the platform ever reads the value behind it.
2420
+ secretRef: true
1887
2421
  },
1888
2422
  // A catalogue value. Nothing is reached by naming a region.
1889
2423
  location: {
1890
2424
  requires: [],
1891
2425
  routable: false,
1892
- identity: null
2426
+ identity: null,
2427
+ secretRef: false
1893
2428
  },
1894
2429
  managedDisk: {
1895
2430
  requires: [
1896
2431
  "resourceGroup"
1897
2432
  ],
1898
2433
  routable: true,
1899
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}"
2434
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}",
2435
+ secretRef: false
1900
2436
  },
1901
2437
  sqlServer: {
1902
2438
  requires: [
1903
2439
  "resourceGroup"
1904
2440
  ],
1905
2441
  routable: true,
1906
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}"
2442
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}",
2443
+ secretRef: false
1907
2444
  },
1908
2445
  sqlDatabase: {
1909
2446
  requires: [
@@ -1911,7 +2448,8 @@ var require_resources = __commonJS({
1911
2448
  "server"
1912
2449
  ],
1913
2450
  routable: true,
1914
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}"
2451
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}",
2452
+ secretRef: false
1915
2453
  },
1916
2454
  // The one type where LISTING and IDENTIFYING are different questions. It lists
1917
2455
  // SUBSCRIPTION-WIDE when no resource group is given (`virtualMachines.listAll()`,
@@ -1931,7 +2469,8 @@ var require_resources = __commonJS({
1931
2469
  "resourceGroup"
1932
2470
  ],
1933
2471
  routable: true,
1934
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}"
2472
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}",
2473
+ secretRef: false
1935
2474
  },
1936
2475
  // The sizes ONE machine can actually be resized to right now — not the region's SKU
1937
2476
  // catalogue. `requires` is genuinely both fields' answer here: the safe set cannot be
@@ -1945,16 +2484,53 @@ var require_resources = __commonJS({
1945
2484
  "vm"
1946
2485
  ],
1947
2486
  routable: false,
1948
- identity: null
2487
+ identity: null,
2488
+ secretRef: false
1949
2489
  }
1950
2490
  };
1951
- exports.DISCOVERY_VOCABULARY = {
2491
+ var DECLARED_DISCOVERY = {
1952
2492
  azure: AZURE_DISCOVERY_TYPES,
1953
2493
  aws: {},
1954
2494
  gcp: {},
1955
2495
  terraform: {}
1956
2496
  };
1957
- exports.DISCOVERABLE_RESOURCE_TYPES = Object.keys(AZURE_DISCOVERY_TYPES);
2497
+ exports.DISCOVERY_VOCABULARY = DECLARED_DISCOVERY;
2498
+ exports.LEGACY_DISCOVERY_PROVIDER = "azure";
2499
+ function discoveryTypesOf(provider) {
2500
+ return Object.keys(exports.DISCOVERY_VOCABULARY[provider]).map((type) => formatDiscoveryType(provider, type));
2501
+ }
2502
+ __name(discoveryTypesOf, "discoveryTypesOf");
2503
+ function declaredDiscoveryTypes() {
2504
+ return enums_1.PROVIDERS.flatMap(discoveryTypesOf);
2505
+ }
2506
+ __name(declaredDiscoveryTypes, "declaredDiscoveryTypes");
2507
+ function liveDiscoveryTypes(registered) {
2508
+ const providers = new Set(registered);
2509
+ return enums_1.PROVIDERS.filter((provider) => providers.has(provider)).flatMap(discoveryTypesOf);
2510
+ }
2511
+ __name(liveDiscoveryTypes, "liveDiscoveryTypes");
2512
+ function canonicalDiscoveryId(value) {
2513
+ const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
2514
+ return parsed ? formatDiscoveryType(parsed.provider, parsed.type) : null;
2515
+ }
2516
+ __name(canonicalDiscoveryId, "canonicalDiscoveryId");
2517
+ function resolveDiscoveryType(value) {
2518
+ const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
2519
+ if (!parsed) return null;
2520
+ const spec = discoveryTypeSpec(parsed.provider, parsed.type);
2521
+ if (!spec) return null;
2522
+ return {
2523
+ id: formatDiscoveryType(parsed.provider, parsed.type),
2524
+ provider: parsed.provider,
2525
+ type: parsed.type,
2526
+ spec
2527
+ };
2528
+ }
2529
+ __name(resolveDiscoveryType, "resolveDiscoveryType");
2530
+ function isSecretReference(discover) {
2531
+ return discover ? resolveDiscoveryType(discover)?.spec.secretRef === true : false;
2532
+ }
2533
+ __name(isSecretReference, "isSecretReference");
1958
2534
  exports.DISCOVERY_TYPE_PATTERN = /^(?:([a-z][a-z0-9]*)\/)?([a-z][a-zA-Z0-9]{0,63})$/;
1959
2535
  function parseDiscoveryType(value, fallbackProvider) {
1960
2536
  const match = exports.DISCOVERY_TYPE_PATTERN.exec(value);
@@ -1978,7 +2554,9 @@ var require_resources = __commonJS({
1978
2554
  }
1979
2555
  __name(formatDiscoveryType, "formatDiscoveryType");
1980
2556
  function discoveryTypeSpec(provider, type) {
1981
- return exports.DISCOVERY_VOCABULARY[provider]?.[type];
2557
+ if (!Object.prototype.hasOwnProperty.call(exports.DISCOVERY_VOCABULARY, provider)) return void 0;
2558
+ const vocabulary = exports.DISCOVERY_VOCABULARY[provider];
2559
+ return Object.prototype.hasOwnProperty.call(vocabulary, type) ? vocabulary[type] : void 0;
1982
2560
  }
1983
2561
  __name(discoveryTypeSpec, "discoveryTypeSpec");
1984
2562
  function dependsOnLookup(keys, dependsOn) {
@@ -2279,6 +2857,28 @@ var require_runs = __commonJS({
2279
2857
  }
2280
2858
  });
2281
2859
 
2860
+ // ../contracts/dist/evidence-pack.js
2861
+ var require_evidence_pack = __commonJS({
2862
+ "../contracts/dist/evidence-pack.js"(exports) {
2863
+ "use strict";
2864
+ Object.defineProperty(exports, "__esModule", {
2865
+ value: true
2866
+ });
2867
+ exports.EVIDENCE_EXPORTED_AUDIT_ACTION = exports.EVIDENCE_DIGEST_HEADER = void 0;
2868
+ exports.evidencePackFilename = evidencePackFilename;
2869
+ exports.EVIDENCE_DIGEST_HEADER = "X-Evidence-Digest";
2870
+ exports.EVIDENCE_EXPORTED_AUDIT_ACTION = "operation.evidence_exported";
2871
+ function evidencePackFilename(operationKey, from, to) {
2872
+ return `${operationKey}-evidence-${compactInstant(from)}-${compactInstant(to)}.json`;
2873
+ }
2874
+ __name(evidencePackFilename, "evidencePackFilename");
2875
+ function compactInstant(iso) {
2876
+ return iso.replace(/\.\d+(?=Z|[+-]\d{2}:?\d{2}$)/, "").replace(/[-:]/g, "");
2877
+ }
2878
+ __name(compactInstant, "compactInstant");
2879
+ }
2880
+ });
2881
+
2282
2882
  // ../contracts/dist/package-egress.js
2283
2883
  var require_package_egress = __commonJS({
2284
2884
  "../contracts/dist/package-egress.js"(exports) {
@@ -2352,18 +2952,18 @@ var require_package_execution = __commonJS({
2352
2952
  exports.PACKAGE_CONTAINER_ENV = {
2353
2953
  /** The control-plane origin the container redeems its work item at and reports to. */
2354
2954
  callbackBaseUrl: "OPERA_CALLBACK_BASE_URL",
2355
- executionId: "OPERA_EXECUTION_ID",
2955
+ executionId: "RUNVERIC_EXECUTION_ID",
2356
2956
  /** Single-use; spent on the first redemption. Never copied into the package's own env. */
2357
2957
  runToken: "OPERA_RUN_TOKEN",
2358
2958
  /** What the bytes it fetched must hash to. Travels beside the work item, never inside it. */
2359
- contentDigest: "OPERA_CONTENT_DIGEST",
2959
+ contentDigest: "RUNVERIC_CONTENT_DIGEST",
2360
2960
  /** Which runtime image this is, recorded on the run. */
2361
2961
  runnerImageDigest: "OPERA_RUNNER_IMAGE_DIGEST",
2362
2962
  /** Where the PACKAGE's outbound traffic is pointed. Absent when it declared no network. */
2363
- egressProxyUrl: "OPERA_EGRESS_PROXY_URL",
2963
+ egressProxyUrl: "RUNVERIC_EGRESS_PROXY_URL",
2364
2964
  /** The runner-only control channel. Never copied into the package's own env. */
2365
- egressControlUrl: "OPERA_EGRESS_CONTROL_URL",
2366
- egressControlToken: "OPERA_EGRESS_CONTROL_TOKEN"
2965
+ egressControlUrl: "RUNVERIC_EGRESS_CONTROL_URL",
2966
+ egressControlToken: "RUNVERIC_EGRESS_CONTROL_TOKEN"
2367
2967
  };
2368
2968
  }
2369
2969
  });
@@ -2375,17 +2975,17 @@ var require_runner_confinement = __commonJS({
2375
2975
  Object.defineProperty(exports, "__esModule", {
2376
2976
  value: true
2377
2977
  });
2378
- exports.RUNNER_NAMESPACE = exports.CONFINEMENT_FINDING_LABEL = exports.CONFINEMENT_FINDING_SENTENCE = exports.RUNNER_ISOLATION_BLOCKS_UNDECLARED_EGRESS = exports.CONFINEMENT_FINDING_BLOCKS_EGRESS = exports.CONFINEMENT_FINDINGS = exports.RUNNER_SUBSTRATE_LABEL = exports.RUNNER_SUBSTRATES = exports.EGRESS_GATE_SHUT = exports.EGRESS_GATE_OPEN = exports.EGRESS_GATE_PREFIX = exports.EGRESS_PROBE_TIMEOUT_MS = exports.EGRESS_PROBE_BLOCKED = exports.EGRESS_PROBE_REACHED = exports.EGRESS_PROBE_PREFIX = void 0;
2978
+ exports.CONFINEMENT_FINDING_LABEL = exports.CONFINEMENT_FINDING_SENTENCE = exports.RUNNER_ISOLATION_BLOCKS_UNDECLARED_EGRESS = exports.CONFINEMENT_FINDING_BLOCKS_EGRESS = exports.CONFINEMENT_FINDINGS = exports.RUNNER_SUBSTRATE_LABEL = exports.RUNNER_SUBSTRATES = exports.EGRESS_GATE_SHUT = exports.EGRESS_GATE_OPEN = exports.EGRESS_GATE_PREFIX = exports.EGRESS_PROBE_TIMEOUT_MS = exports.EGRESS_PROBE_BLOCKED = exports.EGRESS_PROBE_REACHED = exports.EGRESS_PROBE_PREFIX = void 0;
2379
2979
  exports.effectiveBlocksUndeclaredEgress = effectiveBlocksUndeclaredEgress;
2380
2980
  exports.confinementOverclaims = confinementOverclaims;
2381
2981
  exports.runnerBlocksUndeclaredEgress = runnerBlocksUndeclaredEgress;
2382
2982
  exports.poolConfinementSentence = poolConfinementSentence;
2383
2983
  var brand_1 = require_brand();
2384
- exports.EGRESS_PROBE_PREFIX = "opera.egress-probe";
2984
+ exports.EGRESS_PROBE_PREFIX = "runveric.egress-probe";
2385
2985
  exports.EGRESS_PROBE_REACHED = "REACHED";
2386
2986
  exports.EGRESS_PROBE_BLOCKED = "BLOCKED";
2387
2987
  exports.EGRESS_PROBE_TIMEOUT_MS = 5e3;
2388
- exports.EGRESS_GATE_PREFIX = "opera.egress-gate";
2988
+ exports.EGRESS_GATE_PREFIX = "runveric.egress-gate";
2389
2989
  exports.EGRESS_GATE_OPEN = "OPEN";
2390
2990
  exports.EGRESS_GATE_SHUT = "SHUT";
2391
2991
  exports.RUNNER_SUBSTRATES = [
@@ -2440,7 +3040,69 @@ var require_runner_confinement = __commonJS({
2440
3040
  return `Only ${confined} of ${findings.length} runners in this pool block undeclared outbound traffic. A run cannot choose which runner claims it, so this pool is not relied on to confine egress and packages that need their allowlist enforced are not sent here.`;
2441
3041
  }
2442
3042
  __name(poolConfinementSentence, "poolConfinementSentence");
2443
- exports.RUNNER_NAMESPACE = "opera-runner";
3043
+ }
3044
+ });
3045
+
3046
+ // ../contracts/dist/runner-install.js
3047
+ var require_runner_install = __commonJS({
3048
+ "../contracts/dist/runner-install.js"(exports) {
3049
+ "use strict";
3050
+ Object.defineProperty(exports, "__esModule", {
3051
+ value: true
3052
+ });
3053
+ exports.POD_ROLE_BROKER = exports.POD_EGRESS_CONFINED = exports.POD_EGRESS_LABEL = exports.POD_RUNNER = exports.POD_ROLE = exports.POD_MANAGED_BY_VALUE = exports.POD_MANAGED_BY = exports.RUNNER_NETWORK_POLICY_NAME = exports.RUNNER_REPORT_DIRECTORY = exports.RUNNER_STATE_DIRECTORY = exports.RUNNER_CONTAINER_PREFIX = exports.RUNNER_NAMESPACE = exports.RUNNER_HOST_ENV = void 0;
3054
+ exports.RUNNER_HOST_ENV = {
3055
+ /** `docker` or `kubernetes`. Absent means `docker`; present and wrong is refused by name. */
3056
+ substrate: "RUNVERIC_RUNNER_SUBSTRATE",
3057
+ /** The https origin the runner long-polls. */
3058
+ controlPlaneUrl: "RUNVERIC_CONTROL_PLANE_URL",
3059
+ /** The operator-chosen name, also the container's and the Deployment's. */
3060
+ runnerName: "RUNVERIC_RUNNER_NAME",
3061
+ /** Single-use; spent by the first registration. */
3062
+ registrationToken: "RUNVERIC_RUNNER_REGISTRATION_TOKEN",
3063
+ /** The image the dispatcher starts one container of per step. */
3064
+ stepImage: "RUNVERIC_RUNNER_STEP_IMAGE",
3065
+ /** Optional. Absent means the runner runs built-in actions only. */
3066
+ packageImage: "RUNVERIC_RUNNER_PACKAGE_IMAGE",
3067
+ /** Where the credential and the stashed reports live across restarts. */
3068
+ reportDirectory: "RUNVERIC_RUNNER_REPORT_DIR"
3069
+ };
3070
+ exports.RUNNER_NAMESPACE = "runveric-runner";
3071
+ exports.RUNNER_CONTAINER_PREFIX = "runveric-";
3072
+ exports.RUNNER_STATE_DIRECTORY = "/var/lib/runveric-runner";
3073
+ exports.RUNNER_REPORT_DIRECTORY = `${exports.RUNNER_STATE_DIRECTORY}/reports`;
3074
+ exports.RUNNER_NETWORK_POLICY_NAME = "runveric-confined-egress";
3075
+ exports.POD_MANAGED_BY = "runveric.dev/managed-by";
3076
+ exports.POD_MANAGED_BY_VALUE = "runveric-dispatcher";
3077
+ exports.POD_ROLE = "runveric.dev/role";
3078
+ exports.POD_RUNNER = "runveric.dev/runner";
3079
+ exports.POD_EGRESS_LABEL = "runveric.dev/egress";
3080
+ exports.POD_EGRESS_CONFINED = "confined";
3081
+ exports.POD_ROLE_BROKER = "egress-broker";
3082
+ }
3083
+ });
3084
+
3085
+ // ../contracts/dist/authoring-cli.js
3086
+ var require_authoring_cli = __commonJS({
3087
+ "../contracts/dist/authoring-cli.js"(exports) {
3088
+ "use strict";
3089
+ Object.defineProperty(exports, "__esModule", {
3090
+ value: true
3091
+ });
3092
+ exports.RETIRED_AUTHORING_CLI_ENV = exports.AUTHORING_CLI_ENV = void 0;
3093
+ exports.AUTHORING_CLI_ENV = {
3094
+ /** `runveric publish`: the access token. */
3095
+ token: "RUNVERIC_TOKEN",
3096
+ /** `runveric publish`: the control plane's origin. */
3097
+ url: "RUNVERIC_URL",
3098
+ /** `runveric test`: the runner image to run in, digest-pinned — the Author page shows it. */
3099
+ runnerImage: "RUNVERIC_RUNNER_IMAGE"
3100
+ };
3101
+ exports.RETIRED_AUTHORING_CLI_ENV = {
3102
+ token: "OPERA_TOKEN",
3103
+ url: "OPERA_URL",
3104
+ runnerImage: "OPERA_RUNNER_IMAGE"
3105
+ };
2444
3106
  }
2445
3107
  });
2446
3108
 
@@ -2464,9 +3126,10 @@ var require_runners = __commonJS({
2464
3126
  }, "get")
2465
3127
  });
2466
3128
  var package_provenance_1 = require_package_provenance();
2467
- exports.RUNNER_REGISTRATION_TOKEN_HEADER = "x-opera-runner-registration-token";
2468
- exports.RUNNER_CREDENTIAL_HEADER = "x-opera-runner-credential";
2469
- exports.RUNNER_AUTHORIZATION_TOKEN_HEADER = "x-opera-runner-authorization";
3129
+ var runner_install_1 = require_runner_install();
3130
+ exports.RUNNER_REGISTRATION_TOKEN_HEADER = "x-runveric-runner-registration-token";
3131
+ exports.RUNNER_CREDENTIAL_HEADER = "x-runveric-runner-credential";
3132
+ exports.RUNNER_AUTHORIZATION_TOKEN_HEADER = "x-runveric-runner-authorization";
2470
3133
  exports.RUNNER_POOL_ACCEPTS_PROVENANCE_BY_DEFAULT = {
2471
3134
  // Imported from a Git source this tenant connected. Still not accepted by default: a pool
2472
3135
  // is a machine inside a customer's network, and nothing about it is implied by connecting a
@@ -2480,13 +3143,13 @@ var require_runners = __commonJS({
2480
3143
  authored: false
2481
3144
  };
2482
3145
  exports.RUNNER_POOL_DEFAULT_ACCEPTS_PROVENANCE = package_provenance_1.PACKAGE_PROVENANCE_KINDS.filter((kind) => exports.RUNNER_POOL_ACCEPTS_PROVENANCE_BY_DEFAULT[kind]);
2483
- function poolAcceptsProvenance(accepts, provenance) {
2484
- if (provenance === null) return false;
2485
- return accepts.includes(provenance);
3146
+ function poolAcceptsProvenance(accepts, provenance2) {
3147
+ if (provenance2 === null) return false;
3148
+ return accepts.includes(provenance2);
2486
3149
  }
2487
3150
  __name(poolAcceptsProvenance, "poolAcceptsProvenance");
2488
3151
  function containerNameFor(runnerName) {
2489
- return `opera-${runnerName.trim()}`;
3152
+ return `${runner_install_1.RUNNER_CONTAINER_PREFIX}${runnerName.trim()}`;
2490
3153
  }
2491
3154
  __name(containerNameFor, "containerNameFor");
2492
3155
  exports.RUNNER_LIFECYCLES = [
@@ -3309,12 +3972,12 @@ var require_workspace_domain = __commonJS({
3309
3972
  "challenge_expired",
3310
3973
  "held_elsewhere"
3311
3974
  ];
3312
- exports.DOMAIN_VERIFICATION_RECORD_PREFIX = "opera-domain-verification=";
3313
- function domainVerificationRecord(domain, token) {
3975
+ exports.DOMAIN_VERIFICATION_RECORD_PREFIX = "runveric-domain-verification=";
3976
+ function domainVerificationRecord(domain, token2) {
3314
3977
  return {
3315
3978
  name: domain,
3316
3979
  type: "TXT",
3317
- value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${token}`
3980
+ value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${token2}`
3318
3981
  };
3319
3982
  }
3320
3983
  __name(domainVerificationRecord, "domainVerificationRecord");
@@ -3497,9 +4160,16 @@ var require_dist = __commonJS({
3497
4160
  __exportStar(require_tenant_users(), exports);
3498
4161
  __exportStar(require_invitations(), exports);
3499
4162
  __exportStar(require_groups(), exports);
4163
+ __exportStar(require_catalog_policy(), exports);
3500
4164
  __exportStar(require_catalog(), exports);
4165
+ __exportStar(require_catalog_release(), exports);
4166
+ __exportStar(require_action_repository(), exports);
4167
+ __exportStar(require_catalog_release_spec(), exports);
4168
+ __exportStar(require_catalog_lock(), exports);
4169
+ __exportStar(require_catalog_manifest_placeholders(), exports);
3501
4170
  __exportStar(require_change_control(), exports);
3502
4171
  __exportStar(require_execution(), exports);
4172
+ __exportStar(require_decisions(), exports);
3503
4173
  __exportStar(require_execution_site(), exports);
3504
4174
  __exportStar(require_run_placement(), exports);
3505
4175
  __exportStar(require_connections(), exports);
@@ -3510,12 +4180,15 @@ var require_dist = __commonJS({
3510
4180
  __exportStar(require_scheduling(), exports);
3511
4181
  __exportStar(require_operations(), exports);
3512
4182
  __exportStar(require_runs(), exports);
4183
+ __exportStar(require_evidence_pack(), exports);
3513
4184
  __exportStar(require_package_tier(), exports);
3514
4185
  __exportStar(require_package_provenance(), exports);
3515
4186
  __exportStar(require_packages(), exports);
3516
4187
  __exportStar(require_package_egress(), exports);
3517
4188
  __exportStar(require_package_execution(), exports);
3518
4189
  __exportStar(require_runner_confinement(), exports);
4190
+ __exportStar(require_runner_install(), exports);
4191
+ __exportStar(require_authoring_cli(), exports);
3519
4192
  __exportStar(require_runners(), exports);
3520
4193
  __exportStar(require_runner_pool_presence(), exports);
3521
4194
  __exportStar(require_routable_target(), exports);
@@ -3670,6 +4343,7 @@ var require_manifest_schema = __commonJS({
3670
4343
  return contracts_1.RISK_LEVELS;
3671
4344
  }, "get")
3672
4345
  });
4346
+ var contracts_2 = require_dist();
3673
4347
  exports.MANIFEST_FIELDS = [
3674
4348
  "apiVersion",
3675
4349
  "id",
@@ -3695,7 +4369,7 @@ var require_manifest_schema = __commonJS({
3695
4369
  "secrets",
3696
4370
  "credentialScopes",
3697
4371
  "license",
3698
- "minOperaVersion"
4372
+ "minRunvericVersion"
3699
4373
  ];
3700
4374
  exports.PACKAGE_ID_PATTERN = /^[a-z0-9-]+\/[a-z0-9-]+$/;
3701
4375
  exports.SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
@@ -3708,12 +4382,18 @@ var require_manifest_schema = __commonJS({
3708
4382
  "properties",
3709
4383
  "required",
3710
4384
  "additionalProperties",
3711
- // Opera's own annotation, carried INSIDE the schema it orders rather than in a second
4385
+ // Our own annotation, carried INSIDE the schema it orders rather than in a second
3712
4386
  // column: the two would otherwise separate, and a schema whose order lives elsewhere is a
3713
4387
  // schema that renders in the wrong order the first time one of them is copied without the
3714
4388
  // other. Allowed here so a normalized schema round-trips through this validator rather than
3715
4389
  // being rejected as an unknown field.
3716
- "x-opera-order"
4390
+ //
4391
+ // ⛔ THIS LIST IS A REJECT-UNKNOWN ALLOWLIST. `x-opera-order` left it LAST in docs/108 —
4392
+ // after the writer stopped stamping it, 0089 stripped it from every row, and the readers
4393
+ // stopped reading it. Nothing re-validates a STORED schema (every caller of this validator
4394
+ // is handed authored bytes), so the order was safe; an author's file that still carries the
4395
+ // retired key is now refused by name, as any unknown field is.
4396
+ contracts_2.PARAMETER_ORDER_KEY
3717
4397
  ];
3718
4398
  exports.PARAMETER_PROPERTY_FIELDS = [
3719
4399
  "type",
@@ -3814,7 +4494,7 @@ var require_manifest_validator = __commonJS({
3814
4494
  });
3815
4495
  validateCredentialScopes(value.credentialScopes, errors);
3816
4496
  requiredString(value, "license", errors, manifest_schema_1.SPDX_ID_PATTERN, "an SPDX identifier");
3817
- requiredString(value, "minOperaVersion", errors, manifest_schema_1.SEMVER_PATTERN, "MAJOR.MINOR.PATCH");
4497
+ requiredString(value, "minRunvericVersion", errors, manifest_schema_1.SEMVER_PATTERN, "MAJOR.MINOR.PATCH");
3818
4498
  return errors.length === 0 && runtime !== void 0 ? {
3819
4499
  ok: true,
3820
4500
  // The normalized runtime replaces the authored one, exactly as `discover` ids are
@@ -3853,7 +4533,7 @@ var require_manifest_validator = __commonJS({
3853
4533
  const declaredType = validateParameterProperty(name, property, errors);
3854
4534
  normalizedProperties[name] = isRecord(property) ? validateDiscoveryAnnotations(name, property, declaredType, value.properties, declarationOrder, context?.providers, errors, warnings) : property;
3855
4535
  }
3856
- validateParameterOrder(value["x-opera-order"], declarationOrder, errors);
4536
+ validateParameterOrder(value[contracts_1.PARAMETER_ORDER_KEY], contracts_1.PARAMETER_ORDER_KEY, declarationOrder, errors);
3857
4537
  }
3858
4538
  if (!Array.isArray(value.required) || !value.required.every(isString)) {
3859
4539
  add(errors, "parameters.required", "Expected an array of property names.", "type");
@@ -4153,9 +4833,9 @@ var require_manifest_validator = __commonJS({
4153
4833
  }
4154
4834
  }
4155
4835
  __name(validateDependency, "validateDependency");
4156
- function validateParameterOrder(value, declarationOrder, errors) {
4836
+ function validateParameterOrder(value, key, declarationOrder, errors) {
4157
4837
  if (value === void 0) return;
4158
- const path = "parameters.x-opera-order";
4838
+ const path = `parameters.${key}`;
4159
4839
  if (!Array.isArray(value) || !value.every(isString)) {
4160
4840
  add(errors, path, "Expected an array of property names.", "type");
4161
4841
  return;
@@ -5011,7 +5691,7 @@ var require_authoring_lint = __commonJS({
5011
5691
  if (!isRecord(permission) || !Array.isArray(permission.actions)) return;
5012
5692
  permission.actions.forEach((action, actionIndex) => {
5013
5693
  if (typeof action !== "string" || action.includes("*")) return;
5014
- if (permissionTokens(action).some((token) => haystack.includes(token))) return;
5694
+ if (permissionTokens(action).some((token2) => haystack.includes(token2))) return;
5015
5695
  findings.push({
5016
5696
  severity: "warning",
5017
5697
  rule: "package/permission-possibly-unused",
@@ -5086,7 +5766,7 @@ var require_authoring_lint = __commonJS({
5086
5766
  ".sh",
5087
5767
  ".py"
5088
5768
  ];
5089
- var RESULT_FILE_VARIABLE = "OPERA_RESULT_FILE";
5769
+ var RESULT_FILE_VARIABLE = "RUNVERIC_RESULT_FILE";
5090
5770
  var ENTRYPOINT_PHASES = [
5091
5771
  {
5092
5772
  field: "entrypoint",
@@ -5675,7 +6355,7 @@ summary: ${yamlString(`${input.name} \u2014 describe in one line what an operato
5675
6355
  category: compute
5676
6356
  providers:
5677
6357
  - azure
5678
- connectorKey: opera.package-runner
6358
+ connectorKey: ${contracts_1.PACKAGE_RUNNER_CONNECTOR_KEY}
5679
6359
 
5680
6360
  # \u2500\u2500 How it runs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5681
6361
  runtime:
@@ -5715,7 +6395,7 @@ secrets: []
5715
6395
 
5716
6396
  # \u2500\u2500 Publishing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5717
6397
  license: MIT
5718
- minOperaVersion: 1.0.0
6398
+ minRunvericVersion: 1.0.0
5719
6399
  `;
5720
6400
  }
5721
6401
  __name(manifest, "manifest");
@@ -5752,12 +6432,12 @@ minOperaVersion: 1.0.0
5752
6432
  return `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5753
6433
  # OPERA'S WRAPPER. This file is the CONTRACT; ${bodyPath} is YOURS.
5754
6434
  #
5755
- # ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at ${sigil}OPERA_RESULT_FILE. A script
6435
+ # ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at ${sigil}RUNVERIC_RESULT_FILE. A script
5756
6436
  # that exits 0 without writing one has reported nothing, and ${contracts_1.BRAND_SHORT_NAME} says so rather than inventing
5757
6437
  # a result. This file is what makes that impossible to get wrong by accident:
5758
6438
  #
5759
6439
  # * it starts ${bodyPath} \u2014 your script, unchanged;
5760
- # * if your script wrote ${sigil}OPERA_RESULT_FILE itself, this file leaves it exactly alone;
6440
+ # * if your script wrote ${sigil}RUNVERIC_RESULT_FILE itself, this file leaves it exactly alone;
5761
6441
  # * otherwise it writes one for you \u2014 succeeded when your script exits 0, failed with the exit
5762
6442
  # code and a result.error when it does not \u2014 carrying whatever your script printed as the
5763
6443
  # message.
@@ -5768,21 +6448,21 @@ minOperaVersion: 1.0.0
5768
6448
  # WHERE YOUR SCRIPT RUNS. This file moves to the PACKAGE ROOT before starting yours, so a
5769
6449
  # relative path in your script means what it meant on your laptop \u2014 \`source ./lib/util.sh\` and
5770
6450
  # \`. ./lib/helpers.ps1\` resolve against the package, not against wherever the runner happened
5771
- # to start. The package root is READ-ONLY and NON-EXECUTABLE: write to ${sigil}OPERA_WORK_DIR,
6451
+ # to start. The package root is READ-ONLY and NON-EXECUTABLE: write to ${sigil}RUNVERIC_WORK_DIR,
5772
6452
  # and call another script through your interpreter (\`bash ./tool.sh\`) rather than executing it
5773
- # directly. ${sigil}OPERA_PACKAGE_ROOT is exported so your script can be explicit rather than
6453
+ # directly. ${sigil}RUNVERIC_PACKAGE_ROOT is exported so your script can be explicit rather than
5774
6454
  # assume.
5775
6455
  #
5776
6456
  # THE CONTRACT (docs/22), identical in every language:
5777
- # ${sigil}OPERA_PARAMETERS_FILE the operator's answers, as JSON
5778
- # ${sigil}OPERA_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
5779
- # ${sigil}OPERA_RESULT_FILE where the result is WRITTEN \u2014 the only result channel
5780
- # ${sigil}OPERA_DRY_RUN "true" when the operator asked for a rehearsal. PASSED
6457
+ # ${sigil}RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
6458
+ # ${sigil}RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
6459
+ # ${sigil}RUNVERIC_RESULT_FILE where the result is WRITTEN \u2014 the only result channel
6460
+ # ${sigil}RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal. PASSED
5781
6461
  # THROUGH, never acted on here: only your script knows which of
5782
6462
  # its steps change anything.
5783
- # ${sigil}OPERA_WORK_DIR scratch space; nothing else is writable
5784
- # ${sigil}OPERA_PACKAGE_ROOT this package's root, and the working directory
5785
- # Anything printed to stdout becomes a log line. A line starting with OPERA_EVENT becomes a
6463
+ # ${sigil}RUNVERIC_WORK_DIR scratch space; nothing else is writable
6464
+ # ${sigil}RUNVERIC_PACKAGE_ROOT this package's root, and the working directory
6465
+ # Anything printed to stdout becomes a log line. A line starting with RUNVERIC_EVENT becomes a
5786
6466
  # structured event.
5787
6467
  #
5788
6468
  # The runner starts run/${entryFile} the same way this file starts ${bodyPath}.
@@ -5829,7 +6509,7 @@ $ErrorActionPreference = 'Continue'
5829
6509
  # setting only the first is a classic way to get a script that resolves paths differently from
5830
6510
  # the tools it calls.
5831
6511
  $operaPackageRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
5832
- $env:OPERA_PACKAGE_ROOT = $operaPackageRoot
6512
+ $env:RUNVERIC_PACKAGE_ROOT = $operaPackageRoot
5833
6513
  Set-Location -LiteralPath $operaPackageRoot
5834
6514
  [Environment]::CurrentDirectory = $operaPackageRoot
5835
6515
 
@@ -5846,7 +6526,7 @@ $operaCaptured = [System.Collections.Generic.List[string]]::new()
5846
6526
 
5847
6527
  # \u2500\u2500\u2500 THE OPERATOR'S ANSWERS, BOUND TO YOUR OWN param() BLOCK \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
5848
6528
  #
5849
- # ${contracts_1.BRAND_SHORT_NAME} delivers the operator's answers as JSON at $env:OPERA_PARAMETERS_FILE. A script written
6529
+ # ${contracts_1.BRAND_SHORT_NAME} delivers the operator's answers as JSON at $env:RUNVERIC_PARAMETERS_FILE. A script written
5850
6530
  # outside ${contracts_1.BRAND_SHORT_NAME} receives its values through its param() block. THOSE ARE DIFFERENT MECHANISMS,
5851
6531
  # and nothing joins them unless this file does \u2014 so without the lines below a run form built from
5852
6532
  # a param() block would collect six answers and hand your script none of them.
@@ -5865,7 +6545,7 @@ $operaCaptured = [System.Collections.Generic.List[string]]::new()
5865
6545
  #
5866
6546
  # An answer your script does not declare is IGNORED, not an error \u2014 your script is the authority
5867
6547
  # on what it takes. A script with no param() block gets no arguments at all and reads
5868
- # $env:OPERA_PARAMETERS_FILE itself, exactly as before.
6548
+ # $env:RUNVERIC_PARAMETERS_FILE itself, exactly as before.
5869
6549
  $operaDeclared = @()
5870
6550
  try {
5871
6551
  $operaTokens = $null
@@ -5884,10 +6564,10 @@ try {
5884
6564
  }
5885
6565
 
5886
6566
  $operaArguments = @()
5887
- if ($operaDeclared.Count -gt 0 -and (Test-Path -LiteralPath $env:OPERA_PARAMETERS_FILE)) {
6567
+ if ($operaDeclared.Count -gt 0 -and (Test-Path -LiteralPath $env:RUNVERIC_PARAMETERS_FILE)) {
5888
6568
  $operaAnswers = $null
5889
6569
  try {
5890
- $operaAnswers = Get-Content -LiteralPath $env:OPERA_PARAMETERS_FILE -Raw | ConvertFrom-Json
6570
+ $operaAnswers = Get-Content -LiteralPath $env:RUNVERIC_PARAMETERS_FILE -Raw | ConvertFrom-Json
5891
6571
  } catch {
5892
6572
  $operaAnswers = $null
5893
6573
  }
@@ -5939,8 +6619,8 @@ $operaStatus = $LASTEXITCODE
5939
6619
 
5940
6620
  # THE BODY'S OWN RESULT WINS. The wrapper fills a gap; it never overwrites what an author
5941
6621
  # reported for themselves \u2014 outputs, checks and rollbackCapture all arrive this way.
5942
- $operaWritten = (Test-Path -LiteralPath $env:OPERA_RESULT_FILE) -and
5943
- ((Get-Item -LiteralPath $env:OPERA_RESULT_FILE).Length -gt 0)
6622
+ $operaWritten = (Test-Path -LiteralPath $env:RUNVERIC_RESULT_FILE) -and
6623
+ ((Get-Item -LiteralPath $env:RUNVERIC_RESULT_FILE).Length -gt 0)
5944
6624
  if (-not $operaWritten) {
5945
6625
  $operaOutput = ($operaCaptured -join "\`n").Trim()
5946
6626
  # result.message is bounded at 4096 characters. Keep the TAIL, which is where a failure says
@@ -5965,7 +6645,7 @@ if (-not $operaWritten) {
5965
6645
  }
5966
6646
  }
5967
6647
  }
5968
- $operaResult | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:OPERA_RESULT_FILE -Encoding utf8
6648
+ $operaResult | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:RUNVERIC_RESULT_FILE -Encoding utf8
5969
6649
  }
5970
6650
 
5971
6651
  # The body's exit code is this package's exit code. ${contracts_1.BRAND_SHORT_NAME} cross-checks the two \u2014 a result that
@@ -5981,16 +6661,16 @@ exit $operaStatus
5981
6661
  return `${wrapperHeader(interpreter, "$", bodyPath)}
5982
6662
  # THE PACKAGE ROOT, and the working directory your script starts in \u2014 so \`source ./lib/util.sh\`
5983
6663
  # resolves against the package rather than against the scratch directory the runner starts in.
5984
- OPERA_PACKAGE_ROOT="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
5985
- export OPERA_PACKAGE_ROOT
5986
- cd "\${OPERA_PACKAGE_ROOT}"
6664
+ RUNVERIC_PACKAGE_ROOT="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
6665
+ export RUNVERIC_PACKAGE_ROOT
6666
+ cd "\${RUNVERIC_PACKAGE_ROOT}"
5987
6667
 
5988
6668
  # THE ONE PLACE the author's path is written into this file. Everything below refers to the
5989
6669
  # variable, so an entrypoint containing a quote or a \`$\` is a string here and cannot become
5990
6670
  # syntax. The marker is how \`authoredEntrypointOf\` reads this line back \u2014 see that function.
5991
6671
  opera_body_path=${bashLiteral(bodyPath)} # ${ENTRYPOINT_MARKER}
5992
- opera_body="\${OPERA_PACKAGE_ROOT}/\${opera_body_path}"
5993
- opera_capture="\${OPERA_WORK_DIR}/.opera-body-output"
6672
+ opera_body="\${RUNVERIC_PACKAGE_ROOT}/\${opera_body_path}"
6673
+ opera_capture="\${RUNVERIC_WORK_DIR}/.runveric-body-output"
5994
6674
 
5995
6675
  # Both streams merged, so the transcript below is in the order the operator saw; \`tee\` keeps
5996
6676
  # every line arriving live rather than held until the body ends. errexit is lifted for exactly
@@ -6002,17 +6682,17 @@ set -e
6002
6682
 
6003
6683
  # THE BODY'S OWN RESULT WINS. The wrapper fills a gap; it never overwrites what an author
6004
6684
  # reported for themselves \u2014 outputs, checks and rollbackCapture all arrive this way.
6005
- if [ ! -s "\${OPERA_RESULT_FILE}" ]; then
6006
- OPERA_BODY_STATUS="\${opera_status}" OPERA_BODY_CAPTURE="\${opera_capture}" \\
6007
- OPERA_BODY_PATH="\${opera_body_path}" \\
6008
- python3 - > "\${OPERA_RESULT_FILE}" <<'PY'
6685
+ if [ ! -s "\${RUNVERIC_RESULT_FILE}" ]; then
6686
+ RUNVERIC_BODY_STATUS="\${opera_status}" RUNVERIC_BODY_CAPTURE="\${opera_capture}" \\
6687
+ RUNVERIC_BODY_PATH="\${opera_body_path}" \\
6688
+ python3 - > "\${RUNVERIC_RESULT_FILE}" <<'PY'
6009
6689
  import json, os
6010
6690
 
6011
6691
  ${FALLBACK_RESULT_PYTHON}
6012
6692
 
6013
- with open(os.environ["OPERA_BODY_CAPTURE"], encoding="utf-8", errors="replace") as handle:
6693
+ with open(os.environ["RUNVERIC_BODY_CAPTURE"], encoding="utf-8", errors="replace") as handle:
6014
6694
  captured = handle.read()
6015
- print(json.dumps(opera_result(int(os.environ["OPERA_BODY_STATUS"]), captured, os.environ["OPERA_BODY_PATH"])))
6695
+ print(json.dumps(opera_result(int(os.environ["RUNVERIC_BODY_STATUS"]), captured, os.environ["RUNVERIC_BODY_PATH"])))
6016
6696
  PY
6017
6697
  fi
6018
6698
 
@@ -6037,7 +6717,7 @@ ${FALLBACK_RESULT_PYTHON}
6037
6717
  # or \`import\` resolves against the package rather than against the scratch directory the runner
6038
6718
  # starts in. os.chdir moves this process, and the body inherits it.
6039
6719
  opera_package_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
6040
- os.environ["OPERA_PACKAGE_ROOT"] = opera_package_root
6720
+ os.environ["RUNVERIC_PACKAGE_ROOT"] = opera_package_root
6041
6721
  os.chdir(opera_package_root)
6042
6722
 
6043
6723
  # THE ONE PLACE the author's path is written into this file. Everything below refers to the
@@ -6064,7 +6744,7 @@ opera_status = opera_process.wait()
6064
6744
 
6065
6745
  # THE BODY'S OWN RESULT WINS. The wrapper fills a gap; it never overwrites what an author
6066
6746
  # reported for themselves \u2014 outputs, checks and rollbackCapture all arrive this way.
6067
- opera_result_file = os.environ["OPERA_RESULT_FILE"]
6747
+ opera_result_file = os.environ["RUNVERIC_RESULT_FILE"]
6068
6748
  opera_written = os.path.exists(opera_result_file) and os.path.getsize(opera_result_file) > 0
6069
6749
  if not opera_written:
6070
6750
  with open(opera_result_file, "w", encoding="utf-8") as handle:
@@ -6084,27 +6764,27 @@ sys.exit(opera_status)
6084
6764
  # from whether it exits 0 and what it printed. Anything that runs is a working action.
6085
6765
  #
6086
6766
  # What you are given:
6087
- # $env:OPERA_PARAMETERS_FILE the operator's answers, as JSON
6088
- # $env:OPERA_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
6089
- # $env:OPERA_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
6767
+ # $env:RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
6768
+ # $env:RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
6769
+ # $env:RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
6090
6770
  # anything below changes something
6091
- # $env:OPERA_WORK_DIR scratch space; nothing else is writable
6771
+ # $env:RUNVERIC_WORK_DIR scratch space; nothing else is writable
6092
6772
  # Anything you print becomes a log line, and becomes the run's message.
6093
6773
  #
6094
- # WHERE YOU ARE. Your script runs from the package root \u2014 $env:OPERA_PACKAGE_ROOT \u2014 so
6774
+ # WHERE YOU ARE. Your script runs from the package root \u2014 $env:RUNVERIC_PACKAGE_ROOT \u2014 so
6095
6775
  # \`. ./lib/helpers.ps1\` resolves the way it does on your laptop. That directory is READ-ONLY
6096
- # and NON-EXECUTABLE: write to $env:OPERA_WORK_DIR, and start another script through its
6776
+ # and NON-EXECUTABLE: write to $env:RUNVERIC_WORK_DIR, and start another script through its
6097
6777
  # interpreter (\`pwsh ./tool.ps1\`) rather than executing it directly.
6098
6778
  #
6099
6779
  # WANT MORE THAN A MESSAGE \u2014 outputs, a rollbackCapture, your own error code? Write that JSON
6100
- # to $env:OPERA_RESULT_FILE yourself and the wrapper leaves it alone. run/main.ps1 says how.
6780
+ # to $env:RUNVERIC_RESULT_FILE yourself and the wrapper leaves it alone. run/main.ps1 says how.
6101
6781
 
6102
6782
  Set-StrictMode -Version Latest
6103
6783
  $ErrorActionPreference = 'Stop'
6104
6784
 
6105
- $parameters = Get-Content -LiteralPath $env:OPERA_PARAMETERS_FILE -Raw | ConvertFrom-Json
6106
- $context = Get-Content -LiteralPath $env:OPERA_CONTEXT_FILE -Raw | ConvertFrom-Json
6107
- $dryRun = $env:OPERA_DRY_RUN -eq 'true'
6785
+ $parameters = Get-Content -LiteralPath $env:RUNVERIC_PARAMETERS_FILE -Raw | ConvertFrom-Json
6786
+ $context = Get-Content -LiteralPath $env:RUNVERIC_CONTEXT_FILE -Raw | ConvertFrom-Json
6787
+ $dryRun = $env:RUNVERIC_DRY_RUN -eq 'true'
6108
6788
 
6109
6789
  # Strict mode makes reading an absent property an error rather than an empty string, so an
6110
6790
  # optional parameter is asked for explicitly. That is the point of strict mode: the failure
@@ -6118,85 +6798,85 @@ Write-Output "Running for execution $($context.executionId)."
6118
6798
  Write-Output "Note: \${note}"
6119
6799
 
6120
6800
  if ($dryRun) {
6121
- Write-Output 'OPERA_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}'
6801
+ Write-Output 'RUNVERIC_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}'
6122
6802
  } else {
6123
6803
  # Do the real work here.
6124
- Write-Output 'OPERA_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}'
6804
+ Write-Output 'RUNVERIC_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}'
6125
6805
  }
6126
6806
  `,
6127
6807
  bash: `# YOUR SCRIPT. run/main.sh \u2014 ${contracts_1.BRAND_SHORT_NAME}'s wrapper \u2014 starts this file and writes the run's result
6128
6808
  # from whether it exits 0 and what it printed. Anything that runs is a working action.
6129
6809
  #
6130
6810
  # What you are given:
6131
- # $OPERA_PARAMETERS_FILE the operator's answers, as JSON
6132
- # $OPERA_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
6133
- # $OPERA_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
6811
+ # $RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
6812
+ # $RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
6813
+ # $RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
6134
6814
  # anything below changes something
6135
- # $OPERA_WORK_DIR scratch space; nothing else is writable
6815
+ # $RUNVERIC_WORK_DIR scratch space; nothing else is writable
6136
6816
  # Anything you print becomes a log line, and becomes the run's message.
6137
6817
  #
6138
- # WHERE YOU ARE. Your script runs from the package root \u2014 $OPERA_PACKAGE_ROOT \u2014 so
6818
+ # WHERE YOU ARE. Your script runs from the package root \u2014 $RUNVERIC_PACKAGE_ROOT \u2014 so
6139
6819
  # \`source ./lib/util.sh\` resolves the way it does on your laptop. That directory is READ-ONLY
6140
- # and NON-EXECUTABLE: write to $OPERA_WORK_DIR, and start another script through its
6820
+ # and NON-EXECUTABLE: write to $RUNVERIC_WORK_DIR, and start another script through its
6141
6821
  # interpreter (\`bash ./tool.sh\`) rather than executing it directly.
6142
6822
  #
6143
6823
  # WANT MORE THAN A MESSAGE \u2014 outputs, a rollbackCapture, your own error code? Write that JSON
6144
- # to $OPERA_RESULT_FILE yourself and the wrapper leaves it alone. run/main.sh says how.
6824
+ # to $RUNVERIC_RESULT_FILE yourself and the wrapper leaves it alone. run/main.sh says how.
6145
6825
  #
6146
6826
  # Started with \`bash -euo pipefail\`, so a failing command ends the run instead of continuing
6147
6827
  # past it.
6148
6828
 
6149
- note="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["OPERA_PARAMETERS_FILE"])).get("note",""))')"
6150
- execution_id="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["OPERA_CONTEXT_FILE"]))["executionId"])')"
6829
+ note="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["RUNVERIC_PARAMETERS_FILE"])).get("note",""))')"
6830
+ execution_id="$(python3 -c 'import json,os,sys; print(json.load(open(os.environ["RUNVERIC_CONTEXT_FILE"]))["executionId"])')"
6151
6831
 
6152
6832
  echo "Running for execution \${execution_id}."
6153
6833
  echo "Note: \${note}"
6154
6834
 
6155
- if [ "\${OPERA_DRY_RUN}" = "true" ]; then
6156
- echo 'OPERA_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}'
6835
+ if [ "\${RUNVERIC_DRY_RUN}" = "true" ]; then
6836
+ echo 'RUNVERIC_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}'
6157
6837
  else
6158
6838
  # Do the real work here.
6159
- echo 'OPERA_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}'
6839
+ echo 'RUNVERIC_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}'
6160
6840
  fi
6161
6841
  `,
6162
6842
  python: `# YOUR SCRIPT. run/main.py \u2014 ${contracts_1.BRAND_SHORT_NAME}'s wrapper \u2014 starts this file and writes the run's result
6163
6843
  # from whether it exits 0 and what it printed. Anything that runs is a working action.
6164
6844
  #
6165
6845
  # What you are given:
6166
- # OPERA_PARAMETERS_FILE the operator's answers, as JSON
6167
- # OPERA_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
6168
- # OPERA_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
6846
+ # RUNVERIC_PARAMETERS_FILE the operator's answers, as JSON
6847
+ # RUNVERIC_CONTEXT_FILE non-secret run metadata (execution id, provider, scope)
6848
+ # RUNVERIC_DRY_RUN "true" when the operator asked for a rehearsal \u2014 HONOUR IT if
6169
6849
  # anything below changes something
6170
- # OPERA_WORK_DIR scratch space; nothing else is writable
6850
+ # RUNVERIC_WORK_DIR scratch space; nothing else is writable
6171
6851
  # Anything you print becomes a log line, and becomes the run's message.
6172
6852
  #
6173
- # WHERE YOU ARE. Your script runs from the package root \u2014 OPERA_PACKAGE_ROOT \u2014 so a relative
6853
+ # WHERE YOU ARE. Your script runs from the package root \u2014 RUNVERIC_PACKAGE_ROOT \u2014 so a relative
6174
6854
  # \`open()\` or \`import\` resolves the way it does on your laptop. That directory is READ-ONLY
6175
- # and NON-EXECUTABLE: write to OPERA_WORK_DIR, and start another script through its interpreter
6855
+ # and NON-EXECUTABLE: write to RUNVERIC_WORK_DIR, and start another script through its interpreter
6176
6856
  # (\`python3 ./tool.py\`) rather than executing it directly.
6177
6857
  #
6178
6858
  # WANT MORE THAN A MESSAGE \u2014 outputs, a rollbackCapture, your own error code? Write that JSON
6179
- # to OPERA_RESULT_FILE yourself and the wrapper leaves it alone. run/main.py says how.
6859
+ # to RUNVERIC_RESULT_FILE yourself and the wrapper leaves it alone. run/main.py says how.
6180
6860
 
6181
6861
  import json
6182
6862
  import os
6183
6863
 
6184
- with open(os.environ["OPERA_PARAMETERS_FILE"], encoding="utf-8") as handle:
6864
+ with open(os.environ["RUNVERIC_PARAMETERS_FILE"], encoding="utf-8") as handle:
6185
6865
  parameters = json.load(handle)
6186
- with open(os.environ["OPERA_CONTEXT_FILE"], encoding="utf-8") as handle:
6866
+ with open(os.environ["RUNVERIC_CONTEXT_FILE"], encoding="utf-8") as handle:
6187
6867
  context = json.load(handle)
6188
6868
 
6189
- dry_run = os.environ.get("OPERA_DRY_RUN") == "true"
6869
+ dry_run = os.environ.get("RUNVERIC_DRY_RUN") == "true"
6190
6870
  note = parameters.get("note", "")
6191
6871
 
6192
6872
  print(f"Running for execution {context['executionId']}.")
6193
6873
  print(f"Note: {note}")
6194
6874
 
6195
6875
  if dry_run:
6196
- print('OPERA_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}')
6876
+ print('RUNVERIC_EVENT {"level":"info","message":"Dry run \u2014 nothing was changed."}')
6197
6877
  else:
6198
6878
  # Do the real work here.
6199
- print('OPERA_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}')
6879
+ print('RUNVERIC_EVENT {"level":"success","message":"Nothing to change yet \u2014 this is a new action."}')
6200
6880
  `
6201
6881
  };
6202
6882
  var VALIDATE_SCRIPT = {
@@ -6209,7 +6889,7 @@ reason, you do not also need result.error.
6209
6889
 
6210
6890
  Set-StrictMode -Version Latest
6211
6891
  $ErrorActionPreference = 'Stop'
6212
- $parameters = Get-Content -LiteralPath $env:OPERA_PARAMETERS_FILE -Raw | ConvertFrom-Json
6892
+ $parameters = Get-Content -LiteralPath $env:RUNVERIC_PARAMETERS_FILE -Raw | ConvertFrom-Json
6213
6893
 
6214
6894
  $checks = @(
6215
6895
  [ordered]@{ status = 'passed'; message = 'The note is present.' }
@@ -6221,7 +6901,7 @@ $failed = @($checks | Where-Object { $_.status -eq 'failed' }).Count -gt 0
6221
6901
  status = if ($failed) { 'failed' } else { 'succeeded' }
6222
6902
  message = if ($failed) { 'Pre-flight found a blocking problem.' } else { 'Pre-flight passed.' }
6223
6903
  checks = $checks
6224
- } | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:OPERA_RESULT_FILE -Encoding utf8`,
6904
+ } | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:RUNVERIC_RESULT_FILE -Encoding utf8`,
6225
6905
  bash: `The VALIDATE phase \u2014 a pre-flight that changes nothing.
6226
6906
  Enable it with \`validateEntrypoint: validate/\` in action.yaml, then uncomment this file.
6227
6907
 
@@ -6229,7 +6909,7 @@ A validate result carries CHECKS and no outputs. One entry per thing you checked
6229
6909
  passed / warning / failed. If any check failed, set status to "failed" \u2014 the checks ARE the
6230
6910
  reason, you do not also need result.error.
6231
6911
 
6232
- python3 - <<'PY' > "\${OPERA_RESULT_FILE}"
6912
+ python3 - <<'PY' > "\${RUNVERIC_RESULT_FILE}"
6233
6913
  import json
6234
6914
  checks = [{"status": "passed", "message": "The note is present."}]
6235
6915
  failed = any(check["status"] == "failed" for check in checks)
@@ -6253,7 +6933,7 @@ import os
6253
6933
  checks = [{"status": "passed", "message": "The note is present."}]
6254
6934
  failed = any(check["status"] == "failed" for check in checks)
6255
6935
 
6256
- with open(os.environ["OPERA_RESULT_FILE"], "w", encoding="utf-8") as handle:
6936
+ with open(os.environ["RUNVERIC_RESULT_FILE"], "w", encoding="utf-8") as handle:
6257
6937
  json.dump(
6258
6938
  {
6259
6939
  "protocolVersion": "${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}",
@@ -6269,28 +6949,28 @@ with open(os.environ["OPERA_RESULT_FILE"], "w", encoding="utf-8") as handle:
6269
6949
  Enable it by setting \`rollback: true\`, \`rollbackEntrypoint: rollback/\` and
6270
6950
  \`capabilities.rollback: true\` in action.yaml, then uncomment this file.
6271
6951
 
6272
- Rollback reads what the run phase captured, at $env:OPERA_PRIOR_RESULT_FILE. That is why a
6952
+ Rollback reads what the run phase captured, at $env:RUNVERIC_PRIOR_RESULT_FILE. That is why a
6273
6953
  run that intends to be undoable writes \`rollbackCapture\` into its result \u2014 this phase has
6274
6954
  nothing else to work from.
6275
6955
 
6276
6956
  Set-StrictMode -Version Latest
6277
6957
  $ErrorActionPreference = 'Stop'
6278
- $prior = Get-Content -LiteralPath $env:OPERA_PRIOR_RESULT_FILE -Raw | ConvertFrom-Json
6958
+ $prior = Get-Content -LiteralPath $env:RUNVERIC_PRIOR_RESULT_FILE -Raw | ConvertFrom-Json
6279
6959
 
6280
6960
  [ordered]@{
6281
6961
  protocolVersion = '${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}'
6282
6962
  status = 'succeeded'
6283
6963
  message = 'Rolled back.'
6284
- } | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:OPERA_RESULT_FILE -Encoding utf8`,
6964
+ } | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $env:RUNVERIC_RESULT_FILE -Encoding utf8`,
6285
6965
  bash: `The ROLLBACK phase \u2014 undo what the run phase did.
6286
6966
  Enable it by setting \`rollback: true\`, \`rollbackEntrypoint: rollback/\` and
6287
6967
  \`capabilities.rollback: true\` in action.yaml, then uncomment this file.
6288
6968
 
6289
- Rollback reads what the run phase captured, at $OPERA_PRIOR_RESULT_FILE. That is why a run
6969
+ Rollback reads what the run phase captured, at $RUNVERIC_PRIOR_RESULT_FILE. That is why a run
6290
6970
  that intends to be undoable writes \`rollbackCapture\` into its result \u2014 this phase has
6291
6971
  nothing else to work from.
6292
6972
 
6293
- python3 - <<'PY' > "\${OPERA_RESULT_FILE}"
6973
+ python3 - <<'PY' > "\${RUNVERIC_RESULT_FILE}"
6294
6974
  import json
6295
6975
  print(json.dumps({
6296
6976
  "protocolVersion": "${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}",
@@ -6302,17 +6982,17 @@ PY`,
6302
6982
  Enable it by setting \`rollback: true\`, \`rollbackEntrypoint: rollback/\` and
6303
6983
  \`capabilities.rollback: true\` in action.yaml, then uncomment this file.
6304
6984
 
6305
- Rollback reads what the run phase captured, at OPERA_PRIOR_RESULT_FILE. That is why a run
6985
+ Rollback reads what the run phase captured, at RUNVERIC_PRIOR_RESULT_FILE. That is why a run
6306
6986
  that intends to be undoable writes \`rollbackCapture\` into its result \u2014 this phase has
6307
6987
  nothing else to work from.
6308
6988
 
6309
6989
  import json
6310
6990
  import os
6311
6991
 
6312
- with open(os.environ["OPERA_PRIOR_RESULT_FILE"], encoding="utf-8") as handle:
6992
+ with open(os.environ["RUNVERIC_PRIOR_RESULT_FILE"], encoding="utf-8") as handle:
6313
6993
  prior = json.load(handle)
6314
6994
 
6315
- with open(os.environ["OPERA_RESULT_FILE"], "w", encoding="utf-8") as handle:
6995
+ with open(os.environ["RUNVERIC_RESULT_FILE"], "w", encoding="utf-8") as handle:
6316
6996
  json.dump(
6317
6997
  {
6318
6998
  "protocolVersion": "${contracts_1.PACKAGE_RESULT_PROTOCOL_VERSION}",
@@ -6339,7 +7019,7 @@ point of it is that the loop below works before you have written anything.
6339
7019
 
6340
7020
  **\`run/${bodyFile}\` is your script. \`run/${entryFile}\` is ${contracts_1.BRAND_SHORT_NAME}'s wrapper.**
6341
7021
 
6342
- ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`OPERA_RESULT_FILE\`. A package that
7022
+ ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`RUNVERIC_RESULT_FILE\`. A package that
6343
7023
  exits 0 without writing one has reported nothing, and ${contracts_1.BRAND_SHORT_NAME} refuses it rather than inventing a
6344
7024
  result \u2014 that strictness is what makes every check, output and rollback capture in the product
6345
7025
  worth trusting.
@@ -6347,7 +7027,7 @@ worth trusting.
6347
7027
  \`run/${entryFile}\` is what keeps you on the right side of it. It starts \`run/${bodyFile}\`,
6348
7028
  and then:
6349
7029
 
6350
- - if your script wrote \`OPERA_RESULT_FILE\` itself, it leaves that result exactly alone;
7030
+ - if your script wrote \`RUNVERIC_RESULT_FILE\` itself, it leaves that result exactly alone;
6351
7031
  - otherwise it writes one \u2014 **succeeded** when your script exits 0, **failed** with the exit
6352
7032
  code and a \`result.error\` when it does not \u2014 carrying whatever your script printed as the
6353
7033
  message.
@@ -6410,14 +7090,14 @@ script and reports the result.
6410
7090
 
6411
7091
  ## The result contract, and why the wrapper is there
6412
7092
 
6413
- ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`OPERA_RESULT_FILE\`. A package that
7093
+ ${contracts_1.BRAND_SHORT_NAME} reads exactly one thing from a package: the JSON at \`RUNVERIC_RESULT_FILE\`. A package that
6414
7094
  exits 0 without writing one has reported nothing, and ${contracts_1.BRAND_SHORT_NAME} refuses it rather than inventing a
6415
7095
  result \u2014 that strictness is what makes every check, output and rollback capture in the product
6416
7096
  worth trusting.
6417
7097
 
6418
7098
  \`run/${entryFile}\` keeps you on the right side of it. It starts \`${input.entrypoint}\`, and then:
6419
7099
 
6420
- - if your script wrote \`OPERA_RESULT_FILE\` itself, it leaves that result exactly alone;
7100
+ - if your script wrote \`RUNVERIC_RESULT_FILE\` itself, it leaves that result exactly alone;
6421
7101
  - otherwise it writes one \u2014 **succeeded** when your script exits 0, **failed** with the exit
6422
7102
  code and a \`result.error\` when it does not \u2014 carrying whatever your script printed as the
6423
7103
  message.
@@ -6426,12 +7106,12 @@ So a script that only prints something is already a complete action.
6426
7106
 
6427
7107
  ## Where your script runs
6428
7108
 
6429
- From the **package root**, which is also \`OPERA_PACKAGE_ROOT\`. That is what makes
7109
+ From the **package root**, which is also \`RUNVERIC_PACKAGE_ROOT\`. That is what makes
6430
7110
  \`source ./lib/util.sh\` and \`. ./lib/helpers.ps1\` resolve the way they do on your laptop.
6431
7111
 
6432
7112
  Two things about that directory are worth knowing before your first run:
6433
7113
 
6434
- - it is **read-only** \u2014 write to \`OPERA_WORK_DIR\`, the only writable location;
7114
+ - it is **read-only** \u2014 write to \`RUNVERIC_WORK_DIR\`, the only writable location;
6435
7115
  - it is **non-executable** \u2014 start another script through its interpreter
6436
7116
  (\`bash ./tool.sh\`, \`pwsh ./tool.ps1\`) rather than executing it directly.
6437
7117
 
@@ -6486,7 +7166,7 @@ SOFTWARE.
6486
7166
  }
6487
7167
  __name(license, "license");
6488
7168
  var GITIGNORE = `# runveric test writes its scratch here.
6489
- .opera/
7169
+ .runveric/
6490
7170
  *.log
6491
7171
  `;
6492
7172
  function yamlString(value) {
@@ -6857,67 +7537,7 @@ var require_dist2 = __commonJS({
6857
7537
  });
6858
7538
 
6859
7539
  // package.json
6860
- var version = "0.1.1";
6861
-
6862
- // src/check.ts
6863
- var import_action_manifest = __toESM(require_dist2());
6864
- import { resolve } from "path";
6865
-
6866
- // src/package-dir.ts
6867
- import { readdir, readFile, stat } from "fs/promises";
6868
- import { join, relative, sep } from "path";
6869
- var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
6870
- ".git",
6871
- ".opera",
6872
- "node_modules",
6873
- ".venv",
6874
- "__pycache__",
6875
- ".DS_Store"
6876
- ]);
6877
- var MAX_FILES = 2e3;
6878
- var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
6879
- var PackageDirectoryError = class extends Error {
6880
- static {
6881
- __name(this, "PackageDirectoryError");
6882
- }
6883
- };
6884
- async function readPackageDirectory(root) {
6885
- const properties = await stat(root).catch(() => null);
6886
- if (!properties?.isDirectory()) {
6887
- throw new PackageDirectoryError(`${root} is not a directory.`);
6888
- }
6889
- const files = /* @__PURE__ */ new Map();
6890
- let totalBytes = 0;
6891
- const walk = /* @__PURE__ */ __name(async (directory) => {
6892
- for (const entry of await readdir(directory, {
6893
- withFileTypes: true
6894
- })) {
6895
- if (IGNORED_DIRECTORIES.has(entry.name)) continue;
6896
- const full = join(directory, entry.name);
6897
- if (entry.isDirectory()) {
6898
- await walk(full);
6899
- continue;
6900
- }
6901
- if (!entry.isFile()) continue;
6902
- if (files.size >= MAX_FILES) {
6903
- throw new PackageDirectoryError(`This directory holds more than ${MAX_FILES} files. That is almost certainly not a single action package \u2014 run this from the package directory itself.`);
6904
- }
6905
- const contents = await readFile(full);
6906
- totalBytes += contents.byteLength;
6907
- if (totalBytes > MAX_TOTAL_BYTES) {
6908
- throw new PackageDirectoryError(`This directory holds more than ${MAX_TOTAL_BYTES / (1024 * 1024)}MB. An action package is source, not artifacts \u2014 add build output to .gitignore.`);
6909
- }
6910
- files.set(toPackagePath(root, full), contents);
6911
- }
6912
- }, "walk");
6913
- await walk(root);
6914
- return files;
6915
- }
6916
- __name(readPackageDirectory, "readPackageDirectory");
6917
- function toPackagePath(root, full) {
6918
- return relative(root, full).split(sep).join("/");
6919
- }
6920
- __name(toPackagePath, "toPackagePath");
7540
+ var version = "0.2.0";
6921
7541
 
6922
7542
  // src/report.ts
6923
7543
  var useColour = process.stdout.isTTY === true && !process.env.NO_COLOR;
@@ -7026,13 +7646,518 @@ function printResult(result) {
7026
7646
  }
7027
7647
  __name(printResult, "printResult");
7028
7648
 
7029
- // src/check.ts
7649
+ // src/catalog/publish-action.ts
7650
+ var import_contracts2 = __toESM(require_dist());
7651
+ import { readFileSync } from "fs";
7652
+ import { dirname, resolve } from "path";
7653
+ import { parse as parseYaml } from "yaml";
7654
+
7655
+ // src/catalog/artifact.ts
7030
7656
  var import_contracts = __toESM(require_dist());
7031
- async function runCheck(options) {
7032
- const directory = resolve(options.directory ?? ".");
7033
- let files;
7034
- try {
7035
- files = await readPackageDirectory(directory);
7657
+ import { execFileSync } from "child_process";
7658
+ import { createHash } from "crypto";
7659
+ function parseRepository(ref, flag) {
7660
+ if (!import_contracts.ARTIFACT_REPOSITORY_PATTERN.test(ref)) {
7661
+ throw new Error(`${flag} "${ref}" is not a bare registry repository (host[:port]/path, lowercase, no scheme, no tag, no digest). A catalog artifact is addressed by digest; tags are for humans.`);
7662
+ }
7663
+ const cut = ref.indexOf("/");
7664
+ return {
7665
+ ref,
7666
+ host: ref.slice(0, cut),
7667
+ path: ref.slice(cut + 1),
7668
+ namespace: ref.slice(0, ref.lastIndexOf("/"))
7669
+ };
7670
+ }
7671
+ __name(parseRepository, "parseRepository");
7672
+ var isDigest = /* @__PURE__ */ __name((value) => import_contracts.ARTIFACT_DIGEST_PATTERN.test(value), "isDigest");
7673
+ var TAG_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/;
7674
+ function requireTag(tag, origin2) {
7675
+ if (!TAG_PATTERN.test(tag)) {
7676
+ throw new Error(`${origin2} gives the tag "${tag}", which a registry cannot hold: letters, digits, "_", "." and "-", starting with a letter, digit or "_", at most 128 characters. Pass --tag.`);
7677
+ }
7678
+ }
7679
+ __name(requireTag, "requireTag");
7680
+ var LOOPBACK = /^(localhost|127\.0\.0\.1|\[::1\])(:[0-9]+)?$/;
7681
+ var origin = /* @__PURE__ */ __name((host) => `${LOOPBACK.test(host) ? "http" : "https"}://${host}`, "origin");
7682
+ var sha256 = /* @__PURE__ */ __name((bytes) => `sha256:${createHash("sha256").update(bytes).digest("hex")}`, "sha256");
7683
+ var EMPTY_CONFIG = Buffer.from("{}");
7684
+ var EMPTY_CONFIG_DESCRIPTOR = {
7685
+ mediaType: "application/vnd.oci.empty.v1+json",
7686
+ digest: sha256(EMPTY_CONFIG),
7687
+ size: EMPTY_CONFIG.length
7688
+ };
7689
+ var OCI_MANIFEST = "application/vnd.oci.image.manifest.v1+json";
7690
+ function provenance(flags, dir) {
7691
+ const git = /* @__PURE__ */ __name((...args) => {
7692
+ try {
7693
+ return execFileSync("git", [
7694
+ "-C",
7695
+ dir,
7696
+ ...args
7697
+ ], {
7698
+ encoding: "utf8",
7699
+ stdio: [
7700
+ "ignore",
7701
+ "pipe",
7702
+ "ignore"
7703
+ ]
7704
+ }).trim();
7705
+ } catch {
7706
+ return "";
7707
+ }
7708
+ }, "git");
7709
+ const actions = process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` : "";
7710
+ const repository = flags.repository ?? (actions || git("remote", "get-url", "origin"));
7711
+ const commit = flags.commit ?? (process.env.GITHUB_SHA || git("rev-parse", "HEAD"));
7712
+ if (!repository || !commit) {
7713
+ throw new Error("Provenance is required and could not be determined. Pass --repository <url> and --commit <sha> (in GitHub Actions, GITHUB_REPOSITORY and GITHUB_SHA supply them).");
7714
+ }
7715
+ return {
7716
+ repository,
7717
+ commit,
7718
+ publishedAt: (/* @__PURE__ */ new Date()).toISOString()
7719
+ };
7720
+ }
7721
+ __name(provenance, "provenance");
7722
+ function buildArtifact(document) {
7723
+ const documentBytes = Buffer.from(`${JSON.stringify(document, null, 2)}
7724
+ `);
7725
+ const manifest = {
7726
+ schemaVersion: 2,
7727
+ mediaType: OCI_MANIFEST,
7728
+ artifactType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
7729
+ config: EMPTY_CONFIG_DESCRIPTOR,
7730
+ layers: [
7731
+ {
7732
+ mediaType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
7733
+ digest: sha256(documentBytes),
7734
+ size: documentBytes.length
7735
+ }
7736
+ ],
7737
+ /*
7738
+ * ★ SOURCE AND REVISION ARE FOR THE REGISTRY, NOT THE RESOLVER (docs/127 A.1). GHCR connects
7739
+ * a package to its repository through `org.opencontainers.image.source`, and that link is what
7740
+ * lets the repository's workflow token keep publishing to the package its first run created.
7741
+ * The resolver reads none of these — the document's own provenance is the record — so an
7742
+ * artifact published without them still resolves.
7743
+ */
7744
+ annotations: {
7745
+ "org.opencontainers.image.created": document.provenance.publishedAt,
7746
+ "org.opencontainers.image.source": document.provenance.repository,
7747
+ "org.opencontainers.image.revision": document.provenance.commit
7748
+ }
7749
+ };
7750
+ const manifestBytes = Buffer.from(JSON.stringify(manifest));
7751
+ return {
7752
+ documentBytes,
7753
+ manifestBytes,
7754
+ manifestDigest: sha256(manifestBytes)
7755
+ };
7756
+ }
7757
+ __name(buildArtifact, "buildArtifact");
7758
+ function basicCredential() {
7759
+ const pat = process.env.GHCR_TOKEN;
7760
+ if (pat) return Buffer.from(`x:${pat}`).toString("base64");
7761
+ const workflow = process.env.GITHUB_TOKEN;
7762
+ if (workflow) return Buffer.from(`${process.env.GITHUB_ACTOR || "x"}:${workflow}`).toString("base64");
7763
+ return void 0;
7764
+ }
7765
+ __name(basicCredential, "basicCredential");
7766
+ var tokens = /* @__PURE__ */ new Map();
7767
+ async function token(repository, challenge) {
7768
+ const field = /* @__PURE__ */ __name((name) => new RegExp(`${name}="([^"]*)"`).exec(challenge ?? "")?.[1], "field");
7769
+ const realm = field("realm");
7770
+ if (!realm) throw new Error(`${repository.host} asked for credentials without a Bearer challenge.`);
7771
+ const url = new URL(realm);
7772
+ const service = field("service");
7773
+ if (service) url.searchParams.set("service", service);
7774
+ url.searchParams.set("scope", `repository:${repository.path}:pull,push`);
7775
+ const credential = basicCredential();
7776
+ const answer = await fetch(url, {
7777
+ headers: credential ? {
7778
+ Authorization: `Basic ${credential}`
7779
+ } : {}
7780
+ });
7781
+ if (!answer.ok) {
7782
+ throw new Error(`${repository.host} refused a push token for ${repository.path}: HTTP ${answer.status}` + (credential ? "." : " \u2014 set GHCR_TOKEN to a token with write:packages, or in a workflow GITHUB_TOKEN with packages: write."));
7783
+ }
7784
+ const body = await answer.json();
7785
+ const value = body.token ?? body.access_token ?? "";
7786
+ tokens.set(repository.path, value);
7787
+ return value;
7788
+ }
7789
+ __name(token, "token");
7790
+ async function call(repository, method, path, { headers = {}, body } = {}) {
7791
+ const url = path.startsWith("http") ? path : `${origin(repository.host)}/v2/${repository.path}/${path}`;
7792
+ const send = /* @__PURE__ */ __name((bearer) => fetch(url, {
7793
+ method,
7794
+ body,
7795
+ redirect: "follow",
7796
+ headers: {
7797
+ ...headers,
7798
+ ...bearer ? {
7799
+ Authorization: `Bearer ${bearer}`
7800
+ } : {}
7801
+ }
7802
+ }), "send");
7803
+ let answer = await send(tokens.get(repository.path));
7804
+ if (answer.status === 401) answer = await send(await token(repository, answer.headers.get("www-authenticate")));
7805
+ return answer;
7806
+ }
7807
+ __name(call, "call");
7808
+ async function pushBlob(repository, bytes) {
7809
+ const digest = sha256(bytes);
7810
+ if ((await call(repository, "HEAD", `blobs/${digest}`)).ok) return digest;
7811
+ const started = await call(repository, "POST", "blobs/uploads/");
7812
+ if (started.status !== 202) throw new Error(`${repository.host} refused an upload: HTTP ${started.status}`);
7813
+ const location = new URL(started.headers.get("location") ?? "", `${origin(repository.host)}/`);
7814
+ location.searchParams.set("digest", digest);
7815
+ const put = await call(repository, "PUT", location.toString(), {
7816
+ headers: {
7817
+ "Content-Type": "application/octet-stream",
7818
+ "Content-Length": String(bytes.length)
7819
+ },
7820
+ body: bytes
7821
+ });
7822
+ if (put.status !== 201) throw new Error(`${repository.host} rejected blob ${digest}: HTTP ${put.status} ${await put.text()}`);
7823
+ return digest;
7824
+ }
7825
+ __name(pushBlob, "pushBlob");
7826
+ async function manifestExists(repository, digest) {
7827
+ const answer = await call(repository, "HEAD", `manifests/${digest}`, {
7828
+ headers: {
7829
+ Accept: OCI_MANIFEST
7830
+ }
7831
+ });
7832
+ return answer.ok;
7833
+ }
7834
+ __name(manifestExists, "manifestExists");
7835
+ async function pushArtifact(repository, artifact, tag) {
7836
+ await pushBlob(repository, EMPTY_CONFIG);
7837
+ await pushBlob(repository, artifact.documentBytes);
7838
+ for (const reference of [
7839
+ artifact.manifestDigest,
7840
+ tag
7841
+ ]) {
7842
+ const put = await call(repository, "PUT", `manifests/${reference}`, {
7843
+ headers: {
7844
+ "Content-Type": OCI_MANIFEST
7845
+ },
7846
+ body: artifact.manifestBytes
7847
+ });
7848
+ if (put.status !== 201) {
7849
+ throw new Error(`${repository.host} rejected the manifest (${reference}): HTTP ${put.status} ${await put.text()}`);
7850
+ }
7851
+ }
7852
+ const asked = await call(repository, "HEAD", `manifests/${tag}`, {
7853
+ headers: {
7854
+ Accept: OCI_MANIFEST
7855
+ }
7856
+ });
7857
+ const reported = asked.headers.get("docker-content-digest");
7858
+ if (!asked.ok || !reported) {
7859
+ throw new Error(`${repository.host} could not say what :${tag} resolves to (HTTP ${asked.status}).`);
7860
+ }
7861
+ if (reported !== artifact.manifestDigest) {
7862
+ throw new Error(`${repository.host} reports :${tag} as ${reported}, but the bytes sent hash to ${artifact.manifestDigest}. Something re-encoded the manifest; nothing printed here would name what is stored. Refusing to report a digest.`);
7863
+ }
7864
+ return reported;
7865
+ }
7866
+ __name(pushArtifact, "pushArtifact");
7867
+ var defaultTag = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..*$/, "").toLowerCase(), "defaultTag");
7868
+
7869
+ // src/catalog/flags.ts
7870
+ function parseCatalogFlags(argv, known, repeated = []) {
7871
+ const single = {};
7872
+ const many = {};
7873
+ for (let index = 0; index < argv.length; index += 1) {
7874
+ const argument = argv[index];
7875
+ const separator = argument.indexOf("=");
7876
+ const name = separator === -1 ? argument.slice(2) : argument.slice(2, separator);
7877
+ if (!argument.startsWith("--") || !known.includes(name)) throw new Error(`Unknown argument: ${argument}`);
7878
+ let value;
7879
+ if (separator !== -1) {
7880
+ value = argument.slice(separator + 1);
7881
+ } else {
7882
+ value = argv[index + 1];
7883
+ index += 1;
7884
+ }
7885
+ if (value === void 0 || value === "" || value.startsWith("--")) throw new Error(`--${name} needs a value.`);
7886
+ if (repeated.includes(name)) (many[name] ??= []).push(value);
7887
+ else single[name] = value;
7888
+ }
7889
+ return {
7890
+ single,
7891
+ many
7892
+ };
7893
+ }
7894
+ __name(parseCatalogFlags, "parseCatalogFlags");
7895
+
7896
+ // src/catalog/publish-action.ts
7897
+ var PUBLISH_ACTION_USAGE = "runveric catalog publish-action --manifest <file.yaml> --to <registry>/<namespace>/<repository>\n [--tag <tag>] [--repository <source url>] [--commit <sha>]";
7898
+ var isMapping = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
7899
+ async function runPublishAction(argv) {
7900
+ const { single: flags } = parseCatalogFlags(argv, [
7901
+ "manifest",
7902
+ "to",
7903
+ "tag",
7904
+ "repository",
7905
+ "commit"
7906
+ ]);
7907
+ if (!flags.manifest || !flags.to) throw new Error("--manifest and --to are required.");
7908
+ const repository = parseRepository(flags.to, "--to");
7909
+ const path = resolve(flags.manifest);
7910
+ const manifest = readFileSync(path, "utf8");
7911
+ let parsed;
7912
+ try {
7913
+ parsed = parseYaml(manifest);
7914
+ } catch (cause) {
7915
+ throw new Error(`${flags.manifest} is not valid YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
7916
+ }
7917
+ const fields = isMapping(parsed) ? parsed : {};
7918
+ const key = typeof fields.key === "string" && fields.key.length > 0 ? fields.key : null;
7919
+ if (key === null) throw new Error(`${flags.manifest} declares no top-level \`key:\` \u2014 is it a catalog manifest?`);
7920
+ const misnamed = (0, import_contracts2.actionPackageRefusal)(key, repository.ref);
7921
+ if (misnamed !== null) throw new Error(`--to: ${misnamed}`);
7922
+ let tag;
7923
+ if (fields.declares === "definition+version") {
7924
+ const version2 = typeof fields.version === "string" ? fields.version : null;
7925
+ if (version2 === null) throw new Error(`${flags.manifest} declares "definition+version" and no version:.`);
7926
+ if (flags.tag !== void 0 && flags.tag !== version2) {
7927
+ throw new Error(`${flags.manifest} declares version ${version2}, and the tag is ${flags.tag}. Tag the commit ${version2}, or change version: in ${flags.manifest} first.`);
7928
+ }
7929
+ tag = version2;
7930
+ } else {
7931
+ tag = flags.tag ?? defaultTag();
7932
+ }
7933
+ requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.manifest}'s version`);
7934
+ const artifact = buildArtifact({
7935
+ schemaVersion: import_contracts2.CATALOG_ACTION_ARTIFACT_SCHEMA_V1,
7936
+ manifest,
7937
+ provenance: provenance(flags, dirname(path))
7938
+ });
7939
+ const digest = await pushArtifact(repository, artifact, tag);
7940
+ process.stderr.write(`\u2713 ${key} published to ${repository.ref}:${tag}
7941
+ `);
7942
+ process.stderr.write(` the registry reports ${digest} \u2014 the digest a release must name.
7943
+ `);
7944
+ process.stdout.write(`${key}=${repository.ref}@${digest}
7945
+ `);
7946
+ }
7947
+ __name(runPublishAction, "runPublishAction");
7948
+
7949
+ // src/catalog/publish-release.ts
7950
+ var import_contracts3 = __toESM(require_dist());
7951
+ import { readFileSync as readFileSync2 } from "fs";
7952
+ import { parse as parseYaml2 } from "yaml";
7953
+ var PUBLISH_RELEASE_USAGE = "runveric catalog publish-release (--from <release.yaml> | --action <key>=<repository>@<digest> \u2026)\n --to <registry>/<namespace>/<repository> [--version <label>]\n [--tag <tag>] [--repository <source url>] [--commit <sha>]";
7954
+ function entriesFromFlags(values) {
7955
+ return values.map((value) => {
7956
+ const match = /^([^=]+)=([^@]+)@(.+)$/.exec(value);
7957
+ if (!match) throw new Error(`--action "${value}" is not <key>=<repository>@<digest>.`);
7958
+ const [, key = "", artifactRef = "", digest = ""] = match;
7959
+ return {
7960
+ key,
7961
+ artifactRef,
7962
+ digest,
7963
+ label: `--action ${key}`
7964
+ };
7965
+ });
7966
+ }
7967
+ __name(entriesFromFlags, "entriesFromFlags");
7968
+ function specFromFile(path) {
7969
+ let value;
7970
+ try {
7971
+ value = parseYaml2(readFileSync2(path, "utf8"));
7972
+ } catch (cause) {
7973
+ throw new Error(`--from ${path} could not be read as YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
7974
+ }
7975
+ const spec = (0, import_contracts3.parseCatalogReleaseSpec)(value);
7976
+ return {
7977
+ version: spec.version,
7978
+ entries: spec.actions.map((action) => ({
7979
+ ...action,
7980
+ label: `${path}'s ${action.key}`
7981
+ }))
7982
+ };
7983
+ }
7984
+ __name(specFromFile, "specFromFile");
7985
+ async function runPublishRelease(argv) {
7986
+ const { single: flags, many } = parseCatalogFlags(argv, [
7987
+ "to",
7988
+ "version",
7989
+ "action",
7990
+ "from",
7991
+ "tag",
7992
+ "repository",
7993
+ "commit"
7994
+ ], [
7995
+ "action"
7996
+ ]);
7997
+ const actionFlags = many.action ?? [];
7998
+ if (flags.from !== void 0 && actionFlags.length > 0) {
7999
+ throw new Error("--from and --action are alternatives: name the release's actions in release.yaml or as flags, not both.");
8000
+ }
8001
+ if (flags.from !== void 0 && flags.version !== void 0) {
8002
+ throw new Error("--version is read from release.yaml when --from is given; pass one or the other.");
8003
+ }
8004
+ if (!flags.to || flags.from === void 0 && (!flags.version || actionFlags.length === 0)) {
8005
+ throw new Error("--to and either --from, or --version and at least one --action, are required.");
8006
+ }
8007
+ const release = parseRepository(flags.to, "--to");
8008
+ const { version: version2, entries } = flags.from !== void 0 ? specFromFile(flags.from) : {
8009
+ version: flags.version,
8010
+ entries: entriesFromFlags(actionFlags)
8011
+ };
8012
+ const tag = flags.tag ?? (flags.from !== void 0 ? version2 : defaultTag());
8013
+ requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.from}'s version`);
8014
+ const seen = /* @__PURE__ */ new Set();
8015
+ const actions = entries.map((entry) => {
8016
+ if (seen.has(entry.key)) throw new Error(`${entry.label} names "${entry.key}" twice.`);
8017
+ seen.add(entry.key);
8018
+ const repository = parseRepository(entry.artifactRef, entry.label);
8019
+ if (repository.namespace !== release.namespace) {
8020
+ throw new Error(`${entry.label} lives in "${repository.namespace}", outside the release's namespace "${release.namespace}". The server refuses a release that sends it to another host or org.`);
8021
+ }
8022
+ if (!isDigest(entry.digest)) {
8023
+ throw new Error(`${entry.label}: "${entry.digest}" is not a digest. A release names artifacts by digest only.`);
8024
+ }
8025
+ const misnamed = (0, import_contracts3.actionPackageRefusal)(entry.key, entry.artifactRef);
8026
+ if (misnamed !== null) throw new Error(`${entry.label}: ${misnamed}`);
8027
+ return {
8028
+ ...entry,
8029
+ repository
8030
+ };
8031
+ });
8032
+ for (const action of actions) {
8033
+ if (!await manifestExists(action.repository, action.digest)) {
8034
+ throw new Error(`${action.artifactRef}@${action.digest} (${action.key}) is not in the registry.`);
8035
+ }
8036
+ }
8037
+ const artifact = buildArtifact({
8038
+ schemaVersion: import_contracts3.CATALOG_RELEASE_SCHEMA_V1,
8039
+ version: version2,
8040
+ provenance: provenance(flags, process.cwd()),
8041
+ actions: actions.map(({ key, artifactRef, digest: digest2 }) => ({
8042
+ key,
8043
+ artifactRef,
8044
+ digest: digest2
8045
+ }))
8046
+ });
8047
+ const digest = await pushArtifact(release, artifact, tag);
8048
+ process.stderr.write(`\u2713 catalog release ${version2} (${actions.length} action artifact(s)) published to ${release.ref}:${tag}
8049
+ `);
8050
+ process.stderr.write(` the registry reports ${digest} \u2014 the digest to plan and pin.
8051
+ `);
8052
+ process.stdout.write(`${release.ref}@${digest}
8053
+ `);
8054
+ }
8055
+ __name(runPublishRelease, "runPublishRelease");
8056
+
8057
+ // src/catalog/index.ts
8058
+ var CATALOG_USAGE = `runveric catalog \u2014 publish first-party catalog artifacts (docs/127)
8059
+
8060
+ ${PUBLISH_ACTION_USAGE}
8061
+ Publish one action's manifest, verbatim, as an artifact. stdout: <key>=<repository>@<digest>
8062
+
8063
+ ${PUBLISH_RELEASE_USAGE}
8064
+ Publish a release naming each action artifact by digest. stdout: <repository>@<digest>
8065
+
8066
+ Credentials: GHCR_TOKEN (write:packages), or in a workflow GITHUB_TOKEN with packages: write.
8067
+ Provenance: --repository/--commit, else GITHUB_SERVER_URL/GITHUB_REPOSITORY and GITHUB_SHA, else git.`;
8068
+ var SUBCOMMANDS = {
8069
+ "publish-action": runPublishAction,
8070
+ "publish-release": runPublishRelease
8071
+ };
8072
+ async function runCatalog(argv) {
8073
+ const [subcommand, ...rest] = argv;
8074
+ if (subcommand === void 0 || subcommand === "--help" || rest.includes("--help")) {
8075
+ process.stdout.write(`${CATALOG_USAGE}
8076
+ `);
8077
+ return subcommand === void 0 ? 1 : 0;
8078
+ }
8079
+ const run3 = SUBCOMMANDS[subcommand];
8080
+ if (run3 === void 0) {
8081
+ printError(`\u2717 "catalog ${subcommand}" is not a command. There are two: ${Object.keys(SUBCOMMANDS).join(", ")}.`);
8082
+ return 1;
8083
+ }
8084
+ try {
8085
+ await run3(rest);
8086
+ return 0;
8087
+ } catch (error) {
8088
+ printError(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
8089
+ return 1;
8090
+ }
8091
+ }
8092
+ __name(runCatalog, "runCatalog");
8093
+
8094
+ // src/check.ts
8095
+ var import_action_manifest = __toESM(require_dist2());
8096
+ import { resolve as resolve2 } from "path";
8097
+
8098
+ // src/package-dir.ts
8099
+ import { readdir, readFile, stat } from "fs/promises";
8100
+ import { join, relative, sep } from "path";
8101
+ var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
8102
+ ".git",
8103
+ ".runveric",
8104
+ "node_modules",
8105
+ ".venv",
8106
+ "__pycache__",
8107
+ ".DS_Store"
8108
+ ]);
8109
+ var MAX_FILES = 2e3;
8110
+ var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
8111
+ var PackageDirectoryError = class extends Error {
8112
+ static {
8113
+ __name(this, "PackageDirectoryError");
8114
+ }
8115
+ };
8116
+ async function readPackageDirectory(root) {
8117
+ const properties = await stat(root).catch(() => null);
8118
+ if (!properties?.isDirectory()) {
8119
+ throw new PackageDirectoryError(`${root} is not a directory.`);
8120
+ }
8121
+ const files = /* @__PURE__ */ new Map();
8122
+ let totalBytes = 0;
8123
+ const walk = /* @__PURE__ */ __name(async (directory) => {
8124
+ for (const entry of await readdir(directory, {
8125
+ withFileTypes: true
8126
+ })) {
8127
+ if (IGNORED_DIRECTORIES.has(entry.name)) continue;
8128
+ const full = join(directory, entry.name);
8129
+ if (entry.isDirectory()) {
8130
+ await walk(full);
8131
+ continue;
8132
+ }
8133
+ if (!entry.isFile()) continue;
8134
+ if (files.size >= MAX_FILES) {
8135
+ throw new PackageDirectoryError(`This directory holds more than ${MAX_FILES} files. That is almost certainly not a single action package \u2014 run this from the package directory itself.`);
8136
+ }
8137
+ const contents = await readFile(full);
8138
+ totalBytes += contents.byteLength;
8139
+ if (totalBytes > MAX_TOTAL_BYTES) {
8140
+ throw new PackageDirectoryError(`This directory holds more than ${MAX_TOTAL_BYTES / (1024 * 1024)}MB. An action package is source, not artifacts \u2014 add build output to .gitignore.`);
8141
+ }
8142
+ files.set(toPackagePath(root, full), contents);
8143
+ }
8144
+ }, "walk");
8145
+ await walk(root);
8146
+ return files;
8147
+ }
8148
+ __name(readPackageDirectory, "readPackageDirectory");
8149
+ function toPackagePath(root, full) {
8150
+ return relative(root, full).split(sep).join("/");
8151
+ }
8152
+ __name(toPackagePath, "toPackagePath");
8153
+
8154
+ // src/check.ts
8155
+ var import_contracts4 = __toESM(require_dist());
8156
+ async function runCheck(options) {
8157
+ const directory = resolve2(options.directory ?? ".");
8158
+ let files;
8159
+ try {
8160
+ files = await readPackageDirectory(directory);
7036
8161
  } catch (error) {
7037
8162
  printError(style.red(error instanceof Error ? error.message : String(error)));
7038
8163
  return 1;
@@ -7051,7 +8176,7 @@ async function runCheck(options) {
7051
8176
  print(style.red("This package would be REFUSED at import."));
7052
8177
  } else if (report.lintBlocked) {
7053
8178
  print();
7054
- print(style.red("This package would be ACCEPTED at import \u2014 and then misbehave.") + ` The errors above are not things ${import_contracts.BRAND_SHORT_NAME} checks; they are things that go wrong at run time, silently.`);
8179
+ print(style.red("This package would be ACCEPTED at import \u2014 and then misbehave.") + ` The errors above are not things ${import_contracts4.BRAND_SHORT_NAME} checks; they are things that go wrong at run time, silently.`);
7055
8180
  }
7056
8181
  if (report.advisoryCount > 0) {
7057
8182
  print();
@@ -7071,11 +8196,11 @@ async function runCheck(options) {
7071
8196
  __name(runCheck, "runCheck");
7072
8197
 
7073
8198
  // src/init.ts
7074
- var import_contracts2 = __toESM(require_dist());
8199
+ var import_contracts5 = __toESM(require_dist());
7075
8200
  var import_action_manifest2 = __toESM(require_dist2());
7076
8201
  import { mkdir, writeFile } from "fs/promises";
7077
8202
  import { createInterface } from "readline/promises";
7078
- import { dirname, join as join2, resolve as resolve2 } from "path";
8203
+ import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
7079
8204
  async function runInit(options) {
7080
8205
  const interactive = process.stdin.isTTY === true && options.yes !== true;
7081
8206
  const ask = interactive ? prompter() : null;
@@ -7087,7 +8212,7 @@ async function runInit(options) {
7087
8212
  const interpreter = await resolveInterpreter(options.interpreter, ask);
7088
8213
  if (!interpreter) return 1;
7089
8214
  const author = options.author?.trim() || await ask?.("Author", id.split("/")[0]) || id.split("/")[0];
7090
- const directory = resolve2(options.directory ?? id.split("/")[1] ?? "action");
8215
+ const directory = resolve3(options.directory ?? id.split("/")[1] ?? "action");
7091
8216
  const files = (0, import_action_manifest2.scaffoldActionPackage)({
7092
8217
  id,
7093
8218
  name,
@@ -7097,7 +8222,7 @@ async function runInit(options) {
7097
8222
  });
7098
8223
  for (const [path, contents] of files) {
7099
8224
  const target = join2(directory, path);
7100
- await mkdir(dirname(target), {
8225
+ await mkdir(dirname2(target), {
7101
8226
  recursive: true
7102
8227
  });
7103
8228
  await writeFile(target, contents, {
@@ -7140,9 +8265,9 @@ async function resolveId(supplied, ask) {
7140
8265
  }
7141
8266
  __name(resolveId, "resolveId");
7142
8267
  async function resolveInterpreter(supplied, ask) {
7143
- const value = supplied?.trim() || await ask?.(`Interpreter (${import_contracts2.PACKAGE_INTERPRETERS.join(" / ")})`, "bash") || "bash";
8268
+ const value = supplied?.trim() || await ask?.(`Interpreter (${import_contracts5.PACKAGE_INTERPRETERS.join(" / ")})`, "bash") || "bash";
7144
8269
  if (!isInterpreter(value)) {
7145
- printError(style.red(`"${value}" is not an interpreter ${import_contracts2.BRAND_SHORT_NAME} runs.`) + ` The runner image ships ${import_contracts2.PACKAGE_INTERPRETERS.join(", ")}.`);
8270
+ printError(style.red(`"${value}" is not an interpreter ${import_contracts5.BRAND_SHORT_NAME} runs.`) + ` The runner image ships ${import_contracts5.PACKAGE_INTERPRETERS.join(", ")}.`);
7146
8271
  return null;
7147
8272
  }
7148
8273
  return value;
@@ -7163,7 +8288,7 @@ function prompter() {
7163
8288
  }
7164
8289
  __name(prompter, "prompter");
7165
8290
  function isInterpreter(value) {
7166
- return import_contracts2.PACKAGE_INTERPRETERS.includes(value);
8291
+ return import_contracts5.PACKAGE_INTERPRETERS.includes(value);
7167
8292
  }
7168
8293
  __name(isInterpreter, "isInterpreter");
7169
8294
  function titleCase(value) {
@@ -7173,20 +8298,38 @@ __name(titleCase, "titleCase");
7173
8298
 
7174
8299
  // src/publish.ts
7175
8300
  var import_action_manifest3 = __toESM(require_dist2());
7176
- var import_contracts3 = __toESM(require_dist());
7177
- import { resolve as resolve3 } from "path";
7178
- var TOKEN_ENV = "OPERA_TOKEN";
7179
- var URL_ENV = "OPERA_URL";
8301
+ var import_contracts6 = __toESM(require_dist());
8302
+ import { resolve as resolve4 } from "path";
8303
+
8304
+ // src/renamed-variable.ts
8305
+ function renamedVariableRefusal(current, retired, env = process.env) {
8306
+ if (env[current]?.trim()) return null;
8307
+ if (!env[retired]?.trim()) return null;
8308
+ return `${retired} is set, but this CLI reads ${current} \u2014 the variable was renamed in 0.1.2. Rename the export in your shell or CI; ${retired} is no longer read.`;
8309
+ }
8310
+ __name(renamedVariableRefusal, "renamedVariableRefusal");
8311
+
8312
+ // src/publish.ts
8313
+ var TOKEN_ENV = import_contracts6.AUTHORING_CLI_ENV.token;
8314
+ var URL_ENV = import_contracts6.AUTHORING_CLI_ENV.url;
7180
8315
  async function runPublish(options) {
7181
- const directory = resolve3(options.directory ?? ".");
8316
+ const directory = resolve4(options.directory ?? ".");
7182
8317
  const baseUrl = (options.to ?? process.env[URL_ENV] ?? "").trim().replace(/\/+$/, "");
7183
- const token = (options.token ?? process.env[TOKEN_ENV] ?? "").trim();
8318
+ const token2 = (options.token ?? process.env[TOKEN_ENV] ?? "").trim();
8319
+ const stale = [
8320
+ options.to === void 0 ? renamedVariableRefusal(URL_ENV, import_contracts6.RETIRED_AUTHORING_CLI_ENV.url) : null,
8321
+ options.token === void 0 ? renamedVariableRefusal(TOKEN_ENV, import_contracts6.RETIRED_AUTHORING_CLI_ENV.token) : null
8322
+ ].filter((sentence) => sentence !== null);
8323
+ if (stale.length > 0) {
8324
+ for (const sentence of stale) printError(style.red(sentence));
8325
+ return 1;
8326
+ }
7184
8327
  if (!baseUrl) {
7185
- printError(style.red("Where to?") + ` Pass --to https://your-opera.example.com, or set ${URL_ENV}.`);
8328
+ printError(style.red("Where to?") + ` Pass --to https://your-runveric.example.com, or set ${URL_ENV}.`);
7186
8329
  return 1;
7187
8330
  }
7188
- if (!token) {
7189
- printError(style.red(`Publishing needs ${import_contracts3.BRAND_ARTICLE} ${import_contracts3.BRAND_SHORT_NAME} access token.`) + ` Set ${TOKEN_ENV}, or pass --token. (init, check and test need no account at all.)`);
8331
+ if (!token2) {
8332
+ printError(style.red(`Publishing needs ${import_contracts6.BRAND_ARTICLE} ${import_contracts6.BRAND_SHORT_NAME} access token.`) + ` Set ${TOKEN_ENV}, or pass --token. (init, check and test need no account at all.)`);
7190
8333
  return 1;
7191
8334
  }
7192
8335
  let files;
@@ -7206,7 +8349,7 @@ async function runPublish(options) {
7206
8349
  errors: report.errorCount,
7207
8350
  warnings: report.warningCount
7208
8351
  });
7209
- printError(style.red("\nNot published.") + (report.validation.ok ? ` ${import_contracts3.BRAND_SHORT_NAME} would accept this, but the problems above make it misbehave at run time. Fix them, or pass --force if you disagree.` : " Fix the errors above and publish again."));
8352
+ printError(style.red("\nNot published.") + (report.validation.ok ? ` ${import_contracts6.BRAND_SHORT_NAME} would accept this, but the problems above make it misbehave at run time. Fix them, or pass --force if you disagree.` : " Fix the errors above and publish again."));
7210
8353
  return 1;
7211
8354
  }
7212
8355
  if (report.advisoryCount > 0) {
@@ -7233,7 +8376,7 @@ async function runPublish(options) {
7233
8376
  method: "POST",
7234
8377
  headers: {
7235
8378
  "content-type": "application/json",
7236
- authorization: `Bearer ${token}`
8379
+ authorization: `Bearer ${token2}`
7237
8380
  },
7238
8381
  body
7239
8382
  });
@@ -7253,7 +8396,7 @@ Publish refused (${response.status}).`));
7253
8396
  try {
7254
8397
  version2 = JSON.parse(text);
7255
8398
  } catch {
7256
- printError(style.red(`${import_contracts3.BRAND_SHORT_NAME} accepted the package but returned a response we cannot read.`));
8399
+ printError(style.red(`${import_contracts6.BRAND_SHORT_NAME} accepted the package but returned a response we cannot read.`));
7257
8400
  return 1;
7258
8401
  }
7259
8402
  print();
@@ -7262,7 +8405,7 @@ Publish refused (${response.status}).`));
7262
8405
  print(` content digest ${version2.contentDigest}`);
7263
8406
  print(` tier ${version2.tier} ${style.grey(`(${version2.provenance})`)}`);
7264
8407
  print();
7265
- print(style.grey(`It is an immutable version now. Adopt it in ${import_contracts3.BRAND_SHORT_NAME} to put it in the catalog \u2014 publishing does not adopt, so nobody can run it until somebody chooses to.`));
8408
+ print(style.grey(`It is an immutable version now. Adopt it in ${import_contracts6.BRAND_SHORT_NAME} to put it in the catalog \u2014 publishing does not adopt, so nobody can run it until somebody chooses to.`));
7266
8409
  print();
7267
8410
  return 0;
7268
8411
  }
@@ -7274,29 +8417,102 @@ function formatProblem(text) {
7274
8417
  } catch {
7275
8418
  return ` ${text}`;
7276
8419
  }
7277
- const problem = (0, import_contracts3.parseProblemDocument)(parsed);
8420
+ const problem = (0, import_contracts6.parseProblemDocument)(parsed);
7278
8421
  if (!problem) return ` ${text}`;
7279
8422
  const lines = [];
7280
8423
  if (problem.detail) lines.push(` ${problem.detail}`);
7281
- for (const error of (0, import_contracts3.problemFieldErrors)(problem)) {
8424
+ for (const error of (0, import_contracts6.problemFieldErrors)(problem)) {
7282
8425
  lines.push(error.field ? ` ${style.bold(error.field)}: ${error.message}` : ` ${error.message}`);
7283
8426
  }
7284
8427
  return lines.length > 0 ? lines.join("\n") : ` ${text}`;
7285
8428
  }
7286
8429
  __name(formatProblem, "formatProblem");
7287
8430
 
8431
+ // src/runner-image.ts
8432
+ var import_contracts7 = __toESM(require_dist());
8433
+ import { execFile } from "child_process";
8434
+ import { promisify } from "util";
8435
+ var run = promisify(execFile);
8436
+ var LOCAL_RUNNER_IMAGE = "runveric-package-runner:local";
8437
+ var RUNNER_IMAGE_ENV = import_contracts7.AUTHORING_CLI_ENV.runnerImage;
8438
+ var RenamedVariableError = class extends Error {
8439
+ static {
8440
+ __name(this, "RenamedVariableError");
8441
+ }
8442
+ };
8443
+ function resolveRunnerImage(flag) {
8444
+ const explicit = flag?.trim();
8445
+ if (explicit) return {
8446
+ reference: explicit,
8447
+ origin: "flag"
8448
+ };
8449
+ const configured = process.env[RUNNER_IMAGE_ENV]?.trim();
8450
+ if (configured) return {
8451
+ reference: configured,
8452
+ origin: "environment"
8453
+ };
8454
+ const stale = renamedVariableRefusal(RUNNER_IMAGE_ENV, import_contracts7.RETIRED_AUTHORING_CLI_ENV.runnerImage);
8455
+ if (stale) throw new RenamedVariableError(stale);
8456
+ return {
8457
+ reference: LOCAL_RUNNER_IMAGE,
8458
+ origin: "local-build"
8459
+ };
8460
+ }
8461
+ __name(resolveRunnerImage, "resolveRunnerImage");
8462
+ function runnerImageNote(image) {
8463
+ switch (image.origin) {
8464
+ case "flag":
8465
+ return "from --image";
8466
+ case "environment":
8467
+ return `from ${RUNNER_IMAGE_ENV}`;
8468
+ case "local-build":
8469
+ return `locally built \u2014 NOT the image your ${import_contracts7.BRAND_SHORT_NAME} runs; set ${RUNNER_IMAGE_ENV} to match it`;
8470
+ }
8471
+ }
8472
+ __name(runnerImageNote, "runnerImageNote");
8473
+ var DockerMissingError = class extends Error {
8474
+ static {
8475
+ __name(this, "DockerMissingError");
8476
+ }
8477
+ };
8478
+ async function assertDockerAvailable() {
8479
+ try {
8480
+ await run("docker", [
8481
+ "version",
8482
+ "--format",
8483
+ "{{.Server.Version}}"
8484
+ ]);
8485
+ } catch {
8486
+ throw new DockerMissingError(`Docker is not available. \`runveric test\` runs your package in the real ${import_contracts7.BRAND_SHORT_NAME} runner image, which needs a container runtime \u2014 install Docker Desktop (or start it), then try again. \`runveric check\` needs nothing and still works.`);
8487
+ }
8488
+ }
8489
+ __name(assertDockerAvailable, "assertDockerAvailable");
8490
+ async function imageIsPresent(reference) {
8491
+ try {
8492
+ await run("docker", [
8493
+ "image",
8494
+ "inspect",
8495
+ reference
8496
+ ]);
8497
+ return true;
8498
+ } catch {
8499
+ return false;
8500
+ }
8501
+ }
8502
+ __name(imageIsPresent, "imageIsPresent");
8503
+
7288
8504
  // src/test.ts
7289
8505
  var import_action_manifest4 = __toESM(require_dist2());
7290
8506
  import { spawn } from "child_process";
7291
8507
  import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
7292
8508
  import { tmpdir } from "os";
7293
- import { dirname as dirname2, join as join3, resolve as resolve4 } from "path";
8509
+ import { dirname as dirname3, join as join3, resolve as resolve5 } from "path";
7294
8510
  import { StringDecoder } from "string_decoder";
7295
8511
 
7296
8512
  // src/azure-credential.ts
7297
- import { execFile } from "child_process";
7298
- import { promisify } from "util";
7299
- var run = promisify(execFile);
8513
+ import { execFile as execFile2 } from "child_process";
8514
+ import { promisify as promisify2 } from "util";
8515
+ var run2 = promisify2(execFile2);
7300
8516
  var AzureCredentialError = class extends Error {
7301
8517
  static {
7302
8518
  __name(this, "AzureCredentialError");
@@ -7313,7 +8529,7 @@ async function acquireAzureCredential() {
7313
8529
  if (!account.id || !account.tenantId) {
7314
8530
  throw new AzureCredentialError("`az account show` returned no subscription. Run `az login` and `az account set --subscription <id>` first.");
7315
8531
  }
7316
- const token = await azJson([
8532
+ const token2 = await azJson([
7317
8533
  "account",
7318
8534
  "get-access-token",
7319
8535
  "--resource",
@@ -7321,11 +8537,11 @@ async function acquireAzureCredential() {
7321
8537
  "--output",
7322
8538
  "json"
7323
8539
  ]);
7324
- if (!token.accessToken) {
8540
+ if (!token2.accessToken) {
7325
8541
  throw new AzureCredentialError("`az account get-access-token` returned no access token.");
7326
8542
  }
7327
8543
  return {
7328
- token: token.accessToken,
8544
+ token: token2.accessToken,
7329
8545
  // The identical names the credential adapter puts in a lease, so a package reads the same
7330
8546
  // environment here and in production.
7331
8547
  env: {
@@ -7343,7 +8559,7 @@ __name(acquireAzureCredential, "acquireAzureCredential");
7343
8559
  async function azJson(args) {
7344
8560
  let stdout;
7345
8561
  try {
7346
- ({ stdout } = await run("az", args, {
8562
+ ({ stdout } = await run2("az", args, {
7347
8563
  maxBuffer: 8 * 1024 * 1024
7348
8564
  }));
7349
8565
  } catch (error) {
@@ -7359,91 +8575,25 @@ ${message.trim()}`);
7359
8575
  }
7360
8576
  __name(azJson, "azJson");
7361
8577
 
7362
- // src/runner-image.ts
7363
- var import_contracts4 = __toESM(require_dist());
7364
- import { execFile as execFile2 } from "child_process";
7365
- import { promisify as promisify2 } from "util";
7366
- var run2 = promisify2(execFile2);
7367
- var LOCAL_RUNNER_IMAGE = "opera-package-runner:local";
7368
- var RUNNER_IMAGE_ENV = "OPERA_RUNNER_IMAGE";
7369
- function resolveRunnerImage(flag) {
7370
- const explicit = flag?.trim();
7371
- if (explicit) return {
7372
- reference: explicit,
7373
- origin: "flag"
7374
- };
7375
- const configured = process.env[RUNNER_IMAGE_ENV]?.trim();
7376
- if (configured) return {
7377
- reference: configured,
7378
- origin: "environment"
7379
- };
7380
- return {
7381
- reference: LOCAL_RUNNER_IMAGE,
7382
- origin: "local-build"
7383
- };
7384
- }
7385
- __name(resolveRunnerImage, "resolveRunnerImage");
7386
- function runnerImageNote(image) {
7387
- switch (image.origin) {
7388
- case "flag":
7389
- return "from --image";
7390
- case "environment":
7391
- return `from ${RUNNER_IMAGE_ENV}`;
7392
- case "local-build":
7393
- return `locally built \u2014 NOT the image your ${import_contracts4.BRAND_SHORT_NAME} runs; set OPERA_RUNNER_IMAGE to match it`;
7394
- }
7395
- }
7396
- __name(runnerImageNote, "runnerImageNote");
7397
- var DockerMissingError = class extends Error {
7398
- static {
7399
- __name(this, "DockerMissingError");
7400
- }
7401
- };
7402
- async function assertDockerAvailable() {
7403
- try {
7404
- await run2("docker", [
7405
- "version",
7406
- "--format",
7407
- "{{.Server.Version}}"
7408
- ]);
7409
- } catch {
7410
- throw new DockerMissingError(`Docker is not available. \`runveric test\` runs your package in the real ${import_contracts4.BRAND_SHORT_NAME} runner image, which needs a container runtime \u2014 install Docker Desktop (or start it), then try again. \`runveric check\` needs nothing and still works.`);
7411
- }
7412
- }
7413
- __name(assertDockerAvailable, "assertDockerAvailable");
7414
- async function imageIsPresent(reference) {
7415
- try {
7416
- await run2("docker", [
7417
- "image",
7418
- "inspect",
7419
- reference
7420
- ]);
7421
- return true;
7422
- } catch {
7423
- return false;
7424
- }
7425
- }
7426
- __name(imageIsPresent, "imageIsPresent");
7427
-
7428
8578
  // src/test.ts
7429
- var import_contracts5 = __toESM(require_dist());
8579
+ var import_contracts8 = __toESM(require_dist());
7430
8580
  var PHASES = [
7431
8581
  "validate",
7432
8582
  "run",
7433
8583
  "rollback"
7434
8584
  ];
7435
8585
  var CONTAINER = {
7436
- packageRoot: "/opera/package",
7437
- input: "/opera/input",
7438
- output: "/opera/output",
7439
- scratch: "/opera/scratch",
7440
- creds: "/opera/creds"
8586
+ packageRoot: "/runveric/package",
8587
+ input: "/runveric/input",
8588
+ output: "/runveric/output",
8589
+ scratch: "/runveric/scratch",
8590
+ creds: "/runveric/creds"
7441
8591
  };
7442
8592
  async function runTest(options) {
7443
- const directory = resolve4(options.directory ?? ".");
8593
+ const directory = resolve5(options.directory ?? ".");
7444
8594
  const phase = options.phase ?? "run";
7445
8595
  if (!PHASES.includes(phase)) {
7446
- printError(style.red(`"${options.phase}" is not a phase.`) + ` ${import_contracts5.BRAND_SHORT_NAME} runs ${PHASES.join(", ")} \u2014 one at a time, so you can read each one's result.`);
8596
+ printError(style.red(`"${options.phase}" is not a phase.`) + ` ${import_contracts8.BRAND_SHORT_NAME} runs ${PHASES.join(", ")} \u2014 one at a time, so you can read each one's result.`);
7447
8597
  return 1;
7448
8598
  }
7449
8599
  let files;
@@ -7476,7 +8626,13 @@ async function runTest(options) {
7476
8626
  printError(style.red(error instanceof Error ? error.message : String(error)));
7477
8627
  return 1;
7478
8628
  }
7479
- const image = resolveRunnerImage(options.image);
8629
+ let image;
8630
+ try {
8631
+ image = resolveRunnerImage(options.image);
8632
+ } catch (error) {
8633
+ printError(style.red(error instanceof Error ? error.message : String(error)));
8634
+ return 1;
8635
+ }
7480
8636
  try {
7481
8637
  await assertDockerAvailable();
7482
8638
  } catch (error) {
@@ -7509,13 +8665,13 @@ async function runTest(options) {
7509
8665
  print(` runner image ${image.reference} ${style.grey(`(${runnerImageNote(image)})`)}`);
7510
8666
  print(` interpreter ${interpreter}@${manifest.runtime.version}`);
7511
8667
  print(` entrypoint ${entrypoint}${import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[interpreter].entryFile}`);
7512
- print(` identity ${credential ? `${credential.account.name} ${style.grey(`\u2014 YOUR access, not ${import_contracts5.BRAND_SHORT_NAME}'s brokered least-privilege identity`)}` : style.grey("none (--no-credential)")}`);
8668
+ print(` identity ${credential ? `${credential.account.name} ${style.grey(`\u2014 YOUR access, not ${import_contracts8.BRAND_SHORT_NAME}'s brokered least-privilege identity`)}` : style.grey("none (--no-credential)")}`);
7513
8669
  if (manifest.requiresNetwork) {
7514
- print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${import_contracts5.BRAND_SHORT_NAME} enforces it`)}`);
8670
+ print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${import_contracts8.BRAND_SHORT_NAME} enforces it`)}`);
7515
8671
  } else {
7516
8672
  print(` network ${style.grey("none \u2014 this package declares requiresNetwork: false")}`);
7517
8673
  }
7518
- if (options.dryRun) print(` ${style.bold("dry run")} OPERA_DRY_RUN=true`);
8674
+ if (options.dryRun) print(` ${style.bold("dry run")} RUNVERIC_DRY_RUN=true`);
7519
8675
  if (!await imageIsPresent(image.reference)) {
7520
8676
  print(` ${style.grey("pulling the image (first run only)\u2026")}`);
7521
8677
  }
@@ -7613,13 +8769,13 @@ async function materialize(workspace, input) {
7613
8769
  })));
7614
8770
  for (const [path, contents] of input.files) {
7615
8771
  const target = join3(layout.packageRoot, path);
7616
- await mkdir2(dirname2(target), {
8772
+ await mkdir2(dirname3(target), {
7617
8773
  recursive: true
7618
8774
  });
7619
8775
  await writeFile2(target, contents);
7620
8776
  }
7621
8777
  const context = {
7622
- protocolVersion: import_contracts5.PACKAGE_EXECUTION_PROTOCOL_VERSION,
8778
+ protocolVersion: import_contracts8.PACKAGE_EXECUTION_PROTOCOL_VERSION,
7623
8779
  // A recognisable id rather than a random uuid: it appears in the package's own log lines,
7624
8780
  // and "local-test" tells the reader instantly that this was not a real run.
7625
8781
  executionId: "local-test",
@@ -7645,27 +8801,27 @@ async function runContainer(options) {
7645
8801
  const launch = import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[options.interpreter];
7646
8802
  const script = `${CONTAINER.packageRoot}/${options.entrypoint}${launch.entryFile}`;
7647
8803
  const environment = {
7648
- OPERA_PROTOCOL_VERSION: import_contracts5.PACKAGE_RESULT_PROTOCOL_VERSION,
7649
- OPERA_PHASE: options.phase,
7650
- OPERA_CONTEXT_FILE: `${CONTAINER.input}/context.json`,
7651
- OPERA_PARAMETERS_FILE: `${CONTAINER.input}/parameters.json`,
7652
- OPERA_RESULT_FILE: `${CONTAINER.output}/result.json`,
7653
- OPERA_WORK_DIR: CONTAINER.scratch,
7654
- OPERA_CANCEL_FILE: `${CONTAINER.input}/cancel.requested`,
7655
- OPERA_DRY_RUN: options.dryRun ? "true" : "false",
8804
+ RUNVERIC_PROTOCOL_VERSION: import_contracts8.PACKAGE_RESULT_PROTOCOL_VERSION,
8805
+ RUNVERIC_PHASE: options.phase,
8806
+ RUNVERIC_CONTEXT_FILE: `${CONTAINER.input}/context.json`,
8807
+ RUNVERIC_PARAMETERS_FILE: `${CONTAINER.input}/parameters.json`,
8808
+ RUNVERIC_RESULT_FILE: `${CONTAINER.output}/result.json`,
8809
+ RUNVERIC_WORK_DIR: CONTAINER.scratch,
8810
+ RUNVERIC_CANCEL_FILE: `${CONTAINER.input}/cancel.requested`,
8811
+ RUNVERIC_DRY_RUN: options.dryRun ? "true" : "false",
7656
8812
  // HONEST, and matching the ACA runner's own reasoning: this flag means "there is no route
7657
8813
  // out at all". A container with `--network none` genuinely has none; one with a network
7658
8814
  // does not, whether or not an allowlist would have applied.
7659
- OPERA_NETWORK_ISOLATED: options.requiresNetwork ? "false" : "true",
8815
+ RUNVERIC_NETWORK_ISOLATED: options.requiresNetwork ? "false" : "true",
7660
8816
  LANG: "C.UTF-8",
7661
8817
  LC_ALL: "C.UTF-8",
7662
8818
  POWERSHELL_TELEMETRY_OPTOUT: "1",
7663
8819
  DOTNET_CLI_TELEMETRY_OPTOUT: "1",
7664
8820
  ...options.layout.hasPriorResult ? {
7665
- OPERA_PRIOR_RESULT_FILE: `${CONTAINER.input}/prior-result.json`
8821
+ RUNVERIC_PRIOR_RESULT_FILE: `${CONTAINER.input}/prior-result.json`
7666
8822
  } : {},
7667
8823
  ...options.layout.hasCredential ? {
7668
- OPERA_AZURE_TOKEN_FILE: `${CONTAINER.creds}/azure-token`
8824
+ RUNVERIC_AZURE_TOKEN_FILE: `${CONTAINER.creds}/azure-token`
7669
8825
  } : {},
7670
8826
  ...options.credentialEnv ?? {}
7671
8827
  };
@@ -7748,7 +8904,7 @@ async function runContainer(options) {
7748
8904
  __name(runContainer, "runContainer");
7749
8905
  function emitLine(line, stderr, onEvent) {
7750
8906
  if (line.length === 0) return;
7751
- const prefix = "OPERA_EVENT ";
8907
+ const prefix = "RUNVERIC_EVENT ";
7752
8908
  if (!stderr && line.startsWith(prefix)) {
7753
8909
  try {
7754
8910
  const parsed = JSON.parse(line.slice(prefix.length));
@@ -7760,7 +8916,7 @@ function emitLine(line, stderr, onEvent) {
7760
8916
  }
7761
8917
  onEvent({
7762
8918
  level: "warn",
7763
- message: "Package emitted malformed OPERA_EVENT JSON."
8919
+ message: "Package emitted malformed RUNVERIC_EVENT JSON."
7764
8920
  });
7765
8921
  return;
7766
8922
  }
@@ -7772,13 +8928,13 @@ function emitLine(line, stderr, onEvent) {
7772
8928
  __name(emitLine, "emitLine");
7773
8929
  async function reportOutcome(options) {
7774
8930
  if (options.exit.timedOut) {
7775
- printError(style.red("\nThe package did not finish within 15 minutes and was killed.") + ` ${import_contracts5.BRAND_SHORT_NAME} enforces the \`executionTimeout\` in your manifest; this is the local ceiling.`);
8931
+ printError(style.red("\nThe package did not finish within 15 minutes and was killed.") + ` ${import_contracts8.BRAND_SHORT_NAME} enforces the \`executionTimeout\` in your manifest; this is the local ceiling.`);
7776
8932
  return 1;
7777
8933
  }
7778
8934
  const exists = await stat2(options.resultPath).catch(() => null);
7779
8935
  if (!exists?.isFile() || exists.size === 0) {
7780
8936
  printError(style.red("\nThe package wrote no result.") + `
7781
- result.json is the only channel ${import_contracts5.BRAND_SHORT_NAME} reads. Exiting ${options.exit.code ?? "with a signal"} without writing $OPERA_RESULT_FILE is a FAILED run, whatever the script printed.
8937
+ result.json is the only channel ${import_contracts8.BRAND_SHORT_NAME} reads. Exiting ${options.exit.code ?? "with a signal"} without writing $RUNVERIC_RESULT_FILE is a FAILED run, whatever the script printed.
7782
8938
  Write it as the last thing your script does, on every path \u2014 including the ones that fail.`);
7783
8939
  return 1;
7784
8940
  }
@@ -7786,7 +8942,7 @@ async function reportOutcome(options) {
7786
8942
  try {
7787
8943
  parsed = JSON.parse(await readFile2(options.resultPath, "utf8"));
7788
8944
  } catch {
7789
- printError(style.red("\nresult.json is not valid JSON.") + ` ${import_contracts5.BRAND_SHORT_NAME} refuses the run rather than guessing what the package meant.`);
8945
+ printError(style.red("\nresult.json is not valid JSON.") + ` ${import_contracts8.BRAND_SHORT_NAME} refuses the run rather than guessing what the package meant.`);
7790
8946
  return 1;
7791
8947
  }
7792
8948
  let result;
@@ -7794,14 +8950,14 @@ async function reportOutcome(options) {
7794
8950
  result = (0, import_action_manifest4.validatePackageResult)(parsed, options.phase);
7795
8951
  } catch (error) {
7796
8952
  printError(style.red(`
7797
- ${import_contracts5.BRAND_SHORT_NAME} would REFUSE this result.`) + `
8953
+ ${import_contracts8.BRAND_SHORT_NAME} would REFUSE this result.`) + `
7798
8954
  ${error instanceof Error ? error.message : String(error)}
7799
8955
  This is the same check the control plane runs, so fixing it here fixes it there.`);
7800
8956
  return 1;
7801
8957
  }
7802
8958
  printResult(result);
7803
8959
  if (options.exit.code !== 0) {
7804
- print(style.yellow(`The package exited ${options.exit.code} but wrote a valid result. ${import_contracts5.BRAND_SHORT_NAME} reads the result, not the exit code \u2014 but a non-zero exit usually means something failed after the result was written.`));
8960
+ print(style.yellow(`The package exited ${options.exit.code} but wrote a valid result. ${import_contracts8.BRAND_SHORT_NAME} reads the result, not the exit code \u2014 but a non-zero exit usually means something failed after the result was written.`));
7805
8961
  print();
7806
8962
  }
7807
8963
  return result.status === "succeeded" ? 0 : 1;
@@ -7810,7 +8966,7 @@ __name(reportOutcome, "reportOutcome");
7810
8966
  async function readJsonFile(path) {
7811
8967
  let source;
7812
8968
  try {
7813
- source = await readFile2(resolve4(path), "utf8");
8969
+ source = await readFile2(resolve5(path), "utf8");
7814
8970
  } catch {
7815
8971
  throw new Error(`Could not read ${path}.`);
7816
8972
  }
@@ -7828,9 +8984,9 @@ async function readJsonFile(path) {
7828
8984
  __name(readJsonFile, "readJsonFile");
7829
8985
 
7830
8986
  // src/main.ts
7831
- var import_contracts6 = __toESM(require_dist());
8987
+ var import_contracts9 = __toESM(require_dist());
7832
8988
  var USAGE = `
7833
- ${style.bold("runveric")} \u2014 author ${import_contracts6.BRAND_ARTICLE} ${import_contracts6.BRAND_SHORT_NAME} action, without pushing anything.
8989
+ ${style.bold("runveric")} \u2014 author ${import_contracts9.BRAND_ARTICLE} ${import_contracts9.BRAND_SHORT_NAME} action, without pushing anything.
7834
8990
 
7835
8991
  ${style.bold("USAGE")}
7836
8992
  runveric <command> [options]
@@ -7838,8 +8994,9 @@ ${style.bold("USAGE")}
7838
8994
  ${style.bold("COMMANDS")}
7839
8995
  init [directory] Scaffold a working action. It passes check and test immediately.
7840
8996
  check Validate the package. Offline \u2014 no account, no cloud, no network.
7841
- test Run one phase in the real ${import_contracts6.BRAND_SHORT_NAME} runner image.
7842
- publish Upload it to ${import_contracts6.BRAND_SHORT_NAME} as an authored version. Needs an account.
8997
+ test Run one phase in the real ${import_contracts9.BRAND_SHORT_NAME} runner image.
8998
+ publish Upload it to ${import_contracts9.BRAND_SHORT_NAME} as an authored version. Needs an account.
8999
+ catalog <command> Publish first-party catalog artifacts. Not for authoring: see \`runveric catalog --help\`.
7843
9000
 
7844
9001
  ${style.bold("init")}
7845
9002
  --id <publisher/name> The action id. Required.
@@ -7853,18 +9010,18 @@ ${style.bold("check")}
7853
9010
 
7854
9011
  ${style.bold("test")}
7855
9012
  --phase <name> validate | run | rollback. Defaults to run.
7856
- --dry-run Sets OPERA_DRY_RUN=true, as an operator's rehearsal does.
9013
+ --dry-run Sets RUNVERIC_DRY_RUN=true, as an operator's rehearsal does.
7857
9014
  --param <name=value> A parameter value. Repeatable. Defaults come from your schema.
7858
9015
  --params-file <path> A JSON object of parameters. --param overrides it.
7859
9016
  --prior-result <path> The run's result.json. Required shape for --phase rollback.
7860
- --image <ref> The runner image. Defaults to $OPERA_RUNNER_IMAGE.
9017
+ --image <ref> The runner image. Defaults to $${RUNNER_IMAGE_ENV}.
7861
9018
  --no-credential Skip \`az account get-access-token\`.
7862
- --keep Keep the materialized /opera tree for inspection.
9019
+ --keep Keep the materialized /runveric tree for inspection.
7863
9020
 
7864
9021
  ${style.bold("publish")}
7865
- --to <url> Your ${import_contracts6.BRAND_SHORT_NAME} URL. Defaults to $OPERA_URL.
7866
- --token <token> Access token. Defaults to $OPERA_TOKEN.
7867
- --force Publish despite lint errors. Never bypasses ${import_contracts6.BRAND_SHORT_NAME}'s validation.
9022
+ --to <url> Your ${import_contracts9.BRAND_SHORT_NAME} URL. Defaults to $${URL_ENV}.
9023
+ --token <token> Access token. Defaults to $${TOKEN_ENV}.
9024
+ --force Publish despite lint errors. Never bypasses ${import_contracts9.BRAND_SHORT_NAME}'s validation.
7868
9025
 
7869
9026
  ${style.bold("EVERYWHERE")}
7870
9027
  --dir <path> The package directory. Defaults to the current directory.
@@ -7907,7 +9064,9 @@ __name(parseArgv, "parseArgv");
7907
9064
  var first = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0], "first");
7908
9065
  var bool = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0] === "true", "bool");
7909
9066
  async function main() {
7910
- const parsed = parseArgv(process.argv.slice(2));
9067
+ const argv = process.argv.slice(2);
9068
+ if (argv[0] === "catalog") return runCatalog(argv.slice(1));
9069
+ const parsed = parseArgv(argv);
7911
9070
  if (parsed.flags.has("version")) {
7912
9071
  print(version);
7913
9072
  return 0;
@@ -7975,7 +9134,7 @@ async function main() {
7975
9134
  force: bool(parsed, "force")
7976
9135
  });
7977
9136
  default:
7978
- printError(style.red(`"${parsed.command}" is not a command.`) + " There are four: init, check, test, publish.");
9137
+ printError(style.red(`"${parsed.command}" is not a command.`) + " There are four: init, check, test, publish \u2014 and catalog, which publishes first-party catalog artifacts.");
7979
9138
  return 1;
7980
9139
  }
7981
9140
  }