@runveric/cli 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/runveric.mjs CHANGED
@@ -212,13 +212,19 @@ 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_V2 = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V1 = exports.CATALOG_RELEASE_SCHEMA_V1 = exports.EVIDENCE_PACK_SCHEMA_VERSION = exports.ACTION_PROTOCOL_V1 = 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
217
  exports.PACKAGE_RESULT_PROTOCOL_VERSION = "runveric.dev/package-result/v1";
218
218
  exports.PACKAGE_EXECUTION_PROTOCOL_VERSION = "runveric.dev/package-execution/v1";
219
219
  exports.ACTION_MANIFEST_API_VERSION = "runveric.dev/action/v1";
220
220
  exports.PACKAGE_WORK_ITEM_VERSION = "runveric.dev/package-work-item/v1";
221
221
  exports.RUNNER_PROTOCOL_VERSION = "runveric.dev/runner-channel/v2";
222
+ exports.ACTION_PROTOCOL_V1 = "runveric.dev/action-protocol/v1";
223
+ exports.EVIDENCE_PACK_SCHEMA_VERSION = "runveric.dev/evidence-pack/v1";
224
+ exports.CATALOG_RELEASE_SCHEMA_V1 = "runveric.dev/catalog-release/v1";
225
+ exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V1 = "runveric.dev/catalog-action/v1";
226
+ exports.CATALOG_ACTION_ARTIFACT_SCHEMA_V2 = "runveric.dev/catalog-action/v2";
227
+ exports.CATALOG_ARTIFACT_MEDIA_TYPE = "application/vnd.runveric.catalog-artifact+json";
222
228
  exports.PROBLEM_TYPE_BASE = "https://runveric.dev/errors";
223
229
  function problemType(slug) {
224
230
  return `${exports.PROBLEM_TYPE_BASE}/${slug}`;
@@ -301,6 +307,13 @@ var require_permissions = __commonJS({
301
307
  Object.defineProperty(exports, "__esModule", {
302
308
  value: true
303
309
  });
310
+ exports.PLATFORM_PERMISSIONS = void 0;
311
+ exports.PLATFORM_PERMISSIONS = [
312
+ // Which catalog release is live for every workspace (docs/121). Pinning one changes what EVERY
313
+ // workspace can run, so it is neither `policy:manage` (one workspace's own governance) nor
314
+ // `catalog:read` (the right to browse).
315
+ "catalog:release"
316
+ ];
304
317
  }
305
318
  });
306
319
 
@@ -443,6 +456,41 @@ var require_groups = __commonJS({
443
456
  }
444
457
  });
445
458
 
459
+ // ../contracts/dist/catalog-policy.js
460
+ var require_catalog_policy = __commonJS({
461
+ "../contracts/dist/catalog-policy.js"(exports) {
462
+ "use strict";
463
+ Object.defineProperty(exports, "__esModule", {
464
+ value: true
465
+ });
466
+ exports.CATALOG_RULE_EFFECTS = exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = exports.CATALOG_DEFAULT_MODES = void 0;
467
+ exports.effectiveCatalogAccess = effectiveCatalogAccess;
468
+ var CATALOG_DEFAULT_MODE_SET = {
469
+ allow_all: true,
470
+ deny_all: true
471
+ };
472
+ exports.CATALOG_DEFAULT_MODES = Object.keys(CATALOG_DEFAULT_MODE_SET);
473
+ exports.CATALOG_DEFAULT_MODE_WHEN_UNSET = "allow_all";
474
+ var CATALOG_RULE_EFFECT_SET = {
475
+ allow: true,
476
+ deny: true
477
+ };
478
+ exports.CATALOG_RULE_EFFECTS = Object.keys(CATALOG_RULE_EFFECT_SET);
479
+ var RULE_ACCESS = {
480
+ allow: "allowed",
481
+ deny: "blocked"
482
+ };
483
+ var UNRULED_ACCESS = {
484
+ allow_all: "allowed",
485
+ deny_all: "blocked"
486
+ };
487
+ function effectiveCatalogAccess(defaultMode, rule) {
488
+ return rule !== null ? RULE_ACCESS[rule] : UNRULED_ACCESS[defaultMode];
489
+ }
490
+ __name(effectiveCatalogAccess, "effectiveCatalogAccess");
491
+ }
492
+ });
493
+
446
494
  // ../contracts/dist/catalog.js
447
495
  var require_catalog = __commonJS({
448
496
  "../contracts/dist/catalog.js"(exports) {
@@ -558,6 +606,456 @@ var require_catalog = __commonJS({
558
606
  }
559
607
  });
560
608
 
609
+ // ../contracts/dist/catalog-release.js
610
+ var require_catalog_release = __commonJS({
611
+ "../contracts/dist/catalog-release.js"(exports) {
612
+ "use strict";
613
+ Object.defineProperty(exports, "__esModule", {
614
+ value: true
615
+ });
616
+ exports.CatalogReleaseStateError = exports.CATALOG_SYNC_STALE_AFTER_MINUTES = exports.CATALOG_SYNC_INTERVAL_MINUTES = exports.CATALOG_SYNC_SOURCES = exports.CATALOG_SYNC_RUN_OUTCOMES = exports.ARTIFACT_DIGEST_PATTERN = exports.ARTIFACT_REPOSITORY_PATTERN = exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = exports.CATALOG_RELEASE_SCHEMA_VERSIONS = void 0;
617
+ exports.parseCatalogReleaseState = parseCatalogReleaseState;
618
+ var wire_identity_1 = require_wire_identity();
619
+ exports.CATALOG_RELEASE_SCHEMA_VERSIONS = [
620
+ wire_identity_1.CATALOG_RELEASE_SCHEMA_V1
621
+ ];
622
+ exports.CATALOG_ACTION_ARTIFACT_SCHEMA_VERSIONS = [
623
+ wire_identity_1.CATALOG_ACTION_ARTIFACT_SCHEMA_V1,
624
+ wire_identity_1.CATALOG_ACTION_ARTIFACT_SCHEMA_V2
625
+ ];
626
+ exports.ARTIFACT_REPOSITORY_PATTERN = /^[a-z0-9][a-z0-9.-]*(:[0-9]+)?(\/[a-z0-9][a-z0-9._-]*)+$/;
627
+ exports.ARTIFACT_DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/;
628
+ exports.CATALOG_SYNC_RUN_OUTCOMES = [
629
+ "applied",
630
+ "refused",
631
+ "superseded"
632
+ ];
633
+ exports.CATALOG_SYNC_SOURCES = [
634
+ "directory",
635
+ "release"
636
+ ];
637
+ exports.CATALOG_SYNC_INTERVAL_MINUTES = 15;
638
+ exports.CATALOG_SYNC_STALE_AFTER_MINUTES = exports.CATALOG_SYNC_INTERVAL_MINUTES * 2;
639
+ var CatalogReleaseStateError = class CatalogReleaseStateError extends Error {
640
+ static {
641
+ __name(this, "CatalogReleaseStateError");
642
+ }
643
+ constructor(message) {
644
+ super(message);
645
+ this.name = "CatalogReleaseStateError";
646
+ }
647
+ };
648
+ exports.CatalogReleaseStateError = CatalogReleaseStateError;
649
+ function stateObject(path, value) {
650
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
651
+ throw new CatalogReleaseStateError(`${path} is not an object.`);
652
+ }
653
+ return value;
654
+ }
655
+ __name(stateObject, "stateObject");
656
+ function stateString(path, value, pattern, expected = "a non-empty string") {
657
+ if (typeof value !== "string" || value === "" || pattern !== void 0 && !pattern.test(value)) {
658
+ throw new CatalogReleaseStateError(`${path} is ${JSON.stringify(value)}, not ${expected}.`);
659
+ }
660
+ return value;
661
+ }
662
+ __name(stateString, "stateString");
663
+ function stateInstant(path, value) {
664
+ const text = stateString(path, value);
665
+ if (Number.isNaN(Date.parse(text))) {
666
+ throw new CatalogReleaseStateError(`${path} is "${text}", which is not a timestamp.`);
667
+ }
668
+ return text;
669
+ }
670
+ __name(stateInstant, "stateInstant");
671
+ var orNull = /* @__PURE__ */ __name((value, read) => value === null ? null : read(value), "orNull");
672
+ function stateMember(path, value, members) {
673
+ if (typeof value === "string" && members.includes(value)) return value;
674
+ throw new CatalogReleaseStateError(`${path} is ${JSON.stringify(value)}; this build reads ${members.join(", ")}.`);
675
+ }
676
+ __name(stateMember, "stateMember");
677
+ function parseSyncRun(value) {
678
+ const run3 = stateObject("lastSync", value);
679
+ const source = stateMember("lastSync.source", run3.source, exports.CATALOG_SYNC_SOURCES);
680
+ const release = orNull(run3.release, (raw) => {
681
+ const fields = stateObject("lastSync.release", raw);
682
+ return {
683
+ artifactRef: stateString("lastSync.release.artifactRef", fields.artifactRef, exports.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
684
+ digest: stateString("lastSync.release.digest", fields.digest, exports.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
685
+ version: orNull(fields.version, (label) => stateString("lastSync.release.version", label))
686
+ };
687
+ });
688
+ if (source === "release" !== (release !== null)) {
689
+ throw new CatalogReleaseStateError(`lastSync.source is "${source}" and lastSync.release is ${release === null ? "null" : "set"}; a run names a release exactly when a release declared it.`);
690
+ }
691
+ return {
692
+ startedAt: stateInstant("lastSync.startedAt", run3.startedAt),
693
+ finishedAt: stateInstant("lastSync.finishedAt", run3.finishedAt),
694
+ imageTag: orNull(run3.imageTag, (tag) => stateString("lastSync.imageTag", tag)),
695
+ source,
696
+ release,
697
+ outcome: stateMember("lastSync.outcome", run3.outcome, exports.CATALOG_SYNC_RUN_OUTCOMES),
698
+ detail: stateString("lastSync.detail", run3.detail)
699
+ };
700
+ }
701
+ __name(parseSyncRun, "parseSyncRun");
702
+ function parsePinned(value) {
703
+ const pin = stateObject("release", value);
704
+ return {
705
+ artifactRef: stateString("release.artifactRef", pin.artifactRef, exports.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
706
+ digest: stateString("release.digest", pin.digest, exports.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
707
+ version: stateString("release.version", pin.version),
708
+ provenance: orNull(pin.provenance, (raw) => {
709
+ const fields = stateObject("release.provenance", raw);
710
+ return {
711
+ repository: stateString("release.provenance.repository", fields.repository),
712
+ commit: stateString("release.provenance.commit", fields.commit)
713
+ };
714
+ }),
715
+ pinnedBy: stateString("release.pinnedBy", pin.pinnedBy),
716
+ pinnedByName: orNull(pin.pinnedByName, (name) => stateString("release.pinnedByName", name)),
717
+ pinnedAt: stateInstant("release.pinnedAt", pin.pinnedAt)
718
+ };
719
+ }
720
+ __name(parsePinned, "parsePinned");
721
+ function parseCatalogReleaseState(value) {
722
+ const state = stateObject("the catalog release state", value);
723
+ for (const field of [
724
+ "release",
725
+ "imageTag",
726
+ "lastSync",
727
+ "liveSince"
728
+ ]) {
729
+ if (!(field in state)) throw new CatalogReleaseStateError(`${field} is missing.`);
730
+ }
731
+ const release = orNull(state.release, parsePinned);
732
+ const liveSince = orNull(state.liveSince, (instant) => stateInstant("liveSince", instant));
733
+ if (release === null && liveSince !== null) {
734
+ throw new CatalogReleaseStateError("liveSince is set with nothing pinned; only a pin goes live.");
735
+ }
736
+ return {
737
+ release,
738
+ imageTag: orNull(state.imageTag, (tag) => stateString("imageTag", tag)),
739
+ lastSync: orNull(state.lastSync, parseSyncRun),
740
+ liveSince
741
+ };
742
+ }
743
+ __name(parseCatalogReleaseState, "parseCatalogReleaseState");
744
+ }
745
+ });
746
+
747
+ // ../contracts/dist/action-repository.js
748
+ var require_action_repository = __commonJS({
749
+ "../contracts/dist/action-repository.js"(exports) {
750
+ "use strict";
751
+ Object.defineProperty(exports, "__esModule", {
752
+ value: true
753
+ });
754
+ exports.ACTION_SOURCE_REPOSITORY_PREFIX = exports.ACTION_REPOSITORY_PREFIX = void 0;
755
+ exports.actionKeyRefusal = actionKeyRefusal;
756
+ exports.isActionKey = isActionKey;
757
+ exports.actionRepositoryName = actionRepositoryName;
758
+ exports.actionSourceRepository = actionSourceRepository2;
759
+ exports.actionKeyOf = actionKeyOf;
760
+ exports.actionPackageRefusal = actionPackageRefusal4;
761
+ exports.ACTION_REPOSITORY_PREFIX = "action-";
762
+ var WORD = /^[a-z][a-z0-9]*$/;
763
+ var VERB = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*$/;
764
+ function actionKeyRefusal(key) {
765
+ 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.";
766
+ const segments = key.split(".");
767
+ if (segments.length !== 3) {
768
+ return `"${key}" is not an action key: it has ${segments.length} dot-separated segment(s), not 3; ${shape}`;
769
+ }
770
+ const [provider, resource, verb] = segments;
771
+ const not = /* @__PURE__ */ __name((what) => `"${key}" is not an action key: ${what}; ${shape}`, "not");
772
+ if (!WORD.test(provider)) return not(`its provider "${provider}" is not one lowercase word`);
773
+ if (!WORD.test(resource)) return not(`its resource "${resource}" is not one lowercase word`);
774
+ if (!VERB.test(verb)) return not(`its verb "${verb}" is not camelCase without two capitals in a row`);
775
+ return null;
776
+ }
777
+ __name(actionKeyRefusal, "actionKeyRefusal");
778
+ function isActionKey(key) {
779
+ return actionKeyRefusal(key) === null;
780
+ }
781
+ __name(isActionKey, "isActionKey");
782
+ var kebab = /* @__PURE__ */ __name((verb) => verb.replace(/[A-Z]/g, (capital) => `-${capital.toLowerCase()}`), "kebab");
783
+ function actionRepositoryName(key) {
784
+ const refusal = actionKeyRefusal(key);
785
+ if (refusal !== null) throw new Error(refusal);
786
+ const [provider, resource, verb] = key.split(".");
787
+ return `${exports.ACTION_REPOSITORY_PREFIX}${provider}-${resource}-${kebab(verb)}`;
788
+ }
789
+ __name(actionRepositoryName, "actionRepositoryName");
790
+ exports.ACTION_SOURCE_REPOSITORY_PREFIX = "https://github.com/runveric/";
791
+ function actionSourceRepository2(key) {
792
+ return `${exports.ACTION_SOURCE_REPOSITORY_PREFIX}${actionRepositoryName(key)}`;
793
+ }
794
+ __name(actionSourceRepository2, "actionSourceRepository");
795
+ function actionKeyOf(name) {
796
+ if (!name.startsWith(exports.ACTION_REPOSITORY_PREFIX)) return null;
797
+ const [provider, resource, first2, ...rest] = name.slice(exports.ACTION_REPOSITORY_PREFIX.length).split("-");
798
+ if (provider === void 0 || resource === void 0 || first2 === void 0) return null;
799
+ const verb = first2 + rest.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
800
+ const key = `${provider}.${resource}.${verb}`;
801
+ return isActionKey(key) && actionRepositoryName(key) === name ? key : null;
802
+ }
803
+ __name(actionKeyOf, "actionKeyOf");
804
+ function actionPackageRefusal4(key, artifactRef) {
805
+ const keyRefusal = actionKeyRefusal(key);
806
+ if (keyRefusal !== null) return `${keyRefusal} No package can carry it.`;
807
+ const cut = artifactRef.lastIndexOf("/");
808
+ const named = artifactRef.slice(cut + 1);
809
+ const expected = actionRepositoryName(key);
810
+ if (named === expected) return null;
811
+ 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.`;
812
+ }
813
+ __name(actionPackageRefusal4, "actionPackageRefusal");
814
+ }
815
+ });
816
+
817
+ // ../contracts/dist/catalog-release-spec.js
818
+ var require_catalog_release_spec = __commonJS({
819
+ "../contracts/dist/catalog-release-spec.js"(exports) {
820
+ "use strict";
821
+ Object.defineProperty(exports, "__esModule", {
822
+ value: true
823
+ });
824
+ exports.CatalogReleaseSpecError = void 0;
825
+ exports.splitArtifactReference = splitArtifactReference;
826
+ exports.parseCatalogReleaseSpec = parseCatalogReleaseSpec3;
827
+ var CatalogReleaseSpecError = class CatalogReleaseSpecError extends Error {
828
+ static {
829
+ __name(this, "CatalogReleaseSpecError");
830
+ }
831
+ constructor(message) {
832
+ super(message);
833
+ this.name = "CatalogReleaseSpecError";
834
+ }
835
+ };
836
+ exports.CatalogReleaseSpecError = CatalogReleaseSpecError;
837
+ function splitArtifactReference(reference) {
838
+ const at = reference.indexOf("@");
839
+ if (at <= 0 || at === reference.length - 1) return null;
840
+ return {
841
+ artifactRef: reference.slice(0, at),
842
+ digest: reference.slice(at + 1)
843
+ };
844
+ }
845
+ __name(splitArtifactReference, "splitArtifactReference");
846
+ var isMapping3 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
847
+ function parseCatalogReleaseSpec3(value) {
848
+ if (!isMapping3(value)) {
849
+ throw new CatalogReleaseSpecError("release.yaml is not a mapping of `version` and `actions`.");
850
+ }
851
+ const unknown = Object.keys(value).filter((key) => key !== "version" && key !== "actions");
852
+ if (unknown.length > 0) {
853
+ throw new CatalogReleaseSpecError(`release.yaml carries ${unknown.map((key) => `"${key}"`).join(", ")}; it has exactly two fields, \`version\` and \`actions\`.`);
854
+ }
855
+ const { version: version2, actions } = value;
856
+ if (typeof version2 !== "string" || version2.trim().length === 0) {
857
+ 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.`);
858
+ }
859
+ if (actions === null || actions === void 0 || isMapping3(actions) && Object.keys(actions).length === 0) {
860
+ throw new CatalogReleaseSpecError("release.yaml declares no action. An empty release is refused: pinned, it would declare a catalog with nothing in it.");
861
+ }
862
+ if (!isMapping3(actions)) {
863
+ throw new CatalogReleaseSpecError("release.yaml's actions is not a mapping of action key to <repository>@<digest>.");
864
+ }
865
+ return {
866
+ version: version2,
867
+ actions: Object.entries(actions).map(([key, reference]) => {
868
+ const split = typeof reference === "string" ? splitArtifactReference(reference) : null;
869
+ if (split === null) {
870
+ throw new CatalogReleaseSpecError(`release.yaml's ${key} is ${JSON.stringify(reference)}, not <repository>@<digest>.`);
871
+ }
872
+ return {
873
+ key,
874
+ ...split
875
+ };
876
+ })
877
+ };
878
+ }
879
+ __name(parseCatalogReleaseSpec3, "parseCatalogReleaseSpec");
880
+ }
881
+ });
882
+
883
+ // ../contracts/dist/catalog-lock.js
884
+ var require_catalog_lock = __commonJS({
885
+ "../contracts/dist/catalog-lock.js"(exports) {
886
+ "use strict";
887
+ Object.defineProperty(exports, "__esModule", {
888
+ value: true
889
+ });
890
+ exports.MANIFEST_SHA256_PATTERN = exports.CatalogLockError = exports.CATALOG_LOCK_ENGINE_EXECUTION = exports.CATALOG_LOCK_FILE = exports.CATALOG_LOCK_SCHEMA_V1 = void 0;
891
+ exports.parseCatalogLock = parseCatalogLock;
892
+ exports.catalogLockText = catalogLockText;
893
+ exports.authoredElsewhere = authoredElsewhere;
894
+ var action_repository_1 = require_action_repository();
895
+ var catalog_release_1 = require_catalog_release();
896
+ exports.CATALOG_LOCK_SCHEMA_V1 = "runveric.catalog-lock/v1";
897
+ exports.CATALOG_LOCK_FILE = "release.lock.json";
898
+ exports.CATALOG_LOCK_ENGINE_EXECUTION = "engine";
899
+ var CatalogLockError = class CatalogLockError extends Error {
900
+ static {
901
+ __name(this, "CatalogLockError");
902
+ }
903
+ constructor(message) {
904
+ super(message);
905
+ this.name = "CatalogLockError";
906
+ }
907
+ };
908
+ exports.CatalogLockError = CatalogLockError;
909
+ exports.MANIFEST_SHA256_PATTERN = /^[0-9a-f]{64}$/;
910
+ function objectAt(path, value, keys) {
911
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
912
+ throw new CatalogLockError(`${path} is not an object.`);
913
+ }
914
+ const unknown = Object.keys(value).filter((key) => !keys.includes(key));
915
+ if (unknown.length > 0) {
916
+ 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.`);
917
+ }
918
+ return value;
919
+ }
920
+ __name(objectAt, "objectAt");
921
+ function stringAt(path, value, pattern, expected) {
922
+ if (typeof value !== "string" || value.length === 0) {
923
+ throw new CatalogLockError(`${path} is not a non-empty string.`);
924
+ }
925
+ if (pattern !== void 0 && !pattern.test(value)) {
926
+ throw new CatalogLockError(`${path} is "${value}", which is not ${expected}.`);
927
+ }
928
+ return value;
929
+ }
930
+ __name(stringAt, "stringAt");
931
+ function parseCatalogLock(value) {
932
+ const root = objectAt("the lock", value, [
933
+ "schemaVersion",
934
+ "release",
935
+ "actions"
936
+ ]);
937
+ if (root.schemaVersion !== exports.CATALOG_LOCK_SCHEMA_V1) {
938
+ throw new CatalogLockError(`the lock declares schemaVersion ${JSON.stringify(root.schemaVersion)}; this build reads ${exports.CATALOG_LOCK_SCHEMA_V1} only.`);
939
+ }
940
+ const release = objectAt("release", root.release, [
941
+ "artifactRef",
942
+ "digest",
943
+ "version"
944
+ ]);
945
+ const lock = {
946
+ schemaVersion: exports.CATALOG_LOCK_SCHEMA_V1,
947
+ release: {
948
+ artifactRef: stringAt("release.artifactRef", release.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository"),
949
+ digest: stringAt("release.digest", release.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
950
+ version: stringAt("release.version", release.version)
951
+ },
952
+ actions: []
953
+ };
954
+ if (!Array.isArray(root.actions) || root.actions.length === 0) {
955
+ throw new CatalogLockError("actions is not a non-empty array. A release names at least one action.");
956
+ }
957
+ root.actions.forEach((entry, index) => {
958
+ const path = `actions[${index}]`;
959
+ const action = objectAt(path, entry, [
960
+ "key",
961
+ "artifactRef",
962
+ "digest",
963
+ "manifestSha256",
964
+ "provenance",
965
+ "executes",
966
+ "imageDigest"
967
+ ]);
968
+ const key = stringAt(`${path}.key`, action.key);
969
+ const artifactRef = stringAt(`${path}.artifactRef`, action.artifactRef, catalog_release_1.ARTIFACT_REPOSITORY_PATTERN, "a registry repository");
970
+ const misnamed = (0, action_repository_1.actionPackageRefusal)(key, artifactRef);
971
+ if (misnamed !== null) throw new CatalogLockError(`${path}: ${misnamed}`);
972
+ const previous = lock.actions[lock.actions.length - 1];
973
+ if (previous !== void 0 && !(previous.key < key)) {
974
+ throw new CatalogLockError(`${path}.key "${key}" follows "${previous.key}": a lock names each action once, sorted by key.`);
975
+ }
976
+ const provenance2 = objectAt(`${path}.provenance`, action.provenance, [
977
+ "repository",
978
+ "commit"
979
+ ]);
980
+ if (action.executes === void 0 !== (action.imageDigest === void 0)) {
981
+ throw new CatalogLockError(`${path} carries ${action.executes === void 0 ? "imageDigest without executes" : "executes without imageDigest"}; an engine action records both, a built-in neither.`);
982
+ }
983
+ if (action.executes !== void 0 && action.executes !== exports.CATALOG_LOCK_ENGINE_EXECUTION) {
984
+ throw new CatalogLockError(`${path}.executes is ${JSON.stringify(action.executes)}; the one value it takes is "${exports.CATALOG_LOCK_ENGINE_EXECUTION}".`);
985
+ }
986
+ lock.actions.push({
987
+ key,
988
+ artifactRef,
989
+ digest: stringAt(`${path}.digest`, action.digest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest"),
990
+ manifestSha256: stringAt(`${path}.manifestSha256`, action.manifestSha256, exports.MANIFEST_SHA256_PATTERN, "64 lowercase hex characters"),
991
+ provenance: {
992
+ repository: stringAt(`${path}.provenance.repository`, provenance2.repository),
993
+ commit: stringAt(`${path}.provenance.commit`, provenance2.commit)
994
+ },
995
+ ...action.executes === void 0 ? {} : {
996
+ executes: exports.CATALOG_LOCK_ENGINE_EXECUTION,
997
+ imageDigest: stringAt(`${path}.imageDigest`, action.imageDigest, catalog_release_1.ARTIFACT_DIGEST_PATTERN, "a sha256 digest")
998
+ }
999
+ });
1000
+ });
1001
+ return lock;
1002
+ }
1003
+ __name(parseCatalogLock, "parseCatalogLock");
1004
+ function catalogLockText(lock) {
1005
+ const ordered = {
1006
+ schemaVersion: lock.schemaVersion,
1007
+ release: {
1008
+ artifactRef: lock.release.artifactRef,
1009
+ digest: lock.release.digest,
1010
+ version: lock.release.version
1011
+ },
1012
+ actions: [
1013
+ ...lock.actions
1014
+ ].sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map((action) => ({
1015
+ key: action.key,
1016
+ artifactRef: action.artifactRef,
1017
+ digest: action.digest,
1018
+ manifestSha256: action.manifestSha256,
1019
+ provenance: {
1020
+ repository: action.provenance.repository,
1021
+ commit: action.provenance.commit
1022
+ },
1023
+ ...action.executes === void 0 || action.imageDigest === void 0 ? {} : {
1024
+ executes: action.executes,
1025
+ imageDigest: action.imageDigest
1026
+ }
1027
+ }))
1028
+ };
1029
+ return `${JSON.stringify(ordered, null, 2)}
1030
+ `;
1031
+ }
1032
+ __name(catalogLockText, "catalogLockText");
1033
+ function authoredElsewhere(lock, key) {
1034
+ const namespace = lock.release.artifactRef.slice(0, lock.release.artifactRef.lastIndexOf("/"));
1035
+ const organisation = namespace.slice(namespace.lastIndexOf("/") + 1);
1036
+ return `${key} is authored in ${organisation}/${(0, action_repository_1.actionRepositoryName)(key)} \u2014 change it there, publish, assemble, vendor`;
1037
+ }
1038
+ __name(authoredElsewhere, "authoredElsewhere");
1039
+ }
1040
+ });
1041
+
1042
+ // ../contracts/dist/catalog-manifest-placeholders.js
1043
+ var require_catalog_manifest_placeholders = __commonJS({
1044
+ "../contracts/dist/catalog-manifest-placeholders.js"(exports) {
1045
+ "use strict";
1046
+ Object.defineProperty(exports, "__esModule", {
1047
+ value: true
1048
+ });
1049
+ exports.MANIFEST_PLACEHOLDER_PATTERN = exports.MANIFEST_PLACEHOLDERS = void 0;
1050
+ var brand_1 = require_brand();
1051
+ exports.MANIFEST_PLACEHOLDERS = {
1052
+ BRAND_SHORT_NAME: brand_1.BRAND_SHORT_NAME,
1053
+ BRAND_NAME: brand_1.BRAND_NAME
1054
+ };
1055
+ exports.MANIFEST_PLACEHOLDER_PATTERN = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
1056
+ }
1057
+ });
1058
+
561
1059
  // ../contracts/dist/change-control.js
562
1060
  var require_change_control = __commonJS({
563
1061
  "../contracts/dist/change-control.js"(exports) {
@@ -857,8 +1355,8 @@ var require_package_provenance = __commonJS({
857
1355
  pair.accepted,
858
1356
  pair.revoked
859
1357
  ]);
860
- function poolProvenanceAuditAction(provenance, accepted) {
861
- const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[provenance];
1358
+ function poolProvenanceAuditAction(provenance2, accepted) {
1359
+ const pair = exports.POOL_PROVENANCE_AUDIT_ACTIONS[provenance2];
862
1360
  return accepted ? pair.accepted : pair.revoked;
863
1361
  }
864
1362
  __name(poolProvenanceAuditAction, "poolProvenanceAuditAction");
@@ -1286,7 +1784,7 @@ var require_validation = __commonJS({
1286
1784
  Object.defineProperty(exports, "__esModule", {
1287
1785
  value: true
1288
1786
  });
1289
- exports.TARGET_COMPARISON_KINDS = exports.TARGETS_NO_SCOPE = exports.TARGETS_NOT_COUNTED = exports.TARGET_COUNT_ABSENCES = void 0;
1787
+ 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;
1290
1788
  exports.isTargetCountAbsence = isTargetCountAbsence;
1291
1789
  exports.singleTarget = singleTarget;
1292
1790
  exports.noTargetsResolved = noTargetsResolved;
@@ -1296,6 +1794,10 @@ var require_validation = __commonJS({
1296
1794
  exports.eligibleTargetCount = eligibleTargetCount;
1297
1795
  exports.targetsValidationCheck = targetsValidationCheck;
1298
1796
  exports.targetSummaryOf = targetSummaryOf;
1797
+ exports.isCheckLevel = isCheckLevel;
1798
+ exports.isRefusalCode = isRefusalCode;
1799
+ exports.isDecisionCode = isDecisionCode;
1800
+ exports.validationCheckOf = validationCheckOf;
1299
1801
  exports.scopeFailureDetail = scopeFailureDetail;
1300
1802
  exports.targetsUnavailableFrom = targetsUnavailableFrom;
1301
1803
  exports.compareTargets = compareTargets;
@@ -1412,6 +1914,95 @@ var require_validation = __commonJS({
1412
1914
  return exports.TARGETS_NOT_COUNTED;
1413
1915
  }
1414
1916
  __name(targetSummaryOf, "targetSummaryOf");
1917
+ exports.CHECK_LEVELS = [
1918
+ "info",
1919
+ "warn",
1920
+ "error"
1921
+ ];
1922
+ function isCheckLevel(value) {
1923
+ return typeof value === "string" && exports.CHECK_LEVELS.includes(value);
1924
+ }
1925
+ __name(isCheckLevel, "isCheckLevel");
1926
+ exports.REFUSAL_CODES = [
1927
+ "disk.notUnattached",
1928
+ "disk.locked",
1929
+ "disk.shared",
1930
+ "disk.inRecoveryPlan",
1931
+ "disk.keyUnusable",
1932
+ "disk.unverifiable"
1933
+ ];
1934
+ function isRefusalCode(value) {
1935
+ return typeof value === "string" && exports.REFUSAL_CODES.includes(value);
1936
+ }
1937
+ __name(isRefusalCode, "isRefusalCode");
1938
+ exports.DECISION_CODES = [
1939
+ "disk.recentlyDetached",
1940
+ "disk.customerKey",
1941
+ "disk.customerKeyUnverified",
1942
+ "disk.guestEncrypted",
1943
+ "disk.noSnapshot",
1944
+ // A confidential VM's disk: its snapshot keeps the VM's guest state, region-bound, and restoring
1945
+ // one is unverified (docs/126 §6.6.1) — the operator's call, never a refusal.
1946
+ "disk.confidential"
1947
+ ];
1948
+ function isDecisionCode(value) {
1949
+ return typeof value === "string" && exports.DECISION_CODES.includes(value);
1950
+ }
1951
+ __name(isDecisionCode, "isDecisionCode");
1952
+ function validationCheckOf(value) {
1953
+ const raw = typeof value === "object" && value !== null ? value : {};
1954
+ const level = raw.level;
1955
+ if (!isCheckLevel(level)) {
1956
+ return {
1957
+ refusal: `A check level of "${String(level)}" is not in the vocabulary.`
1958
+ };
1959
+ }
1960
+ if (typeof raw.title !== "string" || raw.title.length === 0) {
1961
+ return {
1962
+ refusal: "Every check must carry a title."
1963
+ };
1964
+ }
1965
+ if (raw.decision !== void 0 && raw.refusal !== void 0) {
1966
+ return {
1967
+ refusal: `A check carrying both a decision ("${String(raw.decision)}") and a refusal ("${String(raw.refusal)}") is not a check any producer of ours can build.`
1968
+ };
1969
+ }
1970
+ if (raw.decision !== void 0) {
1971
+ if (!isDecisionCode(raw.decision) || level === "info") {
1972
+ return {
1973
+ refusal: `A check's decision of "${String(raw.decision)}" at level "${level}" is not one this channel accepts.`
1974
+ };
1975
+ }
1976
+ return {
1977
+ check: {
1978
+ level,
1979
+ title: raw.title,
1980
+ decision: raw.decision
1981
+ }
1982
+ };
1983
+ }
1984
+ if (raw.refusal !== void 0) {
1985
+ if (!isRefusalCode(raw.refusal) || level !== "error") {
1986
+ return {
1987
+ refusal: `A check's refusal of "${String(raw.refusal)}" at level "${level}" is not one this channel accepts.`
1988
+ };
1989
+ }
1990
+ return {
1991
+ check: {
1992
+ level,
1993
+ title: raw.title,
1994
+ refusal: raw.refusal
1995
+ }
1996
+ };
1997
+ }
1998
+ return {
1999
+ check: {
2000
+ level,
2001
+ title: raw.title
2002
+ }
2003
+ };
2004
+ }
2005
+ __name(validationCheckOf, "validationCheckOf");
1415
2006
  function scopeFailureDetail(error) {
1416
2007
  const raw = error instanceof Error ? error.message : String(error);
1417
2008
  const oneLine = raw.replace(/\s+/g, " ").trim().replace(/[.!]+$/, "");
@@ -1482,7 +2073,7 @@ var require_execution = __commonJS({
1482
2073
  Object.defineProperty(exports, "__esModule", {
1483
2074
  value: true
1484
2075
  });
1485
- 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;
2076
+ 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.validationCheckOf = 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;
1486
2077
  exports.executionLogStreamSentence = executionLogStreamSentence;
1487
2078
  exports.actionRunnabilityCopy = actionRunnabilityCopy;
1488
2079
  exports.actionRunnabilityHeadline = actionRunnabilityHeadline;
@@ -1493,6 +2084,42 @@ var require_execution = __commonJS({
1493
2084
  var brand_1 = require_brand();
1494
2085
  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.";
1495
2086
  var validation_1 = require_validation();
2087
+ Object.defineProperty(exports, "CHECK_LEVELS", {
2088
+ enumerable: true,
2089
+ get: /* @__PURE__ */ __name(function() {
2090
+ return validation_1.CHECK_LEVELS;
2091
+ }, "get")
2092
+ });
2093
+ Object.defineProperty(exports, "DECISION_CODES", {
2094
+ enumerable: true,
2095
+ get: /* @__PURE__ */ __name(function() {
2096
+ return validation_1.DECISION_CODES;
2097
+ }, "get")
2098
+ });
2099
+ Object.defineProperty(exports, "isCheckLevel", {
2100
+ enumerable: true,
2101
+ get: /* @__PURE__ */ __name(function() {
2102
+ return validation_1.isCheckLevel;
2103
+ }, "get")
2104
+ });
2105
+ Object.defineProperty(exports, "isDecisionCode", {
2106
+ enumerable: true,
2107
+ get: /* @__PURE__ */ __name(function() {
2108
+ return validation_1.isDecisionCode;
2109
+ }, "get")
2110
+ });
2111
+ Object.defineProperty(exports, "isRefusalCode", {
2112
+ enumerable: true,
2113
+ get: /* @__PURE__ */ __name(function() {
2114
+ return validation_1.isRefusalCode;
2115
+ }, "get")
2116
+ });
2117
+ Object.defineProperty(exports, "REFUSAL_CODES", {
2118
+ enumerable: true,
2119
+ get: /* @__PURE__ */ __name(function() {
2120
+ return validation_1.REFUSAL_CODES;
2121
+ }, "get")
2122
+ });
1496
2123
  Object.defineProperty(exports, "compareTargets", {
1497
2124
  enumerable: true,
1498
2125
  get: /* @__PURE__ */ __name(function() {
@@ -1607,6 +2234,12 @@ var require_execution = __commonJS({
1607
2234
  return validation_1.targetsValidationCheck;
1608
2235
  }, "get")
1609
2236
  });
2237
+ Object.defineProperty(exports, "validationCheckOf", {
2238
+ enumerable: true,
2239
+ get: /* @__PURE__ */ __name(function() {
2240
+ return validation_1.validationCheckOf;
2241
+ }, "get")
2242
+ });
1610
2243
  exports.EXECUTION_LOG_ACTIVITIES = [
1611
2244
  "executing",
1612
2245
  "queued",
@@ -1634,6 +2267,7 @@ var require_execution = __commonJS({
1634
2267
  }
1635
2268
  __name(executionLogStreamSentence, "executionLogStreamSentence");
1636
2269
  var ACTION_RUNNABILITY_CAUSE_KIND_SET = {
2270
+ catalog_blocked: true,
1637
2271
  connector_unregistered: true,
1638
2272
  package: true,
1639
2273
  runner_pool: true,
@@ -1643,8 +2277,12 @@ var require_execution = __commonJS({
1643
2277
  };
1644
2278
  exports.ACTION_RUNNABILITY_CAUSE_KINDS = Object.keys(ACTION_RUNNABILITY_CAUSE_KIND_SET);
1645
2279
  exports.CONNECTOR_UNAVAILABLE_MESSAGE = "This action is not yet available to run.";
2280
+ var CATALOG_BLOCKED_CAUSE = "This workspace does not permit this action.";
2281
+ var CATALOG_BLOCKED_REMEDY = "An administrator of this workspace decides which catalog actions may run here, and can allow it.";
1646
2282
  function actionRunnabilityCopy(cause) {
1647
2283
  switch (cause.kind) {
2284
+ case "catalog_blocked":
2285
+ return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY}`;
1648
2286
  case "package":
1649
2287
  return (0, packages_2.actionUnrunnableCopy)(cause.detail);
1650
2288
  case "runner_pool": {
@@ -1676,7 +2314,7 @@ var require_execution = __commonJS({
1676
2314
  }
1677
2315
  __name(describeRoutingPlaces, "describeRoutingPlaces");
1678
2316
  function runnerPoolClauses(cause) {
1679
- const provenance = cause.provenance ? `is ${packages_1.PACKAGE_PROVENANCE_CLAUSES[cause.provenance]}` : (
2317
+ const provenance2 = cause.provenance ? `is ${packages_1.PACKAGE_PROVENANCE_CLAUSES[cause.provenance]}` : (
1680
2318
  // every other value this sentence could take is a claim about where bytes came from.
1681
2319
  "has no recorded provenance"
1682
2320
  );
@@ -1694,7 +2332,7 @@ var require_execution = __commonJS({
1694
2332
  * credential it acts with. A refusal that dropped those would be telling an admin the
1695
2333
  * decision no longer matters.
1696
2334
  */
1697
- 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.`,
2335
+ 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.`,
1698
2336
  remedy: `${acceptedClause(cause.accepts)} ${POOL_CONSENT_REMEDY}`
1699
2337
  };
1700
2338
  }
@@ -1733,6 +2371,8 @@ var require_execution = __commonJS({
1733
2371
  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.`;
1734
2372
  function actionRunnabilityHeadline(cause) {
1735
2373
  switch (cause.kind) {
2374
+ case "catalog_blocked":
2375
+ return "this workspace does not permit its action \u2014 an administrator of this workspace can allow it";
1736
2376
  case "package":
1737
2377
  return (0, packages_2.actionUnrunnableHeadline)(cause.detail);
1738
2378
  case "runner_pool":
@@ -1750,6 +2390,8 @@ var require_execution = __commonJS({
1750
2390
  __name(actionRunnabilityHeadline, "actionRunnabilityHeadline");
1751
2391
  function actionRunnabilityRefusal(cause) {
1752
2392
  switch (cause.kind) {
2393
+ case "catalog_blocked":
2394
+ return `${CATALOG_BLOCKED_CAUSE} ${CATALOG_BLOCKED_REMEDY} Nothing was started.`;
1753
2395
  case "package":
1754
2396
  return (0, packages_2.actionUnrunnableRefusal)(cause.detail, cause);
1755
2397
  case "runner_pool": {
@@ -1774,6 +2416,130 @@ var require_execution = __commonJS({
1774
2416
  }
1775
2417
  });
1776
2418
 
2419
+ // ../contracts/dist/decisions.js
2420
+ var require_decisions = __commonJS({
2421
+ "../contracts/dist/decisions.js"(exports) {
2422
+ "use strict";
2423
+ Object.defineProperty(exports, "__esModule", {
2424
+ value: true
2425
+ });
2426
+ exports.DECISION_ACKNOWLEDGEMENT = exports.TARGET_DISPOSITIONS = exports.UNDECIDED_AT_RUN = void 0;
2427
+ exports.recordedDecisionsOf = recordedDecisionsOf;
2428
+ exports.settleDecisionChecks = settleDecisionChecks;
2429
+ exports.raisedDecisionCodes = raisedDecisionCodes;
2430
+ exports.decisionMismatch = decisionMismatch;
2431
+ exports.decisionMismatchSentence = decisionMismatchSentence;
2432
+ exports.targetDecisionsMismatchSentence = targetDecisionsMismatchSentence;
2433
+ exports.decisionsMadeAtSubmit = decisionsMadeAtSubmit;
2434
+ exports.isTargetDisposition = isTargetDisposition;
2435
+ var validation_1 = require_validation();
2436
+ function recordedDecisionsOf(stored) {
2437
+ if (!Array.isArray(stored)) return [];
2438
+ const read = [];
2439
+ for (const entry of stored) {
2440
+ if (typeof entry !== "object" || entry === null) continue;
2441
+ const { code, acknowledgedBy, acknowledgedAt } = entry;
2442
+ if (!(0, validation_1.isDecisionCode)(code)) continue;
2443
+ if (typeof acknowledgedBy !== "string" || acknowledgedBy.length === 0) continue;
2444
+ if (typeof acknowledgedAt !== "string" || Number.isNaN(Date.parse(acknowledgedAt))) continue;
2445
+ if (read.some((decision) => decision.code === code)) continue;
2446
+ read.push({
2447
+ code,
2448
+ acknowledgedBy,
2449
+ acknowledgedAt
2450
+ });
2451
+ }
2452
+ return read;
2453
+ }
2454
+ __name(recordedDecisionsOf, "recordedDecisionsOf");
2455
+ exports.UNDECIDED_AT_RUN = "Nobody acknowledged this before the run, so it was not acted on.";
2456
+ function settleDecisionChecks(checks, inHand) {
2457
+ if (inHand.phase === "plan") return [
2458
+ ...checks
2459
+ ];
2460
+ return checks.map((check) => {
2461
+ if (!check.decision || check.level === "error") return check;
2462
+ if (inHand.recorded.some((decision) => decision.code === check.decision)) return check;
2463
+ return {
2464
+ ...check,
2465
+ level: "error",
2466
+ title: `${check.title} ${exports.UNDECIDED_AT_RUN}`
2467
+ };
2468
+ });
2469
+ }
2470
+ __name(settleDecisionChecks, "settleDecisionChecks");
2471
+ function raisedDecisionCodes(checks) {
2472
+ const raised = [];
2473
+ for (const check of checks) {
2474
+ if (check.decision && !raised.includes(check.decision)) raised.push(check.decision);
2475
+ }
2476
+ return raised;
2477
+ }
2478
+ __name(raisedDecisionCodes, "raisedDecisionCodes");
2479
+ function decisionMismatch(raised, acknowledged) {
2480
+ const unacknowledged = raised.filter((code) => !acknowledged.includes(code));
2481
+ const notRaised = acknowledged.filter((code) => !raised.includes(code));
2482
+ return unacknowledged.length === 0 && notRaised.length === 0 ? null : {
2483
+ unacknowledged,
2484
+ notRaised
2485
+ };
2486
+ }
2487
+ __name(decisionMismatch, "decisionMismatch");
2488
+ function decisionMismatchSentence(mismatch, checks = []) {
2489
+ return `This run was not submitted: the checks were run again at submit, and they no longer match what was acknowledged. ${mismatchParts(mismatch, checks).join(" ")} Review the plan and submit again.`;
2490
+ }
2491
+ __name(decisionMismatchSentence, "decisionMismatchSentence");
2492
+ function targetDecisionsMismatchSentence(mismatches) {
2493
+ const parts = mismatches.map((target) => target.unknown ? `${target.label}: no longer one of this step's targets.` : `${target.label}: ${mismatchParts(target, target.checks).join(" ")}`);
2494
+ const count = mismatches.length;
2495
+ return `This run was not started: the checks were run again, and for ${count} ${count === 1 ? "target" : "targets"} they no longer match what was decided. ${parts.join(" ")} Review the plan and start the run again.`;
2496
+ }
2497
+ __name(targetDecisionsMismatchSentence, "targetDecisionsMismatchSentence");
2498
+ function mismatchParts(mismatch, checks) {
2499
+ const parts = [];
2500
+ for (const code of mismatch.unacknowledged) {
2501
+ const sentence = checks.find((check) => check.decision === code)?.title ?? exports.DECISION_ACKNOWLEDGEMENT[code];
2502
+ parts.push(`Not acknowledged: ${withStop(sentence)}`);
2503
+ }
2504
+ for (const code of mismatch.notRaised) {
2505
+ parts.push(`Acknowledged, but no longer raised: \u201C${exports.DECISION_ACKNOWLEDGEMENT[code]}\u201D.`);
2506
+ }
2507
+ return parts;
2508
+ }
2509
+ __name(mismatchParts, "mismatchParts");
2510
+ function decisionsMadeAtSubmit(codes, by, at) {
2511
+ return codes.map((code) => ({
2512
+ code,
2513
+ acknowledgedBy: by,
2514
+ acknowledgedAt: at.toISOString()
2515
+ }));
2516
+ }
2517
+ __name(decisionsMadeAtSubmit, "decisionsMadeAtSubmit");
2518
+ exports.TARGET_DISPOSITIONS = [
2519
+ "ready",
2520
+ "left_out",
2521
+ "refused"
2522
+ ];
2523
+ function isTargetDisposition(value) {
2524
+ return typeof value === "string" && exports.TARGET_DISPOSITIONS.includes(value);
2525
+ }
2526
+ __name(isTargetDisposition, "isTargetDisposition");
2527
+ function withStop(sentence) {
2528
+ const trimmed = sentence.trim();
2529
+ return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
2530
+ }
2531
+ __name(withStop, "withStop");
2532
+ exports.DECISION_ACKNOWLEDGEMENT = {
2533
+ "disk.recentlyDetached": "Delete it even though it was detached recently",
2534
+ "disk.customerKey": "Delete it \u2014 restoring later depends on this key staying enabled",
2535
+ "disk.customerKeyUnverified": "Delete it without confirmation that the key is enabled",
2536
+ "disk.guestEncrypted": "Delete it \u2014 restoring later depends on the guest's encryption secrets",
2537
+ "disk.noSnapshot": "Delete it with no snapshot \u2014 this cannot be undone",
2538
+ "disk.confidential": "Delete it, accepting that restoring a confidential VM disk is not verified"
2539
+ };
2540
+ }
2541
+ });
2542
+
1777
2543
  // ../contracts/dist/execution-site.js
1778
2544
  var require_execution_site = __commonJS({
1779
2545
  "../contracts/dist/execution-site.js"(exports) {
@@ -1858,7 +2624,13 @@ var require_resources = __commonJS({
1858
2624
  Object.defineProperty(exports, "__esModule", {
1859
2625
  value: true
1860
2626
  });
1861
- exports.DISCOVERY_TYPE_PATTERN = exports.DISCOVERABLE_RESOURCE_TYPES = exports.DISCOVERY_VOCABULARY = void 0;
2627
+ exports.DISCOVERY_TYPE_PATTERN = exports.LEGACY_DISCOVERY_PROVIDER = exports.DISCOVERY_VOCABULARY = void 0;
2628
+ exports.discoveryTypesOf = discoveryTypesOf;
2629
+ exports.declaredDiscoveryTypes = declaredDiscoveryTypes;
2630
+ exports.liveDiscoveryTypes = liveDiscoveryTypes;
2631
+ exports.canonicalDiscoveryId = canonicalDiscoveryId;
2632
+ exports.resolveDiscoveryType = resolveDiscoveryType;
2633
+ exports.isSecretReference = isSecretReference;
1862
2634
  exports.parseDiscoveryType = parseDiscoveryType;
1863
2635
  exports.formatDiscoveryType = formatDiscoveryType;
1864
2636
  exports.discoveryTypeSpec = discoveryTypeSpec;
@@ -1874,16 +2646,20 @@ var require_resources = __commonJS({
1874
2646
  resourceGroup: {
1875
2647
  requires: [],
1876
2648
  routable: false,
1877
- identity: null
2649
+ identity: null,
2650
+ secretRef: false
1878
2651
  },
1879
2652
  // ROUTABLE BUT NOT NAMABLE, and the two are different questions. A rule matches a vault by
1880
2653
  // its NAME and its tags, which discovery supplies; an ARM vault id needs the resource group
1881
2654
  // this vocabulary never asks for (`requires` is empty so vaults list subscription-wide). See
1882
2655
  // `identity` on the spec above — null is the honest answer, not an omission.
2656
+ // NOT a secret reference: a vault CONTAINS secrets, and naming one reveals nothing a run
2657
+ // could read. The secret inside it is the reference (`keyVaultSecret` below).
1883
2658
  keyVault: {
1884
2659
  requires: [],
1885
2660
  routable: true,
1886
- identity: null
2661
+ identity: null,
2662
+ secretRef: false
1887
2663
  },
1888
2664
  // ★ A DATA-PLANE URI, NOT AN ARM PATH, AND IT IS WHAT DISCOVERY ALREADY RETURNS. The Key
1889
2665
  // Vault SDK reports `SecretProperties.id` as `https://<vault>.vault.azure.net/secrets/<name>`
@@ -1895,27 +2671,33 @@ var require_resources = __commonJS({
1895
2671
  "vault"
1896
2672
  ],
1897
2673
  routable: true,
1898
- identity: "https://{vault}.vault.azure.net/secrets/{name}"
2674
+ identity: "https://{vault}.vault.azure.net/secrets/{name}",
2675
+ // ★ THE ONE TYPE TODAY WHOSE VALUE POINTS AT A SECRET (docs/122 §3.5). The parameter holds
2676
+ // the secret's NAME within its vault; nothing in the platform ever reads the value behind it.
2677
+ secretRef: true
1899
2678
  },
1900
2679
  // A catalogue value. Nothing is reached by naming a region.
1901
2680
  location: {
1902
2681
  requires: [],
1903
2682
  routable: false,
1904
- identity: null
2683
+ identity: null,
2684
+ secretRef: false
1905
2685
  },
1906
2686
  managedDisk: {
1907
2687
  requires: [
1908
2688
  "resourceGroup"
1909
2689
  ],
1910
2690
  routable: true,
1911
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}"
2691
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/disks/{name}",
2692
+ secretRef: false
1912
2693
  },
1913
2694
  sqlServer: {
1914
2695
  requires: [
1915
2696
  "resourceGroup"
1916
2697
  ],
1917
2698
  routable: true,
1918
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}"
2699
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{name}",
2700
+ secretRef: false
1919
2701
  },
1920
2702
  sqlDatabase: {
1921
2703
  requires: [
@@ -1923,7 +2705,8 @@ var require_resources = __commonJS({
1923
2705
  "server"
1924
2706
  ],
1925
2707
  routable: true,
1926
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}"
2708
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Sql/servers/{server}/databases/{name}",
2709
+ secretRef: false
1927
2710
  },
1928
2711
  // The one type where LISTING and IDENTIFYING are different questions. It lists
1929
2712
  // SUBSCRIPTION-WIDE when no resource group is given (`virtualMachines.listAll()`,
@@ -1943,7 +2726,8 @@ var require_resources = __commonJS({
1943
2726
  "resourceGroup"
1944
2727
  ],
1945
2728
  routable: true,
1946
- identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}"
2729
+ identity: "/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers/Microsoft.Compute/virtualMachines/{name}",
2730
+ secretRef: false
1947
2731
  },
1948
2732
  // The sizes ONE machine can actually be resized to right now — not the region's SKU
1949
2733
  // catalogue. `requires` is genuinely both fields' answer here: the safe set cannot be
@@ -1957,16 +2741,53 @@ var require_resources = __commonJS({
1957
2741
  "vm"
1958
2742
  ],
1959
2743
  routable: false,
1960
- identity: null
2744
+ identity: null,
2745
+ secretRef: false
1961
2746
  }
1962
2747
  };
1963
- exports.DISCOVERY_VOCABULARY = {
2748
+ var DECLARED_DISCOVERY = {
1964
2749
  azure: AZURE_DISCOVERY_TYPES,
1965
2750
  aws: {},
1966
2751
  gcp: {},
1967
2752
  terraform: {}
1968
2753
  };
1969
- exports.DISCOVERABLE_RESOURCE_TYPES = Object.keys(AZURE_DISCOVERY_TYPES);
2754
+ exports.DISCOVERY_VOCABULARY = DECLARED_DISCOVERY;
2755
+ exports.LEGACY_DISCOVERY_PROVIDER = "azure";
2756
+ function discoveryTypesOf(provider) {
2757
+ return Object.keys(exports.DISCOVERY_VOCABULARY[provider]).map((type) => formatDiscoveryType(provider, type));
2758
+ }
2759
+ __name(discoveryTypesOf, "discoveryTypesOf");
2760
+ function declaredDiscoveryTypes() {
2761
+ return enums_1.PROVIDERS.flatMap(discoveryTypesOf);
2762
+ }
2763
+ __name(declaredDiscoveryTypes, "declaredDiscoveryTypes");
2764
+ function liveDiscoveryTypes(registered) {
2765
+ const providers = new Set(registered);
2766
+ return enums_1.PROVIDERS.filter((provider) => providers.has(provider)).flatMap(discoveryTypesOf);
2767
+ }
2768
+ __name(liveDiscoveryTypes, "liveDiscoveryTypes");
2769
+ function canonicalDiscoveryId(value) {
2770
+ const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
2771
+ return parsed ? formatDiscoveryType(parsed.provider, parsed.type) : null;
2772
+ }
2773
+ __name(canonicalDiscoveryId, "canonicalDiscoveryId");
2774
+ function resolveDiscoveryType(value) {
2775
+ const parsed = parseDiscoveryType(value.trim(), exports.LEGACY_DISCOVERY_PROVIDER);
2776
+ if (!parsed) return null;
2777
+ const spec = discoveryTypeSpec(parsed.provider, parsed.type);
2778
+ if (!spec) return null;
2779
+ return {
2780
+ id: formatDiscoveryType(parsed.provider, parsed.type),
2781
+ provider: parsed.provider,
2782
+ type: parsed.type,
2783
+ spec
2784
+ };
2785
+ }
2786
+ __name(resolveDiscoveryType, "resolveDiscoveryType");
2787
+ function isSecretReference(discover) {
2788
+ return discover ? resolveDiscoveryType(discover)?.spec.secretRef === true : false;
2789
+ }
2790
+ __name(isSecretReference, "isSecretReference");
1970
2791
  exports.DISCOVERY_TYPE_PATTERN = /^(?:([a-z][a-z0-9]*)\/)?([a-z][a-zA-Z0-9]{0,63})$/;
1971
2792
  function parseDiscoveryType(value, fallbackProvider) {
1972
2793
  const match = exports.DISCOVERY_TYPE_PATTERN.exec(value);
@@ -1990,7 +2811,9 @@ var require_resources = __commonJS({
1990
2811
  }
1991
2812
  __name(formatDiscoveryType, "formatDiscoveryType");
1992
2813
  function discoveryTypeSpec(provider, type) {
1993
- return exports.DISCOVERY_VOCABULARY[provider]?.[type];
2814
+ if (!Object.prototype.hasOwnProperty.call(exports.DISCOVERY_VOCABULARY, provider)) return void 0;
2815
+ const vocabulary = exports.DISCOVERY_VOCABULARY[provider];
2816
+ return Object.prototype.hasOwnProperty.call(vocabulary, type) ? vocabulary[type] : void 0;
1994
2817
  }
1995
2818
  __name(discoveryTypeSpec, "discoveryTypeSpec");
1996
2819
  function dependsOnLookup(keys, dependsOn) {
@@ -2076,8 +2899,59 @@ var require_governance = __commonJS({
2076
2899
  value: true
2077
2900
  });
2078
2901
  exports.ANY_POLICY_MATCH = void 0;
2902
+ exports.approvalAssessmentOf = approvalAssessmentOf;
2903
+ exports.planAssessmentOf = planAssessmentOf;
2079
2904
  exports.approvalOutcome = approvalOutcome;
2080
2905
  exports.policyScopeSentence = policyScopeSentence;
2906
+ var decisions_1 = require_decisions();
2907
+ function approvalAssessmentOf(checks, decided) {
2908
+ const assessment = {
2909
+ facts: [],
2910
+ warnings: [],
2911
+ decisions: []
2912
+ };
2913
+ for (const check of checks) {
2914
+ if (check.decision) {
2915
+ const answer = decided.find((decision) => decision.code === check.decision);
2916
+ if (answer) assessment.decisions.push({
2917
+ ...answer,
2918
+ title: check.title
2919
+ });
2920
+ } else if (check.level === "warn") {
2921
+ assessment.warnings.push(check.title);
2922
+ } else if (check.level === "info") {
2923
+ assessment.facts.push(check.title);
2924
+ }
2925
+ }
2926
+ return assessment;
2927
+ }
2928
+ __name(approvalAssessmentOf, "approvalAssessmentOf");
2929
+ function planAssessmentOf(value) {
2930
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
2931
+ const a = value;
2932
+ if (!Array.isArray(a.facts) || !Array.isArray(a.warnings) || !Array.isArray(a.decisions)) {
2933
+ return null;
2934
+ }
2935
+ const decisions = [];
2936
+ for (const entry of a.decisions) {
2937
+ const [decision] = (0, decisions_1.recordedDecisionsOf)([
2938
+ entry
2939
+ ]);
2940
+ const title = typeof entry === "object" && entry !== null ? entry.title : null;
2941
+ if (!decision || typeof title !== "string") return null;
2942
+ decisions.push({
2943
+ ...decision,
2944
+ title
2945
+ });
2946
+ }
2947
+ const strings = /* @__PURE__ */ __name((list) => list.filter((item) => typeof item === "string"), "strings");
2948
+ return {
2949
+ facts: strings(a.facts),
2950
+ warnings: strings(a.warnings),
2951
+ decisions
2952
+ };
2953
+ }
2954
+ __name(planAssessmentOf, "planAssessmentOf");
2081
2955
  exports.ANY_POLICY_MATCH = {
2082
2956
  environments: [],
2083
2957
  riskLevels: [],
@@ -2188,7 +3062,10 @@ var require_operations = __commonJS({
2188
3062
  Object.defineProperty(exports, "__esModule", {
2189
3063
  value: true
2190
3064
  });
2191
- exports.OPERATION_RUN_TARGET_STATUSES = exports.OPERATION_RUN_STEP_STATUSES = exports.OPERATION_RUN_STATUSES = exports.STEP_FAILURE_POLICIES = exports.OPERATION_STEP_KINDS = exports.DRY_RUN_OUTCOMES = exports.OPERATION_VERIFICATION_LIFECYCLES = exports.OPERATION_HEALTH_STATES = exports.OPERATION_LIFECYCLES = void 0;
3065
+ exports.PREVIEW_UNCHECKED_SENTENCE = exports.PREVIEW_UNCHECKED_REASONS = exports.OPERATION_RUN_TARGET_STATUSES = exports.OPERATION_RUN_STEP_STATUSES = exports.OPERATION_RUN_STATUSES = exports.STEP_FAILURE_POLICIES = exports.OPERATION_STEP_KINDS = exports.DRY_RUN_OUTCOMES = exports.OPERATION_VERIFICATION_LIFECYCLES = exports.OPERATION_HEALTH_STATES = exports.OPERATION_LIFECYCLES = void 0;
3066
+ exports.targetPlanOf = targetPlanOf;
3067
+ var governance_1 = require_governance();
3068
+ var decisions_1 = require_decisions();
2192
3069
  exports.OPERATION_LIFECYCLES = [
2193
3070
  "draft",
2194
3071
  "published",
@@ -2255,9 +3132,38 @@ var require_operations = __commonJS({
2255
3132
  "rolled_back",
2256
3133
  "rollback_failed"
2257
3134
  ];
2258
- }
2259
- });
2260
-
3135
+ function targetPlanOf(stored) {
3136
+ if (!Array.isArray(stored)) return null;
3137
+ const plan = [];
3138
+ for (const entry of stored) {
3139
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
3140
+ const { ref, label, disposition, decisions, assessment, refusal } = entry;
3141
+ if (typeof ref !== "string" || ref.length === 0) continue;
3142
+ if (typeof label !== "string" || !(0, decisions_1.isTargetDisposition)(disposition)) continue;
3143
+ if (refusal !== null && typeof refusal !== "string") continue;
3144
+ plan.push({
3145
+ ref,
3146
+ label,
3147
+ disposition,
3148
+ decisions: (0, decisions_1.recordedDecisionsOf)(decisions),
3149
+ assessment: (0, governance_1.planAssessmentOf)(assessment),
3150
+ refusal: refusal ?? null
3151
+ });
3152
+ }
3153
+ return plan;
3154
+ }
3155
+ __name(targetPlanOf, "targetPlanOf");
3156
+ exports.PREVIEW_UNCHECKED_REASONS = [
3157
+ "depends_on_earlier_output",
3158
+ "connector_plans_no_checks"
3159
+ ];
3160
+ exports.PREVIEW_UNCHECKED_SENTENCE = {
3161
+ depends_on_earlier_output: "Not checked before the run: this step's parameters come from an earlier step's outputs, which exist only once that step has run. Each target is checked when its execution is submitted, and a target that would need a decision is not acted on.",
3162
+ connector_plans_no_checks: "This action runs no checks of its own before the run."
3163
+ };
3164
+ }
3165
+ });
3166
+
2261
3167
  // ../contracts/dist/runs.js
2262
3168
  var require_runs = __commonJS({
2263
3169
  "../contracts/dist/runs.js"(exports) {
@@ -2291,6 +3197,28 @@ var require_runs = __commonJS({
2291
3197
  }
2292
3198
  });
2293
3199
 
3200
+ // ../contracts/dist/evidence-pack.js
3201
+ var require_evidence_pack = __commonJS({
3202
+ "../contracts/dist/evidence-pack.js"(exports) {
3203
+ "use strict";
3204
+ Object.defineProperty(exports, "__esModule", {
3205
+ value: true
3206
+ });
3207
+ exports.EVIDENCE_EXPORTED_AUDIT_ACTION = exports.EVIDENCE_DIGEST_HEADER = void 0;
3208
+ exports.evidencePackFilename = evidencePackFilename;
3209
+ exports.EVIDENCE_DIGEST_HEADER = "X-Evidence-Digest";
3210
+ exports.EVIDENCE_EXPORTED_AUDIT_ACTION = "operation.evidence_exported";
3211
+ function evidencePackFilename(operationKey, from, to) {
3212
+ return `${operationKey}-evidence-${compactInstant(from)}-${compactInstant(to)}.json`;
3213
+ }
3214
+ __name(evidencePackFilename, "evidencePackFilename");
3215
+ function compactInstant(iso) {
3216
+ return iso.replace(/\.\d+(?=Z|[+-]\d{2}:?\d{2}$)/, "").replace(/[-:]/g, "");
3217
+ }
3218
+ __name(compactInstant, "compactInstant");
3219
+ }
3220
+ });
3221
+
2294
3222
  // ../contracts/dist/package-egress.js
2295
3223
  var require_package_egress = __commonJS({
2296
3224
  "../contracts/dist/package-egress.js"(exports) {
@@ -2355,7 +3283,7 @@ var require_package_execution = __commonJS({
2355
3283
  Object.defineProperty(exports, "__esModule", {
2356
3284
  value: true
2357
3285
  });
2358
- exports.PACKAGE_CONTAINER_ENV = exports.PACKAGE_PHASES = void 0;
3286
+ exports.RUN_TOKEN_REJECTED = exports.RUN_TOKEN_HEADER = exports.PACKAGE_RUNNER_ENTRYPOINT = exports.PACKAGE_CONTAINER_ENV = exports.PACKAGE_PHASES = void 0;
2359
3287
  exports.PACKAGE_PHASES = [
2360
3288
  "validate",
2361
3289
  "run",
@@ -2377,6 +3305,11 @@ var require_package_execution = __commonJS({
2377
3305
  egressControlUrl: "RUNVERIC_EGRESS_CONTROL_URL",
2378
3306
  egressControlToken: "RUNVERIC_EGRESS_CONTROL_TOKEN"
2379
3307
  };
3308
+ exports.PACKAGE_RUNNER_ENTRYPOINT = [
3309
+ "/opt/runveric/runner/launch.sh"
3310
+ ];
3311
+ exports.RUN_TOKEN_HEADER = "x-runveric-run-token";
3312
+ exports.RUN_TOKEN_REJECTED = "This run token is not valid for this execution.";
2380
3313
  }
2381
3314
  });
2382
3315
 
@@ -2555,9 +3488,9 @@ var require_runners = __commonJS({
2555
3488
  authored: false
2556
3489
  };
2557
3490
  exports.RUNNER_POOL_DEFAULT_ACCEPTS_PROVENANCE = package_provenance_1.PACKAGE_PROVENANCE_KINDS.filter((kind) => exports.RUNNER_POOL_ACCEPTS_PROVENANCE_BY_DEFAULT[kind]);
2558
- function poolAcceptsProvenance(accepts, provenance) {
2559
- if (provenance === null) return false;
2560
- return accepts.includes(provenance);
3491
+ function poolAcceptsProvenance(accepts, provenance2) {
3492
+ if (provenance2 === null) return false;
3493
+ return accepts.includes(provenance2);
2561
3494
  }
2562
3495
  __name(poolAcceptsProvenance, "poolAcceptsProvenance");
2563
3496
  function containerNameFor(runnerName) {
@@ -2627,6 +3560,803 @@ var require_runners = __commonJS({
2627
3560
  }
2628
3561
  });
2629
3562
 
3563
+ // ../contracts/dist/scoped-credential.js
3564
+ var require_scoped_credential = __commonJS({
3565
+ "../contracts/dist/scoped-credential.js"(exports) {
3566
+ "use strict";
3567
+ Object.defineProperty(exports, "__esModule", {
3568
+ value: true
3569
+ });
3570
+ exports.RunnerStepCredentialExpiredError = exports.RunnerStepCredentialError = exports.STEP_CREDENTIAL_FAILURE = exports.ENGINE_ACTION_TOKEN_HOLDER = exports.RUNNER_STEP_TOKEN_HOLDER = exports.StaticScopedCredential = void 0;
3571
+ var StaticScopedCredential = class StaticScopedCredential {
3572
+ static {
3573
+ __name(this, "StaticScopedCredential");
3574
+ }
3575
+ now;
3576
+ holder;
3577
+ byScope;
3578
+ constructor(tokens2, now = Date.now, holder = exports.RUNNER_STEP_TOKEN_HOLDER) {
3579
+ this.now = now;
3580
+ this.holder = holder;
3581
+ this.byScope = new Map(tokens2.map((token2) => [
3582
+ token2.scope,
3583
+ token2
3584
+ ]));
3585
+ }
3586
+ async getToken(scopes) {
3587
+ const requested = typeof scopes === "string" ? [
3588
+ scopes
3589
+ ] : scopes;
3590
+ for (const scope of requested) {
3591
+ const token2 = this.byScope.get(scope);
3592
+ if (token2) {
3593
+ if (token2.expiresOnTimestamp <= this.now()) {
3594
+ throw new RunnerStepCredentialExpiredError(scope, token2.expiresOnTimestamp, this.now(), this.holder);
3595
+ }
3596
+ return {
3597
+ token: token2.token,
3598
+ expiresOnTimestamp: token2.expiresOnTimestamp
3599
+ };
3600
+ }
3601
+ }
3602
+ throw new RunnerStepCredentialError(requested, [
3603
+ ...this.byScope.keys()
3604
+ ], this.holder);
3605
+ }
3606
+ };
3607
+ exports.StaticScopedCredential = StaticScopedCredential;
3608
+ exports.RUNNER_STEP_TOKEN_HOLDER = {
3609
+ noun: "step",
3610
+ whereDeclared: "The channel mints exactly what a connector's azureAudiences declares, so this connector reaches an Azure resource it does not say it reaches.",
3611
+ whyNoMore: "A step container is handed minted bytes and nothing it can use to get another, so the step ran longer than its own credential and cannot continue."
3612
+ };
3613
+ exports.ENGINE_ACTION_TOKEN_HOLDER = {
3614
+ noun: "action",
3615
+ whereDeclared: "The engine mints exactly the audiences an action's manifest declares in execution.audiences, so this action reaches an Azure resource it does not say it reaches.",
3616
+ whyNoMore: "An action is handed minted bytes and nothing it can use to get another, so it ran longer than its own credential and cannot continue."
3617
+ };
3618
+ exports.STEP_CREDENTIAL_FAILURE = "runvericStepCredentialFailure";
3619
+ var RunnerStepCredentialError = class RunnerStepCredentialError extends Error {
3620
+ static {
3621
+ __name(this, "RunnerStepCredentialError");
3622
+ }
3623
+ constructor(requested, available, holder = exports.RUNNER_STEP_TOKEN_HOLDER) {
3624
+ super(`This ${holder.noun} was authorized for ${available.length > 0 ? available.map(quote).join(", ") : "no scopes"} and asked for ${requested.map(quote).join(", ")}. ${holder.whereDeclared}`);
3625
+ this.name = "RunnerStepCredentialError";
3626
+ }
3627
+ /** Not a fact about the resource — see {@link STEP_CREDENTIAL_FAILURE}. */
3628
+ [exports.STEP_CREDENTIAL_FAILURE] = true;
3629
+ };
3630
+ exports.RunnerStepCredentialError = RunnerStepCredentialError;
3631
+ var RunnerStepCredentialExpiredError = class RunnerStepCredentialExpiredError extends Error {
3632
+ static {
3633
+ __name(this, "RunnerStepCredentialExpiredError");
3634
+ }
3635
+ constructor(scope, expiresOnTimestamp, now, holder = exports.RUNNER_STEP_TOKEN_HOLDER) {
3636
+ super(`This ${holder.noun}'s token for ${quote(scope)} expired ${Math.round((now - expiresOnTimestamp) / 1e3)}s ago, at ${new Date(expiresOnTimestamp).toISOString()}. ${holder.whyNoMore}`);
3637
+ this.name = "RunnerStepCredentialExpiredError";
3638
+ }
3639
+ /** Not a fact about the resource — see {@link STEP_CREDENTIAL_FAILURE}. */
3640
+ [exports.STEP_CREDENTIAL_FAILURE] = true;
3641
+ };
3642
+ exports.RunnerStepCredentialExpiredError = RunnerStepCredentialExpiredError;
3643
+ function quote(scope) {
3644
+ return `"${scope}"`;
3645
+ }
3646
+ __name(quote, "quote");
3647
+ }
3648
+ });
3649
+
3650
+ // ../contracts/dist/action-protocol.js
3651
+ var require_action_protocol = __commonJS({
3652
+ "../contracts/dist/action-protocol.js"(exports) {
3653
+ "use strict";
3654
+ Object.defineProperty(exports, "__esModule", {
3655
+ value: true
3656
+ });
3657
+ exports.ActionProtocolError = exports.JWT_SHAPE = exports.REDACTED_SECRET = exports.ENGINE_RUN_ROUTES = exports.ACTION_PROBE_ENV = exports.ACTION_RUNTIME_PROBE_FLAG = exports.ACTION_CONTAINER_ENV = exports.MAX_PLAN_VALIDATE_TARGETS = exports.ACTION_PROTOCOL_LIMITS = exports.ACTION_EXECUTION_KINDS = exports.ENGINE_CONNECTOR_KEY = exports.ACTION_CONTRACTS = void 0;
3658
+ exports.isActionContract = isActionContract;
3659
+ exports.engineRunUrl = engineRunUrl;
3660
+ exports.actionAudienceRefusal = actionAudienceRefusal;
3661
+ exports.actionTokenScope = actionTokenScope;
3662
+ exports.redactSecrets = redactSecrets;
3663
+ exports.redactJwtShaped = redactJwtShaped;
3664
+ exports.carriesJwtShaped = carriesJwtShaped;
3665
+ exports.jsonByteLength = jsonByteLength;
3666
+ exports.parseActionInvocation = parseActionInvocation;
3667
+ exports.parseActionEvent = parseActionEvent;
3668
+ exports.tooManyEvents = tooManyEvents;
3669
+ exports.parseActionCheckpoint = parseActionCheckpoint;
3670
+ exports.parseCheckpointAck = parseCheckpointAck;
3671
+ exports.parseActionReport = parseActionReport;
3672
+ var wire_identity_1 = require_wire_identity();
3673
+ var package_execution_1 = require_package_execution();
3674
+ var validation_1 = require_validation();
3675
+ exports.ACTION_CONTRACTS = [
3676
+ wire_identity_1.ACTION_PROTOCOL_V1
3677
+ ];
3678
+ function isActionContract(value) {
3679
+ return typeof value === "string" && exports.ACTION_CONTRACTS.includes(value);
3680
+ }
3681
+ __name(isActionContract, "isActionContract");
3682
+ exports.ENGINE_CONNECTOR_KEY = "runveric.engine";
3683
+ exports.ACTION_EXECUTION_KINDS = [
3684
+ "engine"
3685
+ ];
3686
+ exports.ACTION_PROTOCOL_LIMITS = {
3687
+ maxEvents: 500,
3688
+ maxEventBytes: 2048,
3689
+ maxReportBytes: 262144,
3690
+ maxRestorePointBytes: 65536
3691
+ };
3692
+ exports.MAX_PLAN_VALIDATE_TARGETS = 200;
3693
+ exports.ACTION_CONTAINER_ENV = {
3694
+ runToken: "RUNVERIC_ACTION_RUN_TOKEN",
3695
+ callbackBaseUrl: "RUNVERIC_ACTION_CALLBACK_BASE_URL",
3696
+ runId: "RUNVERIC_ACTION_RUN_ID",
3697
+ imageDigest: "RUNVERIC_ACTION_IMAGE_DIGEST"
3698
+ };
3699
+ exports.ACTION_RUNTIME_PROBE_FLAG = "--probe";
3700
+ exports.ACTION_PROBE_ENV = {
3701
+ identities: "RUNVERIC_PROBE_IDENTITIES"
3702
+ };
3703
+ exports.ENGINE_RUN_ROUTES = {
3704
+ base: "internal/engine-runs",
3705
+ invocation: "invocation",
3706
+ events: "events",
3707
+ checkpoint: "checkpoint",
3708
+ report: "report"
3709
+ };
3710
+ function engineRunUrl(callbackBaseUrl, runId, message) {
3711
+ const origin2 = callbackBaseUrl.replace(/\/+$/, "");
3712
+ return `${origin2}/${exports.ENGINE_RUN_ROUTES.base}/${encodeURIComponent(runId)}/${exports.ENGINE_RUN_ROUTES[message]}`;
3713
+ }
3714
+ __name(engineRunUrl, "engineRunUrl");
3715
+ function actionAudienceRefusal(value) {
3716
+ if (typeof value === "string" && AUDIENCE_ORIGIN.test(value)) return null;
3717
+ return `"${String(value)}" is not an audience. An audience is the https origin of the resource a token is for \u2014 lowercase, with no port, path or trailing slash, e.g. "https://management.azure.com".`;
3718
+ }
3719
+ __name(actionAudienceRefusal, "actionAudienceRefusal");
3720
+ var AUDIENCE_ORIGIN = /^https:\/\/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;
3721
+ function actionTokenScope(audience) {
3722
+ return `${audience}/.default`;
3723
+ }
3724
+ __name(actionTokenScope, "actionTokenScope");
3725
+ exports.REDACTED_SECRET = "[redacted]";
3726
+ function redactSecrets(value, secrets) {
3727
+ const present = secrets.filter((secret) => secret.length > 0);
3728
+ if (present.length === 0) return value;
3729
+ return mapStrings(value, (text) => present.reduce((out, secret) => out.split(secret).join(exports.REDACTED_SECRET), text));
3730
+ }
3731
+ __name(redactSecrets, "redactSecrets");
3732
+ exports.JWT_SHAPE = /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/;
3733
+ function redactJwtShaped(value) {
3734
+ const everyMatch = new RegExp(exports.JWT_SHAPE.source, "g");
3735
+ return mapStrings(value, (text) => text.replace(everyMatch, exports.REDACTED_SECRET));
3736
+ }
3737
+ __name(redactJwtShaped, "redactJwtShaped");
3738
+ function carriesJwtShaped(value) {
3739
+ let found = false;
3740
+ mapStrings(value, (text) => {
3741
+ found ||= exports.JWT_SHAPE.test(text);
3742
+ return text;
3743
+ });
3744
+ return found;
3745
+ }
3746
+ __name(carriesJwtShaped, "carriesJwtShaped");
3747
+ function mapStrings(value, scrub) {
3748
+ const walk = /* @__PURE__ */ __name((node) => {
3749
+ if (typeof node === "string") return scrub(node);
3750
+ if (Array.isArray(node)) return node.map(walk);
3751
+ if (node !== null && typeof node === "object") {
3752
+ return Object.fromEntries(Object.entries(node).map(([key, item]) => [
3753
+ scrub(key),
3754
+ walk(item)
3755
+ ]));
3756
+ }
3757
+ return node;
3758
+ }, "walk");
3759
+ return walk(value);
3760
+ }
3761
+ __name(mapStrings, "mapStrings");
3762
+ function jsonByteLength(value) {
3763
+ const text = JSON.stringify(value) ?? "";
3764
+ let bytes = 0;
3765
+ for (const char of text) {
3766
+ const point = char.codePointAt(0) ?? 0;
3767
+ bytes += point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
3768
+ }
3769
+ return bytes;
3770
+ }
3771
+ __name(jsonByteLength, "jsonByteLength");
3772
+ var ActionProtocolError = class ActionProtocolError extends Error {
3773
+ static {
3774
+ __name(this, "ActionProtocolError");
3775
+ }
3776
+ constructor(message) {
3777
+ super(message);
3778
+ this.name = "ActionProtocolError";
3779
+ }
3780
+ };
3781
+ exports.ActionProtocolError = ActionProtocolError;
3782
+ function parseActionInvocation(body) {
3783
+ const raw = objectOf(body, "The invocation");
3784
+ onlyKeys(raw, INVOCATION_KEYS, "The invocation");
3785
+ if (!isActionContract(raw.protocol)) {
3786
+ throw new ActionProtocolError(`The invocation speaks ${String(raw.protocol)}, and this runtime speaks ${exports.ACTION_CONTRACTS.join(", ")}.`);
3787
+ }
3788
+ const phase = raw.phase;
3789
+ if (typeof phase !== "string" || !package_execution_1.PACKAGE_PHASES.includes(phase)) {
3790
+ throw new ActionProtocolError(`An invocation phase of "${String(phase)}" is not in the vocabulary.`);
3791
+ }
3792
+ const actionRaw = objectOf(raw.action, "The invocation's action");
3793
+ onlyKeys(actionRaw, [
3794
+ "key",
3795
+ "version",
3796
+ "image"
3797
+ ], "The invocation's action");
3798
+ const action = {
3799
+ key: nonEmpty(actionRaw.key, "The action's key"),
3800
+ version: nonEmpty(actionRaw.version, "The action's version"),
3801
+ image: digestPinned(actionRaw.image)
3802
+ };
3803
+ const executionRaw = objectOf(raw.execution, "The invocation's execution");
3804
+ onlyKeys(executionRaw, [
3805
+ "id",
3806
+ "tenantId",
3807
+ "correlationId",
3808
+ "dryRun"
3809
+ ], "The invocation's execution");
3810
+ if (typeof executionRaw.dryRun !== "boolean") {
3811
+ throw new ActionProtocolError("The invocation's execution must say whether it is a dry run.");
3812
+ }
3813
+ const execution = {
3814
+ id: nonEmpty(executionRaw.id, "The execution's id"),
3815
+ tenantId: nonEmpty(executionRaw.tenantId, "The execution's tenant"),
3816
+ correlationId: nonEmpty(executionRaw.correlationId, "The execution's correlation id"),
3817
+ dryRun: executionRaw.dryRun
3818
+ };
3819
+ const limits = parseLimits(raw.limits);
3820
+ const decisions = parseDecisions(raw.decisions);
3821
+ const targets = parseTargets(raw.targets);
3822
+ const credentials = {
3823
+ tokens: parseTokens(objectOf(raw.credentials, "The invocation's credentials"))
3824
+ };
3825
+ const restorePoint = nullableObject(raw.restorePoint, "The invocation's restore point");
3826
+ const priorResult = nullableObject(raw.priorResult, "The invocation's prior result");
3827
+ if (restorePoint !== null && jsonByteLength(restorePoint) > limits.maxRestorePointBytes) {
3828
+ throw new ActionProtocolError(`The invocation's restore point is larger than ${limits.maxRestorePointBytes} bytes.`);
3829
+ }
3830
+ const base = {
3831
+ protocol: raw.protocol,
3832
+ action,
3833
+ execution,
3834
+ targets,
3835
+ credentials,
3836
+ limits
3837
+ };
3838
+ if (phase === "validate") {
3839
+ if (raw.mode !== "plan" && raw.mode !== "run") {
3840
+ throw new ActionProtocolError(`A validate invocation's mode of "${String(raw.mode)}" is not "plan" or "run".`);
3841
+ }
3842
+ if (raw.mode !== decisions.phase) {
3843
+ throw new ActionProtocolError(`A validate invocation in ${raw.mode} mode carries ${decisions.phase}-phase decisions; the two are the same fact.`);
3844
+ }
3845
+ assertTargetCount(targets.length, raw.mode === "plan" ? exports.MAX_PLAN_VALIDATE_TARGETS : 1, `A ${raw.mode}-mode validate`);
3846
+ if (restorePoint !== null || priorResult !== null) {
3847
+ throw new ActionProtocolError("A validate invocation carries no restore point and no prior result.");
3848
+ }
3849
+ return {
3850
+ ...base,
3851
+ phase,
3852
+ mode: raw.mode,
3853
+ decisions,
3854
+ restorePoint: null,
3855
+ priorResult: null
3856
+ };
3857
+ }
3858
+ if (raw.mode !== void 0) {
3859
+ throw new ActionProtocolError(`A ${phase} invocation has no mode \u2014 only a validate is asked in a mode.`);
3860
+ }
3861
+ if (decisions.phase !== "run") {
3862
+ throw new ActionProtocolError(`A ${phase} invocation carries a run's decisions, never a plan's.`);
3863
+ }
3864
+ assertTargetCount(targets.length, 1, `A ${phase}`);
3865
+ if (phase === "run") {
3866
+ if (priorResult !== null) {
3867
+ throw new ActionProtocolError("A run invocation carries no prior result \u2014 only a rollback reads one.");
3868
+ }
3869
+ return {
3870
+ ...base,
3871
+ phase,
3872
+ decisions,
3873
+ restorePoint,
3874
+ priorResult: null
3875
+ };
3876
+ }
3877
+ return {
3878
+ ...base,
3879
+ phase: "rollback",
3880
+ decisions,
3881
+ restorePoint,
3882
+ priorResult
3883
+ };
3884
+ }
3885
+ __name(parseActionInvocation, "parseActionInvocation");
3886
+ function parseActionEvent(body, limits, alreadyReceived) {
3887
+ if (alreadyReceived >= limits.maxEvents) throw tooManyEvents(limits);
3888
+ const raw = objectOf(body, "An event");
3889
+ onlyKeys(raw, [
3890
+ "level",
3891
+ "message",
3892
+ "data"
3893
+ ], "An event");
3894
+ if (jsonByteLength(raw) > limits.maxEventBytes) {
3895
+ throw new ActionProtocolError(`An event is larger than ${limits.maxEventBytes} bytes.`);
3896
+ }
3897
+ if (typeof raw.level !== "string" || !Object.hasOwn(EVENT_LEVELS, raw.level)) {
3898
+ throw new ActionProtocolError(`An event level of "${String(raw.level)}" is not in the vocabulary.`);
3899
+ }
3900
+ const message = nonEmpty(raw.message, "An event's message");
3901
+ if (raw.data === void 0) return {
3902
+ level: raw.level,
3903
+ message
3904
+ };
3905
+ return {
3906
+ level: raw.level,
3907
+ message,
3908
+ data: jsonObject(raw.data, "An event's data")
3909
+ };
3910
+ }
3911
+ __name(parseActionEvent, "parseActionEvent");
3912
+ function tooManyEvents(limits) {
3913
+ return new ActionProtocolError(`The action sent more than ${limits.maxEvents} events.`);
3914
+ }
3915
+ __name(tooManyEvents, "tooManyEvents");
3916
+ function parseActionCheckpoint(body, limits) {
3917
+ const raw = objectOf(body, "A checkpoint");
3918
+ onlyKeys(raw, [
3919
+ "kind",
3920
+ "record"
3921
+ ], "A checkpoint");
3922
+ if (raw.kind !== "restore_point") {
3923
+ throw new ActionProtocolError(`A checkpoint of kind "${String(raw.kind)}" is not one this contract knows.`);
3924
+ }
3925
+ const record = jsonObject(raw.record, "A checkpoint's record");
3926
+ if (jsonByteLength(record) > limits.maxRestorePointBytes) {
3927
+ throw new ActionProtocolError(`A restore point is larger than ${limits.maxRestorePointBytes} bytes.`);
3928
+ }
3929
+ return {
3930
+ kind: "restore_point",
3931
+ record
3932
+ };
3933
+ }
3934
+ __name(parseActionCheckpoint, "parseActionCheckpoint");
3935
+ function parseCheckpointAck(body) {
3936
+ const raw = objectOf(body, "A checkpoint acknowledgement");
3937
+ onlyKeys(raw, [
3938
+ "acknowledged"
3939
+ ], "A checkpoint acknowledgement");
3940
+ if (raw.acknowledged !== true) {
3941
+ throw new ActionProtocolError("The checkpoint was not acknowledged.");
3942
+ }
3943
+ return {
3944
+ acknowledged: true
3945
+ };
3946
+ }
3947
+ __name(parseCheckpointAck, "parseCheckpointAck");
3948
+ function parseActionReport(body, sent) {
3949
+ const raw = objectOf(body, "The report");
3950
+ onlyKeys(raw, [
3951
+ "phase",
3952
+ "checks",
3953
+ "outcome"
3954
+ ], "The report");
3955
+ if (jsonByteLength(raw) > sent.limits.maxReportBytes) {
3956
+ throw new ActionProtocolError(`The report is larger than ${sent.limits.maxReportBytes} bytes.`);
3957
+ }
3958
+ if (raw.phase !== sent.phase) {
3959
+ throw new ActionProtocolError(`The report is for phase "${String(raw.phase)}", but this invocation is for "${sent.phase}".`);
3960
+ }
3961
+ if (sent.phase === "validate") {
3962
+ if (raw.checks === void 0 === (raw.outcome === void 0)) {
3963
+ throw new ActionProtocolError("A validate report carries checks, or \u2014 when they could not run \u2014 a failed outcome; exactly one of the two.");
3964
+ }
3965
+ if (raw.outcome !== void 0) {
3966
+ const outcome = parseOutcome(raw.outcome);
3967
+ if (outcome.status !== "failed") {
3968
+ throw new ActionProtocolError(`A validate report's outcome can only say the checks failed to run, never "${outcome.status}" \u2014 the engine decides what checks mean.`);
3969
+ }
3970
+ return {
3971
+ phase: "validate",
3972
+ outcome
3973
+ };
3974
+ }
3975
+ const byRef = objectOf(raw.checks, "A validate report's checks");
3976
+ const expected = new Set(sent.targets.map((target) => target.ref));
3977
+ for (const ref of Object.keys(byRef)) {
3978
+ if (!expected.has(ref)) {
3979
+ throw new ActionProtocolError(`The report answers for "${ref}", which is not one of the targets it was given.`);
3980
+ }
3981
+ }
3982
+ const checks = sent.targets.map(({ ref }) => {
3983
+ if (!Object.hasOwn(byRef, ref)) {
3984
+ throw new ActionProtocolError(`The report says nothing about "${ref}", one of the targets it was given.`);
3985
+ }
3986
+ const list = byRef[ref];
3987
+ if (!Array.isArray(list)) {
3988
+ throw new ActionProtocolError(`The checks for "${ref}" must be an array.`);
3989
+ }
3990
+ return {
3991
+ ref,
3992
+ checks: list.map((entry) => {
3993
+ const parsed = (0, validation_1.validationCheckOf)(entry);
3994
+ if ("refusal" in parsed) throw new ActionProtocolError(`The checks for "${ref}": ${parsed.refusal}`);
3995
+ return parsed.check;
3996
+ })
3997
+ };
3998
+ });
3999
+ return {
4000
+ phase: "validate",
4001
+ checks
4002
+ };
4003
+ }
4004
+ if (raw.checks !== void 0) {
4005
+ throw new ActionProtocolError(`A ${sent.phase} report carries an outcome, never checks \u2014 those are the validate phase's answer.`);
4006
+ }
4007
+ return {
4008
+ phase: sent.phase,
4009
+ outcome: parseOutcome(raw.outcome)
4010
+ };
4011
+ }
4012
+ __name(parseActionReport, "parseActionReport");
4013
+ var INVOCATION_KEYS = [
4014
+ "protocol",
4015
+ "action",
4016
+ "phase",
4017
+ "mode",
4018
+ "execution",
4019
+ "targets",
4020
+ "decisions",
4021
+ "restorePoint",
4022
+ "priorResult",
4023
+ "credentials",
4024
+ "limits"
4025
+ ];
4026
+ var EVENT_LEVELS = {
4027
+ info: true,
4028
+ warn: true,
4029
+ error: true,
4030
+ success: true
4031
+ };
4032
+ var OUTCOME_STATUSES = {
4033
+ succeeded: true,
4034
+ failed: true,
4035
+ cancelled: true
4036
+ };
4037
+ function parseOutcome(value) {
4038
+ if (value === void 0) {
4039
+ throw new ActionProtocolError("A run or rollback report must carry an outcome.");
4040
+ }
4041
+ const raw = objectOf(value, "An outcome");
4042
+ onlyKeys(raw, [
4043
+ "status",
4044
+ "message",
4045
+ "data"
4046
+ ], "An outcome");
4047
+ if (typeof raw.status !== "string" || !Object.hasOwn(OUTCOME_STATUSES, raw.status)) {
4048
+ throw new ActionProtocolError(`An outcome status of "${String(raw.status)}" is not in the vocabulary.`);
4049
+ }
4050
+ const status = raw.status;
4051
+ const message = nonEmpty(raw.message, "An outcome's message");
4052
+ if (raw.data === void 0) return {
4053
+ status,
4054
+ message
4055
+ };
4056
+ return {
4057
+ status,
4058
+ message,
4059
+ data: jsonObject(raw.data, "An outcome's data")
4060
+ };
4061
+ }
4062
+ __name(parseOutcome, "parseOutcome");
4063
+ function parseLimits(value) {
4064
+ const raw = objectOf(value, "The invocation's limits");
4065
+ onlyKeys(raw, [
4066
+ "deadline",
4067
+ "maxEvents",
4068
+ "maxEventBytes",
4069
+ "maxReportBytes",
4070
+ "maxRestorePointBytes"
4071
+ ], "The invocation's limits");
4072
+ return {
4073
+ deadline: isoInstant(raw.deadline, "The deadline"),
4074
+ maxEvents: positiveInteger(raw.maxEvents, "maxEvents"),
4075
+ maxEventBytes: positiveInteger(raw.maxEventBytes, "maxEventBytes"),
4076
+ maxReportBytes: positiveInteger(raw.maxReportBytes, "maxReportBytes"),
4077
+ maxRestorePointBytes: positiveInteger(raw.maxRestorePointBytes, "maxRestorePointBytes")
4078
+ };
4079
+ }
4080
+ __name(parseLimits, "parseLimits");
4081
+ function parseDecisions(value) {
4082
+ const raw = objectOf(value, "The invocation's decisions");
4083
+ if (raw.phase === "plan") {
4084
+ onlyKeys(raw, [
4085
+ "phase"
4086
+ ], "A plan's decisions");
4087
+ return {
4088
+ phase: "plan"
4089
+ };
4090
+ }
4091
+ if (raw.phase !== "run") {
4092
+ throw new ActionProtocolError(`A decisions phase of "${String(raw.phase)}" is not "plan" or "run".`);
4093
+ }
4094
+ onlyKeys(raw, [
4095
+ "phase",
4096
+ "recorded"
4097
+ ], "A run's decisions");
4098
+ if (!Array.isArray(raw.recorded)) {
4099
+ throw new ActionProtocolError("A run's decisions must carry the recorded list, even when it is empty.");
4100
+ }
4101
+ const recorded = raw.recorded.map((entry) => {
4102
+ const decision = objectOf(entry, "A recorded decision");
4103
+ onlyKeys(decision, [
4104
+ "code",
4105
+ "acknowledgedBy",
4106
+ "acknowledgedAt"
4107
+ ], "A recorded decision");
4108
+ if (!(0, validation_1.isDecisionCode)(decision.code)) {
4109
+ throw new ActionProtocolError(`A recorded decision of "${String(decision.code)}" is not in the vocabulary.`);
4110
+ }
4111
+ return {
4112
+ code: decision.code,
4113
+ acknowledgedBy: nonEmpty(decision.acknowledgedBy, "Who acknowledged a decision"),
4114
+ acknowledgedAt: isoInstant(decision.acknowledgedAt, "When a decision was acknowledged")
4115
+ };
4116
+ });
4117
+ if (new Set(recorded.map((decision) => decision.code)).size !== recorded.length) {
4118
+ throw new ActionProtocolError("A run's decisions record one code twice.");
4119
+ }
4120
+ return {
4121
+ phase: "run",
4122
+ recorded
4123
+ };
4124
+ }
4125
+ __name(parseDecisions, "parseDecisions");
4126
+ function parseTargets(value) {
4127
+ if (!Array.isArray(value)) {
4128
+ throw new ActionProtocolError("The invocation's targets must be an array.");
4129
+ }
4130
+ const targets = value.map((entry) => {
4131
+ const raw = objectOf(entry, "A target");
4132
+ onlyKeys(raw, [
4133
+ "ref",
4134
+ "parameters"
4135
+ ], "A target");
4136
+ return {
4137
+ ref: nonEmpty(raw.ref, "A target's ref"),
4138
+ parameters: jsonObject(raw.parameters, "A target's parameters")
4139
+ };
4140
+ });
4141
+ if (new Set(targets.map((target) => target.ref)).size !== targets.length) {
4142
+ throw new ActionProtocolError("The invocation names one target twice.");
4143
+ }
4144
+ return targets;
4145
+ }
4146
+ __name(parseTargets, "parseTargets");
4147
+ function assertTargetCount(count, max, what) {
4148
+ if (count < 1 || count > max) {
4149
+ throw new ActionProtocolError(max === 1 ? `${what} acts on exactly one target; this invocation names ${count}.` : `${what} checks between 1 and ${max} targets; this invocation names ${count}.`);
4150
+ }
4151
+ }
4152
+ __name(assertTargetCount, "assertTargetCount");
4153
+ function parseTokens(credentials) {
4154
+ onlyKeys(credentials, [
4155
+ "tokens"
4156
+ ], "The invocation's credentials");
4157
+ if (!Array.isArray(credentials.tokens)) {
4158
+ throw new ActionProtocolError("The invocation's credentials must carry a tokens array, even when it is empty.");
4159
+ }
4160
+ const tokens2 = credentials.tokens.map((entry) => {
4161
+ const raw = objectOf(entry, "A token");
4162
+ onlyKeys(raw, [
4163
+ "audience",
4164
+ "token",
4165
+ "expiresOn"
4166
+ ], "A token");
4167
+ const refusal = actionAudienceRefusal(raw.audience);
4168
+ if (refusal !== null) throw new ActionProtocolError(refusal);
4169
+ return {
4170
+ audience: raw.audience,
4171
+ token: nonEmpty(raw.token, "A token's value"),
4172
+ expiresOn: isoInstant(raw.expiresOn, "A token's expiry")
4173
+ };
4174
+ });
4175
+ if (new Set(tokens2.map((token2) => token2.audience)).size !== tokens2.length) {
4176
+ throw new ActionProtocolError("The invocation carries two tokens for one audience.");
4177
+ }
4178
+ return tokens2;
4179
+ }
4180
+ __name(parseTokens, "parseTokens");
4181
+ var DIGEST_PINNED_IMAGE = /^[a-z0-9][a-z0-9._\-/:]*@sha256:[0-9a-f]{64}$/;
4182
+ function digestPinned(value) {
4183
+ if (typeof value !== "string" || !DIGEST_PINNED_IMAGE.test(value)) {
4184
+ throw new ActionProtocolError(`The action's image "${String(value)}" is not addressed by digest. Nothing on the execution path resolves a tag (docs/128 \xA713.2).`);
4185
+ }
4186
+ return value;
4187
+ }
4188
+ __name(digestPinned, "digestPinned");
4189
+ function objectOf(value, what) {
4190
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
4191
+ throw new ActionProtocolError(`${what} must be an object.`);
4192
+ }
4193
+ return value;
4194
+ }
4195
+ __name(objectOf, "objectOf");
4196
+ function onlyKeys(raw, allowed, what) {
4197
+ for (const key of Object.keys(raw)) {
4198
+ if (!allowed.includes(key)) {
4199
+ throw new ActionProtocolError(`${what} carries "${key}", which this contract does not have.`);
4200
+ }
4201
+ }
4202
+ }
4203
+ __name(onlyKeys, "onlyKeys");
4204
+ function nonEmpty(value, what) {
4205
+ if (typeof value !== "string" || value.length === 0) {
4206
+ throw new ActionProtocolError(`${what} must be a non-empty string.`);
4207
+ }
4208
+ return value;
4209
+ }
4210
+ __name(nonEmpty, "nonEmpty");
4211
+ function positiveInteger(value, what) {
4212
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
4213
+ throw new ActionProtocolError(`The limit "${what}" must be a positive integer.`);
4214
+ }
4215
+ return value;
4216
+ }
4217
+ __name(positiveInteger, "positiveInteger");
4218
+ function isoInstant(value, what) {
4219
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
4220
+ throw new ActionProtocolError(`${what} must be an ISO-8601 instant.`);
4221
+ }
4222
+ return value;
4223
+ }
4224
+ __name(isoInstant, "isoInstant");
4225
+ function nullableObject(value, what) {
4226
+ return value === null ? null : jsonObject(value, what);
4227
+ }
4228
+ __name(nullableObject, "nullableObject");
4229
+ function jsonObject(value, what) {
4230
+ if (!isJsonObject(value)) {
4231
+ throw new ActionProtocolError(`${what} must be a JSON object.`);
4232
+ }
4233
+ return value;
4234
+ }
4235
+ __name(jsonObject, "jsonObject");
4236
+ function isJsonObject(value) {
4237
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
4238
+ const prototype = Object.getPrototypeOf(value);
4239
+ if (prototype !== Object.prototype && prototype !== null) return false;
4240
+ return Object.values(value).every(isJsonValue);
4241
+ }
4242
+ __name(isJsonObject, "isJsonObject");
4243
+ function isJsonValue(value) {
4244
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
4245
+ if (typeof value === "number") return Number.isFinite(value);
4246
+ if (Array.isArray(value)) return value.every(isJsonValue);
4247
+ return isJsonObject(value);
4248
+ }
4249
+ __name(isJsonValue, "isJsonValue");
4250
+ }
4251
+ });
4252
+
4253
+ // ../contracts/dist/action-signing.js
4254
+ var require_action_signing = __commonJS({
4255
+ "../contracts/dist/action-signing.js"(exports) {
4256
+ "use strict";
4257
+ Object.defineProperty(exports, "__esModule", {
4258
+ value: true
4259
+ });
4260
+ exports.ACTION_SIGNATURE_REPOSITORY = exports.IMAGE_REVISION_LABEL = exports.IMAGE_SOURCE_LABEL = exports.COSIGN_SIGN_PREDICATE_TYPE = exports.IN_TOTO_STATEMENT_TYPE = exports.DSSE_IN_TOTO_PAYLOAD_TYPE = exports.SIGSTORE_BUNDLE_MEDIA_TYPE = void 0;
4261
+ exports.SIGSTORE_BUNDLE_MEDIA_TYPE = "application/vnd.dev.sigstore.bundle.v0.3+json";
4262
+ exports.DSSE_IN_TOTO_PAYLOAD_TYPE = "application/vnd.in-toto+json";
4263
+ exports.IN_TOTO_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
4264
+ exports.COSIGN_SIGN_PREDICATE_TYPE = "https://sigstore.dev/cosign/sign/v1";
4265
+ exports.IMAGE_SOURCE_LABEL = "org.opencontainers.image.source";
4266
+ exports.IMAGE_REVISION_LABEL = "org.opencontainers.image.revision";
4267
+ exports.ACTION_SIGNATURE_REPOSITORY = "ghcr.io/runveric/action-signatures";
4268
+ }
4269
+ });
4270
+
4271
+ // ../contracts/dist/oci-media-types.js
4272
+ var require_oci_media_types = __commonJS({
4273
+ "../contracts/dist/oci-media-types.js"(exports) {
4274
+ "use strict";
4275
+ Object.defineProperty(exports, "__esModule", {
4276
+ value: true
4277
+ });
4278
+ exports.IMAGE_MANIFEST_MEDIA_TYPES = exports.MANIFEST_MEDIA_TYPES = exports.DOCKER_MANIFEST_LIST = exports.OCI_IMAGE_INDEX = exports.DOCKER_IMAGE_MANIFEST = exports.OCI_IMAGE_MANIFEST = void 0;
4279
+ exports.OCI_IMAGE_MANIFEST = "application/vnd.oci.image.manifest.v1+json";
4280
+ exports.DOCKER_IMAGE_MANIFEST = "application/vnd.docker.distribution.manifest.v2+json";
4281
+ exports.OCI_IMAGE_INDEX = "application/vnd.oci.image.index.v1+json";
4282
+ exports.DOCKER_MANIFEST_LIST = "application/vnd.docker.distribution.manifest.list.v2+json";
4283
+ exports.MANIFEST_MEDIA_TYPES = [
4284
+ exports.OCI_IMAGE_MANIFEST,
4285
+ exports.DOCKER_IMAGE_MANIFEST,
4286
+ exports.OCI_IMAGE_INDEX,
4287
+ exports.DOCKER_MANIFEST_LIST
4288
+ ];
4289
+ exports.IMAGE_MANIFEST_MEDIA_TYPES = [
4290
+ exports.OCI_IMAGE_MANIFEST,
4291
+ exports.DOCKER_IMAGE_MANIFEST
4292
+ ];
4293
+ }
4294
+ });
4295
+
4296
+ // ../contracts/dist/action-image.js
4297
+ var require_action_image = __commonJS({
4298
+ "../contracts/dist/action-image.js"(exports) {
4299
+ "use strict";
4300
+ Object.defineProperty(exports, "__esModule", {
4301
+ value: true
4302
+ });
4303
+ exports.MAX_IMAGE_MANIFEST_BYTES = exports.ACTION_RUNTIME_IMAGE = exports.ACTION_RUNTIME_REPOSITORY = void 0;
4304
+ exports.imageConfigDescriptor = imageConfigDescriptor2;
4305
+ exports.actionImageRefusal = actionImageRefusal2;
4306
+ var action_repository_1 = require_action_repository();
4307
+ var action_signing_1 = require_action_signing();
4308
+ var oci_media_types_1 = require_oci_media_types();
4309
+ exports.ACTION_RUNTIME_REPOSITORY = "ghcr.io/runveric/action-runtime";
4310
+ exports.ACTION_RUNTIME_IMAGE = `${exports.ACTION_RUNTIME_REPOSITORY}@sha256:0774c0768c6d1b11e7224ce5bca925549e369fed75eeb412531c1dce10bc8a8a`;
4311
+ exports.MAX_IMAGE_MANIFEST_BYTES = 64 * 1024;
4312
+ var isRecord = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
4313
+ function imageConfigDescriptor2(manifest) {
4314
+ if (!isRecord(manifest) || typeof manifest.mediaType !== "string") return null;
4315
+ if (!oci_media_types_1.IMAGE_MANIFEST_MEDIA_TYPES.includes(manifest.mediaType) || manifest.schemaVersion !== 2) return null;
4316
+ const config = manifest.config;
4317
+ if (!isRecord(config) || typeof config.digest !== "string") return null;
4318
+ if (typeof config.size !== "number" || !Number.isInteger(config.size) || config.size < 0) return null;
4319
+ return {
4320
+ digest: config.digest,
4321
+ size: config.size
4322
+ };
4323
+ }
4324
+ __name(imageConfigDescriptor2, "imageConfigDescriptor");
4325
+ function actionImageRefusal2(input) {
4326
+ const { key, image, manifest, config, provenance: provenance2 } = input;
4327
+ const mediaType = isRecord(manifest) ? manifest.mediaType : void 0;
4328
+ if (typeof mediaType !== "string" || !oci_media_types_1.IMAGE_MANIFEST_MEDIA_TYPES.includes(mediaType)) {
4329
+ return `${image} is ${typeof mediaType === "string" ? `a ${mediaType}` : "a manifest of no declared type"}, not one image. An engine action's digest names exactly one linux/amd64 image manifest \u2014 never an index of several.`;
4330
+ }
4331
+ if (imageConfigDescriptor2(manifest) === null) {
4332
+ return `${image} is not an image manifest \u2014 it names no config descriptor (schemaVersion 2, a digest and a size).`;
4333
+ }
4334
+ if (!isRecord(config)) {
4335
+ return `${image}'s config is not a JSON object, so it cannot say what it was built for.`;
4336
+ }
4337
+ const platform = `${String(config.os)}/${String(config.architecture)}`;
4338
+ if (platform !== "linux/amd64") {
4339
+ return `${image} is built for ${platform}; an engine action runs on linux/amd64, which is what Container Apps Jobs run.`;
4340
+ }
4341
+ const labels = isRecord(config.config) && isRecord(config.config.Labels) ? config.config.Labels : {};
4342
+ const expected = (0, action_repository_1.actionSourceRepository)(key);
4343
+ const source = labels[action_signing_1.IMAGE_SOURCE_LABEL];
4344
+ if (source !== expected) {
4345
+ return `the image of action "${key}" says it was built in ${source === void 0 ? "no repository (it carries no " + action_signing_1.IMAGE_SOURCE_LABEL + " label)" : JSON.stringify(source)}, not ${expected}. A signature binds a digest and never a name, so an image built for another action cannot run under this one's manifest.`;
4346
+ }
4347
+ if (provenance2.repository !== expected) {
4348
+ return `the artifact of action "${key}" says it was authored in ${JSON.stringify(provenance2.repository)}, not ${expected}, which its image names.`;
4349
+ }
4350
+ const revision = labels[action_signing_1.IMAGE_REVISION_LABEL];
4351
+ if (revision !== provenance2.commit) {
4352
+ return `the image of action "${key}" says it was built at ${revision === void 0 ? "no commit (it carries no " + action_signing_1.IMAGE_REVISION_LABEL + " label)" : JSON.stringify(revision)}, not ${provenance2.commit}, the commit its artifact was published from. An image and the manifest it runs under come from one commit, so neither can be swapped for another's.`;
4353
+ }
4354
+ return null;
4355
+ }
4356
+ __name(actionImageRefusal2, "actionImageRefusal");
4357
+ }
4358
+ });
4359
+
2630
4360
  // ../contracts/dist/runner-pool-presence.js
2631
4361
  var require_runner_pool_presence = __commonJS({
2632
4362
  "../contracts/dist/runner-pool-presence.js"(exports) {
@@ -3385,11 +5115,11 @@ var require_workspace_domain = __commonJS({
3385
5115
  "held_elsewhere"
3386
5116
  ];
3387
5117
  exports.DOMAIN_VERIFICATION_RECORD_PREFIX = "runveric-domain-verification=";
3388
- function domainVerificationRecord(domain, token) {
5118
+ function domainVerificationRecord(domain, token2) {
3389
5119
  return {
3390
5120
  name: domain,
3391
5121
  type: "TXT",
3392
- value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${token}`
5122
+ value: `${exports.DOMAIN_VERIFICATION_RECORD_PREFIX}${token2}`
3393
5123
  };
3394
5124
  }
3395
5125
  __name(domainVerificationRecord, "domainVerificationRecord");
@@ -3572,9 +5302,16 @@ var require_dist = __commonJS({
3572
5302
  __exportStar(require_tenant_users(), exports);
3573
5303
  __exportStar(require_invitations(), exports);
3574
5304
  __exportStar(require_groups(), exports);
5305
+ __exportStar(require_catalog_policy(), exports);
3575
5306
  __exportStar(require_catalog(), exports);
5307
+ __exportStar(require_catalog_release(), exports);
5308
+ __exportStar(require_action_repository(), exports);
5309
+ __exportStar(require_catalog_release_spec(), exports);
5310
+ __exportStar(require_catalog_lock(), exports);
5311
+ __exportStar(require_catalog_manifest_placeholders(), exports);
3576
5312
  __exportStar(require_change_control(), exports);
3577
5313
  __exportStar(require_execution(), exports);
5314
+ __exportStar(require_decisions(), exports);
3578
5315
  __exportStar(require_execution_site(), exports);
3579
5316
  __exportStar(require_run_placement(), exports);
3580
5317
  __exportStar(require_connections(), exports);
@@ -3585,6 +5322,7 @@ var require_dist = __commonJS({
3585
5322
  __exportStar(require_scheduling(), exports);
3586
5323
  __exportStar(require_operations(), exports);
3587
5324
  __exportStar(require_runs(), exports);
5325
+ __exportStar(require_evidence_pack(), exports);
3588
5326
  __exportStar(require_package_tier(), exports);
3589
5327
  __exportStar(require_package_provenance(), exports);
3590
5328
  __exportStar(require_packages(), exports);
@@ -3594,6 +5332,11 @@ var require_dist = __commonJS({
3594
5332
  __exportStar(require_runner_install(), exports);
3595
5333
  __exportStar(require_authoring_cli(), exports);
3596
5334
  __exportStar(require_runners(), exports);
5335
+ __exportStar(require_scoped_credential(), exports);
5336
+ __exportStar(require_action_protocol(), exports);
5337
+ __exportStar(require_action_signing(), exports);
5338
+ __exportStar(require_oci_media_types(), exports);
5339
+ __exportStar(require_action_image(), exports);
3597
5340
  __exportStar(require_runner_pool_presence(), exports);
3598
5341
  __exportStar(require_routable_target(), exports);
3599
5342
  __exportStar(require_runner_pool_routing(), exports);
@@ -3831,6 +5574,7 @@ var require_manifest_validator = __commonJS({
3831
5574
  exports.canonicalJson = canonicalJson;
3832
5575
  exports.sha256Hex = sha256Hex;
3833
5576
  exports.isSafeRelativePath = isSafeRelativePath;
5577
+ exports.isEgressDestination = isEgressDestination;
3834
5578
  var node_crypto_1 = __require("crypto");
3835
5579
  var node_net_1 = __require("net");
3836
5580
  var contracts_1 = require_dist();
@@ -5095,7 +6839,7 @@ var require_authoring_lint = __commonJS({
5095
6839
  if (!isRecord(permission) || !Array.isArray(permission.actions)) return;
5096
6840
  permission.actions.forEach((action, actionIndex) => {
5097
6841
  if (typeof action !== "string" || action.includes("*")) return;
5098
- if (permissionTokens(action).some((token) => haystack.includes(token))) return;
6842
+ if (permissionTokens(action).some((token2) => haystack.includes(token2))) return;
5099
6843
  findings.push({
5100
6844
  severity: "warning",
5101
6845
  rule: "package/permission-possibly-unused",
@@ -5242,7 +6986,7 @@ var require_parameters_location = __commonJS({
5242
6986
  });
5243
6987
  exports.resolveParametersFile = resolveParametersFile;
5244
6988
  exports.resolveParametersFileOf = resolveParametersFileOf;
5245
- exports.readManifest = readManifest;
6989
+ exports.readManifest = readManifest2;
5246
6990
  var yaml_1 = __require("yaml");
5247
6991
  var manifest_validator_1 = require_manifest_validator();
5248
6992
  var package_validation_1 = require_package_validation();
@@ -5253,7 +6997,7 @@ var require_parameters_location = __commonJS({
5253
6997
  reason: "manifest_missing"
5254
6998
  };
5255
6999
  }
5256
- const manifest = readManifest(manifestSource);
7000
+ const manifest = readManifest2(manifestSource);
5257
7001
  if (!manifest) return {
5258
7002
  resolved: false,
5259
7003
  reason: "manifest_unparseable"
@@ -5281,7 +7025,7 @@ var require_parameters_location = __commonJS({
5281
7025
  return resolveParametersFile(files.get(package_validation_1.MANIFEST_FILE));
5282
7026
  }
5283
7027
  __name(resolveParametersFileOf, "resolveParametersFileOf");
5284
- function readManifest(source) {
7028
+ function readManifest2(source) {
5285
7029
  try {
5286
7030
  const document = (0, yaml_1.parseDocument)(source);
5287
7031
  if (document.errors.length > 0) return void 0;
@@ -5291,7 +7035,7 @@ var require_parameters_location = __commonJS({
5291
7035
  return void 0;
5292
7036
  }
5293
7037
  }
5294
- __name(readManifest, "readManifest");
7038
+ __name(readManifest2, "readManifest");
5295
7039
  }
5296
7040
  });
5297
7041
 
@@ -6623,7 +8367,8 @@ var require_dist2 = __commonJS({
6623
8367
  Object.defineProperty(exports, "__esModule", {
6624
8368
  value: true
6625
8369
  });
6626
- exports.scaffoldActionPackage = exports.reservedImportPaths = exports.generateImportedPackage = exports.authoredEntrypointOf = exports.authoredBodyPath = exports.powerShellDetection = exports.parseProbeOutput = exports.mapDeclaredParameters = exports.POWERSHELL_PARAM_PROBE = exports.pathSegments = exports.offsetToLocation = exports.locate = exports.interpreterForPath = exports.interpreterEntryFileList = exports.PACKAGE_INTERPRETER_EXTENSIONS = exports.PACKAGE_INTERPRETER_LAUNCH = exports.lintActionPackage = exports.sortFindings = exports.mergeFindings = exports.findingFromValidation = exports.resolveParametersFileOf = exports.resolveParametersFile = exports.readManifest = exports.checkActionPackage = exports.validateActionPackage = exports.REQUIRED_PACKAGE_FILES = exports.MANIFEST_FILE = exports.validatePackageResult = exports.PackageProtocolError = exports.normalizePackageRuntime = exports.checkOciImageReference = exports.PackageRuntimeDeclarationError = exports.PARAMETER_CONTROLS = exports.PARAMETER_PROPERTY_FIELDS = exports.PARAMETER_SCHEMA_FIELDS = exports.CREDENTIAL_SCOPE_PATTERN = exports.SPDX_ID_PATTERN = exports.DURATION_PATTERN = exports.SEMVER_PATTERN = exports.PACKAGE_ID_PATTERN = exports.MANIFEST_FIELDS = exports.RISK_LEVELS = exports.PROVIDERS = exports.ACTION_CATEGORIES = exports.isSafeRelativePath = exports.sha256Hex = exports.canonicalJson = exports.validateParameterSchema = exports.validateManifest = exports.parseAndValidateManifest = void 0;
8370
+ exports.reservedImportPaths = exports.generateImportedPackage = exports.authoredEntrypointOf = exports.authoredBodyPath = exports.powerShellDetection = exports.parseProbeOutput = exports.mapDeclaredParameters = exports.POWERSHELL_PARAM_PROBE = exports.pathSegments = exports.offsetToLocation = exports.locate = exports.interpreterForPath = exports.interpreterEntryFileList = exports.PACKAGE_INTERPRETER_EXTENSIONS = exports.PACKAGE_INTERPRETER_LAUNCH = exports.lintActionPackage = exports.sortFindings = exports.mergeFindings = exports.findingFromValidation = exports.resolveParametersFileOf = exports.resolveParametersFile = exports.readManifest = exports.checkActionPackage = exports.validateActionPackage = exports.REQUIRED_PACKAGE_FILES = exports.MANIFEST_FILE = exports.validatePackageResult = exports.PackageProtocolError = exports.normalizePackageRuntime = exports.checkOciImageReference = exports.PackageRuntimeDeclarationError = exports.PARAMETER_CONTROLS = exports.PARAMETER_PROPERTY_FIELDS = exports.PARAMETER_SCHEMA_FIELDS = exports.CREDENTIAL_SCOPE_PATTERN = exports.SPDX_ID_PATTERN = exports.DURATION_PATTERN = exports.SEMVER_PATTERN = exports.PACKAGE_ID_PATTERN = exports.MANIFEST_FIELDS = exports.RISK_LEVELS = exports.PROVIDERS = exports.ACTION_CATEGORIES = exports.isEgressDestination = exports.isSafeRelativePath = exports.sha256Hex = exports.canonicalJson = exports.validateParameterSchema = exports.validateManifest = exports.parseAndValidateManifest = void 0;
8371
+ exports.scaffoldActionPackage = void 0;
6627
8372
  var manifest_validator_1 = require_manifest_validator();
6628
8373
  Object.defineProperty(exports, "parseAndValidateManifest", {
6629
8374
  enumerable: true,
@@ -6661,6 +8406,12 @@ var require_dist2 = __commonJS({
6661
8406
  return manifest_validator_1.isSafeRelativePath;
6662
8407
  }, "get")
6663
8408
  });
8409
+ Object.defineProperty(exports, "isEgressDestination", {
8410
+ enumerable: true,
8411
+ get: /* @__PURE__ */ __name(function() {
8412
+ return manifest_validator_1.isEgressDestination;
8413
+ }, "get")
8414
+ });
6664
8415
  var manifest_schema_1 = require_manifest_schema();
6665
8416
  Object.defineProperty(exports, "ACTION_CATEGORIES", {
6666
8417
  enumerable: true,
@@ -6941,67 +8692,7 @@ var require_dist2 = __commonJS({
6941
8692
  });
6942
8693
 
6943
8694
  // package.json
6944
- var version = "0.1.2";
6945
-
6946
- // src/check.ts
6947
- var import_action_manifest = __toESM(require_dist2());
6948
- import { resolve } from "path";
6949
-
6950
- // src/package-dir.ts
6951
- import { readdir, readFile, stat } from "fs/promises";
6952
- import { join, relative, sep } from "path";
6953
- var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
6954
- ".git",
6955
- ".runveric",
6956
- "node_modules",
6957
- ".venv",
6958
- "__pycache__",
6959
- ".DS_Store"
6960
- ]);
6961
- var MAX_FILES = 2e3;
6962
- var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
6963
- var PackageDirectoryError = class extends Error {
6964
- static {
6965
- __name(this, "PackageDirectoryError");
6966
- }
6967
- };
6968
- async function readPackageDirectory(root) {
6969
- const properties = await stat(root).catch(() => null);
6970
- if (!properties?.isDirectory()) {
6971
- throw new PackageDirectoryError(`${root} is not a directory.`);
6972
- }
6973
- const files = /* @__PURE__ */ new Map();
6974
- let totalBytes = 0;
6975
- const walk = /* @__PURE__ */ __name(async (directory) => {
6976
- for (const entry of await readdir(directory, {
6977
- withFileTypes: true
6978
- })) {
6979
- if (IGNORED_DIRECTORIES.has(entry.name)) continue;
6980
- const full = join(directory, entry.name);
6981
- if (entry.isDirectory()) {
6982
- await walk(full);
6983
- continue;
6984
- }
6985
- if (!entry.isFile()) continue;
6986
- if (files.size >= MAX_FILES) {
6987
- 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.`);
6988
- }
6989
- const contents = await readFile(full);
6990
- totalBytes += contents.byteLength;
6991
- if (totalBytes > MAX_TOTAL_BYTES) {
6992
- 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.`);
6993
- }
6994
- files.set(toPackagePath(root, full), contents);
6995
- }
6996
- }, "walk");
6997
- await walk(root);
6998
- return files;
6999
- }
7000
- __name(readPackageDirectory, "readPackageDirectory");
7001
- function toPackagePath(root, full) {
7002
- return relative(root, full).split(sep).join("/");
7003
- }
7004
- __name(toPackagePath, "toPackagePath");
8695
+ var version = "0.3.0";
7005
8696
 
7006
8697
  // src/report.ts
7007
8698
  var useColour = process.stdout.isTTY === true && !process.env.NO_COLOR;
@@ -7110,10 +8801,719 @@ function printResult(result) {
7110
8801
  }
7111
8802
  __name(printResult, "printResult");
7112
8803
 
7113
- // src/check.ts
8804
+ // src/catalog/publish-action.ts
8805
+ var import_contracts3 = __toESM(require_dist());
8806
+ import { readFileSync } from "fs";
8807
+ import { dirname, resolve } from "path";
8808
+ import { parse as parseYaml } from "yaml";
8809
+
8810
+ // src/catalog/artifact.ts
7114
8811
  var import_contracts = __toESM(require_dist());
8812
+ import { execFileSync } from "child_process";
8813
+ import { createHash } from "crypto";
8814
+ function parseRepository(ref, flag) {
8815
+ if (!import_contracts.ARTIFACT_REPOSITORY_PATTERN.test(ref)) {
8816
+ 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.`);
8817
+ }
8818
+ const cut = ref.indexOf("/");
8819
+ return {
8820
+ ref,
8821
+ host: ref.slice(0, cut),
8822
+ path: ref.slice(cut + 1),
8823
+ namespace: ref.slice(0, ref.lastIndexOf("/"))
8824
+ };
8825
+ }
8826
+ __name(parseRepository, "parseRepository");
8827
+ var isDigest = /* @__PURE__ */ __name((value) => import_contracts.ARTIFACT_DIGEST_PATTERN.test(value), "isDigest");
8828
+ var TAG_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/;
8829
+ function requireTag(tag, origin2) {
8830
+ if (!TAG_PATTERN.test(tag)) {
8831
+ 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.`);
8832
+ }
8833
+ }
8834
+ __name(requireTag, "requireTag");
8835
+ var LOOPBACK = /^(localhost|127\.0\.0\.1|\[::1\])(:[0-9]+)?$/;
8836
+ var origin = /* @__PURE__ */ __name((host) => `${LOOPBACK.test(host) ? "http" : "https"}://${host}`, "origin");
8837
+ var sha256 = /* @__PURE__ */ __name((bytes) => `sha256:${createHash("sha256").update(bytes).digest("hex")}`, "sha256");
8838
+ var EMPTY_CONFIG = Buffer.from("{}");
8839
+ var EMPTY_CONFIG_DESCRIPTOR = {
8840
+ mediaType: "application/vnd.oci.empty.v1+json",
8841
+ digest: sha256(EMPTY_CONFIG),
8842
+ size: EMPTY_CONFIG.length
8843
+ };
8844
+ var MANIFEST_ACCEPT = import_contracts.MANIFEST_MEDIA_TYPES.join(", ");
8845
+ function provenance(flags, dir) {
8846
+ const git = /* @__PURE__ */ __name((...args) => {
8847
+ try {
8848
+ return execFileSync("git", [
8849
+ "-C",
8850
+ dir,
8851
+ ...args
8852
+ ], {
8853
+ encoding: "utf8",
8854
+ stdio: [
8855
+ "ignore",
8856
+ "pipe",
8857
+ "ignore"
8858
+ ]
8859
+ }).trim();
8860
+ } catch {
8861
+ return "";
8862
+ }
8863
+ }, "git");
8864
+ const actions = process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` : "";
8865
+ const repository = flags.repository ?? (actions || git("remote", "get-url", "origin"));
8866
+ const commit = flags.commit ?? (process.env.GITHUB_SHA || git("rev-parse", "HEAD"));
8867
+ if (!repository || !commit) {
8868
+ 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).");
8869
+ }
8870
+ return {
8871
+ repository,
8872
+ commit,
8873
+ publishedAt: (/* @__PURE__ */ new Date()).toISOString()
8874
+ };
8875
+ }
8876
+ __name(provenance, "provenance");
8877
+ function buildArtifact(document) {
8878
+ const documentBytes = Buffer.from(`${JSON.stringify(document, null, 2)}
8879
+ `);
8880
+ const manifest = {
8881
+ schemaVersion: 2,
8882
+ mediaType: import_contracts.OCI_IMAGE_MANIFEST,
8883
+ artifactType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
8884
+ config: EMPTY_CONFIG_DESCRIPTOR,
8885
+ layers: [
8886
+ {
8887
+ mediaType: import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE,
8888
+ digest: sha256(documentBytes),
8889
+ size: documentBytes.length
8890
+ }
8891
+ ],
8892
+ /*
8893
+ * ★ SOURCE AND REVISION ARE FOR THE REGISTRY, NOT THE RESOLVER (docs/127 A.1). GHCR connects
8894
+ * a package to its repository through `org.opencontainers.image.source`, and that link is what
8895
+ * lets the repository's workflow token keep publishing to the package its first run created.
8896
+ * The resolver reads none of these — the document's own provenance is the record — so an
8897
+ * artifact published without them still resolves.
8898
+ */
8899
+ annotations: {
8900
+ "org.opencontainers.image.created": document.provenance.publishedAt,
8901
+ "org.opencontainers.image.source": document.provenance.repository,
8902
+ "org.opencontainers.image.revision": document.provenance.commit
8903
+ }
8904
+ };
8905
+ const manifestBytes = Buffer.from(JSON.stringify(manifest));
8906
+ return {
8907
+ documentBytes,
8908
+ manifestBytes,
8909
+ manifestDigest: sha256(manifestBytes)
8910
+ };
8911
+ }
8912
+ __name(buildArtifact, "buildArtifact");
8913
+ function basicCredential() {
8914
+ const pat = process.env.GHCR_TOKEN;
8915
+ if (pat) return Buffer.from(`x:${pat}`).toString("base64");
8916
+ const workflow = process.env.GITHUB_TOKEN;
8917
+ if (workflow) return Buffer.from(`${process.env.GITHUB_ACTOR || "x"}:${workflow}`).toString("base64");
8918
+ return void 0;
8919
+ }
8920
+ __name(basicCredential, "basicCredential");
8921
+ var tokens = /* @__PURE__ */ new Map();
8922
+ async function token(repository, challenge, access) {
8923
+ const field = /* @__PURE__ */ __name((name) => new RegExp(`${name}="([^"]*)"`).exec(challenge ?? "")?.[1], "field");
8924
+ const realm = field("realm");
8925
+ if (!realm) throw new Error(`${repository.host} asked for credentials without a Bearer challenge.`);
8926
+ const url = new URL(realm);
8927
+ const service = field("service");
8928
+ if (service) url.searchParams.set("service", service);
8929
+ url.searchParams.set("scope", `repository:${repository.path}:${access}`);
8930
+ const credential = basicCredential();
8931
+ const answer = await fetch(url, {
8932
+ headers: credential ? {
8933
+ Authorization: `Basic ${credential}`
8934
+ } : {}
8935
+ });
8936
+ if (!answer.ok) {
8937
+ throw new Error(`${repository.host} refused a ${access === "pull" ? "pull" : "push"} token for ${repository.path}: HTTP ${answer.status}` + (credential || access === "pull" ? "." : " \u2014 set GHCR_TOKEN to a token with write:packages, or in a workflow GITHUB_TOKEN with packages: write."));
8938
+ }
8939
+ const body = await answer.json();
8940
+ const value = body.token ?? body.access_token ?? "";
8941
+ tokens.set(`${repository.path} ${access}`, value);
8942
+ return value;
8943
+ }
8944
+ __name(token, "token");
8945
+ async function call(repository, method, path, { headers = {}, body, access = "pull,push" } = {}) {
8946
+ const url = path.startsWith("http") ? path : `${origin(repository.host)}/v2/${repository.path}/${path}`;
8947
+ const send = /* @__PURE__ */ __name((bearer) => fetch(url, {
8948
+ method,
8949
+ body,
8950
+ redirect: "follow",
8951
+ headers: {
8952
+ ...headers,
8953
+ ...bearer ? {
8954
+ Authorization: `Bearer ${bearer}`
8955
+ } : {}
8956
+ }
8957
+ }), "send");
8958
+ let answer = await send(tokens.get(`${repository.path} ${access}`));
8959
+ if (answer.status === 401) answer = await send(await token(repository, answer.headers.get("www-authenticate"), access));
8960
+ return answer;
8961
+ }
8962
+ __name(call, "call");
8963
+ async function pushBlob(repository, bytes) {
8964
+ const digest = sha256(bytes);
8965
+ if ((await call(repository, "HEAD", `blobs/${digest}`)).ok) return digest;
8966
+ const started = await call(repository, "POST", "blobs/uploads/");
8967
+ if (started.status !== 202) throw new Error(`${repository.host} refused an upload: HTTP ${started.status}`);
8968
+ const location = new URL(started.headers.get("location") ?? "", `${origin(repository.host)}/`);
8969
+ location.searchParams.set("digest", digest);
8970
+ const put = await call(repository, "PUT", location.toString(), {
8971
+ headers: {
8972
+ "Content-Type": "application/octet-stream",
8973
+ "Content-Length": String(bytes.length)
8974
+ },
8975
+ body: bytes
8976
+ });
8977
+ if (put.status !== 201) throw new Error(`${repository.host} rejected blob ${digest}: HTTP ${put.status} ${await put.text()}`);
8978
+ return digest;
8979
+ }
8980
+ __name(pushBlob, "pushBlob");
8981
+ async function manifestExists(repository, digest) {
8982
+ const answer = await call(repository, "HEAD", `manifests/${digest}`, {
8983
+ headers: {
8984
+ Accept: import_contracts.OCI_IMAGE_MANIFEST
8985
+ }
8986
+ });
8987
+ return answer.ok;
8988
+ }
8989
+ __name(manifestExists, "manifestExists");
8990
+ async function readVerified(repository, kind, digest, size = null) {
8991
+ if (!isDigest(digest)) throw new Error(`"${digest}" is not a digest, so ${repository.ref} cannot be asked for it.`);
8992
+ const answer = await call(repository, "GET", `${kind}/${digest}`, {
8993
+ access: "pull",
8994
+ headers: kind === "manifests" ? {
8995
+ Accept: MANIFEST_ACCEPT
8996
+ } : {}
8997
+ });
8998
+ if (!answer.ok) {
8999
+ throw new Error(`${repository.ref}@${digest} could not be read: HTTP ${answer.status}${answer.status === 404 ? " \u2014 it is not in the registry" : ""}.`);
9000
+ }
9001
+ const bytes = Buffer.from(await answer.arrayBuffer());
9002
+ const served = sha256(bytes);
9003
+ if (served !== digest || size !== null && bytes.length !== size) {
9004
+ throw new Error(`${repository.host} served ${bytes.length} bytes hashing to ${served} for ${repository.ref}@${digest}${size === null ? "" : ` (${size} bytes named)`}. Refused: a digest this command did not compute is not a pin.`);
9005
+ }
9006
+ return bytes;
9007
+ }
9008
+ __name(readVerified, "readVerified");
9009
+ var readManifest = /* @__PURE__ */ __name((repository, digest) => readVerified(repository, "manifests", digest), "readManifest");
9010
+ var readBlob = /* @__PURE__ */ __name((repository, digest, size) => readVerified(repository, "blobs", digest, size), "readBlob");
9011
+ function parseJsonBytes(bytes, what) {
9012
+ try {
9013
+ return JSON.parse(new TextDecoder("utf-8", {
9014
+ fatal: true
9015
+ }).decode(bytes));
9016
+ } catch (cause) {
9017
+ throw new Error(`${what} is not UTF-8 JSON. (${cause instanceof Error ? cause.message : String(cause)})`);
9018
+ }
9019
+ }
9020
+ __name(parseJsonBytes, "parseJsonBytes");
9021
+ async function readArtifactDocument(repository, digest) {
9022
+ const what = `${repository.ref}@${digest}`;
9023
+ const manifest = parseJsonBytes(await readManifest(repository, digest), `the manifest of ${what}`);
9024
+ const layers = Array.isArray(manifest?.layers) ? manifest.layers : [];
9025
+ const layer = layers[0];
9026
+ if (manifest?.mediaType !== import_contracts.OCI_IMAGE_MANIFEST || layers.length !== 1 || layer?.mediaType !== import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE || typeof layer.digest !== "string" || typeof layer.size !== "number") {
9027
+ throw new Error(`${what} is not a catalog artifact: expected one OCI manifest with exactly one ${import_contracts.CATALOG_ARTIFACT_MEDIA_TYPE} layer.`);
9028
+ }
9029
+ return parseJsonBytes(await readBlob(repository, layer.digest, layer.size), `the document of ${what}`);
9030
+ }
9031
+ __name(readArtifactDocument, "readArtifactDocument");
9032
+ async function pushArtifact(repository, artifact, tag) {
9033
+ await pushBlob(repository, EMPTY_CONFIG);
9034
+ await pushBlob(repository, artifact.documentBytes);
9035
+ for (const reference of [
9036
+ artifact.manifestDigest,
9037
+ tag
9038
+ ]) {
9039
+ const put = await call(repository, "PUT", `manifests/${reference}`, {
9040
+ headers: {
9041
+ "Content-Type": import_contracts.OCI_IMAGE_MANIFEST
9042
+ },
9043
+ body: artifact.manifestBytes
9044
+ });
9045
+ if (put.status !== 201) {
9046
+ throw new Error(`${repository.host} rejected the manifest (${reference}): HTTP ${put.status} ${await put.text()}`);
9047
+ }
9048
+ }
9049
+ const asked = await call(repository, "HEAD", `manifests/${tag}`, {
9050
+ headers: {
9051
+ Accept: import_contracts.OCI_IMAGE_MANIFEST
9052
+ }
9053
+ });
9054
+ const reported = asked.headers.get("docker-content-digest");
9055
+ if (!asked.ok || !reported) {
9056
+ throw new Error(`${repository.host} could not say what :${tag} resolves to (HTTP ${asked.status}).`);
9057
+ }
9058
+ if (reported !== artifact.manifestDigest) {
9059
+ 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.`);
9060
+ }
9061
+ return reported;
9062
+ }
9063
+ __name(pushArtifact, "pushArtifact");
9064
+ var defaultTag = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..*$/, "").toLowerCase(), "defaultTag");
9065
+
9066
+ // src/catalog/flags.ts
9067
+ function parseCatalogFlags(argv, known, repeated = []) {
9068
+ const single2 = {};
9069
+ const many = {};
9070
+ for (let index = 0; index < argv.length; index += 1) {
9071
+ const argument = argv[index];
9072
+ const separator = argument.indexOf("=");
9073
+ const name = separator === -1 ? argument.slice(2) : argument.slice(2, separator);
9074
+ if (!argument.startsWith("--") || !known.includes(name)) throw new Error(`Unknown argument: ${argument}`);
9075
+ let value;
9076
+ if (separator !== -1) {
9077
+ value = argument.slice(separator + 1);
9078
+ } else {
9079
+ value = argv[index + 1];
9080
+ index += 1;
9081
+ }
9082
+ if (value === void 0 || value === "" || value.startsWith("--")) throw new Error(`--${name} needs a value.`);
9083
+ if (repeated.includes(name)) (many[name] ??= []).push(value);
9084
+ else single2[name] = value;
9085
+ }
9086
+ return {
9087
+ single: single2,
9088
+ many
9089
+ };
9090
+ }
9091
+ __name(parseCatalogFlags, "parseCatalogFlags");
9092
+
9093
+ // src/catalog/image.ts
9094
+ var import_contracts2 = __toESM(require_dist());
9095
+ function parseImageReference(value, origin2) {
9096
+ const at = value.lastIndexOf("@");
9097
+ const digest = at === -1 ? "" : value.slice(at + 1);
9098
+ if (at === -1 || !isDigest(digest)) {
9099
+ throw new Error(`${origin2} "${value}" is not <repository>@<digest>. An engine action's image is named by its digest \u2014 sha256: and 64 lowercase hex \u2014 never by a tag, which a registry can repoint.`);
9100
+ }
9101
+ const repository = parseRepository(value.slice(0, at), origin2);
9102
+ return {
9103
+ repository,
9104
+ digest,
9105
+ ref: `${repository.ref}@${digest}`
9106
+ };
9107
+ }
9108
+ __name(parseImageReference, "parseImageReference");
9109
+ async function imageRefusal(image, key, provenance2) {
9110
+ const manifestBytes = await readManifest(image.repository, image.digest);
9111
+ if (manifestBytes.length > import_contracts2.MAX_IMAGE_MANIFEST_BYTES) {
9112
+ return `the image manifest of ${image.ref} is ${manifestBytes.length} bytes; the limit is ${import_contracts2.MAX_IMAGE_MANIFEST_BYTES}.`;
9113
+ }
9114
+ const manifest = parseJsonBytes(manifestBytes, `the image manifest of ${image.ref}`);
9115
+ const descriptor = (0, import_contracts2.imageConfigDescriptor)(manifest);
9116
+ const config = descriptor === null ? null : parseJsonBytes(await readBlob(image.repository, descriptor.digest, descriptor.size), `the image config of ${image.ref}`);
9117
+ return (0, import_contracts2.actionImageRefusal)({
9118
+ key,
9119
+ image: image.ref,
9120
+ manifest,
9121
+ config,
9122
+ provenance: provenance2
9123
+ });
9124
+ }
9125
+ __name(imageRefusal, "imageRefusal");
9126
+
9127
+ // src/catalog/publish-action.ts
9128
+ var PUBLISH_ACTION_USAGE = "runveric catalog publish-action --manifest <file.yaml> --to <registry>/<namespace>/<repository>\n [--image <repository>@<digest>] [--tag <tag>]\n [--repository <source url>] [--commit <sha>]";
9129
+ var isMapping = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
9130
+ async function runPublishAction(argv) {
9131
+ const { single: flags } = parseCatalogFlags(argv, [
9132
+ "manifest",
9133
+ "to",
9134
+ "image",
9135
+ "tag",
9136
+ "repository",
9137
+ "commit"
9138
+ ]);
9139
+ if (!flags.manifest || !flags.to) throw new Error("--manifest and --to are required.");
9140
+ const repository = parseRepository(flags.to, "--to");
9141
+ const image = flags.image === void 0 ? null : parseImageReference(flags.image, "--image");
9142
+ if (image !== null && image.repository.ref !== repository.ref) {
9143
+ throw new Error(`--image ${image.ref} is outside the artifact's repository ${repository.ref}. An engine action's image lives in its own package, so its artifact names it by digest alone (docs/128 \xA717.2 D2) \u2014 push the image to ${repository.ref}.`);
9144
+ }
9145
+ const path = resolve(flags.manifest);
9146
+ const manifest = readFileSync(path, "utf8");
9147
+ let parsed;
9148
+ try {
9149
+ parsed = parseYaml(manifest);
9150
+ } catch (cause) {
9151
+ throw new Error(`${flags.manifest} is not valid YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
9152
+ }
9153
+ const fields = isMapping(parsed) ? parsed : {};
9154
+ const key = typeof fields.key === "string" && fields.key.length > 0 ? fields.key : null;
9155
+ if (key === null) throw new Error(`${flags.manifest} declares no top-level \`key:\` \u2014 is it a catalog manifest?`);
9156
+ const misnamed = (0, import_contracts3.actionPackageRefusal)(key, repository.ref);
9157
+ if (misnamed !== null) throw new Error(`--to: ${misnamed}`);
9158
+ const executes = isMapping(fields.execution);
9159
+ if (executes && image === null) {
9160
+ throw new Error(`${flags.manifest} declares execution, so it is an engine action: publish it with --image <repository>@<digest>, the image its workflow built. Without one the server refuses it at plan.`);
9161
+ }
9162
+ if (!executes && image !== null) {
9163
+ throw new Error(`${flags.manifest} declares no execution, so nothing would ever run --image ${image.ref}. A built-in action is published without one.`);
9164
+ }
9165
+ let tag;
9166
+ if (fields.declares === "definition+version") {
9167
+ const version2 = typeof fields.version === "string" ? fields.version : null;
9168
+ if (version2 === null) throw new Error(`${flags.manifest} declares "definition+version" and no version:.`);
9169
+ if (flags.tag !== void 0 && flags.tag !== version2) {
9170
+ 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.`);
9171
+ }
9172
+ tag = version2;
9173
+ } else {
9174
+ tag = flags.tag ?? defaultTag();
9175
+ }
9176
+ requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.manifest}'s version`);
9177
+ const recorded = provenance(flags, dirname(path));
9178
+ if (image !== null) {
9179
+ const refusal = await imageRefusal(image, key, recorded);
9180
+ if (refusal !== null) throw new Error(`--image: ${refusal}`);
9181
+ }
9182
+ const artifact = buildArtifact(image === null ? {
9183
+ schemaVersion: import_contracts3.CATALOG_ACTION_ARTIFACT_SCHEMA_V1,
9184
+ manifest,
9185
+ provenance: recorded
9186
+ } : {
9187
+ schemaVersion: import_contracts3.CATALOG_ACTION_ARTIFACT_SCHEMA_V2,
9188
+ manifest,
9189
+ provenance: recorded,
9190
+ image: {
9191
+ digest: image.digest
9192
+ }
9193
+ });
9194
+ const digest = await pushArtifact(repository, artifact, tag);
9195
+ process.stderr.write(`\u2713 ${key} published to ${repository.ref}:${tag}${image === null ? "" : `, naming the image ${image.digest}`}
9196
+ `);
9197
+ process.stderr.write(` the registry reports ${digest} \u2014 the digest a release must name.
9198
+ `);
9199
+ process.stdout.write(`${key}=${repository.ref}@${digest}
9200
+ `);
9201
+ }
9202
+ __name(runPublishAction, "runPublishAction");
9203
+
9204
+ // src/catalog/publish-release.ts
9205
+ var import_contracts4 = __toESM(require_dist());
9206
+ import { readFileSync as readFileSync2 } from "fs";
9207
+ import { parse as parseYaml2 } from "yaml";
9208
+ 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>]";
9209
+ function entriesFromFlags(values) {
9210
+ return values.map((value) => {
9211
+ const match = /^([^=]+)=([^@]+)@(.+)$/.exec(value);
9212
+ if (!match) throw new Error(`--action "${value}" is not <key>=<repository>@<digest>.`);
9213
+ const [, key = "", artifactRef = "", digest = ""] = match;
9214
+ return {
9215
+ key,
9216
+ artifactRef,
9217
+ digest,
9218
+ label: `--action ${key}`
9219
+ };
9220
+ });
9221
+ }
9222
+ __name(entriesFromFlags, "entriesFromFlags");
9223
+ function specFromFile(path) {
9224
+ let value;
9225
+ try {
9226
+ value = parseYaml2(readFileSync2(path, "utf8"));
9227
+ } catch (cause) {
9228
+ throw new Error(`--from ${path} could not be read as YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
9229
+ }
9230
+ const spec = (0, import_contracts4.parseCatalogReleaseSpec)(value);
9231
+ return {
9232
+ version: spec.version,
9233
+ entries: spec.actions.map((action) => ({
9234
+ ...action,
9235
+ label: `${path}'s ${action.key}`
9236
+ }))
9237
+ };
9238
+ }
9239
+ __name(specFromFile, "specFromFile");
9240
+ async function runPublishRelease(argv) {
9241
+ const { single: flags, many } = parseCatalogFlags(argv, [
9242
+ "to",
9243
+ "version",
9244
+ "action",
9245
+ "from",
9246
+ "tag",
9247
+ "repository",
9248
+ "commit"
9249
+ ], [
9250
+ "action"
9251
+ ]);
9252
+ const actionFlags = many.action ?? [];
9253
+ if (flags.from !== void 0 && actionFlags.length > 0) {
9254
+ throw new Error("--from and --action are alternatives: name the release's actions in release.yaml or as flags, not both.");
9255
+ }
9256
+ if (flags.from !== void 0 && flags.version !== void 0) {
9257
+ throw new Error("--version is read from release.yaml when --from is given; pass one or the other.");
9258
+ }
9259
+ if (!flags.to || flags.from === void 0 && (!flags.version || actionFlags.length === 0)) {
9260
+ throw new Error("--to and either --from, or --version and at least one --action, are required.");
9261
+ }
9262
+ const release = parseRepository(flags.to, "--to");
9263
+ const { version: version2, entries } = flags.from !== void 0 ? specFromFile(flags.from) : {
9264
+ version: flags.version,
9265
+ entries: entriesFromFlags(actionFlags)
9266
+ };
9267
+ const tag = flags.tag ?? (flags.from !== void 0 ? version2 : defaultTag());
9268
+ requireTag(tag, flags.tag !== void 0 ? "--tag" : `${flags.from}'s version`);
9269
+ const seen = /* @__PURE__ */ new Set();
9270
+ const actions = entries.map((entry) => {
9271
+ if (seen.has(entry.key)) throw new Error(`${entry.label} names "${entry.key}" twice.`);
9272
+ seen.add(entry.key);
9273
+ const repository = parseRepository(entry.artifactRef, entry.label);
9274
+ if (repository.namespace !== release.namespace) {
9275
+ 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.`);
9276
+ }
9277
+ if (!isDigest(entry.digest)) {
9278
+ throw new Error(`${entry.label}: "${entry.digest}" is not a digest. A release names artifacts by digest only.`);
9279
+ }
9280
+ const misnamed = (0, import_contracts4.actionPackageRefusal)(entry.key, entry.artifactRef);
9281
+ if (misnamed !== null) throw new Error(`${entry.label}: ${misnamed}`);
9282
+ return {
9283
+ ...entry,
9284
+ repository
9285
+ };
9286
+ });
9287
+ for (const action of actions) {
9288
+ if (!await manifestExists(action.repository, action.digest)) {
9289
+ throw new Error(`${action.artifactRef}@${action.digest} (${action.key}) is not in the registry.`);
9290
+ }
9291
+ }
9292
+ const artifact = buildArtifact({
9293
+ schemaVersion: import_contracts4.CATALOG_RELEASE_SCHEMA_V1,
9294
+ version: version2,
9295
+ provenance: provenance(flags, process.cwd()),
9296
+ actions: actions.map(({ key, artifactRef, digest: digest2 }) => ({
9297
+ key,
9298
+ artifactRef,
9299
+ digest: digest2
9300
+ }))
9301
+ });
9302
+ const digest = await pushArtifact(release, artifact, tag);
9303
+ process.stderr.write(`\u2713 catalog release ${version2} (${actions.length} action artifact(s)) published to ${release.ref}:${tag}
9304
+ `);
9305
+ process.stderr.write(` the registry reports ${digest} \u2014 the digest to plan and pin.
9306
+ `);
9307
+ process.stdout.write(`${release.ref}@${digest}
9308
+ `);
9309
+ }
9310
+ __name(runPublishRelease, "runPublishRelease");
9311
+
9312
+ // src/catalog/verify-image.ts
9313
+ var import_contracts5 = __toESM(require_dist());
9314
+ import { readFileSync as readFileSync3 } from "fs";
9315
+ import { parse as parseYaml3 } from "yaml";
9316
+ var VERIFY_IMAGE_USAGE = "runveric catalog verify-image (<repository>@<digest> --key <action key> --commit <sha> | --from <release.yaml>)";
9317
+ var COMMIT = /^[0-9a-f]{40}$/;
9318
+ var isMapping2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isMapping");
9319
+ function single(value, flags) {
9320
+ if (!flags.key || !flags.commit) throw new Error(`verify-image ${value} needs --key <action key> and --commit <sha>.`);
9321
+ if (!COMMIT.test(flags.commit)) throw new Error(`--commit "${flags.commit}" is not a 40-hex commit.`);
9322
+ const image = parseImageReference(value, "verify-image");
9323
+ const misnamed = (0, import_contracts5.actionPackageRefusal)(flags.key, image.repository.ref);
9324
+ if (misnamed !== null) throw new Error(misnamed);
9325
+ return {
9326
+ key: flags.key,
9327
+ image,
9328
+ provenance: {
9329
+ repository: (0, import_contracts5.actionSourceRepository)(flags.key),
9330
+ commit: flags.commit
9331
+ },
9332
+ label: `--key ${flags.key}`
9333
+ };
9334
+ }
9335
+ __name(single, "single");
9336
+ async function fromRelease(path) {
9337
+ let value;
9338
+ try {
9339
+ value = parseYaml3(readFileSync3(path, "utf8"));
9340
+ } catch (cause) {
9341
+ throw new Error(`--from ${path} could not be read as YAML. (${cause instanceof Error ? cause.message : String(cause)})`);
9342
+ }
9343
+ const candidates = [];
9344
+ for (const action of (0, import_contracts5.parseCatalogReleaseSpec)(value).actions) {
9345
+ const label = `${path}'s ${action.key}`;
9346
+ const misnamed = (0, import_contracts5.actionPackageRefusal)(action.key, action.artifactRef);
9347
+ if (misnamed !== null) throw new Error(`${label}: ${misnamed}`);
9348
+ if (!isDigest(action.digest)) throw new Error(`${label}: "${action.digest}" is not a digest.`);
9349
+ const repository = parseRepository(action.artifactRef, label);
9350
+ const read = await readArtifactDocument(repository, action.digest);
9351
+ const document = isMapping2(read) ? read : {};
9352
+ const schemaVersion = document.schemaVersion;
9353
+ if (schemaVersion === import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V1) continue;
9354
+ if (schemaVersion !== import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V2) {
9355
+ throw new Error(`${label}: ${repository.ref}@${action.digest} declares schemaVersion ${JSON.stringify(schemaVersion)}; this CLI reads ${import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V1} and ${import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V2}.`);
9356
+ }
9357
+ const image = isMapping2(document.image) ? document.image.digest : void 0;
9358
+ const recorded = isMapping2(document.provenance) ? document.provenance : {};
9359
+ if (typeof image !== "string" || typeof recorded.repository !== "string" || typeof recorded.commit !== "string") {
9360
+ throw new Error(`${label}: its ${import_contracts5.CATALOG_ACTION_ARTIFACT_SCHEMA_V2} artifact carries no image digest or no provenance.`);
9361
+ }
9362
+ candidates.push({
9363
+ key: action.key,
9364
+ // ★ The image lives in the artifact's own package (D2) — the artifact names a digest, never a place.
9365
+ image: parseImageReference(`${repository.ref}@${image}`, label),
9366
+ provenance: {
9367
+ repository: recorded.repository,
9368
+ commit: recorded.commit
9369
+ },
9370
+ label
9371
+ });
9372
+ }
9373
+ return candidates;
9374
+ }
9375
+ __name(fromRelease, "fromRelease");
9376
+ async function runVerifyImage(argv) {
9377
+ const image = argv[0] !== void 0 && !argv[0].startsWith("--") ? argv[0] : void 0;
9378
+ const { single: flags } = parseCatalogFlags(image === void 0 ? argv : argv.slice(1), [
9379
+ "key",
9380
+ "commit",
9381
+ "from"
9382
+ ]);
9383
+ if (image !== void 0 && flags.from !== void 0) {
9384
+ throw new Error("An image and --from are alternatives: verify one image, or every engine action release.yaml names.");
9385
+ }
9386
+ if (image === void 0 && flags.from === void 0) {
9387
+ throw new Error(`Name an image or a release. Usage: ${VERIFY_IMAGE_USAGE}`);
9388
+ }
9389
+ if (flags.from !== void 0 && (flags.key !== void 0 || flags.commit !== void 0)) {
9390
+ throw new Error("--key and --commit are read from each artifact when --from is given; pass one or the other.");
9391
+ }
9392
+ const candidates = image !== void 0 ? [
9393
+ single(image, flags)
9394
+ ] : await fromRelease(flags.from);
9395
+ for (const candidate of candidates) {
9396
+ const refusal = await imageRefusal(candidate.image, candidate.key, candidate.provenance);
9397
+ if (refusal !== null) throw new Error(`${candidate.label}: ${refusal} Nothing was signed.`);
9398
+ }
9399
+ for (const candidate of candidates) {
9400
+ process.stderr.write(`\u2713 ${candidate.key}: ${candidate.image.ref} \u2014 built in ${candidate.provenance.repository} at ${candidate.provenance.commit}
9401
+ `);
9402
+ process.stdout.write(`${candidate.image.ref}
9403
+ `);
9404
+ }
9405
+ if (candidates.length === 0) process.stderr.write(`\u2713 ${flags.from} names no engine action \u2014 no image to sign.
9406
+ `);
9407
+ }
9408
+ __name(runVerifyImage, "runVerifyImage");
9409
+
9410
+ // src/catalog/index.ts
9411
+ var CATALOG_USAGE = `runveric catalog \u2014 publish first-party catalog artifacts (docs/127)
9412
+
9413
+ ${PUBLISH_ACTION_USAGE}
9414
+ Publish one action's manifest, verbatim, as an artifact \u2014 with --image, an engine action's
9415
+ artifact naming its image by digest. stdout: <key>=<repository>@<digest>
9416
+
9417
+ ${PUBLISH_RELEASE_USAGE}
9418
+ Publish a release naming each action artifact by digest. stdout: <repository>@<digest>
9419
+
9420
+ ${VERIFY_IMAGE_USAGE}
9421
+ Judge an engine action's image by the server's rule before it is signed.
9422
+ stdout: one <repository>@<digest> per image that passed
9423
+
9424
+ Credentials: GHCR_TOKEN (write:packages), or in a workflow GITHUB_TOKEN with packages: write.
9425
+ Provenance: --repository/--commit, else GITHUB_SERVER_URL/GITHUB_REPOSITORY and GITHUB_SHA, else git.`;
9426
+ var SUBCOMMANDS = {
9427
+ "publish-action": runPublishAction,
9428
+ "publish-release": runPublishRelease,
9429
+ "verify-image": runVerifyImage
9430
+ };
9431
+ async function runCatalog(argv) {
9432
+ const [subcommand, ...rest] = argv;
9433
+ if (subcommand === void 0 || subcommand === "--help" || rest.includes("--help")) {
9434
+ process.stdout.write(`${CATALOG_USAGE}
9435
+ `);
9436
+ return subcommand === void 0 ? 1 : 0;
9437
+ }
9438
+ const run3 = SUBCOMMANDS[subcommand];
9439
+ if (run3 === void 0) {
9440
+ printError(`\u2717 "catalog ${subcommand}" is not a command. There are ${Object.keys(SUBCOMMANDS).length}: ${Object.keys(SUBCOMMANDS).join(", ")}.`);
9441
+ return 1;
9442
+ }
9443
+ try {
9444
+ await run3(rest);
9445
+ return 0;
9446
+ } catch (error) {
9447
+ printError(`\u2717 ${error instanceof Error ? error.message : String(error)}`);
9448
+ return 1;
9449
+ }
9450
+ }
9451
+ __name(runCatalog, "runCatalog");
9452
+
9453
+ // src/check.ts
9454
+ var import_action_manifest = __toESM(require_dist2());
9455
+ import { resolve as resolve2 } from "path";
9456
+
9457
+ // src/package-dir.ts
9458
+ import { readdir, readFile, stat } from "fs/promises";
9459
+ import { join, relative, sep } from "path";
9460
+ var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
9461
+ ".git",
9462
+ ".runveric",
9463
+ "node_modules",
9464
+ ".venv",
9465
+ "__pycache__",
9466
+ ".DS_Store"
9467
+ ]);
9468
+ var MAX_FILES = 2e3;
9469
+ var MAX_TOTAL_BYTES = 16 * 1024 * 1024;
9470
+ var PackageDirectoryError = class extends Error {
9471
+ static {
9472
+ __name(this, "PackageDirectoryError");
9473
+ }
9474
+ };
9475
+ async function readPackageDirectory(root) {
9476
+ const properties = await stat(root).catch(() => null);
9477
+ if (!properties?.isDirectory()) {
9478
+ throw new PackageDirectoryError(`${root} is not a directory.`);
9479
+ }
9480
+ const files = /* @__PURE__ */ new Map();
9481
+ let totalBytes = 0;
9482
+ const walk = /* @__PURE__ */ __name(async (directory) => {
9483
+ for (const entry of await readdir(directory, {
9484
+ withFileTypes: true
9485
+ })) {
9486
+ if (IGNORED_DIRECTORIES.has(entry.name)) continue;
9487
+ const full = join(directory, entry.name);
9488
+ if (entry.isDirectory()) {
9489
+ await walk(full);
9490
+ continue;
9491
+ }
9492
+ if (!entry.isFile()) continue;
9493
+ if (files.size >= MAX_FILES) {
9494
+ 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.`);
9495
+ }
9496
+ const contents = await readFile(full);
9497
+ totalBytes += contents.byteLength;
9498
+ if (totalBytes > MAX_TOTAL_BYTES) {
9499
+ 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.`);
9500
+ }
9501
+ files.set(toPackagePath(root, full), contents);
9502
+ }
9503
+ }, "walk");
9504
+ await walk(root);
9505
+ return files;
9506
+ }
9507
+ __name(readPackageDirectory, "readPackageDirectory");
9508
+ function toPackagePath(root, full) {
9509
+ return relative(root, full).split(sep).join("/");
9510
+ }
9511
+ __name(toPackagePath, "toPackagePath");
9512
+
9513
+ // src/check.ts
9514
+ var import_contracts6 = __toESM(require_dist());
7115
9515
  async function runCheck(options) {
7116
- const directory = resolve(options.directory ?? ".");
9516
+ const directory = resolve2(options.directory ?? ".");
7117
9517
  let files;
7118
9518
  try {
7119
9519
  files = await readPackageDirectory(directory);
@@ -7135,7 +9535,7 @@ async function runCheck(options) {
7135
9535
  print(style.red("This package would be REFUSED at import."));
7136
9536
  } else if (report.lintBlocked) {
7137
9537
  print();
7138
- 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.`);
9538
+ print(style.red("This package would be ACCEPTED at import \u2014 and then misbehave.") + ` The errors above are not things ${import_contracts6.BRAND_SHORT_NAME} checks; they are things that go wrong at run time, silently.`);
7139
9539
  }
7140
9540
  if (report.advisoryCount > 0) {
7141
9541
  print();
@@ -7155,11 +9555,11 @@ async function runCheck(options) {
7155
9555
  __name(runCheck, "runCheck");
7156
9556
 
7157
9557
  // src/init.ts
7158
- var import_contracts2 = __toESM(require_dist());
9558
+ var import_contracts7 = __toESM(require_dist());
7159
9559
  var import_action_manifest2 = __toESM(require_dist2());
7160
9560
  import { mkdir, writeFile } from "fs/promises";
7161
9561
  import { createInterface } from "readline/promises";
7162
- import { dirname, join as join2, resolve as resolve2 } from "path";
9562
+ import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
7163
9563
  async function runInit(options) {
7164
9564
  const interactive = process.stdin.isTTY === true && options.yes !== true;
7165
9565
  const ask = interactive ? prompter() : null;
@@ -7171,7 +9571,7 @@ async function runInit(options) {
7171
9571
  const interpreter = await resolveInterpreter(options.interpreter, ask);
7172
9572
  if (!interpreter) return 1;
7173
9573
  const author = options.author?.trim() || await ask?.("Author", id.split("/")[0]) || id.split("/")[0];
7174
- const directory = resolve2(options.directory ?? id.split("/")[1] ?? "action");
9574
+ const directory = resolve3(options.directory ?? id.split("/")[1] ?? "action");
7175
9575
  const files = (0, import_action_manifest2.scaffoldActionPackage)({
7176
9576
  id,
7177
9577
  name,
@@ -7181,7 +9581,7 @@ async function runInit(options) {
7181
9581
  });
7182
9582
  for (const [path, contents] of files) {
7183
9583
  const target = join2(directory, path);
7184
- await mkdir(dirname(target), {
9584
+ await mkdir(dirname2(target), {
7185
9585
  recursive: true
7186
9586
  });
7187
9587
  await writeFile(target, contents, {
@@ -7224,9 +9624,9 @@ async function resolveId(supplied, ask) {
7224
9624
  }
7225
9625
  __name(resolveId, "resolveId");
7226
9626
  async function resolveInterpreter(supplied, ask) {
7227
- const value = supplied?.trim() || await ask?.(`Interpreter (${import_contracts2.PACKAGE_INTERPRETERS.join(" / ")})`, "bash") || "bash";
9627
+ const value = supplied?.trim() || await ask?.(`Interpreter (${import_contracts7.PACKAGE_INTERPRETERS.join(" / ")})`, "bash") || "bash";
7228
9628
  if (!isInterpreter(value)) {
7229
- printError(style.red(`"${value}" is not an interpreter ${import_contracts2.BRAND_SHORT_NAME} runs.`) + ` The runner image ships ${import_contracts2.PACKAGE_INTERPRETERS.join(", ")}.`);
9629
+ printError(style.red(`"${value}" is not an interpreter ${import_contracts7.BRAND_SHORT_NAME} runs.`) + ` The runner image ships ${import_contracts7.PACKAGE_INTERPRETERS.join(", ")}.`);
7230
9630
  return null;
7231
9631
  }
7232
9632
  return value;
@@ -7247,7 +9647,7 @@ function prompter() {
7247
9647
  }
7248
9648
  __name(prompter, "prompter");
7249
9649
  function isInterpreter(value) {
7250
- return import_contracts2.PACKAGE_INTERPRETERS.includes(value);
9650
+ return import_contracts7.PACKAGE_INTERPRETERS.includes(value);
7251
9651
  }
7252
9652
  __name(isInterpreter, "isInterpreter");
7253
9653
  function titleCase(value) {
@@ -7257,8 +9657,8 @@ __name(titleCase, "titleCase");
7257
9657
 
7258
9658
  // src/publish.ts
7259
9659
  var import_action_manifest3 = __toESM(require_dist2());
7260
- var import_contracts3 = __toESM(require_dist());
7261
- import { resolve as resolve3 } from "path";
9660
+ var import_contracts8 = __toESM(require_dist());
9661
+ import { resolve as resolve4 } from "path";
7262
9662
 
7263
9663
  // src/renamed-variable.ts
7264
9664
  function renamedVariableRefusal(current, retired, env = process.env) {
@@ -7269,15 +9669,15 @@ function renamedVariableRefusal(current, retired, env = process.env) {
7269
9669
  __name(renamedVariableRefusal, "renamedVariableRefusal");
7270
9670
 
7271
9671
  // src/publish.ts
7272
- var TOKEN_ENV = import_contracts3.AUTHORING_CLI_ENV.token;
7273
- var URL_ENV = import_contracts3.AUTHORING_CLI_ENV.url;
9672
+ var TOKEN_ENV = import_contracts8.AUTHORING_CLI_ENV.token;
9673
+ var URL_ENV = import_contracts8.AUTHORING_CLI_ENV.url;
7274
9674
  async function runPublish(options) {
7275
- const directory = resolve3(options.directory ?? ".");
9675
+ const directory = resolve4(options.directory ?? ".");
7276
9676
  const baseUrl = (options.to ?? process.env[URL_ENV] ?? "").trim().replace(/\/+$/, "");
7277
- const token = (options.token ?? process.env[TOKEN_ENV] ?? "").trim();
9677
+ const token2 = (options.token ?? process.env[TOKEN_ENV] ?? "").trim();
7278
9678
  const stale = [
7279
- options.to === void 0 ? renamedVariableRefusal(URL_ENV, import_contracts3.RETIRED_AUTHORING_CLI_ENV.url) : null,
7280
- options.token === void 0 ? renamedVariableRefusal(TOKEN_ENV, import_contracts3.RETIRED_AUTHORING_CLI_ENV.token) : null
9679
+ options.to === void 0 ? renamedVariableRefusal(URL_ENV, import_contracts8.RETIRED_AUTHORING_CLI_ENV.url) : null,
9680
+ options.token === void 0 ? renamedVariableRefusal(TOKEN_ENV, import_contracts8.RETIRED_AUTHORING_CLI_ENV.token) : null
7281
9681
  ].filter((sentence) => sentence !== null);
7282
9682
  if (stale.length > 0) {
7283
9683
  for (const sentence of stale) printError(style.red(sentence));
@@ -7287,8 +9687,8 @@ async function runPublish(options) {
7287
9687
  printError(style.red("Where to?") + ` Pass --to https://your-runveric.example.com, or set ${URL_ENV}.`);
7288
9688
  return 1;
7289
9689
  }
7290
- if (!token) {
7291
- 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.)`);
9690
+ if (!token2) {
9691
+ printError(style.red(`Publishing needs ${import_contracts8.BRAND_ARTICLE} ${import_contracts8.BRAND_SHORT_NAME} access token.`) + ` Set ${TOKEN_ENV}, or pass --token. (init, check and test need no account at all.)`);
7292
9692
  return 1;
7293
9693
  }
7294
9694
  let files;
@@ -7308,7 +9708,7 @@ async function runPublish(options) {
7308
9708
  errors: report.errorCount,
7309
9709
  warnings: report.warningCount
7310
9710
  });
7311
- 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."));
9711
+ printError(style.red("\nNot published.") + (report.validation.ok ? ` ${import_contracts8.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."));
7312
9712
  return 1;
7313
9713
  }
7314
9714
  if (report.advisoryCount > 0) {
@@ -7335,7 +9735,7 @@ async function runPublish(options) {
7335
9735
  method: "POST",
7336
9736
  headers: {
7337
9737
  "content-type": "application/json",
7338
- authorization: `Bearer ${token}`
9738
+ authorization: `Bearer ${token2}`
7339
9739
  },
7340
9740
  body
7341
9741
  });
@@ -7355,7 +9755,7 @@ Publish refused (${response.status}).`));
7355
9755
  try {
7356
9756
  version2 = JSON.parse(text);
7357
9757
  } catch {
7358
- printError(style.red(`${import_contracts3.BRAND_SHORT_NAME} accepted the package but returned a response we cannot read.`));
9758
+ printError(style.red(`${import_contracts8.BRAND_SHORT_NAME} accepted the package but returned a response we cannot read.`));
7359
9759
  return 1;
7360
9760
  }
7361
9761
  print();
@@ -7364,7 +9764,7 @@ Publish refused (${response.status}).`));
7364
9764
  print(` content digest ${version2.contentDigest}`);
7365
9765
  print(` tier ${version2.tier} ${style.grey(`(${version2.provenance})`)}`);
7366
9766
  print();
7367
- 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.`));
9767
+ print(style.grey(`It is an immutable version now. Adopt it in ${import_contracts8.BRAND_SHORT_NAME} to put it in the catalog \u2014 publishing does not adopt, so nobody can run it until somebody chooses to.`));
7368
9768
  print();
7369
9769
  return 0;
7370
9770
  }
@@ -7376,11 +9776,11 @@ function formatProblem(text) {
7376
9776
  } catch {
7377
9777
  return ` ${text}`;
7378
9778
  }
7379
- const problem = (0, import_contracts3.parseProblemDocument)(parsed);
9779
+ const problem = (0, import_contracts8.parseProblemDocument)(parsed);
7380
9780
  if (!problem) return ` ${text}`;
7381
9781
  const lines = [];
7382
9782
  if (problem.detail) lines.push(` ${problem.detail}`);
7383
- for (const error of (0, import_contracts3.problemFieldErrors)(problem)) {
9783
+ for (const error of (0, import_contracts8.problemFieldErrors)(problem)) {
7384
9784
  lines.push(error.field ? ` ${style.bold(error.field)}: ${error.message}` : ` ${error.message}`);
7385
9785
  }
7386
9786
  return lines.length > 0 ? lines.join("\n") : ` ${text}`;
@@ -7388,12 +9788,12 @@ function formatProblem(text) {
7388
9788
  __name(formatProblem, "formatProblem");
7389
9789
 
7390
9790
  // src/runner-image.ts
7391
- var import_contracts4 = __toESM(require_dist());
9791
+ var import_contracts9 = __toESM(require_dist());
7392
9792
  import { execFile } from "child_process";
7393
9793
  import { promisify } from "util";
7394
9794
  var run = promisify(execFile);
7395
9795
  var LOCAL_RUNNER_IMAGE = "runveric-package-runner:local";
7396
- var RUNNER_IMAGE_ENV = import_contracts4.AUTHORING_CLI_ENV.runnerImage;
9796
+ var RUNNER_IMAGE_ENV = import_contracts9.AUTHORING_CLI_ENV.runnerImage;
7397
9797
  var RenamedVariableError = class extends Error {
7398
9798
  static {
7399
9799
  __name(this, "RenamedVariableError");
@@ -7410,7 +9810,7 @@ function resolveRunnerImage(flag) {
7410
9810
  reference: configured,
7411
9811
  origin: "environment"
7412
9812
  };
7413
- const stale = renamedVariableRefusal(RUNNER_IMAGE_ENV, import_contracts4.RETIRED_AUTHORING_CLI_ENV.runnerImage);
9813
+ const stale = renamedVariableRefusal(RUNNER_IMAGE_ENV, import_contracts9.RETIRED_AUTHORING_CLI_ENV.runnerImage);
7414
9814
  if (stale) throw new RenamedVariableError(stale);
7415
9815
  return {
7416
9816
  reference: LOCAL_RUNNER_IMAGE,
@@ -7425,7 +9825,7 @@ function runnerImageNote(image) {
7425
9825
  case "environment":
7426
9826
  return `from ${RUNNER_IMAGE_ENV}`;
7427
9827
  case "local-build":
7428
- return `locally built \u2014 NOT the image your ${import_contracts4.BRAND_SHORT_NAME} runs; set ${RUNNER_IMAGE_ENV} to match it`;
9828
+ return `locally built \u2014 NOT the image your ${import_contracts9.BRAND_SHORT_NAME} runs; set ${RUNNER_IMAGE_ENV} to match it`;
7429
9829
  }
7430
9830
  }
7431
9831
  __name(runnerImageNote, "runnerImageNote");
@@ -7442,7 +9842,7 @@ async function assertDockerAvailable() {
7442
9842
  "{{.Server.Version}}"
7443
9843
  ]);
7444
9844
  } catch {
7445
- 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.`);
9845
+ throw new DockerMissingError(`Docker is not available. \`runveric test\` runs your package in the real ${import_contracts9.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.`);
7446
9846
  }
7447
9847
  }
7448
9848
  __name(assertDockerAvailable, "assertDockerAvailable");
@@ -7465,7 +9865,7 @@ var import_action_manifest4 = __toESM(require_dist2());
7465
9865
  import { spawn } from "child_process";
7466
9866
  import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
7467
9867
  import { tmpdir } from "os";
7468
- import { dirname as dirname2, join as join3, resolve as resolve4 } from "path";
9868
+ import { dirname as dirname3, join as join3, resolve as resolve5 } from "path";
7469
9869
  import { StringDecoder } from "string_decoder";
7470
9870
 
7471
9871
  // src/azure-credential.ts
@@ -7488,7 +9888,7 @@ async function acquireAzureCredential() {
7488
9888
  if (!account.id || !account.tenantId) {
7489
9889
  throw new AzureCredentialError("`az account show` returned no subscription. Run `az login` and `az account set --subscription <id>` first.");
7490
9890
  }
7491
- const token = await azJson([
9891
+ const token2 = await azJson([
7492
9892
  "account",
7493
9893
  "get-access-token",
7494
9894
  "--resource",
@@ -7496,11 +9896,11 @@ async function acquireAzureCredential() {
7496
9896
  "--output",
7497
9897
  "json"
7498
9898
  ]);
7499
- if (!token.accessToken) {
9899
+ if (!token2.accessToken) {
7500
9900
  throw new AzureCredentialError("`az account get-access-token` returned no access token.");
7501
9901
  }
7502
9902
  return {
7503
- token: token.accessToken,
9903
+ token: token2.accessToken,
7504
9904
  // The identical names the credential adapter puts in a lease, so a package reads the same
7505
9905
  // environment here and in production.
7506
9906
  env: {
@@ -7535,7 +9935,7 @@ ${message.trim()}`);
7535
9935
  __name(azJson, "azJson");
7536
9936
 
7537
9937
  // src/test.ts
7538
- var import_contracts5 = __toESM(require_dist());
9938
+ var import_contracts10 = __toESM(require_dist());
7539
9939
  var PHASES = [
7540
9940
  "validate",
7541
9941
  "run",
@@ -7549,10 +9949,10 @@ var CONTAINER = {
7549
9949
  creds: "/runveric/creds"
7550
9950
  };
7551
9951
  async function runTest(options) {
7552
- const directory = resolve4(options.directory ?? ".");
9952
+ const directory = resolve5(options.directory ?? ".");
7553
9953
  const phase = options.phase ?? "run";
7554
9954
  if (!PHASES.includes(phase)) {
7555
- 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.`);
9955
+ printError(style.red(`"${options.phase}" is not a phase.`) + ` ${import_contracts10.BRAND_SHORT_NAME} runs ${PHASES.join(", ")} \u2014 one at a time, so you can read each one's result.`);
7556
9956
  return 1;
7557
9957
  }
7558
9958
  let files;
@@ -7624,9 +10024,9 @@ async function runTest(options) {
7624
10024
  print(` runner image ${image.reference} ${style.grey(`(${runnerImageNote(image)})`)}`);
7625
10025
  print(` interpreter ${interpreter}@${manifest.runtime.version}`);
7626
10026
  print(` entrypoint ${entrypoint}${import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[interpreter].entryFile}`);
7627
- 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)")}`);
10027
+ print(` identity ${credential ? `${credential.account.name} ${style.grey(`\u2014 YOUR access, not ${import_contracts10.BRAND_SHORT_NAME}'s brokered least-privilege identity`)}` : style.grey("none (--no-credential)")}`);
7628
10028
  if (manifest.requiresNetwork) {
7629
- print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${import_contracts5.BRAND_SHORT_NAME} enforces it`)}`);
10029
+ print(` network ${style.yellow("open")} ${style.grey(`\u2014 your allowlist (${(manifest.egressAllowlist ?? []).join(", ")}) is NOT enforced here; ${import_contracts10.BRAND_SHORT_NAME} enforces it`)}`);
7630
10030
  } else {
7631
10031
  print(` network ${style.grey("none \u2014 this package declares requiresNetwork: false")}`);
7632
10032
  }
@@ -7728,13 +10128,13 @@ async function materialize(workspace, input) {
7728
10128
  })));
7729
10129
  for (const [path, contents] of input.files) {
7730
10130
  const target = join3(layout.packageRoot, path);
7731
- await mkdir2(dirname2(target), {
10131
+ await mkdir2(dirname3(target), {
7732
10132
  recursive: true
7733
10133
  });
7734
10134
  await writeFile2(target, contents);
7735
10135
  }
7736
10136
  const context = {
7737
- protocolVersion: import_contracts5.PACKAGE_EXECUTION_PROTOCOL_VERSION,
10137
+ protocolVersion: import_contracts10.PACKAGE_EXECUTION_PROTOCOL_VERSION,
7738
10138
  // A recognisable id rather than a random uuid: it appears in the package's own log lines,
7739
10139
  // and "local-test" tells the reader instantly that this was not a real run.
7740
10140
  executionId: "local-test",
@@ -7760,7 +10160,7 @@ async function runContainer(options) {
7760
10160
  const launch = import_action_manifest4.PACKAGE_INTERPRETER_LAUNCH[options.interpreter];
7761
10161
  const script = `${CONTAINER.packageRoot}/${options.entrypoint}${launch.entryFile}`;
7762
10162
  const environment = {
7763
- RUNVERIC_PROTOCOL_VERSION: import_contracts5.PACKAGE_RESULT_PROTOCOL_VERSION,
10163
+ RUNVERIC_PROTOCOL_VERSION: import_contracts10.PACKAGE_RESULT_PROTOCOL_VERSION,
7764
10164
  RUNVERIC_PHASE: options.phase,
7765
10165
  RUNVERIC_CONTEXT_FILE: `${CONTAINER.input}/context.json`,
7766
10166
  RUNVERIC_PARAMETERS_FILE: `${CONTAINER.input}/parameters.json`,
@@ -7887,13 +10287,13 @@ function emitLine(line, stderr, onEvent) {
7887
10287
  __name(emitLine, "emitLine");
7888
10288
  async function reportOutcome(options) {
7889
10289
  if (options.exit.timedOut) {
7890
- 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.`);
10290
+ printError(style.red("\nThe package did not finish within 15 minutes and was killed.") + ` ${import_contracts10.BRAND_SHORT_NAME} enforces the \`executionTimeout\` in your manifest; this is the local ceiling.`);
7891
10291
  return 1;
7892
10292
  }
7893
10293
  const exists = await stat2(options.resultPath).catch(() => null);
7894
10294
  if (!exists?.isFile() || exists.size === 0) {
7895
10295
  printError(style.red("\nThe package wrote no result.") + `
7896
- result.json is the only channel ${import_contracts5.BRAND_SHORT_NAME} reads. Exiting ${options.exit.code ?? "with a signal"} without writing $RUNVERIC_RESULT_FILE is a FAILED run, whatever the script printed.
10296
+ result.json is the only channel ${import_contracts10.BRAND_SHORT_NAME} reads. Exiting ${options.exit.code ?? "with a signal"} without writing $RUNVERIC_RESULT_FILE is a FAILED run, whatever the script printed.
7897
10297
  Write it as the last thing your script does, on every path \u2014 including the ones that fail.`);
7898
10298
  return 1;
7899
10299
  }
@@ -7901,7 +10301,7 @@ async function reportOutcome(options) {
7901
10301
  try {
7902
10302
  parsed = JSON.parse(await readFile2(options.resultPath, "utf8"));
7903
10303
  } catch {
7904
- printError(style.red("\nresult.json is not valid JSON.") + ` ${import_contracts5.BRAND_SHORT_NAME} refuses the run rather than guessing what the package meant.`);
10304
+ printError(style.red("\nresult.json is not valid JSON.") + ` ${import_contracts10.BRAND_SHORT_NAME} refuses the run rather than guessing what the package meant.`);
7905
10305
  return 1;
7906
10306
  }
7907
10307
  let result;
@@ -7909,14 +10309,14 @@ async function reportOutcome(options) {
7909
10309
  result = (0, import_action_manifest4.validatePackageResult)(parsed, options.phase);
7910
10310
  } catch (error) {
7911
10311
  printError(style.red(`
7912
- ${import_contracts5.BRAND_SHORT_NAME} would REFUSE this result.`) + `
10312
+ ${import_contracts10.BRAND_SHORT_NAME} would REFUSE this result.`) + `
7913
10313
  ${error instanceof Error ? error.message : String(error)}
7914
10314
  This is the same check the control plane runs, so fixing it here fixes it there.`);
7915
10315
  return 1;
7916
10316
  }
7917
10317
  printResult(result);
7918
10318
  if (options.exit.code !== 0) {
7919
- 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.`));
10319
+ print(style.yellow(`The package exited ${options.exit.code} but wrote a valid result. ${import_contracts10.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.`));
7920
10320
  print();
7921
10321
  }
7922
10322
  return result.status === "succeeded" ? 0 : 1;
@@ -7925,7 +10325,7 @@ __name(reportOutcome, "reportOutcome");
7925
10325
  async function readJsonFile(path) {
7926
10326
  let source;
7927
10327
  try {
7928
- source = await readFile2(resolve4(path), "utf8");
10328
+ source = await readFile2(resolve5(path), "utf8");
7929
10329
  } catch {
7930
10330
  throw new Error(`Could not read ${path}.`);
7931
10331
  }
@@ -7943,9 +10343,9 @@ async function readJsonFile(path) {
7943
10343
  __name(readJsonFile, "readJsonFile");
7944
10344
 
7945
10345
  // src/main.ts
7946
- var import_contracts6 = __toESM(require_dist());
10346
+ var import_contracts11 = __toESM(require_dist());
7947
10347
  var USAGE = `
7948
- ${style.bold("runveric")} \u2014 author ${import_contracts6.BRAND_ARTICLE} ${import_contracts6.BRAND_SHORT_NAME} action, without pushing anything.
10348
+ ${style.bold("runveric")} \u2014 author ${import_contracts11.BRAND_ARTICLE} ${import_contracts11.BRAND_SHORT_NAME} action, without pushing anything.
7949
10349
 
7950
10350
  ${style.bold("USAGE")}
7951
10351
  runveric <command> [options]
@@ -7953,8 +10353,9 @@ ${style.bold("USAGE")}
7953
10353
  ${style.bold("COMMANDS")}
7954
10354
  init [directory] Scaffold a working action. It passes check and test immediately.
7955
10355
  check Validate the package. Offline \u2014 no account, no cloud, no network.
7956
- test Run one phase in the real ${import_contracts6.BRAND_SHORT_NAME} runner image.
7957
- publish Upload it to ${import_contracts6.BRAND_SHORT_NAME} as an authored version. Needs an account.
10356
+ test Run one phase in the real ${import_contracts11.BRAND_SHORT_NAME} runner image.
10357
+ publish Upload it to ${import_contracts11.BRAND_SHORT_NAME} as an authored version. Needs an account.
10358
+ catalog <command> Publish first-party catalog artifacts. Not for authoring: see \`runveric catalog --help\`.
7958
10359
 
7959
10360
  ${style.bold("init")}
7960
10361
  --id <publisher/name> The action id. Required.
@@ -7977,9 +10378,9 @@ ${style.bold("test")}
7977
10378
  --keep Keep the materialized /runveric tree for inspection.
7978
10379
 
7979
10380
  ${style.bold("publish")}
7980
- --to <url> Your ${import_contracts6.BRAND_SHORT_NAME} URL. Defaults to $${URL_ENV}.
10381
+ --to <url> Your ${import_contracts11.BRAND_SHORT_NAME} URL. Defaults to $${URL_ENV}.
7981
10382
  --token <token> Access token. Defaults to $${TOKEN_ENV}.
7982
- --force Publish despite lint errors. Never bypasses ${import_contracts6.BRAND_SHORT_NAME}'s validation.
10383
+ --force Publish despite lint errors. Never bypasses ${import_contracts11.BRAND_SHORT_NAME}'s validation.
7983
10384
 
7984
10385
  ${style.bold("EVERYWHERE")}
7985
10386
  --dir <path> The package directory. Defaults to the current directory.
@@ -8022,7 +10423,9 @@ __name(parseArgv, "parseArgv");
8022
10423
  var first = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0], "first");
8023
10424
  var bool = /* @__PURE__ */ __name((parsed, name) => parsed.flags.get(name)?.[0] === "true", "bool");
8024
10425
  async function main() {
8025
- const parsed = parseArgv(process.argv.slice(2));
10426
+ const argv = process.argv.slice(2);
10427
+ if (argv[0] === "catalog") return runCatalog(argv.slice(1));
10428
+ const parsed = parseArgv(argv);
8026
10429
  if (parsed.flags.has("version")) {
8027
10430
  print(version);
8028
10431
  return 0;
@@ -8090,7 +10493,7 @@ async function main() {
8090
10493
  force: bool(parsed, "force")
8091
10494
  });
8092
10495
  default:
8093
- printError(style.red(`"${parsed.command}" is not a command.`) + " There are four: init, check, test, publish.");
10496
+ 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.");
8094
10497
  return 1;
8095
10498
  }
8096
10499
  }